drm for 6.15-rc1

uapi:
 - add mediatek tiled fourcc
 - add support for notifying userspace on device wedged
 
 new driver:
 - appletbdrm: support for Apple Touchbar displays on m1/m2
 - nova-core: skeleton rust driver to develop nova inside off
 
 firmware:
 - add some rust firmware pieces
 
 rust:
 - add 'LocalModule' type alias
 
 component:
 - add helper to query bound status
 
 fbdev:
 - fbtft: remove access to page->index
 
 media:
 - cec: tda998x: import driver from drm
 
 dma-buf:
 - add fast path for single fence merging
 
 tests:
 - fix lockdep warnings
 
 atomic:
 - allow full modeset on connector changes
 - clarify semantics of allow_modeset and drm_atomic_helper_check
 - async-flip: support on arbitary planes
 - writeback: fix UAF
 - Document atomic-state history
 
 format-helper:
 - support ARGB8888 to ARGB4444 conversions
 
 buddy:
 - fix multi-root cleanup
 
 ci:
 - update IGT
 
 dp:
 - support extended wake timeout
 - mst: fix RAD to string conversion
 - increase DPCD eDP control CAP size to 5 bytes
 - add DPCD eDP v1.5 definition
 - add helpers for LTTPR transparent mode
 
 panic:
 - encode QR code according to Fido 2.2
 
 scheduler:
 - add parameter struct for init
 - improve job peek/pop operations
 - optimise drm_sched_job struct layout
 
 ttm:
 - refactor pool allocation
 - add helpers for TTM shrinker
 
 panel-orientation:
 - add a bunch of new quirks
 
 panel:
 - convert panels to multi-style functions
 - edp: Add support for B140UAN04.4, BOE NV140FHM-NZ, CSW MNB601LS1-3,
   LG LP079QX1-SP0V, MNE007QS3-7, STA 116QHD024002, Starry 116KHD024006,
   Lenovo T14s Gen6 Snapdragon
 - himax-hx83102: Add support for CSOT PNA957QT1-1, Kingdisplay
   kd110n11-51ie, Starry 2082109qfh040022-50e
 - visionox-r66451: use multi-style MIPI-DSI functions
 - raydium-rm67200: Add driver for Raydium RM67200
 - simple: Add support for BOE AV123Z7M-N17, BOE AV123Z7M-N17
 - sony-td4353-jdi: Use MIPI-DSI multi-func interface
 - summit: Add driver for Apple Summit display panel
 - visionox-rm692e5: Add driver for Visionox RM692E5
 
 bridge:
 - pass full atomic state to various callbacks
 - adv7511: Report correct capabilities
 - it6505: Fix HDCP V compare
 - snd65dsi86: fix device IDs
 - nwl-dsi: set bridge type
 - ti-sn65si83: add error recovery and set bridge type
 - synopsys: add HDMI audio support
 
 xe:
 - support device-wedged event
 - add mmap support for PCI memory barrier
 - perf pmu integration and expose per-engien activity
 - add EU stall sampling support
 - GPU SVM and Xe SVM implementation
 - use TTM shrinker
 - add survivability mode to allow the driver to do
   firmware updates in critical failure states
 - PXP HWDRM support for MTL and LNL
 - expose package/vram temps over hwmon
 - enable DP tunneling
 - drop mmio_ext abstraction
 - Reject BO evcition if BO is bound to current VM
 - Xe suballocator improvements
 - re-use display vmas when possible
 - add GuC Buffer Cache abstraction
 - PCI ID update for Panther Lake and Battlemage
 - Enable SRIOV for Panther Lake
 - Refactor VRAM manager location
 
 i915:
 - enable extends wake timeout
 - support device-wedged event
 - Enable DP 128b/132b SST DSC
 - FBC dirty rectangle support for display version 30+
 - convert i915/xe to drm client setup
 - Compute HDMI PLLS for rates not in fixed tables
 - Allow DSB usage when PSR is enabled on LNL+
 - Enable panel replay without full modeset
 - Enable async flips with compressed buffers on ICL+
 - support luminance based brightness via DPCD for eDP
 - enable VRR enable/disable without full modeset
 - allow GuC SLPC default strategies on MTL+ for performance
 - lots of display refactoring in move to struct intel_display
 
 amdgpu:
 - add device wedged event
 - support async page flips on overlay planes
 - enable broadcast RGB drm property
 - add info ioctl for virt mode
 - OEM i2c support for RGB lights
 - GC 11.5.2 + 11.5.3 support
 - SDMA 6.1.3 support
 - NBIO 7.9.1 + 7.11.2 support
 - MMHUB 1.8.1 + 3.3.2 support
 - DCN 3.6.0 support
 - Add dynamic workload profile switching for GC 10-12
 - support larger VBIOS sizes
 - Mark gttsize parameters as deprecated
 - Initial JPEG queue resset support
 
 amdkfd:
 - add KFD per process flags for setting precision
 - sync pasid values between KGD and KFD
 - improve GTT/VRAM handling for APUs
 - fix user queue validation on GC7/8
 - SDMA queue reset support
 
 raedeon:
 - rs400 hyperz fix
 
 i2c:
 - td998x: drop platform_data, split driver into media and bridge
 
 ast:
 - transmitter chip detection refactoring
 - vbios display mode refactoring
 - astdp: fix connection status and filter unsupported modes
 - cursor handling refactoring
 
 imagination:
 - check job dependencies with sched helper
 
 ivpu:
 - improve command queue handling
 - use workqueue for IRQ handling
 - add support HW fault injection
 - locking fixes
 
 mgag200:
 - add support for G200eH5
 
 msm:
 - dpu: add concurrent writeback support for DPU 10.x+
 - use LTTPR helpers
 - GPU:
   - Fix obscure GMU suspend failure
   - Expose syncobj timeline support
   - Extend GPU devcoredump with pagetable info
   - a623 support
   - Fix a6xx gen1/gen2 indexed-register blocks in gpu snapshot / devcoredump
 - Display:
   - Add cpu-cfg interconnect paths on SM8560 and SM8650
   - Introduce KMS OMMU fault handler, causing devcoredump snapshot
   - Fixed error pointer dereference in msm_kms_init_aspace()
 - DPU:
   - Fix mode_changing handling
   - Add writeback support on SM6150 (QCS615)
   - Fix DSC programming in 1:1:1 topology
   - Reworked hardware resource allocation, moving it to the CRTC code
   - Enabled support for Concurrent WriteBack (CWB) on SM8650
   - Enabled CDM blocks on all relevant platforms
   - Reworked debugfs interface for BW/clocks debugging
   - Clear perf params before calculating bw
   - Support YUV formats on writeback
   - Fixed double inclusion
   - Fixed writeback in YUV formats when using cloned output, Dropped
     wb2_formats_rgb
   - Corrected dpu_crtc_check_mode_changed and struct dpu_encoder_virt
     kerneldocs
   - Fixed uninitialized variable in dpu_crtc_kickoff_clone_mode()
 - DSI:
   - DSC-related fixes
   - Rework clock programming
 - DSI PHY:
   - Fix 7nm (and lower) PHY programming
   - Add proper DT schema definitions for DSI PHY clocks
 - HDMI:
   - Rework the driver, enabling the use of the HDMI Connector framework
 - Bindings:
   - Added eDP PHY on SA8775P
 
 nouveau:
 - move drm_slave_encoder interface into driver
 - nvkm: refactor GSP RPC
 - use LTTPR helpers
 
 mediatek:
 - HDMI fixup and refinement
 - add MT8188 dsc compatible
 - MT8365 SoC support
 
 panthor:
 - Expose sizes of intenral BOs via fdinfo
 - Fix race between reset and suspend
 - Improve locking
 
 qaic:
 - Add support for AIC200
 
 renesas:
 - Fix limits in DT bindings
 
 rockchip:
 - support rk3562-mali
 - rk3576: Add HDMI support
 - vop2: Add new display modes on RK3588 HDMI0 up to 4K
 - Don't change HDMI reference clock rate
 - Fix DT bindings
 - analogix_dp: add eDP support
 - fix shutodnw
 
 solomon:
 - Set SPI device table to silence warnings
 - Fix pixel and scanline encoding
 
 v3d:
 - handle clock
 
 vc4:
 - Use drm_exec
 - Use dma-resv for wait-BO ioctl
 - Remove seqno infrastructure
 
 virtgpu:
 - Support partial mappings of GEM objects
 - Reserve VGA resources during initialization
 - Fix UAF in virtgpu_dma_buf_free_obj()
 - Add panic support
 
 vkms:
 - Switch to a managed modesetting pipeline
 - Add support for ARGB8888
 - fix UAf
 
 xlnx:
 - Set correct DMA segment size
 - use mutex guards
 - Fix error handling
 - Fix docs
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmfmA2cACgkQDHTzWXnE
 hr7lKQ/+I7gmln3ka8FyKnpwG5KusDpxz3OzgUKHpzOTkXL1+vPMt0vjCKRJKE3D
 zfpUTWNbwlVN0krqmUGyFIeCt8wmevBF6HvQ+GsWbiWltUj3xIlnkV0TVH84XTUo
 0evrXNG9K8sLjMKjrf7yrGL53Ayoaq9IO9wtOws+FCgtykAsMR/IWLrYLpj21ZQ6
 Oclhq5Cz21WRoQpzySR23s3DLi4LHri26RGKbGNh2PzxYwyP/euGW6O+ncEduNmg
 vQLgUfptaM/EubJFG6jxDWZJ2ChIAUUxQuhZwt7DKqRsYIcJKcfDSUzqL95t6SYU
 zewlYmeslvusoesCeTJzHaUj34yqJGsvjFPsHFUEvAy8BVncsqS40D6mhrRMo5nD
 chnSJu+IpDOEEqcdbb1J73zKLw6X3GROM8qUQEThJBD2yTsOdw9d0HXekMDMBXSi
 NayKvXfsBV19rI74FYnRCzHt8BVVANh5qJNnR5RcnPZ2KzHQbV0JFOA9YhxE8vFU
 GWkFWKlpAQUQ+yoTy1kuO9dcUxLIC7QseMeS5BYhcJBMEV78xQuRYRxgsL8YS4Yg
 rIhcb3mZwMFj7jBAqfpLKWiI+GTup+P9vcz7Bvm5iIf8gEhZLUTwqBeAYXQkcWd4
 i1AqDuFR0//sAgODoeU2sg1Q3yTL/i/DhcwvCIPgcDZ/x4Eg868=
 =oK/N
 -----END PGP SIGNATURE-----

Merge tag 'drm-next-2025-03-28' of https://gitlab.freedesktop.org/drm/kernel

Pull drm updates from Dave Airlie:
 "Outside of drm there are some rust patches from Danilo who maintains
  that area in here, and some pieces for drm header check tests.

  The major things in here are a new driver supporting the touchbar
  displays on M1/M2, the nova-core stub driver which is just the vehicle
  for adding rust abstractions and start developing a real driver inside
  of.

  xe adds support for SVM with a non-driver specific SVM core
  abstraction that will hopefully be useful for other drivers, along
  with support for shrinking for TTM devices. I'm sure xe and AMD
  support new devices, but the pipeline depth on these things is hard to
  know what they end up being in the marketplace!

  uapi:
   - add mediatek tiled fourcc
   - add support for notifying userspace on device wedged

  new driver:
   - appletbdrm: support for Apple Touchbar displays on m1/m2
   - nova-core: skeleton rust driver to develop nova inside off

  firmware:
   - add some rust firmware pieces

  rust:
   - add 'LocalModule' type alias

  component:
   - add helper to query bound status

  fbdev:
   - fbtft: remove access to page->index

  media:
   - cec: tda998x: import driver from drm

  dma-buf:
   - add fast path for single fence merging

  tests:
   - fix lockdep warnings

  atomic:
   - allow full modeset on connector changes
   - clarify semantics of allow_modeset and drm_atomic_helper_check
   - async-flip: support on arbitary planes
   - writeback: fix UAF
   - Document atomic-state history

  format-helper:
   - support ARGB8888 to ARGB4444 conversions

  buddy:
   - fix multi-root cleanup

  ci:
   - update IGT

  dp:
   - support extended wake timeout
   - mst: fix RAD to string conversion
   - increase DPCD eDP control CAP size to 5 bytes
   - add DPCD eDP v1.5 definition
   - add helpers for LTTPR transparent mode

  panic:
   - encode QR code according to Fido 2.2

  scheduler:
   - add parameter struct for init
   - improve job peek/pop operations
   - optimise drm_sched_job struct layout

  ttm:
   - refactor pool allocation
   - add helpers for TTM shrinker

  panel-orientation:
   - add a bunch of new quirks

  panel:
   - convert panels to multi-style functions
   - edp: Add support for B140UAN04.4, BOE NV140FHM-NZ, CSW MNB601LS1-3,
     LG LP079QX1-SP0V, MNE007QS3-7, STA 116QHD024002, Starry
     116KHD024006, Lenovo T14s Gen6 Snapdragon
   - himax-hx83102: Add support for CSOT PNA957QT1-1, Kingdisplay
     kd110n11-51ie, Starry 2082109qfh040022-50e
   - visionox-r66451: use multi-style MIPI-DSI functions
   - raydium-rm67200: Add driver for Raydium RM67200
   - simple: Add support for BOE AV123Z7M-N17, BOE AV123Z7M-N17
   - sony-td4353-jdi: Use MIPI-DSI multi-func interface
   - summit: Add driver for Apple Summit display panel
   - visionox-rm692e5: Add driver for Visionox RM692E5

  bridge:
   - pass full atomic state to various callbacks
   - adv7511: Report correct capabilities
   - it6505: Fix HDCP V compare
   - snd65dsi86: fix device IDs
   - nwl-dsi: set bridge type
   - ti-sn65si83: add error recovery and set bridge type
   - synopsys: add HDMI audio support

  xe:
   - support device-wedged event
   - add mmap support for PCI memory barrier
   - perf pmu integration and expose per-engien activity
   - add EU stall sampling support
   - GPU SVM and Xe SVM implementation
   - use TTM shrinker
   - add survivability mode to allow the driver to do firmware updates
     in critical failure states
   - PXP HWDRM support for MTL and LNL
   - expose package/vram temps over hwmon
   - enable DP tunneling
   - drop mmio_ext abstraction
   - Reject BO evcition if BO is bound to current VM
   - Xe suballocator improvements
   - re-use display vmas when possible
   - add GuC Buffer Cache abstraction
   - PCI ID update for Panther Lake and Battlemage
   - Enable SRIOV for Panther Lake
   - Refactor VRAM manager location

  i915:
   - enable extends wake timeout
   - support device-wedged event
   - Enable DP 128b/132b SST DSC
   - FBC dirty rectangle support for display version 30+
   - convert i915/xe to drm client setup
   - Compute HDMI PLLS for rates not in fixed tables
   - Allow DSB usage when PSR is enabled on LNL+
   - Enable panel replay without full modeset
   - Enable async flips with compressed buffers on ICL+
   - support luminance based brightness via DPCD for eDP
   - enable VRR enable/disable without full modeset
   - allow GuC SLPC default strategies on MTL+ for performance
   - lots of display refactoring in move to struct intel_display

  amdgpu:
   - add device wedged event
   - support async page flips on overlay planes
   - enable broadcast RGB drm property
   - add info ioctl for virt mode
   - OEM i2c support for RGB lights
   - GC 11.5.2 + 11.5.3 support
   - SDMA 6.1.3 support
   - NBIO 7.9.1 + 7.11.2 support
   - MMHUB 1.8.1 + 3.3.2 support
   - DCN 3.6.0 support
   - Add dynamic workload profile switching for GC 10-12
   - support larger VBIOS sizes
   - Mark gttsize parameters as deprecated
   - Initial JPEG queue resset support

  amdkfd:
   - add KFD per process flags for setting precision
   - sync pasid values between KGD and KFD
   - improve GTT/VRAM handling for APUs
   - fix user queue validation on GC7/8
   - SDMA queue reset support

  raedeon:
   - rs400 hyperz fix

  i2c:
   - td998x: drop platform_data, split driver into media and bridge

  ast:
   - transmitter chip detection refactoring
   - vbios display mode refactoring
   - astdp: fix connection status and filter unsupported modes
   - cursor handling refactoring

  imagination:
   - check job dependencies with sched helper

  ivpu:
   - improve command queue handling
   - use workqueue for IRQ handling
   - add support HW fault injection
   - locking fixes

  mgag200:
   - add support for G200eH5

  msm:
   - dpu: add concurrent writeback support for DPU 10.x+
   - use LTTPR helpers
   - GPU:
     - Fix obscure GMU suspend failure
     - Expose syncobj timeline support
     - Extend GPU devcoredump with pagetable info
     - a623 support
     - Fix a6xx gen1/gen2 indexed-register blocks in gpu snapshot /
       devcoredump
   - Display:
     - Add cpu-cfg interconnect paths on SM8560 and SM8650
     - Introduce KMS OMMU fault handler, causing devcoredump snapshot
     - Fixed error pointer dereference in msm_kms_init_aspace()
   - DPU:
     - Fix mode_changing handling
     - Add writeback support on SM6150 (QCS615)
     - Fix DSC programming in 1:1:1 topology
     - Reworked hardware resource allocation, moving it to the CRTC code
     - Enabled support for Concurrent WriteBack (CWB) on SM8650
     - Enabled CDM blocks on all relevant platforms
     - Reworked debugfs interface for BW/clocks debugging
     - Clear perf params before calculating bw
     - Support YUV formats on writeback
     - Fixed double inclusion
     - Fixed writeback in YUV formats when using cloned output, Dropped
       wb2_formats_rgb
     - Corrected dpu_crtc_check_mode_changed and struct dpu_encoder_virt
       kerneldocs
     - Fixed uninitialized variable in dpu_crtc_kickoff_clone_mode()
   - DSI:
     - DSC-related fixes
     - Rework clock programming
   - DSI PHY:
     - Fix 7nm (and lower) PHY programming
     - Add proper DT schema definitions for DSI PHY clocks
   - HDMI:
     - Rework the driver, enabling the use of the HDMI Connector
       framework
   - Bindings:
     - Added eDP PHY on SA8775P

  nouveau:
   - move drm_slave_encoder interface into driver
   - nvkm: refactor GSP RPC
   - use LTTPR helpers

  mediatek:
   - HDMI fixup and refinement
   - add MT8188 dsc compatible
   - MT8365 SoC support

  panthor:
   - Expose sizes of intenral BOs via fdinfo
   - Fix race between reset and suspend
   - Improve locking

  qaic:
   - Add support for AIC200

  renesas:
   - Fix limits in DT bindings

  rockchip:
   - support rk3562-mali
   - rk3576: Add HDMI support
   - vop2: Add new display modes on RK3588 HDMI0 up to 4K
   - Don't change HDMI reference clock rate
   - Fix DT bindings
   - analogix_dp: add eDP support
   - fix shutodnw

  solomon:
   - Set SPI device table to silence warnings
   - Fix pixel and scanline encoding

  v3d:
   - handle clock

  vc4:
   - Use drm_exec
   - Use dma-resv for wait-BO ioctl
   - Remove seqno infrastructure

  virtgpu:
   - Support partial mappings of GEM objects
   - Reserve VGA resources during initialization
   - Fix UAF in virtgpu_dma_buf_free_obj()
   - Add panic support

  vkms:
   - Switch to a managed modesetting pipeline
   - Add support for ARGB8888
   - fix UAf

  xlnx:
   - Set correct DMA segment size
   - use mutex guards
   - Fix error handling
   - Fix docs"

* tag 'drm-next-2025-03-28' of https://gitlab.freedesktop.org/drm/kernel: (1762 commits)
  drm/amd/pm: Update feature list for smu_v13_0_6
  drm/amdgpu: Add parameter documentation for amdgpu_sync_fence
  drm/amdgpu/discovery: optionally use fw based ip discovery
  drm/amdgpu/discovery: use specific ip_discovery.bin for legacy asics
  drm/amdgpu/discovery: check ip_discovery fw file available
  drm/amd/pm: Remove unnecessay UQ10 to UINT conversion
  drm/amd/pm: Remove unnecessay UQ10 to UINT conversion
  drm/amdgpu/sdma_v4_4_2: update VM flush implementation for SDMA
  drm/amdgpu: Optimize VM invalidation engine allocation and synchronize GPU TLB flush
  drm/amd/amdgpu: Increase max rings to enable SDMA page ring
  drm/amdgpu: Decode deferred error type in gfx aca bank parser
  drm/amdgpu/gfx11: Add Cleaner Shader Support for GFX11.5 GPUs
  drm/amdgpu/mes: clean up SDMA HQD loop
  drm/amdgpu/mes: enable compute pipes across all MEC
  drm/amdgpu/mes: drop MES 10.x leftovers
  drm/amdgpu/mes: optimize compute loop handling
  drm/amdgpu/sdma: guilty tracking is per instance
  drm/amdgpu/sdma: fix engine reset handling
  drm/amdgpu: remove invalid usage of sched.ready
  drm/amdgpu: add cleaner shader trace point
  ...
This commit is contained in:
Linus Torvalds 2025-03-28 17:44:52 -07:00
commit 0c86b42439
1618 changed files with 142564 additions and 40851 deletions

View File

@ -199,10 +199,11 @@ Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@imgtec.com>
Dengcheng Zhu <dzhu@wavecomp.com> <dengcheng.zhu@mips.com>
<dev.kurt@vandijck-laurijssen.be> <kurt.van.dijck@eia.be>
Dikshita Agarwal <quic_dikshita@quicinc.com> <dikshita@codeaurora.org>
Dmitry Baryshkov <dbaryshkov@gmail.com>
Dmitry Baryshkov <dbaryshkov@gmail.com> <[dbaryshkov@gmail.com]>
Dmitry Baryshkov <dbaryshkov@gmail.com> <dmitry_baryshkov@mentor.com>
Dmitry Baryshkov <dbaryshkov@gmail.com> <dmitry_eremin@mentor.com>
Dmitry Baryshkov <lumag@kernel.org> <dbaryshkov@gmail.com>
Dmitry Baryshkov <lumag@kernel.org> <[dbaryshkov@gmail.com]>
Dmitry Baryshkov <lumag@kernel.org> <dmitry_baryshkov@mentor.com>
Dmitry Baryshkov <lumag@kernel.org> <dmitry_eremin@mentor.com>
Dmitry Baryshkov <lumag@kernel.org> <dmitry.baryshkov@linaro.org>
Dmitry Safonov <0x7f454c46@gmail.com> <dima@arista.com>
Dmitry Safonov <0x7f454c46@gmail.com> <d.safonov@partner.samsung.com>
Dmitry Safonov <0x7f454c46@gmail.com> <dsafonov@virtuozzo.com>
@ -323,7 +324,8 @@ Jeff Johnson <jeff.johnson@oss.qualcomm.com> <quic_jjohnson@quicinc.com>
Jeff Layton <jlayton@kernel.org> <jlayton@poochiereds.net>
Jeff Layton <jlayton@kernel.org> <jlayton@primarydata.com>
Jeff Layton <jlayton@kernel.org> <jlayton@redhat.com>
Jeffrey Hugo <quic_jhugo@quicinc.com> <jhugo@codeaurora.org>
Jeff Hugo <jeff.hugo@oss.qualcomm.com> <jhugo@codeaurora.org>
Jeff Hugo <jeff.hugo@oss.qualcomm.com> <quic_jhugo@quicinc.com>
Jens Axboe <axboe@kernel.dk> <axboe@suse.de>
Jens Axboe <axboe@kernel.dk> <jens.axboe@oracle.com>
Jens Axboe <axboe@kernel.dk> <axboe@fb.com>

View File

@ -3961,6 +3961,10 @@ S: 1 Amherst Street
S: Cambridge, Massachusetts 02139
S: USA
N: Luben Tuikov
E: Luben Tuikov <ltuikov89@gmail.com>
D: Maintainer of the DRM GPU Scheduler
N: Simmule Turner
E: sturner@tele-tv.com
D: Added swapping to filesystem

View File

@ -108,3 +108,19 @@ Contact: intel-xe@lists.freedesktop.org
Description: RO. Package current voltage in millivolt.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/temp2_input
Date: March 2025
KernelVersion: 6.14
Contact: intel-xe@lists.freedesktop.org
Description: RO. Package temperature in millidegree Celsius.
Only supported for particular Intel Xe graphics platforms.
What: /sys/bus/pci/drivers/xe/.../hwmon/hwmon<i>/temp3_input
Date: March 2025
KernelVersion: 6.14
Contact: intel-xe@lists.freedesktop.org
Description: RO. VRAM temperature in millidegree Celsius.
Only supported for particular Intel Xe graphics platforms.

View File

@ -0,0 +1,83 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/apple,h7-display-pipe-mipi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Apple pre-DCP display controller MIPI interface
maintainers:
- Sasha Finkelstein <fnkl.kernel@gmail.com>
description:
The MIPI controller part of the pre-DCP Apple display controller
allOf:
- $ref: dsi-controller.yaml#
properties:
compatible:
items:
- enum:
- apple,t8112-display-pipe-mipi
- apple,t8103-display-pipe-mipi
- const: apple,h7-display-pipe-mipi
reg:
maxItems: 1
power-domains:
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports
properties:
port@0:
$ref: /schemas/graph.yaml#/properties/port
description: Input port. Always connected to the primary controller
port@1:
$ref: /schemas/graph.yaml#/properties/port
description: Output MIPI DSI port to the panel
required:
- port@0
- port@1
required:
- compatible
- reg
- ports
unevaluatedProperties: false
examples:
- |
dsi@28200000 {
compatible = "apple,t8103-display-pipe-mipi", "apple,h7-display-pipe-mipi";
reg = <0x28200000 0xc000>;
power-domains = <&ps_dispdfr_mipi>;
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
dfr_adp_out_mipi: endpoint {
remote-endpoint = <&dfr_adp_out_mipi>;
};
};
port@1 {
reg = <1>;
dfr_panel_in: endpoint {
remote-endpoint = <&dfr_mipi_out_panel>;
};
};
};
};
...

View File

@ -0,0 +1,88 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/apple,h7-display-pipe.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Apple pre-DCP display controller
maintainers:
- Sasha Finkelstein <fnkl.kernel@gmail.com>
description:
A secondary display controller used to drive the "touchbar" on
certain Apple laptops.
properties:
compatible:
items:
- enum:
- apple,t8112-display-pipe
- apple,t8103-display-pipe
- const: apple,h7-display-pipe
reg:
items:
- description: Primary register block, controls planes and blending
- description:
Contains other configuration registers like interrupt
and FIFO control
reg-names:
items:
- const: be
- const: fe
power-domains:
description:
Phandles to pmgr entries that are needed for this controller to turn on.
Aside from that, their specific functions are unknown
maxItems: 2
interrupts:
items:
- description: Unknown function
- description: Primary interrupt. Vsync events are reported via it
interrupt-names:
items:
- const: be
- const: fe
iommus:
maxItems: 1
port:
$ref: /schemas/graph.yaml#/properties/port
description: Output port. Always connected to apple,h7-display-pipe-mipi
required:
- compatible
- reg
- interrupts
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/apple-aic.h>
display-pipe@28200000 {
compatible = "apple,t8103-display-pipe", "apple,h7-display-pipe";
reg = <0x28200000 0xc000>,
<0x28400000 0x4000>;
reg-names = "be", "fe";
power-domains = <&ps_dispdfr_fe>, <&ps_dispdfr_be>;
interrupt-parent = <&aic>;
interrupts = <AIC_IRQ 502 IRQ_TYPE_LEVEL_HIGH>,
<AIC_IRQ 506 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "be", "fe";
iommus = <&displaydfr_dart 0>;
port {
dfr_adp_out_mipi: endpoint {
remote-endpoint = <&dfr_mipi_in_adp>;
};
};
};
...

View File

@ -35,6 +35,9 @@ properties:
vcc-supply:
description: A 1.8V power supply (see regulator/regulator.yaml).
interrupts:
maxItems: 1
ports:
$ref: /schemas/graph.yaml#/properties/ports

View File

@ -27,6 +27,7 @@ properties:
- mediatek,mt8188-dp-intf
- mediatek,mt8192-dpi
- mediatek,mt8195-dp-intf
- mediatek,mt8195-dpi
- items:
- enum:
- mediatek,mt6795-dpi
@ -35,6 +36,10 @@ properties:
- enum:
- mediatek,mt8365-dpi
- const: mediatek,mt8192-dpi
- items:
- enum:
- mediatek,mt8188-dpi
- const: mediatek,mt8195-dpi
reg:
maxItems: 1
@ -116,11 +121,13 @@ examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/power/mt8173-power.h>
dpi: dpi@1401d000 {
compatible = "mediatek,mt8173-dpi";
reg = <0x1401d000 0x1000>;
interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
power-domains = <&spm MT8173_POWER_DOMAIN_MM>;
clocks = <&mmsys CLK_MM_DPI_PIXEL>,
<&mmsys CLK_MM_DPI_ENGINE>,
<&apmixedsys CLK_APMIXED_TVDPLL>;

View File

@ -22,6 +22,9 @@ properties:
oneOf:
- enum:
- mediatek,mt8195-disp-dsc
- items:
- const: mediatek,mt8188-disp-dsc
- const: mediatek,mt8195-disp-dsc
reg:
maxItems: 1

View File

@ -231,6 +231,7 @@ allOf:
then:
properties:
clocks:
minItems: 7
maxItems: 7
clock-names:
items:
@ -248,29 +249,12 @@ allOf:
contains:
enum:
- qcom,msm8916-dsi-ctrl
then:
properties:
clocks:
maxItems: 6
clock-names:
items:
- const: mdp_core
- const: iface
- const: bus
- const: byte
- const: pixel
- const: core
- if:
properties:
compatible:
contains:
enum:
- qcom,msm8953-dsi-ctrl
- qcom,msm8976-dsi-ctrl
then:
properties:
clocks:
minItems: 6
maxItems: 6
clock-names:
items:
@ -291,6 +275,7 @@ allOf:
then:
properties:
clocks:
minItems: 7
maxItems: 7
clock-names:
items:
@ -311,6 +296,7 @@ allOf:
then:
properties:
clocks:
minItems: 7
maxItems: 7
clock-names:
items:
@ -328,28 +314,13 @@ allOf:
contains:
enum:
- qcom,msm8998-dsi-ctrl
- qcom,sm6125-dsi-ctrl
- qcom,sm6350-dsi-ctrl
then:
properties:
clocks:
maxItems: 6
clock-names:
items:
- const: byte
- const: byte_intf
- const: pixel
- const: core
- const: iface
- const: bus
- if:
properties:
compatible:
contains:
enum:
- qcom,sc7180-dsi-ctrl
- qcom,sc7280-dsi-ctrl
- qcom,sdm845-dsi-ctrl
- qcom,sm6115-dsi-ctrl
- qcom,sm6125-dsi-ctrl
- qcom,sm6350-dsi-ctrl
- qcom,sm6375-dsi-ctrl
- qcom,sm6150-dsi-ctrl
- qcom,sm7150-dsi-ctrl
- qcom,sm8150-dsi-ctrl
@ -361,6 +332,7 @@ allOf:
then:
properties:
clocks:
minItems: 6
maxItems: 6
clock-names:
items:
@ -380,6 +352,7 @@ allOf:
then:
properties:
clocks:
minItems: 9
maxItems: 9
clock-names:
items:
@ -393,27 +366,6 @@ allOf:
- const: pixel
- const: core
- if:
properties:
compatible:
contains:
enum:
- qcom,sdm845-dsi-ctrl
- qcom,sm6115-dsi-ctrl
- qcom,sm6375-dsi-ctrl
then:
properties:
clocks:
maxItems: 6
clock-names:
items:
- const: byte
- const: byte_intf
- const: pixel
- const: core
- const: iface
- const: bus
unevaluatedProperties: false
examples:

View File

@ -15,6 +15,8 @@ description:
properties:
"#clock-cells":
const: 1
description:
See include/dt-bindings/clock/qcom,dsi-phy-28nm.h for clock IDs.
"#phy-cells":
const: 0

View File

@ -123,6 +123,7 @@ allOf:
compatible:
contains:
enum:
- qcom,adreno-gmu-623.0
- qcom,adreno-gmu-635.0
- qcom,adreno-gmu-660.1
- qcom,adreno-gmu-663.0

View File

@ -52,6 +52,13 @@ patternProperties:
items:
- const: qcom,sa8775p-dp
"^phy@[0-9a-f]+$":
type: object
additionalProperties: true
properties:
compatible:
const: qcom,sa8775p-edp-phy
required:
- compatible
@ -61,6 +68,7 @@ examples:
- |
#include <dt-bindings/interconnect/qcom,icc.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/clock/qcom,sa8775p-dispcc.h>
#include <dt-bindings/clock/qcom,sa8775p-gcc.h>
#include <dt-bindings/interconnect/qcom,sa8775p-rpmh.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
@ -158,6 +166,26 @@ examples:
};
};
mdss0_dp0_phy: phy@aec2a00 {
compatible = "qcom,sa8775p-edp-phy";
reg = <0x0aec2a00 0x200>,
<0x0aec2200 0xd0>,
<0x0aec2600 0xd0>,
<0x0aec2000 0x1c8>;
clocks = <&dispcc0 MDSS_DISP_CC_MDSS_DPTX0_AUX_CLK>,
<&dispcc0 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,sa8775p-dp";
@ -186,9 +214,9 @@ examples:
assigned-clocks = <&dispcc_mdss_dptx0_link_clk_src>,
<&dispcc_mdss_dptx0_pixel0_clk_src>;
assigned-clock-parents = <&mdss0_edp_phy 0>, <&mdss0_edp_phy 1>;
assigned-clock-parents = <&mdss0_dp0_phy 0>, <&mdss0_dp0_phy 1>;
phys = <&mdss0_edp_phy>;
phys = <&mdss0_dp0_phy>;
phy-names = "dp";
operating-points-v2 = <&dp_opp_table>;

View File

@ -30,10 +30,14 @@ properties:
maxItems: 1
interconnects:
maxItems: 2
items:
- description: Interconnect path from mdp0 port to the data bus
- description: Interconnect path from CPU to the reg bus
interconnect-names:
maxItems: 2
items:
- const: mdp0-mem
- const: cpu-cfg
patternProperties:
"^display-controller@[0-9a-f]+$":
@ -91,9 +95,9 @@ examples:
reg = <0x0ae00000 0x1000>;
reg-names = "mdss";
interconnects = <&mmss_noc MASTER_MDP 0 &gem_noc SLAVE_LLCC 0>,
<&mc_virt MASTER_LLCC 0 &mc_virt SLAVE_EBI1 0>;
interconnect-names = "mdp0-mem", "mdp1-mem";
interconnects = <&mmss_noc MASTER_MDP 0 &mc_virt SLAVE_EBI1 0>,
<&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_DISPLAY_CFG 0>;
interconnect-names = "mdp0-mem", "cpu-cfg";
resets = <&dispcc DISP_CC_MDSS_CORE_BCR>;

View File

@ -29,10 +29,14 @@ properties:
maxItems: 1
interconnects:
maxItems: 2
items:
- description: Interconnect path from mdp0 port to the data bus
- description: Interconnect path from CPU to the reg bus
interconnect-names:
maxItems: 2
items:
- const: mdp0-mem
- const: cpu-cfg
patternProperties:
"^display-controller@[0-9a-f]+$":
@ -75,12 +79,17 @@ examples:
#include <dt-bindings/clock/qcom,rpmh.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/qcom,rpmhpd.h>
#include <dt-bindings/interconnect/qcom,sm8650-rpmh.h>
display-subsystem@ae00000 {
compatible = "qcom,sm8650-mdss";
reg = <0x0ae00000 0x1000>;
reg-names = "mdss";
interconnects = <&mmss_noc MASTER_MDP 0 &mc_virt SLAVE_EBI1 0>,
<&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_DISPLAY_CFG 0>;
interconnect-names = "mdp0-mem", "cpu-cfg";
resets = <&dispcc_core_bcr>;
power-domains = <&dispcc_gdsc>;

View File

@ -0,0 +1,58 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/apple,summit.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Apple "Summit" display panel
maintainers:
- Sasha Finkelstein <fnkl.kernel@gmail.com>
description:
An OLED panel used as a touchbar on certain Apple laptops.
Contains a backlight device, which controls brightness of the panel itself.
The backlight common properties are included for this reason
allOf:
- $ref: panel-common.yaml#
- $ref: /schemas/leds/backlight/common.yaml#
properties:
compatible:
items:
- enum:
- apple,j293-summit
- apple,j493-summit
- const: apple,summit
reg:
maxItems: 1
required:
- compatible
- reg
- max-brightness
- port
unevaluatedProperties: false
examples:
- |
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "apple,j293-summit", "apple,summit";
reg = <0>;
max-brightness = <255>;
port {
endpoint {
remote-endpoint = <&dfr_bridge_out>;
};
};
};
};
...

View File

@ -18,8 +18,14 @@ properties:
- enum:
# Boe nv110wum-l60 11.0" WUXGA TFT LCD panel
- boe,nv110wum-l60
# CSOT pna957qt1-1 10.95" WUXGA TFT LCD panel
- csot,pna957qt1-1
# IVO t109nw41 11.0" WUXGA TFT LCD panel
- ivo,t109nw41
# KINGDISPLAY KD110N11-51IE 10.95" WUXGA TFT LCD panel
- kingdisplay,kd110n11-51ie
# STARRY 2082109QFH040022-50E 10.95" WUXGA TFT LCD panel
- starry,2082109qfh040022-50e
# STARRY himax83102-j02 10.51" WUXGA TFT LCD panel
- starry,himax83102-j02
- const: himax,hx83102

View File

@ -40,6 +40,8 @@ properties:
- auo,g185han01
# AU Optronics Corporation 19.0" (1280x1024) TFT LCD panel
- auo,g190ean01
# BOE AV123Z7M-N17 12.3" (1920x720) LVDS TFT LCD panel
- boe,av123z7m-n17
# Kaohsiung Opto-Electronics Inc. 10.1" WUXGA (1920 x 1200) LVDS TFT LCD panel
- koe,tx26d202vm0bwa
# Lincoln Technology Solutions, LCD185-101CT 10.1" TFT 1920x1200

View File

@ -63,6 +63,8 @@ properties:
- auo,t215hvn01
# Shanghai AVIC Optoelectronics 7" 1024x600 color TFT-LCD panel
- avic,tm070ddh03
# BOE AV101HDT-a10 10.1" 1280x720 LVDS panel
- boe,av101hdt-a10
# BOE BP082WX1-100 8.2" WXGA (1280x800) LVDS panel
- boe,bp082wx1-100
# BOE BP101WX1-100 10.1" WXGA (1280x800) LVDS panel

View File

@ -0,0 +1,72 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/raydium,rm67200.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Raydium RM67200 based MIPI-DSI panels
maintainers:
- Sebastian Reichel <sebastian.reichel@collabora.com>
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
items:
- enum:
- wanchanglong,w552793baa
- const: raydium,rm67200
reg:
maxItems: 1
vdd-supply:
description: 2.8V Logic voltage
iovcc-supply:
description: 1.8V IO voltage
vsp-supply:
description: positive 5.5V voltage
vsn-supply:
description: negative 5.5V voltage
backlight: true
port: true
reset-gpios: true
required:
- compatible
- port
- reg
- reset-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "wanchanglong,w552793baa", "raydium,rm67200";
reg = <0>;
vdd-supply = <&regulator1>;
iovcc-supply = <&regulator2>;
vsp-supply = <&regulator3>;
vsn-supply = <&regulator4>;
reset-gpios = <&gpiobank 42 GPIO_ACTIVE_LOW>;
port {
panel0_in: endpoint {
remote-endpoint = <&dsi0_out>;
};
};
};
};
...

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/panel/visionox,rm692e5.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Visionox RM692E5 6.55" 2400x1080 120Hz MIPI-DSI Panel
maintainers:
- Danila Tikhonov <danila@jiaxyga.com>
description:
The Visionox RM692E5 is a generic DSI Panel IC used to control
AMOLED panels.
allOf:
- $ref: panel-common.yaml#
properties:
compatible:
oneOf:
- enum:
- visionox,rm692e5
- items:
- enum:
- nothing,rm692e5-spacewar
- const: visionox,rm692e5
reg:
maxItems: 1
vdd-supply:
description: 3.3V source voltage rail
vddio-supply:
description: 1.8V I/O source voltage rail
reset-gpios: true
port: true
required:
- compatible
- reg
- reset-gpios
- vdd-supply
- vddio-supply
- port
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
dsi {
#address-cells = <1>;
#size-cells = <0>;
panel@0 {
compatible = "nothing,rm692e5-spacewar",
"visionox,rm692e5";
reg = <0>;
reset-gpios = <&tlmm 44 GPIO_ACTIVE_LOW>;
vdd-supply = <&vdd_oled>;
vddio-supply = <&vdd_io_oled>;
port {
panel_in: endpoint {
remote-endpoint = <&mdss_dsi0_out>;
};
};
};
};
...

View File

@ -47,12 +47,26 @@ properties:
maxItems: 1
# See compatible-specific constraints below.
clocks: true
clock-names: true
clocks:
minItems: 1
maxItems: 8
clock-names:
minItems: 1
maxItems: 8
interrupts:
minItems: 1
maxItems: 4
description: Interrupt specifiers, one per DU channel
resets: true
reset-names: true
resets:
minItems: 1
maxItems: 2
reset-names:
minItems: 1
maxItems: 2
power-domains:
maxItems: 1
@ -74,7 +88,7 @@ properties:
renesas,cmms:
$ref: /schemas/types.yaml#/definitions/phandle-array
minItems: 1
minItems: 2
maxItems: 4
items:
maxItems: 1
@ -174,6 +188,7 @@ allOf:
- pattern: '^dclkin\.[01]$'
interrupts:
minItems: 2
maxItems: 2
resets:
@ -229,6 +244,7 @@ allOf:
- pattern: '^dclkin\.[01]$'
interrupts:
minItems: 2
maxItems: 2
resets:
@ -282,6 +298,7 @@ allOf:
- pattern: '^dclkin\.[01]$'
interrupts:
minItems: 2
maxItems: 2
resets:
@ -336,6 +353,7 @@ allOf:
- pattern: '^dclkin\.[01]$'
interrupts:
minItems: 2
maxItems: 2
resets:
@ -397,6 +415,7 @@ allOf:
- pattern: '^dclkin\.[012]$'
interrupts:
minItems: 3
maxItems: 3
resets:
@ -461,9 +480,11 @@ allOf:
- pattern: '^dclkin\.[0123]$'
interrupts:
minItems: 4
maxItems: 4
resets:
minItems: 2
maxItems: 2
reset-names:
@ -534,9 +555,11 @@ allOf:
- pattern: '^dclkin\.[012]$'
interrupts:
minItems: 3
maxItems: 3
resets:
minItems: 2
maxItems: 2
reset-names:
@ -605,9 +628,11 @@ allOf:
- pattern: '^dclkin\.[013]$'
interrupts:
minItems: 3
maxItems: 3
resets:
minItems: 2
maxItems: 2
reset-names:
@ -726,6 +751,7 @@ allOf:
- pattern: '^dclkin\.[01]$'
interrupts:
minItems: 2
maxItems: 2
resets:

View File

@ -29,6 +29,7 @@ allOf:
properties:
compatible:
enum:
- rockchip,rk3576-dw-hdmi-qp
- rockchip,rk3588-dw-hdmi-qp
reg:
@ -156,7 +157,7 @@ examples:
<GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH 0>,
<GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH 0>;
interrupt-names = "avp", "cec", "earc", "main", "hpd";
phys = <&hdptxphy_hdmi0>;
phys = <&hdptxphy0>;
power-domains = <&power RK3588_PD_VO1>;
resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>;
reset-names = "ref", "hdp";

View File

@ -14,12 +14,14 @@ description:
maintainers:
- Sandy Huang <hjc@rock-chips.com>
- Heiko Stuebner <heiko@sntech.de>
- Andy Yan <andyshrk@163.com>
properties:
compatible:
enum:
- rockchip,rk3566-vop
- rockchip,rk3568-vop
- rockchip,rk3576-vop
- rockchip,rk3588-vop
reg:
@ -37,10 +39,21 @@ properties:
- const: gamma-lut
interrupts:
maxItems: 1
minItems: 1
maxItems: 4
description:
The VOP interrupt is shared by several interrupt sources, such as
frame start (VSYNC), line flag and other status interrupts.
For VOP version under rk3576, the interrupt is shared by several interrupt
sources, such as frame start (VSYNC), line flag and other interrupt status.
For VOP version from rk3576 there is a system interrupt for bus error, and
every video port has it's independent interrupts for vsync and other video
port related error interrupts.
interrupt-names:
items:
- const: sys
- const: vp0
- const: vp1
- const: vp2
# See compatible-specific constraints below.
clocks:
@ -53,6 +66,8 @@ properties:
- description: Pixel clock for video port 2.
- description: Pixel clock for video port 3.
- description: Peripheral(vop grf/dsi) clock.
- description: Alternative pixel clock provided by HDMI0 PHY PLL.
- description: Alternative pixel clock provided by HDMI1 PHY PLL.
clock-names:
minItems: 5
@ -64,6 +79,8 @@ properties:
- const: dclk_vp2
- const: dclk_vp3
- const: pclk_vop
- const: pll_hdmiphy0
- const: pll_hdmiphy1
rockchip,grf:
$ref: /schemas/types.yaml#/definitions/phandle
@ -116,6 +133,72 @@ required:
- ports
allOf:
- if:
properties:
compatible:
contains:
enum:
- rockchip,rk3566-vop
- rockchip,rk3568-vop
then:
properties:
clocks:
maxItems: 5
clock-names:
maxItems: 5
interrupts:
maxItems: 1
interrupt-names: false
ports:
required:
- port@0
- port@1
- port@2
rockchip,vo1-grf: false
rockchip,vop-grf: false
rockchip,pmu: false
required:
- rockchip,grf
- if:
properties:
compatible:
contains:
enum:
- rockchip,rk3576-vop
then:
properties:
clocks:
maxItems: 5
clock-names:
maxItems: 5
interrupts:
minItems: 4
interrupt-names:
minItems: 4
ports:
required:
- port@0
- port@1
- port@2
rockchip,vo1-grf: false
rockchip,vop-grf: false
required:
- rockchip,grf
- rockchip,pmu
- if:
properties:
compatible:
@ -125,8 +208,16 @@ allOf:
properties:
clocks:
minItems: 7
maxItems: 9
clock-names:
minItems: 7
maxItems: 9
interrupts:
maxItems: 1
interrupt-names: false
ports:
required:
@ -141,23 +232,6 @@ allOf:
- rockchip,vop-grf
- rockchip,pmu
else:
properties:
rockchip,vo1-grf: false
rockchip,vop-grf: false
rockchip,pmu: false
clocks:
maxItems: 5
clock-names:
maxItems: 5
ports:
required:
- port@0
- port@1
- port@2
additionalProperties: false
examples:
@ -184,6 +258,7 @@ examples:
"dclk_vp1",
"dclk_vp2";
power-domains = <&power RK3568_PD_VO>;
rockchip,grf = <&grf>;
iommus = <&vop_mmu>;
vop_out: ports {
#address-cells = <1>;

View File

@ -25,6 +25,7 @@ properties:
- renesas,r9a07g044-mali
- renesas,r9a07g054-mali
- rockchip,px30-mali
- rockchip,rk3562-mali
- rockchip,rk3568-mali
- rockchip,rk3576-mali
- const: arm,mali-bifrost # Mali Bifrost GPU model/revision is fully discoverable

View File

@ -340,6 +340,8 @@ patternProperties:
description: Crystalfontz America, Inc.
"^csky,.*":
description: Hangzhou C-SKY Microsystems Co., Ltd
"^csot,.*":
description: Guangzhou China Star Optoelectronics Technology Co., Ltd
"^csq,.*":
description: Shenzen Chuangsiqi Technology Co.,Ltd.
"^ctera,.*":

View File

@ -12,6 +12,9 @@ we have a dedicated glossary for Display Core at
The number of CUs that are active on the system. The number of active
CUs may be less than SE * SH * CU depending on the board configuration.
CE
Constant Engine
CP
Command Processor
@ -68,6 +71,9 @@ we have a dedicated glossary for Display Core at
IB
Indirect Buffer
IMU
Integrated Management Unit (Power Management support)
IP
Intellectual Property blocks
@ -80,6 +86,12 @@ we have a dedicated glossary for Display Core at
KIQ
Kernel Interface Queue
MC
Memory Controller
ME
MicroEngine (Graphics)
MEC
MicroEngine Compute
@ -92,6 +104,9 @@ we have a dedicated glossary for Display Core at
MQD
Memory Queue Descriptor
PFP
Pre-Fetch Parser (Graphics)
PPLib
PowerPlay Library - PowerPlay is the power management component.
@ -99,7 +114,10 @@ we have a dedicated glossary for Display Core at
Platform Security Processor
RLC
RunList Controller
RunList Controller. This name is a remnant of past ages and doesn't have
much meaning today. It's a group of general-purpose helper engines for
the GFX block. It's involved in GFX power management and SR-IOV, among
other things.
SDMA
System DMA
@ -110,14 +128,35 @@ we have a dedicated glossary for Display Core at
SH
SHader array
SMU
System Management Unit
SMU/SMC
System Management Unit / System Management Controller
SRLC
Save/Restore List Control
SRLG
Save/Restore List GPM_MEM
SRLS
Save/Restore List SRM_MEM
SS
Spread Spectrum
TA
Trusted Application
TOC
Table of Contents
UVD
Unified Video Decoder
VCE
Video Compression Engine
VCN
Video Codec Next
VPE
Video Processing Engine

View File

@ -167,9 +167,6 @@ consider asking in the amdgfx and update this page.
MALL
Memory Access at Last Level
MC
Memory Controller
MPC/MPCC
Multiple pipes and plane combine
@ -232,6 +229,3 @@ consider asking in the amdgfx and update this page.
VRR
Variable Refresh Rate
UVD
Unified Video Decoder

View File

@ -10,6 +10,7 @@ GPU Driver Documentation
imagination/index
mcde
meson
nouveau
pl111
tegra
tve200
@ -24,6 +25,7 @@ GPU Driver Documentation
panfrost
panthor
zynqmp
nova/index
.. only:: subproject and html

View File

@ -208,6 +208,13 @@ follows:
``CONFIG_VIRTIO_UML`` and ``CONFIG_UML_PCI_OVER_VIRTIO`` are not
included in it because they are only required for User Mode Linux.
KUnit Coverage Rules
~~~~~~~~~~~~~~~~~~~~
KUnit support is gradually added to the DRM framework and helpers. There's no
general requirement for the framework and helpers to have KUnit tests at the
moment. However, patches that are affecting a function or helper already
covered by KUnit tests must provide tests if the change calls for one.
Legacy Support Code
===================

View File

@ -371,9 +371,119 @@ Reporting causes of resets
Apart from propagating the reset through the stack so apps can recover, it's
really useful for driver developers to learn more about what caused the reset in
the first place. DRM devices should make use of devcoredump to store relevant
information about the reset, so this information can be added to user bug
reports.
the first place. For this, drivers can make use of devcoredump to store relevant
information about the reset and send device wedged event with ``none`` recovery
method (as explained in "Device Wedging" chapter) to notify userspace, so this
information can be collected and added to user bug reports.
Device Wedging
==============
Drivers can optionally make use of device wedged event (implemented as
drm_dev_wedged_event() in DRM subsystem), which notifies userspace of 'wedged'
(hanged/unusable) state of the DRM device through a uevent. This is useful
especially in cases where the device is no longer operating as expected and has
become unrecoverable from driver context. Purpose of this implementation is to
provide drivers a generic way to recover the device with the help of userspace
intervention, without taking any drastic measures (like resetting or
re-enumerating the full bus, on which the underlying physical device is sitting)
in the driver.
A 'wedged' device is basically a device that is declared dead by the driver
after exhausting all possible attempts to recover it from driver context. The
uevent is the notification that is sent to userspace along with a hint about
what could possibly be attempted to recover the device from userspace and bring
it back to usable state. Different drivers may have different ideas of a
'wedged' device depending on hardware implementation of the underlying physical
device, and hence the vendor agnostic nature of the event. It is up to the
drivers to decide when they see the need for device recovery and how they want
to recover from the available methods.
Driver prerequisites
--------------------
The driver, before opting for recovery, needs to make sure that the 'wedged'
device doesn't harm the system as a whole by taking care of the prerequisites.
Necessary actions must include disabling DMA to system memory as well as any
communication channels with other devices. Further, the driver must ensure
that all dma_fences are signalled and any device state that the core kernel
might depend on is cleaned up. All existing mmaps should be invalidated and
page faults should be redirected to a dummy page. Once the event is sent, the
device must be kept in 'wedged' state until the recovery is performed. New
accesses to the device (IOCTLs) should be rejected, preferably with an error
code that resembles the type of failure the device has encountered. This will
signify the reason for wedging, which can be reported to the application if
needed.
Recovery
--------
Current implementation defines three recovery methods, out of which, drivers
can use any one, multiple or none. Method(s) of choice will be sent in the
uevent environment as ``WEDGED=<method1>[,..,<methodN>]`` in order of less to
more side-effects. If driver is unsure about recovery or method is unknown
(like soft/hard system reboot, firmware flashing, physical device replacement
or any other procedure which can't be attempted on the fly), ``WEDGED=unknown``
will be sent instead.
Userspace consumers can parse this event and attempt recovery as per the
following expectations.
=============== ========================================
Recovery method Consumer expectations
=============== ========================================
none optional telemetry collection
rebind unbind + bind driver
bus-reset unbind + bus reset/re-enumeration + bind
unknown consumer policy
=============== ========================================
The only exception to this is ``WEDGED=none``, which signifies that the device
was temporarily 'wedged' at some point but was recovered from driver context
using device specific methods like reset. No explicit recovery is expected from
the consumer in this case, but it can still take additional steps like gathering
telemetry information (devcoredump, syslog). This is useful because the first
hang is usually the most critical one which can result in consequential hangs or
complete wedging.
Consumer prerequisites
----------------------
It is the responsibility of the consumer to make sure that the device or its
resources are not in use by any process before attempting recovery. With IOCTLs
erroring out, all device memory should be unmapped and file descriptors should
be closed to prevent leaks or undefined behaviour. The idea here is to clear the
device of all user context beforehand and set the stage for a clean recovery.
Example
-------
Udev rule::
SUBSYSTEM=="drm", ENV{WEDGED}=="rebind", DEVPATH=="*/drm/card[0-9]",
RUN+="/path/to/rebind.sh $env{DEVPATH}"
Recovery script::
#!/bin/sh
DEVPATH=$(readlink -f /sys/$1/device)
DEVICE=$(basename $DEVPATH)
DRIVER=$(readlink -f $DEVPATH/driver)
echo -n $DEVICE > $DRIVER/unbind
echo -n $DEVICE > $DRIVER/bind
Customization
-------------
Although basic recovery is possible with a simple script, consumers can define
custom policies around recovery. For example, if the driver supports multiple
recovery methods, consumers can opt for the suitable one depending on scenarios
like repeat offences or vendor specific failures. Consumers can also choose to
have the device available for debugging or telemetry collection and base their
recovery decision on the findings. This is useful especially when the driver is
unsure about recovery or method is unknown.
.. _drm_driver_ioctl:

View File

@ -21,7 +21,10 @@ File format specification
- File shall contain one key value pair per one line of text.
- Colon character (`:`) must be used to delimit keys and values.
- All keys shall be prefixed with `drm-`.
- All standardised keys shall be prefixed with `drm-`.
- Driver-specific keys shall be prefixed with `driver_name-`, where
driver_name should ideally be the same as the `name` field in
`struct drm_driver`, although this is not mandatory.
- Whitespace between the delimiter and first non-whitespace character shall be
ignored when parsing.
- Keys are not allowed to contain whitespace characters.

View File

@ -0,0 +1,29 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
===============================
drm/nouveau NVIDIA GPU Driver
===============================
The drm/nouveau driver provides support for a wide range of NVIDIA GPUs,
covering GeForce, Quadro, and Tesla series, from the NV04 architecture up
to the latest Turing, Ampere, Ada families.
NVKM: NVIDIA Kernel Manager
===========================
The NVKM component serves as the core abstraction layer within the nouveau
driver, responsible for managing NVIDIA GPU hardware at the kernel level.
NVKM provides a unified interface for handling various GPU architectures.
It enables resource management, power control, memory handling, and command
submission required for the proper functioning of NVIDIA GPUs under the
nouveau driver.
NVKM plays a critical role in abstracting hardware complexities and
providing a consistent API to upper layers of the driver stack.
GSP Support
------------------------
.. kernel-doc:: drivers/gpu/drm/nouveau/nvkm/subdev/gsp/r535.c
:doc: GSP message queue element

View File

@ -0,0 +1,24 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
==========
Guidelines
==========
This documents contains the guidelines for nova-core. Additionally, all common
guidelines of the Nova project do apply.
Driver API
==========
One main purpose of nova-core is to implement the abstraction around the
firmware interface of GSP and provide a firmware (version) independent API for
2nd level drivers, such as nova-drm or the vGPU manager VFIO driver.
Therefore, it is not permitted to leak firmware (version) specifics, through the
driver API, to 2nd level drivers.
Acceptance Criteria
===================
- To the extend possible, patches submitted to nova-core must be tested for
regressions with all 2nd level drivers.

View File

@ -0,0 +1,446 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
=========
Task List
=========
Tasks may have the following fields:
- ``Complexity``: Describes the required familiarity with Rust and / or the
corresponding kernel APIs or subsystems. There are four different complexities,
``Beginner``, ``Intermediate``, ``Advanced`` and ``Expert``.
- ``Reference``: References to other tasks.
- ``Link``: Links to external resources.
- ``Contact``: The person that can be contacted for further information about
the task.
Enablement (Rust)
=================
Tasks that are not directly related to nova-core, but are preconditions in terms
of required APIs.
FromPrimitive API
-----------------
Sometimes the need arises to convert a number to a value of an enum or a
structure.
A good example from nova-core would be the ``Chipset`` enum type, which defines
the value ``AD102``. When probing the GPU the value ``0x192`` can be read from a
certain register indication the chipset AD102. Hence, the enum value ``AD102``
should be derived from the number ``0x192``. Currently, nova-core uses a custom
implementation (``Chipset::from_u32`` for this.
Instead, it would be desirable to have something like the ``FromPrimitive``
trait [1] from the num crate.
Having this generalization also helps with implementing a generic macro that
automatically generates the corresponding mappings between a value and a number.
| Complexity: Beginner
| Link: https://docs.rs/num/latest/num/trait.FromPrimitive.html
Generic register abstraction
----------------------------
Work out how register constants and structures can be automatically generated
through generalized macros.
Example:
.. code-block:: rust
register!(BOOT0, 0x0, u32, pci::Bar<SIZE>, Fields [
MINOR_REVISION(3:0, RO),
MAJOR_REVISION(7:4, RO),
REVISION(7:0, RO), // Virtual register combining major and minor rev.
])
This could expand to something like:
.. code-block:: rust
const BOOT0_OFFSET: usize = 0x00000000;
const BOOT0_MINOR_REVISION_SHIFT: u8 = 0;
const BOOT0_MINOR_REVISION_MASK: u32 = 0x0000000f;
const BOOT0_MAJOR_REVISION_SHIFT: u8 = 4;
const BOOT0_MAJOR_REVISION_MASK: u32 = 0x000000f0;
const BOOT0_REVISION_SHIFT: u8 = BOOT0_MINOR_REVISION_SHIFT;
const BOOT0_REVISION_MASK: u32 = BOOT0_MINOR_REVISION_MASK | BOOT0_MAJOR_REVISION_MASK;
struct Boot0(u32);
impl Boot0 {
#[inline]
fn read(bar: &RevocableGuard<'_, pci::Bar<SIZE>>) -> Self {
Self(bar.readl(BOOT0_OFFSET))
}
#[inline]
fn minor_revision(&self) -> u32 {
(self.0 & BOOT0_MINOR_REVISION_MASK) >> BOOT0_MINOR_REVISION_SHIFT
}
#[inline]
fn major_revision(&self) -> u32 {
(self.0 & BOOT0_MAJOR_REVISION_MASK) >> BOOT0_MAJOR_REVISION_SHIFT
}
#[inline]
fn revision(&self) -> u32 {
(self.0 & BOOT0_REVISION_MASK) >> BOOT0_REVISION_SHIFT
}
}
Usage:
.. code-block:: rust
let bar = bar.try_access().ok_or(ENXIO)?;
let boot0 = Boot0::read(&bar);
pr_info!("Revision: {}\n", boot0.revision());
| Complexity: Advanced
Delay / Sleep abstractions
--------------------------
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
----------------
Rust abstractions for IRQ handling.
There is active ongoing work from Daniel Almeida [1] for the "core" abstractions
to request IRQs.
Besides optional review and testing work, the required ``pci::Device`` code
around those core abstractions needs to be worked out.
| Complexity: Intermediate
| Link: https://lore.kernel.org/lkml/20250122163932.46697-1-daniel.almeida@collabora.com/ [1]
| Contact: Daniel Almeida
Page abstraction for foreign pages
----------------------------------
Rust abstractions for pages not created by the Rust page abstraction without
direct ownership.
There is active onging work from Abdiel Janulgue [1] and Lina [2].
| Complexity: Advanced
| Link: https://lore.kernel.org/linux-mm/20241119112408.779243-1-abdiel.janulgue@gmail.com/ [1]
| Link: https://lore.kernel.org/rust-for-linux/20250202-rust-page-v1-0-e3170d7fe55e@asahilina.net/ [2]
Scatterlist / sg_table abstractions
-----------------------------------
Rust abstractions for scatterlist / sg_table.
There is preceding work from Abdiel Janulgue, which hasn't made it to the
mailing list yet.
| Complexity: Intermediate
| Contact: Abdiel Janulgue
ELF utils
---------
Rust implementation of ELF header representation to retrieve section header
tables, names, and data from an ELF-formatted images.
There is preceding work from Abdiel Janulgue, which hasn't made it to the
mailing list yet.
| Complexity: Beginner
| Contact: Abdiel Janulgue
PCI MISC APIs
-------------
Extend the existing PCI device / driver abstractions by SR-IOV, config space,
capability, MSI API abstractions.
| Complexity: Beginner
Auxiliary bus abstractions
--------------------------
Rust abstraction for the auxiliary bus APIs.
This is needed to connect nova-core to the nova-drm driver.
| Complexity: Intermediate
Debugfs abstractions
--------------------
Rust abstraction for debugfs APIs.
| Reference: Export GSP log buffers
| Complexity: Intermediate
Vec extensions
--------------
Implement ``Vec::truncate`` and ``Vec::resize``.
Currently this is used for some experimental code to parse the vBIOS.
| Reference vBIOS support
| Complexity: Beginner
GPU (general)
=============
Parse firmware headers
----------------------
Parse ELF headers from the firmware files loaded from the filesystem.
| Reference: ELF utils
| Complexity: Beginner
| Contact: Abdiel Janulgue
Build radix3 page table
-----------------------
Build the radix3 page table to map the firmware.
| Complexity: Intermediate
| Contact: Abdiel Janulgue
vBIOS support
-------------
Parse the vBIOS and probe the structures required for driver initialization.
| Contact: Dave Airlie
| Reference: Vec extensions
| Complexity: Intermediate
Initial Devinit support
-----------------------
Implement BIOS Device Initialization, i.e. memory sizing, waiting, PLL
configuration.
| Contact: Dave Airlie
| Complexity: Beginner
Boot Falcon controller
----------------------
Infrastructure to load and execute falcon (sec2) firmware images; handle the
GSP falcon processor and fwsec loading.
| Complexity: Advanced
| Contact: Dave Airlie
GPU Timer support
-----------------
Support for the GPU's internal timer peripheral.
| Complexity: Beginner
| Contact: Dave Airlie
MMU / PT management
-------------------
Work out the architecture for MMU / page table management.
We need to consider that nova-drm will need rather fine-grained control,
especially in terms of locking, in order to be able to implement asynchronous
Vulkan queues.
While generally sharing the corresponding code is desirable, it needs to be
evaluated how (and if at all) sharing the corresponding code is expedient.
| Complexity: Expert
VRAM memory allocator
---------------------
Investigate options for a VRAM memory allocator.
Some possible options:
- Rust abstractions for
- RB tree (interval tree) / drm_mm
- maple_tree
- native Rust collections
| Complexity: Advanced
Instance Memory
---------------
Implement support for instmem (bar2) used to store page tables.
| Complexity: Intermediate
| Contact: Dave Airlie
GPU System Processor (GSP)
==========================
Export GSP log buffers
----------------------
Recent patches from Timur Tabi [1] added support to expose GSP-RM log buffers
(even after failure to probe the driver) through debugfs.
This is also an interesting feature for nova-core, especially in the early days.
| Link: https://lore.kernel.org/nouveau/20241030202952.694055-2-ttabi@nvidia.com/ [1]
| Reference: Debugfs abstractions
| Complexity: Intermediate
GSP firmware abstraction
------------------------
The GSP-RM firmware API is unstable and may incompatibly change from version to
version, in terms of data structures and semantics.
This problem is one of the big motivations for using Rust for nova-core, since
it turns out that Rust's procedural macro feature provides a rather elegant way
to address this issue:
1. generate Rust structures from the C headers in a separate namespace per version
2. build abstraction structures (within a generic namespace) that implement the
firmware interfaces; annotate the differences in implementation with version
identifiers
3. use a procedural macro to generate the actual per version implementation out
of this abstraction
4. instantiate the correct version type one on runtime (can be sure that all
have the same interface because it's defined by a common trait)
There is a PoC implementation of this pattern, in the context of the nova-core
PoC driver.
This task aims at refining the feature and ideally generalize it, to be usable
by other drivers as well.
| Complexity: Expert
GSP message queue
-----------------
Implement low level GSP message queue (command, status) for communication
between the kernel driver and GSP.
| Complexity: Advanced
| Contact: Dave Airlie
Bootstrap GSP
-------------
Call the boot firmware to boot the GSP processor; execute initial control
messages.
| Complexity: Intermediate
| Contact: Dave Airlie
Client / Device APIs
--------------------
Implement the GSP message interface for client / device allocation and the
corresponding client and device allocation APIs.
| Complexity: Intermediate
| Contact: Dave Airlie
Bar PDE handling
----------------
Synchronize page table handling for BARs between the kernel driver and GSP.
| Complexity: Beginner
| Contact: Dave Airlie
FIFO engine
-----------
Implement support for the FIFO engine, i.e. the corresponding GSP message
interface and provide an API for chid allocation and channel handling.
| Complexity: Advanced
| Contact: Dave Airlie
GR engine
---------
Implement support for the graphics engine, i.e. the corresponding GSP message
interface and provide an API for (golden) context creation and promotion.
| Complexity: Advanced
| Contact: Dave Airlie
CE engine
---------
Implement support for the copy engine, i.e. the corresponding GSP message
interface.
| Complexity: Intermediate
| Contact: Dave Airlie
VFN IRQ controller
------------------
Support for the VFN interrupt controller.
| Complexity: Intermediate
| Contact: Dave Airlie
External APIs
=============
nova-core base API
------------------
Work out the common pieces of the API to connect 2nd level drivers, i.e. vGPU
manager and nova-drm.
| Complexity: Advanced
vGPU manager API
----------------
Work out the API parts required by the vGPU manager, which are not covered by
the base API.
| Complexity: Advanced
nova-core C API
---------------
Implement a C wrapper for the APIs required by the vGPU manager driver.
| Complexity: Intermediate
Testing
=======
CI pipeline
-----------
Investigate option for continuous integration testing.
This can go from as simple as running KUnit tests over running (graphics) CTS to
booting up (multiple) guest VMs to test VFIO use-cases.
It might also be worth to consider the introduction of a new test suite directly
sitting on top of the uAPI for more targeted testing and debugging. There may be
options for collaboration / shared code with the Mesa project.
| Complexity: Advanced

View File

@ -0,0 +1,69 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
==========
Guidelines
==========
This document describes the general project guidelines that apply to nova-core
and nova-drm.
Language
========
The Nova project uses the Rust programming language. In this context, all rules
of the Rust for Linux project as documented in
:doc:`../../rust/general-information` apply. Additionally, the following rules
apply.
- Unless technically necessary otherwise (e.g. uAPI), any driver code is written
in Rust.
- Unless technically necessary, unsafe Rust code must be avoided. In case of
technical necessity, unsafe code should be isolated in a separate component
providing a safe API for other driver code to use.
Style
-----
All rules of the Rust for Linux project as documented in
:doc:`../../rust/coding-guidelines` apply.
For a submit checklist, please also see the `Rust for Linux Submit checklist
addendum <https://rust-for-linux.com/contributing#submit-checklist-addendum>`_.
Documentation
=============
The availability of proper documentation is essential in terms of scalability,
accessibility for new contributors and maintainability of a project in general,
but especially for a driver running as complex hardware as Nova is targeting.
Hence, adding documentation of any kind is very much encouraged by the project.
Besides that, there are some minimum requirements.
- Every non-private structure needs at least a brief doc comment explaining the
semantical sense of the structure, as well as potential locking and lifetime
requirements. It is encouraged to have the same minimum documentation for
non-trivial private structures.
- uAPIs must be fully documented with kernel-doc comments; additionally, the
semantical behavior must be explained including potential special or corner
cases.
- The APIs connecting the 1st level driver (nova-core) with 2nd level drivers
must be fully documented. This includes doc comments, potential locking and
lifetime requirements, as well as example code if applicable.
- Abbreviations must be explained when introduced; terminology must be uniquely
defined.
- Register addresses, layouts, shift values and masks must be defined properly;
unless obvious, the semantical sense must be documented. This only applies if
the author is able to obtain the corresponding information.
Acceptance Criteria
===================
- Patches must only be applied if reviewed by at least one other person on the
mailing list; this also applies for maintainers.

View File

@ -0,0 +1,30 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
=======================
nova NVIDIA GPU drivers
=======================
The nova driver project consists out of two separate drivers nova-core and
nova-drm and intends to supersede the nouveau driver for NVIDIA GPUs based on
the GPU System Processor (GSP).
The following documents apply to both nova-core and nova-drm.
.. toctree::
:titlesonly:
guidelines
nova-core
=========
The nova-core driver is the core driver for NVIDIA GPUs based on GSP. nova-core,
as the 1st level driver, provides an abstraction around the GPUs hard- and
firmware interfaces providing a common base for 2nd level drivers, such as the
vGPU manager VFIO driver and the nova-drm driver.
.. toctree::
:titlesonly:
core/guidelines
core/todo

View File

@ -26,6 +26,8 @@ the currently possible format options:
drm-cycles-panthor: 94439687187
drm-maxfreq-panthor: 1000000000 Hz
drm-curfreq-panthor: 1000000000 Hz
panthor-resident-memory: 10396 KiB
panthor-active-memory: 10396 KiB
drm-total-memory: 16480 KiB
drm-shared-memory: 0
drm-active-memory: 16200 KiB
@ -44,3 +46,11 @@ driver by writing into the appropriate sysfs node::
Where `N` is a bit mask where cycle and timestamp sampling are respectively
enabled by the first and second bits.
Possible `panthor-*-memory` keys are: `active` and `resident`.
These values convey the sizes of the internal driver-owned shmem BO's that
aren't exposed to user-space through a DRM handle, like queue ring buffers,
sync object arrays and heap chunks. Because they are all allocated and pinned
at creation time, only `panthor-resident-memory` is necessary to tell us their
size. `panthor-active-memory` shows the size of kernel BO's associated with
VM's and groups currently being scheduled for execution by the GPU.

View File

@ -0,0 +1,112 @@
.. SPDX-License-Identifier: (GPL-2.0+ OR MIT)
===============
GPU SVM Section
===============
Agreed upon design principles
=============================
* migrate_to_ram path
* Rely only on core MM concepts (migration PTEs, page references, and
page locking).
* No driver specific locks other than locks for hardware interaction in
this path. These are not required and generally a bad idea to
invent driver defined locks to seal core MM races.
* An example of a driver-specific lock causing issues occurred before
fixing do_swap_page to lock the faulting page. A driver-exclusive lock
in migrate_to_ram produced a stable livelock if enough threads read
the faulting page.
* Partial migration is supported (i.e., a subset of pages attempting to
migrate can actually migrate, with only the faulting page guaranteed
to migrate).
* Driver handles mixed migrations via retry loops rather than locking.
* Eviction
* Eviction is defined as migrating data from the GPU back to the
CPU without a virtual address to free up GPU memory.
* Only looking at physical memory data structures and locks as opposed to
looking at virtual memory data structures and locks.
* No looking at mm/vma structs or relying on those being locked.
* The rationale for the above two points is that CPU virtual addresses
can change at any moment, while the physical pages remain stable.
* GPU page table invalidation, which requires a GPU virtual address, is
handled via the notifier that has access to the GPU virtual address.
* GPU fault side
* mmap_read only used around core MM functions which require this lock
and should strive to take mmap_read lock only in GPU SVM layer.
* Big retry loop to handle all races with the mmu notifier under the gpu
pagetable locks/mmu notifier range lock/whatever we end up calling
those.
* Races (especially against concurrent eviction or migrate_to_ram)
should not be handled on the fault side by trying to hold locks;
rather, they should be handled using retry loops. One possible
exception is holding a BO's dma-resv lock during the initial migration
to VRAM, as this is a well-defined lock that can be taken underneath
the mmap_read lock.
* One possible issue with the above approach is if a driver has a strict
migration policy requiring GPU access to occur in GPU memory.
Concurrent CPU access could cause a livelock due to endless retries.
While no current user (Xe) of GPU SVM has such a policy, it is likely
to be added in the future. Ideally, this should be resolved on the
core-MM side rather than through a driver-side lock.
* Physical memory to virtual backpointer
* This does not work, as no pointers from physical memory to virtual
memory should exist. mremap() is an example of the core MM updating
the virtual address without notifying the driver of address
change rather the driver only receiving the invalidation notifier.
* The physical memory backpointer (page->zone_device_data) should remain
stable from allocation to page free. Safely updating this against a
concurrent user would be very difficult unless the page is free.
* GPU pagetable locking
* Notifier lock only protects range tree, pages valid state for a range
(rather than seqno due to wider notifiers), pagetable entries, and
mmu notifier seqno tracking, it is not a global lock to protect
against races.
* All races handled with big retry as mentioned above.
Overview of baseline design
===========================
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Overview
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Locking
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Migration
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Partial Unmapping of Ranges
.. kernel-doc:: drivers/gpu/drm/drm_gpusvm.c
:doc: Examples
Possible future design features
===============================
* Concurrent GPU faults
* CPU faults are concurrent so makes sense to have concurrent GPU
faults.
* Should be possible with fined grained locking in the driver GPU
fault handler.
* No expected GPU SVM changes required.
* Ranges with mixed system and device pages
* Can be added if required to drm_gpusvm_get_pages fairly easily.
* Multi-GPU support
* Work in progress and patches expected after initially landing on GPU
SVM.
* Ideally can be done with little to no changes to GPU SVM.
* Drop ranges in favor of radix tree
* May be desirable for faster notifiers.
* Compound device pages
* Nvidia, AMD, and Intel all have agreed expensive core MM functions in
migrate device layer are a performance bottleneck, having compound
device pages should help increase performance by reducing the number
of these expensive calls.
* Higher order dma mapping for migration
* 4k dma mapping adversely affects migration performance on Intel
hardware, higher order (2M) dma mapping should help here.
* Build common userptr implementation on top of GPU SVM
* Driver side madvise implementation and migration policies
* Pull in pending dma-mapping API changes from Leon / Nvidia when these land

View File

@ -16,6 +16,10 @@ host such documentation:
* Once the code has landed move all the documentation to the right places in
the main core, helper or driver sections.
.. toctree::
gpusvm.rst
.. toctree::
i915_gem_lmem.rst

1
Kbuild
View File

@ -97,3 +97,4 @@ obj-$(CONFIG_SAMPLES) += samples/
obj-$(CONFIG_NET) += net/
obj-y += virt/
obj-y += $(ARCH_DRIVERS)
obj-$(CONFIG_DRM_HEADER_TEST) += include/

View File

@ -7166,7 +7166,7 @@ F: include/linux/power/smartreflex.h
DRM ACCEL DRIVERS FOR INTEL VPU
M: Jacek Lawrynowicz <jacek.lawrynowicz@linux.intel.com>
M: Stanislaw Gruszka <stanislaw.gruszka@linux.intel.com>
M: Maciej Falkowski <maciej.falkowski@linux.intel.com>
L: dri-devel@lists.freedesktop.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
@ -7192,6 +7192,14 @@ S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/sun4i/sun8i*
DRM DRIVER FOR APPLE TOUCH BARS
M: Aun-Ali Zaidi <admin@kodeit.net>
M: Aditya Garg <gargaditya08@live.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/tiny/appletbdrm.c
DRM DRIVER FOR ARM PL111 CLCD
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
@ -7285,8 +7293,7 @@ F: Documentation/devicetree/bindings/display/panel/panel-edp.yaml
F: drivers/gpu/drm/panel/panel-edp.c
DRM DRIVER FOR GENERIC USB DISPLAY
M: Noralf Trønnes <noralf@tronnes.org>
S: Maintained
S: Orphan
W: https://github.com/notro/gud/wiki
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/gud/
@ -7391,15 +7398,14 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/mgag200/
DRM DRIVER FOR MI0283QT
M: Noralf Trønnes <noralf@tronnes.org>
M: Alex Lanzano <lanzano.alex@gmail.com>
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/multi-inno,mi0283qt.txt
F: drivers/gpu/drm/tiny/mi0283qt.c
DRM DRIVER FOR MIPI DBI compatible panels
M: Noralf Trønnes <noralf@tronnes.org>
S: Maintained
S: Orphan
W: https://github.com/notro/panel-mipi-dbi/wiki
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml
@ -7426,7 +7432,7 @@ F: include/uapi/drm/msm_drm.h
DRM DRIVER for Qualcomm display hardware
M: Rob Clark <robdclark@gmail.com>
M: Abhinav Kumar <quic_abhinavk@quicinc.com>
M: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
M: Dmitry Baryshkov <lumag@kernel.org>
R: Sean Paul <sean@poorly.run>
R: Marijn Suijten <marijn.suijten@somainline.org>
L: linux-arm-msm@vger.kernel.org
@ -7438,6 +7444,7 @@ T: git https://gitlab.freedesktop.org/drm/msm.git
F: Documentation/devicetree/bindings/display/msm/
F: drivers/gpu/drm/ci/xfails/msm*
F: drivers/gpu/drm/msm/
F: include/dt-bindings/clock/qcom,dsi-phy-28nm.h
F: include/uapi/drm/msm_drm.h
DRM DRIVER FOR NOVATEK NT35510 PANELS
@ -7483,6 +7490,17 @@ T: git https://gitlab.freedesktop.org/drm/nouveau.git
F: drivers/gpu/drm/nouveau/
F: include/uapi/drm/nouveau_drm.h
CORE DRIVER FOR NVIDIA GPUS [RUST]
M: Danilo Krummrich <dakr@kernel.org>
L: nouveau@lists.freedesktop.org
S: Supported
Q: https://patchwork.freedesktop.org/project/nouveau/
B: https://gitlab.freedesktop.org/drm/nova/-/issues
C: irc://irc.oftc.net/nouveau
T: git https://gitlab.freedesktop.org/drm/nova.git nova-next
F: Documentation/gpu/nova/
F: drivers/gpu/nova-core/
DRM DRIVER FOR OLIMEX LCD-OLINUXINO PANELS
M: Stefan Mavrodiev <stefan@olimex.com>
S: Maintained
@ -7495,7 +7513,7 @@ F: Documentation/devicetree/bindings/display/bridge/ps8640.yaml
F: drivers/gpu/drm/bridge/parade-ps8640.c
DRM DRIVER FOR PERVASIVE DISPLAYS REPAPER PANELS
M: Noralf Trønnes <noralf@tronnes.org>
M: Alex Lanzano <lanzano.alex@gmail.com>
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/repaper.txt
@ -7719,8 +7737,8 @@ X: drivers/gpu/drm/mediatek/
X: drivers/gpu/drm/msm/
X: drivers/gpu/drm/nouveau/
X: drivers/gpu/drm/radeon/
X: drivers/gpu/drm/renesas/rcar-du/
X: drivers/gpu/drm/tegra/
X: drivers/gpu/drm/xe/
DRM DRIVERS FOR ALLWINNER A10
M: Maxime Ripard <mripard@kernel.org>
@ -7873,13 +7891,30 @@ F: drivers/gpu/host1x/
F: include/linux/host1x.h
F: include/uapi/drm/tegra_drm.h
DRM DRIVERS FOR PRE-DCP APPLE DISPLAY OUTPUT
M: Sasha Finkelstein <fnkl.kernel@gmail.com>
R: Janne Grunau <j@jannau.net>
L: dri-devel@lists.freedesktop.org
L: asahi@lists.linux.dev
S: Maintained
W: https://asahilinux.org
B: https://github.com/AsahiLinux/linux/issues
C: irc://irc.oftc.net/asahi-dev
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/apple,h7-display-pipe-mipi.yaml
F: Documentation/devicetree/bindings/display/apple,h7-display-pipe.yaml
F: Documentation/devicetree/bindings/display/panel/apple,summit.yaml
F: drivers/gpu/drm/adp/
F: drivers/gpu/drm/panel/panel-summit.c
DRM DRIVERS FOR RENESAS R-CAR
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
M: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
M: Tomi Valkeinen <tomi.valkeinen+renesas@ideasonboard.com>
R: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
L: dri-devel@lists.freedesktop.org
L: linux-renesas-soc@vger.kernel.org
S: Supported
T: git git://linuxtv.org/pinchartl/media drm/du/next
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.yaml
F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.yaml
@ -8016,12 +8051,12 @@ F: Documentation/gpu/zynqmp.rst
F: drivers/gpu/drm/xlnx/
DRM GPU SCHEDULER
M: Luben Tuikov <ltuikov89@gmail.com>
M: Matthew Brost <matthew.brost@intel.com>
M: Danilo Krummrich <dakr@kernel.org>
M: Philipp Stanner <pstanner@redhat.com>
M: Philipp Stanner <phasta@kernel.org>
R: Christian König <ckoenig.leichtzumerken@gmail.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/scheduler/
F: include/drm/gpu_scheduler.h
@ -8048,6 +8083,8 @@ F: include/drm/drm_privacy_screen*
DRM TTM SUBSYSTEM
M: Christian Koenig <christian.koenig@amd.com>
M: Huang Rui <ray.huang@amd.com>
R: Matthew Auld <matthew.auld@intel.com>
R: Matthew Brost <matthew.brost@intel.com>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
@ -11740,7 +11777,7 @@ F: drivers/gpio/gpio-tangier.c
F: drivers/gpio/gpio-tangier.h
INTEL GVT-g DRIVERS (Intel GPU Virtualization)
M: Zhenyu Wang <zhenyuw@linux.intel.com>
M: Zhenyu Wang <zhenyuw.linux@gmail.com>
M: Zhi Wang <zhi.wang.linux@gmail.com>
L: intel-gvt-dev@lists.freedesktop.org
L: intel-gfx@lists.freedesktop.org
@ -17262,8 +17299,7 @@ M: Russell King <linux@armlinux.org.uk>
S: Maintained
T: git git://git.armlinux.org.uk/~rmk/linux-arm.git drm-tda998x-devel
T: git git://git.armlinux.org.uk/~rmk/linux-arm.git drm-tda998x-fixes
F: drivers/gpu/drm/i2c/tda998x_drv.c
F: include/drm/i2c/tda998x.h
F: drivers/gpu/drm/bridge/tda998x_drv.c
F: include/dt-bindings/display/tda998x.h
K: "nxp,tda998x"
@ -19566,7 +19602,7 @@ F: drivers/clk/qcom/
F: include/dt-bindings/clock/qcom,*
QUALCOMM CLOUD AI (QAIC) DRIVER
M: Jeffrey Hugo <quic_jhugo@quicinc.com>
M: Jeff Hugo <jeff.hugo@oss.qualcomm.com>
R: Carl Vanderlip <quic_carlv@quicinc.com>
L: linux-arm-msm@vger.kernel.org
L: dri-devel@lists.freedesktop.org

View File

@ -714,10 +714,10 @@ CONFIG_VIDEO_ADV7604_CEC=y
CONFIG_VIDEO_ML86V7667=m
CONFIG_IMX_IPUV3_CORE=m
CONFIG_DRM=y
# CONFIG_DRM_I2C_CH7006 is not set
# CONFIG_DRM_I2C_SIL164 is not set
CONFIG_DRM_I2C_NXP_TDA998X=m
CONFIG_DRM_NOUVEAU=m
# CONFIG_DRM_NOUVEAU_CH7006 is not set
# CONFIG_DRM_NOUVEAU_SIL164 is not set
CONFIG_DRM_EXYNOS=m
CONFIG_DRM_EXYNOS_FIMD=y
CONFIG_DRM_EXYNOS_MIXER=y

View File

@ -132,11 +132,11 @@ CONFIG_I2C=y
CONFIG_HWMON=m
CONFIG_DRM=m
CONFIG_DRM_DISPLAY_DP_AUX_CEC=y
# CONFIG_DRM_I2C_CH7006 is not set
# CONFIG_DRM_I2C_SIL164 is not set
CONFIG_DRM_RADEON=m
CONFIG_DRM_NOUVEAU=m
# CONFIG_DRM_NOUVEAU_BACKLIGHT is not set
# CONFIG_DRM_NOUVEAU_CH7006 is not set
# CONFIG_DRM_NOUVEAU_SIL164 is not set
CONFIG_DRM_VGEM=m
CONFIG_DRM_UDL=m
CONFIG_DRM_MGAG200=m

View File

@ -193,11 +193,11 @@ CONFIG_MEDIA_SUPPORT=m
CONFIG_AGP=y
CONFIG_AGP_PARISC=y
CONFIG_DRM=y
# CONFIG_DRM_I2C_CH7006 is not set
# CONFIG_DRM_I2C_SIL164 is not set
CONFIG_DRM_RADEON=y
CONFIG_DRM_NOUVEAU=m
# CONFIG_DRM_NOUVEAU_BACKLIGHT is not set
# CONFIG_DRM_NOUVEAU_CH7006 is not set
# CONFIG_DRM_NOUVEAU_SIL164 is not set
CONFIG_DRM_MGAG200=m
CONFIG_FB=y
CONFIG_FB_PM2=m

View File

@ -34,6 +34,8 @@ static void aie2_job_release(struct kref *ref)
job = container_of(ref, struct amdxdna_sched_job, refcnt);
amdxdna_sched_job_cleanup(job);
atomic64_inc(&job->hwctx->job_free_cnt);
wake_up(&job->hwctx->priv->job_free_wq);
if (job->out_fence)
dma_fence_put(job->out_fence);
kfree(job);
@ -134,7 +136,8 @@ static void aie2_hwctx_wait_for_idle(struct amdxdna_hwctx *hwctx)
if (!fence)
return;
dma_fence_wait(fence, false);
/* Wait up to 2 seconds for fw to finish all pending requests */
dma_fence_wait_timeout(fence, false, msecs_to_jiffies(2000));
dma_fence_put(fence);
}
@ -185,7 +188,7 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
}
static int
aie2_sched_resp_handler(void *handle, const u32 *data, size_t size)
aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size)
{
struct amdxdna_sched_job *job = handle;
struct amdxdna_gem_obj *cmd_abo;
@ -203,7 +206,7 @@ aie2_sched_resp_handler(void *handle, const u32 *data, size_t size)
goto out;
}
status = *data;
status = readl(data);
XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
if (status == AIE2_STATUS_SUCCESS)
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_COMPLETED);
@ -216,7 +219,7 @@ out:
}
static int
aie2_sched_nocmd_resp_handler(void *handle, const u32 *data, size_t size)
aie2_sched_nocmd_resp_handler(void *handle, void __iomem *data, size_t size)
{
struct amdxdna_sched_job *job = handle;
u32 ret = 0;
@ -230,7 +233,7 @@ aie2_sched_nocmd_resp_handler(void *handle, const u32 *data, size_t size)
goto out;
}
status = *data;
status = readl(data);
XDNA_DBG(job->hwctx->client->xdna, "Resp status 0x%x", status);
out:
@ -239,14 +242,14 @@ out:
}
static int
aie2_sched_cmdlist_resp_handler(void *handle, const u32 *data, size_t size)
aie2_sched_cmdlist_resp_handler(void *handle, void __iomem *data, size_t size)
{
struct amdxdna_sched_job *job = handle;
struct amdxdna_gem_obj *cmd_abo;
struct cmd_chain_resp *resp;
struct amdxdna_dev *xdna;
u32 fail_cmd_status;
u32 fail_cmd_idx;
u32 cmd_status;
u32 ret = 0;
cmd_abo = job->cmd_bo;
@ -256,17 +259,17 @@ aie2_sched_cmdlist_resp_handler(void *handle, const u32 *data, size_t size)
goto out;
}
resp = (struct cmd_chain_resp *)data;
cmd_status = readl(data + offsetof(struct cmd_chain_resp, status));
xdna = job->hwctx->client->xdna;
XDNA_DBG(xdna, "Status 0x%x", resp->status);
if (resp->status == AIE2_STATUS_SUCCESS) {
XDNA_DBG(xdna, "Status 0x%x", cmd_status);
if (cmd_status == AIE2_STATUS_SUCCESS) {
amdxdna_cmd_set_state(cmd_abo, ERT_CMD_STATE_COMPLETED);
goto out;
}
/* Slow path to handle error, read from ringbuf on BAR */
fail_cmd_idx = resp->fail_cmd_idx;
fail_cmd_status = resp->fail_cmd_status;
fail_cmd_idx = readl(data + offsetof(struct cmd_chain_resp, fail_cmd_idx));
fail_cmd_status = readl(data + offsetof(struct cmd_chain_resp, fail_cmd_status));
XDNA_DBG(xdna, "Failed cmd idx %d, status 0x%x",
fail_cmd_idx, fail_cmd_status);
@ -361,7 +364,7 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
return DRM_GPU_SCHED_STAT_NOMINAL;
}
const struct drm_sched_backend_ops sched_ops = {
static const struct drm_sched_backend_ops sched_ops = {
.run_job = aie2_sched_job_run,
.free_job = aie2_sched_job_free,
.timedout_job = aie2_sched_job_timedout,
@ -516,6 +519,14 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_client *client = hwctx->client;
struct amdxdna_dev *xdna = client->xdna;
const struct drm_sched_init_args args = {
.ops = &sched_ops,
.num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = HWCTX_MAX_CMDS,
.timeout = msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
.name = hwctx->name,
.dev = xdna->ddev.dev,
};
struct drm_gpu_scheduler *sched;
struct amdxdna_hwctx_priv *priv;
struct amdxdna_gem_obj *heap;
@ -573,9 +584,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
might_lock(&priv->io_lock);
fs_reclaim_release(GFP_KERNEL);
ret = drm_sched_init(sched, &sched_ops, NULL, DRM_SCHED_PRIORITY_COUNT,
HWCTX_MAX_CMDS, 0, msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
NULL, NULL, hwctx->name, xdna->ddev.dev);
ret = drm_sched_init(sched, &args);
if (ret) {
XDNA_ERR(xdna, "Failed to init DRM scheduler. ret %d", ret);
goto free_cmd_bufs;
@ -616,6 +625,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
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);
@ -652,24 +662,22 @@ void aie2_hwctx_fini(struct amdxdna_hwctx *hwctx)
xdna = hwctx->client->xdna;
ndev = xdna->dev_handle;
ndev->hwctx_num--;
drm_sched_wqueue_stop(&hwctx->priv->sched);
/* Now, scheduler will not send command to device. */
aie2_release_resource(hwctx);
/*
* All submitted commands are aborted.
* Restart scheduler queues to cleanup jobs. The amdxdna_sched_job_run()
* will return NODEV if it is called.
*/
drm_sched_wqueue_start(&hwctx->priv->sched);
aie2_hwctx_wait_for_idle(hwctx);
drm_sched_entity_destroy(&hwctx->priv->entity);
drm_sched_fini(&hwctx->priv->sched);
aie2_ctx_syncobj_destroy(hwctx);
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);
/* 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));
drm_sched_fini(&hwctx->priv->sched);
aie2_ctx_syncobj_destroy(hwctx);
for (idx = 0; idx < ARRAY_SIZE(hwctx->priv->cmd_buf); idx++)
drm_gem_object_put(to_gobj(hwctx->priv->cmd_buf[idx]));
@ -879,6 +887,7 @@ retry:
drm_gem_unlock_reservations(job->bos, job->bo_cnt, &acquire_ctx);
aie2_job_put(job);
atomic64_inc(&hwctx->job_submit_cnt);
return 0;

View File

@ -209,16 +209,14 @@ static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u3
return err_col;
}
static int aie2_error_async_cb(void *handle, const u32 *data, size_t size)
static int aie2_error_async_cb(void *handle, void __iomem *data, size_t size)
{
struct async_event_msg_resp *resp;
struct async_event *e = handle;
if (data) {
resp = (struct async_event_msg_resp *)data;
e->resp.type = resp->type;
e->resp.type = readl(data + offsetof(struct async_event_msg_resp, type));
wmb(); /* Update status in the end, so that no lock for here */
e->resp.status = resp->status;
e->resp.status = readl(data + offsetof(struct async_event_msg_resp, status));
}
queue_work(e->wq, &e->work);
return 0;

View File

@ -356,7 +356,7 @@ fail:
}
int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
void *handle, int (*cb)(void*, const u32 *, size_t))
void *handle, int (*cb)(void*, void __iomem *, size_t))
{
struct async_event_msg_req req = { 0 };
struct xdna_mailbox_msg msg = {
@ -435,7 +435,7 @@ int aie2_config_cu(struct amdxdna_hwctx *hwctx)
}
int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t))
int (*notify_cb)(void *, void __iomem *, size_t))
{
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
struct amdxdna_dev *xdna = hwctx->client->xdna;
@ -640,7 +640,7 @@ aie2_cmd_op_to_msg_op(u32 op)
int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t))
int (*notify_cb)(void *, void __iomem *, size_t))
{
struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job);
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
@ -705,7 +705,7 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t))
int (*notify_cb)(void *, void __iomem *, size_t))
{
struct amdxdna_gem_obj *cmdbuf_abo = aie2_cmdlist_get_cmd_buf(job);
struct mailbox_channel *chann = hwctx->priv->mbox_chann;
@ -740,7 +740,7 @@ int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
}
int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t))
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]);

View File

@ -271,18 +271,18 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc
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_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, u32 size,
void *handle, int (*cb)(void*, const u32 *, size_t));
void *handle, int (*cb)(void*, void __iomem *, size_t));
int aie2_config_cu(struct amdxdna_hwctx *hwctx);
int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t));
int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx,
struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t));
int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
struct amdxdna_sched_job *job,
int (*notify_cb)(void *, const u32 *, size_t));
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 *, const u32 *, size_t));
int (*notify_cb)(void *, void __iomem *, size_t));
/* aie2_hwctx.c */
int aie2_hwctx_init(struct amdxdna_hwctx *hwctx);

View File

@ -64,6 +64,7 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
if (ret) {
XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
return ret;
}
ndev->npuclk_freq = freq;
@ -72,6 +73,7 @@ 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;
}
ndev->hclk_freq = freq;
ndev->dpm_level = dpm_level;

View File

@ -220,6 +220,8 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
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);
XDNA_DBG(xdna, "PID %d create HW context %d, ret %d", client->pid, args->handle, ret);
drm_dev_exit(idx);
return 0;

View File

@ -87,6 +87,9 @@ struct amdxdna_hwctx {
struct amdxdna_qos_info qos;
struct amdxdna_hwctx_param_config_cu *cus;
u32 syncobj_hdl;
atomic64_t job_submit_cnt;
atomic64_t job_free_cnt ____cacheline_aligned_in_smp;
};
#define drm_job_to_xdna_job(j) \

View File

@ -91,7 +91,7 @@ struct mailbox_pkg {
struct mailbox_msg {
void *handle;
int (*notify_cb)(void *handle, const u32 *data, size_t size);
int (*notify_cb)(void *handle, void __iomem *data, size_t size);
size_t pkg_size; /* package size in bytes */
struct mailbox_pkg pkg;
};
@ -244,7 +244,7 @@ no_space:
static int
mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *header,
void *data)
void __iomem *data)
{
struct mailbox_msg *mb_msg;
int msg_id;
@ -332,7 +332,7 @@ static int mailbox_get_msg(struct mailbox_channel *mb_chann)
memcpy_fromio((u32 *)&header + 1, read_addr, rest);
read_addr += rest;
ret = mailbox_get_resp(mb_chann, &header, (u32 *)read_addr);
ret = mailbox_get_resp(mb_chann, &header, read_addr);
mailbox_set_headptr(mb_chann, head + msg_size);
/* After update head, it can equal to ringbuf_size. This is expected. */
@ -349,8 +349,6 @@ static irqreturn_t mailbox_irq_handler(int irq, void *p)
trace_mbox_irq_handle(MAILBOX_NAME, irq);
/* Schedule a rx_work to call the callback functions */
queue_work(mb_chann->work_q, &mb_chann->rx_work);
/* Clear IOHUB register */
mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
return IRQ_HANDLED;
}
@ -367,6 +365,9 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
return;
}
again:
mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
while (1) {
/*
* If return is 0, keep consuming next message, until there is
@ -380,10 +381,18 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
if (unlikely(ret)) {
MB_ERR(mb_chann, "Unexpected ret %d, disable irq", ret);
WRITE_ONCE(mb_chann->bad_state, true);
disable_irq(mb_chann->msix_irq);
break;
return;
}
}
/*
* The hardware will not generate interrupt if firmware creates a new
* response right after driver clears interrupt register. Check
* the interrupt register to make sure there is not any new response
* before exiting.
*/
if (mailbox_reg_read(mb_chann, mb_chann->iohub_int_addr))
goto again;
}
int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann,

View File

@ -25,7 +25,7 @@ struct mailbox_channel;
struct xdna_mailbox_msg {
u32 opcode;
void *handle;
int (*notify_cb)(void *handle, const u32 *data, size_t size);
int (*notify_cb)(void *handle, void __iomem *data, size_t size);
u8 *send_data;
size_t send_size;
};

View File

@ -16,7 +16,7 @@
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
int xdna_msg_cb(void *handle, const u32 *data, size_t size)
int xdna_msg_cb(void *handle, void __iomem *data, size_t size)
{
struct xdna_notify *cb_arg = handle;
int ret;
@ -29,9 +29,9 @@ int xdna_msg_cb(void *handle, const u32 *data, size_t size)
goto out;
}
memcpy_fromio(cb_arg->data, data, cb_arg->size);
print_hex_dump_debug("resp data: ", DUMP_PREFIX_OFFSET,
16, 4, data, cb_arg->size, true);
memcpy(cb_arg->data, data, cb_arg->size);
16, 4, cb_arg->data, cb_arg->size, true);
out:
ret = cb_arg->error;
complete(&cb_arg->comp);

View File

@ -35,7 +35,7 @@ struct xdna_notify {
.notify_cb = xdna_msg_cb, \
}
int xdna_msg_cb(void *handle, const u32 *data, size_t size);
int xdna_msg_cb(void *handle, void __iomem *data, size_t size);
int xdna_send_msg_wait(struct amdxdna_dev *xdna, struct mailbox_channel *chann,
struct xdna_mailbox_msg *msg);

View File

@ -4,6 +4,7 @@
*/
#include <linux/debugfs.h>
#include <linux/fault-inject.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_file.h>
@ -397,6 +398,88 @@ 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 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]);
}
return 0;
}
static int priority_bands_fops_open(struct inode *inode, struct file *file)
{
return single_open(file, priority_bands_show, inode->i_private);
}
static ssize_t
priority_bands_fops_write(struct file *file, const char __user *user_buf, size_t size, loff_t *pos)
{
struct seq_file *s = file->private_data;
struct ivpu_device *vdev = s->private;
char buf[64];
u32 grace_period;
u32 process_grace_period;
u32 process_quantum;
u32 band;
int ret;
if (size >= sizeof(buf))
return -EINVAL;
ret = simple_write_to_buffer(buf, sizeof(buf) - 1, pos, user_buf, size);
if (ret < 0)
return ret;
buf[size] = '\0';
ret = sscanf(buf, "%u %u %u %u", &band, &grace_period, &process_grace_period,
&process_quantum);
if (ret != 4)
return -EINVAL;
if (band >= VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT)
return -EINVAL;
vdev->hw->hws.grace_period[band] = grace_period;
vdev->hw->hws.process_grace_period[band] = process_grace_period;
vdev->hw->hws.process_quantum[band] = process_quantum;
return size;
}
static const struct file_operations ivpu_hws_priority_bands_fops = {
.owner = THIS_MODULE,
.open = priority_bands_fops_open,
.write = priority_bands_fops_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
void ivpu_debugfs_init(struct ivpu_device *vdev)
{
struct dentry *debugfs_root = vdev->drm.debugfs_root;
@ -419,6 +502,8 @@ void ivpu_debugfs_init(struct ivpu_device *vdev)
&fw_trace_hw_comp_mask_fops);
debugfs_create_file("fw_trace_level", 0200, debugfs_root, vdev,
&fw_trace_level_fops);
debugfs_create_file("hws_priority_bands", 0200, debugfs_root, vdev,
&ivpu_hws_priority_bands_fops);
debugfs_create_file("reset_engine", 0200, debugfs_root, vdev,
&ivpu_reset_engine_fops);
@ -430,4 +515,8 @@ void ivpu_debugfs_init(struct ivpu_device *vdev)
debugfs_root, vdev, &fw_profiling_freq_fops);
debugfs_create_file("dct", 0644, debugfs_root, vdev, &ivpu_dct_fops);
}
#ifdef CONFIG_FAULT_INJECTION
fault_create_debugfs_attr("fail_hw", debugfs_root, &ivpu_hw_failure);
#endif
}

View File

@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
#include <generated/utsrelease.h>
#include <drm/drm_accel.h>
@ -36,8 +37,6 @@
#define DRIVER_VERSION_STR "1.0.0 " UTS_RELEASE
#endif
static struct lock_class_key submitted_jobs_xa_lock_class_key;
int ivpu_dbg_mask;
module_param_named(dbg_mask, ivpu_dbg_mask, int, 0644);
MODULE_PARM_DESC(dbg_mask, "Driver debug mask. See IVPU_DBG_* macros.");
@ -128,20 +127,18 @@ void ivpu_file_priv_put(struct ivpu_file_priv **link)
kref_put(&file_priv->ref, file_priv_release);
}
static int ivpu_get_capabilities(struct ivpu_device *vdev, struct drm_ivpu_param *args)
bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability)
{
switch (args->index) {
switch (capability) {
case DRM_IVPU_CAP_METRIC_STREAMER:
args->value = 1;
break;
return true;
case DRM_IVPU_CAP_DMA_MEMORY_RANGE:
args->value = 1;
break;
return true;
case DRM_IVPU_CAP_MANAGE_CMDQ:
return vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW;
default:
return -EINVAL;
return false;
}
return 0;
}
static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
@ -201,7 +198,7 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f
args->value = vdev->hw->sku;
break;
case DRM_IVPU_PARAM_CAPABILITIES:
ret = ivpu_get_capabilities(vdev, args);
args->value = ivpu_is_capable(vdev, args->index);
break;
default:
ret = -EINVAL;
@ -310,6 +307,9 @@ static const struct drm_ioctl_desc ivpu_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_GET_DATA, ivpu_ms_get_data_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_STOP, ivpu_ms_stop_ioctl, 0),
DRM_IOCTL_DEF_DRV(IVPU_METRIC_STREAMER_GET_INFO, ivpu_ms_get_info_ioctl, 0),
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),
};
static int ivpu_wait_for_ready(struct ivpu_device *vdev)
@ -421,6 +421,9 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev)
{
ivpu_hw_irq_disable(vdev);
disable_irq(vdev->irq);
cancel_work_sync(&vdev->irq_ipc_work);
cancel_work_sync(&vdev->irq_dct_work);
cancel_work_sync(&vdev->context_abort_work);
ivpu_ipc_disable(vdev);
ivpu_mmu_disable(vdev);
}
@ -453,7 +456,7 @@ static const struct drm_driver driver = {
.postclose = ivpu_postclose,
.gem_create_object = ivpu_gem_create_object,
.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
.gem_prime_import = ivpu_gem_prime_import,
.ioctls = ivpu_drm_ioctls,
.num_ioctls = ARRAY_SIZE(ivpu_drm_ioctls),
@ -465,54 +468,6 @@ static const struct drm_driver driver = {
.major = 1,
};
static void ivpu_context_abort_invalid(struct ivpu_device *vdev)
{
struct ivpu_file_priv *file_priv;
unsigned long ctx_id;
mutex_lock(&vdev->context_list_lock);
xa_for_each(&vdev->context_xa, ctx_id, file_priv) {
if (!file_priv->has_mmu_faults || file_priv->aborted)
continue;
mutex_lock(&file_priv->lock);
ivpu_context_abort_locked(file_priv);
file_priv->aborted = true;
mutex_unlock(&file_priv->lock);
}
mutex_unlock(&vdev->context_list_lock);
}
static irqreturn_t ivpu_irq_thread_handler(int irq, void *arg)
{
struct ivpu_device *vdev = arg;
u8 irq_src;
if (kfifo_is_empty(&vdev->hw->irq.fifo))
return IRQ_NONE;
while (kfifo_get(&vdev->hw->irq.fifo, &irq_src)) {
switch (irq_src) {
case IVPU_HW_IRQ_SRC_IPC:
ivpu_ipc_irq_thread_handler(vdev);
break;
case IVPU_HW_IRQ_SRC_MMU_EVTQ:
ivpu_context_abort_invalid(vdev);
break;
case IVPU_HW_IRQ_SRC_DCT:
ivpu_pm_dct_irq_thread_handler(vdev);
break;
default:
ivpu_err_ratelimited(vdev, "Unknown IRQ source: %u\n", irq_src);
break;
}
}
return IRQ_HANDLED;
}
static int ivpu_irq_init(struct ivpu_device *vdev)
{
struct pci_dev *pdev = to_pci_dev(vdev->drm.dev);
@ -524,12 +479,16 @@ static int ivpu_irq_init(struct ivpu_device *vdev)
return ret;
}
INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn);
INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn);
INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn);
ivpu_irq_handlers_init(vdev);
vdev->irq = pci_irq_vector(pdev, 0);
ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
ivpu_irq_thread_handler, IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler,
IRQF_NO_AUTOEN, DRIVER_NAME, vdev);
if (ret)
ivpu_err(vdev, "Failed to request an IRQ %d\n", ret);
@ -617,7 +576,6 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
xa_init_flags(&vdev->context_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ);
xa_init_flags(&vdev->submitted_jobs_xa, XA_FLAGS_ALLOC1);
xa_init_flags(&vdev->db_xa, XA_FLAGS_ALLOC1);
lockdep_set_class(&vdev->submitted_jobs_xa.xa_lock, &submitted_jobs_xa_lock_class_key);
INIT_LIST_HEAD(&vdev->bo_list);
vdev->db_limit.min = IVPU_MIN_DB;
@ -627,6 +585,10 @@ static int ivpu_dev_init(struct ivpu_device *vdev)
if (ret)
goto err_xa_destroy;
ret = drmm_mutex_init(&vdev->drm, &vdev->submitted_jobs_lock);
if (ret)
goto err_xa_destroy;
ret = drmm_mutex_init(&vdev->drm, &vdev->bo_list_lock);
if (ret)
goto err_xa_destroy;

View File

@ -58,6 +58,7 @@
#define IVPU_PLATFORM_SILICON 0
#define IVPU_PLATFORM_SIMICS 2
#define IVPU_PLATFORM_FPGA 3
#define IVPU_PLATFORM_HSLE 4
#define IVPU_PLATFORM_INVALID 8
#define IVPU_SCHED_MODE_AUTO -1
@ -110,6 +111,7 @@ struct ivpu_wa_table {
bool disable_clock_relinquish;
bool disable_d0i3_msg;
bool wp0_during_power_up;
bool disable_d0i2;
};
struct ivpu_hw_info;
@ -142,9 +144,14 @@ struct ivpu_device {
struct xa_limit db_limit;
u32 db_next;
struct work_struct irq_ipc_work;
struct work_struct irq_dct_work;
struct work_struct context_abort_work;
struct mutex bo_list_lock; /* Protects bo_list */
struct list_head bo_list;
struct mutex submitted_jobs_lock; /* Protects submitted_jobs */
struct xarray submitted_jobs_xa;
struct ivpu_ipc_consumer job_done_consumer;
@ -200,6 +207,9 @@ extern bool ivpu_force_snoop;
#define IVPU_TEST_MODE_MIP_DISABLE BIT(6)
#define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8)
#define IVPU_TEST_MODE_TURBO BIT(9)
#define IVPU_TEST_MODE_CLK_RELINQ_DISABLE BIT(10)
#define IVPU_TEST_MODE_CLK_RELINQ_ENABLE BIT(11)
#define IVPU_TEST_MODE_D0I2_DISABLE BIT(12)
extern int ivpu_test_mode;
struct ivpu_file_priv *ivpu_file_priv_get(struct ivpu_file_priv *file_priv);
@ -208,6 +218,7 @@ void ivpu_file_priv_put(struct ivpu_file_priv **link);
int ivpu_boot(struct ivpu_device *vdev);
int ivpu_shutdown(struct ivpu_device *vdev);
void ivpu_prepare_for_reset(struct ivpu_device *vdev);
bool ivpu_is_capable(struct ivpu_device *vdev, u32 capability);
static inline u8 ivpu_revision(struct ivpu_device *vdev)
{
@ -282,7 +293,8 @@ static inline bool ivpu_is_simics(struct ivpu_device *vdev)
static inline bool ivpu_is_fpga(struct ivpu_device *vdev)
{
return ivpu_get_platform(vdev) == IVPU_PLATFORM_FPGA;
return ivpu_get_platform(vdev) == IVPU_PLATFORM_FPGA ||
ivpu_get_platform(vdev) == IVPU_PLATFORM_HSLE;
}
static inline bool ivpu_is_force_snoop_enabled(struct ivpu_device *vdev)

View File

@ -145,7 +145,10 @@ ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_he
if (ivpu_sched_mode != IVPU_SCHED_MODE_AUTO)
return ivpu_sched_mode;
return VPU_SCHEDULING_MODE_OS;
if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, JSM, 3, 24))
return VPU_SCHEDULING_MODE_OS;
return VPU_SCHEDULING_MODE_HW;
}
static int ivpu_fw_parse(struct ivpu_device *vdev)
@ -531,6 +534,8 @@ static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_
boot_params->d0i3_entry_vpu_ts);
ivpu_dbg(vdev, FW_BOOT, "boot_params.system_time_us = %llu\n",
boot_params->system_time_us);
ivpu_dbg(vdev, FW_BOOT, "boot_params.power_profile = %u\n",
boot_params->power_profile);
}
void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params)
@ -631,6 +636,8 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
boot_params->d0i3_delayed_entry = 1;
boot_params->d0i3_residency_time_us = 0;
boot_params->d0i3_entry_vpu_ts = 0;
if (IVPU_WA(disable_d0i2))
boot_params->power_profile = 1;
boot_params->system_time_us = ktime_to_us(ktime_get_real());
wmb(); /* Flush WC buffers after writing bootparams */

View File

@ -20,6 +20,8 @@
#include "ivpu_mmu.h"
#include "ivpu_mmu_context.h"
MODULE_IMPORT_NS("DMA_BUF");
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)
@ -172,6 +174,47 @@ struct drm_gem_object *ivpu_gem_create_object(struct drm_device *dev, size_t siz
return &bo->base.base;
}
struct drm_gem_object *ivpu_gem_prime_import(struct drm_device *dev,
struct dma_buf *dma_buf)
{
struct device *attach_dev = dev->dev;
struct dma_buf_attachment *attach;
struct sg_table *sgt;
struct drm_gem_object *obj;
int ret;
attach = dma_buf_attach(dma_buf, attach_dev);
if (IS_ERR(attach))
return ERR_CAST(attach);
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);
if (IS_ERR(obj)) {
ret = PTR_ERR(obj);
goto fail_unmap;
}
obj->import_attach = attach;
obj->resv = dma_buf->resv;
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);
return ERR_PTR(ret);
}
static struct ivpu_bo *ivpu_bo_alloc(struct ivpu_device *vdev, u64 size, u32 flags)
{
struct drm_gem_shmem_object *shmem;

View File

@ -28,6 +28,7 @@ int ivpu_bo_pin(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_global(struct ivpu_device *vdev, u64 size, u32 flags);

View File

@ -9,6 +9,16 @@
#include "ivpu_hw_ip.h"
#include <linux/dmi.h>
#include <linux/fault-inject.h>
#include <linux/pm_runtime.h>
#ifdef CONFIG_FAULT_INJECTION
DECLARE_FAULT_ATTR(ivpu_hw_failure);
static char *ivpu_fail_hw;
module_param_named_unsafe(fail_hw, ivpu_fail_hw, charp, 0444);
MODULE_PARM_DESC(fail_hw, "<interval>,<probability>,<space>,<times>");
#endif
static char *platform_to_str(u32 platform)
{
@ -19,43 +29,36 @@ static char *platform_to_str(u32 platform)
return "SIMICS";
case IVPU_PLATFORM_FPGA:
return "FPGA";
case IVPU_PLATFORM_HSLE:
return "HSLE";
default:
return "Invalid platform";
}
}
static const struct dmi_system_id dmi_platform_simulation[] = {
{
.ident = "Intel Simics",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "lnlrvp"),
DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
DMI_MATCH(DMI_BOARD_SERIAL, "123456789"),
},
},
{
.ident = "Intel Simics",
.matches = {
DMI_MATCH(DMI_BOARD_NAME, "Simics"),
},
},
{ }
};
static void platform_init(struct ivpu_device *vdev)
{
if (dmi_check_system(dmi_platform_simulation))
vdev->platform = IVPU_PLATFORM_SIMICS;
else
vdev->platform = IVPU_PLATFORM_SILICON;
int platform = ivpu_hw_btrs_platform_read(vdev);
ivpu_dbg(vdev, MISC, "Platform type: %s (%d)\n",
platform_to_str(vdev->platform), vdev->platform);
ivpu_dbg(vdev, MISC, "Platform type: %s (%d)\n", platform_to_str(platform), platform);
switch (platform) {
case IVPU_PLATFORM_SILICON:
case IVPU_PLATFORM_SIMICS:
case IVPU_PLATFORM_FPGA:
case IVPU_PLATFORM_HSLE:
vdev->platform = platform;
break;
default:
ivpu_err(vdev, "Invalid platform type: %d\n", platform);
break;
}
}
static void wa_init(struct ivpu_device *vdev)
{
vdev->wa.punit_disabled = ivpu_is_fpga(vdev);
vdev->wa.punit_disabled = false;
vdev->wa.clear_runtime_mem = false;
if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL)
@ -65,14 +68,24 @@ static void wa_init(struct ivpu_device *vdev)
ivpu_revision(vdev) < IVPU_HW_IP_REV_LNL_B0)
vdev->wa.disable_clock_relinquish = true;
if (ivpu_test_mode & IVPU_TEST_MODE_CLK_RELINQ_ENABLE)
vdev->wa.disable_clock_relinquish = false;
if (ivpu_test_mode & IVPU_TEST_MODE_CLK_RELINQ_DISABLE)
vdev->wa.disable_clock_relinquish = true;
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
vdev->wa.wp0_during_power_up = true;
if (ivpu_test_mode & IVPU_TEST_MODE_D0I2_DISABLE)
vdev->wa.disable_d0i2 = true;
IVPU_PRINT_WA(punit_disabled);
IVPU_PRINT_WA(clear_runtime_mem);
IVPU_PRINT_WA(interrupt_clear_with_0);
IVPU_PRINT_WA(disable_clock_relinquish);
IVPU_PRINT_WA(wp0_during_power_up);
IVPU_PRINT_WA(disable_d0i2);
}
static void timeouts_init(struct ivpu_device *vdev)
@ -84,12 +97,12 @@ static void timeouts_init(struct ivpu_device *vdev)
vdev->timeout.autosuspend = -1;
vdev->timeout.d0i3_entry_msg = -1;
} else if (ivpu_is_fpga(vdev)) {
vdev->timeout.boot = 100000;
vdev->timeout.jsm = 50000;
vdev->timeout.tdr = 2000000;
vdev->timeout.boot = 50;
vdev->timeout.jsm = 15000;
vdev->timeout.tdr = 30000;
vdev->timeout.autosuspend = -1;
vdev->timeout.d0i3_entry_msg = 500;
vdev->timeout.state_dump_msg = 10;
vdev->timeout.state_dump_msg = 10000;
} else if (ivpu_is_simics(vdev)) {
vdev->timeout.boot = 50;
vdev->timeout.jsm = 500;
@ -110,6 +123,26 @@ static void timeouts_init(struct ivpu_device *vdev)
}
}
static void priority_bands_init(struct ivpu_device *vdev)
{
/* Idle */
vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE] = 0;
vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE] = 50000;
vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE] = 160000;
/* Normal */
vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL] = 50000;
vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL] = 50000;
vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL] = 300000;
/* Focus */
vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS] = 50000;
vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS] = 50000;
vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_FOCUS] = 200000;
/* Realtime */
vdev->hw->hws.grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 0;
vdev->hw->hws.process_grace_period[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 50000;
vdev->hw->hws.process_quantum[VPU_JOB_SCHEDULING_PRIORITY_BAND_REALTIME] = 200000;
}
static void memory_ranges_init(struct ivpu_device *vdev)
{
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) {
@ -248,12 +281,18 @@ int ivpu_hw_init(struct ivpu_device *vdev)
{
ivpu_hw_btrs_info_init(vdev);
ivpu_hw_btrs_freq_ratios_init(vdev);
priority_bands_init(vdev);
memory_ranges_init(vdev);
platform_init(vdev);
wa_init(vdev);
timeouts_init(vdev);
atomic_set(&vdev->hw->firewall_irq_counter, 0);
#ifdef CONFIG_FAULT_INJECTION
if (ivpu_fail_hw)
setup_fault_attr(&ivpu_hw_failure, ivpu_fail_hw);
#endif
return 0;
}
@ -285,8 +324,6 @@ void ivpu_hw_profiling_freq_drive(struct ivpu_device *vdev, bool enable)
void ivpu_irq_handlers_init(struct ivpu_device *vdev)
{
INIT_KFIFO(vdev->hw->irq.fifo);
if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX)
vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_37xx;
else
@ -300,7 +337,6 @@ void ivpu_irq_handlers_init(struct ivpu_device *vdev)
void ivpu_hw_irq_enable(struct ivpu_device *vdev)
{
kfifo_reset(&vdev->hw->irq.fifo);
ivpu_hw_ip_irq_enable(vdev);
ivpu_hw_btrs_irq_enable(vdev);
}
@ -327,9 +363,9 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr)
/* Re-enable global interrupts to re-trigger MSI for pending interrupts */
ivpu_hw_btrs_global_int_enable(vdev);
if (!kfifo_is_empty(&vdev->hw->irq.fifo))
return IRQ_WAKE_THREAD;
if (ip_handled || btrs_handled)
return IRQ_HANDLED;
return IRQ_NONE;
if (!ip_handled && !btrs_handled)
return IRQ_NONE;
pm_runtime_mark_last_busy(vdev->drm.dev);
return IRQ_HANDLED;
}

View File

@ -6,18 +6,10 @@
#ifndef __IVPU_HW_H__
#define __IVPU_HW_H__
#include <linux/kfifo.h>
#include "ivpu_drv.h"
#include "ivpu_hw_btrs.h"
#include "ivpu_hw_ip.h"
#define IVPU_HW_IRQ_FIFO_LENGTH 1024
#define IVPU_HW_IRQ_SRC_IPC 1
#define IVPU_HW_IRQ_SRC_MMU_EVTQ 2
#define IVPU_HW_IRQ_SRC_DCT 3
struct ivpu_addr_range {
resource_size_t start;
resource_size_t end;
@ -27,7 +19,6 @@ struct ivpu_hw_info {
struct {
bool (*btrs_irq_handler)(struct ivpu_device *vdev, int irq);
bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq);
DECLARE_KFIFO(fifo, u8, IVPU_HW_IRQ_FIFO_LENGTH);
} irq;
struct {
struct ivpu_addr_range global;
@ -45,6 +36,11 @@ struct ivpu_hw_info {
u8 pn_ratio;
u32 profiling_freq;
} pll;
struct {
u32 grace_period[VPU_HWS_NUM_PRIORITY_BANDS];
u32 process_quantum[VPU_HWS_NUM_PRIORITY_BANDS];
u32 process_grace_period[VPU_HWS_NUM_PRIORITY_BANDS];
} hws;
u32 tile_fuse;
u32 sku;
u16 config;

View File

@ -630,8 +630,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");
if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_DCT))
ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
queue_work(system_wq, &vdev->irq_dct_work);
}
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, status))
@ -888,3 +887,10 @@ void ivpu_hw_btrs_diagnose_failure(struct ivpu_device *vdev)
else
return diagnose_failure_lnl(vdev);
}
int ivpu_hw_btrs_platform_read(struct ivpu_device *vdev)
{
u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_VPU_STATUS);
return REG_GET_FLD(VPU_HW_BTRS_LNL_VPU_STATUS, PLATFORM, reg);
}

View File

@ -46,5 +46,6 @@ void ivpu_hw_btrs_global_int_disable(struct ivpu_device *vdev);
void ivpu_hw_btrs_irq_enable(struct ivpu_device *vdev);
void ivpu_hw_btrs_irq_disable(struct ivpu_device *vdev);
void ivpu_hw_btrs_diagnose_failure(struct ivpu_device *vdev);
int ivpu_hw_btrs_platform_read(struct ivpu_device *vdev);
#endif /* __IVPU_HW_BTRS_H__ */

View File

@ -86,6 +86,7 @@
#define VPU_HW_BTRS_LNL_VPU_STATUS_POWER_RESOURCE_OWN_ACK_MASK BIT_MASK(7)
#define VPU_HW_BTRS_LNL_VPU_STATUS_PERF_CLK_MASK BIT_MASK(11)
#define VPU_HW_BTRS_LNL_VPU_STATUS_DISABLE_CLK_RELINQUISH_MASK BIT_MASK(12)
#define VPU_HW_BTRS_LNL_VPU_STATUS_PLATFORM_MASK GENMASK(31, 29)
#define VPU_HW_BTRS_LNL_IP_RESET 0x00000160u
#define VPU_HW_BTRS_LNL_IP_RESET_TRIGGER_MASK BIT_MASK(0)

View File

@ -968,14 +968,14 @@ void ivpu_hw_ip_wdt_disable(struct ivpu_device *vdev)
static u32 ipc_rx_count_get_37xx(struct ivpu_device *vdev)
{
u32 count = REGV_RD32_SILENT(VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT);
u32 count = readl(vdev->regv + VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT);
return REG_GET_FLD(VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT, FILL_LEVEL, count);
}
static u32 ipc_rx_count_get_40xx(struct ivpu_device *vdev)
{
u32 count = REGV_RD32_SILENT(VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT);
u32 count = readl(vdev->regv + VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT);
return REG_GET_FLD(VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT, FILL_LEVEL, count);
}

View File

@ -7,6 +7,7 @@
#define __IVPU_HW_REG_IO_H__
#include <linux/bitfield.h>
#include <linux/fault-inject.h>
#include <linux/io.h>
#include <linux/iopoll.h>
@ -16,13 +17,11 @@
#define REG_IO_ERROR 0xffffffff
#define REGB_RD32(reg) ivpu_hw_reg_rd32(vdev, vdev->regb, (reg), #reg, __func__)
#define REGB_RD32_SILENT(reg) readl(vdev->regb + (reg))
#define REGB_RD64(reg) ivpu_hw_reg_rd64(vdev, vdev->regb, (reg), #reg, __func__)
#define REGB_WR32(reg, val) ivpu_hw_reg_wr32(vdev, vdev->regb, (reg), (val), #reg, __func__)
#define REGB_WR64(reg, val) ivpu_hw_reg_wr64(vdev, vdev->regb, (reg), (val), #reg, __func__)
#define REGV_RD32(reg) ivpu_hw_reg_rd32(vdev, vdev->regv, (reg), #reg, __func__)
#define REGV_RD32_SILENT(reg) readl(vdev->regv + (reg))
#define REGV_RD64(reg) ivpu_hw_reg_rd64(vdev, vdev->regv, (reg), #reg, __func__)
#define REGV_WR32(reg, val) ivpu_hw_reg_wr32(vdev, vdev->regv, (reg), (val), #reg, __func__)
#define REGV_WR64(reg, val) ivpu_hw_reg_wr64(vdev, vdev->regv, (reg), (val), #reg, __func__)
@ -47,31 +46,42 @@
#define REG_TEST_FLD_NUM(REG, FLD, num, val) \
((num) == FIELD_GET(REG##_##FLD##_MASK, val))
#define REGB_POLL_FLD(reg, fld, val, timeout_us) \
({ \
u32 var; \
int r; \
ivpu_dbg(vdev, REG, "%s : %s (0x%08x) Polling field %s started (expected 0x%x)\n", \
__func__, #reg, reg, #fld, val); \
r = read_poll_timeout(REGB_RD32_SILENT, var, (FIELD_GET(reg##_##fld##_MASK, var) == (val)),\
REG_POLL_SLEEP_US, timeout_us, false, (reg)); \
ivpu_dbg(vdev, REG, "%s : %s (0x%08x) Polling field %s %s (reg val 0x%08x)\n", \
__func__, #reg, reg, #fld, r ? "ETIMEDOUT" : "OK", var); \
r; \
})
#define REGB_POLL_FLD(reg, fld, exp_fld_val, timeout_us) \
ivpu_hw_reg_poll_fld(vdev, vdev->regb, reg, reg##_##fld##_MASK, \
FIELD_PREP(reg##_##fld##_MASK, exp_fld_val), timeout_us, \
__func__, #reg, #fld)
#define REGV_POLL_FLD(reg, fld, val, timeout_us) \
({ \
u32 var; \
int r; \
ivpu_dbg(vdev, REG, "%s : %s (0x%08x) Polling field %s started (expected 0x%x)\n", \
__func__, #reg, reg, #fld, val); \
r = read_poll_timeout(REGV_RD32_SILENT, var, (FIELD_GET(reg##_##fld##_MASK, var) == (val)),\
REG_POLL_SLEEP_US, timeout_us, false, (reg)); \
ivpu_dbg(vdev, REG, "%s : %s (0x%08x) Polling field %s %s (reg val 0x%08x)\n", \
__func__, #reg, reg, #fld, r ? "ETIMEDOUT" : "OK", var); \
r; \
})
#define REGV_POLL_FLD(reg, fld, exp_fld_val, timeout_us) \
ivpu_hw_reg_poll_fld(vdev, vdev->regv, reg, reg##_##fld##_MASK, \
FIELD_PREP(reg##_##fld##_MASK, exp_fld_val), timeout_us, \
__func__, #reg, #fld)
extern struct fault_attr ivpu_hw_failure;
static inline int __must_check
ivpu_hw_reg_poll_fld(struct ivpu_device *vdev, void __iomem *base,
u32 reg_offset, u32 reg_mask, u32 exp_masked_val, u32 timeout_us,
const char *func_name, const char *reg_name, const char *fld_name)
{
u32 reg_val;
int ret;
ivpu_dbg(vdev, REG, "%s : %s (0x%08x) POLL %s started (exp_val 0x%x)\n",
func_name, reg_name, reg_offset, fld_name, exp_masked_val);
ret = read_poll_timeout(readl, reg_val, (reg_val & reg_mask) == exp_masked_val,
REG_POLL_SLEEP_US, timeout_us, false, base + reg_offset);
#ifdef CONFIG_FAULT_INJECTION
if (should_fail(&ivpu_hw_failure, 1))
ret = -ETIMEDOUT;
#endif
ivpu_dbg(vdev, REG, "%s : %s (0x%08x) POLL %s %s (reg_val 0x%08x)\n",
func_name, reg_name, reg_offset, fld_name, ret ? "ETIMEDOUT" : "OK", reg_val);
return ret;
}
static inline u32
ivpu_hw_reg_rd32(struct ivpu_device *vdev, void __iomem *base, u32 reg,

View File

@ -459,13 +459,12 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev)
}
}
if (!list_empty(&ipc->cb_msg_list))
if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_IPC))
ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
queue_work(system_wq, &vdev->irq_ipc_work);
}
void ivpu_ipc_irq_thread_handler(struct ivpu_device *vdev)
void ivpu_ipc_irq_work_fn(struct work_struct *work)
{
struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work);
struct ivpu_ipc_info *ipc = vdev->ipc;
struct ivpu_ipc_rx_msg *rx_msg, *r;
struct list_head cb_msg_list;

View File

@ -90,7 +90,7 @@ void ivpu_ipc_disable(struct ivpu_device *vdev);
void ivpu_ipc_reset(struct ivpu_device *vdev);
void ivpu_ipc_irq_handler(struct ivpu_device *vdev);
void ivpu_ipc_irq_thread_handler(struct ivpu_device *vdev);
void ivpu_ipc_irq_work_fn(struct work_struct *work);
void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
u32 channel, ivpu_ipc_rx_callback_t callback);

View File

@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/highmem.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
#include <uapi/drm/ivpu_accel.h>
@ -17,6 +18,7 @@
#include "ivpu_ipc.h"
#include "ivpu_job.h"
#include "ivpu_jsm_msg.h"
#include "ivpu_mmu.h"
#include "ivpu_pm.h"
#include "ivpu_trace.h"
#include "vpu_boot_api.h"
@ -83,23 +85,9 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
if (!cmdq)
return NULL;
ret = xa_alloc_cyclic(&vdev->db_xa, &cmdq->db_id, NULL, vdev->db_limit, &vdev->db_next,
GFP_KERNEL);
if (ret < 0) {
ivpu_err(vdev, "Failed to allocate doorbell id: %d\n", ret);
goto err_free_cmdq;
}
ret = xa_alloc_cyclic(&file_priv->cmdq_xa, &cmdq->id, cmdq, file_priv->cmdq_limit,
&file_priv->cmdq_id_next, GFP_KERNEL);
if (ret < 0) {
ivpu_err(vdev, "Failed to allocate command queue id: %d\n", ret);
goto err_erase_db_xa;
}
cmdq->mem = ivpu_bo_create_global(vdev, SZ_4K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE);
if (!cmdq->mem)
goto err_erase_cmdq_xa;
goto err_free_cmdq;
ret = ivpu_preemption_buffers_create(vdev, file_priv, cmdq);
if (ret)
@ -107,10 +95,6 @@ static struct ivpu_cmdq *ivpu_cmdq_alloc(struct ivpu_file_priv *file_priv)
return cmdq;
err_erase_cmdq_xa:
xa_erase(&file_priv->cmdq_xa, cmdq->id);
err_erase_db_xa:
xa_erase(&vdev->db_xa, cmdq->db_id);
err_free_cmdq:
kfree(cmdq);
return NULL;
@ -118,15 +102,44 @@ err_free_cmdq:
static void ivpu_cmdq_free(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
if (!cmdq)
return;
ivpu_preemption_buffers_free(file_priv->vdev, file_priv, cmdq);
ivpu_bo_free(cmdq->mem);
xa_erase(&file_priv->vdev->db_xa, cmdq->db_id);
kfree(cmdq);
}
static struct ivpu_cmdq *ivpu_cmdq_create(struct ivpu_file_priv *file_priv, u8 priority,
bool is_legacy)
{
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_cmdq *cmdq = NULL;
int ret;
lockdep_assert_held(&file_priv->lock);
cmdq = ivpu_cmdq_alloc(file_priv);
if (!cmdq) {
ivpu_err(vdev, "Failed to allocate command queue\n");
return NULL;
}
cmdq->priority = priority;
cmdq->is_legacy = is_legacy;
ret = xa_alloc_cyclic(&file_priv->cmdq_xa, &cmdq->id, cmdq, file_priv->cmdq_limit,
&file_priv->cmdq_id_next, GFP_KERNEL);
if (ret < 0) {
ivpu_err(vdev, "Failed to allocate command queue ID: %d\n", ret);
goto err_free_cmdq;
}
ivpu_dbg(vdev, JOB, "Command queue %d created, ctx %d\n", cmdq->id, file_priv->ctx.id);
return cmdq;
err_free_cmdq:
ivpu_cmdq_free(file_priv, cmdq);
return NULL;
}
static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u16 engine,
u8 priority)
{
@ -152,6 +165,13 @@ static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *
struct ivpu_device *vdev = file_priv->vdev;
int ret;
ret = xa_alloc_cyclic(&vdev->db_xa, &cmdq->db_id, NULL, vdev->db_limit, &vdev->db_next,
GFP_KERNEL);
if (ret < 0) {
ivpu_err(vdev, "Failed to allocate doorbell ID: %d\n", ret);
return ret;
}
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
ret = ivpu_jsm_hws_register_db(vdev, file_priv->ctx.id, cmdq->id, cmdq->db_id,
cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
@ -160,41 +180,52 @@ static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *
cmdq->mem->vpu_addr, ivpu_bo_size(cmdq->mem));
if (!ret)
ivpu_dbg(vdev, JOB, "DB %d registered to cmdq %d ctx %d\n",
cmdq->db_id, cmdq->id, file_priv->ctx.id);
ivpu_dbg(vdev, JOB, "DB %d registered to cmdq %d ctx %d priority %d\n",
cmdq->db_id, cmdq->id, file_priv->ctx.id, cmdq->priority);
else
xa_erase(&vdev->db_xa, cmdq->db_id);
return ret;
}
static int
ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u8 priority)
static void ivpu_cmdq_jobq_init(struct ivpu_device *vdev, struct vpu_job_queue *jobq)
{
jobq->header.engine_idx = VPU_ENGINE_COMPUTE;
jobq->header.head = 0;
jobq->header.tail = 0;
if (ivpu_test_mode & IVPU_TEST_MODE_TURBO) {
ivpu_dbg(vdev, JOB, "Turbo mode enabled");
jobq->header.flags = VPU_JOB_QUEUE_FLAGS_TURBO_MODE;
}
wmb(); /* Flush WC buffer for jobq->header */
}
static inline u32 ivpu_cmdq_get_entry_count(struct ivpu_cmdq *cmdq)
{
size_t size = ivpu_bo_size(cmdq->mem) - sizeof(struct vpu_job_queue_header);
return size / sizeof(struct vpu_job_queue_entry);
}
static int ivpu_cmdq_register(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
struct ivpu_device *vdev = file_priv->vdev;
struct vpu_job_queue_header *jobq_header;
int ret;
lockdep_assert_held(&file_priv->lock);
if (cmdq->db_registered)
if (cmdq->db_id)
return 0;
cmdq->entry_count = (u32)((ivpu_bo_size(cmdq->mem) - sizeof(struct vpu_job_queue_header)) /
sizeof(struct vpu_job_queue_entry));
cmdq->entry_count = ivpu_cmdq_get_entry_count(cmdq);
cmdq->jobq = (struct vpu_job_queue *)ivpu_bo_vaddr(cmdq->mem);
jobq_header = &cmdq->jobq->header;
jobq_header->engine_idx = VPU_ENGINE_COMPUTE;
jobq_header->head = 0;
jobq_header->tail = 0;
if (ivpu_test_mode & IVPU_TEST_MODE_TURBO) {
ivpu_dbg(vdev, JOB, "Turbo mode enabled");
jobq_header->flags = VPU_JOB_QUEUE_FLAGS_TURBO_MODE;
}
wmb(); /* Flush WC buffer for jobq->header */
ivpu_cmdq_jobq_init(vdev, cmdq->jobq);
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
ret = ivpu_hws_cmdq_init(file_priv, cmdq, VPU_ENGINE_COMPUTE, priority);
ret = ivpu_hws_cmdq_init(file_priv, cmdq, VPU_ENGINE_COMPUTE, cmdq->priority);
if (ret)
return ret;
}
@ -203,58 +234,83 @@ ivpu_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq, u8 prio
if (ret)
return ret;
cmdq->db_registered = true;
return 0;
}
static int ivpu_cmdq_fini(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
static int ivpu_cmdq_unregister(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
struct ivpu_device *vdev = file_priv->vdev;
int ret;
lockdep_assert_held(&file_priv->lock);
if (!cmdq->db_registered)
if (!cmdq->db_id)
return 0;
cmdq->db_registered = false;
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) {
ret = ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id);
if (!ret)
ivpu_dbg(vdev, JOB, "Command queue %d destroyed\n", cmdq->id);
ivpu_dbg(vdev, JOB, "Command queue %d destroyed, ctx %d\n",
cmdq->id, file_priv->ctx.id);
}
ret = ivpu_jsm_unregister_db(vdev, cmdq->db_id);
if (!ret)
ivpu_dbg(vdev, JOB, "DB %d unregistered\n", cmdq->db_id);
xa_erase(&file_priv->vdev->db_xa, cmdq->db_id);
cmdq->db_id = 0;
return 0;
}
static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u8 priority)
static inline u8 ivpu_job_to_jsm_priority(u8 priority)
{
if (priority == DRM_IVPU_JOB_PRIORITY_DEFAULT)
return VPU_JOB_SCHEDULING_PRIORITY_BAND_NORMAL;
return priority - 1;
}
static void ivpu_cmdq_destroy(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq)
{
ivpu_cmdq_unregister(file_priv, cmdq);
xa_erase(&file_priv->cmdq_xa, cmdq->id);
ivpu_cmdq_free(file_priv, cmdq);
}
static struct ivpu_cmdq *ivpu_cmdq_acquire_legacy(struct ivpu_file_priv *file_priv, u8 priority)
{
struct ivpu_cmdq *cmdq;
unsigned long cmdq_id;
int ret;
unsigned long id;
lockdep_assert_held(&file_priv->lock);
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
if (cmdq->priority == priority)
xa_for_each(&file_priv->cmdq_xa, id, cmdq)
if (cmdq->is_legacy && cmdq->priority == priority)
break;
if (!cmdq) {
cmdq = ivpu_cmdq_alloc(file_priv);
cmdq = ivpu_cmdq_create(file_priv, priority, true);
if (!cmdq)
return NULL;
cmdq->priority = priority;
}
ret = ivpu_cmdq_init(file_priv, cmdq, priority);
if (ret)
return cmdq;
}
static struct ivpu_cmdq *ivpu_cmdq_acquire(struct ivpu_file_priv *file_priv, u32 cmdq_id)
{
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_cmdq *cmdq;
lockdep_assert_held(&file_priv->lock);
cmdq = xa_load(&file_priv->cmdq_xa, cmdq_id);
if (!cmdq) {
ivpu_warn_ratelimited(vdev, "Failed to find command queue with ID: %u\n", cmdq_id);
return NULL;
}
return cmdq;
}
@ -266,11 +322,8 @@ void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv)
lockdep_assert_held(&file_priv->lock);
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) {
xa_erase(&file_priv->cmdq_xa, cmdq_id);
ivpu_cmdq_fini(file_priv, cmdq);
ivpu_cmdq_free(file_priv, cmdq);
}
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
ivpu_cmdq_destroy(file_priv, cmdq);
}
/*
@ -286,8 +339,10 @@ static void ivpu_cmdq_reset(struct ivpu_file_priv *file_priv)
mutex_lock(&file_priv->lock);
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
cmdq->db_registered = false;
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq) {
xa_erase(&file_priv->vdev->db_xa, cmdq->db_id);
cmdq->db_id = 0;
}
mutex_unlock(&file_priv->lock);
}
@ -305,25 +360,24 @@ void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev)
mutex_unlock(&vdev->context_list_lock);
}
static void ivpu_cmdq_fini_all(struct ivpu_file_priv *file_priv)
{
struct ivpu_cmdq *cmdq;
unsigned long cmdq_id;
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
ivpu_cmdq_fini(file_priv, cmdq);
}
void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv)
{
struct ivpu_device *vdev = file_priv->vdev;
struct ivpu_cmdq *cmdq;
unsigned long cmdq_id;
lockdep_assert_held(&file_priv->lock);
ivpu_dbg(vdev, JOB, "Context ID: %u abort\n", file_priv->ctx.id);
ivpu_cmdq_fini_all(file_priv);
xa_for_each(&file_priv->cmdq_xa, cmdq_id, cmdq)
ivpu_cmdq_unregister(file_priv, cmdq);
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_OS)
ivpu_jsm_context_release(vdev, file_priv->ctx.id);
ivpu_mmu_disable_ssid_events(vdev, file_priv->ctx.id);
file_priv->aborted = true;
}
static int ivpu_cmdq_push_job(struct ivpu_cmdq *cmdq, struct ivpu_job *job)
@ -462,16 +516,14 @@ static struct ivpu_job *ivpu_job_remove_from_submitted_jobs(struct ivpu_device *
{
struct ivpu_job *job;
xa_lock(&vdev->submitted_jobs_xa);
job = __xa_erase(&vdev->submitted_jobs_xa, job_id);
lockdep_assert_held(&vdev->submitted_jobs_lock);
job = xa_erase(&vdev->submitted_jobs_xa, job_id);
if (xa_empty(&vdev->submitted_jobs_xa) && job) {
vdev->busy_time = ktime_add(ktime_sub(ktime_get(), vdev->busy_start_ts),
vdev->busy_time);
}
xa_unlock(&vdev->submitted_jobs_xa);
return job;
}
@ -479,6 +531,28 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
{
struct ivpu_job *job;
lockdep_assert_held(&vdev->submitted_jobs_lock);
job = xa_load(&vdev->submitted_jobs_xa, job_id);
if (!job)
return -ENOENT;
if (job_status == VPU_JSM_STATUS_MVNCI_CONTEXT_VIOLATION_HW) {
guard(mutex)(&job->file_priv->lock);
if (job->file_priv->has_mmu_faults)
return 0;
/*
* Mark context as faulty and defer destruction of the job to jobs abort thread
* handler to synchronize between both faults and jobs returning context violation
* status and ensure both are handled in the same way
*/
job->file_priv->has_mmu_faults = true;
queue_work(system_wq, &vdev->context_abort_work);
return 0;
}
job = ivpu_job_remove_from_submitted_jobs(vdev, job_id);
if (!job)
return -ENOENT;
@ -497,6 +571,10 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32
ivpu_stop_job_timeout_detection(vdev);
ivpu_rpm_put(vdev);
if (!xa_empty(&vdev->submitted_jobs_xa))
ivpu_start_job_timeout_detection(vdev);
return 0;
}
@ -505,11 +583,29 @@ void ivpu_jobs_abort_all(struct ivpu_device *vdev)
struct ivpu_job *job;
unsigned long id;
mutex_lock(&vdev->submitted_jobs_lock);
xa_for_each(&vdev->submitted_jobs_xa, id, job)
ivpu_job_signal_and_destroy(vdev, id, DRM_IVPU_JOB_STATUS_ABORTED);
mutex_unlock(&vdev->submitted_jobs_lock);
}
static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id)
{
struct ivpu_job *job;
unsigned long id;
mutex_lock(&vdev->submitted_jobs_lock);
xa_for_each(&vdev->submitted_jobs_xa, id, job)
if (job->file_priv->ctx.id == ctx_id && job->cmdq_id == cmdq_id)
ivpu_job_signal_and_destroy(vdev, id, DRM_IVPU_JOB_STATUS_ABORTED);
mutex_unlock(&vdev->submitted_jobs_lock);
}
static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id)
{
struct ivpu_file_priv *file_priv = job->file_priv;
struct ivpu_device *vdev = job->vdev;
@ -521,25 +617,35 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
if (ret < 0)
return ret;
mutex_lock(&vdev->submitted_jobs_lock);
mutex_lock(&file_priv->lock);
cmdq = ivpu_cmdq_acquire(file_priv, priority);
if (cmdq_id == 0)
cmdq = ivpu_cmdq_acquire_legacy(file_priv, priority);
else
cmdq = ivpu_cmdq_acquire(file_priv, cmdq_id);
if (!cmdq) {
ivpu_warn_ratelimited(vdev, "Failed to get job queue, ctx %d engine %d prio %d\n",
file_priv->ctx.id, job->engine_idx, priority);
ivpu_warn_ratelimited(vdev, "Failed to get job queue, ctx %d\n", file_priv->ctx.id);
ret = -EINVAL;
goto err_unlock_file_priv;
goto err_unlock;
}
xa_lock(&vdev->submitted_jobs_xa);
ret = ivpu_cmdq_register(file_priv, cmdq);
if (ret) {
ivpu_err(vdev, "Failed to register command queue: %d\n", ret);
goto err_unlock;
}
job->cmdq_id = cmdq->id;
is_first_job = xa_empty(&vdev->submitted_jobs_xa);
ret = __xa_alloc_cyclic(&vdev->submitted_jobs_xa, &job->job_id, job, file_priv->job_limit,
&file_priv->job_id_next, GFP_KERNEL);
ret = xa_alloc_cyclic(&vdev->submitted_jobs_xa, &job->job_id, job, file_priv->job_limit,
&file_priv->job_id_next, GFP_KERNEL);
if (ret < 0) {
ivpu_dbg(vdev, JOB, "Too many active jobs in ctx %d\n",
file_priv->ctx.id);
ret = -EBUSY;
goto err_unlock_submitted_jobs_xa;
goto err_unlock;
}
ret = ivpu_cmdq_push_job(cmdq, job);
@ -559,23 +665,23 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority)
trace_job("submit", job);
ivpu_dbg(vdev, JOB, "Job submitted: id %3u ctx %2d engine %d prio %d addr 0x%llx next %d\n",
job->job_id, file_priv->ctx.id, job->engine_idx, priority,
job->job_id, file_priv->ctx.id, job->engine_idx, cmdq->priority,
job->cmd_buf_vpu_addr, cmdq->jobq->header.tail);
xa_unlock(&vdev->submitted_jobs_xa);
mutex_unlock(&file_priv->lock);
if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_HW))
if (unlikely(ivpu_test_mode & IVPU_TEST_MODE_NULL_HW)) {
ivpu_job_signal_and_destroy(vdev, job->job_id, VPU_JSM_STATUS_SUCCESS);
}
mutex_unlock(&vdev->submitted_jobs_lock);
return 0;
err_erase_xa:
__xa_erase(&vdev->submitted_jobs_xa, job->job_id);
err_unlock_submitted_jobs_xa:
xa_unlock(&vdev->submitted_jobs_xa);
err_unlock_file_priv:
xa_erase(&vdev->submitted_jobs_xa, job->job_id);
err_unlock:
mutex_unlock(&vdev->submitted_jobs_lock);
mutex_unlock(&file_priv->lock);
ivpu_rpm_put(vdev);
return ret;
@ -585,7 +691,7 @@ static int
ivpu_job_prepare_bos_for_submit(struct drm_file *file, struct ivpu_job *job, u32 *buf_handles,
u32 buf_count, u32 commands_offset)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct ivpu_file_priv *file_priv = job->file_priv;
struct ivpu_device *vdev = file_priv->vdev;
struct ww_acquire_ctx acquire_ctx;
enum dma_resv_usage usage;
@ -647,49 +753,20 @@ unlock_reservations:
return ret;
}
static inline u8 ivpu_job_to_hws_priority(struct ivpu_file_priv *file_priv, u8 priority)
static int ivpu_submit(struct drm_file *file, struct ivpu_file_priv *file_priv, u32 cmdq_id,
u32 buffer_count, u32 engine, void __user *buffers_ptr, u32 cmds_offset,
u8 priority)
{
if (priority == DRM_IVPU_JOB_PRIORITY_DEFAULT)
return DRM_IVPU_JOB_PRIORITY_NORMAL;
return priority - 1;
}
int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct ivpu_device *vdev = file_priv->vdev;
struct drm_ivpu_submit *params = data;
struct ivpu_job *job;
u32 *buf_handles;
int idx, ret;
u8 priority;
if (params->engine != DRM_IVPU_ENGINE_COMPUTE)
return -EINVAL;
if (params->priority > DRM_IVPU_JOB_PRIORITY_REALTIME)
return -EINVAL;
if (params->buffer_count == 0 || params->buffer_count > JOB_MAX_BUFFER_COUNT)
return -EINVAL;
if (!IS_ALIGNED(params->commands_offset, 8))
return -EINVAL;
if (!file_priv->ctx.id)
return -EINVAL;
if (file_priv->has_mmu_faults)
return -EBADFD;
buf_handles = kcalloc(params->buffer_count, sizeof(u32), GFP_KERNEL);
buf_handles = kcalloc(buffer_count, sizeof(u32), GFP_KERNEL);
if (!buf_handles)
return -ENOMEM;
ret = copy_from_user(buf_handles,
(void __user *)params->buffers_ptr,
params->buffer_count * sizeof(u32));
ret = copy_from_user(buf_handles, buffers_ptr, buffer_count * sizeof(u32));
if (ret) {
ret = -EFAULT;
goto err_free_handles;
@ -700,27 +777,23 @@ int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
goto err_free_handles;
}
ivpu_dbg(vdev, JOB, "Submit ioctl: ctx %u buf_count %u\n",
file_priv->ctx.id, params->buffer_count);
ivpu_dbg(vdev, JOB, "Submit ioctl: ctx %u buf_count %u\n", file_priv->ctx.id, buffer_count);
job = ivpu_job_create(file_priv, params->engine, params->buffer_count);
job = ivpu_job_create(file_priv, engine, buffer_count);
if (!job) {
ivpu_err(vdev, "Failed to create job\n");
ret = -ENOMEM;
goto err_exit_dev;
}
ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, params->buffer_count,
params->commands_offset);
ret = ivpu_job_prepare_bos_for_submit(file, job, buf_handles, buffer_count, cmds_offset);
if (ret) {
ivpu_err(vdev, "Failed to prepare job: %d\n", ret);
goto err_destroy_job;
}
priority = ivpu_job_to_hws_priority(file_priv, params->priority);
down_read(&vdev->pm->reset_lock);
ret = ivpu_job_submit(job, priority);
ret = ivpu_job_submit(job, priority, cmdq_id);
up_read(&vdev->pm->reset_lock);
if (ret)
goto err_signal_fence;
@ -740,12 +813,122 @@ err_free_handles:
return ret;
}
int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct drm_ivpu_submit *args = data;
u8 priority;
if (args->engine != DRM_IVPU_ENGINE_COMPUTE)
return -EINVAL;
if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME)
return -EINVAL;
if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT)
return -EINVAL;
if (!IS_ALIGNED(args->commands_offset, 8))
return -EINVAL;
if (!file_priv->ctx.id)
return -EINVAL;
if (file_priv->has_mmu_faults)
return -EBADFD;
priority = ivpu_job_to_jsm_priority(args->priority);
return ivpu_submit(file, file_priv, 0, args->buffer_count, args->engine,
(void __user *)args->buffers_ptr, args->commands_offset, priority);
}
int ivpu_cmdq_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct drm_ivpu_cmdq_submit *args = data;
if (!ivpu_is_capable(file_priv->vdev, DRM_IVPU_CAP_MANAGE_CMDQ))
return -ENODEV;
if (args->cmdq_id < IVPU_CMDQ_MIN_ID || args->cmdq_id > IVPU_CMDQ_MAX_ID)
return -EINVAL;
if (args->buffer_count == 0 || args->buffer_count > JOB_MAX_BUFFER_COUNT)
return -EINVAL;
if (!IS_ALIGNED(args->commands_offset, 8))
return -EINVAL;
if (!file_priv->ctx.id)
return -EINVAL;
if (file_priv->has_mmu_faults)
return -EBADFD;
return ivpu_submit(file, file_priv, args->cmdq_id, args->buffer_count, VPU_ENGINE_COMPUTE,
(void __user *)args->buffers_ptr, args->commands_offset, 0);
}
int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct drm_ivpu_cmdq_create *args = data;
struct ivpu_cmdq *cmdq;
if (!ivpu_is_capable(file_priv->vdev, DRM_IVPU_CAP_MANAGE_CMDQ))
return -ENODEV;
if (args->priority > DRM_IVPU_JOB_PRIORITY_REALTIME)
return -EINVAL;
mutex_lock(&file_priv->lock);
cmdq = ivpu_cmdq_create(file_priv, ivpu_job_to_jsm_priority(args->priority), false);
if (cmdq)
args->cmdq_id = cmdq->id;
mutex_unlock(&file_priv->lock);
return cmdq ? 0 : -ENOMEM;
}
int ivpu_cmdq_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
{
struct ivpu_file_priv *file_priv = file->driver_priv;
struct ivpu_device *vdev = file_priv->vdev;
struct drm_ivpu_cmdq_destroy *args = data;
struct ivpu_cmdq *cmdq;
u32 cmdq_id;
int ret;
if (!ivpu_is_capable(vdev, DRM_IVPU_CAP_MANAGE_CMDQ))
return -ENODEV;
mutex_lock(&file_priv->lock);
cmdq = xa_load(&file_priv->cmdq_xa, args->cmdq_id);
if (!cmdq || cmdq->is_legacy) {
ret = -ENOENT;
goto err_unlock;
}
cmdq_id = cmdq->id;
ivpu_cmdq_destroy(file_priv, cmdq);
mutex_unlock(&file_priv->lock);
ivpu_cmdq_abort_all_jobs(vdev, file_priv->ctx.id, cmdq_id);
return 0;
err_unlock:
mutex_unlock(&file_priv->lock);
return ret;
}
static void
ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
struct vpu_jsm_msg *jsm_msg)
{
struct vpu_ipc_msg_payload_job_done *payload;
int ret;
if (!jsm_msg) {
ivpu_err(vdev, "IPC message has no JSM payload\n");
@ -758,9 +941,10 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr,
}
payload = (struct vpu_ipc_msg_payload_job_done *)&jsm_msg->payload;
ret = ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
if (!ret && !xa_empty(&vdev->submitted_jobs_xa))
ivpu_start_job_timeout_detection(vdev);
mutex_lock(&vdev->submitted_jobs_lock);
ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status);
mutex_unlock(&vdev->submitted_jobs_lock);
}
void ivpu_job_done_consumer_init(struct ivpu_device *vdev)
@ -773,3 +957,55 @@ void ivpu_job_done_consumer_fini(struct ivpu_device *vdev)
{
ivpu_ipc_consumer_del(vdev, &vdev->job_done_consumer);
}
void ivpu_context_abort_work_fn(struct work_struct *work)
{
struct ivpu_device *vdev = container_of(work, struct ivpu_device, context_abort_work);
struct ivpu_file_priv *file_priv;
struct ivpu_job *job;
unsigned long ctx_id;
unsigned long id;
if (drm_WARN_ON(&vdev->drm, pm_runtime_get_if_active(vdev->drm.dev) <= 0))
return;
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
ivpu_jsm_reset_engine(vdev, 0);
mutex_lock(&vdev->context_list_lock);
xa_for_each(&vdev->context_xa, ctx_id, file_priv) {
if (!file_priv->has_mmu_faults || file_priv->aborted)
continue;
mutex_lock(&file_priv->lock);
ivpu_context_abort_locked(file_priv);
mutex_unlock(&file_priv->lock);
}
mutex_unlock(&vdev->context_list_lock);
/*
* We will not receive new MMU event interrupts until existing events are discarded
* however, we want to discard these events only after aborting the faulty context
* to avoid generating new faults from that context
*/
ivpu_mmu_discard_events(vdev);
if (vdev->fw->sched_mode != VPU_SCHEDULING_MODE_HW)
goto runtime_put;
ivpu_jsm_hws_resume_engine(vdev, 0);
/*
* In hardware scheduling mode NPU already has stopped processing jobs
* and won't send us any further notifications, thus we have to free job related resources
* and notify userspace
*/
mutex_lock(&vdev->submitted_jobs_lock);
xa_for_each(&vdev->submitted_jobs_xa, id, job)
if (job->file_priv->aborted)
ivpu_job_signal_and_destroy(vdev, job->job_id, DRM_IVPU_JOB_STATUS_ABORTED);
mutex_unlock(&vdev->submitted_jobs_lock);
runtime_put:
pm_runtime_mark_last_busy(vdev->drm.dev);
pm_runtime_put_autosuspend(vdev->drm.dev);
}

View File

@ -30,8 +30,8 @@ struct ivpu_cmdq {
u32 entry_count;
u32 id;
u32 db_id;
bool db_registered;
u8 priority;
bool is_legacy;
};
/**
@ -51,6 +51,7 @@ struct ivpu_job {
struct ivpu_file_priv *file_priv;
struct dma_fence *done_fence;
u64 cmd_buf_vpu_addr;
u32 cmdq_id;
u32 job_id;
u32 engine_idx;
size_t bo_count;
@ -58,14 +59,19 @@ struct ivpu_job {
};
int ivpu_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_cmdq_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_cmdq_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
int ivpu_cmdq_submit_ioctl(struct drm_device *dev, void *data, struct drm_file *file);
void ivpu_context_abort_locked(struct ivpu_file_priv *file_priv);
void ivpu_cmdq_release_all_locked(struct ivpu_file_priv *file_priv);
void ivpu_cmdq_reset_all_contexts(struct ivpu_device *vdev);
void ivpu_cmdq_abort_all_jobs(struct ivpu_device *vdev, u32 ctx_id, u32 cmdq_id);
void ivpu_job_done_consumer_init(struct ivpu_device *vdev);
void ivpu_job_done_consumer_fini(struct ivpu_device *vdev);
void ivpu_context_abort_work_fn(struct work_struct *work);
void ivpu_jobs_abort_all(struct ivpu_device *vdev);

View File

@ -7,6 +7,7 @@
#include "ivpu_hw.h"
#include "ivpu_ipc.h"
#include "ivpu_jsm_msg.h"
#include "vpu_jsm_api.h"
const char *ivpu_jsm_msg_type_to_str(enum vpu_ipc_msg_type type)
{
@ -407,26 +408,18 @@ int ivpu_jsm_hws_setup_priority_bands(struct ivpu_device *vdev)
{
struct vpu_jsm_msg req = { .type = VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP };
struct vpu_jsm_msg resp;
struct ivpu_hw_info *hw = vdev->hw;
struct vpu_ipc_msg_payload_hws_priority_band_setup *setup =
&req.payload.hws_priority_band_setup;
int ret;
/* Idle */
req.payload.hws_priority_band_setup.grace_period[0] = 0;
req.payload.hws_priority_band_setup.process_grace_period[0] = 50000;
req.payload.hws_priority_band_setup.process_quantum[0] = 160000;
/* Normal */
req.payload.hws_priority_band_setup.grace_period[1] = 50000;
req.payload.hws_priority_band_setup.process_grace_period[1] = 50000;
req.payload.hws_priority_band_setup.process_quantum[1] = 300000;
/* Focus */
req.payload.hws_priority_band_setup.grace_period[2] = 50000;
req.payload.hws_priority_band_setup.process_grace_period[2] = 50000;
req.payload.hws_priority_band_setup.process_quantum[2] = 200000;
/* Realtime */
req.payload.hws_priority_band_setup.grace_period[3] = 0;
req.payload.hws_priority_band_setup.process_grace_period[3] = 50000;
req.payload.hws_priority_band_setup.process_quantum[3] = 200000;
req.payload.hws_priority_band_setup.normal_band_percentage = 10;
for (int band = VPU_JOB_SCHEDULING_PRIORITY_BAND_IDLE;
band < VPU_JOB_SCHEDULING_PRIORITY_BAND_COUNT; band++) {
setup->grace_period[band] = hw->hws.grace_period[band];
setup->process_grace_period[band] = hw->hws.process_grace_period[band];
setup->process_quantum[band] = hw->hws.process_quantum[band];
}
setup->normal_band_percentage = 10;
ret = ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_SET_PRIORITY_BAND_SETUP_RSP,
&resp, VPU_IPC_CHAN_ASYNC_CMD, vdev->timeout.jsm);

View File

@ -20,6 +20,12 @@
#define IVPU_MMU_REG_CR0 0x00200020u
#define IVPU_MMU_REG_CR0ACK 0x00200024u
#define IVPU_MMU_REG_CR0ACK_VAL_MASK GENMASK(31, 0)
#define IVPU_MMU_REG_CR0_ATSCHK_MASK BIT(4)
#define IVPU_MMU_REG_CR0_CMDQEN_MASK BIT(3)
#define IVPU_MMU_REG_CR0_EVTQEN_MASK BIT(2)
#define IVPU_MMU_REG_CR0_PRIQEN_MASK BIT(1)
#define IVPU_MMU_REG_CR0_SMMUEN_MASK BIT(0)
#define IVPU_MMU_REG_CR1 0x00200028u
#define IVPU_MMU_REG_CR2 0x0020002cu
#define IVPU_MMU_REG_IRQ_CTRL 0x00200050u
@ -141,12 +147,6 @@
#define IVPU_MMU_IRQ_EVTQ_EN BIT(2)
#define IVPU_MMU_IRQ_GERROR_EN BIT(0)
#define IVPU_MMU_CR0_ATSCHK BIT(4)
#define IVPU_MMU_CR0_CMDQEN BIT(3)
#define IVPU_MMU_CR0_EVTQEN BIT(2)
#define IVPU_MMU_CR0_PRIQEN BIT(1)
#define IVPU_MMU_CR0_SMMUEN BIT(0)
#define IVPU_MMU_CR1_TABLE_SH GENMASK(11, 10)
#define IVPU_MMU_CR1_TABLE_OC GENMASK(9, 8)
#define IVPU_MMU_CR1_TABLE_IC GENMASK(7, 6)
@ -596,7 +596,7 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev)
REGV_WR32(IVPU_MMU_REG_CMDQ_PROD, 0);
REGV_WR32(IVPU_MMU_REG_CMDQ_CONS, 0);
val = IVPU_MMU_CR0_CMDQEN;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, CMDQEN, 0);
ret = ivpu_mmu_reg_write_cr0(vdev, val);
if (ret)
return ret;
@ -617,12 +617,12 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev)
REGV_WR32(IVPU_MMU_REG_EVTQ_PROD_SEC, 0);
REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, 0);
val |= IVPU_MMU_CR0_EVTQEN;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
ret = ivpu_mmu_reg_write_cr0(vdev, val);
if (ret)
return ret;
val |= IVPU_MMU_CR0_ATSCHK;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, ATSCHK, val);
ret = ivpu_mmu_reg_write_cr0(vdev, val);
if (ret)
return ret;
@ -631,7 +631,7 @@ static int ivpu_mmu_reset(struct ivpu_device *vdev)
if (ret)
return ret;
val |= IVPU_MMU_CR0_SMMUEN;
val = REG_SET_FLD(IVPU_MMU_REG_CR0, SMMUEN, val);
return ivpu_mmu_reg_write_cr0(vdev, val);
}
@ -725,8 +725,8 @@ static int ivpu_mmu_cdtab_entry_set(struct ivpu_device *vdev, u32 ssid, u64 cd_d
cd[2] = 0;
cd[3] = 0x0000000000007444;
/* For global context generate memory fault on VPU */
if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID)
/* For global and reserved contexts generate memory fault on VPU */
if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID || ssid == IVPU_RESERVED_CONTEXT_MMU_SSID)
cd[0] |= IVPU_MMU_CD_0_A;
if (valid)
@ -870,28 +870,107 @@ static u32 *ivpu_mmu_get_event(struct ivpu_device *vdev)
return evt;
}
static int ivpu_mmu_evtq_set(struct ivpu_device *vdev, bool enable)
{
u32 val = REGV_RD32(IVPU_MMU_REG_CR0);
if (enable)
val = REG_SET_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
else
val = REG_CLR_FLD(IVPU_MMU_REG_CR0, EVTQEN, val);
REGV_WR32(IVPU_MMU_REG_CR0, val);
return REGV_POLL_FLD(IVPU_MMU_REG_CR0ACK, VAL, val, IVPU_MMU_REG_TIMEOUT_US);
}
static int ivpu_mmu_evtq_enable(struct ivpu_device *vdev)
{
return ivpu_mmu_evtq_set(vdev, true);
}
static int ivpu_mmu_evtq_disable(struct ivpu_device *vdev)
{
return ivpu_mmu_evtq_set(vdev, false);
}
void ivpu_mmu_discard_events(struct ivpu_device *vdev)
{
struct ivpu_mmu_info *mmu = vdev->mmu;
mutex_lock(&mmu->lock);
/*
* Disable event queue (stop MMU from updating the producer)
* to allow synchronization of consumer and producer indexes
*/
ivpu_mmu_evtq_disable(vdev);
vdev->mmu->evtq.cons = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
vdev->mmu->evtq.prod = REGV_RD32(IVPU_MMU_REG_EVTQ_PROD_SEC);
ivpu_mmu_evtq_enable(vdev);
drm_WARN_ON_ONCE(&vdev->drm, vdev->mmu->evtq.cons != vdev->mmu->evtq.prod);
mutex_unlock(&mmu->lock);
}
int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid)
{
struct ivpu_mmu_info *mmu = vdev->mmu;
struct ivpu_mmu_cdtab *cdtab = &mmu->cdtab;
u64 *entry;
u64 val;
if (ssid > IVPU_MMU_CDTAB_ENT_COUNT)
return -EINVAL;
mutex_lock(&mmu->lock);
entry = cdtab->base + (ssid * IVPU_MMU_CDTAB_ENT_SIZE);
val = READ_ONCE(entry[0]);
val &= ~IVPU_MMU_CD_0_R;
WRITE_ONCE(entry[0], val);
if (!ivpu_is_force_snoop_enabled(vdev))
clflush_cache_range(entry, IVPU_MMU_CDTAB_ENT_SIZE);
ivpu_mmu_cmdq_write_cfgi_all(vdev);
ivpu_mmu_cmdq_sync(vdev);
mutex_unlock(&mmu->lock);
return 0;
}
void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev)
{
struct ivpu_file_priv *file_priv;
u32 *event;
u32 ssid;
ivpu_dbg(vdev, IRQ, "MMU event queue\n");
while ((event = ivpu_mmu_get_event(vdev)) != NULL) {
ivpu_mmu_dump_event(vdev, event);
ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, event[0]);
if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID) {
while ((event = ivpu_mmu_get_event(vdev))) {
ssid = FIELD_GET(IVPU_MMU_EVT_SSID_MASK, *event);
if (ssid == IVPU_GLOBAL_CONTEXT_MMU_SSID ||
ssid == IVPU_RESERVED_CONTEXT_MMU_SSID) {
ivpu_mmu_dump_event(vdev, event);
ivpu_pm_trigger_recovery(vdev, "MMU event");
return;
}
ivpu_mmu_user_context_mark_invalid(vdev, ssid);
REGV_WR32(IVPU_MMU_REG_EVTQ_CONS_SEC, vdev->mmu->evtq.cons);
file_priv = xa_load(&vdev->context_xa, ssid);
if (file_priv) {
if (!READ_ONCE(file_priv->has_mmu_faults)) {
ivpu_mmu_dump_event(vdev, event);
WRITE_ONCE(file_priv->has_mmu_faults, true);
}
}
}
if (!kfifo_put(&vdev->hw->irq.fifo, IVPU_HW_IRQ_SRC_MMU_EVTQ))
ivpu_err_ratelimited(vdev, "IRQ FIFO full\n");
queue_work(system_wq, &vdev->context_abort_work);
}
void ivpu_mmu_evtq_dump(struct ivpu_device *vdev)

View File

@ -47,5 +47,7 @@ int ivpu_mmu_invalidate_tlb(struct ivpu_device *vdev, u16 ssid);
void ivpu_mmu_irq_evtq_handler(struct ivpu_device *vdev);
void ivpu_mmu_irq_gerr_handler(struct ivpu_device *vdev);
void ivpu_mmu_evtq_dump(struct ivpu_device *vdev);
void ivpu_mmu_discard_events(struct ivpu_device *vdev);
int ivpu_mmu_disable_ssid_events(struct ivpu_device *vdev, u32 ssid);
#endif /* __IVPU_MMU_H__ */

View File

@ -635,16 +635,3 @@ void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev)
ivpu_mmu_cd_clear(vdev, vdev->rctx.id);
ivpu_mmu_context_fini(vdev, &vdev->rctx);
}
void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid)
{
struct ivpu_file_priv *file_priv;
xa_lock(&vdev->context_xa);
file_priv = xa_load(&vdev->context_xa, ssid);
if (file_priv)
file_priv->has_mmu_faults = true;
xa_unlock(&vdev->context_xa);
}

View File

@ -37,8 +37,6 @@ void ivpu_mmu_global_context_fini(struct ivpu_device *vdev);
int ivpu_mmu_reserved_context_init(struct ivpu_device *vdev);
void ivpu_mmu_reserved_context_fini(struct ivpu_device *vdev);
void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid);
int ivpu_mmu_context_insert_node(struct ivpu_mmu_context *ctx, const struct ivpu_addr_range *range,
u64 size, struct drm_mm_node *node);
void ivpu_mmu_context_remove_node(struct ivpu_mmu_context *ctx, struct drm_mm_node *node);

View File

@ -177,16 +177,11 @@ void ivpu_pm_trigger_recovery(struct ivpu_device *vdev, const char *reason)
return;
}
if (ivpu_is_fpga(vdev)) {
ivpu_err(vdev, "Recovery not available on FPGA\n");
return;
}
/* Trigger recovery if it's not in progress */
if (atomic_cmpxchg(&vdev->pm->reset_pending, 0, 1) == 0) {
ivpu_hw_diagnose_failure(vdev);
ivpu_hw_irq_disable(vdev); /* Disable IRQ early to protect from IRQ storm */
queue_work(system_long_wq, &vdev->pm->recovery_work);
queue_work(system_unbound_wq, &vdev->pm->recovery_work);
}
}
@ -462,8 +457,9 @@ int ivpu_pm_dct_disable(struct ivpu_device *vdev)
return 0;
}
void ivpu_pm_dct_irq_thread_handler(struct ivpu_device *vdev)
void ivpu_pm_irq_dct_work_fn(struct work_struct *work)
{
struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_dct_work);
bool enable;
int ret;

View File

@ -45,6 +45,6 @@ void ivpu_stop_job_timeout_detection(struct ivpu_device *vdev);
int ivpu_pm_dct_init(struct ivpu_device *vdev);
int ivpu_pm_dct_enable(struct ivpu_device *vdev, u8 active_percent);
int ivpu_pm_dct_disable(struct ivpu_device *vdev);
void ivpu_pm_dct_irq_thread_handler(struct ivpu_device *vdev);
void ivpu_pm_irq_dct_work_fn(struct work_struct *work);
#endif /* __IVPU_PM_H__ */

View File

@ -7,11 +7,14 @@
#include <linux/err.h>
#include "ivpu_drv.h"
#include "ivpu_gem.h"
#include "ivpu_fw.h"
#include "ivpu_hw.h"
#include "ivpu_sysfs.h"
/*
/**
* DOC: npu_busy_time_us
*
* npu_busy_time_us is the time that the device spent executing jobs.
* The time is counted when and only when there are jobs submitted to firmware.
*
@ -30,17 +33,42 @@ npu_busy_time_us_show(struct device *dev, struct device_attribute *attr, char *b
struct ivpu_device *vdev = to_ivpu_device(drm);
ktime_t total, now = 0;
xa_lock(&vdev->submitted_jobs_xa);
mutex_lock(&vdev->submitted_jobs_lock);
total = vdev->busy_time;
if (!xa_empty(&vdev->submitted_jobs_xa))
now = ktime_sub(ktime_get(), vdev->busy_start_ts);
xa_unlock(&vdev->submitted_jobs_xa);
mutex_unlock(&vdev->submitted_jobs_lock);
return sysfs_emit(buf, "%lld\n", ktime_to_us(ktime_add(total, now)));
}
static DEVICE_ATTR_RO(npu_busy_time_us);
/**
* DOC: npu_memory_utilization
*
* The npu_memory_utilization is used to report in bytes a current NPU memory utilization.
*
*/
static ssize_t
npu_memory_utilization_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct ivpu_device *vdev = to_ivpu_device(drm);
struct ivpu_bo *bo;
u64 total_npu_memory = 0;
mutex_lock(&vdev->bo_list_lock);
list_for_each_entry(bo, &vdev->bo_list, bo_list_node)
total_npu_memory += bo->base.base.size;
mutex_unlock(&vdev->bo_list_lock);
return sysfs_emit(buf, "%lld\n", total_npu_memory);
}
static DEVICE_ATTR_RO(npu_memory_utilization);
/**
* DOC: sched_mode
*
@ -64,6 +92,7 @@ static DEVICE_ATTR_RO(sched_mode);
static struct attribute *ivpu_dev_attrs[] = {
&dev_attr_npu_busy_time_us.attr,
&dev_attr_npu_memory_utilization.attr,
&dev_attr_sched_mode.attr,
NULL,
};

View File

@ -20,6 +20,11 @@ static unsigned int mhi_timeout_ms = 2000; /* 2 sec default */
module_param(mhi_timeout_ms, uint, 0600);
MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value");
static const char *fw_image_paths[FAMILY_MAX] = {
[FAMILY_AIC100] = "qcom/aic100/sbl.bin",
[FAMILY_AIC200] = "qcom/aic200/sbl.bin",
};
static const struct mhi_channel_config aic100_channels[] = {
{
.name = "QAIC_LOOPBACK",
@ -439,6 +444,297 @@ static const struct mhi_channel_config aic100_channels[] = {
},
};
static const struct mhi_channel_config aic200_channels[] = {
{
.name = "QAIC_LOOPBACK",
.num = 0,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_LOOPBACK",
.num = 1,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_SAHARA",
.num = 2,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_SBL,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_SAHARA",
.num = 3,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_SBL,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_SSR",
.num = 6,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_SSR",
.num = 7,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_CONTROL",
.num = 10,
.num_elements = 128,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_CONTROL",
.num = 11,
.num_elements = 128,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_LOGGING",
.num = 12,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_SBL,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_LOGGING",
.num = 13,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_SBL,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_STATUS",
.num = 14,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_STATUS",
.num = 15,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_TELEMETRY",
.num = 16,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_TELEMETRY",
.num = 17,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_TIMESYNC_PERIODIC",
.num = 22,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "QAIC_TIMESYNC_PERIODIC",
.num = 23,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "IPCR",
.num = 24,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_TO_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = false,
.wake_capable = false,
},
{
.name = "IPCR",
.num = 25,
.num_elements = 32,
.local_elements = 0,
.event_ring = 0,
.dir = DMA_FROM_DEVICE,
.ee_mask = MHI_CH_EE_AMSS,
.pollcfg = 0,
.doorbell = MHI_DB_BRST_DISABLE,
.lpm_notify = false,
.offload_channel = false,
.doorbell_mode_switch = false,
.auto_queue = true,
.wake_capable = false,
},
};
static struct mhi_event_config aic100_events[] = {
{
.num_elements = 32,
@ -454,16 +750,44 @@ static struct mhi_event_config aic100_events[] = {
},
};
static struct mhi_controller_config aic100_config = {
.max_channels = 128,
.timeout_ms = 0, /* controlled by mhi_timeout */
.buf_len = 0,
.num_channels = ARRAY_SIZE(aic100_channels),
.ch_cfg = aic100_channels,
.num_events = ARRAY_SIZE(aic100_events),
.event_cfg = aic100_events,
.use_bounce_buf = false,
.m2_no_db = false,
static struct mhi_event_config aic200_events[] = {
{
.num_elements = 32,
.irq_moderation_ms = 0,
.irq = 0,
.channel = U32_MAX,
.priority = 1,
.mode = MHI_DB_BRST_DISABLE,
.data_type = MHI_ER_CTRL,
.hardware_event = false,
.client_managed = false,
.offload_channel = false,
},
};
static struct mhi_controller_config mhi_cntrl_configs[] = {
[FAMILY_AIC100] = {
.max_channels = 128,
.timeout_ms = 0, /* controlled by mhi_timeout */
.buf_len = 0,
.num_channels = ARRAY_SIZE(aic100_channels),
.ch_cfg = aic100_channels,
.num_events = ARRAY_SIZE(aic100_events),
.event_cfg = aic100_events,
.use_bounce_buf = false,
.m2_no_db = false,
},
[FAMILY_AIC200] = {
.max_channels = 128,
.timeout_ms = 0, /* controlled by mhi_timeout */
.buf_len = 0,
.num_channels = ARRAY_SIZE(aic200_channels),
.ch_cfg = aic200_channels,
.num_events = ARRAY_SIZE(aic200_events),
.event_cfg = aic200_events,
.use_bounce_buf = false,
.m2_no_db = false,
},
};
static int mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 *out)
@ -545,8 +869,9 @@ static int mhi_reset_and_async_power_up(struct mhi_controller *mhi_cntrl)
}
struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar,
int mhi_irq, bool shared_msi)
int mhi_irq, bool shared_msi, int family)
{
struct mhi_controller_config mhi_config = mhi_cntrl_configs[family];
struct mhi_controller *mhi_cntrl;
int ret;
@ -581,11 +906,18 @@ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, voi
if (shared_msi) /* MSI shared with data path, no IRQF_NO_SUSPEND */
mhi_cntrl->irq_flags = IRQF_SHARED;
mhi_cntrl->fw_image = "qcom/aic100/sbl.bin";
mhi_cntrl->fw_image = fw_image_paths[family];
if (family == FAMILY_AIC200) {
mhi_cntrl->name = "AIC200";
mhi_cntrl->seg_len = SZ_512K;
} else {
mhi_cntrl->name = "AIC100";
}
/* use latest configured timeout */
aic100_config.timeout_ms = mhi_timeout_ms;
ret = mhi_register_controller(mhi_cntrl, &aic100_config);
mhi_config.timeout_ms = mhi_timeout_ms;
ret = mhi_register_controller(mhi_cntrl, &mhi_config);
if (ret) {
pci_err(pci_dev, "mhi_register_controller failed %d\n", ret);
return ERR_PTR(ret);

View File

@ -8,7 +8,7 @@
#define MHICONTROLLERQAIC_H_
struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, void __iomem *mhi_bar,
int mhi_irq, bool shared_msi);
int mhi_irq, bool shared_msi, int family);
void qaic_mhi_free_controller(struct mhi_controller *mhi_cntrl, bool link_up);
void qaic_mhi_start_reset(struct mhi_controller *mhi_cntrl);
void qaic_mhi_reset_done(struct mhi_controller *mhi_cntrl);

View File

@ -32,6 +32,12 @@
#define to_accel_kdev(qddev) (to_drm(qddev)->accel->kdev) /* Return Linux device of accel node */
#define to_qaic_device(dev) (to_qaic_drm_device((dev))->qdev)
enum aic_families {
FAMILY_AIC100,
FAMILY_AIC200,
FAMILY_MAX,
};
enum __packed dev_states {
/* Device is offline or will be very soon */
QAIC_OFFLINE,
@ -113,10 +119,10 @@ struct qaic_device {
struct pci_dev *pdev;
/* Req. ID of request that will be queued next in MHI control device */
u32 next_seq_num;
/* Base address of bar 0 */
void __iomem *bar_0;
/* Base address of bar 2 */
void __iomem *bar_2;
/* Base address of the MHI bar */
void __iomem *bar_mhi;
/* Base address of the DBCs bar */
void __iomem *bar_dbc;
/* Controller structure for MHI devices */
struct mhi_controller *mhi_cntrl;
/* MHI control channel device */

View File

@ -34,13 +34,46 @@
MODULE_IMPORT_NS("DMA_BUF");
#define PCI_DEV_AIC080 0xa080
#define PCI_DEV_AIC100 0xa100
#define PCI_DEVICE_ID_QCOM_AIC080 0xa080
#define PCI_DEVICE_ID_QCOM_AIC100 0xa100
#define PCI_DEVICE_ID_QCOM_AIC200 0xa110
#define QAIC_NAME "qaic"
#define QAIC_DESC "Qualcomm Cloud AI Accelerators"
#define CNTL_MAJOR 5
#define CNTL_MINOR 0
struct qaic_device_config {
/* Indicates the AIC family the device belongs to */
int family;
/* A bitmask representing the available BARs */
int bar_mask;
/* An index value used to identify the MHI controller BAR */
unsigned int mhi_bar_idx;
/* An index value used to identify the DBCs BAR */
unsigned int dbc_bar_idx;
};
static const struct qaic_device_config aic080_config = {
.family = FAMILY_AIC100,
.bar_mask = BIT(0) | BIT(2) | BIT(4),
.mhi_bar_idx = 0,
.dbc_bar_idx = 2,
};
static const struct qaic_device_config aic100_config = {
.family = FAMILY_AIC100,
.bar_mask = BIT(0) | BIT(2) | BIT(4),
.mhi_bar_idx = 0,
.dbc_bar_idx = 2,
};
static const struct qaic_device_config aic200_config = {
.family = FAMILY_AIC200,
.bar_mask = BIT(0) | BIT(1) | BIT(2) | BIT(4),
.mhi_bar_idx = 1,
.dbc_bar_idx = 2,
};
bool datapath_polling;
module_param(datapath_polling, bool, 0400);
MODULE_PARM_DESC(datapath_polling, "Operate the datapath in polling mode");
@ -352,7 +385,8 @@ void qaic_dev_reset_clean_local_state(struct qaic_device *qdev)
release_dbc(qdev, i);
}
static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_device_id *id)
static struct qaic_device *create_qdev(struct pci_dev *pdev,
const struct qaic_device_config *config)
{
struct device *dev = &pdev->dev;
struct qaic_drm_device *qddev;
@ -365,12 +399,10 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_de
return NULL;
qdev->dev_state = QAIC_OFFLINE;
if (id->device == PCI_DEV_AIC080 || id->device == PCI_DEV_AIC100) {
qdev->num_dbc = 16;
qdev->dbc = devm_kcalloc(dev, qdev->num_dbc, sizeof(*qdev->dbc), GFP_KERNEL);
if (!qdev->dbc)
return NULL;
}
qdev->num_dbc = 16;
qdev->dbc = devm_kcalloc(dev, qdev->num_dbc, sizeof(*qdev->dbc), GFP_KERNEL);
if (!qdev->dbc)
return NULL;
qddev = devm_drm_dev_alloc(&pdev->dev, &qaic_accel_driver, struct qaic_drm_device, drm);
if (IS_ERR(qddev))
@ -426,17 +458,18 @@ static struct qaic_device *create_qdev(struct pci_dev *pdev, const struct pci_de
return qdev;
}
static int init_pci(struct qaic_device *qdev, struct pci_dev *pdev)
static int init_pci(struct qaic_device *qdev, struct pci_dev *pdev,
const struct qaic_device_config *config)
{
int bars;
int ret;
bars = pci_select_bars(pdev, IORESOURCE_MEM);
bars = pci_select_bars(pdev, IORESOURCE_MEM) & 0x3f;
/* make sure the device has the expected BARs */
if (bars != (BIT(0) | BIT(2) | BIT(4))) {
pci_dbg(pdev, "%s: expected BARs 0, 2, and 4 not found in device. Found 0x%x\n",
__func__, bars);
if (bars != config->bar_mask) {
pci_dbg(pdev, "%s: expected BARs %#x not found in device. Found %#x\n",
__func__, config->bar_mask, bars);
return -EINVAL;
}
@ -449,13 +482,13 @@ static int init_pci(struct qaic_device *qdev, struct pci_dev *pdev)
return ret;
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
qdev->bar_0 = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
if (IS_ERR(qdev->bar_0))
return PTR_ERR(qdev->bar_0);
qdev->bar_mhi = devm_ioremap_resource(&pdev->dev, &pdev->resource[config->mhi_bar_idx]);
if (IS_ERR(qdev->bar_mhi))
return PTR_ERR(qdev->bar_mhi);
qdev->bar_2 = devm_ioremap_resource(&pdev->dev, &pdev->resource[2]);
if (IS_ERR(qdev->bar_2))
return PTR_ERR(qdev->bar_2);
qdev->bar_dbc = devm_ioremap_resource(&pdev->dev, &pdev->resource[config->dbc_bar_idx]);
if (IS_ERR(qdev->bar_dbc))
return PTR_ERR(qdev->bar_dbc);
/* Managed release since we use pcim_enable_device above */
pci_set_master(pdev);
@ -465,14 +498,15 @@ static int init_pci(struct qaic_device *qdev, struct pci_dev *pdev)
static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
{
int irq_count = qdev->num_dbc + 1;
int mhi_irq;
int ret;
int i;
/* Managed release since we use pcim_enable_device */
ret = pci_alloc_irq_vectors(pdev, 32, 32, PCI_IRQ_MSI);
ret = pci_alloc_irq_vectors(pdev, irq_count, irq_count, PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (ret == -ENOSPC) {
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (ret < 0)
return ret;
@ -485,7 +519,8 @@ static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
* interrupted, it shouldn't race with itself.
*/
qdev->single_msi = true;
pci_info(pdev, "Allocating 32 MSIs failed, operating in 1 MSI mode. Performance may be impacted.\n");
pci_info(pdev, "Allocating %d MSIs failed, operating in 1 MSI mode. Performance may be impacted.\n",
irq_count);
} else if (ret < 0) {
return ret;
}
@ -515,21 +550,22 @@ static int init_msi(struct qaic_device *qdev, struct pci_dev *pdev)
static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct qaic_device_config *config = (struct qaic_device_config *)id->driver_data;
struct qaic_device *qdev;
int mhi_irq;
int ret;
int i;
qdev = create_qdev(pdev, id);
qdev = create_qdev(pdev, config);
if (!qdev)
return -ENOMEM;
ret = init_pci(qdev, pdev);
ret = init_pci(qdev, pdev, config);
if (ret)
return ret;
for (i = 0; i < qdev->num_dbc; ++i)
qdev->dbc[i].dbc_base = qdev->bar_2 + QAIC_DBC_OFF(i);
qdev->dbc[i].dbc_base = qdev->bar_dbc + QAIC_DBC_OFF(i);
mhi_irq = init_msi(qdev, pdev);
if (mhi_irq < 0)
@ -539,8 +575,8 @@ static int qaic_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret)
return ret;
qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_0, mhi_irq,
qdev->single_msi);
qdev->mhi_cntrl = qaic_mhi_register_controller(pdev, qdev->bar_mhi, mhi_irq,
qdev->single_msi, config->family);
if (IS_ERR(qdev->mhi_cntrl)) {
ret = PTR_ERR(qdev->mhi_cntrl);
qaic_destroy_drm_device(qdev, QAIC_NO_PARTITION);
@ -607,8 +643,9 @@ static struct mhi_driver qaic_mhi_driver = {
};
static const struct pci_device_id qaic_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, PCI_DEV_AIC080), },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, PCI_DEV_AIC100), },
{ PCI_DEVICE_DATA(QCOM, AIC080, (kernel_ulong_t)&aic080_config), },
{ PCI_DEVICE_DATA(QCOM, AIC100, (kernel_ulong_t)&aic100_config), },
{ PCI_DEVICE_DATA(QCOM, AIC200, (kernel_ulong_t)&aic200_config), },
{ }
};
MODULE_DEVICE_TABLE(pci, qaic_ids);

View File

@ -201,7 +201,7 @@ static int qaic_timesync_probe(struct mhi_device *mhi_dev, const struct mhi_devi
goto free_sync_msg;
/* Qtimer register pointer */
mqtsdev->qtimer_addr = qdev->bar_0 + QTIMER_REG_OFFSET;
mqtsdev->qtimer_addr = qdev->bar_mhi + QTIMER_REG_OFFSET;
timer_setup(timer, qaic_timesync_timer, 0);
timer->expires = jiffies + msecs_to_jiffies(timesync_delay_ms);
add_timer(timer);

View File

@ -160,7 +160,7 @@ struct sahara_context {
struct work_struct fw_work;
struct work_struct dump_work;
struct mhi_device *mhi_dev;
const char **image_table;
const char * const *image_table;
u32 table_size;
u32 active_image_id;
const struct firmware *firmware;
@ -177,7 +177,7 @@ struct sahara_context {
bool is_mem_dump_mode;
};
static const char *aic100_image_table[] = {
static const char * const aic100_image_table[] = {
[1] = "qcom/aic100/fw1.bin",
[2] = "qcom/aic100/fw2.bin",
[4] = "qcom/aic100/fw4.bin",
@ -188,6 +188,34 @@ static const char *aic100_image_table[] = {
[10] = "qcom/aic100/fw10.bin",
};
static const char * const aic200_image_table[] = {
[5] = "qcom/aic200/uefi.elf",
[12] = "qcom/aic200/aic200-nsp.bin",
[23] = "qcom/aic200/aop.mbn",
[32] = "qcom/aic200/tz.mbn",
[33] = "qcom/aic200/hypvm.mbn",
[39] = "qcom/aic200/aic200_abl.elf",
[40] = "qcom/aic200/apdp.mbn",
[41] = "qcom/aic200/devcfg.mbn",
[42] = "qcom/aic200/sec.elf",
[43] = "qcom/aic200/aic200-hlos.elf",
[49] = "qcom/aic200/shrm.elf",
[50] = "qcom/aic200/cpucp.elf",
[51] = "qcom/aic200/aop_devcfg.mbn",
[57] = "qcom/aic200/cpucp_dtbs.elf",
[62] = "qcom/aic200/uefi_dtbs.elf",
[63] = "qcom/aic200/xbl_ac_config.mbn",
[64] = "qcom/aic200/tz_ac_config.mbn",
[65] = "qcom/aic200/hyp_ac_config.mbn",
[66] = "qcom/aic200/pdp.elf",
[67] = "qcom/aic200/pdp_cdb.elf",
[68] = "qcom/aic200/sdi.mbn",
[69] = "qcom/aic200/dcd.mbn",
[73] = "qcom/aic200/gearvm.mbn",
[74] = "qcom/aic200/sti.bin",
[75] = "qcom/aic200/pvs.bin",
};
static int sahara_find_image(struct sahara_context *context, u32 image_id)
{
int ret;
@ -748,8 +776,15 @@ static int sahara_mhi_probe(struct mhi_device *mhi_dev, const struct mhi_device_
context->mhi_dev = mhi_dev;
INIT_WORK(&context->fw_work, sahara_processing);
INIT_WORK(&context->dump_work, sahara_dump_processing);
context->image_table = aic100_image_table;
context->table_size = ARRAY_SIZE(aic100_image_table);
if (!strcmp(mhi_dev->mhi_cntrl->name, "AIC200")) {
context->image_table = aic200_image_table;
context->table_size = ARRAY_SIZE(aic200_image_table);
} else {
context->image_table = aic100_image_table;
context->table_size = ARRAY_SIZE(aic100_image_table);
}
context->active_image_id = SAHARA_IMAGE_ID_NONE;
dev_set_drvdata(&mhi_dev->dev, context);

View File

@ -569,11 +569,28 @@ void component_master_del(struct device *parent,
}
EXPORT_SYMBOL_GPL(component_master_del);
bool component_master_is_bound(struct device *parent,
const struct component_master_ops *ops)
{
struct aggregate_device *adev;
guard(mutex)(&component_mutex);
adev = __aggregate_find(parent, ops);
if (!adev)
return 0;
return adev->bound;
}
EXPORT_SYMBOL_GPL(component_master_is_bound);
static void component_unbind(struct component *component,
struct aggregate_device *adev, void *data)
{
WARN_ON(!component->bound);
dev_dbg(adev->parent, "unbinding %s component %p (ops %ps)\n",
dev_name(component->dev), component, component->ops);
if (component->ops && component->ops->unbind)
component->ops->unbind(component->dev, adev->parent, data);
component->bound = false;

View File

@ -576,7 +576,10 @@ void *devres_open_group(struct device *dev, void *id, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(devres_open_group);
/* Find devres group with ID @id. If @id is NULL, look for the latest. */
/*
* Find devres group with ID @id. If @id is NULL, look for the latest open
* group.
*/
static struct devres_group *find_group(struct device *dev, void *id)
{
struct devres_node *node;
@ -687,6 +690,13 @@ int devres_release_group(struct device *dev, void *id)
spin_unlock_irqrestore(&dev->devres_lock, flags);
release_nodes(dev, &todo);
} else if (list_empty(&dev->devres_head)) {
/*
* dev is probably dying via devres_release_all(): groups
* have already been removed and are on the process of
* being released - don't touch and don't warn.
*/
spin_unlock_irqrestore(&dev->devres_lock, flags);
} else {
WARN_ON(1);
spin_unlock_irqrestore(&dev->devres_lock, flags);

View File

@ -177,6 +177,36 @@ int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic)
}
EXPORT_SYMBOL_GPL(mhi_download_rddm_image);
static void mhi_fw_load_error_dump(struct mhi_controller *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
void __iomem *base = mhi_cntrl->bhi;
int ret, i;
u32 val;
struct {
char *name;
u32 offset;
} error_reg[] = {
{ "ERROR_CODE", BHI_ERRCODE },
{ "ERROR_DBG1", BHI_ERRDBG1 },
{ "ERROR_DBG2", BHI_ERRDBG2 },
{ "ERROR_DBG3", BHI_ERRDBG3 },
{ NULL },
};
read_lock_bh(pm_lock);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
for (i = 0; error_reg[i].name; i++) {
ret = mhi_read_reg(mhi_cntrl, base, error_reg[i].offset, &val);
if (ret)
break;
dev_err(dev, "Reg: %s value: 0x%x\n", error_reg[i].name, val);
}
}
read_unlock_bh(pm_lock);
}
static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
const struct mhi_buf *mhi_buf)
{
@ -226,24 +256,13 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl,
}
static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
dma_addr_t dma_addr,
size_t size)
const struct mhi_buf *mhi_buf)
{
u32 tx_status, val, session_id;
int i, ret;
void __iomem *base = mhi_cntrl->bhi;
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct {
char *name;
u32 offset;
} error_reg[] = {
{ "ERROR_CODE", BHI_ERRCODE },
{ "ERROR_DBG1", BHI_ERRDBG1 },
{ "ERROR_DBG2", BHI_ERRDBG2 },
{ "ERROR_DBG3", BHI_ERRDBG3 },
{ NULL },
};
rwlock_t *pm_lock = &mhi_cntrl->pm_lock;
void __iomem *base = mhi_cntrl->bhi;
u32 tx_status, session_id;
int ret;
read_lock_bh(pm_lock);
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
@ -255,11 +274,9 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
dev_dbg(dev, "Starting image download via BHI. Session ID: %u\n",
session_id);
mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH,
upper_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW,
lower_32_bits(dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, size);
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, upper_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_LOW, lower_32_bits(mhi_buf->dma_addr));
mhi_write_reg(mhi_cntrl, base, BHI_IMGSIZE, mhi_buf->len);
mhi_write_reg(mhi_cntrl, base, BHI_IMGTXDB, session_id);
read_unlock_bh(pm_lock);
@ -274,18 +291,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl,
if (tx_status == BHI_STATUS_ERROR) {
dev_err(dev, "Image transfer failed\n");
read_lock_bh(pm_lock);
if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) {
for (i = 0; error_reg[i].name; i++) {
ret = mhi_read_reg(mhi_cntrl, base,
error_reg[i].offset, &val);
if (ret)
break;
dev_err(dev, "Reg: %s value: 0x%x\n",
error_reg[i].name, val);
}
}
read_unlock_bh(pm_lock);
mhi_fw_load_error_dump(mhi_cntrl);
goto invalid_pm_state;
}
@ -296,6 +302,16 @@ invalid_pm_state:
return -EIO;
}
static void mhi_free_bhi_buffer(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
struct mhi_buf *mhi_buf = image_info->mhi_buf;
dma_free_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len, mhi_buf->buf, mhi_buf->dma_addr);
kfree(image_info->mhi_buf);
kfree(image_info);
}
void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info *image_info)
{
@ -310,6 +326,45 @@ void mhi_free_bhie_table(struct mhi_controller *mhi_cntrl,
kfree(image_info);
}
static int mhi_alloc_bhi_buffer(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
{
struct image_info *img_info;
struct mhi_buf *mhi_buf;
img_info = kzalloc(sizeof(*img_info), GFP_KERNEL);
if (!img_info)
return -ENOMEM;
/* Allocate memory for entry */
img_info->mhi_buf = kzalloc(sizeof(*img_info->mhi_buf), GFP_KERNEL);
if (!img_info->mhi_buf)
goto error_alloc_mhi_buf;
/* Allocate and populate vector table */
mhi_buf = img_info->mhi_buf;
mhi_buf->len = alloc_size;
mhi_buf->buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, mhi_buf->len,
&mhi_buf->dma_addr, GFP_KERNEL);
if (!mhi_buf->buf)
goto error_alloc_segment;
img_info->bhi_vec = NULL;
img_info->entries = 1;
*image_info = img_info;
return 0;
error_alloc_segment:
kfree(mhi_buf);
error_alloc_mhi_buf:
kfree(img_info);
return -ENOMEM;
}
int mhi_alloc_bhie_table(struct mhi_controller *mhi_cntrl,
struct image_info **image_info,
size_t alloc_size)
@ -365,9 +420,9 @@ error_alloc_mhi_buf:
return -ENOMEM;
}
static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
const u8 *buf, size_t remainder,
struct image_info *img_info)
static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl,
const u8 *buf, size_t remainder,
struct image_info *img_info)
{
size_t to_cpy;
struct mhi_buf *mhi_buf = img_info->mhi_buf;
@ -386,15 +441,61 @@ static void mhi_firmware_copy(struct mhi_controller *mhi_cntrl,
}
}
static enum mhi_fw_load_type mhi_fw_load_type_get(const struct mhi_controller *mhi_cntrl)
{
if (mhi_cntrl->fbc_download) {
return MHI_FW_LOAD_FBC;
} else {
if (mhi_cntrl->seg_len)
return MHI_FW_LOAD_BHIE;
else
return MHI_FW_LOAD_BHI;
}
}
static int mhi_load_image_bhi(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
{
struct image_info *image;
int ret;
ret = mhi_alloc_bhi_buffer(mhi_cntrl, &image, size);
if (ret)
return ret;
/* Load the firmware into BHI vec table */
memcpy(image->mhi_buf->buf, fw_data, size);
ret = mhi_fw_load_bhi(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
mhi_free_bhi_buffer(mhi_cntrl, image);
return ret;
}
static int mhi_load_image_bhie(struct mhi_controller *mhi_cntrl, const u8 *fw_data, size_t size)
{
struct image_info *image;
int ret;
ret = mhi_alloc_bhie_table(mhi_cntrl, &image, size);
if (ret)
return ret;
mhi_firmware_copy_bhie(mhi_cntrl, fw_data, size, image);
ret = mhi_fw_load_bhie(mhi_cntrl, &image->mhi_buf[image->entries - 1]);
mhi_free_bhie_table(mhi_cntrl, image);
return ret;
}
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
{
const struct firmware *firmware = NULL;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
enum mhi_fw_load_type fw_load_type;
enum mhi_pm_state new_state;
const char *fw_name;
const u8 *fw_data;
void *buf;
dma_addr_t dma_addr;
size_t size, fw_sz;
int ret;
@ -453,21 +554,17 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
fw_sz = firmware->size;
skip_req_fw:
buf = dma_alloc_coherent(mhi_cntrl->cntrl_dev, size, &dma_addr,
GFP_KERNEL);
if (!buf) {
release_firmware(firmware);
goto error_fw_load;
}
/* Download image using BHI */
memcpy(buf, fw_data, size);
ret = mhi_fw_load_bhi(mhi_cntrl, dma_addr, size);
dma_free_coherent(mhi_cntrl->cntrl_dev, size, buf, dma_addr);
fw_load_type = mhi_fw_load_type_get(mhi_cntrl);
if (fw_load_type == MHI_FW_LOAD_BHIE)
ret = mhi_load_image_bhie(mhi_cntrl, fw_data, size);
else
ret = mhi_load_image_bhi(mhi_cntrl, fw_data, size);
/* Error or in EDL mode, we're done */
if (ret) {
dev_err(dev, "MHI did not load image over BHI, ret: %d\n", ret);
dev_err(dev, "MHI did not load image over BHI%s, ret: %d\n",
fw_load_type == MHI_FW_LOAD_BHIE ? "e" : "",
ret);
release_firmware(firmware);
goto error_fw_load;
}
@ -486,7 +583,7 @@ skip_req_fw:
* If we're doing fbc, populate vector tables while
* device transitioning into MHI READY state
*/
if (mhi_cntrl->fbc_download) {
if (fw_load_type == MHI_FW_LOAD_FBC) {
ret = mhi_alloc_bhie_table(mhi_cntrl, &mhi_cntrl->fbc_image, fw_sz);
if (ret) {
release_firmware(firmware);
@ -494,7 +591,7 @@ skip_req_fw:
}
/* Load the firmware into BHIE vec table */
mhi_firmware_copy(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
mhi_firmware_copy_bhie(mhi_cntrl, fw_data, fw_sz, mhi_cntrl->fbc_image);
}
release_firmware(firmware);
@ -511,7 +608,7 @@ fw_load_ready_state:
return;
error_ready_state:
if (mhi_cntrl->fbc_download) {
if (mhi_cntrl->fbc_image) {
mhi_free_bhie_table(mhi_cntrl, mhi_cntrl->fbc_image);
mhi_cntrl->fbc_image = NULL;
}

View File

@ -1144,7 +1144,7 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
}
mhi_cntrl->bhi = mhi_cntrl->regs + bhi_off;
if (mhi_cntrl->fbc_download || mhi_cntrl->rddm_size) {
if (mhi_cntrl->fbc_download || mhi_cntrl->rddm_size || mhi_cntrl->seg_len) {
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIEOFF,
&bhie_off);
if (ret) {

View File

@ -29,6 +29,13 @@ struct bhi_vec_entry {
u64 size;
};
enum mhi_fw_load_type {
MHI_FW_LOAD_BHI, /* BHI only in PBL */
MHI_FW_LOAD_BHIE, /* BHIe only in PBL */
MHI_FW_LOAD_FBC, /* BHI in PBL followed by BHIe in SBL */
MHI_FW_LOAD_MAX,
};
enum mhi_ch_state_type {
MHI_CH_STATE_TYPE_RESET,
MHI_CH_STATE_TYPE_STOP,

View File

@ -84,8 +84,8 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
struct dma_fence **fences,
struct dma_fence_unwrap *iter)
{
struct dma_fence *tmp, *unsignaled = NULL, **array;
struct dma_fence_array *result;
struct dma_fence *tmp, **array;
ktime_t timestamp;
int i, j, count;
@ -94,6 +94,8 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
for (i = 0; i < num_fences; ++i) {
dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) {
if (!dma_fence_is_signaled(tmp)) {
dma_fence_put(unsignaled);
unsignaled = dma_fence_get(tmp);
++count;
} else {
ktime_t t = dma_fence_timestamp(tmp);
@ -107,9 +109,16 @@ struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
/*
* If we couldn't find a pending fence just return a private signaled
* fence with the timestamp of the last signaled one.
*
* Or if there was a single unsignaled fence left we can return it
* directly and early since that is a major path on many workloads.
*/
if (count == 0)
return dma_fence_allocate_private_stub(timestamp);
else if (count == 1)
return unsignaled;
dma_fence_put(unsignaled);
array = kmalloc_array(count, sizeof(*array), GFP_KERNEL);
if (!array)

View File

@ -28,7 +28,7 @@ static const struct dma_fence_ops mock_ops = {
.get_timeline_name = mock_name,
};
static struct dma_fence *mock_fence(void)
static struct dma_fence *__mock_fence(u64 context, u64 seqno)
{
struct mock_fence *f;
@ -37,12 +37,16 @@ static struct dma_fence *mock_fence(void)
return NULL;
spin_lock_init(&f->lock);
dma_fence_init(&f->base, &mock_ops, &f->lock,
dma_fence_context_alloc(1), 1);
dma_fence_init(&f->base, &mock_ops, &f->lock, context, seqno);
return &f->base;
}
static struct dma_fence *mock_fence(void)
{
return __mock_fence(dma_fence_context_alloc(1), 1);
}
static struct dma_fence *mock_array(unsigned int num_fences, ...)
{
struct dma_fence_array *array;
@ -304,6 +308,177 @@ error_put_f1:
return err;
}
static int unwrap_merge_duplicate(void *arg)
{
struct dma_fence *fence, *f1, *f2;
struct dma_fence_unwrap iter;
int err = 0;
f1 = mock_fence();
if (!f1)
return -ENOMEM;
dma_fence_enable_sw_signaling(f1);
f2 = dma_fence_unwrap_merge(f1, f1);
if (!f2) {
err = -ENOMEM;
goto error_put_f1;
}
dma_fence_unwrap_for_each(fence, &iter, f2) {
if (fence == f1) {
dma_fence_put(f1);
f1 = NULL;
} else {
pr_err("Unexpected fence!\n");
err = -EINVAL;
}
}
if (f1) {
pr_err("Not all fences seen!\n");
err = -EINVAL;
}
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
return err;
}
static int unwrap_merge_seqno(void *arg)
{
struct dma_fence *fence, *f1, *f2, *f3, *f4;
struct dma_fence_unwrap iter;
int err = 0;
u64 ctx[2];
ctx[0] = dma_fence_context_alloc(1);
ctx[1] = dma_fence_context_alloc(1);
f1 = __mock_fence(ctx[1], 1);
if (!f1)
return -ENOMEM;
dma_fence_enable_sw_signaling(f1);
f2 = __mock_fence(ctx[1], 2);
if (!f2) {
err = -ENOMEM;
goto error_put_f1;
}
dma_fence_enable_sw_signaling(f2);
f3 = __mock_fence(ctx[0], 1);
if (!f3) {
err = -ENOMEM;
goto error_put_f2;
}
dma_fence_enable_sw_signaling(f3);
f4 = dma_fence_unwrap_merge(f1, f2, f3);
if (!f4) {
err = -ENOMEM;
goto error_put_f3;
}
dma_fence_unwrap_for_each(fence, &iter, f4) {
if (fence == f3 && f2) {
dma_fence_put(f3);
f3 = NULL;
} else if (fence == f2 && !f3) {
dma_fence_put(f2);
f2 = NULL;
} else {
pr_err("Unexpected fence!\n");
err = -EINVAL;
}
}
if (f2 || f3) {
pr_err("Not all fences seen!\n");
err = -EINVAL;
}
dma_fence_put(f4);
error_put_f3:
dma_fence_put(f3);
error_put_f2:
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
return err;
}
static int unwrap_merge_order(void *arg)
{
struct dma_fence *fence, *f1, *f2, *a1, *a2, *c1, *c2;
struct dma_fence_unwrap iter;
int err = 0;
f1 = mock_fence();
if (!f1)
return -ENOMEM;
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
if (!f2) {
dma_fence_put(f1);
return -ENOMEM;
}
dma_fence_enable_sw_signaling(f2);
a1 = mock_array(2, f1, f2);
if (!a1)
return -ENOMEM;
c1 = mock_chain(NULL, dma_fence_get(f1));
if (!c1)
goto error_put_a1;
c2 = mock_chain(c1, dma_fence_get(f2));
if (!c2)
goto error_put_a1;
/*
* The fences in the chain are the same as in a1 but in oposite order,
* the dma_fence_merge() function should be able to handle that.
*/
a2 = dma_fence_unwrap_merge(a1, c2);
dma_fence_unwrap_for_each(fence, &iter, a2) {
if (fence == f1) {
f1 = NULL;
if (!f2)
pr_err("Unexpected order!\n");
} else if (fence == f2) {
f2 = NULL;
if (f1)
pr_err("Unexpected order!\n");
} else {
pr_err("Unexpected fence!\n");
err = -EINVAL;
}
}
if (f1 || f2) {
pr_err("Not all fences seen!\n");
err = -EINVAL;
}
dma_fence_put(a2);
return err;
error_put_a1:
dma_fence_put(a1);
return -ENOMEM;
}
static int unwrap_merge_complex(void *arg)
{
struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5;
@ -327,7 +502,7 @@ static int unwrap_merge_complex(void *arg)
goto error_put_f2;
/* The resulting array has the fences in reverse */
f4 = dma_fence_unwrap_merge(f2, f1);
f4 = mock_array(2, dma_fence_get(f2), dma_fence_get(f1));
if (!f4)
goto error_put_f3;
@ -367,6 +542,87 @@ error_put_f1:
return err;
}
static int unwrap_merge_complex_seqno(void *arg)
{
struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5, *f6, *f7;
struct dma_fence_unwrap iter;
int err = -ENOMEM;
u64 ctx[2];
ctx[0] = dma_fence_context_alloc(1);
ctx[1] = dma_fence_context_alloc(1);
f1 = __mock_fence(ctx[0], 2);
if (!f1)
return -ENOMEM;
dma_fence_enable_sw_signaling(f1);
f2 = __mock_fence(ctx[1], 1);
if (!f2)
goto error_put_f1;
dma_fence_enable_sw_signaling(f2);
f3 = __mock_fence(ctx[0], 1);
if (!f3)
goto error_put_f2;
dma_fence_enable_sw_signaling(f3);
f4 = __mock_fence(ctx[1], 2);
if (!f4)
goto error_put_f3;
dma_fence_enable_sw_signaling(f4);
f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2));
if (!f5)
goto error_put_f4;
f6 = mock_array(2, dma_fence_get(f3), dma_fence_get(f4));
if (!f6)
goto error_put_f5;
f7 = dma_fence_unwrap_merge(f5, f6);
if (!f7)
goto error_put_f6;
err = 0;
dma_fence_unwrap_for_each(fence, &iter, f7) {
if (fence == f1 && f4) {
dma_fence_put(f1);
f1 = NULL;
} else if (fence == f4 && !f1) {
dma_fence_put(f4);
f4 = NULL;
} else {
pr_err("Unexpected fence!\n");
err = -EINVAL;
}
}
if (f1 || f4) {
pr_err("Not all fences seen!\n");
err = -EINVAL;
}
dma_fence_put(f7);
error_put_f6:
dma_fence_put(f6);
error_put_f5:
dma_fence_put(f5);
error_put_f4:
dma_fence_put(f4);
error_put_f3:
dma_fence_put(f3);
error_put_f2:
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
return err;
}
int dma_fence_unwrap(void)
{
static const struct subtest tests[] = {
@ -375,7 +631,11 @@ int dma_fence_unwrap(void)
SUBTEST(unwrap_chain),
SUBTEST(unwrap_chain_array),
SUBTEST(unwrap_merge),
SUBTEST(unwrap_merge_duplicate),
SUBTEST(unwrap_merge_seqno),
SUBTEST(unwrap_merge_order),
SUBTEST(unwrap_merge_complex),
SUBTEST(unwrap_merge_complex_seqno),
};
return subtests(tests, NULL);

View File

@ -5,3 +5,4 @@
obj-y += host1x/ drm/ vga/
obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/
obj-$(CONFIG_TRACE_GPU_MEM) += trace/
obj-$(CONFIG_NOVA_CORE) += nova-core/

View File

@ -278,6 +278,15 @@ config DRM_GPUVM
GPU-VM representation providing helpers to manage a GPUs virtual
address space
config DRM_GPUSVM
tristate
depends on DRM && DEVICE_PRIVATE
select HMM_MIRROR
select MMU_NOTIFIER
help
GPU-SVM representation providing helpers to manage a GPUs shared
virtual memory
config DRM_BUDDY
tristate
depends on DRM
@ -326,8 +335,6 @@ config DRM_SCHED
tristate
depends on DRM
source "drivers/gpu/drm/i2c/Kconfig"
source "drivers/gpu/drm/arm/Kconfig"
source "drivers/gpu/drm/radeon/Kconfig"
@ -441,6 +448,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
source "drivers/gpu/drm/tidss/Kconfig"
source "drivers/gpu/drm/adp/Kconfig"
source "drivers/gpu/drm/xlnx/Kconfig"
source "drivers/gpu/drm/gud/Kconfig"
@ -494,6 +503,17 @@ config DRM_WERROR
If in doubt, say N.
config DRM_HEADER_TEST
bool "Ensure DRM headers are self-contained and pass kernel-doc"
depends on DRM && EXPERT && BROKEN
default n
help
Ensure the DRM subsystem headers both under drivers/gpu/drm and
include/drm compile, are self-contained, have header guards, and have
no kernel-doc warnings.
If in doubt, say N.
endif
# Separate option because drm_panel_orientation_quirks.c is shared with fbdev

View File

@ -104,6 +104,7 @@ obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o
#
obj-$(CONFIG_DRM_EXEC) += drm_exec.o
obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o
obj-$(CONFIG_DRM_GPUSVM) += drm_gpusvm.o
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
@ -135,7 +136,6 @@ drm_kms_helper-y := \
drm_atomic_state_helper.o \
drm_crtc_helper.o \
drm_damage_helper.o \
drm_encoder_slave.o \
drm_flip_work.o \
drm_format_helper.o \
drm_gem_atomic_helper.o \
@ -198,7 +198,6 @@ obj-$(CONFIG_DRM_INGENIC) += ingenic/
obj-$(CONFIG_DRM_LOGICVC) += logicvc/
obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
obj-$(CONFIG_DRM_MESON) += meson/
obj-y += i2c/
obj-y += panel/
obj-y += bridge/
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
@ -208,6 +207,7 @@ obj-y += mxsfb/
obj-y += tiny/
obj-$(CONFIG_DRM_PL111) += pl111/
obj-$(CONFIG_DRM_TVE200) += tve200/
obj-$(CONFIG_DRM_ADP) += adp/
obj-$(CONFIG_DRM_XEN) += xen/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_DRM_LIMA) += lima/
@ -223,3 +223,21 @@ obj-y += solomon/
obj-$(CONFIG_DRM_SPRD) += sprd/
obj-$(CONFIG_DRM_LOONGSON) += loongson/
obj-$(CONFIG_DRM_POWERVR) += imagination/
# Ensure drm headers are self-contained and pass kernel-doc
hdrtest-files := \
$(shell cd $(src) && find . -maxdepth 1 -name 'drm_*.h') \
$(shell cd $(src) && find display lib -name '*.h')
always-$(CONFIG_DRM_HEADER_TEST) += \
$(patsubst %.h,%.hdrtest, $(hdrtest-files))
# Include the header twice to detect missing include guard.
quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@)
cmd_hdrtest = \
$(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \
$(srctree)/scripts/kernel-doc -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \
touch $@
$(obj)/%.hdrtest: $(src)/%.h FORCE
$(call if_changed_dep,hdrtest)

View File

@ -0,0 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only OR MIT
config DRM_ADP
tristate "DRM Support for pre-DCP Apple display controllers"
depends on DRM && OF && ARM64
depends on ARCH_APPLE || COMPILE_TEST
select DRM_KMS_HELPER
select DRM_BRIDGE_CONNECTOR
select DRM_DISPLAY_HELPER
select DRM_KMS_DMA_HELPER
select DRM_GEM_DMA_HELPER
select DRM_PANEL_BRIDGE
select VIDEOMODE_HELPERS
select DRM_MIPI_DSI
help
Chose this option if you have an Apple Arm laptop with a touchbar.
If M is selected, this module will be called adpdrm.

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only OR MIT
adpdrm-y := adp_drv.o
adpdrm-mipi-y := adp-mipi.o
obj-$(CONFIG_DRM_ADP) += adpdrm.o adpdrm-mipi.o

View File

@ -0,0 +1,276 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/component.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <drm/drm_bridge.h>
#include <drm/drm_mipi_dsi.h>
#define DSI_GEN_HDR 0x6c
#define DSI_GEN_PLD_DATA 0x70
#define DSI_CMD_PKT_STATUS 0x74
#define GEN_PLD_R_EMPTY BIT(4)
#define GEN_PLD_W_FULL BIT(3)
#define GEN_PLD_W_EMPTY BIT(2)
#define GEN_CMD_FULL BIT(1)
#define GEN_CMD_EMPTY BIT(0)
#define GEN_RD_CMD_BUSY BIT(6)
#define CMD_PKT_STATUS_TIMEOUT_US 20000
struct adp_mipi_drv_private {
struct mipi_dsi_host dsi;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
void __iomem *mipi;
};
#define mipi_to_adp(x) container_of(x, struct adp_mipi_drv_private, dsi)
static int adp_dsi_gen_pkt_hdr_write(struct adp_mipi_drv_private *adp, u32 hdr_val)
{
int ret;
u32 val, mask;
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
val, !(val & GEN_CMD_FULL), 1000,
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(adp->dsi.dev, "failed to get available command FIFO\n");
return ret;
}
writel(hdr_val, adp->mipi + DSI_GEN_HDR);
mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
val, (val & mask) == mask,
1000, CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(adp->dsi.dev, "failed to write command FIFO\n");
return ret;
}
return 0;
}
static int adp_dsi_write(struct adp_mipi_drv_private *adp,
const struct mipi_dsi_packet *packet)
{
const u8 *tx_buf = packet->payload;
int len = packet->payload_length, pld_data_bytes = sizeof(u32), ret;
__le32 word;
u32 val;
while (len) {
if (len < pld_data_bytes) {
word = 0;
memcpy(&word, tx_buf, len);
writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA);
len = 0;
} else {
memcpy(&word, tx_buf, pld_data_bytes);
writel(le32_to_cpu(word), adp->mipi + DSI_GEN_PLD_DATA);
tx_buf += pld_data_bytes;
len -= pld_data_bytes;
}
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_W_FULL), 1000,
CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(adp->dsi.dev,
"failed to get available write payload FIFO\n");
return ret;
}
}
word = 0;
memcpy(&word, packet->header, sizeof(packet->header));
return adp_dsi_gen_pkt_hdr_write(adp, le32_to_cpu(word));
}
static int adp_dsi_read(struct adp_mipi_drv_private *adp,
const struct mipi_dsi_msg *msg)
{
int i, j, ret, len = msg->rx_len;
u8 *buf = msg->rx_buf;
u32 val;
/* Wait end of the read operation */
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
val, !(val & GEN_RD_CMD_BUSY),
1000, CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(adp->dsi.dev, "Timeout during read operation\n");
return ret;
}
for (i = 0; i < len; i += 4) {
/* Read fifo must not be empty before all bytes are read */
ret = readl_poll_timeout(adp->mipi + DSI_CMD_PKT_STATUS,
val, !(val & GEN_PLD_R_EMPTY),
1000, CMD_PKT_STATUS_TIMEOUT_US);
if (ret) {
dev_err(adp->dsi.dev, "Read payload FIFO is empty\n");
return ret;
}
val = readl(adp->mipi + DSI_GEN_PLD_DATA);
for (j = 0; j < 4 && j + i < len; j++)
buf[i + j] = val >> (8 * j);
}
return ret;
}
static ssize_t adp_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
struct mipi_dsi_packet packet;
int ret, nb_bytes;
ret = mipi_dsi_create_packet(&packet, msg);
if (ret) {
dev_err(adp->dsi.dev, "failed to create packet: %d\n", ret);
return ret;
}
ret = adp_dsi_write(adp, &packet);
if (ret)
return ret;
if (msg->rx_buf && msg->rx_len) {
ret = adp_dsi_read(adp, msg);
if (ret)
return ret;
nb_bytes = msg->rx_len;
} else {
nb_bytes = packet.size;
}
return nb_bytes;
}
static int adp_dsi_bind(struct device *dev, struct device *master, void *data)
{
return 0;
}
static void adp_dsi_unbind(struct device *dev, struct device *master, void *data)
{
}
static const struct component_ops adp_dsi_component_ops = {
.bind = adp_dsi_bind,
.unbind = adp_dsi_unbind,
};
static int adp_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *dev)
{
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
struct drm_bridge *next;
int ret;
next = devm_drm_of_get_bridge(adp->dsi.dev, adp->dsi.dev->of_node, 1, 0);
if (IS_ERR(next))
return PTR_ERR(next);
adp->next_bridge = next;
drm_bridge_add(&adp->bridge);
ret = component_add(host->dev, &adp_dsi_component_ops);
if (ret) {
pr_err("failed to add dsi_host component: %d\n", ret);
drm_bridge_remove(&adp->bridge);
return ret;
}
return 0;
}
static int adp_dsi_host_detach(struct mipi_dsi_host *host,
struct mipi_dsi_device *dev)
{
struct adp_mipi_drv_private *adp = mipi_to_adp(host);
component_del(host->dev, &adp_dsi_component_ops);
drm_bridge_remove(&adp->bridge);
return 0;
}
static const struct mipi_dsi_host_ops adp_dsi_host_ops = {
.transfer = adp_dsi_host_transfer,
.attach = adp_dsi_host_attach,
.detach = adp_dsi_host_detach,
};
static int adp_dsi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct adp_mipi_drv_private *adp =
container_of(bridge, struct adp_mipi_drv_private, bridge);
return drm_bridge_attach(bridge->encoder, adp->next_bridge, bridge, flags);
}
static const struct drm_bridge_funcs adp_dsi_bridge_funcs = {
.attach = adp_dsi_bridge_attach,
};
static int adp_mipi_probe(struct platform_device *pdev)
{
struct adp_mipi_drv_private *adp;
adp = devm_kzalloc(&pdev->dev, sizeof(*adp), GFP_KERNEL);
if (!adp)
return -ENOMEM;
adp->mipi = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(adp->mipi)) {
dev_err(&pdev->dev, "failed to map mipi mmio");
return PTR_ERR(adp->mipi);
}
adp->dsi.dev = &pdev->dev;
adp->dsi.ops = &adp_dsi_host_ops;
adp->bridge.funcs = &adp_dsi_bridge_funcs;
adp->bridge.of_node = pdev->dev.of_node;
adp->bridge.type = DRM_MODE_CONNECTOR_DSI;
dev_set_drvdata(&pdev->dev, adp);
return mipi_dsi_host_register(&adp->dsi);
}
static void adp_mipi_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct adp_mipi_drv_private *adp = dev_get_drvdata(dev);
mipi_dsi_host_unregister(&adp->dsi);
}
static const struct of_device_id adp_mipi_of_match[] = {
{ .compatible = "apple,h7-display-pipe-mipi", },
{ },
};
MODULE_DEVICE_TABLE(of, adp_mipi_of_match);
static struct platform_driver adp_mipi_platform_driver = {
.driver = {
.name = "adp-mipi",
.of_match_table = adp_mipi_of_match,
},
.probe = adp_mipi_probe,
.remove = adp_mipi_remove,
};
module_platform_driver(adp_mipi_platform_driver);
MODULE_DESCRIPTION("Apple Display Pipe MIPI driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,612 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/component.h>
#include <linux/iopoll.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#define ADP_INT_STATUS 0x34
#define ADP_INT_STATUS_INT_MASK 0x7
#define ADP_INT_STATUS_VBLANK 0x1
#define ADP_CTRL 0x100
#define ADP_CTRL_VBLANK_ON 0x12
#define ADP_CTRL_FIFO_ON 0x601
#define ADP_SCREEN_SIZE 0x0c
#define ADP_SCREEN_HSIZE GENMASK(15, 0)
#define ADP_SCREEN_VSIZE GENMASK(31, 16)
#define ADBE_FIFO 0x10c0
#define ADBE_FIFO_SYNC 0xc0000000
#define ADBE_BLEND_BYPASS 0x2020
#define ADBE_BLEND_EN1 0x2028
#define ADBE_BLEND_EN2 0x2074
#define ADBE_BLEND_EN3 0x202c
#define ADBE_BLEND_EN4 0x2034
#define ADBE_MASK_BUF 0x2200
#define ADBE_SRC_START 0x4040
#define ADBE_SRC_SIZE 0x4048
#define ADBE_DST_START 0x4050
#define ADBE_DST_SIZE 0x4054
#define ADBE_STRIDE 0x4038
#define ADBE_FB_BASE 0x4030
#define ADBE_LAYER_EN1 0x4020
#define ADBE_LAYER_EN2 0x4068
#define ADBE_LAYER_EN3 0x40b4
#define ADBE_LAYER_EN4 0x40f4
#define ADBE_SCALE_CTL 0x40ac
#define ADBE_SCALE_CTL_BYPASS 0x100000
#define ADBE_LAYER_CTL 0x1038
#define ADBE_LAYER_CTL_ENABLE 0x10000
#define ADBE_PIX_FMT 0x402c
#define ADBE_PIX_FMT_XRGB32 0x53e4001
static int adp_open(struct inode *inode, struct file *filp)
{
/*
* The modesetting driver does not check the non-desktop connector
* property and keeps the device open and locked. If the touchbar daemon
* opens the device first, modesetting breaks the whole X session.
* Simply refuse to open the device for X11 server processes as
* workaround.
*/
if (current->comm[0] == 'X')
return -EBUSY;
return drm_open(inode, filp);
}
static const struct file_operations adp_fops = {
.owner = THIS_MODULE,
.open = adp_open,
.release = drm_release,
.unlocked_ioctl = drm_ioctl,
.compat_ioctl = drm_compat_ioctl,
.poll = drm_poll,
.read = drm_read,
.llseek = noop_llseek,
.mmap = drm_gem_mmap,
.fop_flags = FOP_UNSIGNED_OFFSET,
DRM_GEM_DMA_UNMAPPED_AREA_FOPS
};
static int adp_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *drm,
struct drm_mode_create_dumb *args)
{
args->height = ALIGN(args->height, 64);
args->size = args->pitch * args->height;
return drm_gem_dma_dumb_create_internal(file_priv, drm, args);
}
static const struct drm_driver adp_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &adp_fops,
DRM_GEM_DMA_DRIVER_OPS_VMAP_WITH_DUMB_CREATE(adp_drm_gem_dumb_create),
.name = "adp",
.desc = "Apple Display Pipe DRM Driver",
.major = 0,
.minor = 1,
};
struct adp_drv_private {
struct drm_device drm;
struct drm_crtc crtc;
struct drm_encoder *encoder;
struct drm_connector *connector;
struct drm_bridge *next_bridge;
void __iomem *be;
void __iomem *fe;
u32 *mask_buf;
u64 mask_buf_size;
dma_addr_t mask_iova;
int be_irq;
int fe_irq;
spinlock_t irq_lock;
struct drm_pending_vblank_event *event;
};
#define to_adp(x) container_of(x, struct adp_drv_private, drm)
#define crtc_to_adp(x) container_of(x, struct adp_drv_private, crtc)
static int adp_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state;
struct drm_crtc_state *crtc_state;
new_plane_state = drm_atomic_get_new_plane_state(state, plane);
if (!new_plane_state->crtc)
return 0;
crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
return drm_atomic_helper_check_plane_state(new_plane_state,
crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING,
true, true);
}
static void adp_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct adp_drv_private *adp;
struct drm_rect src_rect;
struct drm_gem_dma_object *obj;
struct drm_framebuffer *fb;
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
u32 src_pos, src_size, dst_pos, dst_size;
if (!plane || !new_state)
return;
fb = new_state->fb;
if (!fb)
return;
adp = to_adp(plane->dev);
drm_rect_fp_to_int(&src_rect, &new_state->src);
src_pos = src_rect.x1 << 16 | src_rect.y1;
dst_pos = new_state->dst.x1 << 16 | new_state->dst.y1;
src_size = drm_rect_width(&src_rect) << 16 | drm_rect_height(&src_rect);
dst_size = drm_rect_width(&new_state->dst) << 16 |
drm_rect_height(&new_state->dst);
writel(src_pos, adp->be + ADBE_SRC_START);
writel(src_size, adp->be + ADBE_SRC_SIZE);
writel(dst_pos, adp->be + ADBE_DST_START);
writel(dst_size, adp->be + ADBE_DST_SIZE);
writel(fb->pitches[0], adp->be + ADBE_STRIDE);
obj = drm_fb_dma_get_gem_obj(fb, 0);
if (obj)
writel(obj->dma_addr + fb->offsets[0], adp->be + ADBE_FB_BASE);
writel(BIT(0), adp->be + ADBE_LAYER_EN1);
writel(BIT(0), adp->be + ADBE_LAYER_EN2);
writel(BIT(0), adp->be + ADBE_LAYER_EN3);
writel(BIT(0), adp->be + ADBE_LAYER_EN4);
writel(ADBE_SCALE_CTL_BYPASS, adp->be + ADBE_SCALE_CTL);
writel(ADBE_LAYER_CTL_ENABLE | BIT(0), adp->be + ADBE_LAYER_CTL);
writel(ADBE_PIX_FMT_XRGB32, adp->be + ADBE_PIX_FMT);
}
static void adp_plane_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct adp_drv_private *adp = to_adp(plane->dev);
writel(0x0, adp->be + ADBE_LAYER_EN1);
writel(0x0, adp->be + ADBE_LAYER_EN2);
writel(0x0, adp->be + ADBE_LAYER_EN3);
writel(0x0, adp->be + ADBE_LAYER_EN4);
writel(ADBE_LAYER_CTL_ENABLE, adp->be + ADBE_LAYER_CTL);
}
static const struct drm_plane_helper_funcs adp_plane_helper_funcs = {
.atomic_check = adp_plane_atomic_check,
.atomic_update = adp_plane_atomic_update,
.atomic_disable = adp_plane_atomic_disable,
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS
};
static const struct drm_plane_funcs adp_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
DRM_GEM_SHADOW_PLANE_FUNCS
};
static const u32 plane_formats[] = {
DRM_FORMAT_XRGB8888,
};
#define ALL_CRTCS 1
static struct drm_plane *adp_plane_new(struct adp_drv_private *adp)
{
struct drm_device *drm = &adp->drm;
struct drm_plane *plane;
plane = __drmm_universal_plane_alloc(drm, sizeof(struct drm_plane), 0,
ALL_CRTCS, &adp_plane_funcs,
plane_formats, ARRAY_SIZE(plane_formats),
NULL, DRM_PLANE_TYPE_PRIMARY, "plane");
if (!plane) {
drm_err(drm, "failed to allocate plane");
return ERR_PTR(-ENOMEM);
}
drm_plane_helper_add(plane, &adp_plane_helper_funcs);
return plane;
}
static void adp_enable_vblank(struct adp_drv_private *adp)
{
u32 cur_ctrl;
writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS);
cur_ctrl = readl(adp->fe + ADP_CTRL);
writel(cur_ctrl | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
}
static int adp_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct adp_drv_private *adp = to_adp(dev);
adp_enable_vblank(adp);
return 0;
}
static void adp_disable_vblank(struct adp_drv_private *adp)
{
u32 cur_ctrl;
cur_ctrl = readl(adp->fe + ADP_CTRL);
writel(cur_ctrl & ~ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
writel(ADP_INT_STATUS_INT_MASK, adp->fe + ADP_INT_STATUS);
}
static void adp_crtc_disable_vblank(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct adp_drv_private *adp = to_adp(dev);
adp_disable_vblank(adp);
}
static void adp_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct adp_drv_private *adp = crtc_to_adp(crtc);
writel(BIT(0), adp->be + ADBE_BLEND_EN2);
writel(BIT(4), adp->be + ADBE_BLEND_EN1);
writel(BIT(0), adp->be + ADBE_BLEND_EN3);
writel(BIT(0), adp->be + ADBE_BLEND_BYPASS);
writel(BIT(0), adp->be + ADBE_BLEND_EN4);
}
static void adp_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct adp_drv_private *adp = crtc_to_adp(crtc);
struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
drm_atomic_helper_disable_planes_on_crtc(old_state, false);
writel(0x0, adp->be + ADBE_BLEND_EN2);
writel(0x0, adp->be + ADBE_BLEND_EN1);
writel(0x0, adp->be + ADBE_BLEND_EN3);
writel(0x0, adp->be + ADBE_BLEND_BYPASS);
writel(0x0, adp->be + ADBE_BLEND_EN4);
drm_crtc_vblank_off(crtc);
}
static void adp_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
u32 frame_num = 1;
struct adp_drv_private *adp = crtc_to_adp(crtc);
struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state, crtc);
u64 new_size = ALIGN(new_state->mode.hdisplay *
new_state->mode.vdisplay * 4, PAGE_SIZE);
if (new_size != adp->mask_buf_size) {
if (adp->mask_buf)
dma_free_coherent(crtc->dev->dev, adp->mask_buf_size,
adp->mask_buf, adp->mask_iova);
adp->mask_buf = NULL;
if (new_size != 0) {
adp->mask_buf = dma_alloc_coherent(crtc->dev->dev, new_size,
&adp->mask_iova, GFP_KERNEL);
memset(adp->mask_buf, 0xFF, new_size);
writel(adp->mask_iova, adp->be + ADBE_MASK_BUF);
}
adp->mask_buf_size = new_size;
}
writel(ADBE_FIFO_SYNC | frame_num, adp->be + ADBE_FIFO);
//FIXME: use adbe flush interrupt
spin_lock_irq(&crtc->dev->event_lock);
if (crtc->state->event) {
drm_crtc_vblank_get(crtc);
adp->event = crtc->state->event;
}
crtc->state->event = NULL;
spin_unlock_irq(&crtc->dev->event_lock);
}
static const struct drm_crtc_funcs adp_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = adp_crtc_enable_vblank,
.disable_vblank = adp_crtc_disable_vblank,
};
static const struct drm_crtc_helper_funcs adp_crtc_helper_funcs = {
.atomic_enable = adp_crtc_atomic_enable,
.atomic_disable = adp_crtc_atomic_disable,
.atomic_flush = adp_crtc_atomic_flush,
};
static int adp_setup_crtc(struct adp_drv_private *adp)
{
struct drm_device *drm = &adp->drm;
struct drm_plane *primary;
int ret;
primary = adp_plane_new(adp);
if (IS_ERR(primary))
return PTR_ERR(primary);
ret = drm_crtc_init_with_planes(drm, &adp->crtc, primary,
NULL, &adp_crtc_funcs, NULL);
if (ret)
return ret;
drm_crtc_helper_add(&adp->crtc, &adp_crtc_helper_funcs);
return 0;
}
static const struct drm_mode_config_funcs adp_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static int adp_setup_mode_config(struct adp_drv_private *adp)
{
struct drm_device *drm = &adp->drm;
int ret;
u32 size;
ret = drmm_mode_config_init(drm);
if (ret)
return ret;
/*
* Query screen size restrict the frame buffer size to the screen size
* aligned to the next multiple of 64. This is not necessary but can be
* used as simple check for non-desktop devices.
* Xorg's modesetting driver does not care about the connector
* "non-desktop" property. The max frame buffer width or height can be
* easily checked and a device can be reject if the max width/height is
* smaller than 120 for example.
* Any touchbar daemon is not limited by this small framebuffer size.
*/
size = readl(adp->fe + ADP_SCREEN_SIZE);
drm->mode_config.min_width = 32;
drm->mode_config.min_height = 32;
drm->mode_config.max_width = ALIGN(FIELD_GET(ADP_SCREEN_HSIZE, size), 64);
drm->mode_config.max_height = ALIGN(FIELD_GET(ADP_SCREEN_VSIZE, size), 64);
drm->mode_config.preferred_depth = 24;
drm->mode_config.prefer_shadow = 0;
drm->mode_config.funcs = &adp_mode_config_funcs;
ret = adp_setup_crtc(adp);
if (ret) {
drm_err(drm, "failed to create crtc");
return ret;
}
adp->encoder = drmm_plain_encoder_alloc(drm, NULL, DRM_MODE_ENCODER_DSI, NULL);
if (IS_ERR(adp->encoder)) {
drm_err(drm, "failed to init encoder");
return PTR_ERR(adp->encoder);
}
adp->encoder->possible_crtcs = ALL_CRTCS;
ret = drm_bridge_attach(adp->encoder, adp->next_bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
drm_err(drm, "failed to init bridge chain");
return ret;
}
adp->connector = drm_bridge_connector_init(drm, adp->encoder);
if (IS_ERR(adp->connector))
return PTR_ERR(adp->connector);
drm_connector_attach_encoder(adp->connector, adp->encoder);
ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0) {
drm_err(drm, "failed to initialize vblank");
return ret;
}
drm_mode_config_reset(drm);
return 0;
}
static int adp_parse_of(struct platform_device *pdev, struct adp_drv_private *adp)
{
struct device *dev = &pdev->dev;
adp->be = devm_platform_ioremap_resource_byname(pdev, "be");
if (IS_ERR(adp->be)) {
dev_err(dev, "failed to map display backend mmio");
return PTR_ERR(adp->be);
}
adp->fe = devm_platform_ioremap_resource_byname(pdev, "fe");
if (IS_ERR(adp->fe)) {
dev_err(dev, "failed to map display pipe mmio");
return PTR_ERR(adp->fe);
}
adp->be_irq = platform_get_irq_byname(pdev, "be");
if (adp->be_irq < 0)
return adp->be_irq;
adp->fe_irq = platform_get_irq_byname(pdev, "fe");
if (adp->fe_irq < 0)
return adp->fe_irq;
return 0;
}
static irqreturn_t adp_fe_irq(int irq, void *arg)
{
struct adp_drv_private *adp = (struct adp_drv_private *)arg;
u32 int_status;
u32 int_ctl;
spin_lock(&adp->irq_lock);
int_status = readl(adp->fe + ADP_INT_STATUS);
if (int_status & ADP_INT_STATUS_VBLANK) {
drm_crtc_handle_vblank(&adp->crtc);
spin_lock(&adp->crtc.dev->event_lock);
if (adp->event) {
int_ctl = readl(adp->fe + ADP_CTRL);
if ((int_ctl & 0xF00) == 0x600) {
drm_crtc_send_vblank_event(&adp->crtc, adp->event);
adp->event = NULL;
drm_crtc_vblank_put(&adp->crtc);
}
}
spin_unlock(&adp->crtc.dev->event_lock);
}
writel(int_status, adp->fe + ADP_INT_STATUS);
spin_unlock(&adp->irq_lock);
return IRQ_HANDLED;
}
static int adp_drm_bind(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct adp_drv_private *adp = to_adp(drm);
int err;
adp_disable_vblank(adp);
writel(ADP_CTRL_FIFO_ON | ADP_CTRL_VBLANK_ON, adp->fe + ADP_CTRL);
adp->next_bridge = drmm_of_get_bridge(&adp->drm, dev->of_node, 0, 0);
if (IS_ERR(adp->next_bridge)) {
dev_err(dev, "failed to find next bridge");
return PTR_ERR(adp->next_bridge);
}
err = adp_setup_mode_config(adp);
if (err < 0)
return err;
err = request_irq(adp->fe_irq, adp_fe_irq, 0, "adp-fe", adp);
if (err)
return err;
err = drm_dev_register(&adp->drm, 0);
if (err)
return err;
return 0;
}
static void adp_drm_unbind(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct adp_drv_private *adp = to_adp(drm);
drm_dev_unregister(drm);
drm_atomic_helper_shutdown(drm);
free_irq(adp->fe_irq, adp);
}
static const struct component_master_ops adp_master_ops = {
.bind = adp_drm_bind,
.unbind = adp_drm_unbind,
};
static int compare_dev(struct device *dev, void *data)
{
return dev->of_node == data;
}
static int adp_probe(struct platform_device *pdev)
{
struct device_node *port;
struct component_match *match = NULL;
struct adp_drv_private *adp;
int err;
adp = devm_drm_dev_alloc(&pdev->dev, &adp_driver, struct adp_drv_private, drm);
if (IS_ERR(adp))
return PTR_ERR(adp);
spin_lock_init(&adp->irq_lock);
dev_set_drvdata(&pdev->dev, &adp->drm);
err = adp_parse_of(pdev, adp);
if (err < 0)
return err;
port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
if (!port)
return -ENODEV;
drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
of_node_put(port);
return component_master_add_with_match(&pdev->dev, &adp_master_ops, match);
}
static void adp_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &adp_master_ops);
dev_set_drvdata(&pdev->dev, NULL);
}
static const struct of_device_id adp_of_match[] = {
{ .compatible = "apple,h7-display-pipe", },
{ },
};
MODULE_DEVICE_TABLE(of, adp_of_match);
static struct platform_driver adp_platform_driver = {
.driver = {
.name = "adp",
.of_match_table = adp_of_match,
},
.probe = adp_probe,
.remove = adp_remove,
};
module_platform_driver(adp_platform_driver);
MODULE_DESCRIPTION("Apple Display Pipe DRM driver");
MODULE_LICENSE("GPL");

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