mirror of https://github.com/torvalds/linux.git
drm-next for 6.19-rc1:
new driver:
- Arm Ethos-U65/U85 accel driver
core:
- support the drm color pipeline in vkms/amdgfx
- add support for drm colorop pipeline
- add COLOR PIPELINE plane property
- add DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
- throttle dirty worker with vblank
- use drm_for_each_bridge_in_chain_scoped in drm's bridge code
- Ensure drm_client_modeset tests are enabled in UML
- add simulated vblank interrupt - use in drivers
- dumb buffer sizing helper
- move freeing of drm client memory to driver
- crtc sharpness strength property
- stop using system_wq in scheduler/drivers
- support emergency restore in drm-client
rust:
- make slice::as_flattened usable on all supported rustc
- add FromBytes::from_bytes_prefix() method
- remove redundant device ptr from Rust GEM object
- Change how AlwaysRefCounted is implemented for GEM objects
gpuvm:
- Add deferred vm_bo cleanup to GPUVM (for rust)
atomic:
- cleanup and improve state handling interfaces
buddy:
- optimize block management
dma-buf:
- heaps: Create heap per CMA reserved location
- improve userspace documentation
dp:
- add POST_LT_ADJ_REQ training sequence
- DPCD dSC quirk for synaptics panamera devices
- helpers to query branch DSC max throughput
ttm:
- Rename ttm_bo_put to ttm_bo_fini
- allow page protection flags on risc-v
- rework pipelined eviction fence handling
amdgpu:
- enable amdgpu by default for SI/CI dGPUs
- enable DC by default on SI
- refactor CIK/SI enablement
- add ABM KMS property
- Re-enable DM idle optimizations
- DC Analog encoders support
- Powerplay fixes for fiji/iceland
- Enable DC on bonaire by default
- HMM cleanup
- Add new RAS framework
- DML2.1 updates
- YCbCr420 fixes
- DC FP fixes
- DMUB fixes
- LTTPR fixes
- DTBCLK fixes
- DMU cursor offload handling
- Userq validation improvements
- Unify shutdown callback handling
- Suspend improvements
- Power limit code cleanup
- SR-IOV fixes
- AUX backlight fixes
- DCN 3.5 fixes
- HDMI compliance fixes
- DCN 4.0.1 cursor updates
- DCN interrupt fix
- DC KMS full update improvements
- Add additional HDCP traces
- DCN 3.2 fixes
- DP MST fixes
- Add support for new SR-IOV mailbox interface
- UQ reset support
- HDP flush rework
- VCE1 support
amdkfd:
- HMM cleanups
- Relax checks on save area overallocations
- Fix GPU mappings after prefetch
radeon:
- refactor CIK/SI enablement.
xe:
- Initial Xe3P support
- panic support on VRAM for display
- fix stolen size check
- Loosen used tracking restriction
- New SR-IOV debugfs structure and debugfs updates
- Hide the GPU madvise flag behind a VM_BIND flag
- Always expose VRAM provisioning data on discrete GPUs
- Allow VRAM mappings for userptr when used with SVM
- Allow pinning of p2p dma-buf
- Use per-tile debugfs where appropriate
- Add documentation for Execution Queues
- PF improvements
- VF migration recovery redesign work
- User / Kernel VRAM partitioning
- Update Tile-based messages
- Allow configfs to disable specific GT types
- VF provisioning and migration improvements
- use SVM range helpers in PT layer
- Initial CRI support
- access VF registers using dedicated MMIO view
- limit number of jobs per exec queue
- add sriov_admin sysfs tree
- more crescent island specific support
- debugfs residency counter
- SRIOV migration work
- runtime registers for GFX 35
i915:
- add initial Xe3p_LPD display version 35 support
- Enable LNL+ content adaptive sharpness filter
- Use optimized VRR guardband
- Enable Xe3p LT PHY
- enable FBC support for Xe3p_LPD display
- add display 30.02 firmware support
- refactor SKL+ watermark latency setup
- refactor fbdev handling
- call i915/xe runtime PM via function pointers
- refactor i915/xe stolen memory/display interfaces
- use display version instead of gfx version in display code
- extend i915_display_info with Type-C port details
- lots of display cleanups/refactorings
- set O_LARGEFILE in __create_shmem
- skuip guc communication warning on reset
- fix time conversions
- defeature DRRS on LNL+
- refactor intel_frontbuffer split between i915/xe/display
- convert inteL_rom interfaces to struct drm_device
- unify display register polling interfaces
- aovid lock inversion when pinning to GGTT on CHV/BXT+VTD
panel:
- Add KD116N3730A08/A12, chromebook mt8189
- JT101TM023, LQ079L1SX01,
- GLD070WX3-SL01 MIPI DSI
- Samsung LTL106AL0, Samsung LTL106AL01
- Raystar RFF500F-AWH-DNN
- Winstar WF70A8SYJHLNGA,
- Wanchanglong w552946aaa
- Samsung SOFEF00
- Lenovo X13s panel.
- ilitek-ili9881c : add rpi 5" support
- visionx-rm69299 - add backlight support
- edp - support AUI B116XAN02.0
bridge:
- improve ref counting
- ti-sn65dsi86 - add support for DP mode with HPD
- synopsis: support CEC, init timer with correct freq
- ASL CS5263 DP-to-HDMI bridge support
nova-core:
- introduce bitfield! macro
- introduce safe integer converters
- GSP inits to fully booted state on Ampere
- Use more future-proof register for GPU identification
nova-drm:
- select NOVA_CORE
- 64-bit only
nouveau:
- improve reclocking on tegra 186+
- add large page and compression support
msm:
- GPU:
- Gen8 support: A840 (Kaanapali) and X2-85 (Glymur)
- A612 support
- MDSS:
- Added support for Glymur and QCS8300 platforms
- DPU:
- Enabled Quad-Pipe support, unlocking higher resolutions support
- Added support for Glymur platform
- Documented DPU on QCS8300 platform as supported
- DisplayPort:
- Added support for Glymur platform
- Added support lame remapping inside DP block
- Documented DisplayPort controller on QCS8300 and SM6150/QCS615 as
supported
tegra:
- NVJPG driver
panfrost:
- display JM contexts over debugfs
- export JM contexts to userspace
- improve error and job handling
panthor:
- support custom ASN_HASH for mt8196
- support mali-G1 GPU
- flush shmem write before mapping buffers uncached
- make timeout per-queue instead of per-job
mediatek:
- MT8195/88 HDMIv2/DDCv2 support
rockchip:
- dsi: add support for RK3368
amdxdna:
- enhance runtime PM
- last hardware error reading uapi
- support firmware debug output
- add resource and telemetry data uapi
- preemption support
imx:
- add driver for HDMI TX Parallel audio interface
ivpu:
- add support for user-managed preemption buffer
- add userptr support
- update JSM firware API to 3.33.0
- add better alloc/free warnings
- fix page fault in unbind all bos
- rework bind/unbind of imported buffers
- enable MCA ECC signalling
- split fw runtime and global memory buffers
- add fdinfo memory statistics
tidss:
- convert to drm logging
- logging cleanup
ast:
- refactor generation init paths
- add per chip generation detect_tx_chip
- set quirks for each chip model
atmel-hlcdc:
- set LCDC_ATTRE register in plane disable
- set correct values for plane scaler
solomon:
- use drm helper for get_modes and move_valid
sitronix:
- fix output position when clearing screens
qaic:
- support dma-buf exports
- support new firmware's READ_DATA implementation
- sahara AIC200 image table update
- add sysfs support
- add coredump support
- add uevents support
- PM support
sun4i:
- layer refactors to decouple plane from output
- improve DE33 support
vc4:
- switch to generic CEC helpers
komeda:
- use drm_ logging functions
vkms:
- configfs support for display configuration
vgem:
- fix fence timer deadlock
etnaviv:
- add HWDB entry for GC8000 Nano Ultra VIP r6205
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmkv4wAACgkQDHTzWXnE
hr7GNw/9HITakN5B4DXKZg+Rz38WjsAsSbw1HdgoQR2NMYRw4oVXx3iAVTVLGbvV
vvMCpgWtFE85IEtg96KWynpFjSHOx7xtXe/lckZ5s+VMi00K4c+wMmvFqKwv1Aul
9VqPTe+28xNAzk+gglOtNjd4DRVuXQIhPMQdTELork32jQCteJorod+sZnM+RNBG
0PiiYAKc0VwsPwklHQF7+dBt1yu+8r3ZH5Fc7SjHTuf8Q45Dpayd0HjatSgt9ycn
s8uxAAY41iDLHnj4LwyYtBFYoMH65VqOAzDeaeVdM9xA/3KbIl9of8QnBpvYWhaR
9dnh2Ceve3SjOufOBwK2p6H0RXu8Xo3OLnmw7+u0KwgvbmeBTILp8kgNQuk9beIp
wvkjwpFsDnfo1rKevFMz4R+3U9W3NXP1fjU2tvmnD1lfaby2o3f3AdboaYPbX/xK
YGh79KDsuAkKH4HdMJNg/FpJWIdSKaaWoFBzZqWBZgZBEPOquR8MHUAaLkVJYK/1
wBcJZceQpHRssldsg3bBZxGHK811J46QlvMWaID6TSGIoZ8GC6+1Zt0+bwzxMyUR
49jwRvOWbtYJmZEcR2JF+k1KteMXAp+bXIYqx9e+69c5Guz4w7JW7OLQFVvXszZW
aObzfrkObPpCGYDS1r85/6vT8yqwVUWtwfPvAeDH9130sHBP8Xw=
=gpnQ
-----END PGP SIGNATURE-----
Merge tag 'drm-next-2025-12-03' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie:
"There was a rather late merge of a new color pipeline feature, that
some userspace projects are blocked on, and has seen a lot of work in
amdgpu. This should have seen some time in -next. There is additional
support for this for Intel, that if it arrives in the next day or two
I'll pass it on in another pull request and you can decide if you want
to take it.
Highlights:
- Arm Ethos NPU accelerator driver
- new DRM color pipeline support
- amdgpu will now run discrete SI/CIK cards instead of radeon, which
enables vulkan support in userspace
- msm gets gen8 gpu support
- initial Xe3P support in xe
Full detail summary:
New driver:
- Arm Ethos-U65/U85 accel driver
Core:
- support the drm color pipeline in vkms/amdgfx
- add support for drm colorop pipeline
- add COLOR PIPELINE plane property
- add DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE
- throttle dirty worker with vblank
- use drm_for_each_bridge_in_chain_scoped in drm's bridge code
- Ensure drm_client_modeset tests are enabled in UML
- add simulated vblank interrupt - use in drivers
- dumb buffer sizing helper
- move freeing of drm client memory to driver
- crtc sharpness strength property
- stop using system_wq in scheduler/drivers
- support emergency restore in drm-client
Rust:
- make slice::as_flattened usable on all supported rustc
- add FromBytes::from_bytes_prefix() method
- remove redundant device ptr from Rust GEM object
- Change how AlwaysRefCounted is implemented for GEM objects
gpuvm:
- Add deferred vm_bo cleanup to GPUVM (for rust)
atomic:
- cleanup and improve state handling interfaces
buddy:
- optimize block management
dma-buf:
- heaps: Create heap per CMA reserved location
- improve userspace documentation
dp:
- add POST_LT_ADJ_REQ training sequence
- DPCD dSC quirk for synaptics panamera devices
- helpers to query branch DSC max throughput
ttm:
- Rename ttm_bo_put to ttm_bo_fini
- allow page protection flags on risc-v
- rework pipelined eviction fence handling
amdgpu:
- enable amdgpu by default for SI/CI dGPUs
- enable DC by default on SI
- refactor CIK/SI enablement
- add ABM KMS property
- Re-enable DM idle optimizations
- DC Analog encoders support
- Powerplay fixes for fiji/iceland
- Enable DC on bonaire by default
- HMM cleanup
- Add new RAS framework
- DML2.1 updates
- YCbCr420 fixes
- DC FP fixes
- DMUB fixes
- LTTPR fixes
- DTBCLK fixes
- DMU cursor offload handling
- Userq validation improvements
- Unify shutdown callback handling
- Suspend improvements
- Power limit code cleanup
- SR-IOV fixes
- AUX backlight fixes
- DCN 3.5 fixes
- HDMI compliance fixes
- DCN 4.0.1 cursor updates
- DCN interrupt fix
- DC KMS full update improvements
- Add additional HDCP traces
- DCN 3.2 fixes
- DP MST fixes
- Add support for new SR-IOV mailbox interface
- UQ reset support
- HDP flush rework
- VCE1 support
amdkfd:
- HMM cleanups
- Relax checks on save area overallocations
- Fix GPU mappings after prefetch
radeon:
- refactor CIK/SI enablement
xe:
- Initial Xe3P support
- panic support on VRAM for display
- fix stolen size check
- Loosen used tracking restriction
- New SR-IOV debugfs structure and debugfs updates
- Hide the GPU madvise flag behind a VM_BIND flag
- Always expose VRAM provisioning data on discrete GPUs
- Allow VRAM mappings for userptr when used with SVM
- Allow pinning of p2p dma-buf
- Use per-tile debugfs where appropriate
- Add documentation for Execution Queues
- PF improvements
- VF migration recovery redesign work
- User / Kernel VRAM partitioning
- Update Tile-based messages
- Allow configfs to disable specific GT types
- VF provisioning and migration improvements
- use SVM range helpers in PT layer
- Initial CRI support
- access VF registers using dedicated MMIO view
- limit number of jobs per exec queue
- add sriov_admin sysfs tree
- more crescent island specific support
- debugfs residency counter
- SRIOV migration work
- runtime registers for GFX 35
i915:
- add initial Xe3p_LPD display version 35 support
- Enable LNL+ content adaptive sharpness filter
- Use optimized VRR guardband
- Enable Xe3p LT PHY
- enable FBC support for Xe3p_LPD display
- add display 30.02 firmware support
- refactor SKL+ watermark latency setup
- refactor fbdev handling
- call i915/xe runtime PM via function pointers
- refactor i915/xe stolen memory/display interfaces
- use display version instead of gfx version in display code
- extend i915_display_info with Type-C port details
- lots of display cleanups/refactorings
- set O_LARGEFILE in __create_shmem
- skuip guc communication warning on reset
- fix time conversions
- defeature DRRS on LNL+
- refactor intel_frontbuffer split between i915/xe/display
- convert inteL_rom interfaces to struct drm_device
- unify display register polling interfaces
- aovid lock inversion when pinning to GGTT on CHV/BXT+VTD
panel:
- Add KD116N3730A08/A12, chromebook mt8189
- JT101TM023, LQ079L1SX01,
- GLD070WX3-SL01 MIPI DSI
- Samsung LTL106AL0, Samsung LTL106AL01
- Raystar RFF500F-AWH-DNN
- Winstar WF70A8SYJHLNGA
- Wanchanglong w552946aaa
- Samsung SOFEF00
- Lenovo X13s panel
- ilitek-ili9881c - add rpi 5" support
- visionx-rm69299 - add backlight support
- edp - support AUI B116XAN02.0
bridge:
- improve ref counting
- ti-sn65dsi86 - add support for DP mode with HPD
- synopsis: support CEC, init timer with correct freq
- ASL CS5263 DP-to-HDMI bridge support
nova-core:
- introduce bitfield! macro
- introduce safe integer converters
- GSP inits to fully booted state on Ampere
- Use more future-proof register for GPU identification
nova-drm:
- select NOVA_CORE
- 64-bit only
nouveau:
- improve reclocking on tegra 186+
- add large page and compression support
msm:
- GPU:
- Gen8 support: A840 (Kaanapali) and X2-85 (Glymur)
- A612 support
- MDSS:
- Added support for Glymur and QCS8300 platforms
- DPU:
- Enabled Quad-Pipe support, unlocking higher resolutions support
- Added support for Glymur platform
- Documented DPU on QCS8300 platform as supported
- DisplayPort:
- Added support for Glymur platform
- Added support lame remapping inside DP block
- Documented DisplayPort controller on QCS8300 and SM6150/QCS615
as supported
tegra:
- NVJPG driver
panfrost:
- display JM contexts over debugfs
- export JM contexts to userspace
- improve error and job handling
panthor:
- support custom ASN_HASH for mt8196
- support mali-G1 GPU
- flush shmem write before mapping buffers uncached
- make timeout per-queue instead of per-job
mediatek:
- MT8195/88 HDMIv2/DDCv2 support
rockchip:
- dsi: add support for RK3368
amdxdna:
- enhance runtime PM
- last hardware error reading uapi
- support firmware debug output
- add resource and telemetry data uapi
- preemption support
imx:
- add driver for HDMI TX Parallel audio interface
ivpu:
- add support for user-managed preemption buffer
- add userptr support
- update JSM firware API to 3.33.0
- add better alloc/free warnings
- fix page fault in unbind all bos
- rework bind/unbind of imported buffers
- enable MCA ECC signalling
- split fw runtime and global memory buffers
- add fdinfo memory statistics
tidss:
- convert to drm logging
- logging cleanup
ast:
- refactor generation init paths
- add per chip generation detect_tx_chip
- set quirks for each chip model
atmel-hlcdc:
- set LCDC_ATTRE register in plane disable
- set correct values for plane scaler
solomon:
- use drm helper for get_modes and move_valid
sitronix:
- fix output position when clearing screens
qaic:
- support dma-buf exports
- support new firmware's READ_DATA implementation
- sahara AIC200 image table update
- add sysfs support
- add coredump support
- add uevents support
- PM support
sun4i:
- layer refactors to decouple plane from output
- improve DE33 support
vc4:
- switch to generic CEC helpers
komeda:
- use drm_ logging functions
vkms:
- configfs support for display configuration
vgem:
- fix fence timer deadlock
etnaviv:
- add HWDB entry for GC8000 Nano Ultra VIP r6205"
* tag 'drm-next-2025-12-03' of https://gitlab.freedesktop.org/drm/kernel: (1869 commits)
Revert "drm/amd: Skip power ungate during suspend for VPE"
drm/amdgpu: use common defines for HUB faults
drm/amdgpu/gmc12: add amdgpu_vm_handle_fault() handling
drm/amdgpu/gmc11: add amdgpu_vm_handle_fault() handling
drm/amdgpu: use static ids for ACP platform devs
drm/amdgpu/sdma6: Update SDMA 6.0.3 FW version to include UMQ protected-fence fix
drm/amdgpu: Forward VMID reservation errors
drm/amdgpu/gmc8: Delegate VM faults to soft IRQ handler ring
drm/amdgpu/gmc7: Delegate VM faults to soft IRQ handler ring
drm/amdgpu/gmc6: Delegate VM faults to soft IRQ handler ring
drm/amdgpu/gmc6: Cache VM fault info
drm/amdgpu/gmc6: Don't print MC client as it's unknown
drm/amdgpu/cz_ih: Enable soft IRQ handler ring
drm/amdgpu/tonga_ih: Enable soft IRQ handler ring
drm/amdgpu/iceland_ih: Enable soft IRQ handler ring
drm/amdgpu/cik_ih: Enable soft IRQ handler ring
drm/amdgpu/si_ih: Enable soft IRQ handler ring
drm/amd/display: fix typo in display_mode_core_structs.h
drm/amd/display: fix Smart Power OLED not working after S4
drm/amd/display: Move RGB-type check for audio sync to DCE HW sequence
...
This commit is contained in:
commit
6dfafbd029
|
|
@ -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'
|
||||
- 'drm_for_each_bridge_in_chain_scoped'
|
||||
- 'drm_for_each_connector_iter'
|
||||
- 'drm_for_each_crtc'
|
||||
- 'drm_for_each_crtc_reverse'
|
||||
|
|
|
|||
1
.mailmap
1
.mailmap
|
|
@ -174,6 +174,7 @@ Carlos Bilbao <carlos.bilbao@kernel.org> <bilbao@vt.edu>
|
|||
Changbin Du <changbin.du@intel.com> <changbin.du@gmail.com>
|
||||
Chao Yu <chao@kernel.org> <chao2.yu@samsung.com>
|
||||
Chao Yu <chao@kernel.org> <yuchao0@huawei.com>
|
||||
Chen-Yu Tsai <wens@kernel.org> <wens@csie.org>
|
||||
Chester Lin <chester62515@gmail.com> <clin@suse.com>
|
||||
Chris Chiu <chris.chiu@canonical.com> <chiu@endlessm.com>
|
||||
Chris Chiu <chris.chiu@canonical.com> <chiu@endlessos.org>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
What: /sys/bus/pci/drivers/qaic/XXXX:XX:XX.X/accel/accel<minor_nr>/dbc<N>_state
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
|
||||
Description: Represents the current state of DMA Bridge channel (DBC). Below are the possible
|
||||
states:
|
||||
|
||||
=================== ==========================================================
|
||||
IDLE (0) DBC is free and can be activated
|
||||
ASSIGNED (1) DBC is activated and a workload is running on device
|
||||
BEFORE_SHUTDOWN (2) Sub-system associated with this workload has crashed and
|
||||
it will shutdown soon
|
||||
AFTER_SHUTDOWN (3) Sub-system associated with this workload has crashed and
|
||||
it has shutdown
|
||||
BEFORE_POWER_UP (4) Sub-system associated with this workload is shutdown and
|
||||
it will be powered up soon
|
||||
AFTER_POWER_UP (5) Sub-system associated with this workload is now powered up
|
||||
=================== ==========================================================
|
||||
Users: Any userspace application or clients interested in DBC state.
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
This directory appears for the particular Intel Xe device when:
|
||||
|
||||
- device supports SR-IOV, and
|
||||
- device is a Physical Function (PF), and
|
||||
- driver support for the SR-IOV PF is enabled on given device.
|
||||
|
||||
This directory is used as a root for all attributes required to
|
||||
manage both Physical Function (PF) and Virtual Functions (VFs).
|
||||
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
This directory holds attributes related to the SR-IOV Physical
|
||||
Function (PF).
|
||||
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf1/
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf2/
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<N>/
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
These directories hold attributes related to the SR-IOV Virtual
|
||||
Functions (VFs).
|
||||
|
||||
Note that the VF number <N> is 1-based as described in PCI SR-IOV
|
||||
specification as the Xe driver follows that naming schema.
|
||||
|
||||
There could be "vf1", "vf2" and so on, up to "vf<N>", where <N>
|
||||
matches the value of the "sriov_totalvfs" attribute.
|
||||
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/profile/exec_quantum_ms
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/profile/preempt_timeout_us
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/profile/sched_priority
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/profile/exec_quantum_ms
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/profile/preempt_timeout_us
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/profile/sched_priority
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
These files expose scheduling parameters for the PF and its VFs, and
|
||||
are visible only on Intel Xe platforms that use time-sliced GPU sharing.
|
||||
They can be changed even if VFs are enabled and running and reflect the
|
||||
settings of all tiles/GTs assigned to the given function.
|
||||
|
||||
exec_quantum_ms: (RW) unsigned integer
|
||||
The GT execution quantum (EQ) in [ms] for the given function.
|
||||
Actual quantum value might be aligned per HW/FW requirements.
|
||||
|
||||
Default is 0 (unlimited).
|
||||
|
||||
preempt_timeout_us: (RW) unsigned integer
|
||||
The GT preemption timeout in [us] of the given function.
|
||||
Actual timeout value might be aligned per HW/FW requirements.
|
||||
|
||||
Default is 0 (unlimited).
|
||||
|
||||
sched_priority: (RW/RO) string
|
||||
The GT scheduling priority of the given function.
|
||||
|
||||
"low" - function will be scheduled on the GPU for its EQ/PT
|
||||
only if function has any work already submitted.
|
||||
|
||||
"normal" - functions will be scheduled on the GPU for its EQ/PT
|
||||
irrespective of whether it has submitted a work or not.
|
||||
|
||||
"high" - function will be scheduled on the GPU for its EQ/PT
|
||||
in the next time-slice after the current one completes
|
||||
and function has a work submitted.
|
||||
|
||||
Default is "low".
|
||||
|
||||
When read, this file will display the current and available
|
||||
scheduling priorities. The currently active priority level will
|
||||
be enclosed in square brackets, like:
|
||||
|
||||
[low] normal high
|
||||
|
||||
This file can be read-only if changing the priority is not
|
||||
supported.
|
||||
|
||||
Writes to these attributes may fail with errors like:
|
||||
-EINVAL if provided input is malformed or not recognized,
|
||||
-EPERM if change is not applicable on given HW/FW,
|
||||
-EIO if FW refuses to change the provisioning.
|
||||
|
||||
Reads from these attributes may fail with:
|
||||
-EUCLEAN if value is not consistent across all tiles/GTs.
|
||||
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/.bulk_profile/exec_quantum_ms
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/.bulk_profile/preempt_timeout_us
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/.bulk_profile/sched_priority
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
These files allows bulk reconfiguration of the scheduling parameters
|
||||
of the PF or VFs and are available only for Intel Xe platforms with
|
||||
GPU sharing based on the time-slice basis. These scheduling parameters
|
||||
can be changed even if VFs are enabled and running.
|
||||
|
||||
exec_quantum_ms: (WO) unsigned integer
|
||||
The GT execution quantum (EQ) in [ms] to be applied to all functions.
|
||||
See sriov_admin/{pf,vf<N>}/profile/exec_quantum_ms for more details.
|
||||
|
||||
preempt_timeout_us: (WO) unsigned integer
|
||||
The GT preemption timeout (PT) in [us] to be applied to all functions.
|
||||
See sriov_admin/{pf,vf<N>}/profile/preempt_timeout_us for more details.
|
||||
|
||||
sched_priority: (RW/RO) string
|
||||
The GT scheduling priority to be applied for all functions.
|
||||
See sriov_admin/{pf,vf<N>}/profile/sched_priority for more details.
|
||||
|
||||
Writes to these attributes may fail with errors like:
|
||||
-EINVAL if provided input is malformed or not recognized,
|
||||
-EPERM if change is not applicable on given HW/FW,
|
||||
-EIO if FW refuses to change the provisioning.
|
||||
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/stop
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
This file allows to control scheduling of the VF on the Intel Xe GPU
|
||||
platforms. It allows to implement custom policy mechanism in case VFs
|
||||
are misbehaving or triggering adverse events above defined thresholds.
|
||||
|
||||
stop: (WO) bool
|
||||
All GT executions of given function shall be immediately stopped.
|
||||
To allow scheduling this VF again, the VF FLR must be triggered.
|
||||
|
||||
Writes to this attribute may fail with errors like:
|
||||
-EINVAL if provided input is malformed or not recognized,
|
||||
-EPERM if change is not applicable on given HW/FW,
|
||||
-EIO if FW refuses to change the scheduling.
|
||||
|
||||
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/pf/device
|
||||
What: /sys/bus/pci/drivers/xe/.../sriov_admin/vf<n>/device
|
||||
Date: October 2025
|
||||
KernelVersion: 6.19
|
||||
Contact: intel-xe@lists.freedesktop.org
|
||||
Description:
|
||||
These are symlinks to the underlying PCI device entry representing
|
||||
given Xe SR-IOV function. For the PF, this link is always present.
|
||||
For VFs, this link is present only for currently enabled VFs.
|
||||
|
|
@ -487,8 +487,8 @@ one user crashes, the fallout of that should be limited to that workload and not
|
|||
impact other workloads. SSR accomplishes this.
|
||||
|
||||
If a particular workload crashes, QSM notifies the host via the QAIC_SSR MHI
|
||||
channel. This notification identifies the workload by it's assigned DBC. A
|
||||
multi-stage recovery process is then used to cleanup both sides, and get the
|
||||
channel. This notification identifies the workload by its assigned DBC. A
|
||||
multi-stage recovery process is then used to cleanup both sides, and gets the
|
||||
DBC/NSPs into a working state.
|
||||
|
||||
When SSR occurs, any state in the workload is lost. Any inputs that were in
|
||||
|
|
@ -496,6 +496,27 @@ process, or queued by not yet serviced, are lost. The loaded artifacts will
|
|||
remain in on-card DDR, but the host will need to re-activate the workload if
|
||||
it desires to recover the workload.
|
||||
|
||||
When SSR occurs for a specific NSP, the assigned DBC goes through the
|
||||
following state transactions in order:
|
||||
|
||||
DBC_STATE_BEFORE_SHUTDOWN
|
||||
Indicates that the affected NSP was found in an unrecoverable error
|
||||
condition.
|
||||
DBC_STATE_AFTER_SHUTDOWN
|
||||
Indicates that the NSP is under reset.
|
||||
DBC_STATE_BEFORE_POWER_UP
|
||||
Indicates that the NSP's debug information has been collected, and is
|
||||
ready to be collected by the host (if desired). At that stage the NSP
|
||||
is restarted by QSM.
|
||||
DBC_STATE_AFTER_POWER_UP
|
||||
Indicates that the NSP has been restarted, fully operational and is
|
||||
in idle state.
|
||||
|
||||
SSR also has an optional crashdump collection feature. If enabled, the host can
|
||||
collect the memory dump for the crashed NSP and dump it to the user space via
|
||||
the dev_coredump subsystem. The host can also decline the crashdump collection
|
||||
request from the device.
|
||||
|
||||
Reliability, Accessibility, Serviceability (RAS)
|
||||
================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ polling mode and reenables the IRQ line.
|
|||
This mitigation in QAIC is very effective. The same lprnet usecase that
|
||||
generates 100k IRQs per second (per /proc/interrupts) is reduced to roughly 64
|
||||
IRQs over 5 minutes while keeping the host system stable, and having the same
|
||||
workload throughput performance (within run to run noise variation).
|
||||
workload throughput performance (within run-to-run noise variation).
|
||||
|
||||
Single MSI Mode
|
||||
---------------
|
||||
|
|
@ -49,7 +49,7 @@ useful to be able to fall back to a single MSI when needed.
|
|||
To support this fallback, we allow the case where only one MSI is able to be
|
||||
allocated, and share that one MSI between MHI and the DBCs. The device detects
|
||||
when only one MSI has been configured and directs the interrupts for the DBCs
|
||||
to the interrupt normally used for MHI. Unfortunately this means that the
|
||||
to the interrupt normally used for MHI. Unfortunately, this means that the
|
||||
interrupt handlers for every DBC and MHI wake up for every interrupt that
|
||||
arrives; however, the DBC threaded irq handlers only are started when work to be
|
||||
done is detected (MHI will always start its threaded handler).
|
||||
|
|
@ -62,9 +62,9 @@ never disabled, allowing each new entry to the FIFO to trigger a new interrupt.
|
|||
Neural Network Control (NNC) Protocol
|
||||
=====================================
|
||||
|
||||
The implementation of NNC is split between the KMD (QAIC) and UMD. In general
|
||||
The implementation of NNC is split between the KMD (QAIC) and UMD. In general,
|
||||
QAIC understands how to encode/decode NNC wire protocol, and elements of the
|
||||
protocol which require kernel space knowledge to process (for example, mapping
|
||||
protocol which requires kernel space knowledge to process (for example, mapping
|
||||
host memory to device IOVAs). QAIC understands the structure of a message, and
|
||||
all of the transactions. QAIC does not understand commands (the payload of a
|
||||
passthrough transaction).
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ properties:
|
|||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: HDMI output port
|
||||
|
||||
port@2:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Parallel audio input port
|
||||
|
||||
required:
|
||||
- port@0
|
||||
- port@1
|
||||
|
|
@ -98,5 +102,13 @@ examples:
|
|||
remote-endpoint = <&hdmi0_con>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
|
||||
endpoint {
|
||||
remote-endpoint = <&pai_to_hdmi_tx>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ properties:
|
|||
compatible:
|
||||
enum:
|
||||
- ite,it66121
|
||||
- ite,it66122
|
||||
- ite,it6610
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ description: |
|
|||
R-Car Gen4 SoCs. The encoder can operate in either DSI or CSI-2 mode, with up
|
||||
to four data lanes.
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/display/dsi-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
|
|
@ -80,14 +83,14 @@ required:
|
|||
- resets
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a779a0-cpg-mssr.h>
|
||||
#include <dt-bindings/power/r8a779a0-sysc.h>
|
||||
|
||||
dsi0: dsi-encoder@fed80000 {
|
||||
dsi@fed80000 {
|
||||
compatible = "renesas,r8a779a0-dsi-csi2-tx";
|
||||
reg = <0xfed80000 0x10000>;
|
||||
power-domains = <&sysc R8A779A0_PD_ALWAYS_ON>;
|
||||
|
|
@ -117,4 +120,51 @@ examples:
|
|||
};
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/clock/r8a779g0-cpg-mssr.h>
|
||||
#include <dt-bindings/power/r8a779g0-sysc.h>
|
||||
|
||||
dsi@fed80000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "renesas,r8a779g0-dsi-csi2-tx";
|
||||
reg = <0xfed80000 0x10000>;
|
||||
clocks = <&cpg CPG_MOD 415>,
|
||||
<&cpg CPG_CORE R8A779G0_CLK_DSIEXT>,
|
||||
<&cpg CPG_CORE R8A779G0_CLK_DSIREF>;
|
||||
clock-names = "fck", "dsi", "pll";
|
||||
power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 415>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
dsi0port1_out: endpoint {
|
||||
remote-endpoint = <&panel_in>;
|
||||
data-lanes = <1 2>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
panel@0 {
|
||||
reg = <0>;
|
||||
compatible = "raspberrypi,dsi-7inch", "ilitek,ili9881c";
|
||||
power-supply = <&vcc_lcd_reg>;
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&dsi0port1_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ properties:
|
|||
- const: adi,adv7123
|
||||
- enum:
|
||||
- adi,adv7123
|
||||
- asl-tek,cs5263
|
||||
- dumb-vga-dac
|
||||
- parade,ps185hdm
|
||||
- radxa,ra620
|
||||
- realtek,rtd2171
|
||||
- ti,opa362
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/imx/fsl,imx8mp-hdmi-pai.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Freescale i.MX8MP HDMI Parallel Audio Interface
|
||||
|
||||
maintainers:
|
||||
- Shengjiu Wang <shengjiu.wang@nxp.com>
|
||||
|
||||
description:
|
||||
The HDMI TX Parallel Audio Interface (HTX_PAI) is a bridge between the
|
||||
Audio Subsystem to the HDMI TX Controller.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: fsl,imx8mp-hdmi-pai
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: apb
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
port:
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
description: Output to the HDMI TX controller.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- port
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/imx8mp-clock.h>
|
||||
#include <dt-bindings/power/imx8mp-power.h>
|
||||
|
||||
audio-bridge@32fc4800 {
|
||||
compatible = "fsl,imx8mp-hdmi-pai";
|
||||
reg = <0x32fc4800 0x800>;
|
||||
interrupt-parent = <&irqsteer_hdmi>;
|
||||
interrupts = <14>;
|
||||
clocks = <&clk IMX8MP_CLK_HDMI_APB>;
|
||||
clock-names = "apb";
|
||||
power-domains = <&hdmi_blk_ctrl IMX8MP_HDMIBLK_PD_PAI>;
|
||||
|
||||
port {
|
||||
pai_to_hdmi_tx: endpoint {
|
||||
remote-endpoint = <&hdmi_tx_from_pai>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -18,6 +18,7 @@ properties:
|
|||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,glymur-dp
|
||||
- qcom,sa8775p-dp
|
||||
- qcom,sc7180-dp
|
||||
- qcom,sc7280-dp
|
||||
|
|
@ -31,6 +32,11 @@ properties:
|
|||
- qcom,sm8650-dp
|
||||
- qcom,x1e80100-dp
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,qcs8300-dp
|
||||
- const: qcom,sa8775p-dp
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm6350-dp
|
||||
|
|
@ -53,6 +59,12 @@ properties:
|
|||
- qcom,sm8550-dp
|
||||
- const: qcom,sm8350-dp
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm6150-dp
|
||||
- const: qcom,sm8150-dp
|
||||
- const: qcom,sm8350-dp
|
||||
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,sm8750-dp
|
||||
|
|
@ -195,9 +207,11 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,glymur-dp
|
||||
- qcom,sa8775p-dp
|
||||
- qcom,x1e80100-dp
|
||||
then:
|
||||
$ref: /schemas/sound/dai-common.yaml#
|
||||
oneOf:
|
||||
- required:
|
||||
- aux-bus
|
||||
|
|
@ -239,6 +253,7 @@ allOf:
|
|||
enum:
|
||||
# these platforms support 2 streams MST on some interfaces,
|
||||
# others are SST only
|
||||
- qcom,glymur-dp
|
||||
- qcom,sc8280xp-dp
|
||||
- qcom,x1e80100-dp
|
||||
then:
|
||||
|
|
@ -295,7 +310,7 @@ allOf:
|
|||
minItems: 6
|
||||
maxItems: 8
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ properties:
|
|||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- pattern: '^qcom,adreno-gmu-[67][0-9][0-9]\.[0-9]$'
|
||||
- pattern: '^qcom,adreno-gmu-[6-8][0-9][0-9]\.[0-9]$'
|
||||
- const: qcom,adreno-gmu
|
||||
- items:
|
||||
- pattern: '^qcom,adreno-gmu-x[1-9][0-9][0-9]\.[0-9]$'
|
||||
|
|
@ -299,6 +299,64 @@ allOf:
|
|||
required:
|
||||
- qcom,qmp
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,adreno-gmu-840.1
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Core GMU registers
|
||||
reg-names:
|
||||
items:
|
||||
- const: gmu
|
||||
clocks:
|
||||
items:
|
||||
- description: GPU AHB clock
|
||||
- description: GMU clock
|
||||
- description: GPU CX clock
|
||||
- description: GPU MEMNOC clock
|
||||
- description: GMU HUB clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: ahb
|
||||
- const: gmu
|
||||
- const: cxo
|
||||
- const: memnoc
|
||||
- const: hub
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,adreno-gmu-x285.1
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Core GMU registers
|
||||
reg-names:
|
||||
items:
|
||||
- const: gmu
|
||||
clocks:
|
||||
items:
|
||||
- description: GPU AHB clock
|
||||
- description: GMU clock
|
||||
- description: GPU CX clock
|
||||
- description: GPU MEMNOC clock
|
||||
- description: GMU HUB clock
|
||||
- description: GMU RSCC HUB clock
|
||||
clock-names:
|
||||
items:
|
||||
- const: ahb
|
||||
- const: gmu
|
||||
- const: cxo
|
||||
- const: memnoc
|
||||
- const: hub
|
||||
- const: rscc
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,264 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/msm/qcom,glymur-mdss.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Glymur Display MDSS
|
||||
|
||||
maintainers:
|
||||
- Abel Vesa <abel.vesa@linaro.org>
|
||||
|
||||
description:
|
||||
Glymur MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
|
||||
DPU display controller, DP interfaces, etc.
|
||||
|
||||
$ref: /schemas/display/msm/mdss-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,glymur-mdss
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Display AHB
|
||||
- description: Display hf AXI
|
||||
- description: Display core
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
interconnects:
|
||||
items:
|
||||
- description: Interconnect path from mdp0 port to the data bus
|
||||
- description: Interconnect path from CPU to the reg bus
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: mdp0-mem
|
||||
- const: cpu-cfg
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,glymur-dpu
|
||||
|
||||
"^displayport-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,glymur-dp
|
||||
|
||||
"^phy@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,glymur-dp-phy
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/interconnect/qcom,icc.h>
|
||||
#include <dt-bindings/interconnect/qcom,glymur-rpmh.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/phy/phy-qcom-qmp.h>
|
||||
#include <dt-bindings/power/qcom,rpmhpd.h>
|
||||
|
||||
display-subsystem@ae00000 {
|
||||
compatible = "qcom,glymur-mdss";
|
||||
reg = <0x0ae00000 0x1000>;
|
||||
reg-names = "mdss";
|
||||
|
||||
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
clocks = <&dispcc_ahb_clk>,
|
||||
<&gcc_disp_hf_axi_clk>,
|
||||
<&dispcc_mdp_clk>;
|
||||
clock-names = "bus", "nrt_bus", "core";
|
||||
|
||||
interconnects = <&mmss_noc MASTER_MDP QCOM_ICC_TAG_ALWAYS
|
||||
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>,
|
||||
<&hsc_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
|
||||
&config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
|
||||
interconnect-names = "mdp0-mem",
|
||||
"cpu-cfg";
|
||||
|
||||
resets = <&disp_cc_mdss_core_bcr>;
|
||||
|
||||
power-domains = <&mdss_gdsc>;
|
||||
|
||||
iommus = <&apps_smmu 0x1c00 0x2>;
|
||||
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
display-controller@ae01000 {
|
||||
compatible = "qcom,glymur-dpu";
|
||||
reg = <0x0ae01000 0x8f000>,
|
||||
<0x0aeb0000 0x2008>;
|
||||
reg-names = "mdp", "vbif";
|
||||
|
||||
clocks = <&gcc_axi_clk>,
|
||||
<&dispcc_ahb_clk>,
|
||||
<&dispcc_mdp_lut_clk>,
|
||||
<&dispcc_mdp_clk>,
|
||||
<&dispcc_mdp_vsync_clk>;
|
||||
clock-names = "nrt_bus",
|
||||
"iface",
|
||||
"lut",
|
||||
"core",
|
||||
"vsync";
|
||||
|
||||
assigned-clocks = <&dispcc_mdp_vsync_clk>;
|
||||
assigned-clock-rates = <19200000>;
|
||||
|
||||
operating-points-v2 = <&mdp_opp_table>;
|
||||
power-domains = <&rpmhpd RPMHPD_MMCX>;
|
||||
|
||||
interrupt-parent = <&mdss>;
|
||||
interrupts = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
dpu_intf1_out: endpoint {
|
||||
remote-endpoint = <&dsi0_in>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dpu_intf2_out: endpoint {
|
||||
remote-endpoint = <&dsi1_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdp_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-200000000 {
|
||||
opp-hz = /bits/ 64 <200000000>;
|
||||
required-opps = <&rpmhpd_opp_low_svs>;
|
||||
};
|
||||
|
||||
opp-325000000 {
|
||||
opp-hz = /bits/ 64 <325000000>;
|
||||
required-opps = <&rpmhpd_opp_svs>;
|
||||
};
|
||||
|
||||
opp-375000000 {
|
||||
opp-hz = /bits/ 64 <375000000>;
|
||||
required-opps = <&rpmhpd_opp_svs_l1>;
|
||||
};
|
||||
|
||||
opp-514000000 {
|
||||
opp-hz = /bits/ 64 <514000000>;
|
||||
required-opps = <&rpmhpd_opp_nom>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
displayport-controller@ae90000 {
|
||||
compatible = "qcom,glymur-dp";
|
||||
reg = <0xae90000 0x200>,
|
||||
<0xae90200 0x200>,
|
||||
<0xae90400 0x600>,
|
||||
<0xae91000 0x400>,
|
||||
<0xae91400 0x400>;
|
||||
|
||||
interrupt-parent = <&mdss>;
|
||||
interrupts = <12>;
|
||||
|
||||
clocks = <&dispcc_mdss_ahb_clk>,
|
||||
<&dispcc_dptx0_aux_clk>,
|
||||
<&dispcc_dptx0_link_clk>,
|
||||
<&dispcc_dptx0_link_intf_clk>,
|
||||
<&dispcc_dptx0_pixel0_clk>,
|
||||
<&dispcc_dptx0_pixel1_clk>;
|
||||
clock-names = "core_iface",
|
||||
"core_aux",
|
||||
"ctrl_link",
|
||||
"ctrl_link_iface",
|
||||
"stream_pixel",
|
||||
"stream_1_pixel";
|
||||
|
||||
assigned-clocks = <&dispcc_mdss_dptx0_link_clk_src>,
|
||||
<&dispcc_mdss_dptx0_pixel0_clk_src>,
|
||||
<&dispcc_mdss_dptx0_pixel1_clk_src>;
|
||||
assigned-clock-parents = <&usb_1_ss0_qmpphy QMP_USB43DP_DP_LINK_CLK>,
|
||||
<&usb_1_ss0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>,
|
||||
<&usb_1_ss0_qmpphy QMP_USB43DP_DP_VCO_DIV_CLK>;
|
||||
|
||||
operating-points-v2 = <&mdss_dp0_opp_table>;
|
||||
|
||||
power-domains = <&rpmhpd RPMHPD_MMCX>;
|
||||
|
||||
phys = <&usb_1_ss0_qmpphy QMP_USB43DP_DP_PHY>;
|
||||
phy-names = "dp";
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
mdss_dp0_in: endpoint {
|
||||
remote-endpoint = <&mdss_intf0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
mdss_dp0_out: endpoint {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdss_dp0_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-160000000 {
|
||||
opp-hz = /bits/ 64 <160000000>;
|
||||
required-opps = <&rpmhpd_opp_low_svs>;
|
||||
};
|
||||
|
||||
opp-270000000 {
|
||||
opp-hz = /bits/ 64 <270000000>;
|
||||
required-opps = <&rpmhpd_opp_svs>;
|
||||
};
|
||||
|
||||
opp-540000000 {
|
||||
opp-hz = /bits/ 64 <540000000>;
|
||||
required-opps = <&rpmhpd_opp_svs_l1>;
|
||||
};
|
||||
|
||||
opp-810000000 {
|
||||
opp-hz = /bits/ 64 <810000000>;
|
||||
required-opps = <&rpmhpd_opp_nom>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/msm/qcom,qcs8300-mdss.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm Technologies, Inc. QCS8300 Display MDSS
|
||||
|
||||
maintainers:
|
||||
- Yongxing Mou <yongxing.mou@oss.qualcomm.com>
|
||||
|
||||
description:
|
||||
QCS8300 MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
|
||||
DPU display controller, DP interfaces and EDP etc.
|
||||
|
||||
$ref: /schemas/display/msm/mdss-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,qcs8300-mdss
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: Display AHB
|
||||
- description: Display hf AXI
|
||||
- description: Display core
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
interconnects:
|
||||
maxItems: 3
|
||||
|
||||
interconnect-names:
|
||||
maxItems: 3
|
||||
|
||||
patternProperties:
|
||||
"^display-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,qcs8300-dpu
|
||||
|
||||
"^displayport-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,qcs8300-dp
|
||||
|
||||
"^phy@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,qcs8300-edp-phy
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interconnect/qcom,icc.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/qcom,qcs8300-gcc.h>
|
||||
#include <dt-bindings/clock/qcom,sa8775p-dispcc.h>
|
||||
#include <dt-bindings/interconnect/qcom,qcs8300-rpmh.h>
|
||||
#include <dt-bindings/power/qcom,rpmhpd.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
mdss: display-subsystem@ae00000 {
|
||||
compatible = "qcom,qcs8300-mdss";
|
||||
reg = <0x0ae00000 0x1000>;
|
||||
reg-names = "mdss";
|
||||
|
||||
interconnects = <&mmss_noc MASTER_MDP0 QCOM_ICC_TAG_ACTIVE_ONLY
|
||||
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>,
|
||||
<&mmss_noc MASTER_MDP1 QCOM_ICC_TAG_ACTIVE_ONLY
|
||||
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>,
|
||||
<&gem_noc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY
|
||||
&config_noc SLAVE_DISPLAY_CFG QCOM_ICC_TAG_ACTIVE_ONLY>;
|
||||
interconnect-names = "mdp0-mem",
|
||||
"mdp1-mem",
|
||||
"cpu-cfg";
|
||||
|
||||
resets = <&dispcc_core_bcr>;
|
||||
power-domains = <&dispcc_gdsc>;
|
||||
|
||||
clocks = <&dispcc_ahb_clk>,
|
||||
<&gcc GCC_DISP_HF_AXI_CLK>,
|
||||
<&dispcc_mdp_clk>;
|
||||
|
||||
interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
|
||||
iommus = <&apps_smmu 0x1000 0x402>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
ranges;
|
||||
|
||||
display-controller@ae01000 {
|
||||
compatible = "qcom,qcs8300-dpu", "qcom,sa8775p-dpu";
|
||||
reg = <0x0ae01000 0x8f000>,
|
||||
<0x0aeb0000 0x2008>;
|
||||
reg-names = "mdp", "vbif";
|
||||
|
||||
clocks = <&gcc GCC_DISP_HF_AXI_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_MDP_LUT_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_MDP_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_VSYNC_CLK>;
|
||||
clock-names = "nrt_bus",
|
||||
"iface",
|
||||
"lut",
|
||||
"core",
|
||||
"vsync";
|
||||
|
||||
assigned-clocks = <&dispcc0 MDSS_DISP_CC_MDSS_VSYNC_CLK>;
|
||||
assigned-clock-rates = <19200000>;
|
||||
operating-points-v2 = <&mdp_opp_table>;
|
||||
power-domains = <&rpmhpd RPMHPD_MMCX>;
|
||||
|
||||
interrupt-parent = <&mdss>;
|
||||
interrupts = <0>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
dpu_intf0_out: endpoint {
|
||||
remote-endpoint = <&mdss_dp0_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdp_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-375000000 {
|
||||
opp-hz = /bits/ 64 <375000000>;
|
||||
required-opps = <&rpmhpd_opp_svs_l1>;
|
||||
};
|
||||
|
||||
opp-500000000 {
|
||||
opp-hz = /bits/ 64 <500000000>;
|
||||
required-opps = <&rpmhpd_opp_nom>;
|
||||
};
|
||||
|
||||
opp-575000000 {
|
||||
opp-hz = /bits/ 64 <575000000>;
|
||||
required-opps = <&rpmhpd_opp_turbo>;
|
||||
};
|
||||
|
||||
opp-650000000 {
|
||||
opp-hz = /bits/ 64 <650000000>;
|
||||
required-opps = <&rpmhpd_opp_turbo_l1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdss_dp0_phy: phy@aec2a00 {
|
||||
compatible = "qcom,qcs8300-edp-phy", "qcom,sa8775p-edp-phy";
|
||||
|
||||
reg = <0x0aec2a00 0x200>,
|
||||
<0x0aec2200 0xd0>,
|
||||
<0x0aec2600 0xd0>,
|
||||
<0x0aec2000 0x1c8>;
|
||||
|
||||
clocks = <&dispcc MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>,
|
||||
<&dispcc MDSS_DISP_CC_MDSS_AHB_CLK>;
|
||||
clock-names = "aux",
|
||||
"cfg_ahb";
|
||||
|
||||
#clock-cells = <1>;
|
||||
#phy-cells = <0>;
|
||||
|
||||
vdda-phy-supply = <&vreg_l1c>;
|
||||
vdda-pll-supply = <&vreg_l4a>;
|
||||
};
|
||||
|
||||
displayport-controller@af54000 {
|
||||
compatible = "qcom,qcs8300-dp", "qcom,sa8775p-dp";
|
||||
|
||||
pinctrl-0 = <&dp_hot_plug_det>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
reg = <0xaf54000 0x104>,
|
||||
<0xaf54200 0x0c0>,
|
||||
<0xaf55000 0x770>,
|
||||
<0xaf56000 0x09c>,
|
||||
<0xaf57000 0x09c>,
|
||||
<0xaf58000 0x09c>,
|
||||
<0xaf59000 0x09c>,
|
||||
<0xaf5a000 0x23c>,
|
||||
<0xaf5b000 0x23c>;
|
||||
|
||||
interrupt-parent = <&mdss>;
|
||||
interrupts = <12>;
|
||||
clocks = <&dispcc0 MDSS_DISP_CC_MDSS_AHB_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_LINK_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_LINK_INTF_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL0_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL1_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL2_CLK>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL3_CLK>;
|
||||
clock-names = "core_iface",
|
||||
"core_aux",
|
||||
"ctrl_link",
|
||||
"ctrl_link_iface",
|
||||
"stream_pixel",
|
||||
"stream_1_pixel",
|
||||
"stream_2_pixel",
|
||||
"stream_3_pixel";
|
||||
assigned-clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_LINK_CLK_SRC>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL0_CLK_SRC>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL1_CLK_SRC>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL2_CLK_SRC>,
|
||||
<&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_PIXEL3_CLK_SRC>;
|
||||
assigned-clock-parents = <&mdss_dp0_phy 0>,
|
||||
<&mdss_dp0_phy 1>,
|
||||
<&mdss_dp0_phy 1>,
|
||||
<&mdss_dp0_phy 1>;
|
||||
phys = <&mdss_dp0_phy>;
|
||||
phy-names = "dp";
|
||||
operating-points-v2 = <&dp_opp_table>;
|
||||
power-domains = <&rpmhpd RPMHPD_MMCX>;
|
||||
|
||||
#sound-dai-cells = <0>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
|
||||
mdss_dp0_in: endpoint {
|
||||
remote-endpoint = <&dpu_intf0_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
|
||||
mdss_dp_out: endpoint { };
|
||||
};
|
||||
};
|
||||
|
||||
dp_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-160000000 {
|
||||
opp-hz = /bits/ 64 <160000000>;
|
||||
required-opps = <&rpmhpd_opp_low_svs>;
|
||||
};
|
||||
|
||||
opp-270000000 {
|
||||
opp-hz = /bits/ 64 <270000000>;
|
||||
required-opps = <&rpmhpd_opp_svs>;
|
||||
};
|
||||
|
||||
opp-540000000 {
|
||||
opp-hz = /bits/ 64 <540000000>;
|
||||
required-opps = <&rpmhpd_opp_svs_l1>;
|
||||
};
|
||||
|
||||
opp-810000000 {
|
||||
opp-hz = /bits/ 64 <810000000>;
|
||||
required-opps = <&rpmhpd_opp_nom>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -51,6 +51,14 @@ patternProperties:
|
|||
compatible:
|
||||
const: qcom,sm6150-dpu
|
||||
|
||||
"^displayport-controller@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,sm6150-dp
|
||||
|
||||
"^dsi@[0-9a-f]+$":
|
||||
type: object
|
||||
additionalProperties: true
|
||||
|
|
@ -130,35 +138,37 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
dpu_intf0_out: endpoint {
|
||||
};
|
||||
reg = <0>;
|
||||
|
||||
dpu_intf0_out: endpoint {
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
dpu_intf1_out: endpoint {
|
||||
remote-endpoint = <&mdss_dsi0_in>;
|
||||
};
|
||||
reg = <1>;
|
||||
|
||||
dpu_intf1_out: endpoint {
|
||||
remote-endpoint = <&mdss_dsi0_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
mdp_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-19200000 {
|
||||
opp-hz = /bits/ 64 <19200000>;
|
||||
required-opps = <&rpmhpd_opp_low_svs>;
|
||||
opp-192000000 {
|
||||
opp-hz = /bits/ 64 <192000000>;
|
||||
required-opps = <&rpmhpd_opp_low_svs>;
|
||||
};
|
||||
|
||||
opp-25600000 {
|
||||
opp-hz = /bits/ 64 <25600000>;
|
||||
required-opps = <&rpmhpd_opp_svs>;
|
||||
opp-256000000 {
|
||||
opp-hz = /bits/ 64 <256000000>;
|
||||
required-opps = <&rpmhpd_opp_svs>;
|
||||
};
|
||||
|
||||
opp-307200000 {
|
||||
opp-hz = /bits/ 64 <307200000>;
|
||||
required-opps = <&rpmhpd_opp_nom>;
|
||||
opp-hz = /bits/ 64 <307200000>;
|
||||
required-opps = <&rpmhpd_opp_nom>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,11 +13,17 @@ $ref: /schemas/display/msm/dpu-common.yaml#
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sa8775p-dpu
|
||||
- qcom,sm8650-dpu
|
||||
- qcom,sm8750-dpu
|
||||
- qcom,x1e80100-dpu
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,glymur-dpu
|
||||
- qcom,sa8775p-dpu
|
||||
- qcom,sm8650-dpu
|
||||
- qcom,sm8750-dpu
|
||||
- qcom,x1e80100-dpu
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,qcs8300-dpu
|
||||
- const: qcom,sa8775p-dpu
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,68 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/ilitek,il79900a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ilitek IL79900a based MIPI-DSI panels
|
||||
|
||||
maintainers:
|
||||
- Langyan Ye <yelangyan@huaqin.corp-partner.google.com>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- tianma,tl121bvms07-00
|
||||
- const: ilitek,il79900a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: DSI virtual channel used by the panel
|
||||
|
||||
enable-gpios:
|
||||
maxItems: 1
|
||||
description: GPIO specifier for the enable pin
|
||||
|
||||
avdd-supply:
|
||||
description: Positive analog voltage supply (AVDD)
|
||||
|
||||
avee-supply:
|
||||
description: Negative analog voltage supply (AVEE)
|
||||
|
||||
pp1800-supply:
|
||||
description: 1.8V logic voltage supply
|
||||
|
||||
backlight: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- enable-gpios
|
||||
- avdd-supply
|
||||
- avee-supply
|
||||
- pp1800-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "tianma,tl121bvms07-00", "ilitek,il79900a";
|
||||
reg = <0>;
|
||||
enable-gpios = <&pio 25 0>;
|
||||
avdd-supply = <®_avdd>;
|
||||
avee-supply = <®_avee>;
|
||||
pp1800-supply = <®_pp1800>;
|
||||
backlight = <&backlight>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -20,9 +20,11 @@ properties:
|
|||
- bananapi,lhr050h41
|
||||
- bestar,bsd1218-a101kl68
|
||||
- feixin,k101-im2byl02
|
||||
- raspberrypi,dsi-5inch
|
||||
- raspberrypi,dsi-7inch
|
||||
- startek,kd050hdfia020
|
||||
- tdo,tl050hdv35
|
||||
- wanchanglong,w552946aaa
|
||||
- wanchanglong,w552946aba
|
||||
- const: ilitek,ili9881c
|
||||
|
||||
|
|
@ -30,6 +32,7 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
backlight: true
|
||||
port: true
|
||||
power-supply: true
|
||||
reset-gpios: true
|
||||
rotation: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/lg,ld070wx3-sl01.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LG Corporation 7" WXGA TFT LCD panel
|
||||
|
||||
maintainers:
|
||||
- Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: lg,ld070wx3-sl01
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vcc-supply: true
|
||||
|
||||
backlight: true
|
||||
port: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- vdd-supply
|
||||
- vcc-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "lg,ld070wx3-sl01";
|
||||
reg = <0>;
|
||||
|
||||
vdd-supply = <&vdd_3v3_lcd>;
|
||||
vcc-supply = <&vcc_1v8_lcd>;
|
||||
|
||||
backlight = <&backlight>;
|
||||
|
||||
port {
|
||||
endpoint {
|
||||
remote-endpoint = <&dsi0_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -59,6 +59,8 @@ properties:
|
|||
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
|
||||
- jenson,bl-jt60050-01a
|
||||
- tbs,a711-panel
|
||||
# Winstar WF70A8SYJHLNGA 7" WSVGA (1024x600) color TFT LCD LVDS panel
|
||||
- winstar,wf70a8syjhlnga
|
||||
|
||||
- const: panel-lvds
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ description: |
|
|||
|
||||
If the panel is more advanced a dedicated binding file is required.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
|
||||
compatible:
|
||||
|
|
@ -42,8 +45,6 @@ properties:
|
|||
- kingdisplay,kd097d04
|
||||
# LG ACX467AKM-7 4.95" 1080×1920 LCD Panel
|
||||
- lg,acx467akm-7
|
||||
# LG Corporation 7" WXGA TFT LCD panel
|
||||
- lg,ld070wx3-sl01
|
||||
# LG Corporation 5" HD TFT LCD panel
|
||||
- lg,lh500wx1-sd03
|
||||
# Lincoln LCD197 5" 1080x1920 LCD panel
|
||||
|
|
@ -56,10 +57,6 @@ properties:
|
|||
- panasonic,vvx10f034n00
|
||||
# Samsung s6e3fa7 1080x2220 based AMS559NK06 AMOLED panel
|
||||
- samsung,s6e3fa7-ams559nk06
|
||||
# Samsung s6e3fc2x01 1080x2340 AMOLED panel
|
||||
- samsung,s6e3fc2x01
|
||||
# Samsung sofef00 1080x2280 AMOLED panel
|
||||
- samsung,sofef00
|
||||
# Shangai Top Display Optoelectronics 7" TL070WSH30 1024x600 TFT LCD panel
|
||||
- tdo,tl070wsh30
|
||||
|
||||
|
|
@ -72,31 +69,12 @@ properties:
|
|||
reset-gpios: true
|
||||
port: true
|
||||
power-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,s6e3fc2x01
|
||||
- samsung,sofef00
|
||||
then:
|
||||
properties:
|
||||
power-supply: false
|
||||
required:
|
||||
- vddio-supply
|
||||
else:
|
||||
properties:
|
||||
vddio-supply: false
|
||||
required:
|
||||
- power-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- power-supply
|
||||
- reg
|
||||
|
||||
examples:
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ properties:
|
|||
- innolux,n156bge-l21
|
||||
# Innolux Corporation 7.0" WSVGA (1024x600) TFT LCD panel
|
||||
- innolux,zj070na-01p
|
||||
# JuTouch Technology Co.. 10" JT101TM023 WXGA (1280 x 800) LVDS panel
|
||||
- jutouch,jt101tm023
|
||||
# Kaohsiung Opto-Electronics Inc. 5.7" QVGA (320 x 240) TFT LCD panel
|
||||
- koe,tx14d24vm1bpa
|
||||
# Kaohsiung Opto-Electronics. TX31D200VM0BAA 12.3" HSXGA LVDS panel
|
||||
|
|
@ -268,6 +270,8 @@ properties:
|
|||
- qiaodian,qd43003c0-40
|
||||
# Shenzhen QiShenglong Industrialist Co., Ltd. Gopher 2b 4.3" 480(RGB)x272 TFT LCD panel
|
||||
- qishenglong,gopher2b-lcd
|
||||
# Raystar Optronics, Inc. RFF500F-AWH-DNN 5.0" TFT 840x480
|
||||
- raystar,rff500f-awh-dnn
|
||||
# Rocktech Displays Ltd. RK101II01D-CT 10.1" TFT 1280x800
|
||||
- rocktech,rk101ii01d-ct
|
||||
# Rocktech Display Ltd. RK070ER9427 800(RGB)x480 TFT LCD panel
|
||||
|
|
@ -276,6 +280,8 @@ properties:
|
|||
- rocktech,rk043fn48h
|
||||
# Samsung Electronics 10.1" WXGA (1280x800) TFT LCD panel
|
||||
- samsung,ltl101al01
|
||||
# Samsung Electronics 10.6" FWXGA (1366x768) TFT LCD panel
|
||||
- samsung,ltl106al01
|
||||
# Samsung Electronics 10.1" WSVGA TFT LCD panel
|
||||
- samsung,ltn101nt05
|
||||
# Satoz SAT050AT40H12R2 5.0" WVGA TFT LCD panel
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ title: Ronbo RB070D30 DSI Display Panel
|
|||
maintainers:
|
||||
- Maxime Ripard <mripard@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ronbo,rb070d30
|
||||
|
|
@ -20,10 +23,6 @@ properties:
|
|||
description: GPIO used for the power pin
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO used for the reset pin
|
||||
maxItems: 1
|
||||
|
||||
shlr-gpios:
|
||||
description: GPIO used for the shlr pin (horizontal flip)
|
||||
maxItems: 1
|
||||
|
|
@ -35,10 +34,6 @@ properties:
|
|||
vcc-lcd-supply:
|
||||
description: Power regulator
|
||||
|
||||
backlight:
|
||||
description: Backlight used by the panel
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- power-gpios
|
||||
|
|
@ -47,5 +42,6 @@ required:
|
|||
- shlr-gpios
|
||||
- updn-gpios
|
||||
- vcc-lcd-supply
|
||||
- port
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ properties:
|
|||
- samsung,atna45dc02
|
||||
# Samsung 15.6" 3K (2880x1620 pixels) eDP AMOLED panel
|
||||
- samsung,atna56ac03
|
||||
# Samsung 16.0" 3K (2880x1800 pixels) eDP AMOLED panel
|
||||
- samsung,atna60cl08
|
||||
- const: samsung,atna33xc20
|
||||
|
||||
enable-gpios: true
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/samsung,s6e3fc2x01.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung S6E3FC2X01 AMOLED DDIC
|
||||
|
||||
description: The S6E3FC2X01 is display driver IC with connected panel.
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
# Samsung 6.41 inch, 1080x2340 pixels, 19.5:9 ratio
|
||||
- samsung,s6e3fc2x01-ams641rw
|
||||
- const: samsung,s6e3fc2x01
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios: true
|
||||
|
||||
port: true
|
||||
|
||||
vddio-supply:
|
||||
description: VDD regulator
|
||||
|
||||
vci-supply:
|
||||
description: VCI regulator
|
||||
|
||||
poc-supply:
|
||||
description: POC regulator
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reset-gpios
|
||||
- vddio-supply
|
||||
- vci-supply
|
||||
- poc-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "samsung,s6e3fc2x01-ams641rw", "samsung,s6e3fc2x01";
|
||||
reg = <0>;
|
||||
|
||||
vddio-supply = <&vreg_l14a_1p88>;
|
||||
vci-supply = <&s2dos05_buck1>;
|
||||
poc-supply = <&s2dos05_ldo1>;
|
||||
|
||||
te-gpios = <&tlmm 10 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
pinctrl-0 = <&sde_dsi_active &sde_te_active_sleep>;
|
||||
pinctrl-1 = <&sde_dsi_suspend &sde_te_active_sleep>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&mdss_dsi0_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/samsung,sofef00.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung SOFEF00 AMOLED DDIC
|
||||
|
||||
description: The SOFEF00 is display driver IC with connected panel.
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
# Samsung 6.01 inch, 1080x2160 pixels, 18:9 ratio
|
||||
- samsung,sofef00-ams601nt22
|
||||
# Samsung 6.28 inch, 1080x2280 pixels, 19:9 ratio
|
||||
- samsung,sofef00-ams628nw01
|
||||
- const: samsung,sofef00
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
poc-supply:
|
||||
description: POC regulator
|
||||
|
||||
vci-supply:
|
||||
description: VCI regulator
|
||||
|
||||
vddio-supply:
|
||||
description: VDD regulator
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reset-gpios
|
||||
- poc-supply
|
||||
- vci-supply
|
||||
- vddio-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "samsung,sofef00-ams628nw01", "samsung,sofef00";
|
||||
reg = <0>;
|
||||
|
||||
vddio-supply = <&vreg_l14a_1p88>;
|
||||
vci-supply = <&s2dos05_buck1>;
|
||||
poc-supply = <&s2dos05_ldo1>;
|
||||
|
||||
te-gpios = <&tlmm 10 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
pinctrl-0 = <&panel_active>;
|
||||
pinctrl-1 = <&panel_suspend>;
|
||||
pinctrl-names = "default", "sleep";
|
||||
|
||||
port {
|
||||
panel_in: endpoint {
|
||||
remote-endpoint = <&mdss_dsi0_out>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/sharp,lq079l1sx01.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sharp Microelectronics 7.9" WQXGA TFT LCD panel
|
||||
|
||||
maintainers:
|
||||
- Svyatoslav Ryhel <clamor95@gmail.com>
|
||||
|
||||
description: >
|
||||
This panel requires a dual-channel DSI host to operate and it supports
|
||||
only left-right split mode, where each channel drives the left or right
|
||||
half of the screen and only video mode.
|
||||
|
||||
Each of the DSI channels controls a separate DSI peripheral.
|
||||
The peripheral driven by the first link (DSI-LINK1), left one, is
|
||||
considered the primary peripheral and controls the device.
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common-dual.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sharp,lq079l1sx01
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description: regulator that supplies the analog voltage
|
||||
|
||||
vddio-supply:
|
||||
description: regulator that supplies the I/O voltage
|
||||
|
||||
vsp-supply:
|
||||
description: positive boost supply regulator
|
||||
|
||||
vsn-supply:
|
||||
description: negative boost supply regulator
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
backlight: true
|
||||
ports: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
- vddio-supply
|
||||
- ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "sharp,lq079l1sx01";
|
||||
reg = <0>;
|
||||
|
||||
reset-gpios = <&gpio 59 GPIO_ACTIVE_LOW>;
|
||||
|
||||
avdd-supply = <&avdd_lcd>;
|
||||
vddio-supply = <&vdd_lcd_io>;
|
||||
vsp-supply = <&vsp_5v5_lcd>;
|
||||
vsn-supply = <&vsn_5v5_lcd>;
|
||||
|
||||
backlight = <&backlight>;
|
||||
|
||||
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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/display/panel/synaptics,td4300-panel.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Synaptics TDDI Display Panel Controller
|
||||
|
||||
maintainers:
|
||||
- Kaustabh Chakraborty <kauschluss@disroot.org>
|
||||
|
||||
allOf:
|
||||
- $ref: panel-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- syna,td4101-panel
|
||||
- syna,td4300-panel
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vio-supply:
|
||||
description: core I/O voltage supply
|
||||
|
||||
vsn-supply:
|
||||
description: negative voltage supply for analog circuits
|
||||
|
||||
vsp-supply:
|
||||
description: positive voltage supply for analog circuits
|
||||
|
||||
backlight-gpios:
|
||||
maxItems: 1
|
||||
description: backlight enable GPIO
|
||||
|
||||
reset-gpios: true
|
||||
width-mm: true
|
||||
height-mm: true
|
||||
panel-timing: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- width-mm
|
||||
- height-mm
|
||||
- panel-timing
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
dsi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
panel@0 {
|
||||
compatible = "syna,td4300-panel";
|
||||
reg = <0>;
|
||||
|
||||
vio-supply = <&panel_vio_reg>;
|
||||
vsn-supply = <&panel_vsn_reg>;
|
||||
vsp-supply = <&panel_vsp_reg>;
|
||||
|
||||
backlight-gpios = <&gpd3 5 GPIO_ACTIVE_LOW>;
|
||||
reset-gpios = <&gpd3 4 GPIO_ACTIVE_LOW>;
|
||||
|
||||
width-mm = <68>;
|
||||
height-mm = <121>;
|
||||
|
||||
panel-timing {
|
||||
clock-frequency = <144389520>;
|
||||
|
||||
hactive = <1080>;
|
||||
hsync-len = <4>;
|
||||
hfront-porch = <120>;
|
||||
hback-porch = <32>;
|
||||
|
||||
vactive = <1920>;
|
||||
vsync-len = <2>;
|
||||
vfront-porch = <21>;
|
||||
vback-porch = <4>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
@ -25,6 +25,9 @@ properties:
|
|||
- enum:
|
||||
- renesas,r9a07g054-du # RZ/V2L
|
||||
- const: renesas,r9a07g044-du # RZ/G2L fallback
|
||||
- items:
|
||||
- const: renesas,r9a09g056-du # RZ/V2N
|
||||
- const: renesas,r9a09g057-du # RZ/V2H(P) fallback
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ properties:
|
|||
- rockchip,px30-mipi-dsi
|
||||
- rockchip,rk3128-mipi-dsi
|
||||
- rockchip,rk3288-mipi-dsi
|
||||
- rockchip,rk3368-mipi-dsi
|
||||
- rockchip,rk3399-mipi-dsi
|
||||
- rockchip,rk3568-mipi-dsi
|
||||
- rockchip,rv1126-mipi-dsi
|
||||
|
|
@ -73,6 +74,7 @@ allOf:
|
|||
enum:
|
||||
- rockchip,px30-mipi-dsi
|
||||
- rockchip,rk3128-mipi-dsi
|
||||
- rockchip,rk3368-mipi-dsi
|
||||
- rockchip,rk3568-mipi-dsi
|
||||
- rockchip,rv1126-mipi-dsi
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,14 @@ properties:
|
|||
description:
|
||||
Additional HDMI QP related data is accessed through VO GRF regs.
|
||||
|
||||
frl-enable-gpios:
|
||||
description:
|
||||
Optional GPIO line to be asserted when operating in HDMI 2.1 FRL mode and
|
||||
deasserted for HDMI 1.4/2.0 TMDS. It can be used to control external
|
||||
voltage bias for HDMI data lines. When not present the HDMI encoder will
|
||||
operate in TMDS mode only.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
@ -132,8 +140,10 @@ unevaluatedProperties: false
|
|||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/pinctrl/rockchip.h>
|
||||
#include <dt-bindings/power/rk3588-power.h>
|
||||
#include <dt-bindings/reset/rockchip,rk3588-cru.h>
|
||||
|
||||
|
|
@ -164,6 +174,7 @@ examples:
|
|||
rockchip,grf = <&sys_grf>;
|
||||
rockchip,vo-grf = <&vo1_grf>;
|
||||
#sound-dai-cells = <0>;
|
||||
frl-enable-gpios = <&gpio4 RK_PB1 GPIO_ACTIVE_LOW>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ properties:
|
|||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- mediatek,mt8196-mali
|
||||
- nxp,imx95-mali # G310
|
||||
- rockchip,rk3588-mali
|
||||
- const: arm,mali-valhall-csf # Mali Valhall GPU model/revision is fully discoverable
|
||||
|
||||
|
|
@ -44,7 +46,9 @@ properties:
|
|||
minItems: 1
|
||||
items:
|
||||
- const: core
|
||||
- const: coregroup
|
||||
- enum:
|
||||
- coregroup
|
||||
- stacks
|
||||
- const: stacks
|
||||
|
||||
mali-supply: true
|
||||
|
|
@ -91,7 +95,6 @@ required:
|
|||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- mali-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
|
@ -108,6 +111,29 @@ allOf:
|
|||
power-domains:
|
||||
maxItems: 1
|
||||
power-domain-names: false
|
||||
required:
|
||||
- mali-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: mediatek,mt8196-mali
|
||||
then:
|
||||
properties:
|
||||
mali-supply: false
|
||||
sram-supply: false
|
||||
operating-points-v2: false
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
power-domain-names: false
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: stacks
|
||||
required:
|
||||
- power-domains
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
|
@ -143,5 +169,17 @@ examples:
|
|||
};
|
||||
};
|
||||
};
|
||||
- |
|
||||
gpu@48000000 {
|
||||
compatible = "mediatek,mt8196-mali", "arm,mali-valhall-csf";
|
||||
reg = <0x48000000 0x480000>;
|
||||
clocks = <&gpufreq 0>, <&gpufreq 1>;
|
||||
clock-names = "core", "stacks";
|
||||
interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH 0>,
|
||||
<GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH 0>,
|
||||
<GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
interrupt-names = "job", "mmu", "gpu";
|
||||
power-domains = <&gpufreq>;
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
|||
|
|
@ -13,6 +13,16 @@ maintainers:
|
|||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a7796-gpu
|
||||
- renesas,r8a77961-gpu
|
||||
- const: img,img-gx6250
|
||||
- const: img,img-rogue
|
||||
- items:
|
||||
- const: renesas,r8a77965-gpu
|
||||
- const: img,img-ge7800
|
||||
- const: img,img-rogue
|
||||
- items:
|
||||
- enum:
|
||||
- ti,am62-gpu
|
||||
|
|
@ -82,6 +92,33 @@ required:
|
|||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ti,am62-gpu
|
||||
- ti,j721s2-gpu
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- img,img-ge7800
|
||||
- img,img-gx6250
|
||||
- thead,th1520-gpu
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
minItems: 3
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -90,14 +127,31 @@ allOf:
|
|||
then:
|
||||
properties:
|
||||
power-domains:
|
||||
items:
|
||||
- description: Power domain A
|
||||
maxItems: 1
|
||||
power-domain-names:
|
||||
maxItems: 1
|
||||
required:
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- img,img-bxs-4-64
|
||||
- img,img-ge7800
|
||||
- img,img-gx6250
|
||||
then:
|
||||
properties:
|
||||
power-domains:
|
||||
minItems: 2
|
||||
power-domain-names:
|
||||
minItems: 2
|
||||
required:
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -105,10 +159,6 @@ allOf:
|
|||
const: thead,th1520-gpu
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
minItems: 3
|
||||
power-domains:
|
||||
items:
|
||||
- description: The single, unified power domain for the GPU on the
|
||||
|
|
@ -117,35 +167,6 @@ allOf:
|
|||
required:
|
||||
- power-domains
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: img,img-bxs-4-64
|
||||
then:
|
||||
properties:
|
||||
power-domains:
|
||||
items:
|
||||
- description: Power domain A
|
||||
- description: Power domain B
|
||||
power-domain-names:
|
||||
minItems: 2
|
||||
required:
|
||||
- power-domains
|
||||
- power-domain-names
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ti,am62-gpu
|
||||
- ti,j721s2-gpu
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ properties:
|
|||
- description: Qcom Adreno GPUs implementing "qcom,smmu-500" and "arm,mmu-500"
|
||||
items:
|
||||
- enum:
|
||||
- qcom,glymur-smmu-500
|
||||
- qcom,kaanapali-smmu-500
|
||||
- qcom,milos-smmu-500
|
||||
- qcom,qcm2290-smmu-500
|
||||
- qcom,qcs615-smmu-500
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/npu/arm,ethos.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Arm Ethos U65/U85
|
||||
|
||||
maintainers:
|
||||
- Rob Herring <robh@kernel.org>
|
||||
|
||||
description: >
|
||||
The Arm Ethos-U NPUs are designed for IoT inference applications. The NPUs
|
||||
can accelerate 8-bit and 16-bit integer quantized networks:
|
||||
|
||||
Transformer networks (U85 only)
|
||||
Convolutional Neural Networks (CNN)
|
||||
Recurrent Neural Networks (RNN)
|
||||
|
||||
Further documentation is available here:
|
||||
|
||||
U65 TRM: https://developer.arm.com/documentation/102023/
|
||||
U85 TRM: https://developer.arm.com/documentation/102685/
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- fsl,imx93-npu
|
||||
- const: arm,ethos-u65
|
||||
- items:
|
||||
- {}
|
||||
- const: arm,ethos-u85
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: apb
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
sram:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/imx93-clock.h>
|
||||
|
||||
npu@4a900000 {
|
||||
compatible = "fsl,imx93-npu", "arm,ethos-u65";
|
||||
reg = <0x4a900000 0x1000>;
|
||||
interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>;
|
||||
power-domains = <&mlmix>;
|
||||
clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>;
|
||||
clock-names = "core", "apb";
|
||||
sram = <&sram>;
|
||||
};
|
||||
...
|
||||
|
|
@ -176,6 +176,8 @@ patternProperties:
|
|||
description: All Sensors Corporation
|
||||
"^asix,.*":
|
||||
description: ASIX Electronics Corporation
|
||||
"^asl-tek,.*":
|
||||
description: ASL Xiamen Technology Co., Ltd.
|
||||
"^aspeed,.*":
|
||||
description: ASPEED Technology Inc.
|
||||
"^asrock,.*":
|
||||
|
|
@ -835,6 +837,8 @@ patternProperties:
|
|||
description: JOZ BV
|
||||
"^jty,.*":
|
||||
description: JTY
|
||||
"^jutouch,.*":
|
||||
description: JuTouch Technology Co., Ltd.
|
||||
"^kam,.*":
|
||||
description: Kamstrup A/S
|
||||
"^karo,.*":
|
||||
|
|
@ -1323,6 +1327,8 @@ patternProperties:
|
|||
description: Raumfeld GmbH
|
||||
"^raydium,.*":
|
||||
description: Raydium Semiconductor Corp.
|
||||
"^raystar,.*":
|
||||
description: Raystar Optronics, Inc.
|
||||
"^rda,.*":
|
||||
description: Unisoc Communications, Inc.
|
||||
"^realtek,.*":
|
||||
|
|
|
|||
|
|
@ -92,6 +92,18 @@ GEM Atomic Helper Reference
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_gem_atomic_helper.c
|
||||
:export:
|
||||
|
||||
VBLANK Helper Reference
|
||||
-----------------------
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c
|
||||
:doc: overview
|
||||
|
||||
.. kernel-doc:: include/drm/drm_vblank_helper.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c
|
||||
:export:
|
||||
|
||||
Simple KMS Helper Reference
|
||||
===========================
|
||||
|
||||
|
|
|
|||
|
|
@ -413,6 +413,21 @@ Plane Panic Functions Reference
|
|||
.. kernel-doc:: drivers/gpu/drm/drm_panic.c
|
||||
:export:
|
||||
|
||||
Colorop Abstraction
|
||||
===================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_colorop.c
|
||||
:doc: overview
|
||||
|
||||
Colorop Functions Reference
|
||||
---------------------------
|
||||
|
||||
.. kernel-doc:: include/drm/drm_colorop.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/drm_colorop.c
|
||||
:export:
|
||||
|
||||
Display Modes Function Reference
|
||||
================================
|
||||
|
||||
|
|
|
|||
|
|
@ -44,25 +44,6 @@ automatically generates the corresponding mappings between a value and a number.
|
|||
| Complexity: Beginner
|
||||
| Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html
|
||||
|
||||
Conversion from byte slices for types implementing FromBytes [TRSM]
|
||||
-------------------------------------------------------------------
|
||||
|
||||
We retrieve several structures from byte streams coming from the BIOS or loaded
|
||||
firmware. At the moment converting the bytes slice into the proper type require
|
||||
an inelegant `unsafe` operation; this will go away once `FromBytes` implements
|
||||
a proper `from_bytes` method.
|
||||
|
||||
| Complexity: Beginner
|
||||
|
||||
CoherentAllocation improvements [COHA]
|
||||
--------------------------------------
|
||||
|
||||
`CoherentAllocation` needs a safe way to write into the allocation, and to
|
||||
obtain slices within the allocation.
|
||||
|
||||
| Complexity: Beginner
|
||||
| Contact: Abdiel Janulgue
|
||||
|
||||
Generic register abstraction [REGA]
|
||||
-----------------------------------
|
||||
|
||||
|
|
@ -153,17 +134,6 @@ A `num` core kernel module is being designed to provide these operations.
|
|||
| Complexity: Intermediate
|
||||
| Contact: Alexandre Courbot
|
||||
|
||||
Delay / Sleep abstractions [DLAY]
|
||||
---------------------------------
|
||||
|
||||
Rust abstractions for the kernel's delay() and sleep() functions.
|
||||
|
||||
FUJITA Tomonori plans to work on abstractions for read_poll_timeout_atomic()
|
||||
(and friends) [1].
|
||||
|
||||
| Complexity: Beginner
|
||||
| Link: https://lore.kernel.org/netdev/20250228.080550.354359820929821928.fujita.tomonori@gmail.com/ [1]
|
||||
|
||||
IRQ abstractions
|
||||
----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,378 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
========================
|
||||
Linux Color Pipeline API
|
||||
========================
|
||||
|
||||
What problem are we solving?
|
||||
============================
|
||||
|
||||
We would like to support pre-, and post-blending complex color
|
||||
transformations in display controller hardware in order to allow for
|
||||
HW-supported HDR use-cases, as well as to provide support to
|
||||
color-managed applications, such as video or image editors.
|
||||
|
||||
It is possible to support an HDR output on HW supporting the Colorspace
|
||||
and HDR Metadata drm_connector properties, but that requires the
|
||||
compositor or application to render and compose the content into one
|
||||
final buffer intended for display. Doing so is costly.
|
||||
|
||||
Most modern display HW offers various 1D LUTs, 3D LUTs, matrices, and other
|
||||
operations to support color transformations. These operations are often
|
||||
implemented in fixed-function HW and therefore much more power efficient than
|
||||
performing similar operations via shaders or CPU.
|
||||
|
||||
We would like to make use of this HW functionality to support complex color
|
||||
transformations with no, or minimal CPU or shader load. The switch between HW
|
||||
fixed-function blocks and shaders/CPU must be seamless with no visible
|
||||
difference when fallback to shaders/CPU is neceesary at any time.
|
||||
|
||||
|
||||
How are other OSes solving this problem?
|
||||
========================================
|
||||
|
||||
The most widely supported use-cases regard HDR content, whether video or
|
||||
gaming.
|
||||
|
||||
Most OSes will specify the source content format (color gamut, encoding transfer
|
||||
function, and other metadata, such as max and average light levels) to a driver.
|
||||
Drivers will then program their fixed-function HW accordingly to map from a
|
||||
source content buffer's space to a display's space.
|
||||
|
||||
When fixed-function HW is not available the compositor will assemble a shader to
|
||||
ask the GPU to perform the transformation from the source content format to the
|
||||
display's format.
|
||||
|
||||
A compositor's mapping function and a driver's mapping function are usually
|
||||
entirely separate concepts. On OSes where a HW vendor has no insight into
|
||||
closed-source compositor code such a vendor will tune their color management
|
||||
code to visually match the compositor's. On other OSes, where both mapping
|
||||
functions are open to an implementer they will ensure both mappings match.
|
||||
|
||||
This results in mapping algorithm lock-in, meaning that no-one alone can
|
||||
experiment with or introduce new mapping algorithms and achieve
|
||||
consistent results regardless of which implementation path is taken.
|
||||
|
||||
Why is Linux different?
|
||||
=======================
|
||||
|
||||
Unlike other OSes, where there is one compositor for one or more drivers, on
|
||||
Linux we have a many-to-many relationship. Many compositors; many drivers.
|
||||
In addition each compositor vendor or community has their own view of how
|
||||
color management should be done. This is what makes Linux so beautiful.
|
||||
|
||||
This means that a HW vendor can now no longer tune their driver to one
|
||||
compositor, as tuning it to one could make it look fairly different from
|
||||
another compositor's color mapping.
|
||||
|
||||
We need a better solution.
|
||||
|
||||
|
||||
Descriptive API
|
||||
===============
|
||||
|
||||
An API that describes the source and destination colorspaces is a descriptive
|
||||
API. It describes the input and output color spaces but does not describe
|
||||
how precisely they should be mapped. Such a mapping includes many minute
|
||||
design decision that can greatly affect the look of the final result.
|
||||
|
||||
It is not feasible to describe such mapping with enough detail to ensure the
|
||||
same result from each implementation. In fact, these mappings are a very active
|
||||
research area.
|
||||
|
||||
|
||||
Prescriptive API
|
||||
================
|
||||
|
||||
A prescriptive API describes not the source and destination colorspaces. It
|
||||
instead prescribes a recipe for how to manipulate pixel values to arrive at the
|
||||
desired outcome.
|
||||
|
||||
This recipe is generally an ordered list of straight-forward operations,
|
||||
with clear mathematical definitions, such as 1D LUTs, 3D LUTs, matrices,
|
||||
or other operations that can be described in a precise manner.
|
||||
|
||||
|
||||
The Color Pipeline API
|
||||
======================
|
||||
|
||||
HW color management pipelines can significantly differ between HW
|
||||
vendors in terms of availability, ordering, and capabilities of HW
|
||||
blocks. This makes a common definition of color management blocks and
|
||||
their ordering nigh impossible. Instead we are defining an API that
|
||||
allows user space to discover the HW capabilities in a generic manner,
|
||||
agnostic of specific drivers and hardware.
|
||||
|
||||
|
||||
drm_colorop Object
|
||||
==================
|
||||
|
||||
To support the definition of color pipelines we define the DRM core
|
||||
object type drm_colorop. Individual drm_colorop objects will be chained
|
||||
via the NEXT property of a drm_colorop to constitute a color pipeline.
|
||||
Each drm_colorop object is unique, i.e., even if multiple color
|
||||
pipelines have the same operation they won't share the same drm_colorop
|
||||
object to describe that operation.
|
||||
|
||||
Note that drivers are not expected to map drm_colorop objects statically
|
||||
to specific HW blocks. The mapping of drm_colorop objects is entirely a
|
||||
driver-internal detail and can be as dynamic or static as a driver needs
|
||||
it to be. See more in the Driver Implementation Guide section below.
|
||||
|
||||
Each drm_colorop has three core properties:
|
||||
|
||||
TYPE: An enumeration property, defining the type of transformation, such as
|
||||
* enumerated curve
|
||||
* custom (uniform) 1D LUT
|
||||
* 3x3 matrix
|
||||
* 3x4 matrix
|
||||
* 3D LUT
|
||||
* etc.
|
||||
|
||||
Depending on the type of transformation other properties will describe
|
||||
more details.
|
||||
|
||||
BYPASS: A boolean property that can be used to easily put a block into
|
||||
bypass mode. The BYPASS property is not mandatory for a colorop, as long
|
||||
as the entire pipeline can get bypassed by setting the COLOR_PIPELINE on
|
||||
a plane to '0'.
|
||||
|
||||
NEXT: The ID of the next drm_colorop in a color pipeline, or 0 if this
|
||||
drm_colorop is the last in the chain.
|
||||
|
||||
An example of a drm_colorop object might look like one of these::
|
||||
|
||||
/* 1D enumerated curve */
|
||||
Color operation 42
|
||||
├─ "TYPE": immutable enum {1D enumerated curve, 1D LUT, 3x3 matrix, 3x4 matrix, 3D LUT, etc.} = 1D enumerated curve
|
||||
├─ "BYPASS": bool {true, false}
|
||||
├─ "CURVE_1D_TYPE": enum {sRGB EOTF, sRGB inverse EOTF, PQ EOTF, PQ inverse EOTF, …}
|
||||
└─ "NEXT": immutable color operation ID = 43
|
||||
|
||||
/* custom 4k entry 1D LUT */
|
||||
Color operation 52
|
||||
├─ "TYPE": immutable enum {1D enumerated curve, 1D LUT, 3x3 matrix, 3x4 matrix, 3D LUT, etc.} = 1D LUT
|
||||
├─ "BYPASS": bool {true, false}
|
||||
├─ "SIZE": immutable range = 4096
|
||||
├─ "DATA": blob
|
||||
└─ "NEXT": immutable color operation ID = 0
|
||||
|
||||
/* 17^3 3D LUT */
|
||||
Color operation 72
|
||||
├─ "TYPE": immutable enum {1D enumerated curve, 1D LUT, 3x3 matrix, 3x4 matrix, 3D LUT, etc.} = 3D LUT
|
||||
├─ "BYPASS": bool {true, false}
|
||||
├─ "SIZE": immutable range = 17
|
||||
├─ "DATA": blob
|
||||
└─ "NEXT": immutable color operation ID = 73
|
||||
|
||||
drm_colorop extensibility
|
||||
-------------------------
|
||||
|
||||
Unlike existing DRM core objects, like &drm_plane, drm_colorop is not
|
||||
extensible. This simplifies implementations and keeps all functionality
|
||||
for managing &drm_colorop objects in the DRM core.
|
||||
|
||||
If there is a need one may introduce a simple &drm_colorop_funcs
|
||||
function table in the future, for example to support an IN_FORMATS
|
||||
property on a &drm_colorop.
|
||||
|
||||
If a driver requires the ability to create a driver-specific colorop
|
||||
object they will need to add &drm_colorop func table support with
|
||||
support for the usual functions, like destroy, atomic_duplicate_state,
|
||||
and atomic_destroy_state.
|
||||
|
||||
|
||||
COLOR_PIPELINE Plane Property
|
||||
=============================
|
||||
|
||||
Color Pipelines are created by a driver and advertised via a new
|
||||
COLOR_PIPELINE enum property on each plane. Values of the property
|
||||
always include object id 0, which is the default and means all color
|
||||
processing is disabled. Additional values will be the object IDs of the
|
||||
first drm_colorop in a pipeline. A driver can create and advertise none,
|
||||
one, or more possible color pipelines. A DRM client will select a color
|
||||
pipeline by setting the COLOR PIPELINE to the respective value.
|
||||
|
||||
NOTE: Many DRM clients will set enumeration properties via the string
|
||||
value, often hard-coding it. Since this enumeration is generated based
|
||||
on the colorop object IDs it is important to perform the Color Pipeline
|
||||
Discovery, described below, instead of hard-coding color pipeline
|
||||
assignment. Drivers might generate the enum strings dynamically.
|
||||
Hard-coded strings might only work for specific drivers on a specific
|
||||
pieces of HW. Color Pipeline Discovery can work universally, as long as
|
||||
drivers implement the required color operations.
|
||||
|
||||
The COLOR_PIPELINE property is only exposed when the
|
||||
DRM_CLIENT_CAP_PLANE_COLOR_PIPELINE is set. Drivers shall ignore any
|
||||
existing pre-blend color operations when this cap is set, such as
|
||||
COLOR_RANGE and COLOR_ENCODING. If drivers want to support COLOR_RANGE
|
||||
or COLOR_ENCODING functionality when the color pipeline client cap is
|
||||
set, they are expected to expose colorops in the pipeline to allow for
|
||||
the appropriate color transformation.
|
||||
|
||||
Setting of the COLOR_PIPELINE plane property or drm_colorop properties
|
||||
is only allowed for userspace that sets this client cap.
|
||||
|
||||
An example of a COLOR_PIPELINE property on a plane might look like this::
|
||||
|
||||
Plane 10
|
||||
├─ "TYPE": immutable enum {Overlay, Primary, Cursor} = Primary
|
||||
├─ …
|
||||
└─ "COLOR_PIPELINE": enum {0, 42, 52} = 0
|
||||
|
||||
|
||||
Color Pipeline Discovery
|
||||
========================
|
||||
|
||||
A DRM client wanting color management on a drm_plane will:
|
||||
|
||||
1. Get the COLOR_PIPELINE property of the plane
|
||||
2. iterate all COLOR_PIPELINE enum values
|
||||
3. for each enum value walk the color pipeline (via the NEXT pointers)
|
||||
and see if the available color operations are suitable for the
|
||||
desired color management operations
|
||||
|
||||
If userspace encounters an unknown or unsuitable color operation during
|
||||
discovery it does not need to reject the entire color pipeline outright,
|
||||
as long as the unknown or unsuitable colorop has a "BYPASS" property.
|
||||
Drivers will ensure that a bypassed block does not have any effect.
|
||||
|
||||
An example of chained properties to define an AMD pre-blending color
|
||||
pipeline might look like this::
|
||||
|
||||
Plane 10
|
||||
├─ "TYPE" (immutable) = Primary
|
||||
└─ "COLOR_PIPELINE": enum {0, 44} = 0
|
||||
|
||||
Color operation 44
|
||||
├─ "TYPE" (immutable) = 1D enumerated curve
|
||||
├─ "BYPASS": bool
|
||||
├─ "CURVE_1D_TYPE": enum {sRGB EOTF, PQ EOTF} = sRGB EOTF
|
||||
└─ "NEXT" (immutable) = 45
|
||||
|
||||
Color operation 45
|
||||
├─ "TYPE" (immutable) = 3x4 Matrix
|
||||
├─ "BYPASS": bool
|
||||
├─ "DATA": blob
|
||||
└─ "NEXT" (immutable) = 46
|
||||
|
||||
Color operation 46
|
||||
├─ "TYPE" (immutable) = 1D enumerated curve
|
||||
├─ "BYPASS": bool
|
||||
├─ "CURVE_1D_TYPE": enum {sRGB Inverse EOTF, PQ Inverse EOTF} = sRGB EOTF
|
||||
└─ "NEXT" (immutable) = 47
|
||||
|
||||
Color operation 47
|
||||
├─ "TYPE" (immutable) = 1D LUT
|
||||
├─ "SIZE": immutable range = 4096
|
||||
├─ "DATA": blob
|
||||
└─ "NEXT" (immutable) = 48
|
||||
|
||||
Color operation 48
|
||||
├─ "TYPE" (immutable) = 3D LUT
|
||||
├─ "DATA": blob
|
||||
└─ "NEXT" (immutable) = 49
|
||||
|
||||
Color operation 49
|
||||
├─ "TYPE" (immutable) = 1D enumerated curve
|
||||
├─ "BYPASS": bool
|
||||
├─ "CURVE_1D_TYPE": enum {sRGB EOTF, PQ EOTF} = sRGB EOTF
|
||||
└─ "NEXT" (immutable) = 0
|
||||
|
||||
|
||||
Color Pipeline Programming
|
||||
==========================
|
||||
|
||||
Once a DRM client has found a suitable pipeline it will:
|
||||
|
||||
1. Set the COLOR_PIPELINE enum value to the one pointing at the first
|
||||
drm_colorop object of the desired pipeline
|
||||
2. Set the properties for all drm_colorop objects in the pipeline to the
|
||||
desired values, setting BYPASS to true for unused drm_colorop blocks,
|
||||
and false for enabled drm_colorop blocks
|
||||
3. Perform (TEST_ONLY or not) atomic commit with all the other KMS
|
||||
states it wishes to change
|
||||
|
||||
To configure the pipeline for an HDR10 PQ plane and blending in linear
|
||||
space, a compositor might perform an atomic commit with the following
|
||||
property values::
|
||||
|
||||
Plane 10
|
||||
└─ "COLOR_PIPELINE" = 42
|
||||
|
||||
Color operation 42
|
||||
└─ "BYPASS" = true
|
||||
|
||||
Color operation 44
|
||||
└─ "BYPASS" = true
|
||||
|
||||
Color operation 45
|
||||
└─ "BYPASS" = true
|
||||
|
||||
Color operation 46
|
||||
└─ "BYPASS" = true
|
||||
|
||||
Color operation 47
|
||||
├─ "DATA" = Gamut mapping + tone mapping + night mode
|
||||
└─ "BYPASS" = false
|
||||
|
||||
Color operation 48
|
||||
├─ "CURVE_1D_TYPE" = PQ EOTF
|
||||
└─ "BYPASS" = false
|
||||
|
||||
|
||||
Driver Implementer's Guide
|
||||
==========================
|
||||
|
||||
What does this all mean for driver implementations? As noted above the
|
||||
colorops can map to HW directly but don't need to do so. Here are some
|
||||
suggestions on how to think about creating your color pipelines:
|
||||
|
||||
- Try to expose pipelines that use already defined colorops, even if
|
||||
your hardware pipeline is split differently. This allows existing
|
||||
userspace to immediately take advantage of the hardware.
|
||||
|
||||
- Additionally, try to expose your actual hardware blocks as colorops.
|
||||
Define new colorop types where you believe it can offer significant
|
||||
benefits if userspace learns to program them.
|
||||
|
||||
- Avoid defining new colorops for compound operations with very narrow
|
||||
scope. If you have a hardware block for a special operation that
|
||||
cannot be split further, you can expose that as a new colorop type.
|
||||
However, try to not define colorops for "use cases", especially if
|
||||
they require you to combine multiple hardware blocks.
|
||||
|
||||
- Design new colorops as prescriptive, not descriptive; by the
|
||||
mathematical formula, not by the assumed input and output.
|
||||
|
||||
A defined colorop type must be deterministic. The exact behavior of the
|
||||
colorop must be documented entirely, whether via a mathematical formula
|
||||
or some other description. Its operation can depend only on its
|
||||
properties and input and nothing else, allowed error tolerance
|
||||
notwithstanding.
|
||||
|
||||
|
||||
Driver Forward/Backward Compatibility
|
||||
=====================================
|
||||
|
||||
As this is uAPI drivers can't regress color pipelines that have been
|
||||
introduced for a given HW generation. New HW generations are free to
|
||||
abandon color pipelines advertised for previous generations.
|
||||
Nevertheless, it can be beneficial to carry support for existing color
|
||||
pipelines forward as those will likely already have support in DRM
|
||||
clients.
|
||||
|
||||
Introducing new colorops to a pipeline is fine, as long as they can be
|
||||
bypassed or are purely informational. DRM clients implementing support
|
||||
for the pipeline can always skip unknown properties as long as they can
|
||||
be confident that doing so will not cause unexpected results.
|
||||
|
||||
If a new colorop doesn't fall into one of the above categories
|
||||
(bypassable or informational) the modified pipeline would be unusable
|
||||
for user space. In this case a new pipeline should be defined.
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
1. https://lore.kernel.org/dri-devel/QMers3awXvNCQlyhWdTtsPwkp5ie9bze_hD5nAccFW7a_RXlWjYB7MoUW_8CKLT2bSQwIXVi5H6VULYIxCdgvryZoAoJnC5lZgyK1QWn488=@emersion.fr/
|
||||
|
|
@ -35,3 +35,6 @@ host such documentation:
|
|||
.. toctree::
|
||||
|
||||
i915_vm_bind.rst
|
||||
|
||||
.. toctree::
|
||||
color_pipeline.rst
|
||||
|
|
@ -623,6 +623,43 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de>, Simona Vetter
|
|||
|
||||
Level: Advanced
|
||||
|
||||
Implement a new DUMB_CREATE2 ioctl
|
||||
----------------------------------
|
||||
|
||||
The current DUMB_CREATE ioctl is not well defined. Instead of a pixel and
|
||||
framebuffer format, it only accepts a color mode of vague semantics. Assuming
|
||||
a linear framebuffer, the color mode gives an idea of the supported pixel
|
||||
format. But userspace effectively has to guess the correct values. It really
|
||||
only works reliably with framebuffers in XRGB8888. Userspace has begun to
|
||||
workaround these limitations by computing arbitrary format's buffer sizes and
|
||||
calculating their sizes in terms of XRGB8888 pixels.
|
||||
|
||||
One possible solution is a new ioctl DUMB_CREATE2. It should accept a DRM
|
||||
format and a format modifier to resolve the color mode's ambiguity. As
|
||||
framebuffers can be multi-planar, the new ioctl has to return the buffer size,
|
||||
pitch and GEM handle for each individual color plane.
|
||||
|
||||
In the first step, the new ioctl can be limited to the current features of
|
||||
the existing DUMB_CREATE. Individual drivers can then be extended to support
|
||||
multi-planar formats. Rockchip might require this and would be a good candidate.
|
||||
|
||||
It might also be helpful to userspace to query information about the size of
|
||||
a potential buffer, if allocated. Userspace would supply geometry and format;
|
||||
the kernel would return minimal allocation sizes and scanline pitch. There is
|
||||
interest to allocate that memory from another device and provide it to the
|
||||
DRM driver (say via dma-buf).
|
||||
|
||||
Another requested feature is the ability to allocate a buffer by size, without
|
||||
format. Accelators use this for their buffer allocation and it could likely be
|
||||
generalized.
|
||||
|
||||
In addition to the kernel implementation, there must be user-space support
|
||||
for the new ioctl. There's code in Mesa that might be able to use the new
|
||||
call.
|
||||
|
||||
Contact: Thomas Zimmermann <tzimmermann@suse.de>
|
||||
|
||||
Level: Advanced
|
||||
|
||||
Better Testing
|
||||
==============
|
||||
|
|
|
|||
|
|
@ -51,6 +51,97 @@ To disable the driver, use ::
|
|||
|
||||
sudo modprobe -r vkms
|
||||
|
||||
Configuring With Configfs
|
||||
=========================
|
||||
|
||||
It is possible to create and configure multiple VKMS instances via configfs.
|
||||
|
||||
Start by mounting configfs and loading VKMS::
|
||||
|
||||
sudo mount -t configfs none /config
|
||||
sudo modprobe vkms
|
||||
|
||||
Once VKMS is loaded, ``/config/vkms`` is created automatically. Each directory
|
||||
under ``/config/vkms`` represents a VKMS instance, create a new one::
|
||||
|
||||
sudo mkdir /config/vkms/my-vkms
|
||||
|
||||
By default, the instance is disabled::
|
||||
|
||||
cat /config/vkms/my-vkms/enabled
|
||||
0
|
||||
|
||||
And directories are created for each configurable item of the display pipeline::
|
||||
|
||||
tree /config/vkms/my-vkms
|
||||
├── connectors
|
||||
├── crtcs
|
||||
├── enabled
|
||||
├── encoders
|
||||
└── planes
|
||||
|
||||
To add items to the display pipeline, create one or more directories under the
|
||||
available paths.
|
||||
|
||||
Start by creating one or more planes::
|
||||
|
||||
sudo mkdir /config/vkms/my-vkms/planes/plane0
|
||||
|
||||
Planes have 1 configurable attribute:
|
||||
|
||||
- type: Plane type: 0 overlay, 1 primary, 2 cursor (same values as those
|
||||
exposed by the "type" property of a plane)
|
||||
|
||||
Continue by creating one or more CRTCs::
|
||||
|
||||
sudo mkdir /config/vkms/my-vkms/crtcs/crtc0
|
||||
|
||||
CRTCs have 1 configurable attribute:
|
||||
|
||||
- writeback: Enable or disable writeback connector support by writing 1 or 0
|
||||
|
||||
Next, create one or more encoders::
|
||||
|
||||
sudo mkdir /config/vkms/my-vkms/encoders/encoder0
|
||||
|
||||
Last but not least, create one or more connectors::
|
||||
|
||||
sudo mkdir /config/vkms/my-vkms/connectors/connector0
|
||||
|
||||
Connectors have 1 configurable attribute:
|
||||
|
||||
- status: Connection status: 1 connected, 2 disconnected, 3 unknown (same values
|
||||
as those exposed by the "status" property of a connector)
|
||||
|
||||
To finish the configuration, link the different pipeline items::
|
||||
|
||||
sudo ln -s /config/vkms/my-vkms/crtcs/crtc0 /config/vkms/my-vkms/planes/plane0/possible_crtcs
|
||||
sudo ln -s /config/vkms/my-vkms/crtcs/crtc0 /config/vkms/my-vkms/encoders/encoder0/possible_crtcs
|
||||
sudo ln -s /config/vkms/my-vkms/encoders/encoder0 /config/vkms/my-vkms/connectors/connector0/possible_encoders
|
||||
|
||||
Since at least one primary plane is required, make sure to set the right type::
|
||||
|
||||
echo "1" | sudo tee /config/vkms/my-vkms/planes/plane0/type
|
||||
|
||||
Once you are done configuring the VKMS instance, enable it::
|
||||
|
||||
echo "1" | sudo tee /config/vkms/my-vkms/enabled
|
||||
|
||||
Finally, you can remove the VKMS instance disabling it::
|
||||
|
||||
echo "0" | sudo tee /config/vkms/my-vkms/enabled
|
||||
|
||||
And removing the top level directory and its subdirectories::
|
||||
|
||||
sudo rm /config/vkms/my-vkms/planes/*/possible_crtcs/*
|
||||
sudo rm /config/vkms/my-vkms/encoders/*/possible_crtcs/*
|
||||
sudo rm /config/vkms/my-vkms/connectors/*/possible_encoders/*
|
||||
sudo rmdir /config/vkms/my-vkms/planes/*
|
||||
sudo rmdir /config/vkms/my-vkms/crtcs/*
|
||||
sudo rmdir /config/vkms/my-vkms/encoders/*
|
||||
sudo rmdir /config/vkms/my-vkms/connectors/*
|
||||
sudo rmdir /config/vkms/my-vkms
|
||||
|
||||
Testing With IGT
|
||||
================
|
||||
|
||||
|
|
@ -68,26 +159,23 @@ To return to graphical mode, do::
|
|||
|
||||
sudo systemctl isolate graphical.target
|
||||
|
||||
Once you are in text only mode, you can run tests using the --device switch
|
||||
or IGT_DEVICE variable to specify the device filter for the driver we want
|
||||
to test. IGT_DEVICE can also be used with the run-test.sh script to run the
|
||||
Once you are in text only mode, you can run tests using the IGT_FORCE_DRIVER
|
||||
variable to specify the device filter for the driver we want to test.
|
||||
IGT_FORCE_DRIVER can also be used with the run-tests.sh script to run the
|
||||
tests for a specific driver::
|
||||
|
||||
sudo ./build/tests/<name of test> --device "sys:/sys/devices/platform/vkms"
|
||||
sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/<name of test>
|
||||
sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./scripts/run-tests.sh -t <name of test>
|
||||
sudo IGT_FORCE_DRIVER="vkms" ./build/tests/<name of test>
|
||||
sudo IGT_FORCE_DRIVER="vkms" ./scripts/run-tests.sh -t <name of test>
|
||||
|
||||
For example, to test the functionality of the writeback library,
|
||||
we can run the kms_writeback test::
|
||||
|
||||
sudo ./build/tests/kms_writeback --device "sys:/sys/devices/platform/vkms"
|
||||
sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/kms_writeback
|
||||
sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./scripts/run-tests.sh -t kms_writeback
|
||||
sudo IGT_FORCE_DRIVER="vkms" ./build/tests/kms_writeback
|
||||
sudo IGT_FORCE_DRIVER="vkms" ./scripts/run-tests.sh -t kms_writeback
|
||||
|
||||
You can also run subtests if you do not want to run the entire test::
|
||||
|
||||
sudo ./build/tests/kms_flip --run-subtest basic-plain-flip --device "sys:/sys/devices/platform/vkms"
|
||||
sudo IGT_DEVICE="sys:/sys/devices/platform/vkms" ./build/tests/kms_flip --run-subtest basic-plain-flip
|
||||
sudo IGT_FORCE_DRIVER="vkms" ./build/tests/kms_flip --run-subtest basic-plain-flip
|
||||
|
||||
Testing With KUnit
|
||||
==================
|
||||
|
|
@ -147,21 +235,14 @@ Runtime Configuration
|
|||
---------------------
|
||||
|
||||
We want to be able to reconfigure vkms instance without having to reload the
|
||||
module. Use/Test-cases:
|
||||
module through configfs. Use/Test-cases:
|
||||
|
||||
- Hotplug/hotremove connectors on the fly (to be able to test DP MST handling
|
||||
of compositors).
|
||||
|
||||
- Configure planes/crtcs/connectors (we'd need some code to have more than 1 of
|
||||
them first).
|
||||
|
||||
- Change output configuration: Plug/unplug screens, change EDID, allow changing
|
||||
the refresh rate.
|
||||
|
||||
The currently proposed solution is to expose vkms configuration through
|
||||
configfs. All existing module options should be supported through configfs
|
||||
too.
|
||||
|
||||
Writeback support
|
||||
-----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ DG2, etc is provided to prototype the driver.
|
|||
xe_mm
|
||||
xe_map
|
||||
xe_migrate
|
||||
xe_exec_queue
|
||||
xe_cs
|
||||
xe_pm
|
||||
xe_gt_freq
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
|
||||
|
||||
===============
|
||||
Execution Queue
|
||||
===============
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
|
||||
:doc: Execution Queue
|
||||
|
||||
Internal API
|
||||
============
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue_types.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.h
|
||||
:internal:
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_exec_queue.c
|
||||
:internal:
|
||||
|
|
@ -7,6 +7,9 @@ Xe GT Frequency Management
|
|||
.. kernel-doc:: drivers/gpu/drm/xe/xe_gt_freq.c
|
||||
:doc: Xe GT Frequency Management
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/xe/xe_gt_throttle.c
|
||||
:doc: Xe GT Throttle
|
||||
|
||||
Internal API
|
||||
============
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,52 @@ following heaps:
|
|||
|
||||
- The ``system`` heap allocates virtually contiguous, cacheable, buffers.
|
||||
|
||||
- The ``cma`` heap allocates physically contiguous, cacheable,
|
||||
buffers. Only present if a CMA region is present. Such a region is
|
||||
usually created either through the kernel commandline through the
|
||||
``cma`` parameter, a memory region Device-Tree node with the
|
||||
``linux,cma-default`` property set, or through the ``CMA_SIZE_MBYTES`` or
|
||||
``CMA_SIZE_PERCENTAGE`` Kconfig options. The heap's name in devtmpfs is
|
||||
``default_cma_region``. For backwards compatibility, when the
|
||||
``DMABUF_HEAPS_CMA_LEGACY`` Kconfig option is set, a duplicate node is
|
||||
created following legacy naming conventions; the legacy name might be
|
||||
``reserved``, ``linux,cma``, or ``default-pool``.
|
||||
- The ``default_cma_region`` heap allocates physically contiguous,
|
||||
cacheable, buffers. Only present if a CMA region is present. Such a
|
||||
region is usually created either through the kernel commandline
|
||||
through the ``cma`` parameter, a memory region Device-Tree node with
|
||||
the ``linux,cma-default`` property set, or through the
|
||||
``CMA_SIZE_MBYTES`` or ``CMA_SIZE_PERCENTAGE`` Kconfig options. Prior
|
||||
to Linux 6.17, its name wasn't stable and could be called
|
||||
``reserved``, ``linux,cma``, or ``default-pool``, depending on the
|
||||
platform.
|
||||
|
||||
- A heap will be created for each reusable region in the device tree
|
||||
with the ``shared-dma-pool`` compatible, using the full device tree
|
||||
node name as its name. The buffer semantics are identical to
|
||||
``default-cma-region``.
|
||||
|
||||
Naming Convention
|
||||
=================
|
||||
|
||||
``dma-buf`` heaps name should meet a number of constraints:
|
||||
|
||||
- The name must be stable, and must not change from one version to the other.
|
||||
Userspace identifies heaps by their name, so if the names ever change, we
|
||||
would be likely to introduce regressions.
|
||||
|
||||
- The name must describe the memory region the heap will allocate from, and
|
||||
must uniquely identify it in a given platform. Since userspace applications
|
||||
use the heap name as the discriminant, it must be able to tell which heap it
|
||||
wants to use reliably if there's multiple heaps.
|
||||
|
||||
- The name must not mention implementation details, such as the allocator. The
|
||||
heap driver will change over time, and implementation details when it was
|
||||
introduced might not be relevant in the future.
|
||||
|
||||
- The name should describe properties of the buffers that would be allocated.
|
||||
Doing so will make heap identification easier for userspace. Such properties
|
||||
are:
|
||||
|
||||
- ``contiguous`` for physically contiguous buffers;
|
||||
|
||||
- ``protected`` for encrypted buffers not accessible the OS;
|
||||
|
||||
- The name may describe intended usage. Doing so will make heap identification
|
||||
easier for userspace applications and users.
|
||||
|
||||
For example, assuming a platform with a reserved memory region located
|
||||
at the RAM address 0x42000000, intended to allocate video framebuffers,
|
||||
physically contiguous, and backed by the CMA kernel allocator, good
|
||||
names would be ``memory@42000000-contiguous`` or ``video@42000000``, but
|
||||
``cma-video`` wouldn't.
|
||||
|
|
|
|||
48
MAINTAINERS
48
MAINTAINERS
|
|
@ -1081,7 +1081,7 @@ M: Austin Zheng <austin.zheng@amd.com>
|
|||
M: Jun Lei <jun.lei@amd.com>
|
||||
S: Supported
|
||||
F: drivers/gpu/drm/amd/display/dc/dml/
|
||||
F: drivers/gpu/drm/amd/display/dc/dml2/
|
||||
F: drivers/gpu/drm/amd/display/dc/dml2_0/
|
||||
|
||||
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
|
||||
M: Huang Rui <ray.huang@amd.com>
|
||||
|
|
@ -2022,6 +2022,15 @@ F: arch/arm64/include/asm/arch_timer.h
|
|||
F: drivers/clocksource/arm_arch_timer.c
|
||||
F: drivers/clocksource/arm_arch_timer_mmio.c
|
||||
|
||||
ARM ETHOS-U NPU DRIVER
|
||||
M: Rob Herring (Arm) <robh@kernel.org>
|
||||
M: Tomeu Vizoso <tomeu@tomeuvizoso.net>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/accel/ethosu/
|
||||
F: include/uapi/drm/ethosu_accel.h
|
||||
|
||||
ARM GENERIC INTERRUPT CONTROLLER DRIVERS
|
||||
M: Marc Zyngier <maz@kernel.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
@ -2097,7 +2106,8 @@ F: drivers/gpu/drm/arm/display/komeda/
|
|||
ARM MALI PANFROST DRM DRIVER
|
||||
M: Boris Brezillon <boris.brezillon@collabora.com>
|
||||
M: Rob Herring <robh@kernel.org>
|
||||
R: Steven Price <steven.price@arm.com>
|
||||
M: Steven Price <steven.price@arm.com>
|
||||
M: Adrián Larumbe <adrian.larumbe@collabora.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
|
|
@ -2317,7 +2327,7 @@ S: Maintained
|
|||
F: drivers/clk/sunxi/
|
||||
|
||||
ARM/Allwinner sunXi SoC support
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
M: Chen-Yu Tsai <wens@kernel.org>
|
||||
M: Jernej Skrabec <jernej.skrabec@gmail.com>
|
||||
M: Samuel Holland <samuel@sholland.org>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
|
|
@ -7331,6 +7341,7 @@ F: Documentation/userspace-api/dma-buf-alloc-exchange.rst
|
|||
F: drivers/dma-buf/
|
||||
F: include/linux/*fence.h
|
||||
F: include/linux/dma-buf.h
|
||||
F: include/linux/dma-buf/
|
||||
F: include/linux/dma-resv.h
|
||||
K: \bdma_(?:buf|fence|resv)\b
|
||||
|
||||
|
|
@ -7654,8 +7665,7 @@ F: drivers/accel/
|
|||
F: include/drm/drm_accel.h
|
||||
|
||||
DRM DRIVER FOR ALLWINNER DE2 AND DE3 ENGINE
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
M: Chen-Yu Tsai <wens@kernel.org>
|
||||
R: Jernej Skrabec <jernej.skrabec@gmail.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
|
|
@ -7764,7 +7774,8 @@ F: Documentation/devicetree/bindings/display/panel/panel-edp.yaml
|
|||
F: drivers/gpu/drm/panel/panel-edp.c
|
||||
|
||||
DRM DRIVER FOR GENERIC USB DISPLAY
|
||||
S: Orphan
|
||||
M: Ruben Wauters <rubenru09@aol.com>
|
||||
S: Maintained
|
||||
W: https://github.com/notro/gud/wiki
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/gud/
|
||||
|
|
@ -7886,6 +7897,7 @@ DRM DRIVER for Qualcomm Adreno GPUs
|
|||
M: Rob Clark <robin.clark@oss.qualcomm.com>
|
||||
R: Sean Paul <sean@poorly.run>
|
||||
R: Konrad Dybcio <konradybcio@kernel.org>
|
||||
R: Akhil P Oommen <akhilpo@oss.qualcomm.com>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
L: freedreno@lists.freedesktop.org
|
||||
|
|
@ -7905,7 +7917,7 @@ DRM DRIVER for Qualcomm display hardware
|
|||
M: Rob Clark <robin.clark@oss.qualcomm.com>
|
||||
M: Dmitry Baryshkov <lumag@kernel.org>
|
||||
R: Abhinav Kumar <abhinav.kumar@linux.dev>
|
||||
R: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
|
||||
R: Jessica Zhang <jesszhan0024@gmail.com>
|
||||
R: Sean Paul <sean@poorly.run>
|
||||
R: Marijn Suijten <marijn.suijten@somainline.org>
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
|
|
@ -8074,12 +8086,25 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/display/panel/samsung,s6d7aa0.yaml
|
||||
F: drivers/gpu/drm/panel/panel-samsung-s6d7aa0.c
|
||||
|
||||
DRM DRIVER FOR SAMSUNG S6E3FC2X01 DDIC
|
||||
M: David Heidelberg <david@ixit.cz>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/panel/samsung,s6e3fc2x01.yaml
|
||||
F: drivers/gpu/drm/panel/panel-samsung-s6e3fc2x01.c
|
||||
|
||||
DRM DRIVER FOR SAMSUNG S6E3HA8 PANELS
|
||||
M: Dzmitry Sankouski <dsankouski@gmail.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/panel/samsung,s6e3ha8.yaml
|
||||
F: drivers/gpu/drm/panel/panel-samsung-s6e3ha8.c
|
||||
|
||||
DRM DRIVER FOR SAMSUNG SOFEF00 DDIC
|
||||
M: David Heidelberg <david@ixit.cz>
|
||||
M: Casey Connolly <casey.connolly@linaro.org>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/display/panel/samsung,sofef00.yaml
|
||||
F: drivers/gpu/drm/panel/panel-samsung-sofef00.c
|
||||
|
||||
DRM DRIVER FOR SHARP MEMORY LCD
|
||||
M: Alex Lanzano <lanzano.alex@gmail.com>
|
||||
S: Maintained
|
||||
|
|
@ -8264,12 +8289,12 @@ S: Supported
|
|||
W: https://drm.pages.freedesktop.org/maintainer-tools/drm-rust.html
|
||||
T: git https://gitlab.freedesktop.org/drm/rust/kernel.git
|
||||
F: drivers/gpu/drm/nova/
|
||||
F: drivers/gpu/drm/tyr/
|
||||
F: drivers/gpu/nova-core/
|
||||
F: rust/kernel/drm/
|
||||
|
||||
DRM DRIVERS FOR ALLWINNER A10
|
||||
M: Maxime Ripard <mripard@kernel.org>
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
M: Chen-Yu Tsai <wens@kernel.org>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Supported
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
|
|
@ -8596,6 +8621,7 @@ S: Supported
|
|||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
F: drivers/gpu/drm/scheduler/
|
||||
F: include/drm/gpu_scheduler.h
|
||||
F: include/drm/spsc_queue.h
|
||||
|
||||
DRM GPUVM
|
||||
M: Danilo Krummrich <dakr@kernel.org>
|
||||
|
|
@ -8618,7 +8644,7 @@ F: drivers/gpu/drm/clients/drm_log.c
|
|||
|
||||
DRM PANEL DRIVERS
|
||||
M: Neil Armstrong <neil.armstrong@linaro.org>
|
||||
R: Jessica Zhang <jessica.zhang@oss.qualcomm.com>
|
||||
R: Jessica Zhang <jesszhan0024@gmail.com>
|
||||
L: dri-devel@lists.freedesktop.org
|
||||
S: Maintained
|
||||
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
|
||||
|
|
@ -27832,7 +27858,7 @@ F: drivers/acpi/pmic/intel_pmic_xpower.c
|
|||
N: axp288
|
||||
|
||||
X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS
|
||||
M: Chen-Yu Tsai <wens@csie.org>
|
||||
M: Chen-Yu Tsai <wens@kernel.org>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
N: axp[128]
|
||||
|
|
|
|||
|
|
@ -34,6 +34,41 @@ usb2_picophy2: phy3 {
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
display-subsystem {
|
||||
compatible = "st,sti-display-subsystem";
|
||||
ports = <&compositor>, <&hqvdp>, <&tvout>, <&sti_hdmi>;
|
||||
|
||||
assigned-clocks = <&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>,
|
||||
<&clk_s_c0_pll1 0>,
|
||||
<&clk_s_c0_flexgen CLK_COMPO_DVP>,
|
||||
<&clk_s_c0_flexgen CLK_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP1>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP2>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP3>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP4>;
|
||||
|
||||
assigned-clock-parents = <0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<&clk_s_c0_pll1 0>,
|
||||
<&clk_s_c0_pll1 0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>;
|
||||
|
||||
assigned-clock-rates = <297000000>,
|
||||
<297000000>,
|
||||
<0>,
|
||||
<400000000>,
|
||||
<400000000>;
|
||||
};
|
||||
|
||||
soc {
|
||||
ohci0: usb@9a03c00 {
|
||||
compatible = "st,st-ohci-300x";
|
||||
|
|
@ -99,153 +134,176 @@ ehci1: usb@9a83e00 {
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
sti-display-subsystem@0 {
|
||||
compatible = "st,sti-display-subsystem";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
compositor: display-controller@9d11000 {
|
||||
compatible = "st,stih407-compositor";
|
||||
reg = <0x9d11000 0x1000>;
|
||||
|
||||
reg = <0 0>;
|
||||
assigned-clocks = <&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>,
|
||||
<&clk_s_c0_pll1 0>,
|
||||
<&clk_s_c0_flexgen CLK_COMPO_DVP>,
|
||||
<&clk_s_c0_flexgen CLK_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP1>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP2>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP3>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP4>;
|
||||
clock-names = "compo_main",
|
||||
"compo_aux",
|
||||
"pix_main",
|
||||
"pix_aux",
|
||||
"pix_gdp1",
|
||||
"pix_gdp2",
|
||||
"pix_gdp3",
|
||||
"pix_gdp4",
|
||||
"main_parent",
|
||||
"aux_parent";
|
||||
|
||||
assigned-clock-parents = <0>,
|
||||
<0>,
|
||||
<0>,
|
||||
<&clk_s_c0_pll1 0>,
|
||||
<&clk_s_c0_pll1 0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
clocks = <&clk_s_c0_flexgen CLK_COMPO_DVP>,
|
||||
<&clk_s_c0_flexgen CLK_COMPO_DVP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP1>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP2>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP3>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP4>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
|
||||
reset-names = "compo-main", "compo-aux";
|
||||
resets = <&softreset STIH407_COMPO_SOFTRESET>,
|
||||
<&softreset STIH407_COMPO_SOFTRESET>;
|
||||
st,vtg = <&vtg_main>, <&vtg_aux>;
|
||||
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
compo_main_out: endpoint {
|
||||
remote-endpoint = <&tvout_in0>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
compo_aux_out: endpoint {
|
||||
remote-endpoint = <&tvout_in1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
tvout: encoder@8d08000 {
|
||||
compatible = "st,stih407-tvout";
|
||||
reg = <0x8d08000 0x1000>;
|
||||
reg-names = "tvout-reg";
|
||||
reset-names = "tvout";
|
||||
resets = <&softreset STIH407_HDTVOUT_SOFTRESET>;
|
||||
assigned-clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_TMDS_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
|
||||
<&clk_s_d0_flexgen CLK_PCM_0>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_HDDAC>,
|
||||
<&clk_s_d2_flexgen CLK_HDDAC>;
|
||||
|
||||
assigned-clock-parents = <&clk_s_d2_quadfs 0>,
|
||||
<&clk_tmdsout_hdmi>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d0_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>;
|
||||
|
||||
assigned-clock-rates = <297000000>,
|
||||
<297000000>,
|
||||
<0>,
|
||||
<400000000>,
|
||||
<400000000>;
|
||||
|
||||
ranges;
|
||||
|
||||
sti-compositor@9d11000 {
|
||||
compatible = "st,stih407-compositor";
|
||||
reg = <0x9d11000 0x1000>;
|
||||
|
||||
clock-names = "compo_main",
|
||||
"compo_aux",
|
||||
"pix_main",
|
||||
"pix_aux",
|
||||
"pix_gdp1",
|
||||
"pix_gdp2",
|
||||
"pix_gdp3",
|
||||
"pix_gdp4",
|
||||
"main_parent",
|
||||
"aux_parent";
|
||||
|
||||
clocks = <&clk_s_c0_flexgen CLK_COMPO_DVP>,
|
||||
<&clk_s_c0_flexgen CLK_COMPO_DVP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_AUX_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP1>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP2>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP3>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_GDP4>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
|
||||
reset-names = "compo-main", "compo-aux";
|
||||
resets = <&softreset STIH407_COMPO_SOFTRESET>,
|
||||
<&softreset STIH407_COMPO_SOFTRESET>;
|
||||
st,vtg = <&vtg_main>, <&vtg_aux>;
|
||||
};
|
||||
|
||||
sti-tvout@8d08000 {
|
||||
compatible = "st,stih407-tvout";
|
||||
reg = <0x8d08000 0x1000>;
|
||||
reg-names = "tvout-reg";
|
||||
reset-names = "tvout";
|
||||
resets = <&softreset STIH407_HDTVOUT_SOFTRESET>;
|
||||
ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
assigned-clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_TMDS_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
|
||||
<&clk_s_d0_flexgen CLK_PCM_0>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_HDDAC>,
|
||||
<&clk_s_d2_flexgen CLK_HDDAC>;
|
||||
#size-cells = <0>;
|
||||
|
||||
assigned-clock-parents = <&clk_s_d2_quadfs 0>,
|
||||
<&clk_tmdsout_hdmi>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d0_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 0>;
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
tvout_in0: endpoint {
|
||||
remote-endpoint = <&compo_main_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
tvout_in1: endpoint {
|
||||
remote-endpoint = <&compo_aux_out>;
|
||||
};
|
||||
};
|
||||
|
||||
port@2 {
|
||||
reg = <2>;
|
||||
tvout_out0: endpoint {
|
||||
remote-endpoint = <&hdmi_in>;
|
||||
};
|
||||
};
|
||||
|
||||
port@3 {
|
||||
reg = <3>;
|
||||
tvout_out1: endpoint {
|
||||
remote-endpoint = <&hda_in>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sti_hdmi: sti-hdmi@8d04000 {
|
||||
compatible = "st,stih407-hdmi";
|
||||
reg = <0x8d04000 0x1000>;
|
||||
reg-names = "hdmi-reg";
|
||||
#sound-dai-cells = <0>;
|
||||
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "irq";
|
||||
clock-names = "pix",
|
||||
"tmds",
|
||||
"phy",
|
||||
"audio",
|
||||
"main_parent",
|
||||
"aux_parent";
|
||||
sti_hdmi: hdmi@8d04000 {
|
||||
compatible = "st,stih407-hdmi";
|
||||
reg = <0x8d04000 0x1000>;
|
||||
reg-names = "hdmi-reg";
|
||||
#sound-dai-cells = <0>;
|
||||
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "irq";
|
||||
clock-names = "pix",
|
||||
"tmds",
|
||||
"phy",
|
||||
"audio",
|
||||
"main_parent",
|
||||
"aux_parent";
|
||||
|
||||
clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_TMDS_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
|
||||
<&clk_s_d0_flexgen CLK_PCM_0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
clocks = <&clk_s_d2_flexgen CLK_PIX_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_TMDS_HDMI>,
|
||||
<&clk_s_d2_flexgen CLK_REF_HDMIPHY>,
|
||||
<&clk_s_d0_flexgen CLK_PCM_0>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
|
||||
hdmi,hpd-gpio = <&pio5 3 GPIO_ACTIVE_LOW>;
|
||||
reset-names = "hdmi";
|
||||
resets = <&softreset STIH407_HDMI_TX_PHY_SOFTRESET>;
|
||||
ddc = <&hdmiddc>;
|
||||
hdmi,hpd-gpio = <&pio5 3 GPIO_ACTIVE_LOW>;
|
||||
reset-names = "hdmi";
|
||||
resets = <&softreset STIH407_HDMI_TX_PHY_SOFTRESET>;
|
||||
ddc = <&hdmiddc>;
|
||||
|
||||
port {
|
||||
hdmi_in: endpoint {
|
||||
remote-endpoint = <&tvout_out0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
sti-hda@8d02000 {
|
||||
compatible = "st,stih407-hda";
|
||||
status = "disabled";
|
||||
reg = <0x8d02000 0x400>, <0x92b0120 0x4>;
|
||||
reg-names = "hda-reg", "video-dacs-ctrl";
|
||||
clock-names = "pix",
|
||||
"hddac",
|
||||
"main_parent",
|
||||
"aux_parent";
|
||||
clocks = <&clk_s_d2_flexgen CLK_PIX_HDDAC>,
|
||||
<&clk_s_d2_flexgen CLK_HDDAC>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
};
|
||||
analog@8d02000 {
|
||||
compatible = "st,stih407-hda";
|
||||
status = "disabled";
|
||||
reg = <0x8d02000 0x400>, <0x92b0120 0x4>;
|
||||
reg-names = "hda-reg", "video-dacs-ctrl";
|
||||
clock-names = "pix",
|
||||
"hddac",
|
||||
"main_parent",
|
||||
"aux_parent";
|
||||
clocks = <&clk_s_d2_flexgen CLK_PIX_HDDAC>,
|
||||
<&clk_s_d2_flexgen CLK_HDDAC>,
|
||||
<&clk_s_d2_quadfs 0>,
|
||||
<&clk_s_d2_quadfs 1>;
|
||||
|
||||
sti-hqvdp@9c00000 {
|
||||
compatible = "st,stih407-hqvdp";
|
||||
reg = <0x9C00000 0x100000>;
|
||||
clock-names = "hqvdp", "pix_main";
|
||||
clocks = <&clk_s_c0_flexgen CLK_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>;
|
||||
reset-names = "hqvdp";
|
||||
resets = <&softreset STIH407_HDQVDP_SOFTRESET>;
|
||||
st,vtg = <&vtg_main>;
|
||||
port {
|
||||
hda_in: endpoint {
|
||||
remote-endpoint = <&tvout_out1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
hqvdp: plane@9c00000 {
|
||||
compatible = "st,stih407-hqvdp";
|
||||
reg = <0x9C00000 0x100000>;
|
||||
clock-names = "hqvdp", "pix_main";
|
||||
clocks = <&clk_s_c0_flexgen CLK_MAIN_DISP>,
|
||||
<&clk_s_d2_flexgen CLK_PIX_MAIN_DISP>;
|
||||
reset-names = "hqvdp";
|
||||
resets = <&softreset STIH407_HDQVDP_SOFTRESET>;
|
||||
st,vtg = <&vtg_main>;
|
||||
};
|
||||
|
||||
bdisp0:bdisp@9f10000 {
|
||||
compatible = "st,stih407-bdisp";
|
||||
reg = <0x9f10000 0x1000>;
|
||||
|
|
|
|||
|
|
@ -250,6 +250,28 @@ dummy: clock-dummy {
|
|||
clock-output-names = "dummy";
|
||||
};
|
||||
|
||||
gpu_opp_table: opp-table {
|
||||
compatible = "operating-points-v2";
|
||||
|
||||
opp-500000000 {
|
||||
opp-hz = /bits/ 64 <500000000>;
|
||||
opp-hz-real = /bits/ 64 <500000000>;
|
||||
opp-microvolt = <920000>;
|
||||
};
|
||||
|
||||
opp-800000000 {
|
||||
opp-hz = /bits/ 64 <800000000>;
|
||||
opp-hz-real = /bits/ 64 <800000000>;
|
||||
opp-microvolt = <920000>;
|
||||
};
|
||||
|
||||
opp-1000000000 {
|
||||
opp-hz = /bits/ 64 <1000000000>;
|
||||
opp-hz-real = /bits/ 64 <1000000000>;
|
||||
opp-microvolt = <920000>;
|
||||
};
|
||||
};
|
||||
|
||||
clk_ext1: clock-ext1 {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
|
|
@ -2139,6 +2161,21 @@ netc_emdio: mdio@0,0 {
|
|||
};
|
||||
};
|
||||
|
||||
gpu: gpu@4d900000 {
|
||||
compatible = "nxp,imx95-mali", "arm,mali-valhall-csf";
|
||||
reg = <0 0x4d900000 0 0x480000>;
|
||||
clocks = <&scmi_clk IMX95_CLK_GPU>, <&scmi_clk IMX95_CLK_GPUAPB>;
|
||||
clock-names = "core", "coregroup";
|
||||
interrupts = <GIC_SPI 289 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 290 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 288 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "job", "mmu", "gpu";
|
||||
operating-points-v2 = <&gpu_opp_table>;
|
||||
power-domains = <&scmi_devpd IMX95_PD_GPU>;
|
||||
#cooling-cells = <2>;
|
||||
dynamic-power-coefficient = <1013>;
|
||||
};
|
||||
|
||||
ddr-pmu@4e090dc0 {
|
||||
compatible = "fsl,imx95-ddr-pmu", "fsl,imx93-ddr-pmu";
|
||||
reg = <0x0 0x4e090dc0 0x0 0x200>;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ menuconfig DRM_ACCEL
|
|||
and debugfs).
|
||||
|
||||
source "drivers/accel/amdxdna/Kconfig"
|
||||
source "drivers/accel/ethosu/Kconfig"
|
||||
source "drivers/accel/habanalabs/Kconfig"
|
||||
source "drivers/accel/ivpu/Kconfig"
|
||||
source "drivers/accel/qaic/Kconfig"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/
|
||||
obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/
|
||||
obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/
|
||||
obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/
|
||||
obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ amdxdna-y := \
|
|||
amdxdna_mailbox.o \
|
||||
amdxdna_mailbox_helper.o \
|
||||
amdxdna_pci_drv.o \
|
||||
amdxdna_pm.o \
|
||||
amdxdna_sysfs.o \
|
||||
amdxdna_ubuf.o \
|
||||
npu1_regs.o \
|
||||
|
|
|
|||
|
|
@ -1,2 +1 @@
|
|||
- Add debugfs support
|
||||
- Add debug BO support
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "amdxdna_gem.h"
|
||||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
#include "amdxdna_pm.h"
|
||||
|
||||
static bool force_cmdlist;
|
||||
module_param(force_cmdlist, bool, 0600);
|
||||
|
|
@ -88,7 +89,7 @@ static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hw
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = aie2_config_cu(hwctx);
|
||||
ret = aie2_config_cu(hwctx, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Config cu failed, ret %d", ret);
|
||||
goto out;
|
||||
|
|
@ -167,14 +168,11 @@ static int aie2_hwctx_resume_cb(struct amdxdna_hwctx *hwctx, void *arg)
|
|||
|
||||
int aie2_hwctx_resume(struct amdxdna_client *client)
|
||||
{
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
|
||||
/*
|
||||
* The resume path cannot guarantee that mailbox channel can be
|
||||
* regenerated. If this happen, when submit message to this
|
||||
* mailbox channel, error will return.
|
||||
*/
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
return amdxdna_hwctx_walk(client, NULL, aie2_hwctx_resume_cb);
|
||||
}
|
||||
|
||||
|
|
@ -184,12 +182,13 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
|
|||
struct dma_fence *fence = job->fence;
|
||||
|
||||
trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
|
||||
|
||||
amdxdna_pm_suspend_put(job->hwctx->client->xdna);
|
||||
job->hwctx->priv->completed++;
|
||||
dma_fence_signal(fence);
|
||||
|
||||
up(&job->hwctx->priv->job_sem);
|
||||
job->job_done = true;
|
||||
dma_fence_put(fence);
|
||||
mmput_async(job->mm);
|
||||
aie2_job_put(job);
|
||||
}
|
||||
|
|
@ -204,10 +203,13 @@ aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size)
|
|||
|
||||
cmd_abo = job->cmd_bo;
|
||||
|
||||
if (unlikely(!data))
|
||||
if (unlikely(job->job_timeout)) {
|
||||
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_TIMEOUT);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(size != sizeof(u32))) {
|
||||
if (unlikely(!data) || unlikely(size != sizeof(u32))) {
|
||||
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
|
|
@ -226,11 +228,10 @@ aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size)
|
|||
}
|
||||
|
||||
static int
|
||||
aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
|
||||
aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size)
|
||||
{
|
||||
struct amdxdna_sched_job *job = handle;
|
||||
int ret = 0;
|
||||
u32 status;
|
||||
|
||||
if (unlikely(!data))
|
||||
goto out;
|
||||
|
|
@ -240,8 +241,7 @@ aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
|
|||
goto out;
|
||||
}
|
||||
|
||||
status = readl(data);
|
||||
XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
|
||||
job->drv_cmd->result = readl(data);
|
||||
|
||||
out:
|
||||
aie2_sched_notify(job);
|
||||
|
|
@ -260,6 +260,13 @@ aie2_sched_cmdlist_resp_handler(void *handle, void __iomem *data, size_t size)
|
|||
int ret = 0;
|
||||
|
||||
cmd_abo = job->cmd_bo;
|
||||
|
||||
if (unlikely(job->job_timeout)) {
|
||||
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_TIMEOUT);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(!data) || unlikely(size != sizeof(u32) * 3)) {
|
||||
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_ABORT);
|
||||
ret = -EINVAL;
|
||||
|
|
@ -314,8 +321,18 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
|
|||
kref_get(&job->refcnt);
|
||||
fence = dma_fence_get(job->fence);
|
||||
|
||||
if (unlikely(!cmd_abo)) {
|
||||
ret = aie2_sync_bo(hwctx, job, aie2_sched_nocmd_resp_handler);
|
||||
if (job->drv_cmd) {
|
||||
switch (job->drv_cmd->opcode) {
|
||||
case SYNC_DEBUG_BO:
|
||||
ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
|
||||
break;
|
||||
case ATTACH_DEBUG_BO:
|
||||
ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +379,7 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
|
|||
|
||||
xdna = hwctx->client->xdna;
|
||||
trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq);
|
||||
job->job_timeout = true;
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
aie2_hwctx_stop(xdna, hwctx, sched_job);
|
||||
|
||||
|
|
@ -531,13 +549,12 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
|||
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
|
||||
.credit_limit = HWCTX_MAX_CMDS,
|
||||
.timeout = msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
|
||||
.name = hwctx->name,
|
||||
.name = "amdxdna_js",
|
||||
.dev = xdna->ddev.dev,
|
||||
};
|
||||
struct drm_gpu_scheduler *sched;
|
||||
struct amdxdna_hwctx_priv *priv;
|
||||
struct amdxdna_gem_obj *heap;
|
||||
struct amdxdna_dev_hdl *ndev;
|
||||
int i, ret;
|
||||
|
||||
priv = kzalloc(sizeof(*hwctx->priv), GFP_KERNEL);
|
||||
|
|
@ -610,10 +627,14 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
|||
goto free_entity;
|
||||
}
|
||||
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto free_col_list;
|
||||
|
||||
ret = aie2_alloc_resource(hwctx);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Alloc hw resource failed, ret %d", ret);
|
||||
goto free_col_list;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
ret = aie2_map_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
|
||||
|
|
@ -628,10 +649,9 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
|||
XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret);
|
||||
goto release_resource;
|
||||
}
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
|
||||
hwctx->status = HWCTX_STAT_INIT;
|
||||
ndev = xdna->dev_handle;
|
||||
ndev->hwctx_num++;
|
||||
init_waitqueue_head(&priv->job_free_wq);
|
||||
|
||||
XDNA_DBG(xdna, "hwctx %s init completed", hwctx->name);
|
||||
|
|
@ -640,6 +660,8 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
|||
|
||||
release_resource:
|
||||
aie2_release_resource(hwctx);
|
||||
suspend_put:
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
free_col_list:
|
||||
kfree(hwctx->col_list);
|
||||
free_entity:
|
||||
|
|
@ -662,26 +684,25 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
|
|||
|
||||
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
|
||||
{
|
||||
struct amdxdna_dev_hdl *ndev;
|
||||
struct amdxdna_dev *xdna;
|
||||
int idx;
|
||||
|
||||
xdna = hwctx->client->xdna;
|
||||
ndev = xdna->dev_handle;
|
||||
ndev->hwctx_num--;
|
||||
|
||||
XDNA_DBG(xdna, "%s sequence number %lld", hwctx->name, hwctx->priv->seq);
|
||||
drm_sched_entity_destroy(&hwctx->priv->entity);
|
||||
|
||||
aie2_hwctx_wait_for_idle(hwctx);
|
||||
|
||||
/* Request fw to destroy hwctx and cancel the rest pending requests */
|
||||
aie2_release_resource(hwctx);
|
||||
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
drm_sched_entity_destroy(&hwctx->priv->entity);
|
||||
|
||||
/* Wait for all submitted jobs to be completed or canceled */
|
||||
wait_event(hwctx->priv->job_free_wq,
|
||||
atomic64_read(&hwctx->job_submit_cnt) ==
|
||||
atomic64_read(&hwctx->job_free_cnt));
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
|
||||
drm_sched_fini(&hwctx->priv->sched);
|
||||
aie2_ctx_syncobj_destroy(hwctx);
|
||||
|
|
@ -697,6 +718,14 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
|
|||
kfree(hwctx->cus);
|
||||
}
|
||||
|
||||
static int aie2_config_cu_resp_handler(void *handle, void __iomem *data, size_t size)
|
||||
{
|
||||
struct amdxdna_hwctx *hwctx = handle;
|
||||
|
||||
amdxdna_pm_suspend_put(hwctx->client->xdna);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size)
|
||||
{
|
||||
struct amdxdna_hwctx_param_config_cu *config = buf;
|
||||
|
|
@ -728,10 +757,14 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size
|
|||
if (!hwctx->cus)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = aie2_config_cu(hwctx);
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto free_cus;
|
||||
|
||||
ret = aie2_config_cu(hwctx, aie2_config_cu_resp_handler);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Config CU to firmware failed, ret %d", ret);
|
||||
goto free_cus;
|
||||
goto pm_suspend_put;
|
||||
}
|
||||
|
||||
wmb(); /* To avoid locking in command submit when check status */
|
||||
|
|
@ -739,12 +772,82 @@ static int aie2_hwctx_cu_config(struct amdxdna_hwctx *hwctx, void *buf, u32 size
|
|||
|
||||
return 0;
|
||||
|
||||
pm_suspend_put:
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
free_cus:
|
||||
kfree(hwctx->cus);
|
||||
hwctx->cus = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void aie2_cmd_wait(struct amdxdna_hwctx *hwctx, u64 seq)
|
||||
{
|
||||
struct dma_fence *out_fence = aie2_cmd_get_out_fence(hwctx, seq);
|
||||
|
||||
if (!out_fence) {
|
||||
XDNA_ERR(hwctx->client->xdna, "Failed to get fence");
|
||||
return;
|
||||
}
|
||||
|
||||
dma_fence_wait_timeout(out_fence, false, MAX_SCHEDULE_TIMEOUT);
|
||||
dma_fence_put(out_fence);
|
||||
}
|
||||
|
||||
static int aie2_hwctx_cfg_debug_bo(struct amdxdna_hwctx *hwctx, u32 bo_hdl,
|
||||
bool attach)
|
||||
{
|
||||
struct amdxdna_client *client = hwctx->client;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_drv_cmd cmd = { 0 };
|
||||
struct amdxdna_gem_obj *abo;
|
||||
u64 seq;
|
||||
int ret;
|
||||
|
||||
abo = amdxdna_gem_get_obj(client, bo_hdl, AMDXDNA_BO_DEV);
|
||||
if (!abo) {
|
||||
XDNA_ERR(xdna, "Get bo %d failed", bo_hdl);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (attach) {
|
||||
if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE) {
|
||||
ret = -EBUSY;
|
||||
goto put_obj;
|
||||
}
|
||||
cmd.opcode = ATTACH_DEBUG_BO;
|
||||
} else {
|
||||
if (abo->assigned_hwctx != hwctx->id) {
|
||||
ret = -EINVAL;
|
||||
goto put_obj;
|
||||
}
|
||||
cmd.opcode = DETACH_DEBUG_BO;
|
||||
}
|
||||
|
||||
ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
|
||||
&bo_hdl, 1, hwctx->id, &seq);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Submit command failed");
|
||||
goto put_obj;
|
||||
}
|
||||
|
||||
aie2_cmd_wait(hwctx, seq);
|
||||
if (cmd.result) {
|
||||
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
|
||||
goto put_obj;
|
||||
}
|
||||
|
||||
if (attach)
|
||||
abo->assigned_hwctx = hwctx->id;
|
||||
else
|
||||
abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE;
|
||||
|
||||
XDNA_DBG(xdna, "Config debug BO %d to %s", bo_hdl, hwctx->name);
|
||||
|
||||
put_obj:
|
||||
amdxdna_gem_put_obj(abo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size)
|
||||
{
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
|
|
@ -754,14 +857,40 @@ int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *bu
|
|||
case DRM_AMDXDNA_HWCTX_CONFIG_CU:
|
||||
return aie2_hwctx_cu_config(hwctx, buf, size);
|
||||
case DRM_AMDXDNA_HWCTX_ASSIGN_DBG_BUF:
|
||||
return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, true);
|
||||
case DRM_AMDXDNA_HWCTX_REMOVE_DBG_BUF:
|
||||
return -EOPNOTSUPP;
|
||||
return aie2_hwctx_cfg_debug_bo(hwctx, (u32)value, false);
|
||||
default:
|
||||
XDNA_DBG(xdna, "Not supported type %d", type);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl)
|
||||
{
|
||||
struct amdxdna_client *client = hwctx->client;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_drv_cmd cmd = { 0 };
|
||||
u64 seq;
|
||||
int ret;
|
||||
|
||||
cmd.opcode = SYNC_DEBUG_BO;
|
||||
ret = amdxdna_cmd_submit(client, &cmd, AMDXDNA_INVALID_BO_HANDLE,
|
||||
&debug_bo_hdl, 1, hwctx->id, &seq);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Submit command failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
aie2_cmd_wait(hwctx, seq);
|
||||
if (cmd.result) {
|
||||
XDNA_ERR(xdna, "Response failure 0x%x", cmd.result);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_populate_range(struct amdxdna_gem_obj *abo)
|
||||
{
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
|
||||
|
|
@ -862,11 +991,15 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
goto free_chain;
|
||||
}
|
||||
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto cleanup_job;
|
||||
|
||||
retry:
|
||||
ret = drm_gem_lock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
|
||||
if (ret) {
|
||||
XDNA_WARN(xdna, "Failed to lock BOs, ret %d", ret);
|
||||
goto cleanup_job;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
for (i = 0; i < job->bo_cnt; i++) {
|
||||
|
|
@ -874,7 +1007,7 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
if (ret) {
|
||||
XDNA_WARN(xdna, "Failed to reserve fences %d", ret);
|
||||
drm_gem_unlock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
|
||||
goto cleanup_job;
|
||||
goto suspend_put;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -889,12 +1022,12 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
|
||||
} else if (time_after(jiffies, timeout)) {
|
||||
ret = -ETIME;
|
||||
goto cleanup_job;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
ret = aie2_populate_range(abo);
|
||||
if (ret)
|
||||
goto cleanup_job;
|
||||
goto suspend_put;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
|
@ -920,6 +1053,8 @@ int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
|
||||
return 0;
|
||||
|
||||
suspend_put:
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
cleanup_job:
|
||||
drm_sched_job_cleanup(&job->base);
|
||||
free_chain:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "aie2_msg_priv.h"
|
||||
#include "aie2_pci.h"
|
||||
#include "amdxdna_error.h"
|
||||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
|
|
@ -46,6 +47,7 @@ enum aie_module_type {
|
|||
AIE_MEM_MOD = 0,
|
||||
AIE_CORE_MOD,
|
||||
AIE_PL_MOD,
|
||||
AIE_UNKNOWN_MOD,
|
||||
};
|
||||
|
||||
enum aie_error_category {
|
||||
|
|
@ -143,6 +145,31 @@ static const struct aie_event_category aie_ml_shim_tile_event_cat[] = {
|
|||
EVENT_CATEGORY(74U, AIE_ERROR_LOCK),
|
||||
};
|
||||
|
||||
static const enum amdxdna_error_num aie_cat_err_num_map[] = {
|
||||
[AIE_ERROR_SATURATION] = AMDXDNA_ERROR_NUM_AIE_SATURATION,
|
||||
[AIE_ERROR_FP] = AMDXDNA_ERROR_NUM_AIE_FP,
|
||||
[AIE_ERROR_STREAM] = AMDXDNA_ERROR_NUM_AIE_STREAM,
|
||||
[AIE_ERROR_ACCESS] = AMDXDNA_ERROR_NUM_AIE_ACCESS,
|
||||
[AIE_ERROR_BUS] = AMDXDNA_ERROR_NUM_AIE_BUS,
|
||||
[AIE_ERROR_INSTRUCTION] = AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
|
||||
[AIE_ERROR_ECC] = AMDXDNA_ERROR_NUM_AIE_ECC,
|
||||
[AIE_ERROR_LOCK] = AMDXDNA_ERROR_NUM_AIE_LOCK,
|
||||
[AIE_ERROR_DMA] = AMDXDNA_ERROR_NUM_AIE_DMA,
|
||||
[AIE_ERROR_MEM_PARITY] = AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
|
||||
[AIE_ERROR_UNKNOWN] = AMDXDNA_ERROR_NUM_UNKNOWN,
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(aie_cat_err_num_map) == AIE_ERROR_UNKNOWN + 1);
|
||||
|
||||
static const enum amdxdna_error_module aie_err_mod_map[] = {
|
||||
[AIE_MEM_MOD] = AMDXDNA_ERROR_MODULE_AIE_MEMORY,
|
||||
[AIE_CORE_MOD] = AMDXDNA_ERROR_MODULE_AIE_CORE,
|
||||
[AIE_PL_MOD] = AMDXDNA_ERROR_MODULE_AIE_PL,
|
||||
[AIE_UNKNOWN_MOD] = AMDXDNA_ERROR_MODULE_UNKNOWN,
|
||||
};
|
||||
|
||||
static_assert(ARRAY_SIZE(aie_err_mod_map) == AIE_UNKNOWN_MOD + 1);
|
||||
|
||||
static enum aie_error_category
|
||||
aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
|
||||
{
|
||||
|
|
@ -176,12 +203,40 @@ aie_get_error_category(u8 row, u8 event_id, enum aie_module_type mod_type)
|
|||
if (event_id != lut[i].event_id)
|
||||
continue;
|
||||
|
||||
if (lut[i].category > AIE_ERROR_UNKNOWN)
|
||||
return AIE_ERROR_UNKNOWN;
|
||||
|
||||
return lut[i].category;
|
||||
}
|
||||
|
||||
return AIE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
static void aie2_update_last_async_error(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
|
||||
{
|
||||
struct aie_error *errs = err_info;
|
||||
enum amdxdna_error_module err_mod;
|
||||
enum aie_error_category aie_err;
|
||||
enum amdxdna_error_num err_num;
|
||||
struct aie_error *last_err;
|
||||
|
||||
last_err = &errs[num_err - 1];
|
||||
if (last_err->mod_type >= AIE_UNKNOWN_MOD) {
|
||||
err_num = aie_cat_err_num_map[AIE_ERROR_UNKNOWN];
|
||||
err_mod = aie_err_mod_map[AIE_UNKNOWN_MOD];
|
||||
} else {
|
||||
aie_err = aie_get_error_category(last_err->row,
|
||||
last_err->event_id,
|
||||
last_err->mod_type);
|
||||
err_num = aie_cat_err_num_map[aie_err];
|
||||
err_mod = aie_err_mod_map[last_err->mod_type];
|
||||
}
|
||||
|
||||
ndev->last_async_err.err_code = AMDXDNA_ERROR_ENCODE(err_num, err_mod);
|
||||
ndev->last_async_err.ts_us = ktime_to_us(ktime_get_real());
|
||||
ndev->last_async_err.ex_err_code = AMDXDNA_EXTRA_ERR_ENCODE(last_err->row, last_err->col);
|
||||
}
|
||||
|
||||
static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u32 num_err)
|
||||
{
|
||||
struct aie_error *errs = err_info;
|
||||
|
|
@ -264,29 +319,14 @@ static void aie2_error_worker(struct work_struct *err_work)
|
|||
}
|
||||
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
aie2_update_last_async_error(e->ndev, info->payload, info->err_cnt);
|
||||
|
||||
/* Re-sent this event to firmware */
|
||||
if (aie2_error_event_send(e))
|
||||
XDNA_WARN(xdna, "Unable to register async event");
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
}
|
||||
|
||||
int aie2_error_async_events_send(struct amdxdna_dev_hdl *ndev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
struct async_event *e;
|
||||
int i, ret;
|
||||
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
for (i = 0; i < ndev->async_events->event_cnt; i++) {
|
||||
e = &ndev->async_events->event[i];
|
||||
ret = aie2_error_event_send(e);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
|
|
@ -341,6 +381,10 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
|
|||
e->size = ASYNC_BUF_SIZE;
|
||||
e->resp.status = MAX_AIE2_STATUS_CODE;
|
||||
INIT_WORK(&e->work, aie2_error_worker);
|
||||
|
||||
ret = aie2_error_event_send(e);
|
||||
if (ret)
|
||||
goto free_wq;
|
||||
}
|
||||
|
||||
ndev->async_events = events;
|
||||
|
|
@ -349,6 +393,8 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
|
|||
events->event_cnt, events->size);
|
||||
return 0;
|
||||
|
||||
free_wq:
|
||||
destroy_workqueue(events->wq);
|
||||
free_buf:
|
||||
dma_free_noncoherent(xdna->ddev.dev, events->size, events->buf,
|
||||
events->addr, DMA_FROM_DEVICE);
|
||||
|
|
@ -356,3 +402,18 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
|
|||
kfree(events);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args)
|
||||
{
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
|
||||
args->num_element = 1;
|
||||
args->element_size = sizeof(ndev->last_async_err);
|
||||
if (copy_to_user(u64_to_user_ptr(args->buffer),
|
||||
&ndev->last_async_err, args->element_size))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#define DECLARE_AIE2_MSG(name, op) \
|
||||
DECLARE_XDNA_MSG_COMMON(name, op, MAX_AIE2_STATUS_CODE)
|
||||
|
||||
#define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops)
|
||||
|
||||
static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
|
||||
struct xdna_mailbox_msg *msg)
|
||||
{
|
||||
|
|
@ -37,7 +39,7 @@ static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
|
|||
if (!ndev->mgmt_chann)
|
||||
return -ENODEV;
|
||||
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
drm_WARN_ON(&xdna->ddev, xdna->rpm_on && !mutex_is_locked(&xdna->dev_lock));
|
||||
ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg);
|
||||
if (ret == -ETIME) {
|
||||
xdna_mailbox_stop_channel(ndev->mgmt_chann);
|
||||
|
|
@ -45,7 +47,7 @@ static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
|
|||
ndev->mgmt_chann = NULL;
|
||||
}
|
||||
|
||||
if (!ret && *hdl->data != AIE2_STATUS_SUCCESS) {
|
||||
if (!ret && *hdl->status != AIE2_STATUS_SUCCESS) {
|
||||
XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
|
||||
msg->opcode, *hdl->data);
|
||||
ret = -EINVAL;
|
||||
|
|
@ -208,6 +210,14 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
|
|||
hwctx->fw_ctx_id = resp.context_id;
|
||||
WARN_ONCE(hwctx->fw_ctx_id == -1, "Unexpected context id");
|
||||
|
||||
if (ndev->force_preempt_enabled) {
|
||||
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FORCE_PREEMPT, &hwctx->fw_ctx_id);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "failed to enable force preempt %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
cq_pair = &resp.cq_pair[0];
|
||||
x2i.mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.head_addr);
|
||||
x2i.mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, cq_pair->x2i_q.tail_addr);
|
||||
|
|
@ -233,6 +243,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
|
|||
ret = -EINVAL;
|
||||
goto out_destroy_context;
|
||||
}
|
||||
ndev->hwctx_num++;
|
||||
|
||||
XDNA_DBG(xdna, "%s mailbox channel irq: %d, msix_id: %d",
|
||||
hwctx->name, ret, resp.msix_id);
|
||||
|
|
@ -267,6 +278,7 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc
|
|||
hwctx->fw_ctx_id);
|
||||
hwctx->priv->mbox_chann = NULL;
|
||||
hwctx->fw_ctx_id = -1;
|
||||
ndev->hwctx_num--;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -332,11 +344,6 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (resp.status != AIE2_STATUS_SUCCESS) {
|
||||
XDNA_ERR(xdna, "Query NPU status failed, status 0x%x", resp.status);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
XDNA_DBG(xdna, "Query NPU status completed");
|
||||
|
||||
if (size < resp.size) {
|
||||
|
|
@ -358,6 +365,55 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
|
||||
char __user *buf, u32 size,
|
||||
struct amdxdna_drm_query_telemetry_header *header)
|
||||
{
|
||||
DECLARE_AIE2_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
dma_addr_t dma_addr;
|
||||
u8 *addr;
|
||||
int ret;
|
||||
|
||||
if (header->type >= MAX_TELEMETRY_TYPE)
|
||||
return -EINVAL;
|
||||
|
||||
addr = dma_alloc_noncoherent(xdna->ddev.dev, size, &dma_addr,
|
||||
DMA_FROM_DEVICE, GFP_KERNEL);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
req.buf_addr = dma_addr;
|
||||
req.buf_size = size;
|
||||
req.type = header->type;
|
||||
|
||||
drm_clflush_virt_range(addr, size); /* device can access */
|
||||
ret = aie2_send_mgmt_msg_wait(ndev, &msg);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Query telemetry failed, status %d", ret);
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
if (size < resp.size) {
|
||||
ret = -EINVAL;
|
||||
XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
if (copy_to_user(buf, addr, resp.size)) {
|
||||
ret = -EFAULT;
|
||||
XDNA_ERR(xdna, "Failed to copy telemetry to user space");
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
header->major = resp.major;
|
||||
header->minor = resp.minor;
|
||||
|
||||
free_buf:
|
||||
dma_free_noncoherent(xdna->ddev.dev, size, addr, dma_addr, DMA_FROM_DEVICE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
|
||||
void *handle, int (*cb)(void*, void __iomem *, size_t))
|
||||
{
|
||||
|
|
@ -377,15 +433,17 @@ int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr,
|
|||
return xdna_mailbox_send_msg(ndev->mgmt_chann, &msg, TX_TIMEOUT);
|
||||
}
|
||||
|
||||
int aie2_config_cu(struct amdxdna_hwctx *hwctx)
|
||||
int aie2_config_cu(struct amdxdna_hwctx *hwctx,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t))
|
||||
{
|
||||
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
u32 shift = xdna->dev_info->dev_mem_buf_shift;
|
||||
DECLARE_AIE2_MSG(config_cu, MSG_OP_CONFIG_CU);
|
||||
struct config_cu_req req = { 0 };
|
||||
struct xdna_mailbox_msg msg;
|
||||
struct drm_gem_object *gobj;
|
||||
struct amdxdna_gem_obj *abo;
|
||||
int ret, i;
|
||||
int i;
|
||||
|
||||
if (!chann)
|
||||
return -ENODEV;
|
||||
|
|
@ -423,80 +481,411 @@ int aie2_config_cu(struct amdxdna_hwctx *hwctx)
|
|||
}
|
||||
req.num_cus = hwctx->cus->num_cus;
|
||||
|
||||
ret = xdna_send_msg_wait(xdna, chann, &msg);
|
||||
if (ret == -ETIME)
|
||||
aie2_destroy_context(xdna->dev_handle, hwctx);
|
||||
msg.send_data = (u8 *)&req;
|
||||
msg.send_size = sizeof(req);
|
||||
msg.handle = hwctx;
|
||||
msg.opcode = MSG_OP_CONFIG_CU;
|
||||
msg.notify_cb = notify_cb;
|
||||
return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
|
||||
}
|
||||
|
||||
if (resp.status == AIE2_STATUS_SUCCESS) {
|
||||
XDNA_DBG(xdna, "Configure %d CUs, ret %d", req.num_cus, ret);
|
||||
return 0;
|
||||
static int aie2_init_exec_cu_req(struct amdxdna_gem_obj *cmd_bo, void *req,
|
||||
size_t *size, u32 *msg_op)
|
||||
{
|
||||
struct execute_buffer_req *cu_req = req;
|
||||
u32 cmd_len;
|
||||
void *cmd;
|
||||
|
||||
cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
if (cmd_len > sizeof(cu_req->payload))
|
||||
return -EINVAL;
|
||||
|
||||
cu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (cu_req->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(cu_req->payload, cmd, cmd_len);
|
||||
|
||||
*size = sizeof(*cu_req);
|
||||
*msg_op = MSG_OP_EXECUTE_BUFFER_CF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_init_exec_dpu_req(struct amdxdna_gem_obj *cmd_bo, void *req,
|
||||
size_t *size, u32 *msg_op)
|
||||
{
|
||||
struct exec_dpu_req *dpu_req = req;
|
||||
struct amdxdna_cmd_start_npu *sn;
|
||||
u32 cmd_len;
|
||||
|
||||
sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
if (cmd_len - sizeof(*sn) > sizeof(dpu_req->payload))
|
||||
return -EINVAL;
|
||||
|
||||
dpu_req->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (dpu_req->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
dpu_req->inst_buf_addr = sn->buffer;
|
||||
dpu_req->inst_size = sn->buffer_size;
|
||||
dpu_req->inst_prop_cnt = sn->prop_count;
|
||||
memcpy(dpu_req->payload, sn->prop_args, cmd_len - sizeof(*sn));
|
||||
|
||||
*size = sizeof(*dpu_req);
|
||||
*msg_op = MSG_OP_EXEC_DPU;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aie2_init_exec_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt)
|
||||
{
|
||||
struct cmd_chain_req *chain_req = req;
|
||||
|
||||
chain_req->buf_addr = slot_addr;
|
||||
chain_req->buf_size = size;
|
||||
chain_req->count = cmd_cnt;
|
||||
}
|
||||
|
||||
static void aie2_init_npu_chain_req(void *req, u64 slot_addr, size_t size, u32 cmd_cnt)
|
||||
{
|
||||
struct cmd_chain_npu_req *npu_chain_req = req;
|
||||
|
||||
npu_chain_req->flags = 0;
|
||||
npu_chain_req->reserved = 0;
|
||||
npu_chain_req->buf_addr = slot_addr;
|
||||
npu_chain_req->buf_size = size;
|
||||
npu_chain_req->count = cmd_cnt;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_execbuf_cf *cf_slot = slot;
|
||||
u32 cmd_len;
|
||||
void *cmd;
|
||||
|
||||
cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
if (*size < sizeof(*cf_slot) + cmd_len)
|
||||
return -EINVAL;
|
||||
|
||||
cf_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (cf_slot->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
cf_slot->arg_cnt = cmd_len / sizeof(u32);
|
||||
memcpy(cf_slot->args, cmd, cmd_len);
|
||||
/* Accurate slot size to hint firmware to do necessary copy */
|
||||
*size = sizeof(*cf_slot) + cmd_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_dpu *dpu_slot = slot;
|
||||
struct amdxdna_cmd_start_npu *sn;
|
||||
u32 cmd_len;
|
||||
u32 arg_sz;
|
||||
|
||||
sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
arg_sz = cmd_len - sizeof(*sn);
|
||||
if (cmd_len < sizeof(*sn) || arg_sz > MAX_DPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (*size < sizeof(*dpu_slot) + arg_sz)
|
||||
return -EINVAL;
|
||||
|
||||
dpu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (dpu_slot->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
dpu_slot->inst_buf_addr = sn->buffer;
|
||||
dpu_slot->inst_size = sn->buffer_size;
|
||||
dpu_slot->inst_prop_cnt = sn->prop_count;
|
||||
dpu_slot->arg_cnt = arg_sz / sizeof(u32);
|
||||
memcpy(dpu_slot->args, sn->prop_args, arg_sz);
|
||||
|
||||
/* Accurate slot size to hint firmware to do necessary copy */
|
||||
*size = sizeof(*dpu_slot) + arg_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_cmdlist_unsupp(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static u32 aie2_get_chain_msg_op(u32 cmd_op)
|
||||
{
|
||||
switch (cmd_op) {
|
||||
case ERT_START_CU:
|
||||
return MSG_OP_CHAIN_EXEC_BUFFER_CF;
|
||||
case ERT_START_NPU:
|
||||
return MSG_OP_CHAIN_EXEC_DPU;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return MSG_OP_MAX_OPCODE;
|
||||
}
|
||||
|
||||
static struct aie2_exec_msg_ops legacy_exec_message_ops = {
|
||||
.init_cu_req = aie2_init_exec_cu_req,
|
||||
.init_dpu_req = aie2_init_exec_dpu_req,
|
||||
.init_chain_req = aie2_init_exec_chain_req,
|
||||
.fill_cf_slot = aie2_cmdlist_fill_cf,
|
||||
.fill_dpu_slot = aie2_cmdlist_fill_dpu,
|
||||
.fill_preempt_slot = aie2_cmdlist_unsupp,
|
||||
.fill_elf_slot = aie2_cmdlist_unsupp,
|
||||
.get_chain_msg_op = aie2_get_chain_msg_op,
|
||||
};
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_npu_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_npu *npu_slot = slot;
|
||||
u32 cmd_len;
|
||||
void *cmd;
|
||||
|
||||
cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
if (*size < sizeof(*npu_slot) + cmd_len)
|
||||
return -EINVAL;
|
||||
|
||||
npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (npu_slot->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
memset(npu_slot, 0, sizeof(*npu_slot));
|
||||
npu_slot->type = EXEC_NPU_TYPE_NON_ELF;
|
||||
npu_slot->arg_cnt = cmd_len / sizeof(u32);
|
||||
memcpy(npu_slot->args, cmd, cmd_len);
|
||||
|
||||
*size = sizeof(*npu_slot) + cmd_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_npu *npu_slot = slot;
|
||||
struct amdxdna_cmd_start_npu *sn;
|
||||
u32 cmd_len;
|
||||
u32 arg_sz;
|
||||
|
||||
sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
arg_sz = cmd_len - sizeof(*sn);
|
||||
if (cmd_len < sizeof(*sn) || arg_sz > MAX_NPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (*size < sizeof(*npu_slot) + arg_sz)
|
||||
return -EINVAL;
|
||||
|
||||
npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (npu_slot->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
memset(npu_slot, 0, sizeof(*npu_slot));
|
||||
npu_slot->type = EXEC_NPU_TYPE_PARTIAL_ELF;
|
||||
npu_slot->inst_buf_addr = sn->buffer;
|
||||
npu_slot->inst_size = sn->buffer_size;
|
||||
npu_slot->inst_prop_cnt = sn->prop_count;
|
||||
npu_slot->arg_cnt = arg_sz / sizeof(u32);
|
||||
memcpy(npu_slot->args, sn->prop_args, arg_sz);
|
||||
|
||||
*size = sizeof(*npu_slot) + arg_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_npu *npu_slot = slot;
|
||||
struct amdxdna_cmd_preempt_data *pd;
|
||||
u32 cmd_len;
|
||||
u32 arg_sz;
|
||||
|
||||
pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
arg_sz = cmd_len - sizeof(*pd);
|
||||
if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (*size < sizeof(*npu_slot) + arg_sz)
|
||||
return -EINVAL;
|
||||
|
||||
npu_slot->cu_idx = amdxdna_cmd_get_cu_idx(cmd_bo);
|
||||
if (npu_slot->cu_idx == INVALID_CU_IDX)
|
||||
return -EINVAL;
|
||||
|
||||
memset(npu_slot, 0, sizeof(*npu_slot));
|
||||
npu_slot->type = EXEC_NPU_TYPE_PREEMPT;
|
||||
npu_slot->inst_buf_addr = pd->inst_buf;
|
||||
npu_slot->save_buf_addr = pd->save_buf;
|
||||
npu_slot->restore_buf_addr = pd->restore_buf;
|
||||
npu_slot->inst_size = pd->inst_size;
|
||||
npu_slot->save_size = pd->save_size;
|
||||
npu_slot->restore_size = pd->restore_size;
|
||||
npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
|
||||
npu_slot->arg_cnt = arg_sz / sizeof(u32);
|
||||
memcpy(npu_slot->args, pd->prop_args, arg_sz);
|
||||
|
||||
*size = sizeof(*npu_slot) + arg_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size)
|
||||
{
|
||||
struct cmd_chain_slot_npu *npu_slot = slot;
|
||||
struct amdxdna_cmd_preempt_data *pd;
|
||||
u32 cmd_len;
|
||||
u32 arg_sz;
|
||||
|
||||
pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len);
|
||||
arg_sz = cmd_len - sizeof(*pd);
|
||||
if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (*size < sizeof(*npu_slot) + arg_sz)
|
||||
return -EINVAL;
|
||||
|
||||
memset(npu_slot, 0, sizeof(*npu_slot));
|
||||
npu_slot->type = EXEC_NPU_TYPE_ELF;
|
||||
npu_slot->inst_buf_addr = pd->inst_buf;
|
||||
npu_slot->save_buf_addr = pd->save_buf;
|
||||
npu_slot->restore_buf_addr = pd->restore_buf;
|
||||
npu_slot->inst_size = pd->inst_size;
|
||||
npu_slot->save_size = pd->save_size;
|
||||
npu_slot->restore_size = pd->restore_size;
|
||||
npu_slot->inst_prop_cnt = pd->inst_prop_cnt;
|
||||
npu_slot->arg_cnt = 1;
|
||||
npu_slot->args[0] = AIE2_EXEC_BUFFER_KERNEL_OP_TXN;
|
||||
|
||||
*size = struct_size(npu_slot, args, npu_slot->arg_cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 aie2_get_npu_chain_msg_op(u32 cmd_op)
|
||||
{
|
||||
return MSG_OP_CHAIN_EXEC_NPU;
|
||||
}
|
||||
|
||||
static struct aie2_exec_msg_ops npu_exec_message_ops = {
|
||||
.init_cu_req = aie2_init_exec_cu_req,
|
||||
.init_dpu_req = aie2_init_exec_dpu_req,
|
||||
.init_chain_req = aie2_init_npu_chain_req,
|
||||
.fill_cf_slot = aie2_cmdlist_fill_npu_cf,
|
||||
.fill_dpu_slot = aie2_cmdlist_fill_npu_dpu,
|
||||
.fill_preempt_slot = aie2_cmdlist_fill_npu_preempt,
|
||||
.fill_elf_slot = aie2_cmdlist_fill_npu_elf,
|
||||
.get_chain_msg_op = aie2_get_npu_chain_msg_op,
|
||||
};
|
||||
|
||||
static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo,
|
||||
size_t *size, u32 *msg_op)
|
||||
{
|
||||
struct amdxdna_dev *xdna = cmd_abo->client->xdna;
|
||||
int ret;
|
||||
u32 op;
|
||||
|
||||
|
||||
op = amdxdna_cmd_get_op(cmd_abo);
|
||||
switch (op) {
|
||||
case ERT_START_CU:
|
||||
ret = EXEC_MSG_OPS(xdna)->init_cu_req(cmd_abo, req, size, msg_op);
|
||||
if (ret) {
|
||||
XDNA_DBG(xdna, "Init CU req failed ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case ERT_START_NPU:
|
||||
ret = EXEC_MSG_OPS(xdna)->init_dpu_req(cmd_abo, req, size, msg_op);
|
||||
if (ret) {
|
||||
XDNA_DBG(xdna, "Init DPU req failed ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Unsupported op %d", op);
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
XDNA_ERR(xdna, "Command opcode 0x%x failed, status 0x%x ret %d",
|
||||
msg.opcode, resp.status, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
|
||||
size_t *size, u32 *cmd_op)
|
||||
{
|
||||
struct amdxdna_dev *xdna = cmd_abo->client->xdna;
|
||||
int ret;
|
||||
u32 op;
|
||||
|
||||
op = amdxdna_cmd_get_op(cmd_abo);
|
||||
if (*cmd_op == ERT_INVALID_CMD)
|
||||
*cmd_op = op;
|
||||
else if (op != *cmd_op)
|
||||
return -EINVAL;
|
||||
|
||||
switch (op) {
|
||||
case ERT_START_CU:
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_cf_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
case ERT_START_NPU:
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
case ERT_START_NPU_PREEMPT:
|
||||
if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
|
||||
return -EOPNOTSUPP;
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
case ERT_START_NPU_PREEMPT_ELF:
|
||||
if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
|
||||
return -EOPNOTSUPP;
|
||||
ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size);
|
||||
break;
|
||||
default:
|
||||
XDNA_INFO(xdna, "Unsupported op %d", op);
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void aie2_msg_init(struct amdxdna_dev_hdl *ndev)
|
||||
{
|
||||
if (AIE2_FEATURE_ON(ndev, AIE2_NPU_COMMAND))
|
||||
ndev->exec_msg_ops = &npu_exec_message_ops;
|
||||
else
|
||||
ndev->exec_msg_ops = &legacy_exec_message_ops;
|
||||
}
|
||||
|
||||
static inline struct amdxdna_gem_obj *
|
||||
aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
|
||||
{
|
||||
int idx = get_job_idx(job->seq);
|
||||
|
||||
return job->hwctx->priv->cmd_buf[idx];
|
||||
}
|
||||
|
||||
int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t))
|
||||
{
|
||||
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
|
||||
union {
|
||||
struct execute_buffer_req ebuf;
|
||||
struct exec_dpu_req dpu;
|
||||
} req;
|
||||
struct xdna_mailbox_msg msg;
|
||||
u32 payload_len;
|
||||
void *payload;
|
||||
int cu_idx;
|
||||
union exec_req req;
|
||||
int ret;
|
||||
u32 op;
|
||||
|
||||
if (!chann)
|
||||
return -ENODEV;
|
||||
|
||||
payload = amdxdna_cmd_get_payload(cmd_abo, &payload_len);
|
||||
if (!payload) {
|
||||
XDNA_ERR(xdna, "Invalid command, cannot get payload");
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = aie2_init_exec_req(&req, cmd_abo, &msg.send_size, &msg.opcode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cu_idx = amdxdna_cmd_get_cu_idx(cmd_abo);
|
||||
if (cu_idx < 0) {
|
||||
XDNA_DBG(xdna, "Invalid cu idx");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
op = amdxdna_cmd_get_op(cmd_abo);
|
||||
switch (op) {
|
||||
case ERT_START_CU:
|
||||
if (unlikely(payload_len > sizeof(req.ebuf.payload)))
|
||||
XDNA_DBG(xdna, "Invalid ebuf payload len: %d", payload_len);
|
||||
req.ebuf.cu_idx = cu_idx;
|
||||
memcpy(req.ebuf.payload, payload, sizeof(req.ebuf.payload));
|
||||
msg.send_size = sizeof(req.ebuf);
|
||||
msg.opcode = MSG_OP_EXECUTE_BUFFER_CF;
|
||||
break;
|
||||
case ERT_START_NPU: {
|
||||
struct amdxdna_cmd_start_npu *sn = payload;
|
||||
|
||||
if (unlikely(payload_len - sizeof(*sn) > sizeof(req.dpu.payload)))
|
||||
XDNA_DBG(xdna, "Invalid dpu payload len: %d", payload_len);
|
||||
req.dpu.inst_buf_addr = sn->buffer;
|
||||
req.dpu.inst_size = sn->buffer_size;
|
||||
req.dpu.inst_prop_cnt = sn->prop_count;
|
||||
req.dpu.cu_idx = cu_idx;
|
||||
memcpy(req.dpu.payload, sn->prop_args, sizeof(req.dpu.payload));
|
||||
msg.send_size = sizeof(req.dpu);
|
||||
msg.opcode = MSG_OP_EXEC_DPU;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
XDNA_DBG(xdna, "Invalid ERT cmd op code: %d", op);
|
||||
return -EINVAL;
|
||||
}
|
||||
msg.handle = job;
|
||||
msg.notify_cb = notify_cb;
|
||||
msg.send_data = (u8 *)&req;
|
||||
|
|
@ -512,135 +901,6 @@ int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_one_slot_cf(void *cmd_buf, u32 offset,
|
||||
struct amdxdna_gem_obj *abo, u32 *size)
|
||||
{
|
||||
struct cmd_chain_slot_execbuf_cf *buf = cmd_buf + offset;
|
||||
int cu_idx = amdxdna_cmd_get_cu_idx(abo);
|
||||
u32 payload_len;
|
||||
void *payload;
|
||||
|
||||
if (cu_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
payload = amdxdna_cmd_get_payload(abo, &payload_len);
|
||||
if (!payload)
|
||||
return -EINVAL;
|
||||
|
||||
if (!slot_has_space(*buf, offset, payload_len))
|
||||
return -ENOSPC;
|
||||
|
||||
buf->cu_idx = cu_idx;
|
||||
buf->arg_cnt = payload_len / sizeof(u32);
|
||||
memcpy(buf->args, payload, payload_len);
|
||||
/* Accurate buf size to hint firmware to do necessary copy */
|
||||
*size = sizeof(*buf) + payload_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_one_slot_dpu(void *cmd_buf, u32 offset,
|
||||
struct amdxdna_gem_obj *abo, u32 *size)
|
||||
{
|
||||
struct cmd_chain_slot_dpu *buf = cmd_buf + offset;
|
||||
int cu_idx = amdxdna_cmd_get_cu_idx(abo);
|
||||
struct amdxdna_cmd_start_npu *sn;
|
||||
u32 payload_len;
|
||||
void *payload;
|
||||
u32 arg_sz;
|
||||
|
||||
if (cu_idx < 0)
|
||||
return -EINVAL;
|
||||
|
||||
payload = amdxdna_cmd_get_payload(abo, &payload_len);
|
||||
if (!payload)
|
||||
return -EINVAL;
|
||||
sn = payload;
|
||||
arg_sz = payload_len - sizeof(*sn);
|
||||
if (payload_len < sizeof(*sn) || arg_sz > MAX_DPU_ARGS_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!slot_has_space(*buf, offset, arg_sz))
|
||||
return -ENOSPC;
|
||||
|
||||
buf->inst_buf_addr = sn->buffer;
|
||||
buf->inst_size = sn->buffer_size;
|
||||
buf->inst_prop_cnt = sn->prop_count;
|
||||
buf->cu_idx = cu_idx;
|
||||
buf->arg_cnt = arg_sz / sizeof(u32);
|
||||
memcpy(buf->args, sn->prop_args, arg_sz);
|
||||
|
||||
/* Accurate buf size to hint firmware to do necessary copy */
|
||||
*size = sizeof(*buf) + arg_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
aie2_cmdlist_fill_one_slot(u32 op, struct amdxdna_gem_obj *cmdbuf_abo, u32 offset,
|
||||
struct amdxdna_gem_obj *abo, u32 *size)
|
||||
{
|
||||
u32 this_op = amdxdna_cmd_get_op(abo);
|
||||
void *cmd_buf = cmdbuf_abo->mem.kva;
|
||||
int ret;
|
||||
|
||||
if (this_op != op) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case ERT_START_CU:
|
||||
ret = aie2_cmdlist_fill_one_slot_cf(cmd_buf, offset, abo, size);
|
||||
break;
|
||||
case ERT_START_NPU:
|
||||
ret = aie2_cmdlist_fill_one_slot_dpu(cmd_buf, offset, abo, size);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
done:
|
||||
if (ret) {
|
||||
XDNA_ERR(abo->client->xdna, "Can't fill slot for cmd op %d ret %d",
|
||||
op, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct amdxdna_gem_obj *
|
||||
aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
|
||||
{
|
||||
int idx = get_job_idx(job->seq);
|
||||
|
||||
return job->hwctx->priv->cmd_buf[idx];
|
||||
}
|
||||
|
||||
static void
|
||||
aie2_cmdlist_prepare_request(struct cmd_chain_req *req,
|
||||
struct amdxdna_gem_obj *cmdbuf_abo, u32 size, u32 cnt)
|
||||
{
|
||||
req->buf_addr = cmdbuf_abo->mem.dev_addr;
|
||||
req->buf_size = size;
|
||||
req->count = cnt;
|
||||
drm_clflush_virt_range(cmdbuf_abo->mem.kva, size);
|
||||
XDNA_DBG(cmdbuf_abo->client->xdna, "Command buf addr 0x%llx size 0x%x count %d",
|
||||
req->buf_addr, size, cnt);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
aie2_cmd_op_to_msg_op(u32 op)
|
||||
{
|
||||
switch (op) {
|
||||
case ERT_START_CU:
|
||||
return MSG_OP_CHAIN_EXEC_BUFFER_CF;
|
||||
case ERT_START_NPU:
|
||||
return MSG_OP_CHAIN_EXEC_DPU;
|
||||
default:
|
||||
return MSG_OP_MAX_OPCODE;
|
||||
}
|
||||
}
|
||||
|
||||
int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
|
||||
struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t))
|
||||
|
|
@ -649,12 +909,13 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
|
|||
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
|
||||
struct amdxdna_client *client = hwctx->client;
|
||||
struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_cmd_chain *payload;
|
||||
struct xdna_mailbox_msg msg;
|
||||
struct cmd_chain_req req;
|
||||
union exec_chain_req req;
|
||||
u32 payload_len;
|
||||
u32 offset = 0;
|
||||
u32 size;
|
||||
size_t size;
|
||||
int ret;
|
||||
u32 op;
|
||||
u32 i;
|
||||
|
|
@ -665,41 +926,42 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
|
|||
payload_len < struct_size(payload, data, payload->command_count))
|
||||
return -EINVAL;
|
||||
|
||||
op = ERT_INVALID_CMD;
|
||||
for (i = 0; i < payload->command_count; i++) {
|
||||
u32 boh = (u32)(payload->data[i]);
|
||||
struct amdxdna_gem_obj *abo;
|
||||
|
||||
abo = amdxdna_gem_get_obj(client, boh, AMDXDNA_BO_CMD);
|
||||
if (!abo) {
|
||||
XDNA_ERR(client->xdna, "Failed to find cmd BO %d", boh);
|
||||
XDNA_ERR(xdna, "Failed to find cmd BO %d", boh);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* All sub-cmd should have same op, use the first one. */
|
||||
if (i == 0)
|
||||
op = amdxdna_cmd_get_op(abo);
|
||||
|
||||
ret = aie2_cmdlist_fill_one_slot(op, cmdbuf_abo, offset, abo, &size);
|
||||
size = cmdbuf_abo->mem.size - offset;
|
||||
ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva + offset,
|
||||
abo, &size, &op);
|
||||
amdxdna_gem_put_obj(abo);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
/* The offset is the accumulated total size of the cmd buffer */
|
||||
aie2_cmdlist_prepare_request(&req, cmdbuf_abo, offset, payload->command_count);
|
||||
|
||||
msg.opcode = aie2_cmd_op_to_msg_op(op);
|
||||
msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op);
|
||||
if (msg.opcode == MSG_OP_MAX_OPCODE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* The offset is the accumulated total size of the cmd buffer */
|
||||
EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr,
|
||||
offset, payload->command_count);
|
||||
drm_clflush_virt_range(cmdbuf_abo->mem.kva, offset);
|
||||
|
||||
msg.handle = job;
|
||||
msg.notify_cb = notify_cb;
|
||||
msg.send_data = (u8 *)&req;
|
||||
msg.send_size = sizeof(req);
|
||||
ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
|
||||
if (ret) {
|
||||
XDNA_ERR(hwctx->client->xdna, "Send message failed");
|
||||
XDNA_ERR(xdna, "Send message failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -712,23 +974,27 @@ int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
|
|||
{
|
||||
struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job);
|
||||
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
struct amdxdna_gem_obj *cmd_abo = job->cmd_bo;
|
||||
struct xdna_mailbox_msg msg;
|
||||
struct cmd_chain_req req;
|
||||
u32 size;
|
||||
union exec_chain_req req;
|
||||
u32 op = ERT_INVALID_CMD;
|
||||
size_t size;
|
||||
int ret;
|
||||
u32 op;
|
||||
|
||||
op = amdxdna_cmd_get_op(cmd_abo);
|
||||
ret = aie2_cmdlist_fill_one_slot(op, cmdbuf_abo, 0, cmd_abo, &size);
|
||||
size = cmdbuf_abo->mem.size;
|
||||
ret = aie2_cmdlist_fill_slot(cmdbuf_abo->mem.kva, cmd_abo, &size, &op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
aie2_cmdlist_prepare_request(&req, cmdbuf_abo, size, 1);
|
||||
|
||||
msg.opcode = aie2_cmd_op_to_msg_op(op);
|
||||
msg.opcode = EXEC_MSG_OPS(xdna)->get_chain_msg_op(op);
|
||||
if (msg.opcode == MSG_OP_MAX_OPCODE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
EXEC_MSG_OPS(xdna)->init_chain_req(&req, cmdbuf_abo->mem.dev_addr,
|
||||
size, 1);
|
||||
drm_clflush_virt_range(cmdbuf_abo->mem.kva, size);
|
||||
|
||||
msg.handle = job;
|
||||
msg.notify_cb = notify_cb;
|
||||
msg.send_data = (u8 *)&req;
|
||||
|
|
@ -753,7 +1019,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
int ret = 0;
|
||||
|
||||
req.src_addr = 0;
|
||||
req.dst_addr = abo->mem.dev_addr - hwctx->client->dev_heap->mem.dev_addr;
|
||||
req.dst_addr = amdxdna_dev_bo_offset(abo);
|
||||
req.size = abo->mem.size;
|
||||
|
||||
/* Device to Host */
|
||||
|
|
@ -777,3 +1043,32 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t))
|
||||
{
|
||||
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
|
||||
struct amdxdna_gem_obj *abo = to_xdna_obj(job->bos[0]);
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
struct config_debug_bo_req req;
|
||||
struct xdna_mailbox_msg msg;
|
||||
|
||||
if (job->drv_cmd->opcode == ATTACH_DEBUG_BO)
|
||||
req.config = DEBUG_BO_REGISTER;
|
||||
else
|
||||
req.config = DEBUG_BO_UNREGISTER;
|
||||
|
||||
req.offset = amdxdna_dev_bo_offset(abo);
|
||||
req.size = abo->mem.size;
|
||||
|
||||
XDNA_DBG(xdna, "offset 0x%llx size 0x%llx config %d",
|
||||
req.offset, req.size, req.config);
|
||||
|
||||
msg.handle = job;
|
||||
msg.notify_cb = notify_cb;
|
||||
msg.send_data = (u8 *)&req;
|
||||
msg.send_size = sizeof(req);
|
||||
msg.opcode = MSG_OP_CONFIG_DEBUG_BO;
|
||||
|
||||
return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
enum aie2_msg_opcode {
|
||||
MSG_OP_CREATE_CONTEXT = 0x2,
|
||||
MSG_OP_DESTROY_CONTEXT = 0x3,
|
||||
MSG_OP_SYNC_BO = 0x7,
|
||||
MSG_OP_GET_TELEMETRY = 0x4,
|
||||
MSG_OP_SYNC_BO = 0x7,
|
||||
MSG_OP_EXECUTE_BUFFER_CF = 0xC,
|
||||
MSG_OP_QUERY_COL_STATUS = 0xD,
|
||||
MSG_OP_QUERY_AIE_TILE_INFO = 0xE,
|
||||
|
|
@ -18,6 +19,8 @@ enum aie2_msg_opcode {
|
|||
MSG_OP_CONFIG_CU = 0x11,
|
||||
MSG_OP_CHAIN_EXEC_BUFFER_CF = 0x12,
|
||||
MSG_OP_CHAIN_EXEC_DPU = 0x13,
|
||||
MSG_OP_CONFIG_DEBUG_BO = 0x14,
|
||||
MSG_OP_CHAIN_EXEC_NPU = 0x18,
|
||||
MSG_OP_MAX_XRT_OPCODE,
|
||||
MSG_OP_SUSPEND = 0x101,
|
||||
MSG_OP_RESUME = 0x102,
|
||||
|
|
@ -135,6 +138,28 @@ struct destroy_ctx_resp {
|
|||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
|
||||
enum telemetry_type {
|
||||
TELEMETRY_TYPE_DISABLED,
|
||||
TELEMETRY_TYPE_HEALTH,
|
||||
TELEMETRY_TYPE_ERROR_INFO,
|
||||
TELEMETRY_TYPE_PROFILING,
|
||||
TELEMETRY_TYPE_DEBUG,
|
||||
MAX_TELEMETRY_TYPE
|
||||
};
|
||||
|
||||
struct get_telemetry_req {
|
||||
enum telemetry_type type;
|
||||
__u64 buf_addr;
|
||||
__u32 buf_size;
|
||||
} __packed;
|
||||
|
||||
struct get_telemetry_resp {
|
||||
__u32 major;
|
||||
__u32 minor;
|
||||
__u32 size;
|
||||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
|
||||
struct execute_buffer_req {
|
||||
__u32 cu_idx;
|
||||
__u32 payload[19];
|
||||
|
|
@ -148,6 +173,18 @@ struct exec_dpu_req {
|
|||
__u32 payload[35];
|
||||
} __packed;
|
||||
|
||||
enum exec_npu_type {
|
||||
EXEC_NPU_TYPE_NON_ELF = 0x1,
|
||||
EXEC_NPU_TYPE_PARTIAL_ELF = 0x2,
|
||||
EXEC_NPU_TYPE_PREEMPT = 0x3,
|
||||
EXEC_NPU_TYPE_ELF = 0x4,
|
||||
};
|
||||
|
||||
union exec_req {
|
||||
struct execute_buffer_req ebuf;
|
||||
struct exec_dpu_req dpu_req;
|
||||
};
|
||||
|
||||
struct execute_buffer_resp {
|
||||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
|
|
@ -319,9 +356,6 @@ struct async_event_msg_resp {
|
|||
} __packed;
|
||||
|
||||
#define MAX_CHAIN_CMDBUF_SIZE SZ_4K
|
||||
#define slot_has_space(slot, offset, payload_size) \
|
||||
(MAX_CHAIN_CMDBUF_SIZE >= (offset) + (payload_size) + \
|
||||
sizeof(typeof(slot)))
|
||||
|
||||
struct cmd_chain_slot_execbuf_cf {
|
||||
__u32 cu_idx;
|
||||
|
|
@ -339,12 +373,41 @@ struct cmd_chain_slot_dpu {
|
|||
__u32 args[] __counted_by(arg_cnt);
|
||||
};
|
||||
|
||||
#define MAX_NPU_ARGS_SIZE (26 * sizeof(__u32))
|
||||
#define AIE2_EXEC_BUFFER_KERNEL_OP_TXN 3
|
||||
struct cmd_chain_slot_npu {
|
||||
enum exec_npu_type type;
|
||||
u64 inst_buf_addr;
|
||||
u64 save_buf_addr;
|
||||
u64 restore_buf_addr;
|
||||
u32 inst_size;
|
||||
u32 save_size;
|
||||
u32 restore_size;
|
||||
u32 inst_prop_cnt;
|
||||
u32 cu_idx;
|
||||
u32 arg_cnt;
|
||||
u32 args[] __counted_by(arg_cnt);
|
||||
} __packed;
|
||||
|
||||
struct cmd_chain_req {
|
||||
__u64 buf_addr;
|
||||
__u32 buf_size;
|
||||
__u32 count;
|
||||
} __packed;
|
||||
|
||||
struct cmd_chain_npu_req {
|
||||
u32 flags;
|
||||
u32 reserved;
|
||||
u64 buf_addr;
|
||||
u32 buf_size;
|
||||
u32 count;
|
||||
} __packed;
|
||||
|
||||
union exec_chain_req {
|
||||
struct cmd_chain_npu_req npu_req;
|
||||
struct cmd_chain_req req;
|
||||
};
|
||||
|
||||
struct cmd_chain_resp {
|
||||
enum aie2_msg_status status;
|
||||
__u32 fail_cmd_idx;
|
||||
|
|
@ -365,4 +428,21 @@ struct sync_bo_req {
|
|||
struct sync_bo_resp {
|
||||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
|
||||
#define DEBUG_BO_UNREGISTER 0
|
||||
#define DEBUG_BO_REGISTER 1
|
||||
struct config_debug_bo_req {
|
||||
__u64 offset;
|
||||
__u64 size;
|
||||
/*
|
||||
* config operations.
|
||||
* DEBUG_BO_REGISTER: Register debug buffer
|
||||
* DEBUG_BO_UNREGISTER: Unregister debug buffer
|
||||
*/
|
||||
__u32 config;
|
||||
} __packed;
|
||||
|
||||
struct config_debug_bo_resp {
|
||||
enum aie2_msg_status status;
|
||||
} __packed;
|
||||
#endif /* _AIE2_MSG_PRIV_H_ */
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "amdxdna_gem.h"
|
||||
#include "amdxdna_mailbox.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
#include "amdxdna_pm.h"
|
||||
|
||||
static int aie2_max_col = XRS_MAX_COL;
|
||||
module_param(aie2_max_col, uint, 0600);
|
||||
|
|
@ -54,6 +55,7 @@ struct mgmt_mbox_chann_info {
|
|||
|
||||
static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor)
|
||||
{
|
||||
const struct aie2_fw_feature_tbl *feature;
|
||||
struct amdxdna_dev *xdna = ndev->xdna;
|
||||
|
||||
/*
|
||||
|
|
@ -77,6 +79,17 @@ static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 f
|
|||
XDNA_ERR(xdna, "Firmware minor version smaller than supported");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (feature = ndev->priv->fw_feature_tbl; feature && feature->min_minor;
|
||||
feature++) {
|
||||
if (fw_minor < feature->min_minor)
|
||||
continue;
|
||||
if (feature->max_minor > 0 && fw_minor > feature->max_minor)
|
||||
continue;
|
||||
|
||||
set_bit(feature->feature, &ndev->feature_mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +183,10 @@ int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
|
|||
if (cfg->category != category)
|
||||
continue;
|
||||
|
||||
if (cfg->feature_mask &&
|
||||
bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX))
|
||||
continue;
|
||||
|
||||
value = val ? *val : cfg->value;
|
||||
ret = aie2_set_runtime_cfg(ndev, cfg->type, value);
|
||||
if (ret) {
|
||||
|
|
@ -223,15 +240,6 @@ static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!ndev->async_events)
|
||||
return 0;
|
||||
|
||||
ret = aie2_error_async_events_send(ndev);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Send async events failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -257,6 +265,8 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ndev->total_col = min(aie2_max_col, ndev->metadata.cols);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -338,6 +348,7 @@ static void aie2_hw_stop(struct amdxdna_dev *xdna)
|
|||
ndev->mbox = NULL;
|
||||
aie2_psp_stop(ndev->psp_hdl);
|
||||
aie2_smu_fini(ndev);
|
||||
aie2_error_async_events_free(ndev);
|
||||
pci_disable_device(pdev);
|
||||
|
||||
ndev->dev_status = AIE2_DEV_INIT;
|
||||
|
|
@ -424,6 +435,18 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
|
|||
goto destroy_mgmt_chann;
|
||||
}
|
||||
|
||||
ret = aie2_mgmt_fw_query(ndev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "failed to query fw, ret %d", ret);
|
||||
goto destroy_mgmt_chann;
|
||||
}
|
||||
|
||||
ret = aie2_error_async_events_alloc(ndev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret);
|
||||
goto destroy_mgmt_chann;
|
||||
}
|
||||
|
||||
ndev->dev_status = AIE2_DEV_START;
|
||||
|
||||
return 0;
|
||||
|
|
@ -459,7 +482,6 @@ static int aie2_hw_resume(struct amdxdna_dev *xdna)
|
|||
struct amdxdna_client *client;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&xdna->dev_lock);
|
||||
ret = aie2_hw_start(xdna);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Start hardware failed, %d", ret);
|
||||
|
|
@ -565,13 +587,6 @@ static int aie2_init(struct amdxdna_dev *xdna)
|
|||
goto release_fw;
|
||||
}
|
||||
|
||||
ret = aie2_mgmt_fw_query(ndev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Query firmware failed, ret %d", ret);
|
||||
goto stop_hw;
|
||||
}
|
||||
ndev->total_col = min(aie2_max_col, ndev->metadata.cols);
|
||||
|
||||
xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1;
|
||||
for (i = 0; i < xrs_cfg.clk_list.num_levels; i++)
|
||||
xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk;
|
||||
|
|
@ -587,30 +602,11 @@ static int aie2_init(struct amdxdna_dev *xdna)
|
|||
goto stop_hw;
|
||||
}
|
||||
|
||||
ret = aie2_error_async_events_alloc(ndev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret);
|
||||
goto stop_hw;
|
||||
}
|
||||
|
||||
ret = aie2_error_async_events_send(ndev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Send async events failed, ret %d", ret);
|
||||
goto async_event_free;
|
||||
}
|
||||
|
||||
/* Issue a command to make sure firmware handled async events */
|
||||
ret = aie2_query_firmware_version(ndev, &ndev->xdna->fw_ver);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Re-query firmware version failed");
|
||||
goto async_event_free;
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
aie2_msg_init(ndev);
|
||||
amdxdna_pm_init(xdna);
|
||||
return 0;
|
||||
|
||||
async_event_free:
|
||||
aie2_error_async_events_free(ndev);
|
||||
stop_hw:
|
||||
aie2_hw_stop(xdna);
|
||||
release_fw:
|
||||
|
|
@ -621,10 +617,8 @@ static int aie2_init(struct amdxdna_dev *xdna)
|
|||
|
||||
static void aie2_fini(struct amdxdna_dev *xdna)
|
||||
{
|
||||
struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
|
||||
|
||||
amdxdna_pm_fini(xdna);
|
||||
aie2_hw_stop(xdna);
|
||||
aie2_error_async_events_free(ndev);
|
||||
}
|
||||
|
||||
static int aie2_get_aie_status(struct amdxdna_client *client,
|
||||
|
|
@ -845,7 +839,120 @@ static int aie2_get_hwctx_status(struct amdxdna_client *client,
|
|||
}
|
||||
|
||||
args->buffer_size -= (u32)(array_args.buffer - args->buffer);
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_query_resource_info(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_get_info *args)
|
||||
{
|
||||
struct amdxdna_drm_get_resource_info res_info;
|
||||
const struct amdxdna_dev_priv *priv;
|
||||
struct amdxdna_dev_hdl *ndev;
|
||||
struct amdxdna_dev *xdna;
|
||||
|
||||
xdna = client->xdna;
|
||||
ndev = xdna->dev_handle;
|
||||
priv = ndev->priv;
|
||||
|
||||
res_info.npu_clk_max = priv->dpm_clk_tbl[ndev->max_dpm_level].hclk;
|
||||
res_info.npu_tops_max = ndev->max_tops;
|
||||
res_info.npu_task_max = priv->hwctx_limit;
|
||||
res_info.npu_tops_curr = ndev->curr_tops;
|
||||
res_info.npu_task_curr = ndev->hwctx_num;
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, sizeof(res_info)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_fill_hwctx_map(struct amdxdna_hwctx *hwctx, void *arg)
|
||||
{
|
||||
struct amdxdna_dev *xdna = hwctx->client->xdna;
|
||||
u32 *map = arg;
|
||||
|
||||
if (hwctx->fw_ctx_id >= xdna->dev_handle->priv->hwctx_limit) {
|
||||
XDNA_ERR(xdna, "Invalid fw ctx id %d/%d ", hwctx->fw_ctx_id,
|
||||
xdna->dev_handle->priv->hwctx_limit);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map[hwctx->fw_ctx_id] = hwctx->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_get_telemetry(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_get_info *args)
|
||||
{
|
||||
struct amdxdna_drm_query_telemetry_header *header __free(kfree) = NULL;
|
||||
u32 telemetry_data_sz, header_sz, elem_num;
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_client *tmp_client;
|
||||
int ret;
|
||||
|
||||
elem_num = xdna->dev_handle->priv->hwctx_limit;
|
||||
header_sz = struct_size(header, map, elem_num);
|
||||
if (args->buffer_size <= header_sz) {
|
||||
XDNA_ERR(xdna, "Invalid buffer size");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
telemetry_data_sz = args->buffer_size - header_sz;
|
||||
if (telemetry_data_sz > SZ_4M) {
|
||||
XDNA_ERR(xdna, "Buffer size is too big, %d", telemetry_data_sz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
header = kzalloc(header_sz, GFP_KERNEL);
|
||||
if (!header)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(header, u64_to_user_ptr(args->buffer), sizeof(*header))) {
|
||||
XDNA_ERR(xdna, "Failed to copy telemetry header from user");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
header->map_num_elements = elem_num;
|
||||
list_for_each_entry(tmp_client, &xdna->client_list, node) {
|
||||
ret = amdxdna_hwctx_walk(tmp_client, &header->map,
|
||||
aie2_fill_hwctx_map);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = aie2_query_telemetry(xdna->dev_handle,
|
||||
u64_to_user_ptr(args->buffer + header_sz),
|
||||
telemetry_data_sz, header);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Query telemetry failed ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(args->buffer), header, header_sz)) {
|
||||
XDNA_ERR(xdna, "Copy header failed");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_get_preempt_state(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_get_info *args)
|
||||
{
|
||||
struct amdxdna_drm_attribute_state state = {};
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_dev_hdl *ndev;
|
||||
|
||||
ndev = xdna->dev_handle;
|
||||
if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE)
|
||||
state.state = ndev->force_preempt_enabled;
|
||||
else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE)
|
||||
state.state = ndev->frame_boundary_preempt;
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args)
|
||||
|
|
@ -856,6 +963,10 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i
|
|||
if (!drm_dev_enter(&xdna->ddev, &idx))
|
||||
return -ENODEV;
|
||||
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto dev_exit;
|
||||
|
||||
switch (args->param) {
|
||||
case DRM_AMDXDNA_QUERY_AIE_STATUS:
|
||||
ret = aie2_get_aie_status(client, args);
|
||||
|
|
@ -878,12 +989,25 @@ static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_i
|
|||
case DRM_AMDXDNA_GET_POWER_MODE:
|
||||
ret = aie2_get_power_mode(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_QUERY_TELEMETRY:
|
||||
ret = aie2_get_telemetry(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_QUERY_RESOURCE_INFO:
|
||||
ret = aie2_query_resource_info(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE:
|
||||
case DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE:
|
||||
ret = aie2_get_preempt_state(client, args);
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
XDNA_DBG(xdna, "Got param %d", args->param);
|
||||
|
||||
dev_exit:
|
||||
drm_dev_exit(idx);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -898,6 +1022,12 @@ static int aie2_query_ctx_status_array(struct amdxdna_client *client,
|
|||
|
||||
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
|
||||
|
||||
if (args->element_size > SZ_4K || args->num_element > SZ_1K) {
|
||||
XDNA_DBG(xdna, "Invalid element size %d or number of element %d",
|
||||
args->element_size, args->num_element);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
array_args.element_size = min(args->element_size,
|
||||
sizeof(struct amdxdna_drm_hwctx_entry));
|
||||
array_args.buffer = args->buffer;
|
||||
|
|
@ -914,7 +1044,7 @@ static int aie2_query_ctx_status_array(struct amdxdna_client *client,
|
|||
args->num_element = (u32)((array_args.buffer - args->buffer) /
|
||||
args->element_size);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_get_array(struct amdxdna_client *client,
|
||||
|
|
@ -926,16 +1056,26 @@ static int aie2_get_array(struct amdxdna_client *client,
|
|||
if (!drm_dev_enter(&xdna->ddev, &idx))
|
||||
return -ENODEV;
|
||||
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto dev_exit;
|
||||
|
||||
switch (args->param) {
|
||||
case DRM_AMDXDNA_HW_CONTEXT_ALL:
|
||||
ret = aie2_query_ctx_status_array(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_HW_LAST_ASYNC_ERR:
|
||||
ret = aie2_get_array_async_error(xdna->dev_handle, args);
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
XDNA_DBG(xdna, "Got param %d", args->param);
|
||||
|
||||
dev_exit:
|
||||
drm_dev_exit(idx);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -965,6 +1105,38 @@ static int aie2_set_power_mode(struct amdxdna_client *client,
|
|||
return aie2_pm_set_mode(xdna->dev_handle, power_mode);
|
||||
}
|
||||
|
||||
static int aie2_set_preempt_state(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_set_state *args)
|
||||
{
|
||||
struct amdxdna_dev_hdl *ndev = client->xdna->dev_handle;
|
||||
struct amdxdna_drm_attribute_state state;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&state, u64_to_user_ptr(args->buffer), sizeof(state)))
|
||||
return -EFAULT;
|
||||
|
||||
if (state.state > 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (XDNA_MBZ_DBG(client->xdna, state.pad, sizeof(state.pad)))
|
||||
return -EINVAL;
|
||||
|
||||
if (args->param == DRM_AMDXDNA_SET_FORCE_PREEMPT) {
|
||||
ndev->force_preempt_enabled = state.state;
|
||||
} else if (args->param == DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT) {
|
||||
val = state.state;
|
||||
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
|
||||
&val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ndev->frame_boundary_preempt = state.state;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aie2_set_state(struct amdxdna_client *client,
|
||||
struct amdxdna_drm_set_state *args)
|
||||
{
|
||||
|
|
@ -974,16 +1146,26 @@ static int aie2_set_state(struct amdxdna_client *client,
|
|||
if (!drm_dev_enter(&xdna->ddev, &idx))
|
||||
return -ENODEV;
|
||||
|
||||
ret = amdxdna_pm_resume_get(xdna);
|
||||
if (ret)
|
||||
goto dev_exit;
|
||||
|
||||
switch (args->param) {
|
||||
case DRM_AMDXDNA_SET_POWER_MODE:
|
||||
ret = aie2_set_power_mode(client, args);
|
||||
break;
|
||||
case DRM_AMDXDNA_SET_FORCE_PREEMPT:
|
||||
case DRM_AMDXDNA_SET_FRAME_BOUNDARY_PREEMPT:
|
||||
ret = aie2_set_preempt_state(client, args);
|
||||
break;
|
||||
default:
|
||||
XDNA_ERR(xdna, "Not supported request parameter %u", args->param);
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
amdxdna_pm_suspend_put(xdna);
|
||||
dev_exit:
|
||||
drm_dev_exit(idx);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -998,6 +1180,7 @@ const struct amdxdna_dev_ops aie2_ops = {
|
|||
.hwctx_init = aie2_hwctx_init,
|
||||
.hwctx_fini = aie2_hwctx_fini,
|
||||
.hwctx_config = aie2_hwctx_config,
|
||||
.hwctx_sync_debug_bo = aie2_hwctx_sync_debug_bo,
|
||||
.cmd_submit = aie2_cmd_submit,
|
||||
.hmm_invalidate = aie2_hmm_invalidate,
|
||||
.get_array = aie2_get_array,
|
||||
|
|
|
|||
|
|
@ -110,12 +110,15 @@ struct aie_metadata {
|
|||
enum rt_config_category {
|
||||
AIE2_RT_CFG_INIT,
|
||||
AIE2_RT_CFG_CLK_GATING,
|
||||
AIE2_RT_CFG_FORCE_PREEMPT,
|
||||
AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT,
|
||||
};
|
||||
|
||||
struct rt_config {
|
||||
u32 type;
|
||||
u32 value;
|
||||
u32 category;
|
||||
unsigned long feature_mask;
|
||||
};
|
||||
|
||||
struct dpm_clk_freq {
|
||||
|
|
@ -156,6 +159,19 @@ enum aie2_dev_status {
|
|||
AIE2_DEV_START,
|
||||
};
|
||||
|
||||
struct aie2_exec_msg_ops {
|
||||
int (*init_cu_req)(struct amdxdna_gem_obj *cmd_bo, void *req,
|
||||
size_t *size, u32 *msg_op);
|
||||
int (*init_dpu_req)(struct amdxdna_gem_obj *cmd_bo, void *req,
|
||||
size_t *size, u32 *msg_op);
|
||||
void (*init_chain_req)(void *req, u64 slot_addr, size_t size, u32 cmd_cnt);
|
||||
int (*fill_cf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
int (*fill_dpu_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
int (*fill_preempt_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
int (*fill_elf_slot)(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *size);
|
||||
u32 (*get_chain_msg_op)(u32 cmd_op);
|
||||
};
|
||||
|
||||
struct amdxdna_dev_hdl {
|
||||
struct amdxdna_dev *xdna;
|
||||
const struct amdxdna_dev_priv *priv;
|
||||
|
|
@ -173,6 +189,8 @@ struct amdxdna_dev_hdl {
|
|||
u32 total_col;
|
||||
struct aie_version version;
|
||||
struct aie_metadata metadata;
|
||||
unsigned long feature_mask;
|
||||
struct aie2_exec_msg_ops *exec_msg_ops;
|
||||
|
||||
/* power management and clock*/
|
||||
enum amdxdna_power_mode_type pw_mode;
|
||||
|
|
@ -182,6 +200,10 @@ struct amdxdna_dev_hdl {
|
|||
u32 clk_gating;
|
||||
u32 npuclk_freq;
|
||||
u32 hclk_freq;
|
||||
u32 max_tops;
|
||||
u32 curr_tops;
|
||||
u32 force_preempt_enabled;
|
||||
u32 frame_boundary_preempt;
|
||||
|
||||
/* Mailbox and the management channel */
|
||||
struct mailbox *mbox;
|
||||
|
|
@ -190,6 +212,8 @@ struct amdxdna_dev_hdl {
|
|||
|
||||
enum aie2_dev_status dev_status;
|
||||
u32 hwctx_num;
|
||||
|
||||
struct amdxdna_async_error last_async_err;
|
||||
};
|
||||
|
||||
#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
|
||||
|
|
@ -204,12 +228,27 @@ struct aie2_hw_ops {
|
|||
int (*set_dpm)(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
|
||||
};
|
||||
|
||||
enum aie2_fw_feature {
|
||||
AIE2_NPU_COMMAND,
|
||||
AIE2_PREEMPT,
|
||||
AIE2_FEATURE_MAX
|
||||
};
|
||||
|
||||
struct aie2_fw_feature_tbl {
|
||||
enum aie2_fw_feature feature;
|
||||
u32 max_minor;
|
||||
u32 min_minor;
|
||||
};
|
||||
|
||||
#define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask)
|
||||
|
||||
struct amdxdna_dev_priv {
|
||||
const char *fw_path;
|
||||
u64 protocol_major;
|
||||
u64 protocol_minor;
|
||||
const struct rt_config *rt_config;
|
||||
const struct dpm_clk_freq *dpm_clk_tbl;
|
||||
const struct aie2_fw_feature_tbl *fw_feature_tbl;
|
||||
|
||||
#define COL_ALIGN_NONE 0
|
||||
#define COL_ALIGN_NATURE 1
|
||||
|
|
@ -217,6 +256,7 @@ struct amdxdna_dev_priv {
|
|||
u32 mbox_dev_addr;
|
||||
/* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */
|
||||
u32 mbox_size;
|
||||
u32 hwctx_limit;
|
||||
u32 sram_dev_addr;
|
||||
struct aie2_bar_off_pair sram_offs[SRAM_MAX_INDEX];
|
||||
struct aie2_bar_off_pair psp_regs_off[PSP_MAX_REGS];
|
||||
|
|
@ -234,6 +274,7 @@ extern const struct dpm_clk_freq npu1_dpm_clk_table[];
|
|||
extern const struct dpm_clk_freq npu4_dpm_clk_table[];
|
||||
extern const struct rt_config npu1_default_rt_cfg[];
|
||||
extern const struct rt_config npu4_default_rt_cfg[];
|
||||
extern const struct aie2_fw_feature_tbl npu4_fw_feature_table[];
|
||||
|
||||
/* aie2_smu.c */
|
||||
int aie2_smu_init(struct amdxdna_dev_hdl *ndev);
|
||||
|
|
@ -253,10 +294,12 @@ void aie2_psp_stop(struct psp_device *psp);
|
|||
/* aie2_error.c */
|
||||
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev);
|
||||
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_error_async_events_send(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_error_async_msg_thread(void *data);
|
||||
int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev,
|
||||
struct amdxdna_drm_get_array *args);
|
||||
|
||||
/* aie2_message.c */
|
||||
void aie2_msg_init(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_resume_fw(struct amdxdna_dev_hdl *ndev);
|
||||
int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value);
|
||||
|
|
@ -270,9 +313,13 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
|
|||
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
|
||||
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
|
||||
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled);
|
||||
int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
|
||||
char __user *buf, u32 size,
|
||||
struct amdxdna_drm_query_telemetry_header *header);
|
||||
int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
|
||||
void *handle, int (*cb)(void*, void __iomem *, size_t));
|
||||
int aie2_config_cu(struct amdxdna_hwctx *hwctx);
|
||||
int aie2_config_cu(struct amdxdna_hwctx *hwctx,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
|
||||
|
|
@ -283,11 +330,14 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
|
|||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
|
||||
int (*notify_cb)(void *, void __iomem *, size_t));
|
||||
|
||||
/* aie2_hwctx.c */
|
||||
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);
|
||||
void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx);
|
||||
int aie2_hwctx_config(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
|
||||
int aie2_hwctx_sync_debug_bo(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
|
||||
void aie2_hwctx_suspend(struct amdxdna_client *client);
|
||||
int aie2_hwctx_resume(struct amdxdna_client *client);
|
||||
int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "aie2_pci.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
#include "amdxdna_pm.h"
|
||||
|
||||
#define SMU_RESULT_OK 1
|
||||
|
||||
|
|
@ -22,6 +23,13 @@
|
|||
#define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7
|
||||
#define AIE2_SMU_SET_HARD_DPMLEVEL 0x8
|
||||
|
||||
#define NPU4_DPM_TOPS(ndev, dpm_level) \
|
||||
({ \
|
||||
typeof(ndev) _ndev = ndev; \
|
||||
(4096 * (_ndev)->total_col * \
|
||||
(_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
|
||||
})
|
||||
|
||||
static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
|
||||
u32 reg_arg, u32 *out)
|
||||
{
|
||||
|
|
@ -59,12 +67,16 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
|
|||
u32 freq;
|
||||
int ret;
|
||||
|
||||
ret = amdxdna_pm_resume_get(ndev->xdna);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
|
||||
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
|
||||
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
|
||||
return ret;
|
||||
goto suspend_put;
|
||||
}
|
||||
ndev->npuclk_freq = freq;
|
||||
|
||||
|
|
@ -73,49 +85,78 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
|
|||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
|
||||
ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
|
||||
return ret;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
amdxdna_pm_suspend_put(ndev->xdna);
|
||||
ndev->hclk_freq = freq;
|
||||
ndev->dpm_level = dpm_level;
|
||||
ndev->max_tops = 2 * ndev->total_col;
|
||||
ndev->curr_tops = ndev->max_tops * freq / 1028;
|
||||
|
||||
XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
|
||||
ndev->npuclk_freq, ndev->hclk_freq);
|
||||
|
||||
return 0;
|
||||
|
||||
suspend_put:
|
||||
amdxdna_pm_suspend_put(ndev->xdna);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = amdxdna_pm_resume_get(ndev->xdna);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
|
||||
dpm_level, ret);
|
||||
return ret;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
|
||||
dpm_level, ret);
|
||||
return ret;
|
||||
goto suspend_put;
|
||||
}
|
||||
|
||||
amdxdna_pm_suspend_put(ndev->xdna);
|
||||
ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
|
||||
ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
|
||||
ndev->dpm_level = dpm_level;
|
||||
ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
|
||||
ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
|
||||
|
||||
XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
|
||||
ndev->npuclk_freq, ndev->hclk_freq);
|
||||
|
||||
return 0;
|
||||
|
||||
suspend_put:
|
||||
amdxdna_pm_suspend_put(ndev->xdna);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Failing to set power off indicates an unrecoverable hardware or
|
||||
* firmware error.
|
||||
*/
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
|
||||
if (ret) {
|
||||
XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
|
||||
|
|
|
|||
|
|
@ -113,14 +113,14 @@ void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size)
|
|||
return &cmd->data[num_masks];
|
||||
}
|
||||
|
||||
int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
|
||||
u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
|
||||
{
|
||||
struct amdxdna_cmd *cmd = abo->mem.kva;
|
||||
u32 num_masks, i;
|
||||
u32 *cu_mask;
|
||||
|
||||
if (amdxdna_cmd_get_op(abo) == ERT_CMD_CHAIN)
|
||||
return -1;
|
||||
return INVALID_CU_IDX;
|
||||
|
||||
num_masks = 1 + FIELD_GET(AMDXDNA_CMD_EXTRA_CU_MASK, cmd->header);
|
||||
cu_mask = cmd->data;
|
||||
|
|
@ -129,7 +129,7 @@ int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo)
|
|||
return ffs(cu_mask[i]) - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return INVALID_CU_IDX;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -161,19 +161,14 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
|
|||
if (args->ext || args->ext_flags)
|
||||
return -EINVAL;
|
||||
|
||||
if (!drm_dev_enter(dev, &idx))
|
||||
return -ENODEV;
|
||||
|
||||
hwctx = kzalloc(sizeof(*hwctx), GFP_KERNEL);
|
||||
if (!hwctx) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
if (!hwctx)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(&hwctx->qos, u64_to_user_ptr(args->qos_p), sizeof(hwctx->qos))) {
|
||||
XDNA_ERR(xdna, "Access QoS info failed");
|
||||
ret = -EFAULT;
|
||||
goto free_hwctx;
|
||||
kfree(hwctx);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
hwctx->client = client;
|
||||
|
|
@ -181,30 +176,36 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
|
|||
hwctx->num_tiles = args->num_tiles;
|
||||
hwctx->mem_size = args->mem_size;
|
||||
hwctx->max_opc = args->max_opc;
|
||||
|
||||
guard(mutex)(&xdna->dev_lock);
|
||||
|
||||
if (!drm_dev_enter(dev, &idx)) {
|
||||
ret = -ENODEV;
|
||||
goto free_hwctx;
|
||||
}
|
||||
|
||||
ret = xdna->dev_info->ops->hwctx_init(hwctx);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
|
||||
goto dev_exit;
|
||||
}
|
||||
|
||||
hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->fw_ctx_id);
|
||||
if (!hwctx->name) {
|
||||
ret = -ENOMEM;
|
||||
goto fini_hwctx;
|
||||
}
|
||||
|
||||
ret = xa_alloc_cyclic(&client->hwctx_xa, &hwctx->id, hwctx,
|
||||
XA_LIMIT(AMDXDNA_INVALID_CTX_HANDLE + 1, MAX_HWCTX_ID),
|
||||
&client->next_hwctxid, GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
XDNA_ERR(xdna, "Allocate hwctx ID failed, ret %d", ret);
|
||||
goto free_hwctx;
|
||||
}
|
||||
|
||||
hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->id);
|
||||
if (!hwctx->name) {
|
||||
ret = -ENOMEM;
|
||||
goto rm_id;
|
||||
}
|
||||
|
||||
mutex_lock(&xdna->dev_lock);
|
||||
ret = xdna->dev_info->ops->hwctx_init(hwctx);
|
||||
if (ret) {
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
|
||||
goto free_name;
|
||||
}
|
||||
|
||||
args->handle = hwctx->id;
|
||||
args->syncobj_handle = hwctx->syncobj_hdl;
|
||||
mutex_unlock(&xdna->dev_lock);
|
||||
|
||||
atomic64_set(&hwctx->job_submit_cnt, 0);
|
||||
atomic64_set(&hwctx->job_free_cnt, 0);
|
||||
|
|
@ -214,12 +215,12 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
|
|||
|
||||
free_name:
|
||||
kfree(hwctx->name);
|
||||
rm_id:
|
||||
xa_erase(&client->hwctx_xa, hwctx->id);
|
||||
fini_hwctx:
|
||||
xdna->dev_info->ops->hwctx_fini(hwctx);
|
||||
dev_exit:
|
||||
drm_dev_exit(idx);
|
||||
free_hwctx:
|
||||
kfree(hwctx);
|
||||
exit:
|
||||
drm_dev_exit(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +328,38 @@ int amdxdna_drm_config_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
|
|||
return ret;
|
||||
}
|
||||
|
||||
int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
|
||||
{
|
||||
struct amdxdna_dev *xdna = client->xdna;
|
||||
struct amdxdna_hwctx *hwctx;
|
||||
struct amdxdna_gem_obj *abo;
|
||||
struct drm_gem_object *gobj;
|
||||
int ret, idx;
|
||||
|
||||
if (!xdna->dev_info->ops->hwctx_sync_debug_bo)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
gobj = drm_gem_object_lookup(client->filp, debug_bo_hdl);
|
||||
if (!gobj)
|
||||
return -EINVAL;
|
||||
|
||||
abo = to_xdna_obj(gobj);
|
||||
guard(mutex)(&xdna->dev_lock);
|
||||
idx = srcu_read_lock(&client->hwctx_srcu);
|
||||
hwctx = xa_load(&client->hwctx_xa, abo->assigned_hwctx);
|
||||
if (!hwctx) {
|
||||
ret = -EINVAL;
|
||||
goto unlock_srcu;
|
||||
}
|
||||
|
||||
ret = xdna->dev_info->ops->hwctx_sync_debug_bo(hwctx, debug_bo_hdl);
|
||||
|
||||
unlock_srcu:
|
||||
srcu_read_unlock(&client->hwctx_srcu, idx);
|
||||
drm_gem_object_put(gobj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
|
||||
{
|
||||
|
|
@ -389,9 +422,11 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
|
|||
trace_amdxdna_debug_point(job->hwctx->name, job->seq, "job release");
|
||||
amdxdna_arg_bos_put(job);
|
||||
amdxdna_gem_put_obj(job->cmd_bo);
|
||||
dma_fence_put(job->fence);
|
||||
}
|
||||
|
||||
int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
struct amdxdna_drv_cmd *drv_cmd,
|
||||
u32 cmd_bo_hdl, u32 *arg_bo_hdls, u32 arg_bo_cnt,
|
||||
u32 hwctx_hdl, u64 *seq)
|
||||
{
|
||||
|
|
@ -405,6 +440,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
|
|||
if (!job)
|
||||
return -ENOMEM;
|
||||
|
||||
job->drv_cmd = drv_cmd;
|
||||
|
||||
if (cmd_bo_hdl != AMDXDNA_INVALID_BO_HANDLE) {
|
||||
job->cmd_bo = amdxdna_gem_get_obj(client, cmd_bo_hdl, AMDXDNA_BO_CMD);
|
||||
if (!job->cmd_bo) {
|
||||
|
|
@ -412,8 +449,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
|
|||
ret = -EINVAL;
|
||||
goto free_job;
|
||||
}
|
||||
} else {
|
||||
job->cmd_bo = NULL;
|
||||
}
|
||||
|
||||
ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
|
||||
|
|
@ -431,11 +466,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
|
|||
goto unlock_srcu;
|
||||
}
|
||||
|
||||
if (hwctx->status != HWCTX_STAT_READY) {
|
||||
XDNA_ERR(xdna, "HW Context is not ready");
|
||||
ret = -EINVAL;
|
||||
goto unlock_srcu;
|
||||
}
|
||||
|
||||
job->hwctx = hwctx;
|
||||
job->mm = current->mm;
|
||||
|
|
@ -512,7 +542,7 @@ static int amdxdna_drm_submit_execbuf(struct amdxdna_client *client,
|
|||
}
|
||||
}
|
||||
|
||||
ret = amdxdna_cmd_submit(client, cmd_bo_hdl, arg_bo_hdls,
|
||||
ret = amdxdna_cmd_submit(client, NULL, cmd_bo_hdl, arg_bo_hdls,
|
||||
args->arg_count, args->hwctx, &args->seq);
|
||||
if (ret)
|
||||
XDNA_DBG(xdna, "Submit cmds failed, ret %d", ret);
|
||||
|
|
|
|||
|
|
@ -13,9 +13,12 @@
|
|||
struct amdxdna_hwctx_priv;
|
||||
|
||||
enum ert_cmd_opcode {
|
||||
ERT_START_CU = 0,
|
||||
ERT_CMD_CHAIN = 19,
|
||||
ERT_START_NPU = 20,
|
||||
ERT_START_CU = 0,
|
||||
ERT_CMD_CHAIN = 19,
|
||||
ERT_START_NPU = 20,
|
||||
ERT_START_NPU_PREEMPT = 21,
|
||||
ERT_START_NPU_PREEMPT_ELF = 22,
|
||||
ERT_INVALID_CMD = ~0U,
|
||||
};
|
||||
|
||||
enum ert_cmd_state {
|
||||
|
|
@ -54,6 +57,21 @@ struct amdxdna_cmd_chain {
|
|||
u64 data[] __counted_by(command_count);
|
||||
};
|
||||
|
||||
/*
|
||||
* Interpretation of the beginning of data payload for ERT_START_NPU_PREEMPT in
|
||||
* amdxdna_cmd. The rest of the payload in amdxdna_cmd is regular kernel args.
|
||||
*/
|
||||
struct amdxdna_cmd_preempt_data {
|
||||
u64 inst_buf; /* instruction buffer address */
|
||||
u64 save_buf; /* save buffer address */
|
||||
u64 restore_buf; /* restore buffer address */
|
||||
u32 inst_size; /* size of instruction buffer in bytes */
|
||||
u32 save_size; /* size of save buffer in bytes */
|
||||
u32 restore_size; /* size of restore buffer in bytes */
|
||||
u32 inst_prop_cnt; /* properties count */
|
||||
u32 prop_args[]; /* properties and regular kernel arguments */
|
||||
};
|
||||
|
||||
/* Exec buffer command header format */
|
||||
#define AMDXDNA_CMD_STATE GENMASK(3, 0)
|
||||
#define AMDXDNA_CMD_EXTRA_CU_MASK GENMASK(11, 10)
|
||||
|
|
@ -64,6 +82,8 @@ struct amdxdna_cmd {
|
|||
u32 data[];
|
||||
};
|
||||
|
||||
#define INVALID_CU_IDX (~0U)
|
||||
|
||||
struct amdxdna_hwctx {
|
||||
struct amdxdna_client *client;
|
||||
struct amdxdna_hwctx_priv *priv;
|
||||
|
|
@ -95,6 +115,17 @@ struct amdxdna_hwctx {
|
|||
#define drm_job_to_xdna_job(j) \
|
||||
container_of(j, struct amdxdna_sched_job, base)
|
||||
|
||||
enum amdxdna_job_opcode {
|
||||
SYNC_DEBUG_BO,
|
||||
ATTACH_DEBUG_BO,
|
||||
DETACH_DEBUG_BO,
|
||||
};
|
||||
|
||||
struct amdxdna_drv_cmd {
|
||||
enum amdxdna_job_opcode opcode;
|
||||
u32 result;
|
||||
};
|
||||
|
||||
struct amdxdna_sched_job {
|
||||
struct drm_sched_job base;
|
||||
struct kref refcnt;
|
||||
|
|
@ -105,7 +136,9 @@ struct amdxdna_sched_job {
|
|||
/* user can wait on this fence */
|
||||
struct dma_fence *out_fence;
|
||||
bool job_done;
|
||||
bool job_timeout;
|
||||
u64 seq;
|
||||
struct amdxdna_drv_cmd *drv_cmd;
|
||||
struct amdxdna_gem_obj *cmd_bo;
|
||||
size_t bo_cnt;
|
||||
struct drm_gem_object *bos[] __counted_by(bo_cnt);
|
||||
|
|
@ -137,15 +170,17 @@ amdxdna_cmd_get_state(struct amdxdna_gem_obj *abo)
|
|||
}
|
||||
|
||||
void *amdxdna_cmd_get_payload(struct amdxdna_gem_obj *abo, u32 *size);
|
||||
int amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo);
|
||||
u32 amdxdna_cmd_get_cu_idx(struct amdxdna_gem_obj *abo);
|
||||
|
||||
void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job);
|
||||
void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
|
||||
int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
|
||||
int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
|
||||
int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
|
||||
|
||||
int amdxdna_cmd_submit(struct amdxdna_client *client,
|
||||
u32 cmd_bo_hdls, u32 *arg_bo_hdls, u32 arg_bo_cnt,
|
||||
struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
|
||||
u32 *arg_bo_hdls, u32 arg_bo_cnt,
|
||||
u32 hwctx_hdl, u64 *seq);
|
||||
|
||||
int amdxdna_cmd_wait(struct amdxdna_client *client, u32 hwctx_hdl,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _AMDXDNA_ERROR_H_
|
||||
#define _AMDXDNA_ERROR_H_
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
#define AMDXDNA_ERR_DRV_AIE 4
|
||||
#define AMDXDNA_ERR_SEV_CRITICAL 3
|
||||
#define AMDXDNA_ERR_CLASS_AIE 2
|
||||
|
||||
#define AMDXDNA_ERR_NUM_MASK GENMASK_U64(15, 0)
|
||||
#define AMDXDNA_ERR_DRV_MASK GENMASK_U64(23, 16)
|
||||
#define AMDXDNA_ERR_SEV_MASK GENMASK_U64(31, 24)
|
||||
#define AMDXDNA_ERR_MOD_MASK GENMASK_U64(39, 32)
|
||||
#define AMDXDNA_ERR_CLASS_MASK GENMASK_U64(47, 40)
|
||||
|
||||
enum amdxdna_error_num {
|
||||
AMDXDNA_ERROR_NUM_AIE_SATURATION = 3,
|
||||
AMDXDNA_ERROR_NUM_AIE_FP,
|
||||
AMDXDNA_ERROR_NUM_AIE_STREAM,
|
||||
AMDXDNA_ERROR_NUM_AIE_ACCESS,
|
||||
AMDXDNA_ERROR_NUM_AIE_BUS,
|
||||
AMDXDNA_ERROR_NUM_AIE_INSTRUCTION,
|
||||
AMDXDNA_ERROR_NUM_AIE_ECC,
|
||||
AMDXDNA_ERROR_NUM_AIE_LOCK,
|
||||
AMDXDNA_ERROR_NUM_AIE_DMA,
|
||||
AMDXDNA_ERROR_NUM_AIE_MEM_PARITY,
|
||||
AMDXDNA_ERROR_NUM_UNKNOWN = 15,
|
||||
};
|
||||
|
||||
enum amdxdna_error_module {
|
||||
AMDXDNA_ERROR_MODULE_AIE_CORE = 3,
|
||||
AMDXDNA_ERROR_MODULE_AIE_MEMORY,
|
||||
AMDXDNA_ERROR_MODULE_AIE_SHIM,
|
||||
AMDXDNA_ERROR_MODULE_AIE_NOC,
|
||||
AMDXDNA_ERROR_MODULE_AIE_PL,
|
||||
AMDXDNA_ERROR_MODULE_UNKNOWN = 8,
|
||||
};
|
||||
|
||||
#define AMDXDNA_ERROR_ENCODE(err_num, err_mod) \
|
||||
(FIELD_PREP(AMDXDNA_ERR_NUM_MASK, err_num) | \
|
||||
FIELD_PREP_CONST(AMDXDNA_ERR_DRV_MASK, AMDXDNA_ERR_DRV_AIE) | \
|
||||
FIELD_PREP_CONST(AMDXDNA_ERR_SEV_MASK, AMDXDNA_ERR_SEV_CRITICAL) | \
|
||||
FIELD_PREP(AMDXDNA_ERR_MOD_MASK, err_mod) | \
|
||||
FIELD_PREP_CONST(AMDXDNA_ERR_CLASS_MASK, AMDXDNA_ERR_CLASS_AIE))
|
||||
|
||||
#define AMDXDNA_EXTRA_ERR_COL_MASK GENMASK_U64(7, 0)
|
||||
#define AMDXDNA_EXTRA_ERR_ROW_MASK GENMASK_U64(15, 8)
|
||||
|
||||
#define AMDXDNA_EXTRA_ERR_ENCODE(row, col) \
|
||||
(FIELD_PREP(AMDXDNA_EXTRA_ERR_COL_MASK, col) | \
|
||||
FIELD_PREP(AMDXDNA_EXTRA_ERR_ROW_MASK, row))
|
||||
|
||||
#endif /* _AMDXDNA_ERROR_H_ */
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_gem_shmem_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/gpu_scheduler.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-direct.h>
|
||||
|
|
@ -392,35 +393,33 @@ static const struct dma_buf_ops amdxdna_dmabuf_ops = {
|
|||
.vunmap = drm_gem_dmabuf_vunmap,
|
||||
};
|
||||
|
||||
static int amdxdna_gem_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
|
||||
static int amdxdna_gem_obj_vmap(struct amdxdna_gem_obj *abo, void **vaddr)
|
||||
{
|
||||
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
|
||||
|
||||
iosys_map_clear(map);
|
||||
|
||||
dma_resv_assert_held(obj->resv);
|
||||
struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
|
||||
int ret;
|
||||
|
||||
if (is_import_bo(abo))
|
||||
dma_buf_vmap(abo->dma_buf, map);
|
||||
ret = dma_buf_vmap_unlocked(abo->dma_buf, &map);
|
||||
else
|
||||
drm_gem_shmem_object_vmap(obj, map);
|
||||
ret = drm_gem_vmap(to_gobj(abo), &map);
|
||||
|
||||
if (!map->vaddr)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
*vaddr = map.vaddr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
|
||||
static void amdxdna_gem_obj_vunmap(struct amdxdna_gem_obj *abo)
|
||||
{
|
||||
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
|
||||
struct iosys_map map;
|
||||
|
||||
dma_resv_assert_held(obj->resv);
|
||||
if (!abo->mem.kva)
|
||||
return;
|
||||
|
||||
iosys_map_set_vaddr(&map, abo->mem.kva);
|
||||
|
||||
if (is_import_bo(abo))
|
||||
dma_buf_vunmap(abo->dma_buf, map);
|
||||
dma_buf_vunmap_unlocked(abo->dma_buf, &map);
|
||||
else
|
||||
drm_gem_shmem_object_vunmap(obj, map);
|
||||
drm_gem_vunmap(to_gobj(abo), &map);
|
||||
}
|
||||
|
||||
static struct dma_buf *amdxdna_gem_prime_export(struct drm_gem_object *gobj, int flags)
|
||||
|
|
@ -455,7 +454,6 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
|
|||
{
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev);
|
||||
struct amdxdna_gem_obj *abo = to_xdna_obj(gobj);
|
||||
struct iosys_map map = IOSYS_MAP_INIT_VADDR(abo->mem.kva);
|
||||
|
||||
XDNA_DBG(xdna, "BO type %d xdna_addr 0x%llx", abo->type, abo->mem.dev_addr);
|
||||
|
||||
|
|
@ -468,7 +466,7 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
|
|||
if (abo->type == AMDXDNA_BO_DEV_HEAP)
|
||||
drm_mm_takedown(&abo->mm);
|
||||
|
||||
drm_gem_vunmap(gobj, &map);
|
||||
amdxdna_gem_obj_vunmap(abo);
|
||||
mutex_destroy(&abo->lock);
|
||||
|
||||
if (is_import_bo(abo)) {
|
||||
|
|
@ -489,8 +487,8 @@ static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = {
|
|||
.pin = drm_gem_shmem_object_pin,
|
||||
.unpin = drm_gem_shmem_object_unpin,
|
||||
.get_sg_table = drm_gem_shmem_object_get_sg_table,
|
||||
.vmap = amdxdna_gem_obj_vmap,
|
||||
.vunmap = amdxdna_gem_obj_vunmap,
|
||||
.vmap = drm_gem_shmem_object_vmap,
|
||||
.vunmap = drm_gem_shmem_object_vunmap,
|
||||
.mmap = amdxdna_gem_obj_mmap,
|
||||
.vm_ops = &drm_gem_shmem_vm_ops,
|
||||
.export = amdxdna_gem_prime_export,
|
||||
|
|
@ -663,7 +661,6 @@ amdxdna_drm_create_dev_heap(struct drm_device *dev,
|
|||
struct drm_file *filp)
|
||||
{
|
||||
struct amdxdna_client *client = filp->driver_priv;
|
||||
struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(dev);
|
||||
struct amdxdna_gem_obj *abo;
|
||||
int ret;
|
||||
|
|
@ -692,12 +689,11 @@ amdxdna_drm_create_dev_heap(struct drm_device *dev,
|
|||
abo->mem.dev_addr = client->xdna->dev_info->dev_mem_base;
|
||||
drm_mm_init(&abo->mm, abo->mem.dev_addr, abo->mem.size);
|
||||
|
||||
ret = drm_gem_vmap(to_gobj(abo), &map);
|
||||
ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Vmap heap bo failed, ret %d", ret);
|
||||
goto release_obj;
|
||||
}
|
||||
abo->mem.kva = map.vaddr;
|
||||
|
||||
client->dev_heap = abo;
|
||||
drm_gem_object_get(to_gobj(abo));
|
||||
|
|
@ -748,7 +744,6 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev,
|
|||
struct amdxdna_drm_create_bo *args,
|
||||
struct drm_file *filp)
|
||||
{
|
||||
struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL);
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(dev);
|
||||
struct amdxdna_gem_obj *abo;
|
||||
int ret;
|
||||
|
|
@ -770,12 +765,11 @@ amdxdna_drm_create_cmd_bo(struct drm_device *dev,
|
|||
abo->type = AMDXDNA_BO_CMD;
|
||||
abo->client = filp->driver_priv;
|
||||
|
||||
ret = drm_gem_vmap(to_gobj(abo), &map);
|
||||
ret = amdxdna_gem_obj_vmap(abo, &abo->mem.kva);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Vmap cmd bo failed, ret %d", ret);
|
||||
goto release_obj;
|
||||
}
|
||||
abo->mem.kva = map.vaddr;
|
||||
|
||||
return abo;
|
||||
|
||||
|
|
@ -969,6 +963,9 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
|
|||
XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
|
||||
args->handle, args->offset, args->size);
|
||||
|
||||
if (args->direction == SYNC_DIRECT_FROM_DEVICE)
|
||||
ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle);
|
||||
|
||||
put_obj:
|
||||
drm_gem_object_put(gobj);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#define _AMDXDNA_GEM_H_
|
||||
|
||||
#include <linux/hmm.h>
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
struct amdxdna_umap {
|
||||
struct vm_area_struct *vma;
|
||||
|
|
@ -62,6 +63,11 @@ static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo)
|
|||
drm_gem_object_put(to_gobj(abo));
|
||||
}
|
||||
|
||||
static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
|
||||
{
|
||||
return abo->mem.dev_addr - abo->client->dev_heap->mem.dev_addr;
|
||||
}
|
||||
|
||||
void amdxdna_umap_put(struct amdxdna_umap *mapp);
|
||||
|
||||
struct drm_gem_object *
|
||||
|
|
|
|||
|
|
@ -194,7 +194,8 @@ static void mailbox_release_msg(struct mailbox_channel *mb_chann,
|
|||
{
|
||||
MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x",
|
||||
mb_msg->pkg.header.id, mb_msg->pkg.header.opcode);
|
||||
mb_msg->notify_cb(mb_msg->handle, NULL, 0);
|
||||
if (mb_msg->notify_cb)
|
||||
mb_msg->notify_cb(mb_msg->handle, NULL, 0);
|
||||
kfree(mb_msg);
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +249,7 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade
|
|||
{
|
||||
struct mailbox_msg *mb_msg;
|
||||
int msg_id;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
msg_id = header->id;
|
||||
if (!mailbox_validate_msgid(msg_id)) {
|
||||
|
|
@ -265,9 +266,11 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade
|
|||
|
||||
MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x",
|
||||
header->opcode, header->total_size, header->id);
|
||||
ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size);
|
||||
if (unlikely(ret))
|
||||
MB_ERR(mb_chann, "Message callback ret %d", ret);
|
||||
if (mb_msg->notify_cb) {
|
||||
ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size);
|
||||
if (unlikely(ret))
|
||||
MB_ERR(mb_chann, "Message callback ret %d", ret);
|
||||
}
|
||||
|
||||
kfree(mb_msg);
|
||||
return ret;
|
||||
|
|
@ -513,6 +516,7 @@ xdna_mailbox_create_channel(struct mailbox *mb,
|
|||
}
|
||||
|
||||
mb_chann->bad_state = false;
|
||||
mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
|
||||
|
||||
MB_DBG(mb_chann, "Mailbox channel created (irq: %d)", mb_chann->msix_irq);
|
||||
return mb_chann;
|
||||
|
|
|
|||
|
|
@ -16,16 +16,18 @@ struct xdna_notify {
|
|||
u32 *data;
|
||||
size_t size;
|
||||
int error;
|
||||
u32 *status;
|
||||
};
|
||||
|
||||
#define DECLARE_XDNA_MSG_COMMON(name, op, status) \
|
||||
#define DECLARE_XDNA_MSG_COMMON(name, op, s) \
|
||||
struct name##_req req = { 0 }; \
|
||||
struct name##_resp resp = { status }; \
|
||||
struct name##_resp resp = { .status = s }; \
|
||||
struct xdna_notify hdl = { \
|
||||
.error = 0, \
|
||||
.data = (u32 *)&resp, \
|
||||
.size = sizeof(resp), \
|
||||
.comp = COMPLETION_INITIALIZER_ONSTACK(hdl.comp), \
|
||||
.status = (u32 *)&resp.status, \
|
||||
}; \
|
||||
struct xdna_mailbox_msg msg = { \
|
||||
.send_data = (u8 *)&req, \
|
||||
|
|
|
|||
|
|
@ -13,13 +13,11 @@
|
|||
#include <drm/gpu_scheduler.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "amdxdna_ctx.h"
|
||||
#include "amdxdna_gem.h"
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
#define AMDXDNA_AUTOSUSPEND_DELAY 5000 /* milliseconds */
|
||||
#include "amdxdna_pm.h"
|
||||
|
||||
MODULE_FIRMWARE("amdnpu/1502_00/npu.sbin");
|
||||
MODULE_FIRMWARE("amdnpu/17f0_10/npu.sbin");
|
||||
|
|
@ -29,9 +27,14 @@ MODULE_FIRMWARE("amdnpu/17f0_20/npu.sbin");
|
|||
/*
|
||||
* 0.0: Initial version
|
||||
* 0.1: Support getting all hardware contexts by DRM_IOCTL_AMDXDNA_GET_ARRAY
|
||||
* 0.2: Support getting last error hardware error
|
||||
* 0.3: Support firmware debug buffer
|
||||
* 0.4: Support getting resource information
|
||||
* 0.5: Support getting telemetry data
|
||||
* 0.6: Support preemption
|
||||
*/
|
||||
#define AMDXDNA_DRIVER_MAJOR 0
|
||||
#define AMDXDNA_DRIVER_MINOR 1
|
||||
#define AMDXDNA_DRIVER_MINOR 6
|
||||
|
||||
/*
|
||||
* Bind the driver base on (vendor_id, device_id) pair and later use the
|
||||
|
|
@ -61,17 +64,9 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
|
|||
struct amdxdna_client *client;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(ddev->dev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Failed to get rpm, ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
client = kzalloc(sizeof(*client), GFP_KERNEL);
|
||||
if (!client) {
|
||||
ret = -ENOMEM;
|
||||
goto put_rpm;
|
||||
}
|
||||
if (!client)
|
||||
return -ENOMEM;
|
||||
|
||||
client->pid = pid_nr(rcu_access_pointer(filp->pid));
|
||||
client->xdna = xdna;
|
||||
|
|
@ -106,9 +101,6 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
|
|||
iommu_sva_unbind_device(client->sva);
|
||||
failed:
|
||||
kfree(client);
|
||||
put_rpm:
|
||||
pm_runtime_mark_last_busy(ddev->dev);
|
||||
pm_runtime_put_autosuspend(ddev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -130,8 +122,6 @@ static void amdxdna_drm_close(struct drm_device *ddev, struct drm_file *filp)
|
|||
|
||||
XDNA_DBG(xdna, "pid %d closed", client->pid);
|
||||
kfree(client);
|
||||
pm_runtime_mark_last_busy(ddev->dev);
|
||||
pm_runtime_put_autosuspend(ddev->dev);
|
||||
}
|
||||
|
||||
static int amdxdna_flush(struct file *f, fl_owner_t id)
|
||||
|
|
@ -310,19 +300,12 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
goto failed_dev_fini;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(dev, AMDXDNA_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_allow(dev);
|
||||
|
||||
ret = drm_dev_register(&xdna->ddev, 0);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "DRM register failed, ret %d", ret);
|
||||
pm_runtime_forbid(dev);
|
||||
goto failed_sysfs_fini;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
return 0;
|
||||
|
||||
failed_sysfs_fini:
|
||||
|
|
@ -339,14 +322,10 @@ static int amdxdna_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
static void amdxdna_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = pci_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct amdxdna_client *client;
|
||||
|
||||
destroy_workqueue(xdna->notifier_wq);
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
drm_dev_unplug(&xdna->ddev);
|
||||
amdxdna_sysfs_fini(xdna);
|
||||
|
||||
|
|
@ -365,29 +344,9 @@ static void amdxdna_remove(struct pci_dev *pdev)
|
|||
mutex_unlock(&xdna->dev_lock);
|
||||
}
|
||||
|
||||
static int amdxdna_pmops_suspend(struct device *dev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
if (!xdna->dev_info->ops->suspend)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return xdna->dev_info->ops->suspend(xdna);
|
||||
}
|
||||
|
||||
static int amdxdna_pmops_resume(struct device *dev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = pci_get_drvdata(to_pci_dev(dev));
|
||||
|
||||
if (!xdna->dev_info->ops->resume)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return xdna->dev_info->ops->resume(xdna);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops amdxdna_pm_ops = {
|
||||
SYSTEM_SLEEP_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume)
|
||||
RUNTIME_PM_OPS(amdxdna_pmops_suspend, amdxdna_pmops_resume, NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume)
|
||||
RUNTIME_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume, NULL)
|
||||
};
|
||||
|
||||
static struct pci_driver amdxdna_pci_driver = {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#ifndef _AMDXDNA_PCI_DRV_H_
|
||||
#define _AMDXDNA_PCI_DRV_H_
|
||||
|
||||
#include <drm/drm_print.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ struct amdxdna_dev_ops {
|
|||
int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
|
||||
void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
|
||||
int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
|
||||
int (*hwctx_sync_debug_bo)(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
|
||||
void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
|
||||
int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
|
||||
int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args);
|
||||
|
|
@ -99,6 +101,7 @@ struct amdxdna_dev {
|
|||
struct amdxdna_fw_ver fw_ver;
|
||||
struct rw_semaphore notifier_lock; /* for mmu notifier*/
|
||||
struct workqueue_struct *notifier_wq;
|
||||
bool rpm_on;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#include <drm/amdxdna_accel.h>
|
||||
#include <drm/drm_drv.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "amdxdna_pm.h"
|
||||
|
||||
#define AMDXDNA_AUTOSUSPEND_DELAY 5000 /* milliseconds */
|
||||
|
||||
int amdxdna_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev));
|
||||
int ret = -EOPNOTSUPP;
|
||||
bool rpm;
|
||||
|
||||
if (xdna->dev_info->ops->suspend) {
|
||||
rpm = xdna->rpm_on;
|
||||
xdna->rpm_on = false;
|
||||
ret = xdna->dev_info->ops->suspend(xdna);
|
||||
xdna->rpm_on = rpm;
|
||||
}
|
||||
|
||||
XDNA_DBG(xdna, "Suspend done ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int amdxdna_pm_resume(struct device *dev)
|
||||
{
|
||||
struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev));
|
||||
int ret = -EOPNOTSUPP;
|
||||
bool rpm;
|
||||
|
||||
if (xdna->dev_info->ops->resume) {
|
||||
rpm = xdna->rpm_on;
|
||||
xdna->rpm_on = false;
|
||||
ret = xdna->dev_info->ops->resume(xdna);
|
||||
xdna->rpm_on = rpm;
|
||||
}
|
||||
|
||||
XDNA_DBG(xdna, "Resume done ret %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int amdxdna_pm_resume_get(struct amdxdna_dev *xdna)
|
||||
{
|
||||
struct device *dev = xdna->ddev.dev;
|
||||
int ret;
|
||||
|
||||
if (!xdna->rpm_on)
|
||||
return 0;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret) {
|
||||
XDNA_ERR(xdna, "Resume failed: %d", ret);
|
||||
pm_runtime_set_suspended(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna)
|
||||
{
|
||||
struct device *dev = xdna->ddev.dev;
|
||||
|
||||
if (!xdna->rpm_on)
|
||||
return;
|
||||
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
void amdxdna_pm_init(struct amdxdna_dev *xdna)
|
||||
{
|
||||
struct device *dev = xdna->ddev.dev;
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, AMDXDNA_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
xdna->rpm_on = true;
|
||||
}
|
||||
|
||||
void amdxdna_pm_fini(struct amdxdna_dev *xdna)
|
||||
{
|
||||
struct device *dev = xdna->ddev.dev;
|
||||
|
||||
xdna->rpm_on = false;
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2025, Advanced Micro Devices, Inc.
|
||||
*/
|
||||
|
||||
#ifndef _AMDXDNA_PM_H_
|
||||
#define _AMDXDNA_PM_H_
|
||||
|
||||
#include "amdxdna_pci_drv.h"
|
||||
|
||||
int amdxdna_pm_suspend(struct device *dev);
|
||||
int amdxdna_pm_resume(struct device *dev);
|
||||
int amdxdna_pm_resume_get(struct amdxdna_dev *xdna);
|
||||
void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna);
|
||||
void amdxdna_pm_init(struct amdxdna_dev *xdna);
|
||||
void amdxdna_pm_fini(struct amdxdna_dev *xdna);
|
||||
|
||||
#endif /* _AMDXDNA_PM_H_ */
|
||||
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
const struct rt_config npu1_default_rt_cfg[] = {
|
||||
{ 2, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
|
||||
{ 4, 1, AIE2_RT_CFG_INIT }, /* Debug BO */
|
||||
{ 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 0 },
|
||||
};
|
||||
|
|
@ -62,16 +63,23 @@ const struct dpm_clk_freq npu1_dpm_clk_table[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct aie2_fw_feature_tbl npu1_fw_feature_table[] = {
|
||||
{ .feature = AIE2_NPU_COMMAND, .min_minor = 8 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct amdxdna_dev_priv npu1_dev_priv = {
|
||||
.fw_path = "amdnpu/1502_00/npu.sbin",
|
||||
.protocol_major = 0x5,
|
||||
.protocol_minor = 0x7,
|
||||
.rt_config = npu1_default_rt_cfg,
|
||||
.dpm_clk_tbl = npu1_dpm_clk_table,
|
||||
.fw_feature_tbl = npu1_fw_feature_table,
|
||||
.col_align = COL_ALIGN_NONE,
|
||||
.mbox_dev_addr = NPU1_MBOX_BAR_BASE,
|
||||
.mbox_size = 0, /* Use BAR size */
|
||||
.sram_dev_addr = NPU1_SRAM_BAR_BASE,
|
||||
.hwctx_limit = 6,
|
||||
.sram_offs = {
|
||||
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU1_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
|
||||
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU1_SRAM, MPNPU_SRAM_I2X_MAILBOX_15),
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ static const struct amdxdna_dev_priv npu2_dev_priv = {
|
|||
.protocol_minor = 0x6,
|
||||
.rt_config = npu4_default_rt_cfg,
|
||||
.dpm_clk_tbl = npu4_dpm_clk_table,
|
||||
.fw_feature_tbl = npu4_fw_feature_table,
|
||||
.col_align = COL_ALIGN_NATURE,
|
||||
.mbox_dev_addr = NPU2_MBOX_BAR_BASE,
|
||||
.mbox_size = 0, /* Use BAR size */
|
||||
.sram_dev_addr = NPU2_SRAM_BAR_BASE,
|
||||
.hwctx_limit = 16,
|
||||
.sram_offs = {
|
||||
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
|
||||
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU2_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
|
||||
|
|
|
|||
|
|
@ -63,10 +63,14 @@
|
|||
|
||||
const struct rt_config npu4_default_rt_cfg[] = {
|
||||
{ 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
|
||||
{ 10, 1, AIE2_RT_CFG_INIT }, /* DEBUG BUF */
|
||||
{ 14, 0, AIE2_RT_CFG_INIT, BIT_U64(AIE2_PREEMPT) }, /* Frame boundary preemption */
|
||||
{ 1, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 2, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 3, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 4, 1, AIE2_RT_CFG_CLK_GATING }, /* Clock gating on */
|
||||
{ 13, 0, AIE2_RT_CFG_FORCE_PREEMPT },
|
||||
{ 14, 0, AIE2_RT_CFG_FRAME_BOUNDARY_PREEMPT },
|
||||
{ 0 },
|
||||
};
|
||||
|
||||
|
|
@ -82,16 +86,24 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = {
|
||||
{ .feature = AIE2_NPU_COMMAND, .min_minor = 15 },
|
||||
{ .feature = AIE2_PREEMPT, .min_minor = 12 },
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
static const struct amdxdna_dev_priv npu4_dev_priv = {
|
||||
.fw_path = "amdnpu/17f0_10/npu.sbin",
|
||||
.protocol_major = 0x6,
|
||||
.protocol_minor = 12,
|
||||
.rt_config = npu4_default_rt_cfg,
|
||||
.dpm_clk_tbl = npu4_dpm_clk_table,
|
||||
.fw_feature_tbl = npu4_fw_feature_table,
|
||||
.col_align = COL_ALIGN_NATURE,
|
||||
.mbox_dev_addr = NPU4_MBOX_BAR_BASE,
|
||||
.mbox_size = 0, /* Use BAR size */
|
||||
.sram_dev_addr = NPU4_SRAM_BAR_BASE,
|
||||
.hwctx_limit = 16,
|
||||
.sram_offs = {
|
||||
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU4_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
|
||||
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU4_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ static const struct amdxdna_dev_priv npu5_dev_priv = {
|
|||
.protocol_minor = 12,
|
||||
.rt_config = npu4_default_rt_cfg,
|
||||
.dpm_clk_tbl = npu4_dpm_clk_table,
|
||||
.fw_feature_tbl = npu4_fw_feature_table,
|
||||
.col_align = COL_ALIGN_NATURE,
|
||||
.mbox_dev_addr = NPU5_MBOX_BAR_BASE,
|
||||
.mbox_size = 0, /* Use BAR size */
|
||||
.sram_dev_addr = NPU5_SRAM_BAR_BASE,
|
||||
.hwctx_limit = 16,
|
||||
.sram_offs = {
|
||||
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU5_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
|
||||
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU5_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
|
||||
|
|
|
|||
|
|
@ -67,10 +67,12 @@ static const struct amdxdna_dev_priv npu6_dev_priv = {
|
|||
.protocol_minor = 12,
|
||||
.rt_config = npu4_default_rt_cfg,
|
||||
.dpm_clk_tbl = npu4_dpm_clk_table,
|
||||
.fw_feature_tbl = npu4_fw_feature_table,
|
||||
.col_align = COL_ALIGN_NATURE,
|
||||
.mbox_dev_addr = NPU6_MBOX_BAR_BASE,
|
||||
.mbox_size = 0, /* Use BAR size */
|
||||
.sram_dev_addr = NPU6_SRAM_BAR_BASE,
|
||||
.hwctx_limit = 16,
|
||||
.sram_offs = {
|
||||
DEFINE_BAR_OFFSET(MBOX_CHANN_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_0),
|
||||
DEFINE_BAR_OFFSET(FW_ALIVE_OFF, NPU6_SRAM, MPNPU_SRAM_X2I_MAILBOX_15),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
config DRM_ACCEL_ARM_ETHOSU
|
||||
tristate "Arm Ethos-U65/U85 NPU"
|
||||
depends on HAS_IOMEM
|
||||
depends on DRM_ACCEL
|
||||
select DRM_GEM_DMA_HELPER
|
||||
select DRM_SCHED
|
||||
select GENERIC_ALLOCATOR
|
||||
help
|
||||
Enables driver for Arm Ethos-U65/U85 NPUs
|
||||
|
|
@ -0,0 +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
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only or MIT */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
|
||||
#ifndef __ETHOSU_DEVICE_H__
|
||||
#define __ETHOSU_DEVICE_H__
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/gpu_scheduler.h>
|
||||
|
||||
#include <drm/ethosu_accel.h>
|
||||
|
||||
struct clk;
|
||||
struct gen_pool;
|
||||
|
||||
#define NPU_REG_ID 0x0000
|
||||
#define NPU_REG_STATUS 0x0004
|
||||
#define NPU_REG_CMD 0x0008
|
||||
#define NPU_REG_RESET 0x000c
|
||||
#define NPU_REG_QBASE 0x0010
|
||||
#define NPU_REG_QBASE_HI 0x0014
|
||||
#define NPU_REG_QREAD 0x0018
|
||||
#define NPU_REG_QCONFIG 0x001c
|
||||
#define NPU_REG_QSIZE 0x0020
|
||||
#define NPU_REG_PROT 0x0024
|
||||
#define NPU_REG_CONFIG 0x0028
|
||||
#define NPU_REG_REGIONCFG 0x003c
|
||||
#define NPU_REG_AXILIMIT0 0x0040 // U65
|
||||
#define NPU_REG_AXILIMIT1 0x0044 // U65
|
||||
#define NPU_REG_AXILIMIT2 0x0048 // U65
|
||||
#define NPU_REG_AXILIMIT3 0x004c // U65
|
||||
#define NPU_REG_MEM_ATTR0 0x0040 // U85
|
||||
#define NPU_REG_MEM_ATTR1 0x0044 // U85
|
||||
#define NPU_REG_MEM_ATTR2 0x0048 // U85
|
||||
#define NPU_REG_MEM_ATTR3 0x004c // U85
|
||||
#define NPU_REG_AXI_SRAM 0x0050 // U85
|
||||
#define NPU_REG_AXI_EXT 0x0054 // U85
|
||||
|
||||
#define NPU_REG_BASEP(x) (0x0080 + (x) * 8)
|
||||
#define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8)
|
||||
#define NPU_BASEP_REGION_MAX 8
|
||||
|
||||
#define ID_ARCH_MAJOR_MASK GENMASK(31, 28)
|
||||
#define ID_ARCH_MINOR_MASK GENMASK(27, 20)
|
||||
#define ID_ARCH_PATCH_MASK GENMASK(19, 16)
|
||||
#define ID_VER_MAJOR_MASK GENMASK(11, 8)
|
||||
#define ID_VER_MINOR_MASK GENMASK(7, 4)
|
||||
|
||||
#define CONFIG_MACS_PER_CC_MASK GENMASK(3, 0)
|
||||
#define CONFIG_CMD_STREAM_VER_MASK GENMASK(7, 4)
|
||||
|
||||
#define STATUS_STATE_RUNNING BIT(0)
|
||||
#define STATUS_IRQ_RAISED BIT(1)
|
||||
#define STATUS_BUS_STATUS BIT(2)
|
||||
#define STATUS_RESET_STATUS BIT(3)
|
||||
#define STATUS_CMD_PARSE_ERR BIT(4)
|
||||
#define STATUS_CMD_END_REACHED BIT(5)
|
||||
|
||||
#define CMD_CLEAR_IRQ BIT(1)
|
||||
#define CMD_TRANSITION_TO_RUN BIT(0)
|
||||
|
||||
#define RESET_PENDING_CSL BIT(1)
|
||||
#define RESET_PENDING_CPL BIT(0)
|
||||
|
||||
#define PROT_ACTIVE_CSL BIT(1)
|
||||
|
||||
enum ethosu_cmds {
|
||||
NPU_OP_CONV = 0x2,
|
||||
NPU_OP_DEPTHWISE = 0x3,
|
||||
NPU_OP_POOL = 0x5,
|
||||
NPU_OP_ELEMENTWISE = 0x6,
|
||||
NPU_OP_RESIZE = 0x7, // U85 only
|
||||
NPU_OP_DMA_START = 0x10,
|
||||
NPU_SET_IFM_PAD_TOP = 0x100,
|
||||
NPU_SET_IFM_PAD_LEFT = 0x101,
|
||||
NPU_SET_IFM_PAD_RIGHT = 0x102,
|
||||
NPU_SET_IFM_PAD_BOTTOM = 0x103,
|
||||
NPU_SET_IFM_DEPTH_M1 = 0x104,
|
||||
NPU_SET_IFM_PRECISION = 0x105,
|
||||
NPU_SET_IFM_BROADCAST = 0x108,
|
||||
NPU_SET_IFM_WIDTH0_M1 = 0x10a,
|
||||
NPU_SET_IFM_HEIGHT0_M1 = 0x10b,
|
||||
NPU_SET_IFM_HEIGHT1_M1 = 0x10c,
|
||||
NPU_SET_IFM_REGION = 0x10f,
|
||||
NPU_SET_OFM_WIDTH_M1 = 0x111,
|
||||
NPU_SET_OFM_HEIGHT_M1 = 0x112,
|
||||
NPU_SET_OFM_DEPTH_M1 = 0x113,
|
||||
NPU_SET_OFM_PRECISION = 0x114,
|
||||
NPU_SET_OFM_WIDTH0_M1 = 0x11a,
|
||||
NPU_SET_OFM_HEIGHT0_M1 = 0x11b,
|
||||
NPU_SET_OFM_HEIGHT1_M1 = 0x11c,
|
||||
NPU_SET_OFM_REGION = 0x11f,
|
||||
NPU_SET_KERNEL_WIDTH_M1 = 0x120,
|
||||
NPU_SET_KERNEL_HEIGHT_M1 = 0x121,
|
||||
NPU_SET_KERNEL_STRIDE = 0x122,
|
||||
NPU_SET_WEIGHT_REGION = 0x128,
|
||||
NPU_SET_SCALE_REGION = 0x129,
|
||||
NPU_SET_DMA0_SRC_REGION = 0x130,
|
||||
NPU_SET_DMA0_DST_REGION = 0x131,
|
||||
NPU_SET_DMA0_SIZE0 = 0x132,
|
||||
NPU_SET_DMA0_SIZE1 = 0x133,
|
||||
NPU_SET_IFM2_BROADCAST = 0x180,
|
||||
NPU_SET_IFM2_PRECISION = 0x185,
|
||||
NPU_SET_IFM2_WIDTH0_M1 = 0x18a,
|
||||
NPU_SET_IFM2_HEIGHT0_M1 = 0x18b,
|
||||
NPU_SET_IFM2_HEIGHT1_M1 = 0x18c,
|
||||
NPU_SET_IFM2_REGION = 0x18f,
|
||||
NPU_SET_IFM_BASE0 = 0x4000,
|
||||
NPU_SET_IFM_BASE1 = 0x4001,
|
||||
NPU_SET_IFM_BASE2 = 0x4002,
|
||||
NPU_SET_IFM_BASE3 = 0x4003,
|
||||
NPU_SET_IFM_STRIDE_X = 0x4004,
|
||||
NPU_SET_IFM_STRIDE_Y = 0x4005,
|
||||
NPU_SET_IFM_STRIDE_C = 0x4006,
|
||||
NPU_SET_OFM_BASE0 = 0x4010,
|
||||
NPU_SET_OFM_BASE1 = 0x4011,
|
||||
NPU_SET_OFM_BASE2 = 0x4012,
|
||||
NPU_SET_OFM_BASE3 = 0x4013,
|
||||
NPU_SET_OFM_STRIDE_X = 0x4014,
|
||||
NPU_SET_OFM_STRIDE_Y = 0x4015,
|
||||
NPU_SET_OFM_STRIDE_C = 0x4016,
|
||||
NPU_SET_WEIGHT_BASE = 0x4020,
|
||||
NPU_SET_WEIGHT_LENGTH = 0x4021,
|
||||
NPU_SET_SCALE_BASE = 0x4022,
|
||||
NPU_SET_SCALE_LENGTH = 0x4023,
|
||||
NPU_SET_DMA0_SRC = 0x4030,
|
||||
NPU_SET_DMA0_DST = 0x4031,
|
||||
NPU_SET_DMA0_LEN = 0x4032,
|
||||
NPU_SET_DMA0_SRC_STRIDE0 = 0x4033,
|
||||
NPU_SET_DMA0_SRC_STRIDE1 = 0x4034,
|
||||
NPU_SET_DMA0_DST_STRIDE0 = 0x4035,
|
||||
NPU_SET_DMA0_DST_STRIDE1 = 0x4036,
|
||||
NPU_SET_IFM2_BASE0 = 0x4080,
|
||||
NPU_SET_IFM2_BASE1 = 0x4081,
|
||||
NPU_SET_IFM2_BASE2 = 0x4082,
|
||||
NPU_SET_IFM2_BASE3 = 0x4083,
|
||||
NPU_SET_IFM2_STRIDE_X = 0x4084,
|
||||
NPU_SET_IFM2_STRIDE_Y = 0x4085,
|
||||
NPU_SET_IFM2_STRIDE_C = 0x4086,
|
||||
NPU_SET_WEIGHT1_BASE = 0x4090,
|
||||
NPU_SET_WEIGHT1_LENGTH = 0x4091,
|
||||
NPU_SET_SCALE1_BASE = 0x4092,
|
||||
NPU_SET_WEIGHT2_BASE = 0x4092,
|
||||
NPU_SET_SCALE1_LENGTH = 0x4093,
|
||||
NPU_SET_WEIGHT2_LENGTH = 0x4093,
|
||||
NPU_SET_WEIGHT3_BASE = 0x4094,
|
||||
NPU_SET_WEIGHT3_LENGTH = 0x4095,
|
||||
};
|
||||
|
||||
#define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */
|
||||
|
||||
/**
|
||||
* struct ethosu_device - Ethosu device
|
||||
*/
|
||||
struct ethosu_device {
|
||||
/** @base: Base drm_device. */
|
||||
struct drm_device base;
|
||||
|
||||
/** @iomem: CPU mapping of the registers. */
|
||||
void __iomem *regs;
|
||||
|
||||
void __iomem *sram;
|
||||
struct gen_pool *srampool;
|
||||
dma_addr_t sramphys;
|
||||
|
||||
struct clk_bulk_data *clks;
|
||||
int num_clks;
|
||||
int irq;
|
||||
|
||||
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;
|
||||
|
||||
struct drm_gpu_scheduler sched;
|
||||
/* For ethosu_job_do_push() */
|
||||
struct mutex sched_lock;
|
||||
u64 fence_context;
|
||||
u64 emit_seqno;
|
||||
};
|
||||
|
||||
#define to_ethosu_device(drm_dev) \
|
||||
((struct ethosu_device *)container_of(drm_dev, struct ethosu_device, base))
|
||||
|
||||
static inline bool ethosu_is_u65(const struct ethosu_device *ethosudev)
|
||||
{
|
||||
return FIELD_GET(ID_ARCH_MAJOR_MASK, ethosudev->npu_info.id) == 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,403 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only or MIT
|
||||
// Copyright (C) 2025 Arm, Ltd.
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_ioctl.h>
|
||||
#include <drm/drm_utils.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_accel.h>
|
||||
#include <drm/ethosu_accel.h>
|
||||
|
||||
#include "ethosu_drv.h"
|
||||
#include "ethosu_device.h"
|
||||
#include "ethosu_gem.h"
|
||||
#include "ethosu_job.h"
|
||||
|
||||
static int ethosu_ioctl_dev_query(struct drm_device *ddev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct ethosu_device *ethosudev = to_ethosu_device(ddev);
|
||||
struct drm_ethosu_dev_query *args = data;
|
||||
|
||||
if (!args->pointer) {
|
||||
switch (args->type) {
|
||||
case DRM_ETHOSU_DEV_QUERY_NPU_INFO:
|
||||
args->size = sizeof(ethosudev->npu_info);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
switch (args->type) {
|
||||
case DRM_ETHOSU_DEV_QUERY_NPU_INFO:
|
||||
if (args->size < offsetofend(struct drm_ethosu_npu_info, sram_size))
|
||||
return -EINVAL;
|
||||
return copy_struct_to_user(u64_to_user_ptr(args->pointer),
|
||||
args->size,
|
||||
ðosudev->npu_info,
|
||||
sizeof(ethosudev->npu_info), NULL);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define ETHOSU_BO_FLAGS DRM_ETHOSU_BO_NO_MMAP
|
||||
|
||||
static int ethosu_ioctl_bo_create(struct drm_device *ddev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_ethosu_bo_create *args = data;
|
||||
int cookie, ret;
|
||||
|
||||
if (!drm_dev_enter(ddev, &cookie))
|
||||
return -ENODEV;
|
||||
|
||||
if (!args->size || (args->flags & ~ETHOSU_BO_FLAGS)) {
|
||||
ret = -EINVAL;
|
||||
goto out_dev_exit;
|
||||
}
|
||||
|
||||
ret = ethosu_gem_create_with_handle(file, ddev, &args->size,
|
||||
args->flags, &args->handle);
|
||||
|
||||
out_dev_exit:
|
||||
drm_dev_exit(cookie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ethosu_ioctl_bo_wait(struct drm_device *ddev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_ethosu_bo_wait *args = data;
|
||||
int cookie, ret;
|
||||
unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
|
||||
|
||||
if (args->pad)
|
||||
return -EINVAL;
|
||||
|
||||
if (!drm_dev_enter(ddev, &cookie))
|
||||
return -ENODEV;
|
||||
|
||||
ret = drm_gem_dma_resv_wait(file, args->handle, true, timeout);
|
||||
|
||||
drm_dev_exit(cookie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ethosu_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_ethosu_bo_mmap_offset *args = data;
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
if (args->pad)
|
||||
return -EINVAL;
|
||||
|
||||
obj = drm_gem_object_lookup(file, args->handle);
|
||||
if (!obj)
|
||||
return -ENOENT;
|
||||
|
||||
args->offset = drm_vma_node_offset_addr(&obj->vma_node);
|
||||
drm_gem_object_put(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethosu_ioctl_cmdstream_bo_create(struct drm_device *ddev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_ethosu_cmdstream_bo_create *args = data;
|
||||
int cookie, ret;
|
||||
|
||||
if (!drm_dev_enter(ddev, &cookie))
|
||||
return -ENODEV;
|
||||
|
||||
if (!args->size || !args->data || args->pad || args->flags) {
|
||||
ret = -EINVAL;
|
||||
goto out_dev_exit;
|
||||
}
|
||||
|
||||
args->flags |= DRM_ETHOSU_BO_NO_MMAP;
|
||||
|
||||
ret = ethosu_gem_cmdstream_create(file, ddev, args->size, args->data,
|
||||
args->flags, &args->handle);
|
||||
|
||||
out_dev_exit:
|
||||
drm_dev_exit(cookie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ethosu_open(struct drm_device *ddev, struct drm_file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -EINVAL;
|
||||
|
||||
struct ethosu_file_priv __free(kfree) *priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto err_put_mod;
|
||||
}
|
||||
priv->edev = to_ethosu_device(ddev);
|
||||
|
||||
ret = ethosu_job_open(priv);
|
||||
if (ret)
|
||||
goto err_put_mod;
|
||||
|
||||
file->driver_priv = no_free_ptr(priv);
|
||||
return 0;
|
||||
|
||||
err_put_mod:
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file)
|
||||
{
|
||||
ethosu_job_close(file->driver_priv);
|
||||
kfree(file->driver_priv);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = {
|
||||
#define ETHOSU_IOCTL(n, func, flags) \
|
||||
DRM_IOCTL_DEF_DRV(ETHOSU_##n, ethosu_ioctl_##func, flags)
|
||||
|
||||
ETHOSU_IOCTL(DEV_QUERY, dev_query, 0),
|
||||
ETHOSU_IOCTL(BO_CREATE, bo_create, 0),
|
||||
ETHOSU_IOCTL(BO_WAIT, bo_wait, 0),
|
||||
ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0),
|
||||
ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0),
|
||||
ETHOSU_IOCTL(SUBMIT, submit, 0),
|
||||
};
|
||||
|
||||
DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops);
|
||||
|
||||
/*
|
||||
* Ethosu driver version:
|
||||
* - 1.0 - initial interface
|
||||
*/
|
||||
static const struct drm_driver ethosu_drm_driver = {
|
||||
.driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM,
|
||||
.open = ethosu_open,
|
||||
.postclose = ethosu_postclose,
|
||||
.ioctls = ethosu_drm_driver_ioctls,
|
||||
.num_ioctls = ARRAY_SIZE(ethosu_drm_driver_ioctls),
|
||||
.fops = ðosu_drm_driver_fops,
|
||||
.name = "ethosu",
|
||||
.desc = "Arm Ethos-U Accel driver",
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
|
||||
.gem_create_object = ethosu_gem_create_object,
|
||||
};
|
||||
|
||||
#define U65_DRAM_AXI_LIMIT_CFG 0x1f3f0002
|
||||
#define U65_SRAM_AXI_LIMIT_CFG 0x1f3f00b0
|
||||
#define U85_AXI_EXT_CFG 0x00021f3f
|
||||
#define U85_AXI_SRAM_CFG 0x00021f3f
|
||||
#define U85_MEM_ATTR0_CFG 0x00000000
|
||||
#define U85_MEM_ATTR2_CFG 0x000000b7
|
||||
|
||||
static int ethosu_reset(struct ethosu_device *ethosudev)
|
||||
{
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
writel_relaxed(RESET_PENDING_CSL, ethosudev->regs + NPU_REG_RESET);
|
||||
ret = readl_poll_timeout(ethosudev->regs + NPU_REG_STATUS, reg,
|
||||
!FIELD_GET(STATUS_RESET_STATUS, reg),
|
||||
USEC_PER_MSEC, USEC_PER_SEC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!FIELD_GET(PROT_ACTIVE_CSL, readl_relaxed(ethosudev->regs + NPU_REG_PROT))) {
|
||||
dev_warn(ethosudev->base.dev, "Could not reset to non-secure mode (PROT = %x)\n",
|
||||
readl_relaxed(ethosudev->regs + NPU_REG_PROT));
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign region 2 (SRAM) to AXI M0 (AXILIMIT0),
|
||||
* everything else to AXI M1 (AXILIMIT2)
|
||||
*/
|
||||
writel_relaxed(0x0000aa8a, ethosudev->regs + NPU_REG_REGIONCFG);
|
||||
if (ethosu_is_u65(ethosudev)) {
|
||||
writel_relaxed(U65_SRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT0);
|
||||
writel_relaxed(U65_DRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT2);
|
||||
} else {
|
||||
writel_relaxed(U85_AXI_SRAM_CFG, ethosudev->regs + NPU_REG_AXI_SRAM);
|
||||
writel_relaxed(U85_AXI_EXT_CFG, ethosudev->regs + NPU_REG_AXI_EXT);
|
||||
writel_relaxed(U85_MEM_ATTR0_CFG, ethosudev->regs + NPU_REG_MEM_ATTR0); // SRAM
|
||||
writel_relaxed(U85_MEM_ATTR2_CFG, ethosudev->regs + NPU_REG_MEM_ATTR2); // DRAM
|
||||
}
|
||||
|
||||
if (ethosudev->sram)
|
||||
memset_io(ethosudev->sram, 0, ethosudev->npu_info.sram_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethosu_device_resume(struct device *dev)
|
||||
{
|
||||
struct ethosu_device *ethosudev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(ethosudev->num_clks, ethosudev->clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ethosu_reset(ethosudev);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ethosu_device_suspend(struct device *dev)
|
||||
{
|
||||
struct ethosu_device *ethosudev = dev_get_drvdata(dev);
|
||||
|
||||
clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethosu_sram_init(struct ethosu_device *ethosudev)
|
||||
{
|
||||
ethosudev->npu_info.sram_size = 0;
|
||||
|
||||
ethosudev->srampool = of_gen_pool_get(ethosudev->base.dev->of_node, "sram", 0);
|
||||
if (!ethosudev->srampool)
|
||||
return 0;
|
||||
|
||||
ethosudev->npu_info.sram_size = gen_pool_size(ethosudev->srampool);
|
||||
|
||||
ethosudev->sram = (void __iomem *)gen_pool_dma_alloc(ethosudev->srampool,
|
||||
ethosudev->npu_info.sram_size,
|
||||
ðosudev->sramphys);
|
||||
if (!ethosudev->sram) {
|
||||
dev_err(ethosudev->base.dev, "failed to allocate from SRAM pool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethosu_init(struct ethosu_device *ethosudev)
|
||||
{
|
||||
int ret;
|
||||
u32 id, config;
|
||||
|
||||
ret = ethosu_device_resume(ethosudev->base.dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50);
|
||||
pm_runtime_use_autosuspend(ethosudev->base.dev);
|
||||
ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
pm_runtime_get_noresume(ethosudev->base.dev);
|
||||
|
||||
ethosudev->npu_info.id = id = readl_relaxed(ethosudev->regs + NPU_REG_ID);
|
||||
ethosudev->npu_info.config = config = readl_relaxed(ethosudev->regs + NPU_REG_CONFIG);
|
||||
|
||||
ethosu_sram_init(ethosudev);
|
||||
|
||||
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",
|
||||
FIELD_GET(ID_ARCH_MAJOR_MASK, id),
|
||||
FIELD_GET(ID_ARCH_MINOR_MASK, id),
|
||||
FIELD_GET(ID_ARCH_PATCH_MASK, id),
|
||||
FIELD_GET(ID_VER_MAJOR_MASK, id),
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethosu_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct ethosu_device *ethosudev;
|
||||
|
||||
ethosudev = devm_drm_dev_alloc(&pdev->dev, ðosu_drm_driver,
|
||||
struct ethosu_device, base);
|
||||
if (IS_ERR(ethosudev))
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, ethosudev);
|
||||
|
||||
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
|
||||
|
||||
ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
|
||||
ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks);
|
||||
if (ethosudev->num_clks < 0)
|
||||
return ethosudev->num_clks;
|
||||
|
||||
ret = ethosu_job_init(ethosudev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ethosu_init(ethosudev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = drm_dev_register(ðosudev->base, 0);
|
||||
if (ret)
|
||||
pm_runtime_dont_use_autosuspend(ethosudev->base.dev);
|
||||
|
||||
pm_runtime_put_autosuspend(ethosudev->base.dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ethosu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ethosu_device *ethosudev = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
drm_dev_unregister(ðosudev->base);
|
||||
ethosu_job_fini(ethosudev);
|
||||
if (ethosudev->sram)
|
||||
gen_pool_free(ethosudev->srampool, (unsigned long)ethosudev->sram,
|
||||
ethosudev->npu_info.sram_size);
|
||||
}
|
||||
|
||||
static const struct of_device_id dt_match[] = {
|
||||
{ .compatible = "arm,ethos-u65" },
|
||||
{ .compatible = "arm,ethos-u85" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dt_match);
|
||||
|
||||
static DEFINE_RUNTIME_DEV_PM_OPS(ethosu_pm_ops,
|
||||
ethosu_device_suspend,
|
||||
ethosu_device_resume,
|
||||
NULL);
|
||||
|
||||
static struct platform_driver ethosu_driver = {
|
||||
.probe = ethosu_probe,
|
||||
.remove = ethosu_remove,
|
||||
.driver = {
|
||||
.name = "ethosu",
|
||||
.pm = pm_ptr(ðosu_pm_ops),
|
||||
.of_match_table = dt_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ethosu_driver);
|
||||
|
||||
MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
|
||||
MODULE_DESCRIPTION("Arm Ethos-U Accel Driver");
|
||||
MODULE_LICENSE("Dual MIT/GPL");
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
#ifndef __ETHOSU_DRV_H__
|
||||
#define __ETHOSU_DRV_H__
|
||||
|
||||
#include <drm/gpu_scheduler.h>
|
||||
|
||||
struct ethosu_device;
|
||||
|
||||
struct ethosu_file_priv {
|
||||
struct ethosu_device *edev;
|
||||
struct drm_sched_entity sched_entity;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,704 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only or MIT
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <drm/ethosu_accel.h>
|
||||
|
||||
#include "ethosu_device.h"
|
||||
#include "ethosu_gem.h"
|
||||
|
||||
static void ethosu_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct ethosu_gem_object *bo = to_ethosu_bo(obj);
|
||||
|
||||
kfree(bo->info);
|
||||
drm_gem_free_mmap_offset(&bo->base.base);
|
||||
drm_gem_dma_free(&bo->base);
|
||||
}
|
||||
|
||||
static int ethosu_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
|
||||
{
|
||||
struct ethosu_gem_object *bo = to_ethosu_bo(obj);
|
||||
|
||||
/* Don't allow mmap on objects that have the NO_MMAP flag set. */
|
||||
if (bo->flags & DRM_ETHOSU_BO_NO_MMAP)
|
||||
return -EINVAL;
|
||||
|
||||
return drm_gem_dma_object_mmap(obj, vma);
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs ethosu_gem_funcs = {
|
||||
.free = ethosu_gem_free_object,
|
||||
.print_info = drm_gem_dma_object_print_info,
|
||||
.get_sg_table = drm_gem_dma_object_get_sg_table,
|
||||
.vmap = drm_gem_dma_object_vmap,
|
||||
.mmap = ethosu_gem_mmap,
|
||||
.vm_ops = &drm_gem_dma_vm_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* ethosu_gem_create_object - Implementation of driver->gem_create_object.
|
||||
* @ddev: DRM device
|
||||
* @size: Size in bytes of the memory the object will reference
|
||||
*
|
||||
* This lets the GEM helpers allocate object structs for us, and keep
|
||||
* our BO stats correct.
|
||||
*/
|
||||
struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev, size_t size)
|
||||
{
|
||||
struct ethosu_gem_object *obj;
|
||||
|
||||
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
obj->base.base.funcs = ðosu_gem_funcs;
|
||||
return &obj->base.base;
|
||||
}
|
||||
|
||||
/**
|
||||
* ethosu_gem_create_with_handle() - Create a GEM object and attach it to a handle.
|
||||
* @file: DRM file.
|
||||
* @ddev: DRM device.
|
||||
* @size: Size of the GEM object to allocate.
|
||||
* @flags: Combination of drm_ethosu_bo_flags flags.
|
||||
* @handle: Pointer holding the handle pointing to the new GEM object.
|
||||
*
|
||||
* Return: Zero on success
|
||||
*/
|
||||
int ethosu_gem_create_with_handle(struct drm_file *file,
|
||||
struct drm_device *ddev,
|
||||
u64 *size, u32 flags, u32 *handle)
|
||||
{
|
||||
struct drm_gem_dma_object *mem;
|
||||
struct ethosu_gem_object *bo;
|
||||
int ret;
|
||||
|
||||
mem = drm_gem_dma_create(ddev, *size);
|
||||
if (IS_ERR(mem))
|
||||
return PTR_ERR(mem);
|
||||
|
||||
bo = to_ethosu_bo(&mem->base);
|
||||
bo->flags = flags;
|
||||
|
||||
/*
|
||||
* Allocate an id of idr table where the obj is registered
|
||||
* and handle has the id what user can see.
|
||||
*/
|
||||
ret = drm_gem_handle_create(file, &mem->base, handle);
|
||||
if (!ret)
|
||||
*size = bo->base.base.size;
|
||||
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_put(&mem->base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dma {
|
||||
s8 region;
|
||||
u64 len;
|
||||
u64 offset;
|
||||
s64 stride[2];
|
||||
};
|
||||
|
||||
struct dma_state {
|
||||
u16 size0;
|
||||
u16 size1;
|
||||
s8 mode;
|
||||
struct dma src;
|
||||
struct dma dst;
|
||||
};
|
||||
|
||||
struct buffer {
|
||||
u64 base;
|
||||
u32 length;
|
||||
s8 region;
|
||||
};
|
||||
|
||||
struct feat_matrix {
|
||||
u64 base[4];
|
||||
s64 stride_x;
|
||||
s64 stride_y;
|
||||
s64 stride_c;
|
||||
s8 region;
|
||||
u8 broadcast;
|
||||
u16 stride_kernel;
|
||||
u16 precision;
|
||||
u16 depth;
|
||||
u16 width;
|
||||
u16 width0;
|
||||
u16 height[3];
|
||||
u8 pad_top;
|
||||
u8 pad_left;
|
||||
u8 pad_bottom;
|
||||
u8 pad_right;
|
||||
};
|
||||
|
||||
struct cmd_state {
|
||||
struct dma_state dma;
|
||||
struct buffer scale[2];
|
||||
struct buffer weight[4];
|
||||
struct feat_matrix ofm;
|
||||
struct feat_matrix ifm;
|
||||
struct feat_matrix ifm2;
|
||||
};
|
||||
|
||||
static void cmd_state_init(struct cmd_state *st)
|
||||
{
|
||||
/* Initialize to all 1s to detect missing setup */
|
||||
memset(st, 0xff, sizeof(*st));
|
||||
}
|
||||
|
||||
static u64 cmd_to_addr(u32 *cmd)
|
||||
{
|
||||
return ((u64)((cmd[0] & 0xff0000) << 16)) | cmd[1];
|
||||
}
|
||||
|
||||
static u64 dma_length(struct ethosu_validated_cmdstream_info *info,
|
||||
struct dma_state *dma_st, struct dma *dma)
|
||||
{
|
||||
s8 mode = dma_st->mode;
|
||||
u64 len = dma->len;
|
||||
|
||||
if (mode >= 1) {
|
||||
len += dma->stride[0];
|
||||
len *= dma_st->size0;
|
||||
}
|
||||
if (mode == 2) {
|
||||
len += dma->stride[1];
|
||||
len *= dma_st->size1;
|
||||
}
|
||||
if (dma->region >= 0)
|
||||
info->region_size[dma->region] = max(info->region_size[dma->region],
|
||||
len + dma->offset);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info,
|
||||
struct feat_matrix *fm,
|
||||
u32 x, u32 y, u32 c)
|
||||
{
|
||||
u32 element_size, storage = fm->precision >> 14;
|
||||
int tile = 0;
|
||||
u64 addr;
|
||||
|
||||
if (fm->region < 0)
|
||||
return U64_MAX;
|
||||
|
||||
switch (storage) {
|
||||
case 0:
|
||||
if (x >= fm->width0 + 1) {
|
||||
x -= fm->width0 + 1;
|
||||
tile += 1;
|
||||
}
|
||||
if (y >= fm->height[tile] + 1) {
|
||||
y -= fm->height[tile] + 1;
|
||||
tile += 2;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (y >= fm->height[1] + 1) {
|
||||
y -= fm->height[1] + 1;
|
||||
tile = 2;
|
||||
} else if (y >= fm->height[0] + 1) {
|
||||
y -= fm->height[0] + 1;
|
||||
tile = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (fm->base[tile] == U64_MAX)
|
||||
return U64_MAX;
|
||||
|
||||
addr = fm->base[tile] + y * fm->stride_y;
|
||||
|
||||
switch ((fm->precision >> 6) & 0x3) { // format
|
||||
case 0: //nhwc:
|
||||
addr += x * fm->stride_x + c;
|
||||
break;
|
||||
case 1: //nhcwb16:
|
||||
element_size = BIT((fm->precision >> 1) & 0x3);
|
||||
|
||||
addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size;
|
||||
break;
|
||||
}
|
||||
|
||||
info->region_size[fm->region] = max(info->region_size[fm->region], addr + 1);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static int calc_sizes(struct drm_device *ddev,
|
||||
struct ethosu_validated_cmdstream_info *info,
|
||||
u16 op, struct cmd_state *st,
|
||||
bool ifm, bool ifm2, bool weight, bool scale)
|
||||
{
|
||||
u64 len;
|
||||
|
||||
if (ifm) {
|
||||
if (st->ifm.stride_kernel == U16_MAX)
|
||||
return -EINVAL;
|
||||
u32 stride_y = ((st->ifm.stride_kernel >> 8) & 0x2) +
|
||||
((st->ifm.stride_kernel >> 1) & 0x1) + 1;
|
||||
u32 stride_x = ((st->ifm.stride_kernel >> 5) & 0x2) +
|
||||
(st->ifm.stride_kernel & 0x1) + 1;
|
||||
u32 ifm_height = st->ofm.height[2] * stride_y +
|
||||
st->ifm.height[2] - (st->ifm.pad_top + st->ifm.pad_bottom);
|
||||
u32 ifm_width = st->ofm.width * stride_x +
|
||||
st->ifm.width - (st->ifm.pad_left + st->ifm.pad_right);
|
||||
|
||||
len = feat_matrix_length(info, &st->ifm, ifm_width,
|
||||
ifm_height, st->ifm.depth);
|
||||
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
|
||||
op, st->ifm.region, st->ifm.base[0], len);
|
||||
if (len == U64_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ifm2) {
|
||||
len = feat_matrix_length(info, &st->ifm2, st->ifm.depth,
|
||||
0, st->ofm.depth);
|
||||
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
|
||||
op, st->ifm2.region, st->ifm2.base[0], len);
|
||||
if (len == U64_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (weight) {
|
||||
dev_dbg(ddev->dev, "op %d: W:%d:0x%llx-0x%llx\n",
|
||||
op, st->weight[0].region, st->weight[0].base,
|
||||
st->weight[0].base + st->weight[0].length - 1);
|
||||
if (st->weight[0].region < 0 || st->weight[0].base == U64_MAX ||
|
||||
st->weight[0].length == U32_MAX)
|
||||
return -EINVAL;
|
||||
info->region_size[st->weight[0].region] =
|
||||
max(info->region_size[st->weight[0].region],
|
||||
st->weight[0].base + st->weight[0].length);
|
||||
}
|
||||
|
||||
if (scale) {
|
||||
dev_dbg(ddev->dev, "op %d: S:%d:0x%llx-0x%llx\n",
|
||||
op, st->scale[0].region, st->scale[0].base,
|
||||
st->scale[0].base + st->scale[0].length - 1);
|
||||
if (st->scale[0].region < 0 || st->scale[0].base == U64_MAX ||
|
||||
st->scale[0].length == U32_MAX)
|
||||
return -EINVAL;
|
||||
info->region_size[st->scale[0].region] =
|
||||
max(info->region_size[st->scale[0].region],
|
||||
st->scale[0].base + st->scale[0].length);
|
||||
}
|
||||
|
||||
len = feat_matrix_length(info, &st->ofm, st->ofm.width,
|
||||
st->ofm.height[2], st->ofm.depth);
|
||||
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
|
||||
op, st->ofm.region, st->ofm.base[0], len);
|
||||
if (len == U64_MAX)
|
||||
return -EINVAL;
|
||||
info->output_region[st->ofm.region] = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int calc_sizes_elemwise(struct drm_device *ddev,
|
||||
struct ethosu_validated_cmdstream_info *info,
|
||||
u16 op, struct cmd_state *st,
|
||||
bool ifm, bool ifm2)
|
||||
{
|
||||
u32 height, width, depth;
|
||||
u64 len;
|
||||
|
||||
if (ifm) {
|
||||
height = st->ifm.broadcast & 0x1 ? 0 : st->ofm.height[2];
|
||||
width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width;
|
||||
depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth;
|
||||
|
||||
len = feat_matrix_length(info, &st->ifm, width,
|
||||
height, depth);
|
||||
dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n",
|
||||
op, st->ifm.region, st->ifm.base[0], len);
|
||||
if (len == U64_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ifm2) {
|
||||
height = st->ifm2.broadcast & 0x1 ? 0 : st->ofm.height[2];
|
||||
width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width;
|
||||
depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth;
|
||||
|
||||
len = feat_matrix_length(info, &st->ifm2, width,
|
||||
height, depth);
|
||||
dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n",
|
||||
op, st->ifm2.region, st->ifm2.base[0], len);
|
||||
if (len == U64_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
len = feat_matrix_length(info, &st->ofm, st->ofm.width,
|
||||
st->ofm.height[2], st->ofm.depth);
|
||||
dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n",
|
||||
op, st->ofm.region, st->ofm.base[0], len);
|
||||
if (len == U64_MAX)
|
||||
return -EINVAL;
|
||||
info->output_region[st->ofm.region] = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev,
|
||||
u32 __user *ucmds,
|
||||
struct ethosu_gem_object *bo,
|
||||
u32 size)
|
||||
{
|
||||
struct ethosu_validated_cmdstream_info __free(kfree) *info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
struct ethosu_device *edev = to_ethosu_device(ddev);
|
||||
u32 *bocmds = bo->base.vaddr;
|
||||
struct cmd_state st;
|
||||
int i, ret;
|
||||
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
info->cmd_size = size;
|
||||
|
||||
cmd_state_init(&st);
|
||||
|
||||
for (i = 0; i < size / 4; i++) {
|
||||
bool use_ifm, use_ifm2, use_scale;
|
||||
u64 dstlen, srclen;
|
||||
u16 cmd, param;
|
||||
u32 cmds[2];
|
||||
u64 addr;
|
||||
|
||||
if (get_user(cmds[0], ucmds++))
|
||||
return -EFAULT;
|
||||
|
||||
bocmds[i] = cmds[0];
|
||||
|
||||
cmd = cmds[0];
|
||||
param = cmds[0] >> 16;
|
||||
|
||||
if (cmd & 0x4000) {
|
||||
if (get_user(cmds[1], ucmds++))
|
||||
return -EFAULT;
|
||||
|
||||
i++;
|
||||
bocmds[i] = cmds[1];
|
||||
addr = cmd_to_addr(cmds);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case NPU_OP_DMA_START:
|
||||
srclen = dma_length(info, &st.dma, &st.dma.src);
|
||||
dstlen = dma_length(info, &st.dma, &st.dma.dst);
|
||||
|
||||
if (st.dma.dst.region >= 0)
|
||||
info->output_region[st.dma.dst.region] = true;
|
||||
dev_dbg(ddev->dev, "cmd: DMA SRC:%d:0x%llx+0x%llx DST:%d:0x%llx+0x%llx\n",
|
||||
st.dma.src.region, st.dma.src.offset, srclen,
|
||||
st.dma.dst.region, st.dma.dst.offset, dstlen);
|
||||
break;
|
||||
case NPU_OP_CONV:
|
||||
case NPU_OP_DEPTHWISE:
|
||||
use_ifm2 = param & 0x1; // weights_ifm2
|
||||
use_scale = !(st.ofm.precision & 0x100);
|
||||
ret = calc_sizes(ddev, info, cmd, &st, true, use_ifm2,
|
||||
!use_ifm2, use_scale);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case NPU_OP_POOL:
|
||||
use_ifm = param != 0x4; // pooling mode
|
||||
use_scale = !(st.ofm.precision & 0x100);
|
||||
ret = calc_sizes(ddev, info, cmd, &st, use_ifm, false,
|
||||
false, use_scale);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case NPU_OP_ELEMENTWISE:
|
||||
use_ifm2 = !((st.ifm2.broadcast == 8) || (param == 5) ||
|
||||
(param == 6) || (param == 7) || (param == 0x24));
|
||||
use_ifm = st.ifm.broadcast != 8;
|
||||
ret = calc_sizes_elemwise(ddev, info, cmd, &st, use_ifm, use_ifm2);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case NPU_OP_RESIZE: // U85 only
|
||||
WARN_ON(1); // TODO
|
||||
break;
|
||||
case NPU_SET_KERNEL_WIDTH_M1:
|
||||
st.ifm.width = param;
|
||||
break;
|
||||
case NPU_SET_KERNEL_HEIGHT_M1:
|
||||
st.ifm.height[2] = param;
|
||||
break;
|
||||
case NPU_SET_KERNEL_STRIDE:
|
||||
st.ifm.stride_kernel = param;
|
||||
break;
|
||||
case NPU_SET_IFM_PAD_TOP:
|
||||
st.ifm.pad_top = param & 0x7f;
|
||||
break;
|
||||
case NPU_SET_IFM_PAD_LEFT:
|
||||
st.ifm.pad_left = param & 0x7f;
|
||||
break;
|
||||
case NPU_SET_IFM_PAD_RIGHT:
|
||||
st.ifm.pad_right = param & 0xff;
|
||||
break;
|
||||
case NPU_SET_IFM_PAD_BOTTOM:
|
||||
st.ifm.pad_bottom = param & 0xff;
|
||||
break;
|
||||
case NPU_SET_IFM_DEPTH_M1:
|
||||
st.ifm.depth = param;
|
||||
break;
|
||||
case NPU_SET_IFM_PRECISION:
|
||||
st.ifm.precision = param;
|
||||
break;
|
||||
case NPU_SET_IFM_BROADCAST:
|
||||
st.ifm.broadcast = param;
|
||||
break;
|
||||
case NPU_SET_IFM_REGION:
|
||||
st.ifm.region = param & 0x7f;
|
||||
break;
|
||||
case NPU_SET_IFM_WIDTH0_M1:
|
||||
st.ifm.width0 = param;
|
||||
break;
|
||||
case NPU_SET_IFM_HEIGHT0_M1:
|
||||
st.ifm.height[0] = param;
|
||||
break;
|
||||
case NPU_SET_IFM_HEIGHT1_M1:
|
||||
st.ifm.height[1] = param;
|
||||
break;
|
||||
case NPU_SET_IFM_BASE0:
|
||||
case NPU_SET_IFM_BASE1:
|
||||
case NPU_SET_IFM_BASE2:
|
||||
case NPU_SET_IFM_BASE3:
|
||||
st.ifm.base[cmd & 0x3] = addr;
|
||||
break;
|
||||
case NPU_SET_IFM_STRIDE_X:
|
||||
st.ifm.stride_x = addr;
|
||||
break;
|
||||
case NPU_SET_IFM_STRIDE_Y:
|
||||
st.ifm.stride_y = addr;
|
||||
break;
|
||||
case NPU_SET_IFM_STRIDE_C:
|
||||
st.ifm.stride_c = addr;
|
||||
break;
|
||||
|
||||
case NPU_SET_OFM_WIDTH_M1:
|
||||
st.ofm.width = param;
|
||||
break;
|
||||
case NPU_SET_OFM_HEIGHT_M1:
|
||||
st.ofm.height[2] = param;
|
||||
break;
|
||||
case NPU_SET_OFM_DEPTH_M1:
|
||||
st.ofm.depth = param;
|
||||
break;
|
||||
case NPU_SET_OFM_PRECISION:
|
||||
st.ofm.precision = param;
|
||||
break;
|
||||
case NPU_SET_OFM_REGION:
|
||||
st.ofm.region = param & 0x7;
|
||||
break;
|
||||
case NPU_SET_OFM_WIDTH0_M1:
|
||||
st.ofm.width0 = param;
|
||||
break;
|
||||
case NPU_SET_OFM_HEIGHT0_M1:
|
||||
st.ofm.height[0] = param;
|
||||
break;
|
||||
case NPU_SET_OFM_HEIGHT1_M1:
|
||||
st.ofm.height[1] = param;
|
||||
break;
|
||||
case NPU_SET_OFM_BASE0:
|
||||
case NPU_SET_OFM_BASE1:
|
||||
case NPU_SET_OFM_BASE2:
|
||||
case NPU_SET_OFM_BASE3:
|
||||
st.ofm.base[cmd & 0x3] = addr;
|
||||
break;
|
||||
case NPU_SET_OFM_STRIDE_X:
|
||||
st.ofm.stride_x = addr;
|
||||
break;
|
||||
case NPU_SET_OFM_STRIDE_Y:
|
||||
st.ofm.stride_y = addr;
|
||||
break;
|
||||
case NPU_SET_OFM_STRIDE_C:
|
||||
st.ofm.stride_c = addr;
|
||||
break;
|
||||
|
||||
case NPU_SET_IFM2_BROADCAST:
|
||||
st.ifm2.broadcast = param;
|
||||
break;
|
||||
case NPU_SET_IFM2_PRECISION:
|
||||
st.ifm2.precision = param;
|
||||
break;
|
||||
case NPU_SET_IFM2_REGION:
|
||||
st.ifm2.region = param & 0x7;
|
||||
break;
|
||||
case NPU_SET_IFM2_WIDTH0_M1:
|
||||
st.ifm2.width0 = param;
|
||||
break;
|
||||
case NPU_SET_IFM2_HEIGHT0_M1:
|
||||
st.ifm2.height[0] = param;
|
||||
break;
|
||||
case NPU_SET_IFM2_HEIGHT1_M1:
|
||||
st.ifm2.height[1] = param;
|
||||
break;
|
||||
case NPU_SET_IFM2_BASE0:
|
||||
case NPU_SET_IFM2_BASE1:
|
||||
case NPU_SET_IFM2_BASE2:
|
||||
case NPU_SET_IFM2_BASE3:
|
||||
st.ifm2.base[cmd & 0x3] = addr;
|
||||
break;
|
||||
case NPU_SET_IFM2_STRIDE_X:
|
||||
st.ifm2.stride_x = addr;
|
||||
break;
|
||||
case NPU_SET_IFM2_STRIDE_Y:
|
||||
st.ifm2.stride_y = addr;
|
||||
break;
|
||||
case NPU_SET_IFM2_STRIDE_C:
|
||||
st.ifm2.stride_c = addr;
|
||||
break;
|
||||
|
||||
case NPU_SET_WEIGHT_REGION:
|
||||
st.weight[0].region = param & 0x7;
|
||||
break;
|
||||
case NPU_SET_SCALE_REGION:
|
||||
st.scale[0].region = param & 0x7;
|
||||
break;
|
||||
case NPU_SET_WEIGHT_BASE:
|
||||
st.weight[0].base = addr;
|
||||
break;
|
||||
case NPU_SET_WEIGHT_LENGTH:
|
||||
st.weight[0].length = cmds[1];
|
||||
break;
|
||||
case NPU_SET_SCALE_BASE:
|
||||
st.scale[0].base = addr;
|
||||
break;
|
||||
case NPU_SET_SCALE_LENGTH:
|
||||
st.scale[0].length = cmds[1];
|
||||
break;
|
||||
case NPU_SET_WEIGHT1_BASE:
|
||||
st.weight[1].base = addr;
|
||||
break;
|
||||
case NPU_SET_WEIGHT1_LENGTH:
|
||||
st.weight[1].length = cmds[1];
|
||||
break;
|
||||
case NPU_SET_SCALE1_BASE: // NPU_SET_WEIGHT2_BASE (U85)
|
||||
if (ethosu_is_u65(edev))
|
||||
st.scale[1].base = addr;
|
||||
else
|
||||
st.weight[2].base = addr;
|
||||
break;
|
||||
case NPU_SET_SCALE1_LENGTH: // NPU_SET_WEIGHT2_LENGTH (U85)
|
||||
if (ethosu_is_u65(edev))
|
||||
st.scale[1].length = cmds[1];
|
||||
else
|
||||
st.weight[1].length = cmds[1];
|
||||
break;
|
||||
case NPU_SET_WEIGHT3_BASE:
|
||||
st.weight[3].base = addr;
|
||||
break;
|
||||
case NPU_SET_WEIGHT3_LENGTH:
|
||||
st.weight[3].length = cmds[1];
|
||||
break;
|
||||
|
||||
case NPU_SET_DMA0_SRC_REGION:
|
||||
if (param & 0x100)
|
||||
st.dma.src.region = -1;
|
||||
else
|
||||
st.dma.src.region = param & 0x7;
|
||||
st.dma.mode = (param >> 9) & 0x3;
|
||||
break;
|
||||
case NPU_SET_DMA0_DST_REGION:
|
||||
if (param & 0x100)
|
||||
st.dma.dst.region = -1;
|
||||
else
|
||||
st.dma.dst.region = param & 0x7;
|
||||
break;
|
||||
case NPU_SET_DMA0_SIZE0:
|
||||
st.dma.size0 = param;
|
||||
break;
|
||||
case NPU_SET_DMA0_SIZE1:
|
||||
st.dma.size1 = param;
|
||||
break;
|
||||
case NPU_SET_DMA0_SRC_STRIDE0:
|
||||
st.dma.src.stride[0] = ((s64)addr << 24) >> 24;
|
||||
break;
|
||||
case NPU_SET_DMA0_SRC_STRIDE1:
|
||||
st.dma.src.stride[1] = ((s64)addr << 24) >> 24;
|
||||
break;
|
||||
case NPU_SET_DMA0_DST_STRIDE0:
|
||||
st.dma.dst.stride[0] = ((s64)addr << 24) >> 24;
|
||||
break;
|
||||
case NPU_SET_DMA0_DST_STRIDE1:
|
||||
st.dma.dst.stride[1] = ((s64)addr << 24) >> 24;
|
||||
break;
|
||||
case NPU_SET_DMA0_SRC:
|
||||
st.dma.src.offset = addr;
|
||||
break;
|
||||
case NPU_SET_DMA0_DST:
|
||||
st.dma.dst.offset = addr;
|
||||
break;
|
||||
case NPU_SET_DMA0_LEN:
|
||||
st.dma.src.len = st.dma.dst.len = addr;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < NPU_BASEP_REGION_MAX; i++) {
|
||||
if (!info->region_size[i])
|
||||
continue;
|
||||
dev_dbg(ddev->dev, "region %d max size: 0x%llx\n",
|
||||
i, info->region_size[i]);
|
||||
}
|
||||
|
||||
bo->info = no_free_ptr(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ethosu_gem_cmdstream_create() - Create a GEM object and attach it to a handle.
|
||||
* @file: DRM file.
|
||||
* @ddev: DRM device.
|
||||
* @exclusive_vm: Exclusive VM. Not NULL if the GEM object can't be shared.
|
||||
* @size: Size of the GEM object to allocate.
|
||||
* @flags: Combination of drm_ethosu_bo_flags flags.
|
||||
* @handle: Pointer holding the handle pointing to the new GEM object.
|
||||
*
|
||||
* Return: Zero on success
|
||||
*/
|
||||
int ethosu_gem_cmdstream_create(struct drm_file *file,
|
||||
struct drm_device *ddev,
|
||||
u32 size, u64 data, u32 flags, u32 *handle)
|
||||
{
|
||||
int ret;
|
||||
struct drm_gem_dma_object *mem;
|
||||
struct ethosu_gem_object *bo;
|
||||
|
||||
mem = drm_gem_dma_create(ddev, size);
|
||||
if (IS_ERR(mem))
|
||||
return PTR_ERR(mem);
|
||||
|
||||
bo = to_ethosu_bo(&mem->base);
|
||||
bo->flags = flags;
|
||||
|
||||
ret = ethosu_gem_cmdstream_copy_and_validate(ddev,
|
||||
(void __user *)(uintptr_t)data,
|
||||
bo, size);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* Allocate an id of idr table where the obj is registered
|
||||
* and handle has the id what user can see.
|
||||
*/
|
||||
ret = drm_gem_handle_create(file, &mem->base, handle);
|
||||
|
||||
fail:
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_put(&mem->base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 or MIT */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
|
||||
#ifndef __ETHOSU_GEM_H__
|
||||
#define __ETHOSU_GEM_H__
|
||||
|
||||
#include "ethosu_device.h"
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
|
||||
struct ethosu_validated_cmdstream_info {
|
||||
u32 cmd_size;
|
||||
u64 region_size[NPU_BASEP_REGION_MAX];
|
||||
bool output_region[NPU_BASEP_REGION_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ethosu_gem_object - Driver specific GEM object.
|
||||
*/
|
||||
struct ethosu_gem_object {
|
||||
/** @base: Inherit from drm_gem_shmem_object. */
|
||||
struct drm_gem_dma_object base;
|
||||
|
||||
struct ethosu_validated_cmdstream_info *info;
|
||||
|
||||
/** @flags: Combination of drm_ethosu_bo_flags flags. */
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
static inline
|
||||
struct ethosu_gem_object *to_ethosu_bo(struct drm_gem_object *obj)
|
||||
{
|
||||
return container_of(to_drm_gem_dma_obj(obj), struct ethosu_gem_object, base);
|
||||
}
|
||||
|
||||
struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev,
|
||||
size_t size);
|
||||
|
||||
int ethosu_gem_create_with_handle(struct drm_file *file,
|
||||
struct drm_device *ddev,
|
||||
u64 *size, u32 flags, uint32_t *handle);
|
||||
|
||||
int ethosu_gem_cmdstream_create(struct drm_file *file,
|
||||
struct drm_device *ddev,
|
||||
u32 size, u64 data, u32 flags, u32 *handle);
|
||||
|
||||
#endif /* __ETHOSU_GEM_H__ */
|
||||
|
|
@ -0,0 +1,497 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only OR MIT
|
||||
/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_gem.h>
|
||||
#include <drm/drm_gem_dma_helper.h>
|
||||
#include <drm/drm_print.h>
|
||||
#include <drm/ethosu_accel.h>
|
||||
|
||||
#include "ethosu_device.h"
|
||||
#include "ethosu_drv.h"
|
||||
#include "ethosu_gem.h"
|
||||
#include "ethosu_job.h"
|
||||
|
||||
#define JOB_TIMEOUT_MS 500
|
||||
|
||||
static struct ethosu_job *to_ethosu_job(struct drm_sched_job *sched_job)
|
||||
{
|
||||
return container_of(sched_job, struct ethosu_job, base);
|
||||
}
|
||||
|
||||
static const char *ethosu_fence_get_driver_name(struct dma_fence *fence)
|
||||
{
|
||||
return "ethosu";
|
||||
}
|
||||
|
||||
static const char *ethosu_fence_get_timeline_name(struct dma_fence *fence)
|
||||
{
|
||||
return "ethosu-npu";
|
||||
}
|
||||
|
||||
static const struct dma_fence_ops ethosu_fence_ops = {
|
||||
.get_driver_name = ethosu_fence_get_driver_name,
|
||||
.get_timeline_name = ethosu_fence_get_timeline_name,
|
||||
};
|
||||
|
||||
static void ethosu_job_hw_submit(struct ethosu_device *dev, struct ethosu_job *job)
|
||||
{
|
||||
struct drm_gem_dma_object *cmd_bo = to_drm_gem_dma_obj(job->cmd_bo);
|
||||
struct ethosu_validated_cmdstream_info *cmd_info = to_ethosu_bo(job->cmd_bo)->info;
|
||||
|
||||
for (int i = 0; i < job->region_cnt; i++) {
|
||||
struct drm_gem_dma_object *bo;
|
||||
int region = job->region_bo_num[i];
|
||||
|
||||
bo = to_drm_gem_dma_obj(job->region_bo[i]);
|
||||
writel_relaxed(lower_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP(region));
|
||||
writel_relaxed(upper_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP_HI(region));
|
||||
dev_dbg(dev->base.dev, "Region %d base addr = %pad\n", region, &bo->dma_addr);
|
||||
}
|
||||
|
||||
if (job->sram_size) {
|
||||
writel_relaxed(lower_32_bits(dev->sramphys),
|
||||
dev->regs + NPU_REG_BASEP(ETHOSU_SRAM_REGION));
|
||||
writel_relaxed(upper_32_bits(dev->sramphys),
|
||||
dev->regs + NPU_REG_BASEP_HI(ETHOSU_SRAM_REGION));
|
||||
dev_dbg(dev->base.dev, "Region %d base addr = %pad (SRAM)\n",
|
||||
ETHOSU_SRAM_REGION, &dev->sramphys);
|
||||
}
|
||||
|
||||
writel_relaxed(lower_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE);
|
||||
writel_relaxed(upper_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE_HI);
|
||||
writel_relaxed(cmd_info->cmd_size, dev->regs + NPU_REG_QSIZE);
|
||||
|
||||
writel(CMD_TRANSITION_TO_RUN, dev->regs + NPU_REG_CMD);
|
||||
|
||||
dev_dbg(dev->base.dev,
|
||||
"Submitted cmd at %pad to core\n", &cmd_bo->dma_addr);
|
||||
}
|
||||
|
||||
static int ethosu_acquire_object_fences(struct ethosu_job *job)
|
||||
{
|
||||
int i, ret;
|
||||
struct drm_gem_object **bos = job->region_bo;
|
||||
struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
|
||||
|
||||
for (i = 0; i < job->region_cnt; i++) {
|
||||
bool is_write;
|
||||
|
||||
if (!bos[i])
|
||||
break;
|
||||
|
||||
ret = dma_resv_reserve_fences(bos[i]->resv, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
is_write = info->output_region[job->region_bo_num[i]];
|
||||
ret = drm_sched_job_add_implicit_dependencies(&job->base, bos[i],
|
||||
is_write);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ethosu_attach_object_fences(struct ethosu_job *job)
|
||||
{
|
||||
int i;
|
||||
struct dma_fence *fence = job->inference_done_fence;
|
||||
struct drm_gem_object **bos = job->region_bo;
|
||||
struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info;
|
||||
|
||||
for (i = 0; i < job->region_cnt; i++)
|
||||
if (info->output_region[job->region_bo_num[i]])
|
||||
dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE);
|
||||
}
|
||||
|
||||
static int ethosu_job_push(struct ethosu_job *job)
|
||||
{
|
||||
struct ww_acquire_ctx acquire_ctx;
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_lock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ethosu_acquire_object_fences(job);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = pm_runtime_resume_and_get(job->dev->base.dev);
|
||||
if (!ret) {
|
||||
guard(mutex)(&job->dev->sched_lock);
|
||||
|
||||
drm_sched_job_arm(&job->base);
|
||||
job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished);
|
||||
kref_get(&job->refcount); /* put by scheduler job completion */
|
||||
drm_sched_entity_push_job(&job->base);
|
||||
ethosu_attach_object_fences(job);
|
||||
}
|
||||
|
||||
out:
|
||||
drm_gem_unlock_reservations(job->region_bo, job->region_cnt, &acquire_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ethosu_job_cleanup(struct kref *ref)
|
||||
{
|
||||
struct ethosu_job *job = container_of(ref, struct ethosu_job,
|
||||
refcount);
|
||||
unsigned int i;
|
||||
|
||||
pm_runtime_put_autosuspend(job->dev->base.dev);
|
||||
|
||||
dma_fence_put(job->done_fence);
|
||||
dma_fence_put(job->inference_done_fence);
|
||||
|
||||
for (i = 0; i < job->region_cnt; i++)
|
||||
drm_gem_object_put(job->region_bo[i]);
|
||||
|
||||
drm_gem_object_put(job->cmd_bo);
|
||||
|
||||
kfree(job);
|
||||
}
|
||||
|
||||
static void ethosu_job_put(struct ethosu_job *job)
|
||||
{
|
||||
kref_put(&job->refcount, ethosu_job_cleanup);
|
||||
}
|
||||
|
||||
static void ethosu_job_free(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct ethosu_job *job = to_ethosu_job(sched_job);
|
||||
|
||||
drm_sched_job_cleanup(sched_job);
|
||||
ethosu_job_put(job);
|
||||
}
|
||||
|
||||
static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job)
|
||||
{
|
||||
struct ethosu_job *job = to_ethosu_job(sched_job);
|
||||
struct ethosu_device *dev = job->dev;
|
||||
struct dma_fence *fence = job->done_fence;
|
||||
|
||||
if (unlikely(job->base.s_fence->finished.error))
|
||||
return NULL;
|
||||
|
||||
dma_fence_init(fence, ðosu_fence_ops, &dev->fence_lock,
|
||||
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);
|
||||
}
|
||||
|
||||
return fence;
|
||||
}
|
||||
|
||||
static void ethosu_job_handle_irq(struct ethosu_device *dev)
|
||||
{
|
||||
u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
|
||||
|
||||
if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) {
|
||||
dev_err(dev->base.dev, "Error IRQ - %x\n", status);
|
||||
drm_sched_fault(&dev->sched);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ethosu_job_irq_handler_thread(int irq, void *data)
|
||||
{
|
||||
struct ethosu_device *dev = data;
|
||||
|
||||
ethosu_job_handle_irq(dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ethosu_job_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct ethosu_device *dev = data;
|
||||
u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS);
|
||||
|
||||
if (!(status & STATUS_IRQ_RAISED))
|
||||
return IRQ_NONE;
|
||||
|
||||
writel_relaxed(CMD_CLEAR_IRQ, dev->regs + NPU_REG_CMD);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad)
|
||||
{
|
||||
struct ethosu_job *job = to_ethosu_job(bad);
|
||||
struct ethosu_device *dev = job->dev;
|
||||
bool running;
|
||||
u32 *bocmds = to_drm_gem_dma_obj(job->cmd_bo)->vaddr;
|
||||
u32 cmdaddr;
|
||||
|
||||
cmdaddr = readl_relaxed(dev->regs + NPU_REG_QREAD);
|
||||
running = FIELD_GET(STATUS_STATE_RUNNING, readl_relaxed(dev->regs + NPU_REG_STATUS));
|
||||
|
||||
if (running) {
|
||||
int ret;
|
||||
u32 reg;
|
||||
|
||||
ret = readl_relaxed_poll_timeout(dev->regs + NPU_REG_QREAD,
|
||||
reg,
|
||||
reg != cmdaddr,
|
||||
USEC_PER_MSEC, 100 * USEC_PER_MSEC);
|
||||
|
||||
/* If still running and progress is being made, just return */
|
||||
if (!ret)
|
||||
return DRM_GPU_SCHED_STAT_NO_HANG;
|
||||
}
|
||||
|
||||
dev_err(dev->base.dev, "NPU sched timed out: NPU %s, cmdstream offset 0x%x: 0x%x\n",
|
||||
running ? "running" : "stopped",
|
||||
cmdaddr, bocmds[cmdaddr / 4]);
|
||||
|
||||
drm_sched_stop(&dev->sched, bad);
|
||||
|
||||
scoped_guard(mutex, &dev->job_lock)
|
||||
dev->in_flight_job = NULL;
|
||||
|
||||
/* Proceed with reset now. */
|
||||
pm_runtime_force_suspend(dev->base.dev);
|
||||
pm_runtime_force_resume(dev->base.dev);
|
||||
|
||||
/* Restart the scheduler */
|
||||
drm_sched_start(&dev->sched, 0);
|
||||
|
||||
return DRM_GPU_SCHED_STAT_RESET;
|
||||
}
|
||||
|
||||
static const struct drm_sched_backend_ops ethosu_sched_ops = {
|
||||
.run_job = ethosu_job_run,
|
||||
.timedout_job = ethosu_job_timedout,
|
||||
.free_job = ethosu_job_free
|
||||
};
|
||||
|
||||
int ethosu_job_init(struct ethosu_device *edev)
|
||||
{
|
||||
struct device *dev = edev->base.dev;
|
||||
struct drm_sched_init_args args = {
|
||||
.ops = ðosu_sched_ops,
|
||||
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
|
||||
.credit_limit = 1,
|
||||
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
|
||||
.name = dev_name(dev),
|
||||
.dev = dev,
|
||||
};
|
||||
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;
|
||||
|
||||
edev->irq = platform_get_irq(to_platform_device(dev), 0);
|
||||
if (edev->irq < 0)
|
||||
return edev->irq;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, edev->irq,
|
||||
ethosu_job_irq_handler,
|
||||
ethosu_job_irq_handler_thread,
|
||||
IRQF_SHARED, KBUILD_MODNAME,
|
||||
edev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
edev->fence_context = dma_fence_context_alloc(1);
|
||||
|
||||
ret = drm_sched_init(&edev->sched, &args);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to create scheduler: %d\n", ret);
|
||||
goto err_sched;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_sched:
|
||||
drm_sched_fini(&edev->sched);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ethosu_job_fini(struct ethosu_device *dev)
|
||||
{
|
||||
drm_sched_fini(&dev->sched);
|
||||
}
|
||||
|
||||
int ethosu_job_open(struct ethosu_file_priv *ethosu_priv)
|
||||
{
|
||||
struct ethosu_device *dev = ethosu_priv->edev;
|
||||
struct drm_gpu_scheduler *sched = &dev->sched;
|
||||
int ret;
|
||||
|
||||
ret = drm_sched_entity_init(ðosu_priv->sched_entity,
|
||||
DRM_SCHED_PRIORITY_NORMAL,
|
||||
&sched, 1, NULL);
|
||||
return WARN_ON(ret);
|
||||
}
|
||||
|
||||
void ethosu_job_close(struct ethosu_file_priv *ethosu_priv)
|
||||
{
|
||||
struct drm_sched_entity *entity = ðosu_priv->sched_entity;
|
||||
|
||||
drm_sched_entity_destroy(entity);
|
||||
}
|
||||
|
||||
static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file,
|
||||
struct drm_ethosu_job *job)
|
||||
{
|
||||
struct ethosu_device *edev = to_ethosu_device(dev);
|
||||
struct ethosu_file_priv *file_priv = file->driver_priv;
|
||||
struct ethosu_job *ejob = NULL;
|
||||
struct ethosu_validated_cmdstream_info *cmd_info;
|
||||
int ret = 0;
|
||||
|
||||
/* BO region 2 is reserved if SRAM is used */
|
||||
if (job->region_bo_handles[ETHOSU_SRAM_REGION] && job->sram_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (edev->npu_info.sram_size < job->sram_size)
|
||||
return -EINVAL;
|
||||
|
||||
ejob = kzalloc(sizeof(*ejob), GFP_KERNEL);
|
||||
if (!ejob)
|
||||
return -ENOMEM;
|
||||
|
||||
kref_init(&ejob->refcount);
|
||||
|
||||
ejob->dev = edev;
|
||||
ejob->sram_size = job->sram_size;
|
||||
|
||||
ejob->done_fence = kzalloc(sizeof(*ejob->done_fence), GFP_KERNEL);
|
||||
if (!ejob->done_fence) {
|
||||
ret = -ENOMEM;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
|
||||
ret = drm_sched_job_init(&ejob->base,
|
||||
&file_priv->sched_entity,
|
||||
1, NULL, file->client_id);
|
||||
if (ret)
|
||||
goto out_put_job;
|
||||
|
||||
ejob->cmd_bo = drm_gem_object_lookup(file, job->cmd_bo);
|
||||
if (!ejob->cmd_bo) {
|
||||
ret = -ENOENT;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
cmd_info = to_ethosu_bo(ejob->cmd_bo)->info;
|
||||
if (!cmd_info) {
|
||||
ret = -EINVAL;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NPU_BASEP_REGION_MAX; i++) {
|
||||
struct drm_gem_object *gem;
|
||||
|
||||
/* Can only omit a BO handle if the region is not used or used for SRAM */
|
||||
if (!job->region_bo_handles[i] &&
|
||||
(!cmd_info->region_size[i] || (i == ETHOSU_SRAM_REGION && job->sram_size)))
|
||||
continue;
|
||||
|
||||
if (job->region_bo_handles[i] && !cmd_info->region_size[i]) {
|
||||
dev_err(dev->dev,
|
||||
"Cmdstream BO handle %d set for unused region %d\n",
|
||||
job->region_bo_handles[i], i);
|
||||
ret = -EINVAL;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
|
||||
gem = drm_gem_object_lookup(file, job->region_bo_handles[i]);
|
||||
if (!gem) {
|
||||
dev_err(dev->dev,
|
||||
"Invalid BO handle %d for region %d\n",
|
||||
job->region_bo_handles[i], i);
|
||||
ret = -ENOENT;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
|
||||
ejob->region_bo[ejob->region_cnt] = gem;
|
||||
ejob->region_bo_num[ejob->region_cnt] = i;
|
||||
ejob->region_cnt++;
|
||||
|
||||
if (to_ethosu_bo(gem)->info) {
|
||||
dev_err(dev->dev,
|
||||
"Cmdstream BO handle %d used for region %d\n",
|
||||
job->region_bo_handles[i], i);
|
||||
ret = -EINVAL;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
|
||||
/* Verify the command stream doesn't have accesses outside the BO */
|
||||
if (cmd_info->region_size[i] > gem->size) {
|
||||
dev_err(dev->dev,
|
||||
"cmd stream region %d size greater than BO size (%llu > %zu)\n",
|
||||
i, cmd_info->region_size[i], gem->size);
|
||||
ret = -EOVERFLOW;
|
||||
goto out_cleanup_job;
|
||||
}
|
||||
}
|
||||
ret = ethosu_job_push(ejob);
|
||||
|
||||
out_cleanup_job:
|
||||
if (ret)
|
||||
drm_sched_job_cleanup(&ejob->base);
|
||||
out_put_job:
|
||||
ethosu_job_put(ejob);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file)
|
||||
{
|
||||
struct drm_ethosu_submit *args = data;
|
||||
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_array(args->job_count, sizeof(*jobs), GFP_KERNEL);
|
||||
if (!jobs)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(jobs,
|
||||
(void __user *)(uintptr_t)args->jobs,
|
||||
args->job_count * sizeof(*jobs))) {
|
||||
drm_dbg(dev, "Failed to copy incoming job array\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
for (i = 0; i < args->job_count; i++) {
|
||||
ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only OR MIT */
|
||||
/* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */
|
||||
/* Copyright 2025 Arm, Ltd. */
|
||||
|
||||
#ifndef __ETHOSU_JOB_H__
|
||||
#define __ETHOSU_JOB_H__
|
||||
|
||||
#include <linux/kref.h>
|
||||
#include <drm/gpu_scheduler.h>
|
||||
|
||||
struct ethosu_device;
|
||||
struct ethosu_file_priv;
|
||||
|
||||
struct ethosu_job {
|
||||
struct drm_sched_job base;
|
||||
struct ethosu_device *dev;
|
||||
|
||||
struct drm_gem_object *cmd_bo;
|
||||
struct drm_gem_object *region_bo[NPU_BASEP_REGION_MAX];
|
||||
u8 region_bo_num[NPU_BASEP_REGION_MAX];
|
||||
u8 region_cnt;
|
||||
u32 sram_size;
|
||||
|
||||
/* Fence to be signaled by drm-sched once its done with the job */
|
||||
struct dma_fence *inference_done_fence;
|
||||
|
||||
/* Fence to be signaled by IRQ handler when the job is complete. */
|
||||
struct dma_fence *done_fence;
|
||||
|
||||
struct kref refcount;
|
||||
};
|
||||
|
||||
int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file);
|
||||
|
||||
int ethosu_job_init(struct ethosu_device *dev);
|
||||
void ethosu_job_fini(struct ethosu_device *dev);
|
||||
int ethosu_job_open(struct ethosu_file_priv *ethosu_priv);
|
||||
void ethosu_job_close(struct ethosu_file_priv *ethosu_priv);
|
||||
|
||||
#endif
|
||||
|
|
@ -6,6 +6,7 @@ intel_vpu-y := \
|
|||
ivpu_fw.o \
|
||||
ivpu_fw_log.o \
|
||||
ivpu_gem.o \
|
||||
ivpu_gem_userptr.o \
|
||||
ivpu_hw.o \
|
||||
ivpu_hw_btrs.o \
|
||||
ivpu_hw_ip.o \
|
||||
|
|
|
|||
|
|
@ -398,35 +398,25 @@ static int dct_active_set(void *data, u64 active_percent)
|
|||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(ivpu_dct_fops, dct_active_get, dct_active_set, "%llu\n");
|
||||
|
||||
static void print_priority_band(struct seq_file *s, struct ivpu_hw_info *hw,
|
||||
int band, const char *name)
|
||||
{
|
||||
seq_printf(s, "%-9s: grace_period %9u process_grace_period %9u process_quantum %9u\n",
|
||||
name,
|
||||
hw->hws.grace_period[band],
|
||||
hw->hws.process_grace_period[band],
|
||||
hw->hws.process_quantum[band]);
|
||||
}
|
||||
|
||||
static int priority_bands_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct ivpu_device *vdev = s->private;
|
||||
struct ivpu_hw_info *hw = vdev->hw;
|
||||
|
||||
for (int band = VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE;
|
||||
band < VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT; band++) {
|
||||
switch (band) {
|
||||
case VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE:
|
||||
seq_puts(s, "Idle: ");
|
||||
break;
|
||||
|
||||
case VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL:
|
||||
seq_puts(s, "Normal: ");
|
||||
break;
|
||||
|
||||
case VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS:
|
||||
seq_puts(s, "Focus: ");
|
||||
break;
|
||||
|
||||
case VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME:
|
||||
seq_puts(s, "Realtime: ");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(s, "grace_period %9u process_grace_period %9u process_quantum %9u\n",
|
||||
hw->hws.grace_period[band], hw->hws.process_grace_period[band],
|
||||
hw->hws.process_quantum[band]);
|
||||
}
|
||||
print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE, "Idle");
|
||||
print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL, "Normal");
|
||||
print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS, "Focus");
|
||||
print_priority_band(s, hw, VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME, "Realtime");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ MODULE_PARM_DESC(pll_max_ratio, "Maximum PLL ratio used to set NPU frequency");
|
|||
|
||||
int ivpu_sched_mode = IVPU_SCHED_MODE_AUTO;
|
||||
module_param_named(sched_mode, ivpu_sched_mode, int, 0444);
|
||||
MODULE_PARM_DESC(sched_mode, "Scheduler mode: -1 - Use default scheduler, 0 - Use OS scheduler, 1 - Use HW scheduler");
|
||||
MODULE_PARM_DESC(sched_mode, "Scheduler mode: -1 - Use default scheduler, 0 - Use OS scheduler (supported on 27XX - 50XX), 1 - Use HW scheduler");
|
||||
|
||||
bool ivpu_disable_mmu_cont_pages;
|
||||
module_param_named(disable_mmu_cont_pages, ivpu_disable_mmu_cont_pages, bool, 0444);
|
||||
|
|
@ -134,6 +134,8 @@ bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability)
|
|||
return true;
|
||||
case DRM_IVPU_CAP_DMA_MEMORY_RANGE:
|
||||
return true;
|
||||
case DRM_IVPU_CAP_BO_CREATE_FROM_USERPTR:
|
||||
return true;
|
||||
case DRM_IVPU_CAP_MANAGE_CMDQ:
|
||||
return vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW;
|
||||
default:
|
||||
|
|
@ -200,6 +202,9 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f
|
|||
case DRM_IVPU_PARAM_CAPABILITIES:
|
||||
args->value = ivpu_is_capable(vdev, args->index);
|
||||
break;
|
||||
case DRM_IVPU_PARAM_PREEMPT_BUFFER_SIZE:
|
||||
args->value = ivpu_fw_preempt_buf_size(vdev);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
|
@ -310,6 +315,7 @@ static const struct drm_ioctl_desc ivpu_drm_ioctls[] = {
|
|||
DRM_IOCTL_DEF_DRV(IVPU_CMDQ_CREATE, ivpu_cmdq_create_ioctl, 0),
|
||||
DRM_IOCTL_DEF_DRV(IVPU_CMDQ_DESTROY, ivpu_cmdq_destroy_ioctl, 0),
|
||||
DRM_IOCTL_DEF_DRV(IVPU_CMDQ_SUBMIT, ivpu_cmdq_submit_ioctl, 0),
|
||||
DRM_IOCTL_DEF_DRV(IVPU_BO_CREATE_FROM_USERPTR, ivpu_bo_create_from_userptr_ioctl, 0),
|
||||
};
|
||||
|
||||
static int ivpu_wait_for_ready(struct ivpu_device *vdev)
|
||||
|
|
@ -377,8 +383,7 @@ int ivpu_boot(struct ivpu_device *vdev)
|
|||
drm_WARN_ON(&vdev->drm, atomic_read(&vdev->job_timeout_counter));
|
||||
drm_WARN_ON(&vdev->drm, !xa_empty(&vdev->submitted_jobs_xa));
|
||||
|
||||
/* Update boot params located at first 4KB of FW memory */
|
||||
ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem));
|
||||
ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem_bp));
|
||||
|
||||
ret = ivpu_hw_boot_fw(vdev);
|
||||
if (ret) {
|
||||
|
|
@ -450,6 +455,9 @@ int ivpu_shutdown(struct ivpu_device *vdev)
|
|||
static const struct file_operations ivpu_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
DRM_ACCEL_FOPS,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_fdinfo,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct drm_driver driver = {
|
||||
|
|
@ -464,6 +472,9 @@ static const struct drm_driver driver = {
|
|||
.ioctls = ivpu_drm_ioctls,
|
||||
.num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls),
|
||||
.fops = &ivpu_fops,
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = drm_show_memory_stats,
|
||||
#endif
|
||||
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
|
|
@ -705,6 +716,7 @@ static struct pci_device_id ivpu_pci_ids[] = {
|
|||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PTL_P) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_WCL) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_NVL) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ivpu_pci_ids);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#define PCI_DEVICE_ID_LNL 0x643e
|
||||
#define PCI_DEVICE_ID_PTL_P 0xb03e
|
||||
#define PCI_DEVICE_ID_WCL 0xfd3e
|
||||
#define PCI_DEVICE_ID_NVL 0xd71d
|
||||
|
||||
#define IVPU_HW_IP_37XX 37
|
||||
#define IVPU_HW_IP_40XX 40
|
||||
|
|
@ -78,6 +79,7 @@
|
|||
#define IVPU_DBG_KREF BIT(11)
|
||||
#define IVPU_DBG_RPM BIT(12)
|
||||
#define IVPU_DBG_MMU_MAP BIT(13)
|
||||
#define IVPU_DBG_IOCTL BIT(14)
|
||||
|
||||
#define ivpu_err(vdev, fmt, ...) \
|
||||
drm_err(&(vdev)->drm, "%s(): " fmt, __func__, ##__VA_ARGS__)
|
||||
|
|
@ -245,6 +247,8 @@ static inline int ivpu_hw_ip_gen(struct ivpu_device *vdev)
|
|||
case PCI_DEVICE_ID_PTL_P:
|
||||
case PCI_DEVICE_ID_WCL:
|
||||
return IVPU_HW_IP_50XX;
|
||||
case PCI_DEVICE_ID_NVL:
|
||||
return IVPU_HW_IP_60XX;
|
||||
default:
|
||||
dump_stack();
|
||||
ivpu_err(vdev, "Unknown NPU IP generation\n");
|
||||
|
|
@ -261,6 +265,7 @@ static inline int ivpu_hw_btrs_gen(struct ivpu_device *vdev)
|
|||
case PCI_DEVICE_ID_LNL:
|
||||
case PCI_DEVICE_ID_PTL_P:
|
||||
case PCI_DEVICE_ID_WCL:
|
||||
case PCI_DEVICE_ID_NVL:
|
||||
return IVPU_HW_BTRS_LNL;
|
||||
default:
|
||||
dump_stack();
|
||||
|
|
|
|||
|
|
@ -17,15 +17,10 @@
|
|||
#include "ivpu_ipc.h"
|
||||
#include "ivpu_pm.h"
|
||||
|
||||
#define FW_GLOBAL_MEM_START (2ull * SZ_1G)
|
||||
#define FW_GLOBAL_MEM_END (3ull * SZ_1G)
|
||||
#define FW_SHARED_MEM_SIZE SZ_256M /* Must be aligned to FW_SHARED_MEM_ALIGNMENT */
|
||||
#define FW_SHARED_MEM_ALIGNMENT SZ_128K /* VPU MTRR limitation */
|
||||
#define FW_RUNTIME_MAX_SIZE SZ_512M
|
||||
#define FW_SHAVE_NN_MAX_SIZE SZ_2M
|
||||
#define FW_RUNTIME_MIN_ADDR (FW_GLOBAL_MEM_START)
|
||||
#define FW_RUNTIME_MAX_ADDR (FW_GLOBAL_MEM_END - FW_SHARED_MEM_SIZE)
|
||||
#define FW_FILE_IMAGE_OFFSET (VPU_FW_HEADER_SIZE + FW_VERSION_HEADER_SIZE)
|
||||
#define FW_PREEMPT_BUF_MIN_SIZE SZ_4K
|
||||
#define FW_PREEMPT_BUF_MAX_SIZE SZ_32M
|
||||
|
||||
#define WATCHDOG_MSS_REDIRECT 32
|
||||
#define WATCHDOG_NCE_REDIRECT 33
|
||||
|
|
@ -61,12 +56,14 @@ static struct {
|
|||
{ IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v0.0.bin" },
|
||||
{ IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v1.bin" },
|
||||
{ IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v0.0.bin" },
|
||||
{ IVPU_HW_IP_60XX, "intel/vpu/vpu_60xx_v1.bin" },
|
||||
};
|
||||
|
||||
/* Production fw_names from the table above */
|
||||
MODULE_FIRMWARE("intel/vpu/vpu_37xx_v1.bin");
|
||||
MODULE_FIRMWARE("intel/vpu/vpu_40xx_v1.bin");
|
||||
MODULE_FIRMWARE("intel/vpu/vpu_50xx_v1.bin");
|
||||
MODULE_FIRMWARE("intel/vpu/vpu_60xx_v1.bin");
|
||||
|
||||
static int ivpu_fw_request(struct ivpu_device *vdev)
|
||||
{
|
||||
|
|
@ -131,9 +128,14 @@ ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_hea
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool is_within_range(u64 addr, size_t size, u64 range_start, size_t range_size)
|
||||
bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range)
|
||||
{
|
||||
if (addr < range_start || addr + size > range_start + range_size)
|
||||
u64 addr_end;
|
||||
|
||||
if (!range || check_add_overflow(addr, size, &addr_end))
|
||||
return false;
|
||||
|
||||
if (addr < range->start || addr_end > range->end)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -142,6 +144,12 @@ static bool is_within_range(u64 addr, size_t size, u64 range_start, size_t range
|
|||
static u32
|
||||
ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr)
|
||||
{
|
||||
if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX &&
|
||||
ivpu_sched_mode == VPU_SCHEDULING_MODE_OS) {
|
||||
ivpu_warn(vdev, "OS sched mode is not supported, using HW mode\n");
|
||||
return VPU_SCHEDULING_MODE_HW;
|
||||
}
|
||||
|
||||
if (ivpu_sched_mode != IVPU_SCHED_MODE_AUTO)
|
||||
return ivpu_sched_mode;
|
||||
|
||||
|
|
@ -151,11 +159,56 @@ ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_he
|
|||
return VPU_SCHEDULING_MODE_HW;
|
||||
}
|
||||
|
||||
static void
|
||||
ivpu_preemption_config_parse(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr)
|
||||
{
|
||||
struct ivpu_fw_info *fw = vdev->fw;
|
||||
u32 primary_preempt_buf_size, secondary_preempt_buf_size;
|
||||
|
||||
if (fw_hdr->preemption_buffer_1_max_size)
|
||||
primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size;
|
||||
else
|
||||
primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
|
||||
|
||||
if (fw_hdr->preemption_buffer_2_max_size)
|
||||
secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_max_size;
|
||||
else
|
||||
secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size;
|
||||
|
||||
ivpu_dbg(vdev, FW_BOOT, "Preemption buffer size, primary: %u, secondary: %u\n",
|
||||
primary_preempt_buf_size, secondary_preempt_buf_size);
|
||||
|
||||
if (primary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE ||
|
||||
secondary_preempt_buf_size < FW_PREEMPT_BUF_MIN_SIZE) {
|
||||
ivpu_warn(vdev, "Preemption buffers size too small\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (primary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE ||
|
||||
secondary_preempt_buf_size > FW_PREEMPT_BUF_MAX_SIZE) {
|
||||
ivpu_warn(vdev, "Preemption buffers size too big\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fw->sched_mode != VPU_SCHEDULING_MODE_HW)
|
||||
return;
|
||||
|
||||
if (ivpu_test_mode & IVPU_TEST_MODE_MIP_DISABLE)
|
||||
return;
|
||||
|
||||
vdev->fw->primary_preempt_buf_size = ALIGN(primary_preempt_buf_size, PAGE_SIZE);
|
||||
vdev->fw->secondary_preempt_buf_size = ALIGN(secondary_preempt_buf_size, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static int ivpu_fw_parse(struct ivpu_device *vdev)
|
||||
{
|
||||
struct ivpu_fw_info *fw = vdev->fw;
|
||||
const struct vpu_firmware_header *fw_hdr = (const void *)fw->file->data;
|
||||
u64 runtime_addr, image_load_addr, runtime_size, image_size;
|
||||
struct ivpu_addr_range fw_image_range;
|
||||
u64 boot_params_addr, boot_params_size;
|
||||
u64 fw_version_addr, fw_version_size;
|
||||
u64 runtime_addr, runtime_size;
|
||||
u64 image_load_addr, image_size;
|
||||
|
||||
if (fw->file->size <= FW_FILE_IMAGE_OFFSET) {
|
||||
ivpu_err(vdev, "Firmware file is too small: %zu\n", fw->file->size);
|
||||
|
|
@ -167,18 +220,37 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
runtime_addr = fw_hdr->boot_params_load_address;
|
||||
runtime_size = fw_hdr->runtime_size;
|
||||
image_load_addr = fw_hdr->image_load_address;
|
||||
image_size = fw_hdr->image_size;
|
||||
boot_params_addr = fw_hdr->boot_params_load_address;
|
||||
boot_params_size = SZ_4K;
|
||||
|
||||
if (runtime_addr < FW_RUNTIME_MIN_ADDR || runtime_addr > FW_RUNTIME_MAX_ADDR) {
|
||||
ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx\n", runtime_addr);
|
||||
if (!ivpu_is_within_range(boot_params_addr, boot_params_size, &vdev->hw->ranges.runtime)) {
|
||||
ivpu_err(vdev, "Invalid boot params address: 0x%llx\n", boot_params_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (runtime_size < fw->file->size || runtime_size > FW_RUNTIME_MAX_SIZE) {
|
||||
ivpu_err(vdev, "Invalid firmware runtime size: %llu\n", runtime_size);
|
||||
fw_version_addr = fw_hdr->firmware_version_load_address;
|
||||
fw_version_size = ALIGN(fw_hdr->firmware_version_size, SZ_4K);
|
||||
|
||||
if (fw_version_size != SZ_4K) {
|
||||
ivpu_err(vdev, "Invalid firmware version size: %u\n",
|
||||
fw_hdr->firmware_version_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ivpu_is_within_range(fw_version_addr, fw_version_size, &vdev->hw->ranges.runtime)) {
|
||||
ivpu_err(vdev, "Invalid firmware version address: 0x%llx\n", fw_version_addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
runtime_addr = fw_hdr->image_load_address;
|
||||
runtime_size = fw_hdr->runtime_size - boot_params_size - fw_version_size;
|
||||
|
||||
image_load_addr = fw_hdr->image_load_address;
|
||||
image_size = fw_hdr->image_size;
|
||||
|
||||
if (!ivpu_is_within_range(runtime_addr, runtime_size, &vdev->hw->ranges.runtime)) {
|
||||
ivpu_err(vdev, "Invalid firmware runtime address: 0x%llx and size %llu\n",
|
||||
runtime_addr, runtime_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -187,23 +259,25 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (image_load_addr < runtime_addr ||
|
||||
image_load_addr + image_size > runtime_addr + runtime_size) {
|
||||
ivpu_err(vdev, "Invalid firmware load address size: 0x%llx and size %llu\n",
|
||||
if (!ivpu_is_within_range(image_load_addr, image_size, &vdev->hw->ranges.runtime)) {
|
||||
ivpu_err(vdev, "Invalid firmware load address: 0x%llx and size %llu\n",
|
||||
image_load_addr, image_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ivpu_hw_range_init(vdev, &fw_image_range, image_load_addr, image_size))
|
||||
return -EINVAL;
|
||||
|
||||
if (!ivpu_is_within_range(fw_hdr->entry_point, SZ_4K, &fw_image_range)) {
|
||||
ivpu_err(vdev, "Invalid entry point: 0x%llx\n", fw_hdr->entry_point);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw_hdr->shave_nn_fw_size > FW_SHAVE_NN_MAX_SIZE) {
|
||||
ivpu_err(vdev, "SHAVE NN firmware is too big: %u\n", fw_hdr->shave_nn_fw_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (fw_hdr->entry_point < image_load_addr ||
|
||||
fw_hdr->entry_point >= image_load_addr + image_size) {
|
||||
ivpu_err(vdev, "Invalid entry point: 0x%llx\n", fw_hdr->entry_point);
|
||||
return -EINVAL;
|
||||
}
|
||||
ivpu_dbg(vdev, FW_BOOT, "Header version: 0x%x, format 0x%x\n",
|
||||
fw_hdr->header_version, fw_hdr->image_format);
|
||||
|
||||
|
|
@ -217,6 +291,10 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
|
|||
if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3))
|
||||
return -EINVAL;
|
||||
|
||||
fw->boot_params_addr = boot_params_addr;
|
||||
fw->boot_params_size = boot_params_size;
|
||||
fw->fw_version_addr = fw_version_addr;
|
||||
fw->fw_version_size = fw_version_size;
|
||||
fw->runtime_addr = runtime_addr;
|
||||
fw->runtime_size = runtime_size;
|
||||
fw->image_load_offset = image_load_addr - runtime_addr;
|
||||
|
|
@ -235,22 +313,13 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
|
|||
fw->sched_mode = ivpu_fw_sched_mode_select(vdev, fw_hdr);
|
||||
ivpu_info(vdev, "Scheduler mode: %s\n", fw->sched_mode ? "HW" : "OS");
|
||||
|
||||
if (fw_hdr->preemption_buffer_1_max_size)
|
||||
fw->primary_preempt_buf_size = fw_hdr->preemption_buffer_1_max_size;
|
||||
else
|
||||
fw->primary_preempt_buf_size = fw_hdr->preemption_buffer_1_size;
|
||||
ivpu_preemption_config_parse(vdev, fw_hdr);
|
||||
ivpu_dbg(vdev, FW_BOOT, "Mid-inference preemption %s supported\n",
|
||||
ivpu_fw_preempt_buf_size(vdev) ? "is" : "is not");
|
||||
|
||||
if (fw_hdr->preemption_buffer_2_max_size)
|
||||
fw->secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_max_size;
|
||||
else
|
||||
fw->secondary_preempt_buf_size = fw_hdr->preemption_buffer_2_size;
|
||||
ivpu_dbg(vdev, FW_BOOT, "Preemption buffer sizes: primary %u, secondary %u\n",
|
||||
fw->primary_preempt_buf_size, fw->secondary_preempt_buf_size);
|
||||
|
||||
if (fw_hdr->ro_section_start_address && !is_within_range(fw_hdr->ro_section_start_address,
|
||||
fw_hdr->ro_section_size,
|
||||
fw_hdr->image_load_address,
|
||||
fw_hdr->image_size)) {
|
||||
if (fw_hdr->ro_section_start_address &&
|
||||
!ivpu_is_within_range(fw_hdr->ro_section_start_address, fw_hdr->ro_section_size,
|
||||
&fw_image_range)) {
|
||||
ivpu_err(vdev, "Invalid read-only section: start address 0x%llx, size %u\n",
|
||||
fw_hdr->ro_section_start_address, fw_hdr->ro_section_size);
|
||||
return -EINVAL;
|
||||
|
|
@ -259,12 +328,18 @@ static int ivpu_fw_parse(struct ivpu_device *vdev)
|
|||
fw->read_only_addr = fw_hdr->ro_section_start_address;
|
||||
fw->read_only_size = fw_hdr->ro_section_size;
|
||||
|
||||
ivpu_dbg(vdev, FW_BOOT, "Size: file %lu image %u runtime %u shavenn %u\n",
|
||||
fw->file->size, fw->image_size, fw->runtime_size, fw->shave_nn_size);
|
||||
ivpu_dbg(vdev, FW_BOOT, "Address: runtime 0x%llx, load 0x%llx, entry point 0x%llx\n",
|
||||
fw->runtime_addr, image_load_addr, fw->entry_point);
|
||||
ivpu_dbg(vdev, FW_BOOT, "Boot params: address 0x%llx, size %llu\n",
|
||||
fw->boot_params_addr, fw->boot_params_size);
|
||||
ivpu_dbg(vdev, FW_BOOT, "FW version: address 0x%llx, size %llu\n",
|
||||
fw->fw_version_addr, fw->fw_version_size);
|
||||
ivpu_dbg(vdev, FW_BOOT, "Runtime: address 0x%llx, size %u\n",
|
||||
fw->runtime_addr, fw->runtime_size);
|
||||
ivpu_dbg(vdev, FW_BOOT, "Image load offset: 0x%llx, size %u\n",
|
||||
fw->image_load_offset, fw->image_size);
|
||||
ivpu_dbg(vdev, FW_BOOT, "Read-only section: address 0x%llx, size %u\n",
|
||||
fw->read_only_addr, fw->read_only_size);
|
||||
ivpu_dbg(vdev, FW_BOOT, "FW entry point: 0x%llx\n", fw->entry_point);
|
||||
ivpu_dbg(vdev, FW_BOOT, "SHAVE NN size: %u\n", fw->shave_nn_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -291,39 +366,33 @@ ivpu_fw_init_wa(struct ivpu_device *vdev)
|
|||
IVPU_PRINT_WA(disable_d0i3_msg);
|
||||
}
|
||||
|
||||
static int ivpu_fw_update_global_range(struct ivpu_device *vdev)
|
||||
{
|
||||
struct ivpu_fw_info *fw = vdev->fw;
|
||||
u64 start = ALIGN(fw->runtime_addr + fw->runtime_size, FW_SHARED_MEM_ALIGNMENT);
|
||||
u64 size = FW_SHARED_MEM_SIZE;
|
||||
|
||||
if (start + size > FW_GLOBAL_MEM_END) {
|
||||
ivpu_err(vdev, "No space for shared region, start %lld, size %lld\n", start, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.global, start, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ivpu_fw_mem_init(struct ivpu_device *vdev)
|
||||
{
|
||||
struct ivpu_fw_info *fw = vdev->fw;
|
||||
struct ivpu_addr_range fw_range;
|
||||
int log_verb_size;
|
||||
int ret;
|
||||
|
||||
ret = ivpu_fw_update_global_range(vdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
fw->mem_bp = ivpu_bo_create_runtime(vdev, fw->boot_params_addr, fw->boot_params_size,
|
||||
DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
|
||||
if (!fw->mem_bp) {
|
||||
ivpu_err(vdev, "Failed to create firmware boot params memory buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fw_range.start = fw->runtime_addr;
|
||||
fw_range.end = fw->runtime_addr + fw->runtime_size;
|
||||
fw->mem = ivpu_bo_create(vdev, &vdev->gctx, &fw_range, fw->runtime_size,
|
||||
DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
|
||||
fw->mem_fw_ver = ivpu_bo_create_runtime(vdev, fw->fw_version_addr, fw->fw_version_size,
|
||||
DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
|
||||
if (!fw->mem_fw_ver) {
|
||||
ivpu_err(vdev, "Failed to create firmware version memory buffer\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_free_bp;
|
||||
}
|
||||
|
||||
fw->mem = ivpu_bo_create_runtime(vdev, fw->runtime_addr, fw->runtime_size,
|
||||
DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
|
||||
if (!fw->mem) {
|
||||
ivpu_err(vdev, "Failed to create firmware runtime memory buffer\n");
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto err_free_fw_ver;
|
||||
}
|
||||
|
||||
ret = ivpu_mmu_context_set_pages_ro(vdev, &vdev->gctx, fw->read_only_addr,
|
||||
|
|
@ -372,6 +441,10 @@ static int ivpu_fw_mem_init(struct ivpu_device *vdev)
|
|||
ivpu_bo_free(fw->mem_log_crit);
|
||||
err_free_fw_mem:
|
||||
ivpu_bo_free(fw->mem);
|
||||
err_free_fw_ver:
|
||||
ivpu_bo_free(fw->mem_fw_ver);
|
||||
err_free_bp:
|
||||
ivpu_bo_free(fw->mem_bp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -387,10 +460,14 @@ static void ivpu_fw_mem_fini(struct ivpu_device *vdev)
|
|||
ivpu_bo_free(fw->mem_log_verb);
|
||||
ivpu_bo_free(fw->mem_log_crit);
|
||||
ivpu_bo_free(fw->mem);
|
||||
ivpu_bo_free(fw->mem_fw_ver);
|
||||
ivpu_bo_free(fw->mem_bp);
|
||||
|
||||
fw->mem_log_verb = NULL;
|
||||
fw->mem_log_crit = NULL;
|
||||
fw->mem = NULL;
|
||||
fw->mem_fw_ver = NULL;
|
||||
fw->mem_bp = NULL;
|
||||
}
|
||||
|
||||
int ivpu_fw_init(struct ivpu_device *vdev)
|
||||
|
|
@ -483,11 +560,6 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
|
|||
ivpu_dbg(vdev, FW_BOOT, "boot_params.cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg = 0x%x\n",
|
||||
boot_params->cache_defaults[VPU_BOOT_L2_CACHE_CFG_NN].cfg);
|
||||
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.global_memory_allocator_base = 0x%llx\n",
|
||||
boot_params->global_memory_allocator_base);
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.global_memory_allocator_size = 0x%x\n",
|
||||
boot_params->global_memory_allocator_size);
|
||||
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.shave_nn_fw_base = 0x%llx\n",
|
||||
boot_params->shave_nn_fw_base);
|
||||
|
||||
|
|
@ -495,10 +567,6 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
|
|||
boot_params->watchdog_irq_mss);
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.watchdog_irq_nce = 0x%x\n",
|
||||
boot_params->watchdog_irq_nce);
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.host_to_vpu_irq = 0x%x\n",
|
||||
boot_params->host_to_vpu_irq);
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.job_done_irq = 0x%x\n",
|
||||
boot_params->job_done_irq);
|
||||
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.host_version_id = 0x%x\n",
|
||||
boot_params->host_version_id);
|
||||
|
|
@ -546,6 +614,8 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
|
|||
boot_params->system_time_us);
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.power_profile = 0x%x\n",
|
||||
boot_params->power_profile);
|
||||
ivpu_dbg(vdev, FW_BOOT, "boot_params.vpu_uses_ecc_mca_signal = 0x%x\n",
|
||||
boot_params->vpu_uses_ecc_mca_signal);
|
||||
}
|
||||
|
||||
void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params)
|
||||
|
|
@ -572,6 +642,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
|
|||
return;
|
||||
}
|
||||
|
||||
memset(boot_params, 0, sizeof(*boot_params));
|
||||
vdev->pm->is_warmboot = false;
|
||||
|
||||
boot_params->magic = VPU_BOOT_PARAMS_MAGIC;
|
||||
|
|
@ -647,6 +718,8 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
|
|||
boot_params->d0i3_entry_vpu_ts = 0;
|
||||
if (IVPU_WA(disable_d0i2))
|
||||
boot_params->power_profile |= BIT(1);
|
||||
boot_params->vpu_uses_ecc_mca_signal =
|
||||
ivpu_hw_uses_ecc_mca_signal(vdev) ? VPU_BOOT_MCA_ECC_BOTH : 0;
|
||||
|
||||
boot_params->system_time_us = ktime_to_us(ktime_get_real());
|
||||
wmb(); /* Flush WC buffers after writing bootparams */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2020-2024 Intel Corporation
|
||||
* Copyright (C) 2020-2025 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __IVPU_FW_H__
|
||||
|
|
@ -19,10 +19,16 @@ struct ivpu_fw_info {
|
|||
const struct firmware *file;
|
||||
const char *name;
|
||||
char version[FW_VERSION_STR_SIZE];
|
||||
struct ivpu_bo *mem_bp;
|
||||
struct ivpu_bo *mem_fw_ver;
|
||||
struct ivpu_bo *mem;
|
||||
struct ivpu_bo *mem_shave_nn;
|
||||
struct ivpu_bo *mem_log_crit;
|
||||
struct ivpu_bo *mem_log_verb;
|
||||
u64 boot_params_addr;
|
||||
u64 boot_params_size;
|
||||
u64 fw_version_addr;
|
||||
u64 fw_version_size;
|
||||
u64 runtime_addr;
|
||||
u32 runtime_size;
|
||||
u64 image_load_offset;
|
||||
|
|
@ -42,6 +48,7 @@ struct ivpu_fw_info {
|
|||
u64 last_heartbeat;
|
||||
};
|
||||
|
||||
bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range);
|
||||
int ivpu_fw_init(struct ivpu_device *vdev);
|
||||
void ivpu_fw_fini(struct ivpu_device *vdev);
|
||||
void ivpu_fw_load(struct ivpu_device *vdev);
|
||||
|
|
@ -52,4 +59,9 @@ static inline bool ivpu_fw_is_cold_boot(struct ivpu_device *vdev)
|
|||
return vdev->fw->entry_point == vdev->fw->cold_boot_entry_point;
|
||||
}
|
||||
|
||||
static inline u32 ivpu_fw_preempt_buf_size(struct ivpu_device *vdev)
|
||||
{
|
||||
return vdev->fw->primary_preempt_buf_size + vdev->fw->secondary_preempt_buf_size;
|
||||
}
|
||||
|
||||
#endif /* __IVPU_FW_H__ */
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <drm/drm_utils.h>
|
||||
|
||||
#include "ivpu_drv.h"
|
||||
#include "ivpu_fw.h"
|
||||
#include "ivpu_gem.h"
|
||||
#include "ivpu_hw.h"
|
||||
#include "ivpu_mmu.h"
|
||||
|
|
@ -27,8 +28,8 @@ static const struct drm_gem_object_funcs ivpu_gem_funcs;
|
|||
static inline void ivpu_dbg_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, const char *action)
|
||||
{
|
||||
ivpu_dbg(vdev, BO,
|
||||
"%6s: bo %8p vpu_addr %9llx size %8zu ctx %d has_pages %d dma_mapped %d mmu_mapped %d wc %d imported %d\n",
|
||||
action, bo, bo->vpu_addr, ivpu_bo_size(bo), bo->ctx_id,
|
||||
"%6s: bo %8p size %9zu ctx %d vpu_addr %9llx pages %d sgt %d mmu_mapped %d wc %d imported %d\n",
|
||||
action, bo, ivpu_bo_size(bo), bo->ctx_id, bo->vpu_addr,
|
||||
(bool)bo->base.pages, (bool)bo->base.sgt, bo->mmu_mapped, bo->base.map_wc,
|
||||
(bool)drm_gem_is_imported(&bo->base.base));
|
||||
}
|
||||
|
|
@ -43,22 +44,47 @@ static inline void ivpu_bo_unlock(struct ivpu_bo *bo)
|
|||
dma_resv_unlock(bo->base.base.resv);
|
||||
}
|
||||
|
||||
static struct sg_table *ivpu_bo_map_attachment(struct ivpu_device *vdev, struct ivpu_bo *bo)
|
||||
{
|
||||
struct sg_table *sgt;
|
||||
|
||||
drm_WARN_ON(&vdev->drm, !bo->base.base.import_attach);
|
||||
|
||||
ivpu_bo_lock(bo);
|
||||
|
||||
sgt = bo->base.sgt;
|
||||
if (!sgt) {
|
||||
sgt = dma_buf_map_attachment(bo->base.base.import_attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(sgt))
|
||||
ivpu_err(vdev, "Failed to map BO in IOMMU: %ld\n", PTR_ERR(sgt));
|
||||
else
|
||||
bo->base.sgt = sgt;
|
||||
}
|
||||
|
||||
ivpu_bo_unlock(bo);
|
||||
|
||||
return sgt;
|
||||
}
|
||||
|
||||
/*
|
||||
* ivpu_bo_pin() - pin the backing physical pages and map them to VPU.
|
||||
* ivpu_bo_bind() - pin the backing physical pages and map them to VPU.
|
||||
*
|
||||
* This function pins physical memory pages, then maps the physical pages
|
||||
* to IOMMU address space and finally updates the VPU MMU page tables
|
||||
* to allow the VPU to translate VPU address to IOMMU address.
|
||||
*/
|
||||
int __must_check ivpu_bo_pin(struct ivpu_bo *bo)
|
||||
int __must_check ivpu_bo_bind(struct ivpu_bo *bo)
|
||||
{
|
||||
struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
|
||||
struct sg_table *sgt;
|
||||
int ret = 0;
|
||||
|
||||
ivpu_dbg_bo(vdev, bo, "pin");
|
||||
ivpu_dbg_bo(vdev, bo, "bind");
|
||||
|
||||
sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
|
||||
if (bo->base.base.import_attach)
|
||||
sgt = ivpu_bo_map_attachment(vdev, bo);
|
||||
else
|
||||
sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
|
||||
if (IS_ERR(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
ivpu_err(vdev, "Failed to map BO in IOMMU: %d\n", ret);
|
||||
|
|
@ -70,7 +96,7 @@ int __must_check ivpu_bo_pin(struct ivpu_bo *bo)
|
|||
if (!bo->mmu_mapped) {
|
||||
drm_WARN_ON(&vdev->drm, !bo->ctx);
|
||||
ret = ivpu_mmu_context_map_sgt(vdev, bo->ctx, bo->vpu_addr, sgt,
|
||||
ivpu_bo_is_snooped(bo));
|
||||
ivpu_bo_is_snooped(bo), ivpu_bo_is_read_only(bo));
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Failed to map BO in MMU: %d\n", ret);
|
||||
goto unlock;
|
||||
|
|
@ -99,9 +125,9 @@ ivpu_bo_alloc_vpu_addr(struct ivpu_bo *bo, struct ivpu_mmu_context *ctx,
|
|||
ret = ivpu_mmu_context_insert_node(ctx, range, ivpu_bo_size(bo), &bo->mm_node);
|
||||
if (!ret) {
|
||||
bo->ctx = ctx;
|
||||
bo->ctx_id = ctx->id;
|
||||
bo->vpu_addr = bo->mm_node.start;
|
||||
} else {
|
||||
ivpu_err(vdev, "Failed to add BO to context %u: %d\n", ctx->id, ret);
|
||||
ivpu_dbg_bo(vdev, bo, "vaddr");
|
||||
}
|
||||
|
||||
ivpu_bo_unlock(bo);
|
||||
|
|
@ -115,7 +141,7 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo)
|
|||
{
|
||||
struct ivpu_device *vdev = ivpu_bo_to_vdev(bo);
|
||||
|
||||
lockdep_assert(dma_resv_held(bo->base.base.resv) || !kref_read(&bo->base.base.refcount));
|
||||
dma_resv_assert_held(bo->base.base.resv);
|
||||
|
||||
if (bo->mmu_mapped) {
|
||||
drm_WARN_ON(&vdev->drm, !bo->ctx);
|
||||
|
|
@ -130,13 +156,15 @@ static void ivpu_bo_unbind_locked(struct ivpu_bo *bo)
|
|||
bo->ctx = NULL;
|
||||
}
|
||||
|
||||
if (drm_gem_is_imported(&bo->base.base))
|
||||
return;
|
||||
|
||||
if (bo->base.sgt) {
|
||||
dma_unmap_sgtable(vdev->drm.dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0);
|
||||
sg_free_table(bo->base.sgt);
|
||||
kfree(bo->base.sgt);
|
||||
if (bo->base.base.import_attach) {
|
||||
dma_buf_unmap_attachment(bo->base.base.import_attach,
|
||||
bo->base.sgt, DMA_BIDIRECTIONAL);
|
||||
} else {
|
||||
dma_unmap_sgtable(vdev->drm.dev, bo->base.sgt, DMA_BIDIRECTIONAL, 0);
|
||||
sg_free_table(bo->base.sgt);
|
||||
kfree(bo->base.sgt);
|
||||
}
|
||||
bo->base.sgt = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -182,10 +210,11 @@ struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t siz
|
|||
struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
struct ivpu_device *vdev = to_ivpu_device(dev);
|
||||
struct device *attach_dev = dev->dev;
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sgt;
|
||||
struct drm_gem_object *obj;
|
||||
struct ivpu_bo *bo;
|
||||
int ret;
|
||||
|
||||
attach = dma_buf_attach(dma_buf, attach_dev);
|
||||
|
|
@ -194,25 +223,25 @@ struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
|
|||
|
||||
get_dma_buf(dma_buf);
|
||||
|
||||
sgt = dma_buf_map_attachment_unlocked(attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(sgt)) {
|
||||
ret = PTR_ERR(sgt);
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
|
||||
obj = drm_gem_shmem_prime_import_sg_table(dev, attach, NULL);
|
||||
if (IS_ERR(obj)) {
|
||||
ret = PTR_ERR(obj);
|
||||
goto fail_unmap;
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
obj->import_attach = attach;
|
||||
obj->resv = dma_buf->resv;
|
||||
|
||||
bo = to_ivpu_bo(obj);
|
||||
|
||||
mutex_lock(&vdev->bo_list_lock);
|
||||
list_add_tail(&bo->bo_list_node, &vdev->bo_list);
|
||||
mutex_unlock(&vdev->bo_list_lock);
|
||||
|
||||
ivpu_dbg(vdev, BO, "import: bo %8p size %9zu\n", bo, ivpu_bo_size(bo));
|
||||
|
||||
return obj;
|
||||
|
||||
fail_unmap:
|
||||
dma_buf_unmap_attachment_unlocked(attach, sgt, DMA_BIDIRECTIONAL);
|
||||
fail_detach:
|
||||
dma_buf_detach(dma_buf, attach);
|
||||
dma_buf_put(dma_buf);
|
||||
|
|
@ -220,7 +249,7 @@ struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags, u32 ctx_id)
|
||||
static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags)
|
||||
{
|
||||
struct drm_gem_shmem_object *shmem;
|
||||
struct ivpu_bo *bo;
|
||||
|
|
@ -238,7 +267,6 @@ static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 fla
|
|||
return ERR_CAST(shmem);
|
||||
|
||||
bo = to_ivpu_bo(&shmem->base);
|
||||
bo->ctx_id = ctx_id;
|
||||
bo->base.map_wc = flags & DRM_IVPU_BO_WC;
|
||||
bo->flags = flags;
|
||||
|
||||
|
|
@ -246,7 +274,7 @@ static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 fla
|
|||
list_add_tail(&bo->bo_list_node, &vdev->bo_list);
|
||||
mutex_unlock(&vdev->bo_list_lock);
|
||||
|
||||
ivpu_dbg_bo(vdev, bo, "alloc");
|
||||
ivpu_dbg(vdev, BO, " alloc: bo %8p size %9llu\n", bo, size);
|
||||
|
||||
return bo;
|
||||
}
|
||||
|
|
@ -259,8 +287,8 @@ static int ivpu_gem_bo_open(struct drm_gem_object *obj, struct drm_file *file)
|
|||
struct ivpu_addr_range *range;
|
||||
|
||||
if (bo->ctx) {
|
||||
ivpu_warn(vdev, "Can't add BO to ctx %u: already in ctx %u\n",
|
||||
file_priv->ctx.id, bo->ctx->id);
|
||||
ivpu_dbg(vdev, IOCTL, "Can't add BO %pe to ctx %u: already in ctx %u\n",
|
||||
bo, file_priv->ctx.id, bo->ctx->id);
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
|
|
@ -281,23 +309,41 @@ static void ivpu_gem_bo_free(struct drm_gem_object *obj)
|
|||
|
||||
ivpu_dbg_bo(vdev, bo, "free");
|
||||
|
||||
drm_WARN_ON(&vdev->drm, list_empty(&bo->bo_list_node));
|
||||
|
||||
mutex_lock(&vdev->bo_list_lock);
|
||||
list_del(&bo->bo_list_node);
|
||||
mutex_unlock(&vdev->bo_list_lock);
|
||||
|
||||
drm_WARN_ON(&vdev->drm, !drm_gem_is_imported(&bo->base.base) &&
|
||||
!dma_resv_test_signaled(obj->resv, DMA_RESV_USAGE_READ));
|
||||
drm_WARN_ON(&vdev->drm, ivpu_bo_size(bo) == 0);
|
||||
drm_WARN_ON(&vdev->drm, bo->base.vaddr);
|
||||
|
||||
ivpu_bo_lock(bo);
|
||||
ivpu_bo_unbind_locked(bo);
|
||||
ivpu_bo_unlock(bo);
|
||||
|
||||
mutex_unlock(&vdev->bo_list_lock);
|
||||
|
||||
drm_WARN_ON(&vdev->drm, bo->mmu_mapped);
|
||||
drm_WARN_ON(&vdev->drm, bo->ctx);
|
||||
|
||||
drm_WARN_ON(obj->dev, refcount_read(&bo->base.pages_use_count) > 1);
|
||||
drm_WARN_ON(obj->dev, bo->base.base.vma_node.vm_files.rb_node);
|
||||
drm_gem_shmem_free(&bo->base);
|
||||
}
|
||||
|
||||
static enum drm_gem_object_status ivpu_gem_status(struct drm_gem_object *obj)
|
||||
{
|
||||
struct ivpu_bo *bo = to_ivpu_bo(obj);
|
||||
enum drm_gem_object_status status = 0;
|
||||
|
||||
if (ivpu_bo_is_resident(bo))
|
||||
status |= DRM_GEM_OBJECT_RESIDENT;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const struct drm_gem_object_funcs ivpu_gem_funcs = {
|
||||
.free = ivpu_gem_bo_free,
|
||||
.open = ivpu_gem_bo_open,
|
||||
|
|
@ -308,6 +354,7 @@ static const struct drm_gem_object_funcs ivpu_gem_funcs = {
|
|||
.vmap = drm_gem_shmem_object_vmap,
|
||||
.vunmap = drm_gem_shmem_object_vunmap,
|
||||
.mmap = drm_gem_shmem_object_mmap,
|
||||
.status = ivpu_gem_status,
|
||||
.vm_ops = &drm_gem_shmem_vm_ops,
|
||||
};
|
||||
|
||||
|
|
@ -320,25 +367,33 @@ int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *fi
|
|||
struct ivpu_bo *bo;
|
||||
int ret;
|
||||
|
||||
if (args->flags & ~DRM_IVPU_BO_FLAGS)
|
||||
if (args->flags & ~DRM_IVPU_BO_FLAGS) {
|
||||
ivpu_dbg(vdev, IOCTL, "Invalid BO flags 0x%x\n", args->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (size == 0)
|
||||
if (size == 0) {
|
||||
ivpu_dbg(vdev, IOCTL, "Invalid BO size %llu\n", args->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bo = ivpu_bo_alloc(vdev, size, args->flags, file_priv->ctx.id);
|
||||
bo = ivpu_bo_alloc(vdev, size, args->flags);
|
||||
if (IS_ERR(bo)) {
|
||||
ivpu_err(vdev, "Failed to allocate BO: %pe (ctx %u size %llu flags 0x%x)",
|
||||
ivpu_dbg(vdev, IOCTL, "Failed to allocate BO: %pe ctx %u size %llu flags 0x%x\n",
|
||||
bo, file_priv->ctx.id, args->size, args->flags);
|
||||
return PTR_ERR(bo);
|
||||
}
|
||||
|
||||
drm_WARN_ON(&vdev->drm, bo->base.base.handle_count != 0);
|
||||
|
||||
ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
|
||||
if (ret)
|
||||
ivpu_err(vdev, "Failed to create handle for BO: %pe (ctx %u size %llu flags 0x%x)",
|
||||
if (ret) {
|
||||
ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",
|
||||
bo, file_priv->ctx.id, args->size, args->flags);
|
||||
else
|
||||
} else {
|
||||
args->vpu_addr = bo->vpu_addr;
|
||||
drm_WARN_ON(&vdev->drm, bo->base.base.handle_count != 1);
|
||||
}
|
||||
|
||||
drm_gem_object_put(&bo->base.base);
|
||||
|
||||
|
|
@ -360,18 +415,21 @@ ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
|||
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(range->end));
|
||||
drm_WARN_ON(&vdev->drm, !PAGE_ALIGNED(size));
|
||||
|
||||
bo = ivpu_bo_alloc(vdev, size, flags, IVPU_GLOBAL_CONTEXT_MMU_SSID);
|
||||
bo = ivpu_bo_alloc(vdev, size, flags);
|
||||
if (IS_ERR(bo)) {
|
||||
ivpu_err(vdev, "Failed to allocate BO: %pe (vpu_addr 0x%llx size %llu flags 0x%x)",
|
||||
ivpu_err(vdev, "Failed to allocate BO: %pe vpu_addr 0x%llx size %llu flags 0x%x\n",
|
||||
bo, range->start, size, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = ivpu_bo_alloc_vpu_addr(bo, ctx, range);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Failed to allocate NPU address for BO: %pe ctx %u size %llu: %d\n",
|
||||
bo, ctx->id, size, ret);
|
||||
goto err_put;
|
||||
}
|
||||
|
||||
ret = ivpu_bo_pin(bo);
|
||||
ret = ivpu_bo_bind(bo);
|
||||
if (ret)
|
||||
goto err_put;
|
||||
|
||||
|
|
@ -391,6 +449,21 @@ ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags)
|
||||
{
|
||||
struct ivpu_addr_range range;
|
||||
|
||||
if (!ivpu_is_within_range(addr, size, &vdev->hw->ranges.runtime)) {
|
||||
ivpu_err(vdev, "Invalid runtime BO address 0x%llx size %llu\n", addr, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ivpu_hw_range_init(vdev, &range, addr, size))
|
||||
return NULL;
|
||||
|
||||
return ivpu_bo_create(vdev, &vdev->gctx, &range, size, flags);
|
||||
}
|
||||
|
||||
struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags)
|
||||
{
|
||||
return ivpu_bo_create(vdev, &vdev->gctx, &vdev->hw->ranges.global, size, flags);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2020-2023 Intel Corporation
|
||||
* Copyright (C) 2020-2025 Intel Corporation
|
||||
*/
|
||||
#ifndef __IVPU_GEM_H__
|
||||
#define __IVPU_GEM_H__
|
||||
|
|
@ -24,19 +24,22 @@ struct ivpu_bo {
|
|||
bool mmu_mapped;
|
||||
};
|
||||
|
||||
int ivpu_bo_pin(struct ivpu_bo *bo);
|
||||
int ivpu_bo_bind(struct ivpu_bo *bo);
|
||||
void ivpu_bo_unbind_all_bos_from_context(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx);
|
||||
|
||||
struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t size);
|
||||
struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf);
|
||||
struct ivpu_bo *ivpu_bo_create(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx,
|
||||
struct ivpu_addr_range *range, u64 size, u32 flags);
|
||||
struct ivpu_bo *ivpu_bo_create_runtime(struct ivpu_device *vdev, u64 addr, u64 size, u32 flags);
|
||||
struct ivpu_bo *ivpu_bo_create_global(struct ivpu_device *vdev, u64 size, u32 flags);
|
||||
void ivpu_bo_free(struct ivpu_bo *bo);
|
||||
|
||||
int ivpu_bo_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
|
||||
int ivpu_bo_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
|
||||
int ivpu_bo_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
|
||||
int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
void ivpu_bo_list(struct drm_device *dev, struct drm_printer *p);
|
||||
void ivpu_bo_list_print(struct drm_device *dev);
|
||||
|
|
@ -74,6 +77,16 @@ static inline bool ivpu_bo_is_snooped(struct ivpu_bo *bo)
|
|||
return ivpu_bo_cache_mode(bo) == DRM_IVPU_BO_CACHED;
|
||||
}
|
||||
|
||||
static inline bool ivpu_bo_is_read_only(struct ivpu_bo *bo)
|
||||
{
|
||||
return bo->flags & DRM_IVPU_BO_READ_ONLY;
|
||||
}
|
||||
|
||||
static inline bool ivpu_bo_is_resident(struct ivpu_bo *bo)
|
||||
{
|
||||
return !!bo->base.pages;
|
||||
}
|
||||
|
||||
static inline void *ivpu_to_cpu_addr(struct ivpu_bo *bo, u32 vpu_addr)
|
||||
{
|
||||
if (vpu_addr < bo->vpu_addr)
|
||||
|
|
@ -96,4 +109,9 @@ static inline u32 cpu_to_vpu_addr(struct ivpu_bo *bo, void *cpu_addr)
|
|||
return bo->vpu_addr + (cpu_addr - ivpu_bo_vaddr(bo));
|
||||
}
|
||||
|
||||
static inline bool ivpu_bo_is_mappable(struct ivpu_bo *bo)
|
||||
{
|
||||
return bo->flags & DRM_IVPU_BO_MAPPABLE;
|
||||
}
|
||||
|
||||
#endif /* __IVPU_GEM_H__ */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,213 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2020-2025 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/capability.h>
|
||||
|
||||
#include <drm/drm_device.h>
|
||||
#include <drm/drm_file.h>
|
||||
#include <drm/drm_gem.h>
|
||||
|
||||
#include "ivpu_drv.h"
|
||||
#include "ivpu_gem.h"
|
||||
|
||||
static struct sg_table *
|
||||
ivpu_gem_userptr_dmabuf_map(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct sg_table *sgt = attachment->dmabuf->priv;
|
||||
int ret;
|
||||
|
||||
ret = dma_map_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return sgt;
|
||||
}
|
||||
|
||||
static void ivpu_gem_userptr_dmabuf_unmap(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *sgt,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
dma_unmap_sgtable(attachment->dev, sgt, direction, DMA_ATTR_SKIP_CPU_SYNC);
|
||||
}
|
||||
|
||||
static void ivpu_gem_userptr_dmabuf_release(struct dma_buf *dma_buf)
|
||||
{
|
||||
struct sg_table *sgt = dma_buf->priv;
|
||||
struct sg_page_iter page_iter;
|
||||
struct page *page;
|
||||
|
||||
for_each_sgtable_page(sgt, &page_iter, 0) {
|
||||
page = sg_page_iter_page(&page_iter);
|
||||
unpin_user_page(page);
|
||||
}
|
||||
|
||||
sg_free_table(sgt);
|
||||
kfree(sgt);
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops ivpu_gem_userptr_dmabuf_ops = {
|
||||
.map_dma_buf = ivpu_gem_userptr_dmabuf_map,
|
||||
.unmap_dma_buf = ivpu_gem_userptr_dmabuf_unmap,
|
||||
.release = ivpu_gem_userptr_dmabuf_release,
|
||||
};
|
||||
|
||||
static struct dma_buf *
|
||||
ivpu_create_userptr_dmabuf(struct ivpu_device *vdev, void __user *user_ptr,
|
||||
size_t size, uint32_t flags)
|
||||
{
|
||||
struct dma_buf_export_info exp_info = {};
|
||||
struct dma_buf *dma_buf;
|
||||
struct sg_table *sgt;
|
||||
struct page **pages;
|
||||
unsigned long nr_pages = size >> PAGE_SHIFT;
|
||||
unsigned int gup_flags = FOLL_LONGTERM;
|
||||
int ret, i, pinned;
|
||||
|
||||
/* Add FOLL_WRITE only if the BO is not read-only */
|
||||
if (!(flags & DRM_IVPU_BO_READ_ONLY))
|
||||
gup_flags |= FOLL_WRITE;
|
||||
|
||||
pages = kvmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pinned = pin_user_pages_fast((unsigned long)user_ptr, nr_pages, gup_flags, pages);
|
||||
if (pinned < 0) {
|
||||
ret = pinned;
|
||||
ivpu_dbg(vdev, IOCTL, "Failed to pin user pages: %d\n", ret);
|
||||
goto free_pages_array;
|
||||
}
|
||||
|
||||
if (pinned != nr_pages) {
|
||||
ivpu_dbg(vdev, IOCTL, "Pinned %d pages, expected %lu\n", pinned, nr_pages);
|
||||
ret = -EFAULT;
|
||||
goto unpin_pages;
|
||||
}
|
||||
|
||||
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt) {
|
||||
ret = -ENOMEM;
|
||||
goto unpin_pages;
|
||||
}
|
||||
|
||||
ret = sg_alloc_table_from_pages(sgt, pages, nr_pages, 0, size, GFP_KERNEL);
|
||||
if (ret) {
|
||||
ivpu_dbg(vdev, IOCTL, "Failed to create sg table: %d\n", ret);
|
||||
goto free_sgt;
|
||||
}
|
||||
|
||||
exp_info.exp_name = "ivpu_userptr_dmabuf";
|
||||
exp_info.owner = THIS_MODULE;
|
||||
exp_info.ops = &ivpu_gem_userptr_dmabuf_ops;
|
||||
exp_info.size = size;
|
||||
exp_info.flags = O_RDWR | O_CLOEXEC;
|
||||
exp_info.priv = sgt;
|
||||
|
||||
dma_buf = dma_buf_export(&exp_info);
|
||||
if (IS_ERR(dma_buf)) {
|
||||
ret = PTR_ERR(dma_buf);
|
||||
ivpu_dbg(vdev, IOCTL, "Failed to export userptr dma-buf: %d\n", ret);
|
||||
goto free_sg_table;
|
||||
}
|
||||
|
||||
kvfree(pages);
|
||||
return dma_buf;
|
||||
|
||||
free_sg_table:
|
||||
sg_free_table(sgt);
|
||||
free_sgt:
|
||||
kfree(sgt);
|
||||
unpin_pages:
|
||||
for (i = 0; i < pinned; i++)
|
||||
unpin_user_page(pages[i]);
|
||||
free_pages_array:
|
||||
kvfree(pages);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct ivpu_bo *
|
||||
ivpu_bo_create_from_userptr(struct ivpu_device *vdev, void __user *user_ptr,
|
||||
size_t size, uint32_t flags)
|
||||
{
|
||||
struct dma_buf *dma_buf;
|
||||
struct drm_gem_object *obj;
|
||||
struct ivpu_bo *bo;
|
||||
|
||||
dma_buf = ivpu_create_userptr_dmabuf(vdev, user_ptr, size, flags);
|
||||
if (IS_ERR(dma_buf))
|
||||
return ERR_CAST(dma_buf);
|
||||
|
||||
obj = ivpu_gem_prime_import(&vdev->drm, dma_buf);
|
||||
if (IS_ERR(obj)) {
|
||||
dma_buf_put(dma_buf);
|
||||
return ERR_CAST(obj);
|
||||
}
|
||||
|
||||
dma_buf_put(dma_buf);
|
||||
|
||||
bo = to_ivpu_bo(obj);
|
||||
bo->flags = flags;
|
||||
|
||||
return bo;
|
||||
}
|
||||
|
||||
int ivpu_bo_create_from_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
|
||||
{
|
||||
struct drm_ivpu_bo_create_from_userptr *args = data;
|
||||
struct ivpu_file_priv *file_priv = file->driver_priv;
|
||||
struct ivpu_device *vdev = to_ivpu_device(dev);
|
||||
void __user *user_ptr = u64_to_user_ptr(args->user_ptr);
|
||||
struct ivpu_bo *bo;
|
||||
int ret;
|
||||
|
||||
if (args->flags & ~(DRM_IVPU_BO_HIGH_MEM | DRM_IVPU_BO_DMA_MEM | DRM_IVPU_BO_READ_ONLY)) {
|
||||
ivpu_dbg(vdev, IOCTL, "Invalid BO flags: 0x%x\n", args->flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!args->user_ptr || !args->size) {
|
||||
ivpu_dbg(vdev, IOCTL, "Userptr or size are zero: ptr %llx size %llu\n",
|
||||
args->user_ptr, args->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!PAGE_ALIGNED(args->user_ptr) || !PAGE_ALIGNED(args->size)) {
|
||||
ivpu_dbg(vdev, IOCTL, "Userptr or size not page aligned: ptr %llx size %llu\n",
|
||||
args->user_ptr, args->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!access_ok(user_ptr, args->size)) {
|
||||
ivpu_dbg(vdev, IOCTL, "Userptr is not accessible: ptr %llx size %llu\n",
|
||||
args->user_ptr, args->size);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
bo = ivpu_bo_create_from_userptr(vdev, user_ptr, args->size, args->flags);
|
||||
if (IS_ERR(bo))
|
||||
return PTR_ERR(bo);
|
||||
|
||||
ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
|
||||
if (ret) {
|
||||
ivpu_dbg(vdev, IOCTL, "Failed to create handle for BO: %pe ctx %u size %llu flags 0x%x\n",
|
||||
bo, file_priv->ctx.id, args->size, args->flags);
|
||||
} else {
|
||||
ivpu_dbg(vdev, BO, "Created userptr BO: handle=%u vpu_addr=0x%llx size=%llu flags=0x%x\n",
|
||||
args->handle, bo->vpu_addr, args->size, bo->flags);
|
||||
args->vpu_addr = bo->vpu_addr;
|
||||
}
|
||||
|
||||
drm_gem_object_put(&bo->base.base);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@
|
|||
#include "ivpu_hw_btrs.h"
|
||||
#include "ivpu_hw_ip.h"
|
||||
|
||||
#include <asm/msr-index.h>
|
||||
#include <asm/msr.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/fault-inject.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
@ -20,6 +22,10 @@ module_param_named_unsafe(fail_hw, ivpu_fail_hw, charp, 0444);
|
|||
MODULE_PARM_DESC(fail_hw, "<interval>,<probability>,<space>,<times>");
|
||||
#endif
|
||||
|
||||
#define FW_SHARED_MEM_ALIGNMENT SZ_512K /* VPU MTRR limitation */
|
||||
|
||||
#define ECC_MCA_SIGNAL_ENABLE_MASK 0xff
|
||||
|
||||
static char *platform_to_str(u32 platform)
|
||||
{
|
||||
switch (platform) {
|
||||
|
|
@ -147,19 +153,39 @@ static void priority_bands_init(struct ivpu_device *vdev)
|
|||
vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 200000;
|
||||
}
|
||||
|
||||
int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start, u64 size)
|
||||
{
|
||||
u64 end;
|
||||
|
||||
if (!range || check_add_overflow(start, size, &end)) {
|
||||
ivpu_err(vdev, "Invalid range: start 0x%llx size %llu\n", start, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
range->start = start;
|
||||
range->end = end;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void memory_ranges_init(struct ivpu_device *vdev)
|
||||
{
|
||||
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M);
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.user, 0x88000000, 511 * SZ_1M);
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x180000000, SZ_2G);
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.dma, 0x200000000, SZ_128G);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x84800000, SZ_64M);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0xa0000000, 511 * SZ_1M);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x180000000, SZ_2G);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.dma, 0x200000000, SZ_128G);
|
||||
} else {
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.global, 0x80000000, SZ_512M);
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.shave, 0x80000000, SZ_2G);
|
||||
ivpu_hw_range_init(&vdev->hw->ranges.user, 0x100000000, SZ_256G);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.runtime, 0x80000000, SZ_64M);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.global, 0x90000000, SZ_256M);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.shave, 0x80000000, SZ_2G);
|
||||
ivpu_hw_range_init(vdev, &vdev->hw->ranges.user, 0x100000000, SZ_256G);
|
||||
vdev->hw->ranges.dma = vdev->hw->ranges.user;
|
||||
}
|
||||
|
||||
drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vdev->hw->ranges.global.start,
|
||||
FW_SHARED_MEM_ALIGNMENT));
|
||||
}
|
||||
|
||||
static int wp_enable(struct ivpu_device *vdev)
|
||||
|
|
@ -373,3 +399,22 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
|
|||
pm_runtime_mark_last_busy(vdev->drm.dev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
bool ivpu_hw_uses_ecc_mca_signal(struct ivpu_device *vdev)
|
||||
{
|
||||
unsigned long long msr_integrity_caps;
|
||||
int ret;
|
||||
|
||||
if (ivpu_hw_ip_gen(vdev) < IVPU_HW_IP_50XX)
|
||||
return false;
|
||||
|
||||
ret = rdmsrq_safe(MSR_INTEGRITY_CAPS, &msr_integrity_caps);
|
||||
if (ret) {
|
||||
ivpu_warn(vdev, "Error reading MSR_INTEGRITY_CAPS: %d", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
ivpu_dbg(vdev, MISC, "MSR_INTEGRITY_CAPS: 0x%llx\n", msr_integrity_caps);
|
||||
|
||||
return msr_integrity_caps & ECC_MCA_SIGNAL_ENABLE_MASK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ struct ivpu_hw_info {
|
|||
bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq);
|
||||
} irq;
|
||||
struct {
|
||||
struct ivpu_addr_range runtime;
|
||||
struct ivpu_addr_range global;
|
||||
struct ivpu_addr_range user;
|
||||
struct ivpu_addr_range shave;
|
||||
|
|
@ -51,6 +52,8 @@ struct ivpu_hw_info {
|
|||
};
|
||||
|
||||
int ivpu_hw_init(struct ivpu_device *vdev);
|
||||
int ivpu_hw_range_init(struct ivpu_device *vdev, struct ivpu_addr_range *range, u64 start,
|
||||
u64 size);
|
||||
int ivpu_hw_power_up(struct ivpu_device *vdev);
|
||||
int ivpu_hw_power_down(struct ivpu_device *vdev);
|
||||
int ivpu_hw_reset(struct ivpu_device *vdev);
|
||||
|
|
@ -60,6 +63,7 @@ void ivpu_irq_handlers_init(struct ivpu_device *vdev);
|
|||
void ivpu_hw_irq_enable(struct ivpu_device *vdev);
|
||||
void ivpu_hw_irq_disable(struct ivpu_device *vdev);
|
||||
irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr);
|
||||
bool ivpu_hw_uses_ecc_mca_signal(struct ivpu_device *vdev);
|
||||
|
||||
static inline u32 ivpu_hw_btrs_irq_handler(struct ivpu_device *vdev, int irq)
|
||||
{
|
||||
|
|
@ -71,12 +75,6 @@ static inline u32 ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq)
|
|||
return vdev->hw->irq.ip_irq_handler(vdev, irq);
|
||||
}
|
||||
|
||||
static inline void ivpu_hw_range_init(struct ivpu_addr_range *range, u64 start, u64 size)
|
||||
{
|
||||
range->start = start;
|
||||
range->end = start + size;
|
||||
}
|
||||
|
||||
static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range)
|
||||
{
|
||||
return range->end - range->start;
|
||||
|
|
|
|||
|
|
@ -321,6 +321,14 @@ static int wait_for_pll_lock(struct ivpu_device *vdev, bool enable)
|
|||
return REGB_POLL_FLD(VPU_HW_BTRS_MTL_PLL_STATUS, LOCK, exp_val, PLL_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int wait_for_cdyn_deassert(struct ivpu_device *vdev)
|
||||
{
|
||||
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
|
||||
return 0;
|
||||
|
||||
return REGB_POLL_FLD(VPU_HW_BTRS_LNL_CDYN, CDYN, 0, PLL_TIMEOUT_US);
|
||||
}
|
||||
|
||||
int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
|
||||
{
|
||||
struct wp_request wp;
|
||||
|
|
@ -354,6 +362,14 @@ int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (!enable) {
|
||||
ret = wait_for_cdyn_deassert(vdev);
|
||||
if (ret) {
|
||||
ivpu_err(vdev, "Timed out waiting for CDYN deassert\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -673,7 +689,7 @@ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq)
|
|||
|
||||
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR, status)) {
|
||||
ivpu_dbg(vdev, IRQ, "Survivability IRQ\n");
|
||||
queue_work(system_wq, &vdev->irq_dct_work);
|
||||
queue_work(system_percpu_wq, &vdev->irq_dct_work);
|
||||
}
|
||||
|
||||
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status)) {
|
||||
|
|
@ -752,7 +768,7 @@ int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable)
|
|||
}
|
||||
}
|
||||
|
||||
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent)
|
||||
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u8 active_percent)
|
||||
{
|
||||
u32 val = 0;
|
||||
u32 cmd = enable ? DCT_ENABLE : DCT_DISABLE;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ u32 ivpu_hw_btrs_dpu_freq_get(struct ivpu_device *vdev);
|
|||
bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq);
|
||||
bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq);
|
||||
int ivpu_hw_btrs_dct_get_request(struct ivpu_device *vdev, bool *enable);
|
||||
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u32 active_percent);
|
||||
void ivpu_hw_btrs_dct_set_status(struct ivpu_device *vdev, bool enable, u8 active_percent);
|
||||
u32 ivpu_hw_btrs_telemetry_offset_get(struct ivpu_device *vdev);
|
||||
u32 ivpu_hw_btrs_telemetry_size_get(struct ivpu_device *vdev);
|
||||
u32 ivpu_hw_btrs_telemetry_enable_get(struct ivpu_device *vdev);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue