Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

drm-misc-next for v6.19-rc1:

UAPI Changes:

Cross-subsystem Changes:
- Update DT bindings for renesas and powervr-rogue.
- Update MAINTAINERS email and add spsc_queue.

Core Changes:
- Allow ttm page protection flags on risc-v.
- Move freeing of drm client memory to driver.

Driver Changes:
- Assorted small fixes and updates to qaic, ivpu, st7571-i2c, gud,
amdxdna.
- Allow configuration of vkms' display through configfs.
- Add Arm Ethos-U65/U85 accel driver.

Signed-off-by: Simona Vetter <simona.vetter@ffwll.ch>
From: Maarten Lankhorst <dev@lankhorst.se>
Link: https://patch.msgid.link/32b43261-3c99-49d9-92ee-615ada1d01e8@lankhorst.se

+3553 -127
+1
.mailmap
··· 173 173 Changbin Du <changbin.du@intel.com> <changbin.du@gmail.com> 174 174 Chao Yu <chao@kernel.org> <chao2.yu@samsung.com> 175 175 Chao Yu <chao@kernel.org> <yuchao0@huawei.com> 176 + Chen-Yu Tsai <wens@kernel.org> <wens@csie.org> 176 177 Chester Lin <chester62515@gmail.com> <clin@suse.com> 177 178 Chris Chiu <chris.chiu@canonical.com> <chiu@endlessm.com> 178 179 Chris Chiu <chris.chiu@canonical.com> <chiu@endlessos.org>
+51 -2
Documentation/devicetree/bindings/display/bridge/renesas,dsi-csi2-tx.yaml
··· 14 14 R-Car Gen4 SoCs. The encoder can operate in either DSI or CSI-2 mode, with up 15 15 to four data lanes. 16 16 17 + allOf: 18 + - $ref: /schemas/display/dsi-controller.yaml# 19 + 17 20 properties: 18 21 compatible: 19 22 enum: ··· 83 80 - resets 84 81 - ports 85 82 86 - additionalProperties: false 83 + unevaluatedProperties: false 87 84 88 85 examples: 89 86 - | 90 87 #include <dt-bindings/clock/r8a779a0-cpg-mssr.h> 91 88 #include <dt-bindings/power/r8a779a0-sysc.h> 92 89 93 - dsi0: dsi-encoder@fed80000 { 90 + dsi@fed80000 { 94 91 compatible = "renesas,r8a779a0-dsi-csi2-tx"; 95 92 reg = <0xfed80000 0x10000>; 96 93 power-domains = <&sysc R8A779A0_PD_ALWAYS_ON>; ··· 116 113 dsi0_out: endpoint { 117 114 data-lanes = <1 2>; 118 115 remote-endpoint = <&sn65dsi86_in>; 116 + }; 117 + }; 118 + }; 119 + }; 120 + 121 + - | 122 + #include <dt-bindings/clock/r8a779g0-cpg-mssr.h> 123 + #include <dt-bindings/power/r8a779g0-sysc.h> 124 + 125 + dsi@fed80000 { 126 + #address-cells = <1>; 127 + #size-cells = <0>; 128 + compatible = "renesas,r8a779g0-dsi-csi2-tx"; 129 + reg = <0xfed80000 0x10000>; 130 + clocks = <&cpg CPG_MOD 415>, 131 + <&cpg CPG_CORE R8A779G0_CLK_DSIEXT>, 132 + <&cpg CPG_CORE R8A779G0_CLK_DSIREF>; 133 + clock-names = "fck", "dsi", "pll"; 134 + power-domains = <&sysc R8A779G0_PD_ALWAYS_ON>; 135 + resets = <&cpg 415>; 136 + 137 + ports { 138 + #address-cells = <1>; 139 + #size-cells = <0>; 140 + 141 + port@0 { 142 + reg = <0>; 143 + }; 144 + 145 + port@1 { 146 + reg = <1>; 147 + 148 + dsi0port1_out: endpoint { 149 + remote-endpoint = <&panel_in>; 150 + data-lanes = <1 2>; 151 + }; 152 + }; 153 + }; 154 + 155 + panel@0 { 156 + reg = <0>; 157 + compatible = "raspberrypi,dsi-7inch"; 158 + 159 + port { 160 + panel_in: endpoint { 161 + remote-endpoint = <&dsi0port1_out>; 119 162 }; 120 163 }; 121 164 };
+51 -35
Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml
··· 15 15 oneOf: 16 16 - items: 17 17 - enum: 18 + - renesas,r8a7796-gpu 19 + - renesas,r8a77961-gpu 20 + - const: img,img-gx6250 21 + - const: img,img-rogue 22 + - items: 23 + - enum: 18 24 - ti,am62-gpu 19 25 - const: img,img-axe-1-16m 20 26 # This deprecated element must be kept around to allow old kernels to ··· 92 86 properties: 93 87 compatible: 94 88 contains: 89 + enum: 90 + - ti,am62-gpu 91 + - ti,j721s2-gpu 92 + then: 93 + properties: 94 + clocks: 95 + maxItems: 1 96 + 97 + 98 + - if: 99 + properties: 100 + compatible: 101 + contains: 102 + enum: 103 + - img,img-gx6250 104 + - thead,th1520-gpu 105 + then: 106 + properties: 107 + clocks: 108 + minItems: 3 109 + clock-names: 110 + minItems: 3 111 + 112 + - if: 113 + properties: 114 + compatible: 115 + contains: 95 116 const: img,img-axe-1-16m 96 117 then: 97 118 properties: 98 119 power-domains: 99 - items: 100 - - description: Power domain A 120 + maxItems: 1 101 121 power-domain-names: 102 122 maxItems: 1 123 + required: 124 + - power-domains 125 + - power-domain-names 126 + 127 + - if: 128 + properties: 129 + compatible: 130 + contains: 131 + enum: 132 + - img,img-gx6250 133 + - img,img-bxs-4-64 134 + then: 135 + properties: 136 + power-domains: 137 + minItems: 2 138 + power-domain-names: 139 + minItems: 2 103 140 required: 104 141 - power-domains 105 142 - power-domain-names ··· 154 105 const: thead,th1520-gpu 155 106 then: 156 107 properties: 157 - clocks: 158 - minItems: 3 159 - clock-names: 160 - minItems: 3 161 108 power-domains: 162 109 items: 163 110 - description: The single, unified power domain for the GPU on the ··· 161 116 power-domain-names: false 162 117 required: 163 118 - power-domains 164 - 165 - - if: 166 - properties: 167 - compatible: 168 - contains: 169 - const: img,img-bxs-4-64 170 - then: 171 - properties: 172 - power-domains: 173 - items: 174 - - description: Power domain A 175 - - description: Power domain B 176 - power-domain-names: 177 - minItems: 2 178 - required: 179 - - power-domains 180 - - power-domain-names 181 - 182 - - if: 183 - properties: 184 - compatible: 185 - contains: 186 - enum: 187 - - ti,am62-gpu 188 - - ti,j721s2-gpu 189 - then: 190 - properties: 191 - clocks: 192 - maxItems: 1 193 119 194 120 examples: 195 121 - |
+79
Documentation/devicetree/bindings/npu/arm,ethos.yaml
··· 1 + # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/npu/arm,ethos.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Arm Ethos U65/U85 8 + 9 + maintainers: 10 + - Rob Herring <robh@kernel.org> 11 + 12 + description: > 13 + The Arm Ethos-U NPUs are designed for IoT inference applications. The NPUs 14 + can accelerate 8-bit and 16-bit integer quantized networks: 15 + 16 + Transformer networks (U85 only) 17 + Convolutional Neural Networks (CNN) 18 + Recurrent Neural Networks (RNN) 19 + 20 + Further documentation is available here: 21 + 22 + U65 TRM: https://developer.arm.com/documentation/102023/ 23 + U85 TRM: https://developer.arm.com/documentation/102685/ 24 + 25 + properties: 26 + compatible: 27 + oneOf: 28 + - items: 29 + - enum: 30 + - fsl,imx93-npu 31 + - const: arm,ethos-u65 32 + - items: 33 + - {} 34 + - const: arm,ethos-u85 35 + 36 + reg: 37 + maxItems: 1 38 + 39 + interrupts: 40 + maxItems: 1 41 + 42 + clocks: 43 + maxItems: 2 44 + 45 + clock-names: 46 + items: 47 + - const: core 48 + - const: apb 49 + 50 + power-domains: 51 + maxItems: 1 52 + 53 + sram: 54 + maxItems: 1 55 + 56 + required: 57 + - compatible 58 + - reg 59 + - interrupts 60 + - clocks 61 + 62 + additionalProperties: false 63 + 64 + examples: 65 + - | 66 + #include <dt-bindings/interrupt-controller/irq.h> 67 + #include <dt-bindings/interrupt-controller/arm-gic.h> 68 + #include <dt-bindings/clock/imx93-clock.h> 69 + 70 + npu@4a900000 { 71 + compatible = "fsl,imx93-npu", "arm,ethos-u65"; 72 + reg = <0x4a900000 0x1000>; 73 + interrupts = <GIC_SPI 178 IRQ_TYPE_LEVEL_HIGH>; 74 + power-domains = <&mlmix>; 75 + clocks = <&clk IMX93_CLK_ML>, <&clk IMX93_CLK_ML_APB>; 76 + clock-names = "core", "apb"; 77 + sram = <&sram>; 78 + }; 79 + ...
+92 -8
Documentation/gpu/vkms.rst
··· 51 51 52 52 sudo modprobe -r vkms 53 53 54 + Configuring With Configfs 55 + ========================= 56 + 57 + It is possible to create and configure multiple VKMS instances via configfs. 58 + 59 + Start by mounting configfs and loading VKMS:: 60 + 61 + sudo mount -t configfs none /config 62 + sudo modprobe vkms 63 + 64 + Once VKMS is loaded, ``/config/vkms`` is created automatically. Each directory 65 + under ``/config/vkms`` represents a VKMS instance, create a new one:: 66 + 67 + sudo mkdir /config/vkms/my-vkms 68 + 69 + By default, the instance is disabled:: 70 + 71 + cat /config/vkms/my-vkms/enabled 72 + 0 73 + 74 + And directories are created for each configurable item of the display pipeline:: 75 + 76 + tree /config/vkms/my-vkms 77 + ├── connectors 78 + ├── crtcs 79 + ├── enabled 80 + ├── encoders 81 + └── planes 82 + 83 + To add items to the display pipeline, create one or more directories under the 84 + available paths. 85 + 86 + Start by creating one or more planes:: 87 + 88 + sudo mkdir /config/vkms/my-vkms/planes/plane0 89 + 90 + Planes have 1 configurable attribute: 91 + 92 + - type: Plane type: 0 overlay, 1 primary, 2 cursor (same values as those 93 + exposed by the "type" property of a plane) 94 + 95 + Continue by creating one or more CRTCs:: 96 + 97 + sudo mkdir /config/vkms/my-vkms/crtcs/crtc0 98 + 99 + CRTCs have 1 configurable attribute: 100 + 101 + - writeback: Enable or disable writeback connector support by writing 1 or 0 102 + 103 + Next, create one or more encoders:: 104 + 105 + sudo mkdir /config/vkms/my-vkms/encoders/encoder0 106 + 107 + Last but not least, create one or more connectors:: 108 + 109 + sudo mkdir /config/vkms/my-vkms/connectors/connector0 110 + 111 + Connectors have 1 configurable attribute: 112 + 113 + - status: Connection status: 1 connected, 2 disconnected, 3 unknown (same values 114 + as those exposed by the "status" property of a connector) 115 + 116 + To finish the configuration, link the different pipeline items:: 117 + 118 + sudo ln -s /config/vkms/my-vkms/crtcs/crtc0 /config/vkms/my-vkms/planes/plane0/possible_crtcs 119 + sudo ln -s /config/vkms/my-vkms/crtcs/crtc0 /config/vkms/my-vkms/encoders/encoder0/possible_crtcs 120 + sudo ln -s /config/vkms/my-vkms/encoders/encoder0 /config/vkms/my-vkms/connectors/connector0/possible_encoders 121 + 122 + Since at least one primary plane is required, make sure to set the right type:: 123 + 124 + echo "1" | sudo tee /config/vkms/my-vkms/planes/plane0/type 125 + 126 + Once you are done configuring the VKMS instance, enable it:: 127 + 128 + echo "1" | sudo tee /config/vkms/my-vkms/enabled 129 + 130 + Finally, you can remove the VKMS instance disabling it:: 131 + 132 + echo "0" | sudo tee /config/vkms/my-vkms/enabled 133 + 134 + And removing the top level directory and its subdirectories:: 135 + 136 + sudo rm /config/vkms/my-vkms/planes/*/possible_crtcs/* 137 + sudo rm /config/vkms/my-vkms/encoders/*/possible_crtcs/* 138 + sudo rm /config/vkms/my-vkms/connectors/*/possible_encoders/* 139 + sudo rmdir /config/vkms/my-vkms/planes/* 140 + sudo rmdir /config/vkms/my-vkms/crtcs/* 141 + sudo rmdir /config/vkms/my-vkms/encoders/* 142 + sudo rmdir /config/vkms/my-vkms/connectors/* 143 + sudo rmdir /config/vkms/my-vkms 144 + 54 145 Testing With IGT 55 146 ================ 56 147 ··· 238 147 --------------------- 239 148 240 149 We want to be able to reconfigure vkms instance without having to reload the 241 - module. Use/Test-cases: 150 + module through configfs. Use/Test-cases: 242 151 243 152 - Hotplug/hotremove connectors on the fly (to be able to test DP MST handling 244 153 of compositors). 245 154 246 - - Configure planes/crtcs/connectors (we'd need some code to have more than 1 of 247 - them first). 248 - 249 155 - Change output configuration: Plug/unplug screens, change EDID, allow changing 250 156 the refresh rate. 251 - 252 - The currently proposed solution is to expose vkms configuration through 253 - configfs. All existing module options should be supported through configfs 254 - too. 255 157 256 158 Writeback support 257 159 -----------------
+14 -4
MAINTAINERS
··· 2017 2017 F: drivers/clocksource/arm_arch_timer.c 2018 2018 F: drivers/clocksource/arm_arch_timer_mmio.c 2019 2019 2020 + ARM ETHOS-U NPU DRIVER 2021 + M: Rob Herring (Arm) <robh@kernel.org> 2022 + M: Tomeu Vizoso <tomeu@tomeuvizoso.net> 2023 + L: dri-devel@lists.freedesktop.org 2024 + S: Supported 2025 + T: git https://gitlab.freedesktop.org/drm/misc/kernel.git 2026 + F: drivers/accel/ethosu/ 2027 + F: include/uapi/drm/ethosu_accel.h 2028 + 2020 2029 ARM GENERIC INTERRUPT CONTROLLER DRIVERS 2021 2030 M: Marc Zyngier <maz@kernel.org> 2022 2031 L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) ··· 2308 2299 F: drivers/clk/sunxi/ 2309 2300 2310 2301 ARM/Allwinner sunXi SoC support 2311 - M: Chen-Yu Tsai <wens@csie.org> 2302 + M: Chen-Yu Tsai <wens@kernel.org> 2312 2303 M: Jernej Skrabec <jernej.skrabec@gmail.com> 2313 2304 M: Samuel Holland <samuel@sholland.org> 2314 2305 L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) ··· 7648 7639 F: include/drm/drm_accel.h 7649 7640 7650 7641 DRM DRIVER FOR ALLWINNER DE2 AND DE3 ENGINE 7651 - M: Chen-Yu Tsai <wens@csie.org> 7642 + M: Chen-Yu Tsai <wens@kernel.org> 7652 7643 R: Jernej Skrabec <jernej.skrabec@gmail.com> 7653 7644 L: dri-devel@lists.freedesktop.org 7654 7645 S: Supported ··· 8262 8253 F: rust/kernel/drm/ 8263 8254 8264 8255 DRM DRIVERS FOR ALLWINNER A10 8265 - M: Chen-Yu Tsai <wens@csie.org> 8256 + M: Chen-Yu Tsai <wens@kernel.org> 8266 8257 L: dri-devel@lists.freedesktop.org 8267 8258 S: Supported 8268 8259 T: git https://gitlab.freedesktop.org/drm/misc/kernel.git ··· 8589 8580 T: git https://gitlab.freedesktop.org/drm/misc/kernel.git 8590 8581 F: drivers/gpu/drm/scheduler/ 8591 8582 F: include/drm/gpu_scheduler.h 8583 + F: include/drm/spsc_queue.h 8592 8584 8593 8585 DRM GPUVM 8594 8586 M: Danilo Krummrich <dakr@kernel.org> ··· 27717 27707 N: axp288 27718 27708 27719 27709 X-POWERS MULTIFUNCTION PMIC DEVICE DRIVERS 27720 - M: Chen-Yu Tsai <wens@csie.org> 27710 + M: Chen-Yu Tsai <wens@kernel.org> 27721 27711 L: linux-kernel@vger.kernel.org 27722 27712 S: Maintained 27723 27713 N: axp[128]
+1
drivers/accel/Kconfig
··· 25 25 and debugfs). 26 26 27 27 source "drivers/accel/amdxdna/Kconfig" 28 + source "drivers/accel/ethosu/Kconfig" 28 29 source "drivers/accel/habanalabs/Kconfig" 29 30 source "drivers/accel/ivpu/Kconfig" 30 31 source "drivers/accel/qaic/Kconfig"
+1
drivers/accel/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 3 3 obj-$(CONFIG_DRM_ACCEL_AMDXDNA) += amdxdna/ 4 + obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) += ethosu/ 4 5 obj-$(CONFIG_DRM_ACCEL_HABANALABS) += habanalabs/ 5 6 obj-$(CONFIG_DRM_ACCEL_IVPU) += ivpu/ 6 7 obj-$(CONFIG_DRM_ACCEL_QAIC) += qaic/
+1 -1
drivers/accel/amdxdna/aie2_ctx.c
··· 879 879 aie2_cmd_wait(hwctx, seq); 880 880 if (cmd.result) { 881 881 XDNA_ERR(xdna, "Response failure 0x%x", cmd.result); 882 - return ret; 882 + return -EINVAL; 883 883 } 884 884 885 885 return 0;
+2 -2
drivers/accel/amdxdna/aie2_pci.c
··· 822 822 } 823 823 824 824 args->buffer_size -= (u32)(array_args.buffer - args->buffer); 825 - return ret; 825 + return 0; 826 826 } 827 827 828 828 static int aie2_get_info(struct amdxdna_client *client, struct amdxdna_drm_get_info *args) ··· 904 904 args->num_element = (u32)((array_args.buffer - args->buffer) / 905 905 args->element_size); 906 906 907 - return ret; 907 + return 0; 908 908 } 909 909 910 910 static int aie2_get_array(struct amdxdna_client *client,
+11
drivers/accel/ethosu/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + 3 + config DRM_ACCEL_ARM_ETHOSU 4 + tristate "Arm Ethos-U65/U85 NPU" 5 + depends on HAS_IOMEM 6 + depends on DRM_ACCEL 7 + select DRM_GEM_DMA_HELPER 8 + select DRM_SCHED 9 + select GENERIC_ALLOCATOR 10 + help 11 + Enables driver for Arm Ethos-U65/U85 NPUs
+4
drivers/accel/ethosu/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + 3 + obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o 4 + ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o
+197
drivers/accel/ethosu/ethosu_device.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only or MIT */ 2 + /* Copyright 2025 Arm, Ltd. */ 3 + 4 + #ifndef __ETHOSU_DEVICE_H__ 5 + #define __ETHOSU_DEVICE_H__ 6 + 7 + #include <linux/bitfield.h> 8 + #include <linux/bits.h> 9 + #include <linux/types.h> 10 + 11 + #include <drm/drm_device.h> 12 + #include <drm/gpu_scheduler.h> 13 + 14 + #include <drm/ethosu_accel.h> 15 + 16 + struct clk; 17 + struct gen_pool; 18 + 19 + #define NPU_REG_ID 0x0000 20 + #define NPU_REG_STATUS 0x0004 21 + #define NPU_REG_CMD 0x0008 22 + #define NPU_REG_RESET 0x000c 23 + #define NPU_REG_QBASE 0x0010 24 + #define NPU_REG_QBASE_HI 0x0014 25 + #define NPU_REG_QREAD 0x0018 26 + #define NPU_REG_QCONFIG 0x001c 27 + #define NPU_REG_QSIZE 0x0020 28 + #define NPU_REG_PROT 0x0024 29 + #define NPU_REG_CONFIG 0x0028 30 + #define NPU_REG_REGIONCFG 0x003c 31 + #define NPU_REG_AXILIMIT0 0x0040 // U65 32 + #define NPU_REG_AXILIMIT1 0x0044 // U65 33 + #define NPU_REG_AXILIMIT2 0x0048 // U65 34 + #define NPU_REG_AXILIMIT3 0x004c // U65 35 + #define NPU_REG_MEM_ATTR0 0x0040 // U85 36 + #define NPU_REG_MEM_ATTR1 0x0044 // U85 37 + #define NPU_REG_MEM_ATTR2 0x0048 // U85 38 + #define NPU_REG_MEM_ATTR3 0x004c // U85 39 + #define NPU_REG_AXI_SRAM 0x0050 // U85 40 + #define NPU_REG_AXI_EXT 0x0054 // U85 41 + 42 + #define NPU_REG_BASEP(x) (0x0080 + (x) * 8) 43 + #define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8) 44 + #define NPU_BASEP_REGION_MAX 8 45 + 46 + #define ID_ARCH_MAJOR_MASK GENMASK(31, 28) 47 + #define ID_ARCH_MINOR_MASK GENMASK(27, 20) 48 + #define ID_ARCH_PATCH_MASK GENMASK(19, 16) 49 + #define ID_VER_MAJOR_MASK GENMASK(11, 8) 50 + #define ID_VER_MINOR_MASK GENMASK(7, 4) 51 + 52 + #define CONFIG_MACS_PER_CC_MASK GENMASK(3, 0) 53 + #define CONFIG_CMD_STREAM_VER_MASK GENMASK(7, 4) 54 + 55 + #define STATUS_STATE_RUNNING BIT(0) 56 + #define STATUS_IRQ_RAISED BIT(1) 57 + #define STATUS_BUS_STATUS BIT(2) 58 + #define STATUS_RESET_STATUS BIT(3) 59 + #define STATUS_CMD_PARSE_ERR BIT(4) 60 + #define STATUS_CMD_END_REACHED BIT(5) 61 + 62 + #define CMD_CLEAR_IRQ BIT(1) 63 + #define CMD_TRANSITION_TO_RUN BIT(0) 64 + 65 + #define RESET_PENDING_CSL BIT(1) 66 + #define RESET_PENDING_CPL BIT(0) 67 + 68 + #define PROT_ACTIVE_CSL BIT(1) 69 + 70 + enum ethosu_cmds { 71 + NPU_OP_CONV = 0x2, 72 + NPU_OP_DEPTHWISE = 0x3, 73 + NPU_OP_POOL = 0x5, 74 + NPU_OP_ELEMENTWISE = 0x6, 75 + NPU_OP_RESIZE = 0x7, // U85 only 76 + NPU_OP_DMA_START = 0x10, 77 + NPU_SET_IFM_PAD_TOP = 0x100, 78 + NPU_SET_IFM_PAD_LEFT = 0x101, 79 + NPU_SET_IFM_PAD_RIGHT = 0x102, 80 + NPU_SET_IFM_PAD_BOTTOM = 0x103, 81 + NPU_SET_IFM_DEPTH_M1 = 0x104, 82 + NPU_SET_IFM_PRECISION = 0x105, 83 + NPU_SET_IFM_BROADCAST = 0x108, 84 + NPU_SET_IFM_WIDTH0_M1 = 0x10a, 85 + NPU_SET_IFM_HEIGHT0_M1 = 0x10b, 86 + NPU_SET_IFM_HEIGHT1_M1 = 0x10c, 87 + NPU_SET_IFM_REGION = 0x10f, 88 + NPU_SET_OFM_WIDTH_M1 = 0x111, 89 + NPU_SET_OFM_HEIGHT_M1 = 0x112, 90 + NPU_SET_OFM_DEPTH_M1 = 0x113, 91 + NPU_SET_OFM_PRECISION = 0x114, 92 + NPU_SET_OFM_WIDTH0_M1 = 0x11a, 93 + NPU_SET_OFM_HEIGHT0_M1 = 0x11b, 94 + NPU_SET_OFM_HEIGHT1_M1 = 0x11c, 95 + NPU_SET_OFM_REGION = 0x11f, 96 + NPU_SET_KERNEL_WIDTH_M1 = 0x120, 97 + NPU_SET_KERNEL_HEIGHT_M1 = 0x121, 98 + NPU_SET_KERNEL_STRIDE = 0x122, 99 + NPU_SET_WEIGHT_REGION = 0x128, 100 + NPU_SET_SCALE_REGION = 0x129, 101 + NPU_SET_DMA0_SRC_REGION = 0x130, 102 + NPU_SET_DMA0_DST_REGION = 0x131, 103 + NPU_SET_DMA0_SIZE0 = 0x132, 104 + NPU_SET_DMA0_SIZE1 = 0x133, 105 + NPU_SET_IFM2_BROADCAST = 0x180, 106 + NPU_SET_IFM2_PRECISION = 0x185, 107 + NPU_SET_IFM2_WIDTH0_M1 = 0x18a, 108 + NPU_SET_IFM2_HEIGHT0_M1 = 0x18b, 109 + NPU_SET_IFM2_HEIGHT1_M1 = 0x18c, 110 + NPU_SET_IFM2_REGION = 0x18f, 111 + NPU_SET_IFM_BASE0 = 0x4000, 112 + NPU_SET_IFM_BASE1 = 0x4001, 113 + NPU_SET_IFM_BASE2 = 0x4002, 114 + NPU_SET_IFM_BASE3 = 0x4003, 115 + NPU_SET_IFM_STRIDE_X = 0x4004, 116 + NPU_SET_IFM_STRIDE_Y = 0x4005, 117 + NPU_SET_IFM_STRIDE_C = 0x4006, 118 + NPU_SET_OFM_BASE0 = 0x4010, 119 + NPU_SET_OFM_BASE1 = 0x4011, 120 + NPU_SET_OFM_BASE2 = 0x4012, 121 + NPU_SET_OFM_BASE3 = 0x4013, 122 + NPU_SET_OFM_STRIDE_X = 0x4014, 123 + NPU_SET_OFM_STRIDE_Y = 0x4015, 124 + NPU_SET_OFM_STRIDE_C = 0x4016, 125 + NPU_SET_WEIGHT_BASE = 0x4020, 126 + NPU_SET_WEIGHT_LENGTH = 0x4021, 127 + NPU_SET_SCALE_BASE = 0x4022, 128 + NPU_SET_SCALE_LENGTH = 0x4023, 129 + NPU_SET_DMA0_SRC = 0x4030, 130 + NPU_SET_DMA0_DST = 0x4031, 131 + NPU_SET_DMA0_LEN = 0x4032, 132 + NPU_SET_DMA0_SRC_STRIDE0 = 0x4033, 133 + NPU_SET_DMA0_SRC_STRIDE1 = 0x4034, 134 + NPU_SET_DMA0_DST_STRIDE0 = 0x4035, 135 + NPU_SET_DMA0_DST_STRIDE1 = 0x4036, 136 + NPU_SET_IFM2_BASE0 = 0x4080, 137 + NPU_SET_IFM2_BASE1 = 0x4081, 138 + NPU_SET_IFM2_BASE2 = 0x4082, 139 + NPU_SET_IFM2_BASE3 = 0x4083, 140 + NPU_SET_IFM2_STRIDE_X = 0x4084, 141 + NPU_SET_IFM2_STRIDE_Y = 0x4085, 142 + NPU_SET_IFM2_STRIDE_C = 0x4086, 143 + NPU_SET_WEIGHT1_BASE = 0x4090, 144 + NPU_SET_WEIGHT1_LENGTH = 0x4091, 145 + NPU_SET_SCALE1_BASE = 0x4092, 146 + NPU_SET_WEIGHT2_BASE = 0x4092, 147 + NPU_SET_SCALE1_LENGTH = 0x4093, 148 + NPU_SET_WEIGHT2_LENGTH = 0x4093, 149 + NPU_SET_WEIGHT3_BASE = 0x4094, 150 + NPU_SET_WEIGHT3_LENGTH = 0x4095, 151 + }; 152 + 153 + #define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */ 154 + 155 + /** 156 + * struct ethosu_device - Ethosu device 157 + */ 158 + struct ethosu_device { 159 + /** @base: Base drm_device. */ 160 + struct drm_device base; 161 + 162 + /** @iomem: CPU mapping of the registers. */ 163 + void __iomem *regs; 164 + 165 + void __iomem *sram; 166 + struct gen_pool *srampool; 167 + dma_addr_t sramphys; 168 + 169 + struct clk_bulk_data *clks; 170 + int num_clks; 171 + int irq; 172 + 173 + struct drm_ethosu_npu_info npu_info; 174 + 175 + struct ethosu_job *in_flight_job; 176 + /* For in_flight_job and ethosu_job_hw_submit() */ 177 + struct mutex job_lock; 178 + 179 + /* For dma_fence */ 180 + spinlock_t fence_lock; 181 + 182 + struct drm_gpu_scheduler sched; 183 + /* For ethosu_job_do_push() */ 184 + struct mutex sched_lock; 185 + u64 fence_context; 186 + u64 emit_seqno; 187 + }; 188 + 189 + #define to_ethosu_device(drm_dev) \ 190 + ((struct ethosu_device *)container_of(drm_dev, struct ethosu_device, base)) 191 + 192 + static inline bool ethosu_is_u65(const struct ethosu_device *ethosudev) 193 + { 194 + return FIELD_GET(ID_ARCH_MAJOR_MASK, ethosudev->npu_info.id) == 1; 195 + } 196 + 197 + #endif
+403
drivers/accel/ethosu/ethosu_drv.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only or MIT 2 + // Copyright (C) 2025 Arm, Ltd. 3 + 4 + #include <linux/bitfield.h> 5 + #include <linux/clk.h> 6 + #include <linux/genalloc.h> 7 + #include <linux/io.h> 8 + #include <linux/iopoll.h> 9 + #include <linux/module.h> 10 + #include <linux/mod_devicetable.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/pm_runtime.h> 13 + 14 + #include <drm/drm_drv.h> 15 + #include <drm/drm_ioctl.h> 16 + #include <drm/drm_utils.h> 17 + #include <drm/drm_gem.h> 18 + #include <drm/drm_accel.h> 19 + #include <drm/ethosu_accel.h> 20 + 21 + #include "ethosu_drv.h" 22 + #include "ethosu_device.h" 23 + #include "ethosu_gem.h" 24 + #include "ethosu_job.h" 25 + 26 + static int ethosu_ioctl_dev_query(struct drm_device *ddev, void *data, 27 + struct drm_file *file) 28 + { 29 + struct ethosu_device *ethosudev = to_ethosu_device(ddev); 30 + struct drm_ethosu_dev_query *args = data; 31 + 32 + if (!args->pointer) { 33 + switch (args->type) { 34 + case DRM_ETHOSU_DEV_QUERY_NPU_INFO: 35 + args->size = sizeof(ethosudev->npu_info); 36 + return 0; 37 + default: 38 + return -EINVAL; 39 + } 40 + } 41 + 42 + switch (args->type) { 43 + case DRM_ETHOSU_DEV_QUERY_NPU_INFO: 44 + if (args->size < offsetofend(struct drm_ethosu_npu_info, sram_size)) 45 + return -EINVAL; 46 + return copy_struct_to_user(u64_to_user_ptr(args->pointer), 47 + args->size, 48 + &ethosudev->npu_info, 49 + sizeof(ethosudev->npu_info), NULL); 50 + default: 51 + return -EINVAL; 52 + } 53 + } 54 + 55 + #define ETHOSU_BO_FLAGS DRM_ETHOSU_BO_NO_MMAP 56 + 57 + static int ethosu_ioctl_bo_create(struct drm_device *ddev, void *data, 58 + struct drm_file *file) 59 + { 60 + struct drm_ethosu_bo_create *args = data; 61 + int cookie, ret; 62 + 63 + if (!drm_dev_enter(ddev, &cookie)) 64 + return -ENODEV; 65 + 66 + if (!args->size || (args->flags & ~ETHOSU_BO_FLAGS)) { 67 + ret = -EINVAL; 68 + goto out_dev_exit; 69 + } 70 + 71 + ret = ethosu_gem_create_with_handle(file, ddev, &args->size, 72 + args->flags, &args->handle); 73 + 74 + out_dev_exit: 75 + drm_dev_exit(cookie); 76 + return ret; 77 + } 78 + 79 + static int ethosu_ioctl_bo_wait(struct drm_device *ddev, void *data, 80 + struct drm_file *file) 81 + { 82 + struct drm_ethosu_bo_wait *args = data; 83 + int cookie, ret; 84 + unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns); 85 + 86 + if (args->pad) 87 + return -EINVAL; 88 + 89 + if (!drm_dev_enter(ddev, &cookie)) 90 + return -ENODEV; 91 + 92 + ret = drm_gem_dma_resv_wait(file, args->handle, true, timeout); 93 + 94 + drm_dev_exit(cookie); 95 + return ret; 96 + } 97 + 98 + static int ethosu_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data, 99 + struct drm_file *file) 100 + { 101 + struct drm_ethosu_bo_mmap_offset *args = data; 102 + struct drm_gem_object *obj; 103 + 104 + if (args->pad) 105 + return -EINVAL; 106 + 107 + obj = drm_gem_object_lookup(file, args->handle); 108 + if (!obj) 109 + return -ENOENT; 110 + 111 + args->offset = drm_vma_node_offset_addr(&obj->vma_node); 112 + drm_gem_object_put(obj); 113 + return 0; 114 + } 115 + 116 + static int ethosu_ioctl_cmdstream_bo_create(struct drm_device *ddev, void *data, 117 + struct drm_file *file) 118 + { 119 + struct drm_ethosu_cmdstream_bo_create *args = data; 120 + int cookie, ret; 121 + 122 + if (!drm_dev_enter(ddev, &cookie)) 123 + return -ENODEV; 124 + 125 + if (!args->size || !args->data || args->pad || args->flags) { 126 + ret = -EINVAL; 127 + goto out_dev_exit; 128 + } 129 + 130 + args->flags |= DRM_ETHOSU_BO_NO_MMAP; 131 + 132 + ret = ethosu_gem_cmdstream_create(file, ddev, args->size, args->data, 133 + args->flags, &args->handle); 134 + 135 + out_dev_exit: 136 + drm_dev_exit(cookie); 137 + return ret; 138 + } 139 + 140 + static int ethosu_open(struct drm_device *ddev, struct drm_file *file) 141 + { 142 + int ret = 0; 143 + 144 + if (!try_module_get(THIS_MODULE)) 145 + return -EINVAL; 146 + 147 + struct ethosu_file_priv __free(kfree) *priv = kzalloc(sizeof(*priv), GFP_KERNEL); 148 + if (!priv) { 149 + ret = -ENOMEM; 150 + goto err_put_mod; 151 + } 152 + priv->edev = to_ethosu_device(ddev); 153 + 154 + ret = ethosu_job_open(priv); 155 + if (ret) 156 + goto err_put_mod; 157 + 158 + file->driver_priv = no_free_ptr(priv); 159 + return 0; 160 + 161 + err_put_mod: 162 + module_put(THIS_MODULE); 163 + return ret; 164 + } 165 + 166 + static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file) 167 + { 168 + ethosu_job_close(file->driver_priv); 169 + kfree(file->driver_priv); 170 + module_put(THIS_MODULE); 171 + } 172 + 173 + static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = { 174 + #define ETHOSU_IOCTL(n, func, flags) \ 175 + DRM_IOCTL_DEF_DRV(ETHOSU_##n, ethosu_ioctl_##func, flags) 176 + 177 + ETHOSU_IOCTL(DEV_QUERY, dev_query, 0), 178 + ETHOSU_IOCTL(BO_CREATE, bo_create, 0), 179 + ETHOSU_IOCTL(BO_WAIT, bo_wait, 0), 180 + ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0), 181 + ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0), 182 + ETHOSU_IOCTL(SUBMIT, submit, 0), 183 + }; 184 + 185 + DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops); 186 + 187 + /* 188 + * Ethosu driver version: 189 + * - 1.0 - initial interface 190 + */ 191 + static const struct drm_driver ethosu_drm_driver = { 192 + .driver_features = DRIVER_COMPUTE_ACCEL | DRIVER_GEM, 193 + .open = ethosu_open, 194 + .postclose = ethosu_postclose, 195 + .ioctls = ethosu_drm_driver_ioctls, 196 + .num_ioctls = ARRAY_SIZE(ethosu_drm_driver_ioctls), 197 + .fops = &ethosu_drm_driver_fops, 198 + .name = "ethosu", 199 + .desc = "Arm Ethos-U Accel driver", 200 + .major = 1, 201 + .minor = 0, 202 + 203 + .gem_create_object = ethosu_gem_create_object, 204 + }; 205 + 206 + #define U65_DRAM_AXI_LIMIT_CFG 0x1f3f0002 207 + #define U65_SRAM_AXI_LIMIT_CFG 0x1f3f00b0 208 + #define U85_AXI_EXT_CFG 0x00021f3f 209 + #define U85_AXI_SRAM_CFG 0x00021f3f 210 + #define U85_MEM_ATTR0_CFG 0x00000000 211 + #define U85_MEM_ATTR2_CFG 0x000000b7 212 + 213 + static int ethosu_reset(struct ethosu_device *ethosudev) 214 + { 215 + int ret; 216 + u32 reg; 217 + 218 + writel_relaxed(RESET_PENDING_CSL, ethosudev->regs + NPU_REG_RESET); 219 + ret = readl_poll_timeout(ethosudev->regs + NPU_REG_STATUS, reg, 220 + !FIELD_GET(STATUS_RESET_STATUS, reg), 221 + USEC_PER_MSEC, USEC_PER_SEC); 222 + if (ret) 223 + return ret; 224 + 225 + if (!FIELD_GET(PROT_ACTIVE_CSL, readl_relaxed(ethosudev->regs + NPU_REG_PROT))) { 226 + dev_warn(ethosudev->base.dev, "Could not reset to non-secure mode (PROT = %x)\n", 227 + readl_relaxed(ethosudev->regs + NPU_REG_PROT)); 228 + } 229 + 230 + /* 231 + * Assign region 2 (SRAM) to AXI M0 (AXILIMIT0), 232 + * everything else to AXI M1 (AXILIMIT2) 233 + */ 234 + writel_relaxed(0x0000aa8a, ethosudev->regs + NPU_REG_REGIONCFG); 235 + if (ethosu_is_u65(ethosudev)) { 236 + writel_relaxed(U65_SRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT0); 237 + writel_relaxed(U65_DRAM_AXI_LIMIT_CFG, ethosudev->regs + NPU_REG_AXILIMIT2); 238 + } else { 239 + writel_relaxed(U85_AXI_SRAM_CFG, ethosudev->regs + NPU_REG_AXI_SRAM); 240 + writel_relaxed(U85_AXI_EXT_CFG, ethosudev->regs + NPU_REG_AXI_EXT); 241 + writel_relaxed(U85_MEM_ATTR0_CFG, ethosudev->regs + NPU_REG_MEM_ATTR0); // SRAM 242 + writel_relaxed(U85_MEM_ATTR2_CFG, ethosudev->regs + NPU_REG_MEM_ATTR2); // DRAM 243 + } 244 + 245 + if (ethosudev->sram) 246 + memset_io(ethosudev->sram, 0, ethosudev->npu_info.sram_size); 247 + 248 + return 0; 249 + } 250 + 251 + static int ethosu_device_resume(struct device *dev) 252 + { 253 + struct ethosu_device *ethosudev = dev_get_drvdata(dev); 254 + int ret; 255 + 256 + ret = clk_bulk_prepare_enable(ethosudev->num_clks, ethosudev->clks); 257 + if (ret) 258 + return ret; 259 + 260 + ret = ethosu_reset(ethosudev); 261 + if (!ret) 262 + return 0; 263 + 264 + clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks); 265 + return ret; 266 + } 267 + 268 + static int ethosu_device_suspend(struct device *dev) 269 + { 270 + struct ethosu_device *ethosudev = dev_get_drvdata(dev); 271 + 272 + clk_bulk_disable_unprepare(ethosudev->num_clks, ethosudev->clks); 273 + return 0; 274 + } 275 + 276 + static int ethosu_sram_init(struct ethosu_device *ethosudev) 277 + { 278 + ethosudev->npu_info.sram_size = 0; 279 + 280 + ethosudev->srampool = of_gen_pool_get(ethosudev->base.dev->of_node, "sram", 0); 281 + if (!ethosudev->srampool) 282 + return 0; 283 + 284 + ethosudev->npu_info.sram_size = gen_pool_size(ethosudev->srampool); 285 + 286 + ethosudev->sram = (void __iomem *)gen_pool_dma_alloc(ethosudev->srampool, 287 + ethosudev->npu_info.sram_size, 288 + &ethosudev->sramphys); 289 + if (!ethosudev->sram) { 290 + dev_err(ethosudev->base.dev, "failed to allocate from SRAM pool\n"); 291 + return -ENOMEM; 292 + } 293 + 294 + return 0; 295 + } 296 + 297 + static int ethosu_init(struct ethosu_device *ethosudev) 298 + { 299 + int ret; 300 + u32 id, config; 301 + 302 + ret = ethosu_device_resume(ethosudev->base.dev); 303 + if (ret) 304 + return ret; 305 + 306 + pm_runtime_set_autosuspend_delay(ethosudev->base.dev, 50); 307 + pm_runtime_use_autosuspend(ethosudev->base.dev); 308 + ret = devm_pm_runtime_set_active_enabled(ethosudev->base.dev); 309 + if (ret) 310 + return ret; 311 + pm_runtime_get_noresume(ethosudev->base.dev); 312 + 313 + ethosudev->npu_info.id = id = readl_relaxed(ethosudev->regs + NPU_REG_ID); 314 + ethosudev->npu_info.config = config = readl_relaxed(ethosudev->regs + NPU_REG_CONFIG); 315 + 316 + ethosu_sram_init(ethosudev); 317 + 318 + dev_info(ethosudev->base.dev, 319 + "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n", 320 + FIELD_GET(ID_ARCH_MAJOR_MASK, id), 321 + FIELD_GET(ID_ARCH_MINOR_MASK, id), 322 + FIELD_GET(ID_ARCH_PATCH_MASK, id), 323 + FIELD_GET(ID_VER_MAJOR_MASK, id), 324 + FIELD_GET(ID_VER_MINOR_MASK, id), 325 + FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config), 326 + 1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config), 327 + ethosudev->npu_info.sram_size / 1024); 328 + 329 + return 0; 330 + } 331 + 332 + static int ethosu_probe(struct platform_device *pdev) 333 + { 334 + int ret; 335 + struct ethosu_device *ethosudev; 336 + 337 + ethosudev = devm_drm_dev_alloc(&pdev->dev, &ethosu_drm_driver, 338 + struct ethosu_device, base); 339 + if (IS_ERR(ethosudev)) 340 + return -ENOMEM; 341 + platform_set_drvdata(pdev, ethosudev); 342 + 343 + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); 344 + 345 + ethosudev->regs = devm_platform_ioremap_resource(pdev, 0); 346 + 347 + ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &ethosudev->clks); 348 + if (ethosudev->num_clks < 0) 349 + return ethosudev->num_clks; 350 + 351 + ret = ethosu_job_init(ethosudev); 352 + if (ret) 353 + return ret; 354 + 355 + ret = ethosu_init(ethosudev); 356 + if (ret) 357 + return ret; 358 + 359 + ret = drm_dev_register(&ethosudev->base, 0); 360 + if (ret) 361 + pm_runtime_dont_use_autosuspend(ethosudev->base.dev); 362 + 363 + pm_runtime_put_autosuspend(ethosudev->base.dev); 364 + return ret; 365 + } 366 + 367 + static void ethosu_remove(struct platform_device *pdev) 368 + { 369 + struct ethosu_device *ethosudev = dev_get_drvdata(&pdev->dev); 370 + 371 + drm_dev_unregister(&ethosudev->base); 372 + ethosu_job_fini(ethosudev); 373 + if (ethosudev->sram) 374 + gen_pool_free(ethosudev->srampool, (unsigned long)ethosudev->sram, 375 + ethosudev->npu_info.sram_size); 376 + } 377 + 378 + static const struct of_device_id dt_match[] = { 379 + { .compatible = "arm,ethos-u65" }, 380 + { .compatible = "arm,ethos-u85" }, 381 + {} 382 + }; 383 + MODULE_DEVICE_TABLE(of, dt_match); 384 + 385 + static DEFINE_RUNTIME_DEV_PM_OPS(ethosu_pm_ops, 386 + ethosu_device_suspend, 387 + ethosu_device_resume, 388 + NULL); 389 + 390 + static struct platform_driver ethosu_driver = { 391 + .probe = ethosu_probe, 392 + .remove = ethosu_remove, 393 + .driver = { 394 + .name = "ethosu", 395 + .pm = pm_ptr(&ethosu_pm_ops), 396 + .of_match_table = dt_match, 397 + }, 398 + }; 399 + module_platform_driver(ethosu_driver); 400 + 401 + MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); 402 + MODULE_DESCRIPTION("Arm Ethos-U Accel Driver"); 403 + MODULE_LICENSE("Dual MIT/GPL");
+15
drivers/accel/ethosu/ethosu_drv.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ 2 + /* Copyright 2025 Arm, Ltd. */ 3 + #ifndef __ETHOSU_DRV_H__ 4 + #define __ETHOSU_DRV_H__ 5 + 6 + #include <drm/gpu_scheduler.h> 7 + 8 + struct ethosu_device; 9 + 10 + struct ethosu_file_priv { 11 + struct ethosu_device *edev; 12 + struct drm_sched_entity sched_entity; 13 + }; 14 + 15 + #endif
+704
drivers/accel/ethosu/ethosu_gem.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only or MIT 2 + /* Copyright 2025 Arm, Ltd. */ 3 + 4 + #include <linux/err.h> 5 + #include <linux/slab.h> 6 + 7 + #include <drm/ethosu_accel.h> 8 + 9 + #include "ethosu_device.h" 10 + #include "ethosu_gem.h" 11 + 12 + static void ethosu_gem_free_object(struct drm_gem_object *obj) 13 + { 14 + struct ethosu_gem_object *bo = to_ethosu_bo(obj); 15 + 16 + kfree(bo->info); 17 + drm_gem_free_mmap_offset(&bo->base.base); 18 + drm_gem_dma_free(&bo->base); 19 + } 20 + 21 + static int ethosu_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) 22 + { 23 + struct ethosu_gem_object *bo = to_ethosu_bo(obj); 24 + 25 + /* Don't allow mmap on objects that have the NO_MMAP flag set. */ 26 + if (bo->flags & DRM_ETHOSU_BO_NO_MMAP) 27 + return -EINVAL; 28 + 29 + return drm_gem_dma_object_mmap(obj, vma); 30 + } 31 + 32 + static const struct drm_gem_object_funcs ethosu_gem_funcs = { 33 + .free = ethosu_gem_free_object, 34 + .print_info = drm_gem_dma_object_print_info, 35 + .get_sg_table = drm_gem_dma_object_get_sg_table, 36 + .vmap = drm_gem_dma_object_vmap, 37 + .mmap = ethosu_gem_mmap, 38 + .vm_ops = &drm_gem_dma_vm_ops, 39 + }; 40 + 41 + /** 42 + * ethosu_gem_create_object - Implementation of driver->gem_create_object. 43 + * @ddev: DRM device 44 + * @size: Size in bytes of the memory the object will reference 45 + * 46 + * This lets the GEM helpers allocate object structs for us, and keep 47 + * our BO stats correct. 48 + */ 49 + struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev, size_t size) 50 + { 51 + struct ethosu_gem_object *obj; 52 + 53 + obj = kzalloc(sizeof(*obj), GFP_KERNEL); 54 + if (!obj) 55 + return ERR_PTR(-ENOMEM); 56 + 57 + obj->base.base.funcs = &ethosu_gem_funcs; 58 + return &obj->base.base; 59 + } 60 + 61 + /** 62 + * ethosu_gem_create_with_handle() - Create a GEM object and attach it to a handle. 63 + * @file: DRM file. 64 + * @ddev: DRM device. 65 + * @size: Size of the GEM object to allocate. 66 + * @flags: Combination of drm_ethosu_bo_flags flags. 67 + * @handle: Pointer holding the handle pointing to the new GEM object. 68 + * 69 + * Return: Zero on success 70 + */ 71 + int ethosu_gem_create_with_handle(struct drm_file *file, 72 + struct drm_device *ddev, 73 + u64 *size, u32 flags, u32 *handle) 74 + { 75 + struct drm_gem_dma_object *mem; 76 + struct ethosu_gem_object *bo; 77 + int ret; 78 + 79 + mem = drm_gem_dma_create(ddev, *size); 80 + if (IS_ERR(mem)) 81 + return PTR_ERR(mem); 82 + 83 + bo = to_ethosu_bo(&mem->base); 84 + bo->flags = flags; 85 + 86 + /* 87 + * Allocate an id of idr table where the obj is registered 88 + * and handle has the id what user can see. 89 + */ 90 + ret = drm_gem_handle_create(file, &mem->base, handle); 91 + if (!ret) 92 + *size = bo->base.base.size; 93 + 94 + /* drop reference from allocate - handle holds it now. */ 95 + drm_gem_object_put(&mem->base); 96 + 97 + return ret; 98 + } 99 + 100 + struct dma { 101 + s8 region; 102 + u64 len; 103 + u64 offset; 104 + s64 stride[2]; 105 + }; 106 + 107 + struct dma_state { 108 + u16 size0; 109 + u16 size1; 110 + s8 mode; 111 + struct dma src; 112 + struct dma dst; 113 + }; 114 + 115 + struct buffer { 116 + u64 base; 117 + u32 length; 118 + s8 region; 119 + }; 120 + 121 + struct feat_matrix { 122 + u64 base[4]; 123 + s64 stride_x; 124 + s64 stride_y; 125 + s64 stride_c; 126 + s8 region; 127 + u8 broadcast; 128 + u16 stride_kernel; 129 + u16 precision; 130 + u16 depth; 131 + u16 width; 132 + u16 width0; 133 + u16 height[3]; 134 + u8 pad_top; 135 + u8 pad_left; 136 + u8 pad_bottom; 137 + u8 pad_right; 138 + }; 139 + 140 + struct cmd_state { 141 + struct dma_state dma; 142 + struct buffer scale[2]; 143 + struct buffer weight[4]; 144 + struct feat_matrix ofm; 145 + struct feat_matrix ifm; 146 + struct feat_matrix ifm2; 147 + }; 148 + 149 + static void cmd_state_init(struct cmd_state *st) 150 + { 151 + /* Initialize to all 1s to detect missing setup */ 152 + memset(st, 0xff, sizeof(*st)); 153 + } 154 + 155 + static u64 cmd_to_addr(u32 *cmd) 156 + { 157 + return ((u64)((cmd[0] & 0xff0000) << 16)) | cmd[1]; 158 + } 159 + 160 + static u64 dma_length(struct ethosu_validated_cmdstream_info *info, 161 + struct dma_state *dma_st, struct dma *dma) 162 + { 163 + s8 mode = dma_st->mode; 164 + u64 len = dma->len; 165 + 166 + if (mode >= 1) { 167 + len += dma->stride[0]; 168 + len *= dma_st->size0; 169 + } 170 + if (mode == 2) { 171 + len += dma->stride[1]; 172 + len *= dma_st->size1; 173 + } 174 + if (dma->region >= 0) 175 + info->region_size[dma->region] = max(info->region_size[dma->region], 176 + len + dma->offset); 177 + 178 + return len; 179 + } 180 + 181 + static u64 feat_matrix_length(struct ethosu_validated_cmdstream_info *info, 182 + struct feat_matrix *fm, 183 + u32 x, u32 y, u32 c) 184 + { 185 + u32 element_size, storage = fm->precision >> 14; 186 + int tile = 0; 187 + u64 addr; 188 + 189 + if (fm->region < 0) 190 + return U64_MAX; 191 + 192 + switch (storage) { 193 + case 0: 194 + if (x >= fm->width0 + 1) { 195 + x -= fm->width0 + 1; 196 + tile += 1; 197 + } 198 + if (y >= fm->height[tile] + 1) { 199 + y -= fm->height[tile] + 1; 200 + tile += 2; 201 + } 202 + break; 203 + case 1: 204 + if (y >= fm->height[1] + 1) { 205 + y -= fm->height[1] + 1; 206 + tile = 2; 207 + } else if (y >= fm->height[0] + 1) { 208 + y -= fm->height[0] + 1; 209 + tile = 1; 210 + } 211 + break; 212 + } 213 + if (fm->base[tile] == U64_MAX) 214 + return U64_MAX; 215 + 216 + addr = fm->base[tile] + y * fm->stride_y; 217 + 218 + switch ((fm->precision >> 6) & 0x3) { // format 219 + case 0: //nhwc: 220 + addr += x * fm->stride_x + c; 221 + break; 222 + case 1: //nhcwb16: 223 + element_size = BIT((fm->precision >> 1) & 0x3); 224 + 225 + addr += (c / 16) * fm->stride_c + (16 * x + (c & 0xf)) * element_size; 226 + break; 227 + } 228 + 229 + info->region_size[fm->region] = max(info->region_size[fm->region], addr + 1); 230 + 231 + return addr; 232 + } 233 + 234 + static int calc_sizes(struct drm_device *ddev, 235 + struct ethosu_validated_cmdstream_info *info, 236 + u16 op, struct cmd_state *st, 237 + bool ifm, bool ifm2, bool weight, bool scale) 238 + { 239 + u64 len; 240 + 241 + if (ifm) { 242 + if (st->ifm.stride_kernel == U16_MAX) 243 + return -EINVAL; 244 + u32 stride_y = ((st->ifm.stride_kernel >> 8) & 0x2) + 245 + ((st->ifm.stride_kernel >> 1) & 0x1) + 1; 246 + u32 stride_x = ((st->ifm.stride_kernel >> 5) & 0x2) + 247 + (st->ifm.stride_kernel & 0x1) + 1; 248 + u32 ifm_height = st->ofm.height[2] * stride_y + 249 + st->ifm.height[2] - (st->ifm.pad_top + st->ifm.pad_bottom); 250 + u32 ifm_width = st->ofm.width * stride_x + 251 + st->ifm.width - (st->ifm.pad_left + st->ifm.pad_right); 252 + 253 + len = feat_matrix_length(info, &st->ifm, ifm_width, 254 + ifm_height, st->ifm.depth); 255 + dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", 256 + op, st->ifm.region, st->ifm.base[0], len); 257 + if (len == U64_MAX) 258 + return -EINVAL; 259 + } 260 + 261 + if (ifm2) { 262 + len = feat_matrix_length(info, &st->ifm2, st->ifm.depth, 263 + 0, st->ofm.depth); 264 + dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", 265 + op, st->ifm2.region, st->ifm2.base[0], len); 266 + if (len == U64_MAX) 267 + return -EINVAL; 268 + } 269 + 270 + if (weight) { 271 + dev_dbg(ddev->dev, "op %d: W:%d:0x%llx-0x%llx\n", 272 + op, st->weight[0].region, st->weight[0].base, 273 + st->weight[0].base + st->weight[0].length - 1); 274 + if (st->weight[0].region < 0 || st->weight[0].base == U64_MAX || 275 + st->weight[0].length == U32_MAX) 276 + return -EINVAL; 277 + info->region_size[st->weight[0].region] = 278 + max(info->region_size[st->weight[0].region], 279 + st->weight[0].base + st->weight[0].length); 280 + } 281 + 282 + if (scale) { 283 + dev_dbg(ddev->dev, "op %d: S:%d:0x%llx-0x%llx\n", 284 + op, st->scale[0].region, st->scale[0].base, 285 + st->scale[0].base + st->scale[0].length - 1); 286 + if (st->scale[0].region < 0 || st->scale[0].base == U64_MAX || 287 + st->scale[0].length == U32_MAX) 288 + return -EINVAL; 289 + info->region_size[st->scale[0].region] = 290 + max(info->region_size[st->scale[0].region], 291 + st->scale[0].base + st->scale[0].length); 292 + } 293 + 294 + len = feat_matrix_length(info, &st->ofm, st->ofm.width, 295 + st->ofm.height[2], st->ofm.depth); 296 + dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", 297 + op, st->ofm.region, st->ofm.base[0], len); 298 + if (len == U64_MAX) 299 + return -EINVAL; 300 + info->output_region[st->ofm.region] = true; 301 + 302 + return 0; 303 + } 304 + 305 + static int calc_sizes_elemwise(struct drm_device *ddev, 306 + struct ethosu_validated_cmdstream_info *info, 307 + u16 op, struct cmd_state *st, 308 + bool ifm, bool ifm2) 309 + { 310 + u32 height, width, depth; 311 + u64 len; 312 + 313 + if (ifm) { 314 + height = st->ifm.broadcast & 0x1 ? 0 : st->ofm.height[2]; 315 + width = st->ifm.broadcast & 0x2 ? 0 : st->ofm.width; 316 + depth = st->ifm.broadcast & 0x4 ? 0 : st->ofm.depth; 317 + 318 + len = feat_matrix_length(info, &st->ifm, width, 319 + height, depth); 320 + dev_dbg(ddev->dev, "op %d: IFM:%d:0x%llx-0x%llx\n", 321 + op, st->ifm.region, st->ifm.base[0], len); 322 + if (len == U64_MAX) 323 + return -EINVAL; 324 + } 325 + 326 + if (ifm2) { 327 + height = st->ifm2.broadcast & 0x1 ? 0 : st->ofm.height[2]; 328 + width = st->ifm2.broadcast & 0x2 ? 0 : st->ofm.width; 329 + depth = st->ifm2.broadcast & 0x4 ? 0 : st->ofm.depth; 330 + 331 + len = feat_matrix_length(info, &st->ifm2, width, 332 + height, depth); 333 + dev_dbg(ddev->dev, "op %d: IFM2:%d:0x%llx-0x%llx\n", 334 + op, st->ifm2.region, st->ifm2.base[0], len); 335 + if (len == U64_MAX) 336 + return -EINVAL; 337 + } 338 + 339 + len = feat_matrix_length(info, &st->ofm, st->ofm.width, 340 + st->ofm.height[2], st->ofm.depth); 341 + dev_dbg(ddev->dev, "op %d: OFM:%d:0x%llx-0x%llx\n", 342 + op, st->ofm.region, st->ofm.base[0], len); 343 + if (len == U64_MAX) 344 + return -EINVAL; 345 + info->output_region[st->ofm.region] = true; 346 + 347 + return 0; 348 + } 349 + 350 + static int ethosu_gem_cmdstream_copy_and_validate(struct drm_device *ddev, 351 + u32 __user *ucmds, 352 + struct ethosu_gem_object *bo, 353 + u32 size) 354 + { 355 + struct ethosu_validated_cmdstream_info __free(kfree) *info = kzalloc(sizeof(*info), GFP_KERNEL); 356 + struct ethosu_device *edev = to_ethosu_device(ddev); 357 + u32 *bocmds = bo->base.vaddr; 358 + struct cmd_state st; 359 + int i, ret; 360 + 361 + if (!info) 362 + return -ENOMEM; 363 + info->cmd_size = size; 364 + 365 + cmd_state_init(&st); 366 + 367 + for (i = 0; i < size / 4; i++) { 368 + bool use_ifm, use_ifm2, use_scale; 369 + u64 dstlen, srclen; 370 + u16 cmd, param; 371 + u32 cmds[2]; 372 + u64 addr; 373 + 374 + if (get_user(cmds[0], ucmds++)) 375 + return -EFAULT; 376 + 377 + bocmds[i] = cmds[0]; 378 + 379 + cmd = cmds[0]; 380 + param = cmds[0] >> 16; 381 + 382 + if (cmd & 0x4000) { 383 + if (get_user(cmds[1], ucmds++)) 384 + return -EFAULT; 385 + 386 + i++; 387 + bocmds[i] = cmds[1]; 388 + addr = cmd_to_addr(cmds); 389 + } 390 + 391 + switch (cmd) { 392 + case NPU_OP_DMA_START: 393 + srclen = dma_length(info, &st.dma, &st.dma.src); 394 + dstlen = dma_length(info, &st.dma, &st.dma.dst); 395 + 396 + if (st.dma.dst.region >= 0) 397 + info->output_region[st.dma.dst.region] = true; 398 + dev_dbg(ddev->dev, "cmd: DMA SRC:%d:0x%llx+0x%llx DST:%d:0x%llx+0x%llx\n", 399 + st.dma.src.region, st.dma.src.offset, srclen, 400 + st.dma.dst.region, st.dma.dst.offset, dstlen); 401 + break; 402 + case NPU_OP_CONV: 403 + case NPU_OP_DEPTHWISE: 404 + use_ifm2 = param & 0x1; // weights_ifm2 405 + use_scale = !(st.ofm.precision & 0x100); 406 + ret = calc_sizes(ddev, info, cmd, &st, true, use_ifm2, 407 + !use_ifm2, use_scale); 408 + if (ret) 409 + return ret; 410 + break; 411 + case NPU_OP_POOL: 412 + use_ifm = param != 0x4; // pooling mode 413 + use_scale = !(st.ofm.precision & 0x100); 414 + ret = calc_sizes(ddev, info, cmd, &st, use_ifm, false, 415 + false, use_scale); 416 + if (ret) 417 + return ret; 418 + break; 419 + case NPU_OP_ELEMENTWISE: 420 + use_ifm2 = !((st.ifm2.broadcast == 8) || (param == 5) || 421 + (param == 6) || (param == 7) || (param == 0x24)); 422 + use_ifm = st.ifm.broadcast != 8; 423 + ret = calc_sizes_elemwise(ddev, info, cmd, &st, use_ifm, use_ifm2); 424 + if (ret) 425 + return ret; 426 + break; 427 + case NPU_OP_RESIZE: // U85 only 428 + WARN_ON(1); // TODO 429 + break; 430 + case NPU_SET_KERNEL_WIDTH_M1: 431 + st.ifm.width = param; 432 + break; 433 + case NPU_SET_KERNEL_HEIGHT_M1: 434 + st.ifm.height[2] = param; 435 + break; 436 + case NPU_SET_KERNEL_STRIDE: 437 + st.ifm.stride_kernel = param; 438 + break; 439 + case NPU_SET_IFM_PAD_TOP: 440 + st.ifm.pad_top = param & 0x7f; 441 + break; 442 + case NPU_SET_IFM_PAD_LEFT: 443 + st.ifm.pad_left = param & 0x7f; 444 + break; 445 + case NPU_SET_IFM_PAD_RIGHT: 446 + st.ifm.pad_right = param & 0xff; 447 + break; 448 + case NPU_SET_IFM_PAD_BOTTOM: 449 + st.ifm.pad_bottom = param & 0xff; 450 + break; 451 + case NPU_SET_IFM_DEPTH_M1: 452 + st.ifm.depth = param; 453 + break; 454 + case NPU_SET_IFM_PRECISION: 455 + st.ifm.precision = param; 456 + break; 457 + case NPU_SET_IFM_BROADCAST: 458 + st.ifm.broadcast = param; 459 + break; 460 + case NPU_SET_IFM_REGION: 461 + st.ifm.region = param & 0x7f; 462 + break; 463 + case NPU_SET_IFM_WIDTH0_M1: 464 + st.ifm.width0 = param; 465 + break; 466 + case NPU_SET_IFM_HEIGHT0_M1: 467 + st.ifm.height[0] = param; 468 + break; 469 + case NPU_SET_IFM_HEIGHT1_M1: 470 + st.ifm.height[1] = param; 471 + break; 472 + case NPU_SET_IFM_BASE0: 473 + case NPU_SET_IFM_BASE1: 474 + case NPU_SET_IFM_BASE2: 475 + case NPU_SET_IFM_BASE3: 476 + st.ifm.base[cmd & 0x3] = addr; 477 + break; 478 + case NPU_SET_IFM_STRIDE_X: 479 + st.ifm.stride_x = addr; 480 + break; 481 + case NPU_SET_IFM_STRIDE_Y: 482 + st.ifm.stride_y = addr; 483 + break; 484 + case NPU_SET_IFM_STRIDE_C: 485 + st.ifm.stride_c = addr; 486 + break; 487 + 488 + case NPU_SET_OFM_WIDTH_M1: 489 + st.ofm.width = param; 490 + break; 491 + case NPU_SET_OFM_HEIGHT_M1: 492 + st.ofm.height[2] = param; 493 + break; 494 + case NPU_SET_OFM_DEPTH_M1: 495 + st.ofm.depth = param; 496 + break; 497 + case NPU_SET_OFM_PRECISION: 498 + st.ofm.precision = param; 499 + break; 500 + case NPU_SET_OFM_REGION: 501 + st.ofm.region = param & 0x7; 502 + break; 503 + case NPU_SET_OFM_WIDTH0_M1: 504 + st.ofm.width0 = param; 505 + break; 506 + case NPU_SET_OFM_HEIGHT0_M1: 507 + st.ofm.height[0] = param; 508 + break; 509 + case NPU_SET_OFM_HEIGHT1_M1: 510 + st.ofm.height[1] = param; 511 + break; 512 + case NPU_SET_OFM_BASE0: 513 + case NPU_SET_OFM_BASE1: 514 + case NPU_SET_OFM_BASE2: 515 + case NPU_SET_OFM_BASE3: 516 + st.ofm.base[cmd & 0x3] = addr; 517 + break; 518 + case NPU_SET_OFM_STRIDE_X: 519 + st.ofm.stride_x = addr; 520 + break; 521 + case NPU_SET_OFM_STRIDE_Y: 522 + st.ofm.stride_y = addr; 523 + break; 524 + case NPU_SET_OFM_STRIDE_C: 525 + st.ofm.stride_c = addr; 526 + break; 527 + 528 + case NPU_SET_IFM2_BROADCAST: 529 + st.ifm2.broadcast = param; 530 + break; 531 + case NPU_SET_IFM2_PRECISION: 532 + st.ifm2.precision = param; 533 + break; 534 + case NPU_SET_IFM2_REGION: 535 + st.ifm2.region = param & 0x7; 536 + break; 537 + case NPU_SET_IFM2_WIDTH0_M1: 538 + st.ifm2.width0 = param; 539 + break; 540 + case NPU_SET_IFM2_HEIGHT0_M1: 541 + st.ifm2.height[0] = param; 542 + break; 543 + case NPU_SET_IFM2_HEIGHT1_M1: 544 + st.ifm2.height[1] = param; 545 + break; 546 + case NPU_SET_IFM2_BASE0: 547 + case NPU_SET_IFM2_BASE1: 548 + case NPU_SET_IFM2_BASE2: 549 + case NPU_SET_IFM2_BASE3: 550 + st.ifm2.base[cmd & 0x3] = addr; 551 + break; 552 + case NPU_SET_IFM2_STRIDE_X: 553 + st.ifm2.stride_x = addr; 554 + break; 555 + case NPU_SET_IFM2_STRIDE_Y: 556 + st.ifm2.stride_y = addr; 557 + break; 558 + case NPU_SET_IFM2_STRIDE_C: 559 + st.ifm2.stride_c = addr; 560 + break; 561 + 562 + case NPU_SET_WEIGHT_REGION: 563 + st.weight[0].region = param & 0x7; 564 + break; 565 + case NPU_SET_SCALE_REGION: 566 + st.scale[0].region = param & 0x7; 567 + break; 568 + case NPU_SET_WEIGHT_BASE: 569 + st.weight[0].base = addr; 570 + break; 571 + case NPU_SET_WEIGHT_LENGTH: 572 + st.weight[0].length = cmds[1]; 573 + break; 574 + case NPU_SET_SCALE_BASE: 575 + st.scale[0].base = addr; 576 + break; 577 + case NPU_SET_SCALE_LENGTH: 578 + st.scale[0].length = cmds[1]; 579 + break; 580 + case NPU_SET_WEIGHT1_BASE: 581 + st.weight[1].base = addr; 582 + break; 583 + case NPU_SET_WEIGHT1_LENGTH: 584 + st.weight[1].length = cmds[1]; 585 + break; 586 + case NPU_SET_SCALE1_BASE: // NPU_SET_WEIGHT2_BASE (U85) 587 + if (ethosu_is_u65(edev)) 588 + st.scale[1].base = addr; 589 + else 590 + st.weight[2].base = addr; 591 + break; 592 + case NPU_SET_SCALE1_LENGTH: // NPU_SET_WEIGHT2_LENGTH (U85) 593 + if (ethosu_is_u65(edev)) 594 + st.scale[1].length = cmds[1]; 595 + else 596 + st.weight[1].length = cmds[1]; 597 + break; 598 + case NPU_SET_WEIGHT3_BASE: 599 + st.weight[3].base = addr; 600 + break; 601 + case NPU_SET_WEIGHT3_LENGTH: 602 + st.weight[3].length = cmds[1]; 603 + break; 604 + 605 + case NPU_SET_DMA0_SRC_REGION: 606 + if (param & 0x100) 607 + st.dma.src.region = -1; 608 + else 609 + st.dma.src.region = param & 0x7; 610 + st.dma.mode = (param >> 9) & 0x3; 611 + break; 612 + case NPU_SET_DMA0_DST_REGION: 613 + if (param & 0x100) 614 + st.dma.dst.region = -1; 615 + else 616 + st.dma.dst.region = param & 0x7; 617 + break; 618 + case NPU_SET_DMA0_SIZE0: 619 + st.dma.size0 = param; 620 + break; 621 + case NPU_SET_DMA0_SIZE1: 622 + st.dma.size1 = param; 623 + break; 624 + case NPU_SET_DMA0_SRC_STRIDE0: 625 + st.dma.src.stride[0] = ((s64)addr << 24) >> 24; 626 + break; 627 + case NPU_SET_DMA0_SRC_STRIDE1: 628 + st.dma.src.stride[1] = ((s64)addr << 24) >> 24; 629 + break; 630 + case NPU_SET_DMA0_DST_STRIDE0: 631 + st.dma.dst.stride[0] = ((s64)addr << 24) >> 24; 632 + break; 633 + case NPU_SET_DMA0_DST_STRIDE1: 634 + st.dma.dst.stride[1] = ((s64)addr << 24) >> 24; 635 + break; 636 + case NPU_SET_DMA0_SRC: 637 + st.dma.src.offset = addr; 638 + break; 639 + case NPU_SET_DMA0_DST: 640 + st.dma.dst.offset = addr; 641 + break; 642 + case NPU_SET_DMA0_LEN: 643 + st.dma.src.len = st.dma.dst.len = addr; 644 + break; 645 + default: 646 + break; 647 + } 648 + } 649 + 650 + for (i = 0; i < NPU_BASEP_REGION_MAX; i++) { 651 + if (!info->region_size[i]) 652 + continue; 653 + dev_dbg(ddev->dev, "region %d max size: 0x%llx\n", 654 + i, info->region_size[i]); 655 + } 656 + 657 + bo->info = no_free_ptr(info); 658 + return 0; 659 + } 660 + 661 + /** 662 + * ethosu_gem_cmdstream_create() - Create a GEM object and attach it to a handle. 663 + * @file: DRM file. 664 + * @ddev: DRM device. 665 + * @exclusive_vm: Exclusive VM. Not NULL if the GEM object can't be shared. 666 + * @size: Size of the GEM object to allocate. 667 + * @flags: Combination of drm_ethosu_bo_flags flags. 668 + * @handle: Pointer holding the handle pointing to the new GEM object. 669 + * 670 + * Return: Zero on success 671 + */ 672 + int ethosu_gem_cmdstream_create(struct drm_file *file, 673 + struct drm_device *ddev, 674 + u32 size, u64 data, u32 flags, u32 *handle) 675 + { 676 + int ret; 677 + struct drm_gem_dma_object *mem; 678 + struct ethosu_gem_object *bo; 679 + 680 + mem = drm_gem_dma_create(ddev, size); 681 + if (IS_ERR(mem)) 682 + return PTR_ERR(mem); 683 + 684 + bo = to_ethosu_bo(&mem->base); 685 + bo->flags = flags; 686 + 687 + ret = ethosu_gem_cmdstream_copy_and_validate(ddev, 688 + (void __user *)(uintptr_t)data, 689 + bo, size); 690 + if (ret) 691 + goto fail; 692 + 693 + /* 694 + * Allocate an id of idr table where the obj is registered 695 + * and handle has the id what user can see. 696 + */ 697 + ret = drm_gem_handle_create(file, &mem->base, handle); 698 + 699 + fail: 700 + /* drop reference from allocate - handle holds it now. */ 701 + drm_gem_object_put(&mem->base); 702 + 703 + return ret; 704 + }
+46
drivers/accel/ethosu/ethosu_gem.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 or MIT */ 2 + /* Copyright 2025 Arm, Ltd. */ 3 + 4 + #ifndef __ETHOSU_GEM_H__ 5 + #define __ETHOSU_GEM_H__ 6 + 7 + #include "ethosu_device.h" 8 + #include <drm/drm_gem_dma_helper.h> 9 + 10 + struct ethosu_validated_cmdstream_info { 11 + u32 cmd_size; 12 + u64 region_size[NPU_BASEP_REGION_MAX]; 13 + bool output_region[NPU_BASEP_REGION_MAX]; 14 + }; 15 + 16 + /** 17 + * struct ethosu_gem_object - Driver specific GEM object. 18 + */ 19 + struct ethosu_gem_object { 20 + /** @base: Inherit from drm_gem_shmem_object. */ 21 + struct drm_gem_dma_object base; 22 + 23 + struct ethosu_validated_cmdstream_info *info; 24 + 25 + /** @flags: Combination of drm_ethosu_bo_flags flags. */ 26 + u32 flags; 27 + }; 28 + 29 + static inline 30 + struct ethosu_gem_object *to_ethosu_bo(struct drm_gem_object *obj) 31 + { 32 + return container_of(to_drm_gem_dma_obj(obj), struct ethosu_gem_object, base); 33 + } 34 + 35 + struct drm_gem_object *ethosu_gem_create_object(struct drm_device *ddev, 36 + size_t size); 37 + 38 + int ethosu_gem_create_with_handle(struct drm_file *file, 39 + struct drm_device *ddev, 40 + u64 *size, u32 flags, uint32_t *handle); 41 + 42 + int ethosu_gem_cmdstream_create(struct drm_file *file, 43 + struct drm_device *ddev, 44 + u32 size, u64 data, u32 flags, u32 *handle); 45 + 46 + #endif /* __ETHOSU_GEM_H__ */
+496
drivers/accel/ethosu/ethosu_job.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only OR MIT 2 + /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */ 3 + /* Copyright 2025 Arm, Ltd. */ 4 + 5 + #include <linux/bitfield.h> 6 + #include <linux/genalloc.h> 7 + #include <linux/interrupt.h> 8 + #include <linux/iopoll.h> 9 + #include <linux/platform_device.h> 10 + #include <linux/pm_runtime.h> 11 + 12 + #include <drm/drm_file.h> 13 + #include <drm/drm_gem.h> 14 + #include <drm/drm_gem_dma_helper.h> 15 + #include <drm/ethosu_accel.h> 16 + 17 + #include "ethosu_device.h" 18 + #include "ethosu_drv.h" 19 + #include "ethosu_gem.h" 20 + #include "ethosu_job.h" 21 + 22 + #define JOB_TIMEOUT_MS 500 23 + 24 + static struct ethosu_job *to_ethosu_job(struct drm_sched_job *sched_job) 25 + { 26 + return container_of(sched_job, struct ethosu_job, base); 27 + } 28 + 29 + static const char *ethosu_fence_get_driver_name(struct dma_fence *fence) 30 + { 31 + return "ethosu"; 32 + } 33 + 34 + static const char *ethosu_fence_get_timeline_name(struct dma_fence *fence) 35 + { 36 + return "ethosu-npu"; 37 + } 38 + 39 + static const struct dma_fence_ops ethosu_fence_ops = { 40 + .get_driver_name = ethosu_fence_get_driver_name, 41 + .get_timeline_name = ethosu_fence_get_timeline_name, 42 + }; 43 + 44 + static void ethosu_job_hw_submit(struct ethosu_device *dev, struct ethosu_job *job) 45 + { 46 + struct drm_gem_dma_object *cmd_bo = to_drm_gem_dma_obj(job->cmd_bo); 47 + struct ethosu_validated_cmdstream_info *cmd_info = to_ethosu_bo(job->cmd_bo)->info; 48 + 49 + for (int i = 0; i < job->region_cnt; i++) { 50 + struct drm_gem_dma_object *bo; 51 + int region = job->region_bo_num[i]; 52 + 53 + bo = to_drm_gem_dma_obj(job->region_bo[i]); 54 + writel_relaxed(lower_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP(region)); 55 + writel_relaxed(upper_32_bits(bo->dma_addr), dev->regs + NPU_REG_BASEP_HI(region)); 56 + dev_dbg(dev->base.dev, "Region %d base addr = %pad\n", region, &bo->dma_addr); 57 + } 58 + 59 + if (job->sram_size) { 60 + writel_relaxed(lower_32_bits(dev->sramphys), 61 + dev->regs + NPU_REG_BASEP(ETHOSU_SRAM_REGION)); 62 + writel_relaxed(upper_32_bits(dev->sramphys), 63 + dev->regs + NPU_REG_BASEP_HI(ETHOSU_SRAM_REGION)); 64 + dev_dbg(dev->base.dev, "Region %d base addr = %pad (SRAM)\n", 65 + ETHOSU_SRAM_REGION, &dev->sramphys); 66 + } 67 + 68 + writel_relaxed(lower_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE); 69 + writel_relaxed(upper_32_bits(cmd_bo->dma_addr), dev->regs + NPU_REG_QBASE_HI); 70 + writel_relaxed(cmd_info->cmd_size, dev->regs + NPU_REG_QSIZE); 71 + 72 + writel(CMD_TRANSITION_TO_RUN, dev->regs + NPU_REG_CMD); 73 + 74 + dev_dbg(dev->base.dev, 75 + "Submitted cmd at %pad to core\n", &cmd_bo->dma_addr); 76 + } 77 + 78 + static int ethosu_acquire_object_fences(struct ethosu_job *job) 79 + { 80 + int i, ret; 81 + struct drm_gem_object **bos = job->region_bo; 82 + struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info; 83 + 84 + for (i = 0; i < job->region_cnt; i++) { 85 + bool is_write; 86 + 87 + if (!bos[i]) 88 + break; 89 + 90 + ret = dma_resv_reserve_fences(bos[i]->resv, 1); 91 + if (ret) 92 + return ret; 93 + 94 + is_write = info->output_region[job->region_bo_num[i]]; 95 + ret = drm_sched_job_add_implicit_dependencies(&job->base, bos[i], 96 + is_write); 97 + if (ret) 98 + return ret; 99 + } 100 + 101 + return 0; 102 + } 103 + 104 + static void ethosu_attach_object_fences(struct ethosu_job *job) 105 + { 106 + int i; 107 + struct dma_fence *fence = job->inference_done_fence; 108 + struct drm_gem_object **bos = job->region_bo; 109 + struct ethosu_validated_cmdstream_info *info = to_ethosu_bo(job->cmd_bo)->info; 110 + 111 + for (i = 0; i < job->region_cnt; i++) 112 + if (info->output_region[job->region_bo_num[i]]) 113 + dma_resv_add_fence(bos[i]->resv, fence, DMA_RESV_USAGE_WRITE); 114 + } 115 + 116 + static int ethosu_job_push(struct ethosu_job *job) 117 + { 118 + struct ww_acquire_ctx acquire_ctx; 119 + int ret; 120 + 121 + ret = drm_gem_lock_reservations(job->region_bo, job->region_cnt, &acquire_ctx); 122 + if (ret) 123 + return ret; 124 + 125 + ret = ethosu_acquire_object_fences(job); 126 + if (ret) 127 + goto out; 128 + 129 + ret = pm_runtime_resume_and_get(job->dev->base.dev); 130 + if (!ret) { 131 + guard(mutex)(&job->dev->sched_lock); 132 + 133 + drm_sched_job_arm(&job->base); 134 + job->inference_done_fence = dma_fence_get(&job->base.s_fence->finished); 135 + kref_get(&job->refcount); /* put by scheduler job completion */ 136 + drm_sched_entity_push_job(&job->base); 137 + ethosu_attach_object_fences(job); 138 + } 139 + 140 + out: 141 + drm_gem_unlock_reservations(job->region_bo, job->region_cnt, &acquire_ctx); 142 + return ret; 143 + } 144 + 145 + static void ethosu_job_cleanup(struct kref *ref) 146 + { 147 + struct ethosu_job *job = container_of(ref, struct ethosu_job, 148 + refcount); 149 + unsigned int i; 150 + 151 + pm_runtime_put_autosuspend(job->dev->base.dev); 152 + 153 + dma_fence_put(job->done_fence); 154 + dma_fence_put(job->inference_done_fence); 155 + 156 + for (i = 0; i < job->region_cnt; i++) 157 + drm_gem_object_put(job->region_bo[i]); 158 + 159 + drm_gem_object_put(job->cmd_bo); 160 + 161 + kfree(job); 162 + } 163 + 164 + static void ethosu_job_put(struct ethosu_job *job) 165 + { 166 + kref_put(&job->refcount, ethosu_job_cleanup); 167 + } 168 + 169 + static void ethosu_job_free(struct drm_sched_job *sched_job) 170 + { 171 + struct ethosu_job *job = to_ethosu_job(sched_job); 172 + 173 + drm_sched_job_cleanup(sched_job); 174 + ethosu_job_put(job); 175 + } 176 + 177 + static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) 178 + { 179 + struct ethosu_job *job = to_ethosu_job(sched_job); 180 + struct ethosu_device *dev = job->dev; 181 + struct dma_fence *fence = job->done_fence; 182 + 183 + if (unlikely(job->base.s_fence->finished.error)) 184 + return NULL; 185 + 186 + dma_fence_init(fence, &ethosu_fence_ops, &dev->fence_lock, 187 + dev->fence_context, ++dev->emit_seqno); 188 + dma_fence_get(fence); 189 + 190 + scoped_guard(mutex, &dev->job_lock) { 191 + dev->in_flight_job = job; 192 + ethosu_job_hw_submit(dev, job); 193 + } 194 + 195 + return fence; 196 + } 197 + 198 + static void ethosu_job_handle_irq(struct ethosu_device *dev) 199 + { 200 + u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS); 201 + 202 + if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) { 203 + dev_err(dev->base.dev, "Error IRQ - %x\n", status); 204 + drm_sched_fault(&dev->sched); 205 + return; 206 + } 207 + 208 + scoped_guard(mutex, &dev->job_lock) { 209 + if (dev->in_flight_job) { 210 + dma_fence_signal(dev->in_flight_job->done_fence); 211 + dev->in_flight_job = NULL; 212 + } 213 + } 214 + } 215 + 216 + static irqreturn_t ethosu_job_irq_handler_thread(int irq, void *data) 217 + { 218 + struct ethosu_device *dev = data; 219 + 220 + ethosu_job_handle_irq(dev); 221 + 222 + return IRQ_HANDLED; 223 + } 224 + 225 + static irqreturn_t ethosu_job_irq_handler(int irq, void *data) 226 + { 227 + struct ethosu_device *dev = data; 228 + u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS); 229 + 230 + if (!(status & STATUS_IRQ_RAISED)) 231 + return IRQ_NONE; 232 + 233 + writel_relaxed(CMD_CLEAR_IRQ, dev->regs + NPU_REG_CMD); 234 + return IRQ_WAKE_THREAD; 235 + } 236 + 237 + static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad) 238 + { 239 + struct ethosu_job *job = to_ethosu_job(bad); 240 + struct ethosu_device *dev = job->dev; 241 + bool running; 242 + u32 *bocmds = to_drm_gem_dma_obj(job->cmd_bo)->vaddr; 243 + u32 cmdaddr; 244 + 245 + cmdaddr = readl_relaxed(dev->regs + NPU_REG_QREAD); 246 + running = FIELD_GET(STATUS_STATE_RUNNING, readl_relaxed(dev->regs + NPU_REG_STATUS)); 247 + 248 + if (running) { 249 + int ret; 250 + u32 reg; 251 + 252 + ret = readl_relaxed_poll_timeout(dev->regs + NPU_REG_QREAD, 253 + reg, 254 + reg != cmdaddr, 255 + USEC_PER_MSEC, 100 * USEC_PER_MSEC); 256 + 257 + /* If still running and progress is being made, just return */ 258 + if (!ret) 259 + return DRM_GPU_SCHED_STAT_NO_HANG; 260 + } 261 + 262 + dev_err(dev->base.dev, "NPU sched timed out: NPU %s, cmdstream offset 0x%x: 0x%x\n", 263 + running ? "running" : "stopped", 264 + cmdaddr, bocmds[cmdaddr / 4]); 265 + 266 + drm_sched_stop(&dev->sched, bad); 267 + 268 + scoped_guard(mutex, &dev->job_lock) 269 + dev->in_flight_job = NULL; 270 + 271 + /* Proceed with reset now. */ 272 + pm_runtime_force_suspend(dev->base.dev); 273 + pm_runtime_force_resume(dev->base.dev); 274 + 275 + /* Restart the scheduler */ 276 + drm_sched_start(&dev->sched, 0); 277 + 278 + return DRM_GPU_SCHED_STAT_RESET; 279 + } 280 + 281 + static const struct drm_sched_backend_ops ethosu_sched_ops = { 282 + .run_job = ethosu_job_run, 283 + .timedout_job = ethosu_job_timedout, 284 + .free_job = ethosu_job_free 285 + }; 286 + 287 + int ethosu_job_init(struct ethosu_device *edev) 288 + { 289 + struct device *dev = edev->base.dev; 290 + struct drm_sched_init_args args = { 291 + .ops = &ethosu_sched_ops, 292 + .num_rqs = DRM_SCHED_PRIORITY_COUNT, 293 + .credit_limit = 1, 294 + .timeout = msecs_to_jiffies(JOB_TIMEOUT_MS), 295 + .name = dev_name(dev), 296 + .dev = dev, 297 + }; 298 + int ret; 299 + 300 + spin_lock_init(&edev->fence_lock); 301 + ret = devm_mutex_init(dev, &edev->job_lock); 302 + if (ret) 303 + return ret; 304 + ret = devm_mutex_init(dev, &edev->sched_lock); 305 + if (ret) 306 + return ret; 307 + 308 + edev->irq = platform_get_irq(to_platform_device(dev), 0); 309 + if (edev->irq < 0) 310 + return edev->irq; 311 + 312 + ret = devm_request_threaded_irq(dev, edev->irq, 313 + ethosu_job_irq_handler, 314 + ethosu_job_irq_handler_thread, 315 + IRQF_SHARED, KBUILD_MODNAME, 316 + edev); 317 + if (ret) { 318 + dev_err(dev, "failed to request irq\n"); 319 + return ret; 320 + } 321 + 322 + edev->fence_context = dma_fence_context_alloc(1); 323 + 324 + ret = drm_sched_init(&edev->sched, &args); 325 + if (ret) { 326 + dev_err(dev, "Failed to create scheduler: %d\n", ret); 327 + goto err_sched; 328 + } 329 + 330 + return 0; 331 + 332 + err_sched: 333 + drm_sched_fini(&edev->sched); 334 + return ret; 335 + } 336 + 337 + void ethosu_job_fini(struct ethosu_device *dev) 338 + { 339 + drm_sched_fini(&dev->sched); 340 + } 341 + 342 + int ethosu_job_open(struct ethosu_file_priv *ethosu_priv) 343 + { 344 + struct ethosu_device *dev = ethosu_priv->edev; 345 + struct drm_gpu_scheduler *sched = &dev->sched; 346 + int ret; 347 + 348 + ret = drm_sched_entity_init(&ethosu_priv->sched_entity, 349 + DRM_SCHED_PRIORITY_NORMAL, 350 + &sched, 1, NULL); 351 + return WARN_ON(ret); 352 + } 353 + 354 + void ethosu_job_close(struct ethosu_file_priv *ethosu_priv) 355 + { 356 + struct drm_sched_entity *entity = &ethosu_priv->sched_entity; 357 + 358 + drm_sched_entity_destroy(entity); 359 + } 360 + 361 + static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file, 362 + struct drm_ethosu_job *job) 363 + { 364 + struct ethosu_device *edev = to_ethosu_device(dev); 365 + struct ethosu_file_priv *file_priv = file->driver_priv; 366 + struct ethosu_job *ejob = NULL; 367 + struct ethosu_validated_cmdstream_info *cmd_info; 368 + int ret = 0; 369 + 370 + /* BO region 2 is reserved if SRAM is used */ 371 + if (job->region_bo_handles[ETHOSU_SRAM_REGION] && job->sram_size) 372 + return -EINVAL; 373 + 374 + if (edev->npu_info.sram_size < job->sram_size) 375 + return -EINVAL; 376 + 377 + ejob = kzalloc(sizeof(*ejob), GFP_KERNEL); 378 + if (!ejob) 379 + return -ENOMEM; 380 + 381 + kref_init(&ejob->refcount); 382 + 383 + ejob->dev = edev; 384 + ejob->sram_size = job->sram_size; 385 + 386 + ejob->done_fence = kzalloc(sizeof(*ejob->done_fence), GFP_KERNEL); 387 + if (!ejob->done_fence) { 388 + ret = -ENOMEM; 389 + goto out_cleanup_job; 390 + } 391 + 392 + ret = drm_sched_job_init(&ejob->base, 393 + &file_priv->sched_entity, 394 + 1, NULL, file->client_id); 395 + if (ret) 396 + goto out_put_job; 397 + 398 + ejob->cmd_bo = drm_gem_object_lookup(file, job->cmd_bo); 399 + if (!ejob->cmd_bo) { 400 + ret = -ENOENT; 401 + goto out_cleanup_job; 402 + } 403 + cmd_info = to_ethosu_bo(ejob->cmd_bo)->info; 404 + if (!cmd_info) { 405 + ret = -EINVAL; 406 + goto out_cleanup_job; 407 + } 408 + 409 + for (int i = 0; i < NPU_BASEP_REGION_MAX; i++) { 410 + struct drm_gem_object *gem; 411 + 412 + /* Can only omit a BO handle if the region is not used or used for SRAM */ 413 + if (!job->region_bo_handles[i] && 414 + (!cmd_info->region_size[i] || (i == ETHOSU_SRAM_REGION && job->sram_size))) 415 + continue; 416 + 417 + if (job->region_bo_handles[i] && !cmd_info->region_size[i]) { 418 + dev_err(dev->dev, 419 + "Cmdstream BO handle %d set for unused region %d\n", 420 + job->region_bo_handles[i], i); 421 + ret = -EINVAL; 422 + goto out_cleanup_job; 423 + } 424 + 425 + gem = drm_gem_object_lookup(file, job->region_bo_handles[i]); 426 + if (!gem) { 427 + dev_err(dev->dev, 428 + "Invalid BO handle %d for region %d\n", 429 + job->region_bo_handles[i], i); 430 + ret = -ENOENT; 431 + goto out_cleanup_job; 432 + } 433 + 434 + ejob->region_bo[ejob->region_cnt] = gem; 435 + ejob->region_bo_num[ejob->region_cnt] = i; 436 + ejob->region_cnt++; 437 + 438 + if (to_ethosu_bo(gem)->info) { 439 + dev_err(dev->dev, 440 + "Cmdstream BO handle %d used for region %d\n", 441 + job->region_bo_handles[i], i); 442 + ret = -EINVAL; 443 + goto out_cleanup_job; 444 + } 445 + 446 + /* Verify the command stream doesn't have accesses outside the BO */ 447 + if (cmd_info->region_size[i] > gem->size) { 448 + dev_err(dev->dev, 449 + "cmd stream region %d size greater than BO size (%llu > %zu)\n", 450 + i, cmd_info->region_size[i], gem->size); 451 + ret = -EOVERFLOW; 452 + goto out_cleanup_job; 453 + } 454 + } 455 + ret = ethosu_job_push(ejob); 456 + 457 + out_cleanup_job: 458 + if (ret) 459 + drm_sched_job_cleanup(&ejob->base); 460 + out_put_job: 461 + ethosu_job_put(ejob); 462 + 463 + return ret; 464 + } 465 + 466 + int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file) 467 + { 468 + struct drm_ethosu_submit *args = data; 469 + int ret = 0; 470 + unsigned int i = 0; 471 + 472 + if (args->pad) { 473 + drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n"); 474 + return -EINVAL; 475 + } 476 + 477 + struct drm_ethosu_job __free(kvfree) *jobs = 478 + kvmalloc_array(args->job_count, sizeof(*jobs), GFP_KERNEL); 479 + if (!jobs) 480 + return -ENOMEM; 481 + 482 + if (copy_from_user(jobs, 483 + (void __user *)(uintptr_t)args->jobs, 484 + args->job_count * sizeof(*jobs))) { 485 + drm_dbg(dev, "Failed to copy incoming job array\n"); 486 + return -EFAULT; 487 + } 488 + 489 + for (i = 0; i < args->job_count; i++) { 490 + ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]); 491 + if (ret) 492 + return ret; 493 + } 494 + 495 + return 0; 496 + }
+40
drivers/accel/ethosu/ethosu_job.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ 2 + /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */ 3 + /* Copyright 2025 Arm, Ltd. */ 4 + 5 + #ifndef __ETHOSU_JOB_H__ 6 + #define __ETHOSU_JOB_H__ 7 + 8 + #include <linux/kref.h> 9 + #include <drm/gpu_scheduler.h> 10 + 11 + struct ethosu_device; 12 + struct ethosu_file_priv; 13 + 14 + struct ethosu_job { 15 + struct drm_sched_job base; 16 + struct ethosu_device *dev; 17 + 18 + struct drm_gem_object *cmd_bo; 19 + struct drm_gem_object *region_bo[NPU_BASEP_REGION_MAX]; 20 + u8 region_bo_num[NPU_BASEP_REGION_MAX]; 21 + u8 region_cnt; 22 + u32 sram_size; 23 + 24 + /* Fence to be signaled by drm-sched once its done with the job */ 25 + struct dma_fence *inference_done_fence; 26 + 27 + /* Fence to be signaled by IRQ handler when the job is complete. */ 28 + struct dma_fence *done_fence; 29 + 30 + struct kref refcount; 31 + }; 32 + 33 + int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *file); 34 + 35 + int ethosu_job_init(struct ethosu_device *dev); 36 + void ethosu_job_fini(struct ethosu_device *dev); 37 + int ethosu_job_open(struct ethosu_file_priv *ethosu_priv); 38 + void ethosu_job_close(struct ethosu_file_priv *ethosu_priv); 39 + 40 + #endif
+1
drivers/accel/ivpu/ivpu_drv.c
··· 707 707 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) }, 708 708 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PTL_P) }, 709 709 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_WCL) }, 710 + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_NVL) }, 710 711 { } 711 712 }; 712 713 MODULE_DEVICE_TABLE(pci, ivpu_pci_ids);
+4
drivers/accel/ivpu/ivpu_drv.h
··· 27 27 #define PCI_DEVICE_ID_LNL 0x643e 28 28 #define PCI_DEVICE_ID_PTL_P 0xb03e 29 29 #define PCI_DEVICE_ID_WCL 0xfd3e 30 + #define PCI_DEVICE_ID_NVL 0xd71d 30 31 31 32 #define IVPU_HW_IP_37XX 37 32 33 #define IVPU_HW_IP_40XX 40 ··· 246 245 case PCI_DEVICE_ID_PTL_P: 247 246 case PCI_DEVICE_ID_WCL: 248 247 return IVPU_HW_IP_50XX; 248 + case PCI_DEVICE_ID_NVL: 249 + return IVPU_HW_IP_60XX; 249 250 default: 250 251 dump_stack(); 251 252 ivpu_err(vdev, "Unknown NPU IP generation\n"); ··· 264 261 case PCI_DEVICE_ID_LNL: 265 262 case PCI_DEVICE_ID_PTL_P: 266 263 case PCI_DEVICE_ID_WCL: 264 + case PCI_DEVICE_ID_NVL: 267 265 return IVPU_HW_BTRS_LNL; 268 266 default: 269 267 dump_stack();
+2
drivers/accel/ivpu/ivpu_fw.c
··· 56 56 { IVPU_HW_IP_40XX, "intel/vpu/vpu_40xx_v0.0.bin" }, 57 57 { IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v1.bin" }, 58 58 { IVPU_HW_IP_50XX, "intel/vpu/vpu_50xx_v0.0.bin" }, 59 + { IVPU_HW_IP_60XX, "intel/vpu/vpu_60xx_v1.bin" }, 59 60 }; 60 61 61 62 /* Production fw_names from the table above */ 62 63 MODULE_FIRMWARE("intel/vpu/vpu_37xx_v1.bin"); 63 64 MODULE_FIRMWARE("intel/vpu/vpu_40xx_v1.bin"); 64 65 MODULE_FIRMWARE("intel/vpu/vpu_50xx_v1.bin"); 66 + MODULE_FIRMWARE("intel/vpu/vpu_60xx_v1.bin"); 65 67 66 68 static int ivpu_fw_request(struct ivpu_device *vdev) 67 69 {
+10
drivers/accel/ivpu/ivpu_hw_ip.c
··· 691 691 status = high ? 46 : 3; 692 692 break; 693 693 694 + case PCI_DEVICE_ID_NVL: 695 + post = high ? 198 : 17; 696 + post1 = 0; 697 + post2 = high ? 198 : 17; 698 + status = 0; 699 + break; 700 + 694 701 default: 695 702 dump_stack(); 696 703 ivpu_err(vdev, "Unknown device ID\n"); ··· 896 889 897 890 static int soc_cpu_enable(struct ivpu_device *vdev) 898 891 { 892 + if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX) 893 + return 0; 894 + 899 895 return soc_cpu_drive_40xx(vdev, true); 900 896 } 901 897
+7 -7
drivers/accel/qaic/qaic_control.c
··· 31 31 #define MANAGE_MAGIC_NUMBER ((__force __le32)0x43494151) /* "QAIC" in little endian */ 32 32 #define QAIC_DBC_Q_GAP SZ_256 33 33 #define QAIC_DBC_Q_BUF_ALIGN SZ_4K 34 - #define QAIC_MANAGE_EXT_MSG_LENGTH SZ_64K /* Max DMA message length */ 34 + #define QAIC_MANAGE_WIRE_MSG_LENGTH SZ_64K /* Max DMA message length */ 35 35 #define QAIC_WRAPPER_MAX_SIZE SZ_4K 36 36 #define QAIC_MHI_RETRY_WAIT_MS 100 37 37 #define QAIC_MHI_RETRY_MAX 20 ··· 368 368 if (in_trans->hdr.len % 8 != 0) 369 369 return -EINVAL; 370 370 371 - if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_EXT_MSG_LENGTH) 371 + if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_WIRE_MSG_LENGTH) 372 372 return -ENOSPC; 373 373 374 374 trans_wrapper = add_wrapper(wrappers, ··· 496 496 497 497 nents = sgt->nents; 498 498 nents_dma = nents; 499 - *size = QAIC_MANAGE_EXT_MSG_LENGTH - msg_hdr_len - sizeof(**out_trans); 499 + *size = QAIC_MANAGE_WIRE_MSG_LENGTH - msg_hdr_len - sizeof(**out_trans); 500 500 for_each_sgtable_dma_sg(sgt, sg, i) { 501 501 *size -= sizeof(*asp); 502 502 /* Save 1K for possible follow-up transactions. */ ··· 577 577 578 578 /* There should be enough space to hold at least one ASP entry. */ 579 579 if (size_add(msg_hdr_len, sizeof(*out_trans) + sizeof(struct wire_addr_size_pair)) > 580 - QAIC_MANAGE_EXT_MSG_LENGTH) 580 + QAIC_MANAGE_WIRE_MSG_LENGTH) 581 581 return -ENOMEM; 582 582 583 583 xfer = kmalloc(sizeof(*xfer), GFP_KERNEL); ··· 646 646 msg = &wrapper->msg; 647 647 msg_hdr_len = le32_to_cpu(msg->hdr.len); 648 648 649 - if (size_add(msg_hdr_len, sizeof(*out_trans)) > QAIC_MANAGE_MAX_MSG_LENGTH) 649 + if (size_add(msg_hdr_len, sizeof(*out_trans)) > QAIC_MANAGE_WIRE_MSG_LENGTH) 650 650 return -ENOSPC; 651 651 652 652 if (!in_trans->queue_size) ··· 731 731 msg = &wrapper->msg; 732 732 msg_hdr_len = le32_to_cpu(msg->hdr.len); 733 733 734 - if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_MAX_MSG_LENGTH) 734 + if (size_add(msg_hdr_len, in_trans->hdr.len) > QAIC_MANAGE_WIRE_MSG_LENGTH) 735 735 return -ENOSPC; 736 736 737 737 trans_wrapper = add_wrapper(wrappers, sizeof(*trans_wrapper)); ··· 1054 1054 init_completion(&elem.xfer_done); 1055 1055 if (likely(!qdev->cntl_lost_buf)) { 1056 1056 /* 1057 - * The max size of request to device is QAIC_MANAGE_EXT_MSG_LENGTH. 1057 + * The max size of request to device is QAIC_MANAGE_WIRE_MSG_LENGTH. 1058 1058 * The max size of response from device is QAIC_MANAGE_MAX_MSG_LENGTH. 1059 1059 */ 1060 1060 out_buf = kmalloc(QAIC_MANAGE_MAX_MSG_LENGTH, GFP_KERNEL);
+1 -1
drivers/accel/qaic/qaic_data.c
··· 1959 1959 * enable_dbc - Enable the DBC. DBCs are disabled by removing the context of 1960 1960 * user. Add user context back to DBC to enable it. This function trusts the 1961 1961 * DBC ID passed and expects the DBC to be disabled. 1962 - * @qdev: Qranium device handle 1962 + * @qdev: qaic device handle 1963 1963 * @dbc_id: ID of the DBC 1964 1964 * @usr: User context 1965 1965 */
+4 -1
drivers/accel/qaic/sahara.c
··· 198 198 [23] = "qcom/aic200/aop.mbn", 199 199 [32] = "qcom/aic200/tz.mbn", 200 200 [33] = "qcom/aic200/hypvm.mbn", 201 + [38] = "qcom/aic200/xbl_config.elf", 201 202 [39] = "qcom/aic200/aic200_abl.elf", 202 203 [40] = "qcom/aic200/apdp.mbn", 203 204 [41] = "qcom/aic200/devcfg.mbn", ··· 207 206 [49] = "qcom/aic200/shrm.elf", 208 207 [50] = "qcom/aic200/cpucp.elf", 209 208 [51] = "qcom/aic200/aop_devcfg.mbn", 209 + [54] = "qcom/aic200/qupv3fw.elf", 210 210 [57] = "qcom/aic200/cpucp_dtbs.elf", 211 211 [62] = "qcom/aic200/uefi_dtbs.elf", 212 212 [63] = "qcom/aic200/xbl_ac_config.mbn", ··· 219 217 [69] = "qcom/aic200/dcd.mbn", 220 218 [73] = "qcom/aic200/gearvm.mbn", 221 219 [74] = "qcom/aic200/sti.bin", 222 - [75] = "qcom/aic200/pvs.bin", 220 + [76] = "qcom/aic200/tz_qti_config.mbn", 221 + [78] = "qcom/aic200/pvs.bin", 223 222 }; 224 223 225 224 static bool is_streaming(struct sahara_context *context)
-2
drivers/gpu/drm/armada/armada_fbdev.c
··· 28 28 fbh->fb->funcs->destroy(fbh->fb); 29 29 30 30 drm_client_release(&fbh->client); 31 - drm_fb_helper_unprepare(fbh); 32 - kfree(fbh); 33 31 } 34 32 35 33 static const struct fb_ops armada_fb_ops = {
+15 -2
drivers/gpu/drm/clients/drm_fbdev_client.c
··· 13 13 * struct drm_client_funcs 14 14 */ 15 15 16 + static void drm_fbdev_client_free(struct drm_client_dev *client) 17 + { 18 + struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 19 + 20 + drm_fb_helper_unprepare(fb_helper); 21 + kfree(fb_helper); 22 + } 23 + 16 24 static void drm_fbdev_client_unregister(struct drm_client_dev *client) 17 25 { 18 26 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 19 27 20 28 if (fb_helper->info) { 29 + /* 30 + * Fully probed framebuffer device 31 + */ 21 32 drm_fb_helper_unregister_info(fb_helper); 22 33 } else { 34 + /* 35 + * Partially initialized client, no framebuffer device yet 36 + */ 23 37 drm_client_release(&fb_helper->client); 24 - drm_fb_helper_unprepare(fb_helper); 25 - kfree(fb_helper); 26 38 } 27 39 } 28 40 ··· 94 82 95 83 static const struct drm_client_funcs drm_fbdev_client_funcs = { 96 84 .owner = THIS_MODULE, 85 + .free = drm_fbdev_client_free, 97 86 .unregister = drm_fbdev_client_unregister, 98 87 .restore = drm_fbdev_client_restore, 99 88 .hotplug = drm_fbdev_client_hotplug,
+12 -4
drivers/gpu/drm/clients/drm_log.c
··· 293 293 } 294 294 } 295 295 296 - static void drm_log_client_unregister(struct drm_client_dev *client) 296 + static void drm_log_client_free(struct drm_client_dev *client) 297 297 { 298 298 struct drm_log *dlog = client_to_drm_log(client); 299 299 struct drm_device *dev = client->dev; 300 + 301 + kfree(dlog); 302 + 303 + drm_dbg(dev, "Unregistered with drm log\n"); 304 + } 305 + 306 + static void drm_log_client_unregister(struct drm_client_dev *client) 307 + { 308 + struct drm_log *dlog = client_to_drm_log(client); 300 309 301 310 unregister_console(&dlog->con); 302 311 303 312 mutex_lock(&dlog->lock); 304 313 drm_log_free_scanout(client); 305 - drm_client_release(client); 306 314 mutex_unlock(&dlog->lock); 307 - kfree(dlog); 308 - drm_dbg(dev, "Unregistered with drm log\n"); 315 + drm_client_release(client); 309 316 } 310 317 311 318 static int drm_log_client_hotplug(struct drm_client_dev *client) ··· 346 339 347 340 static const struct drm_client_funcs drm_log_client_funcs = { 348 341 .owner = THIS_MODULE, 342 + .free = drm_log_client_free, 349 343 .unregister = drm_log_client_unregister, 350 344 .hotplug = drm_log_client_hotplug, 351 345 .suspend = drm_log_client_suspend,
+4
drivers/gpu/drm/drm_client.c
··· 168 168 169 169 drm_client_modeset_free(client); 170 170 drm_client_close(client); 171 + 172 + if (client->funcs && client->funcs->free) 173 + client->funcs->free(client); 174 + 171 175 drm_dev_put(dev); 172 176 } 173 177 EXPORT_SYMBOL(drm_client_release);
+5 -4
drivers/gpu/drm/drm_client_event.c
··· 39 39 mutex_lock(&dev->clientlist_mutex); 40 40 list_for_each_entry_safe(client, tmp, &dev->clientlist, list) { 41 41 list_del(&client->list); 42 - if (client->funcs && client->funcs->unregister) { 42 + /* 43 + * Unregistering consumes and frees the client. 44 + */ 45 + if (client->funcs && client->funcs->unregister) 43 46 client->funcs->unregister(client); 44 - } else { 47 + else 45 48 drm_client_release(client); 46 - kfree(client); 47 - } 48 49 } 49 50 mutex_unlock(&dev->clientlist_mutex); 50 51 }
-4
drivers/gpu/drm/drm_fbdev_dma.c
··· 57 57 drm_client_buffer_vunmap(fb_helper->buffer); 58 58 drm_client_framebuffer_delete(fb_helper->buffer); 59 59 drm_client_release(&fb_helper->client); 60 - drm_fb_helper_unprepare(fb_helper); 61 - kfree(fb_helper); 62 60 } 63 61 64 62 static const struct fb_ops drm_fbdev_dma_fb_ops = { ··· 90 92 drm_client_buffer_vunmap(fb_helper->buffer); 91 93 drm_client_framebuffer_delete(fb_helper->buffer); 92 94 drm_client_release(&fb_helper->client); 93 - drm_fb_helper_unprepare(fb_helper); 94 - kfree(fb_helper); 95 95 } 96 96 97 97 static const struct fb_ops drm_fbdev_dma_shadowed_fb_ops = {
-2
drivers/gpu/drm/drm_fbdev_shmem.c
··· 65 65 drm_client_buffer_vunmap(fb_helper->buffer); 66 66 drm_client_framebuffer_delete(fb_helper->buffer); 67 67 drm_client_release(&fb_helper->client); 68 - drm_fb_helper_unprepare(fb_helper); 69 - kfree(fb_helper); 70 68 } 71 69 72 70 static const struct fb_ops drm_fbdev_shmem_fb_ops = {
-2
drivers/gpu/drm/drm_fbdev_ttm.c
··· 53 53 drm_client_framebuffer_delete(fb_helper->buffer); 54 54 55 55 drm_client_release(&fb_helper->client); 56 - drm_fb_helper_unprepare(fb_helper); 57 - kfree(fb_helper); 58 56 } 59 57 60 58 static const struct fb_ops drm_fbdev_ttm_fb_ops = {
-2
drivers/gpu/drm/drm_gem_atomic_helper.c
··· 334 334 } 335 335 336 336 shadow_plane_state = kzalloc(sizeof(*shadow_plane_state), GFP_KERNEL); 337 - if (!shadow_plane_state) 338 - return; 339 337 __drm_gem_reset_shadow_plane(plane, shadow_plane_state); 340 338 } 341 339 EXPORT_SYMBOL(drm_gem_reset_shadow_plane);
-2
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
··· 42 42 drm_framebuffer_remove(fb); 43 43 44 44 drm_client_release(&fb_helper->client); 45 - drm_fb_helper_unprepare(fb_helper); 46 - kfree(fb_helper); 47 45 } 48 46 49 47 static const struct fb_ops exynos_drm_fb_ops = {
-3
drivers/gpu/drm/gma500/fbdev.c
··· 84 84 drm_gem_object_put(obj); 85 85 86 86 drm_client_release(&fb_helper->client); 87 - 88 - drm_fb_helper_unprepare(fb_helper); 89 - kfree(fb_helper); 90 87 } 91 88 92 89 static const struct fb_ops psb_fbdev_fb_ops = {
+24 -21
drivers/gpu/drm/gud/gud_drv.c
··· 249 249 return gud_usb_set(gdrm, request, 0, &val, sizeof(val)); 250 250 } 251 251 252 - static int gud_get_properties(struct gud_device *gdrm) 252 + static int gud_plane_add_properties(struct gud_device *gdrm) 253 253 { 254 254 struct gud_property_req *properties; 255 255 unsigned int i, num_properties; ··· 463 463 return PTR_ERR(gdrm); 464 464 465 465 drm = &gdrm->drm; 466 - drm->mode_config.funcs = &gud_mode_config_funcs; 467 - ret = drmm_mode_config_init(drm); 468 - if (ret) 469 - return ret; 470 466 471 467 gdrm->flags = le32_to_cpu(desc.flags); 472 468 gdrm->compression = desc.compression & GUD_COMPRESSION_LZ4; ··· 479 483 if (ret) 480 484 return ret; 481 485 486 + usb_set_intfdata(intf, gdrm); 487 + 488 + dma_dev = usb_intf_get_dma_device(intf); 489 + if (dma_dev) { 490 + drm_dev_set_dma_dev(drm, dma_dev); 491 + put_device(dma_dev); 492 + } else { 493 + dev_warn(dev, "buffer sharing not supported"); /* not an error */ 494 + } 495 + 496 + /* Mode config init */ 497 + ret = drmm_mode_config_init(drm); 498 + if (ret) 499 + return ret; 500 + 482 501 drm->mode_config.min_width = le32_to_cpu(desc.min_width); 483 502 drm->mode_config.max_width = le32_to_cpu(desc.max_width); 484 503 drm->mode_config.min_height = le32_to_cpu(desc.min_height); 485 504 drm->mode_config.max_height = le32_to_cpu(desc.max_height); 505 + drm->mode_config.funcs = &gud_mode_config_funcs; 486 506 507 + /* Format init */ 487 508 formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL); 488 509 /* Add room for emulated XRGB8888 */ 489 510 formats = devm_kmalloc_array(dev, GUD_FORMATS_MAX_NUM + 1, sizeof(*formats), GFP_KERNEL); ··· 600 587 return -ENOMEM; 601 588 } 602 589 590 + /* Pipeline init */ 603 591 ret = drm_universal_plane_init(drm, &gdrm->plane, 0, 604 592 &gud_plane_funcs, 605 593 formats, num_formats, ··· 612 598 drm_plane_helper_add(&gdrm->plane, &gud_plane_helper_funcs); 613 599 drm_plane_enable_fb_damage_clips(&gdrm->plane); 614 600 615 - devm_kfree(dev, formats); 616 - devm_kfree(dev, formats_dev); 617 - 618 - ret = gud_get_properties(gdrm); 601 + ret = gud_plane_add_properties(gdrm); 619 602 if (ret) { 620 - dev_err(dev, "Failed to get properties (error=%d)\n", ret); 603 + dev_err(dev, "Failed to add properties (error=%d)\n", ret); 621 604 return ret; 622 605 } 623 606 ··· 632 621 } 633 622 634 623 drm_mode_config_reset(drm); 635 - 636 - usb_set_intfdata(intf, gdrm); 637 - 638 - dma_dev = usb_intf_get_dma_device(intf); 639 - if (dma_dev) { 640 - drm_dev_set_dma_dev(drm, dma_dev); 641 - put_device(dma_dev); 642 - } else { 643 - dev_warn(dev, "buffer sharing not supported"); /* not an error */ 644 - } 624 + drm_kms_helper_poll_init(drm); 645 625 646 626 drm_debugfs_add_file(drm, "stats", gud_stats_debugfs, NULL); 647 627 ··· 640 638 if (ret) 641 639 return ret; 642 640 643 - drm_kms_helper_poll_init(drm); 641 + devm_kfree(dev, formats); 642 + devm_kfree(dev, formats_dev); 644 643 645 644 drm_client_setup(drm, NULL); 646 645
-2
drivers/gpu/drm/i915/display/intel_fbdev.c
··· 146 146 drm_framebuffer_remove(fb_helper->fb); 147 147 148 148 drm_client_release(&fb_helper->client); 149 - drm_fb_helper_unprepare(fb_helper); 150 - kfree(fb_helper); 151 149 } 152 150 153 151 __diag_push();
-2
drivers/gpu/drm/msm/msm_fbdev.c
··· 52 52 drm_framebuffer_remove(fb); 53 53 54 54 drm_client_release(&helper->client); 55 - drm_fb_helper_unprepare(helper); 56 - kfree(helper); 57 55 } 58 56 59 57 static const struct fb_ops msm_fb_ops = {
-2
drivers/gpu/drm/omapdrm/omap_fbdev.c
··· 103 103 drm_framebuffer_remove(fb); 104 104 105 105 drm_client_release(&helper->client); 106 - drm_fb_helper_unprepare(helper); 107 - kfree(helper); 108 106 } 109 107 110 108 /*
-2
drivers/gpu/drm/radeon/radeon_fbdev.c
··· 184 184 radeon_fbdev_destroy_pinned_object(gobj); 185 185 186 186 drm_client_release(&fb_helper->client); 187 - drm_fb_helper_unprepare(fb_helper); 188 - kfree(fb_helper); 189 187 } 190 188 191 189 static const struct fb_ops radeon_fbdev_fb_ops = {
+1 -1
drivers/gpu/drm/sitronix/st7571-i2c.c
··· 322 322 size = (rect->x2 - rect->x1) * (rect->y2 - rect->y1) / 4; 323 323 memcpy(st7571->hwbuf, vmap->vaddr, size); 324 324 break; 325 - }; 325 + } 326 326 } 327 327 328 328 static int st7571_fb_update_rect_monochrome(struct drm_framebuffer *fb, struct drm_rect *rect)
+1 -1
drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
··· 258 258 259 259 ret = -EINVAL; 260 260 261 - crtc_state = drm_atomic_get_crtc_state(plane_state->state, plane_state->crtc); 261 + crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, plane_state->crtc); 262 262 if (drm_WARN_ON_ONCE(dev, !crtc_state)) 263 263 goto err_drm_gem_end_shadow_fb_access; 264 264 sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
-2
drivers/gpu/drm/tegra/fbdev.c
··· 53 53 drm_framebuffer_remove(fb); 54 54 55 55 drm_client_release(&helper->client); 56 - drm_fb_helper_unprepare(helper); 57 - kfree(helper); 58 56 } 59 57 60 58 static const struct fb_ops tegra_fb_ops = {
+2 -1
drivers/gpu/drm/ttm/ttm_module.c
··· 74 74 #endif /* CONFIG_UML */ 75 75 #endif /* __i386__ || __x86_64__ */ 76 76 #if defined(__ia64__) || defined(__arm__) || defined(__aarch64__) || \ 77 - defined(__powerpc__) || defined(__mips__) || defined(__loongarch__) 77 + defined(__powerpc__) || defined(__mips__) || defined(__loongarch__) || \ 78 + defined(__riscv) 78 79 if (caching == ttm_write_combined) 79 80 tmp = pgprot_writecombine(tmp); 80 81 else
+1
drivers/gpu/drm/vkms/Kconfig
··· 7 7 select DRM_KMS_HELPER 8 8 select DRM_GEM_SHMEM_HELPER 9 9 select CRC32 10 + select CONFIGFS_FS 10 11 default n 11 12 help 12 13 Virtual Kernel Mode-Setting (VKMS) is used for testing or for
+2 -1
drivers/gpu/drm/vkms/Makefile
··· 8 8 vkms_composer.o \ 9 9 vkms_writeback.o \ 10 10 vkms_connector.o \ 11 - vkms_config.o 11 + vkms_config.o \ 12 + vkms_configfs.o 12 13 13 14 obj-$(CONFIG_DRM_VKMS) += vkms.o 14 15 obj-$(CONFIG_DRM_VKMS_KUNIT_TEST) += tests/
+24
drivers/gpu/drm/vkms/tests/vkms_config_test.c
··· 957 957 vkms_config_destroy(config); 958 958 } 959 959 960 + static void vkms_config_test_connector_status(struct kunit *test) 961 + { 962 + struct vkms_config *config; 963 + struct vkms_config_connector *connector_cfg; 964 + enum drm_connector_status status; 965 + 966 + config = vkms_config_create("test"); 967 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, config); 968 + 969 + connector_cfg = vkms_config_create_connector(config); 970 + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector_cfg); 971 + 972 + status = vkms_config_connector_get_status(connector_cfg); 973 + KUNIT_EXPECT_EQ(test, status, connector_status_connected); 974 + 975 + vkms_config_connector_set_status(connector_cfg, 976 + connector_status_disconnected); 977 + status = vkms_config_connector_get_status(connector_cfg); 978 + KUNIT_EXPECT_EQ(test, status, connector_status_disconnected); 979 + 980 + vkms_config_destroy(config); 981 + } 982 + 960 983 static struct kunit_case vkms_config_test_cases[] = { 961 984 KUNIT_CASE(vkms_config_test_empty_config), 962 985 KUNIT_CASE_PARAM(vkms_config_test_default_config, ··· 1001 978 KUNIT_CASE(vkms_config_test_plane_get_possible_crtcs), 1002 979 KUNIT_CASE(vkms_config_test_encoder_get_possible_crtcs), 1003 980 KUNIT_CASE(vkms_config_test_connector_get_possible_encoders), 981 + KUNIT_CASE(vkms_config_test_connector_status), 1004 982 {} 1005 983 }; 1006 984
+6 -2
drivers/gpu/drm/vkms/vkms_config.c
··· 361 361 vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg) 362 362 seq_puts(m, "encoder\n"); 363 363 364 - vkms_config_for_each_connector(vkmsdev->config, connector_cfg) 365 - seq_puts(m, "connector\n"); 364 + vkms_config_for_each_connector(vkmsdev->config, connector_cfg) { 365 + seq_puts(m, "connector:\n"); 366 + seq_printf(m, "\tstatus=%d\n", 367 + vkms_config_connector_get_status(connector_cfg)); 368 + } 366 369 367 370 return 0; 368 371 } ··· 591 588 return ERR_PTR(-ENOMEM); 592 589 593 590 connector_cfg->config = config; 591 + connector_cfg->status = connector_status_connected; 594 592 xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC); 595 593 596 594 list_add_tail(&connector_cfg->link, &config->connectors);
+26
drivers/gpu/drm/vkms/vkms_config.h
··· 7 7 #include <linux/types.h> 8 8 #include <linux/xarray.h> 9 9 10 + #include <drm/drm_connector.h> 11 + 10 12 #include "vkms_drv.h" 11 13 12 14 /** ··· 101 99 * 102 100 * @link: Link to the others connector in vkms_config 103 101 * @config: The vkms_config this connector belongs to 102 + * @status: Status (connected, disconnected...) of the connector 104 103 * @possible_encoders: Array of encoders that can be used with this connector 105 104 * @connector: Internal usage. This pointer should never be considered as valid. 106 105 * It can be used to store a temporary reference to a VKMS connector ··· 112 109 struct list_head link; 113 110 struct vkms_config *config; 114 111 112 + enum drm_connector_status status; 115 113 struct xarray possible_encoders; 116 114 117 115 /* Internal usage */ ··· 437 433 */ 438 434 void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg, 439 435 struct vkms_config_encoder *encoder_cfg); 436 + 437 + /** 438 + * vkms_config_connector_get_status() - Return the status of the connector 439 + * @connector_cfg: Connector to get the status from 440 + */ 441 + static inline enum drm_connector_status 442 + vkms_config_connector_get_status(struct vkms_config_connector *connector_cfg) 443 + { 444 + return connector_cfg->status; 445 + } 446 + 447 + /** 448 + * vkms_config_connector_set_status() - Set the status of the connector 449 + * @connector_cfg: Connector to set the status to 450 + * @status: New connector status 451 + */ 452 + static inline void 453 + vkms_config_connector_set_status(struct vkms_config_connector *connector_cfg, 454 + enum drm_connector_status status) 455 + { 456 + connector_cfg->status = status; 457 + } 440 458 441 459 #endif /* _VKMS_CONFIG_H_ */
+833
drivers/gpu/drm/vkms/vkms_configfs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + #include <linux/cleanup.h> 3 + #include <linux/configfs.h> 4 + #include <linux/mutex.h> 5 + #include <linux/slab.h> 6 + 7 + #include "vkms_drv.h" 8 + #include "vkms_config.h" 9 + #include "vkms_configfs.h" 10 + #include "vkms_connector.h" 11 + 12 + /* To avoid registering configfs more than once or unregistering on error */ 13 + static bool is_configfs_registered; 14 + 15 + /** 16 + * struct vkms_configfs_device - Configfs representation of a VKMS device 17 + * 18 + * @group: Top level configuration group that represents a VKMS device. 19 + * Initialized when a new directory is created under "/config/vkms/" 20 + * @planes_group: Default subgroup of @group at "/config/vkms/planes" 21 + * @crtcs_group: Default subgroup of @group at "/config/vkms/crtcs" 22 + * @encoders_group: Default subgroup of @group at "/config/vkms/encoders" 23 + * @connectors_group: Default subgroup of @group at "/config/vkms/connectors" 24 + * @lock: Lock used to project concurrent access to the configuration attributes 25 + * @config: Protected by @lock. Configuration of the VKMS device 26 + * @enabled: Protected by @lock. The device is created or destroyed when this 27 + * option changes 28 + */ 29 + struct vkms_configfs_device { 30 + struct config_group group; 31 + struct config_group planes_group; 32 + struct config_group crtcs_group; 33 + struct config_group encoders_group; 34 + struct config_group connectors_group; 35 + 36 + struct mutex lock; 37 + struct vkms_config *config; 38 + bool enabled; 39 + }; 40 + 41 + /** 42 + * struct vkms_configfs_plane - Configfs representation of a plane 43 + * 44 + * @group: Top level configuration group that represents a plane. 45 + * Initialized when a new directory is created under "/config/vkms/planes" 46 + * @possible_crtcs_group: Default subgroup of @group at "plane/possible_crtcs" 47 + * @dev: The vkms_configfs_device this plane belongs to 48 + * @config: Configuration of the VKMS plane 49 + */ 50 + struct vkms_configfs_plane { 51 + struct config_group group; 52 + struct config_group possible_crtcs_group; 53 + struct vkms_configfs_device *dev; 54 + struct vkms_config_plane *config; 55 + }; 56 + 57 + /** 58 + * struct vkms_configfs_crtc - Configfs representation of a CRTC 59 + * 60 + * @group: Top level configuration group that represents a CRTC. 61 + * Initialized when a new directory is created under "/config/vkms/crtcs" 62 + * @dev: The vkms_configfs_device this CRTC belongs to 63 + * @config: Configuration of the VKMS CRTC 64 + */ 65 + struct vkms_configfs_crtc { 66 + struct config_group group; 67 + struct vkms_configfs_device *dev; 68 + struct vkms_config_crtc *config; 69 + }; 70 + 71 + /** 72 + * struct vkms_configfs_encoder - Configfs representation of a encoder 73 + * 74 + * @group: Top level configuration group that represents a encoder. 75 + * Initialized when a new directory is created under "/config/vkms/encoders" 76 + * @possible_crtcs_group: Default subgroup of @group at "encoder/possible_crtcs" 77 + * @dev: The vkms_configfs_device this encoder belongs to 78 + * @config: Configuration of the VKMS encoder 79 + */ 80 + struct vkms_configfs_encoder { 81 + struct config_group group; 82 + struct config_group possible_crtcs_group; 83 + struct vkms_configfs_device *dev; 84 + struct vkms_config_encoder *config; 85 + }; 86 + 87 + /** 88 + * struct vkms_configfs_connector - Configfs representation of a connector 89 + * 90 + * @group: Top level configuration group that represents a connector. 91 + * Initialized when a new directory is created under "/config/vkms/connectors" 92 + * @possible_encoders_group: Default subgroup of @group at 93 + * "connector/possible_encoders" 94 + * @dev: The vkms_configfs_device this connector belongs to 95 + * @config: Configuration of the VKMS connector 96 + */ 97 + struct vkms_configfs_connector { 98 + struct config_group group; 99 + struct config_group possible_encoders_group; 100 + struct vkms_configfs_device *dev; 101 + struct vkms_config_connector *config; 102 + }; 103 + 104 + #define device_item_to_vkms_configfs_device(item) \ 105 + container_of(to_config_group((item)), struct vkms_configfs_device, \ 106 + group) 107 + 108 + #define child_group_to_vkms_configfs_device(group) \ 109 + device_item_to_vkms_configfs_device((&(group)->cg_item)->ci_parent) 110 + 111 + #define plane_item_to_vkms_configfs_plane(item) \ 112 + container_of(to_config_group((item)), struct vkms_configfs_plane, group) 113 + 114 + #define plane_possible_crtcs_item_to_vkms_configfs_plane(item) \ 115 + container_of(to_config_group((item)), struct vkms_configfs_plane, \ 116 + possible_crtcs_group) 117 + 118 + #define crtc_item_to_vkms_configfs_crtc(item) \ 119 + container_of(to_config_group((item)), struct vkms_configfs_crtc, group) 120 + 121 + #define encoder_item_to_vkms_configfs_encoder(item) \ 122 + container_of(to_config_group((item)), struct vkms_configfs_encoder, \ 123 + group) 124 + 125 + #define encoder_possible_crtcs_item_to_vkms_configfs_encoder(item) \ 126 + container_of(to_config_group((item)), struct vkms_configfs_encoder, \ 127 + possible_crtcs_group) 128 + 129 + #define connector_item_to_vkms_configfs_connector(item) \ 130 + container_of(to_config_group((item)), struct vkms_configfs_connector, \ 131 + group) 132 + 133 + #define connector_possible_encoders_item_to_vkms_configfs_connector(item) \ 134 + container_of(to_config_group((item)), struct vkms_configfs_connector, \ 135 + possible_encoders_group) 136 + 137 + static ssize_t crtc_writeback_show(struct config_item *item, char *page) 138 + { 139 + struct vkms_configfs_crtc *crtc; 140 + bool writeback; 141 + 142 + crtc = crtc_item_to_vkms_configfs_crtc(item); 143 + 144 + scoped_guard(mutex, &crtc->dev->lock) 145 + writeback = vkms_config_crtc_get_writeback(crtc->config); 146 + 147 + return sprintf(page, "%d\n", writeback); 148 + } 149 + 150 + static ssize_t crtc_writeback_store(struct config_item *item, const char *page, 151 + size_t count) 152 + { 153 + struct vkms_configfs_crtc *crtc; 154 + bool writeback; 155 + 156 + crtc = crtc_item_to_vkms_configfs_crtc(item); 157 + 158 + if (kstrtobool(page, &writeback)) 159 + return -EINVAL; 160 + 161 + scoped_guard(mutex, &crtc->dev->lock) { 162 + if (crtc->dev->enabled) 163 + return -EBUSY; 164 + 165 + vkms_config_crtc_set_writeback(crtc->config, writeback); 166 + } 167 + 168 + return (ssize_t)count; 169 + } 170 + 171 + CONFIGFS_ATTR(crtc_, writeback); 172 + 173 + static struct configfs_attribute *crtc_item_attrs[] = { 174 + &crtc_attr_writeback, 175 + NULL, 176 + }; 177 + 178 + static void crtc_release(struct config_item *item) 179 + { 180 + struct vkms_configfs_crtc *crtc; 181 + struct mutex *lock; 182 + 183 + crtc = crtc_item_to_vkms_configfs_crtc(item); 184 + lock = &crtc->dev->lock; 185 + 186 + scoped_guard(mutex, lock) { 187 + vkms_config_destroy_crtc(crtc->dev->config, crtc->config); 188 + kfree(crtc); 189 + } 190 + } 191 + 192 + static struct configfs_item_operations crtc_item_operations = { 193 + .release = &crtc_release, 194 + }; 195 + 196 + static const struct config_item_type crtc_item_type = { 197 + .ct_attrs = crtc_item_attrs, 198 + .ct_item_ops = &crtc_item_operations, 199 + .ct_owner = THIS_MODULE, 200 + }; 201 + 202 + static struct config_group *make_crtc_group(struct config_group *group, 203 + const char *name) 204 + { 205 + struct vkms_configfs_device *dev; 206 + struct vkms_configfs_crtc *crtc; 207 + 208 + dev = child_group_to_vkms_configfs_device(group); 209 + 210 + scoped_guard(mutex, &dev->lock) { 211 + if (dev->enabled) 212 + return ERR_PTR(-EBUSY); 213 + 214 + crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); 215 + if (!crtc) 216 + return ERR_PTR(-ENOMEM); 217 + 218 + crtc->dev = dev; 219 + 220 + crtc->config = vkms_config_create_crtc(dev->config); 221 + if (IS_ERR(crtc->config)) { 222 + kfree(crtc); 223 + return ERR_CAST(crtc->config); 224 + } 225 + 226 + config_group_init_type_name(&crtc->group, name, &crtc_item_type); 227 + } 228 + 229 + return &crtc->group; 230 + } 231 + 232 + static struct configfs_group_operations crtcs_group_operations = { 233 + .make_group = &make_crtc_group, 234 + }; 235 + 236 + static const struct config_item_type crtc_group_type = { 237 + .ct_group_ops = &crtcs_group_operations, 238 + .ct_owner = THIS_MODULE, 239 + }; 240 + 241 + static int plane_possible_crtcs_allow_link(struct config_item *src, 242 + struct config_item *target) 243 + { 244 + struct vkms_configfs_plane *plane; 245 + struct vkms_configfs_crtc *crtc; 246 + int ret; 247 + 248 + if (target->ci_type != &crtc_item_type) 249 + return -EINVAL; 250 + 251 + plane = plane_possible_crtcs_item_to_vkms_configfs_plane(src); 252 + crtc = crtc_item_to_vkms_configfs_crtc(target); 253 + 254 + scoped_guard(mutex, &plane->dev->lock) { 255 + if (plane->dev->enabled) 256 + return -EBUSY; 257 + 258 + ret = vkms_config_plane_attach_crtc(plane->config, crtc->config); 259 + } 260 + 261 + return ret; 262 + } 263 + 264 + static void plane_possible_crtcs_drop_link(struct config_item *src, 265 + struct config_item *target) 266 + { 267 + struct vkms_configfs_plane *plane; 268 + struct vkms_configfs_crtc *crtc; 269 + 270 + plane = plane_possible_crtcs_item_to_vkms_configfs_plane(src); 271 + crtc = crtc_item_to_vkms_configfs_crtc(target); 272 + 273 + scoped_guard(mutex, &plane->dev->lock) 274 + vkms_config_plane_detach_crtc(plane->config, crtc->config); 275 + } 276 + 277 + static struct configfs_item_operations plane_possible_crtcs_item_operations = { 278 + .allow_link = plane_possible_crtcs_allow_link, 279 + .drop_link = plane_possible_crtcs_drop_link, 280 + }; 281 + 282 + static const struct config_item_type plane_possible_crtcs_group_type = { 283 + .ct_item_ops = &plane_possible_crtcs_item_operations, 284 + .ct_owner = THIS_MODULE, 285 + }; 286 + 287 + static ssize_t plane_type_show(struct config_item *item, char *page) 288 + { 289 + struct vkms_configfs_plane *plane; 290 + enum drm_plane_type type; 291 + 292 + plane = plane_item_to_vkms_configfs_plane(item); 293 + 294 + scoped_guard(mutex, &plane->dev->lock) 295 + type = vkms_config_plane_get_type(plane->config); 296 + 297 + return sprintf(page, "%u", type); 298 + } 299 + 300 + static ssize_t plane_type_store(struct config_item *item, const char *page, 301 + size_t count) 302 + { 303 + struct vkms_configfs_plane *plane; 304 + enum drm_plane_type type; 305 + 306 + plane = plane_item_to_vkms_configfs_plane(item); 307 + 308 + if (kstrtouint(page, 10, &type)) 309 + return -EINVAL; 310 + 311 + if (type != DRM_PLANE_TYPE_OVERLAY && type != DRM_PLANE_TYPE_PRIMARY && 312 + type != DRM_PLANE_TYPE_CURSOR) 313 + return -EINVAL; 314 + 315 + scoped_guard(mutex, &plane->dev->lock) { 316 + if (plane->dev->enabled) 317 + return -EBUSY; 318 + 319 + vkms_config_plane_set_type(plane->config, type); 320 + } 321 + 322 + return (ssize_t)count; 323 + } 324 + 325 + CONFIGFS_ATTR(plane_, type); 326 + 327 + static struct configfs_attribute *plane_item_attrs[] = { 328 + &plane_attr_type, 329 + NULL, 330 + }; 331 + 332 + static void plane_release(struct config_item *item) 333 + { 334 + struct vkms_configfs_plane *plane; 335 + struct mutex *lock; 336 + 337 + plane = plane_item_to_vkms_configfs_plane(item); 338 + lock = &plane->dev->lock; 339 + 340 + scoped_guard(mutex, lock) { 341 + vkms_config_destroy_plane(plane->config); 342 + kfree(plane); 343 + } 344 + } 345 + 346 + static struct configfs_item_operations plane_item_operations = { 347 + .release = &plane_release, 348 + }; 349 + 350 + static const struct config_item_type plane_item_type = { 351 + .ct_attrs = plane_item_attrs, 352 + .ct_item_ops = &plane_item_operations, 353 + .ct_owner = THIS_MODULE, 354 + }; 355 + 356 + static struct config_group *make_plane_group(struct config_group *group, 357 + const char *name) 358 + { 359 + struct vkms_configfs_device *dev; 360 + struct vkms_configfs_plane *plane; 361 + 362 + dev = child_group_to_vkms_configfs_device(group); 363 + 364 + scoped_guard(mutex, &dev->lock) { 365 + if (dev->enabled) 366 + return ERR_PTR(-EBUSY); 367 + 368 + plane = kzalloc(sizeof(*plane), GFP_KERNEL); 369 + if (!plane) 370 + return ERR_PTR(-ENOMEM); 371 + 372 + plane->dev = dev; 373 + 374 + plane->config = vkms_config_create_plane(dev->config); 375 + if (IS_ERR(plane->config)) { 376 + kfree(plane); 377 + return ERR_CAST(plane->config); 378 + } 379 + 380 + config_group_init_type_name(&plane->group, name, &plane_item_type); 381 + 382 + config_group_init_type_name(&plane->possible_crtcs_group, 383 + "possible_crtcs", 384 + &plane_possible_crtcs_group_type); 385 + configfs_add_default_group(&plane->possible_crtcs_group, 386 + &plane->group); 387 + } 388 + 389 + return &plane->group; 390 + } 391 + 392 + static struct configfs_group_operations planes_group_operations = { 393 + .make_group = &make_plane_group, 394 + }; 395 + 396 + static const struct config_item_type plane_group_type = { 397 + .ct_group_ops = &planes_group_operations, 398 + .ct_owner = THIS_MODULE, 399 + }; 400 + 401 + static int encoder_possible_crtcs_allow_link(struct config_item *src, 402 + struct config_item *target) 403 + { 404 + struct vkms_configfs_encoder *encoder; 405 + struct vkms_configfs_crtc *crtc; 406 + int ret; 407 + 408 + if (target->ci_type != &crtc_item_type) 409 + return -EINVAL; 410 + 411 + encoder = encoder_possible_crtcs_item_to_vkms_configfs_encoder(src); 412 + crtc = crtc_item_to_vkms_configfs_crtc(target); 413 + 414 + scoped_guard(mutex, &encoder->dev->lock) { 415 + if (encoder->dev->enabled) 416 + return -EBUSY; 417 + 418 + ret = vkms_config_encoder_attach_crtc(encoder->config, crtc->config); 419 + } 420 + 421 + return ret; 422 + } 423 + 424 + static void encoder_possible_crtcs_drop_link(struct config_item *src, 425 + struct config_item *target) 426 + { 427 + struct vkms_configfs_encoder *encoder; 428 + struct vkms_configfs_crtc *crtc; 429 + 430 + encoder = encoder_possible_crtcs_item_to_vkms_configfs_encoder(src); 431 + crtc = crtc_item_to_vkms_configfs_crtc(target); 432 + 433 + scoped_guard(mutex, &encoder->dev->lock) 434 + vkms_config_encoder_detach_crtc(encoder->config, crtc->config); 435 + } 436 + 437 + static struct configfs_item_operations encoder_possible_crtcs_item_operations = { 438 + .allow_link = encoder_possible_crtcs_allow_link, 439 + .drop_link = encoder_possible_crtcs_drop_link, 440 + }; 441 + 442 + static const struct config_item_type encoder_possible_crtcs_group_type = { 443 + .ct_item_ops = &encoder_possible_crtcs_item_operations, 444 + .ct_owner = THIS_MODULE, 445 + }; 446 + 447 + static void encoder_release(struct config_item *item) 448 + { 449 + struct vkms_configfs_encoder *encoder; 450 + struct mutex *lock; 451 + 452 + encoder = encoder_item_to_vkms_configfs_encoder(item); 453 + lock = &encoder->dev->lock; 454 + 455 + scoped_guard(mutex, lock) { 456 + vkms_config_destroy_encoder(encoder->dev->config, encoder->config); 457 + kfree(encoder); 458 + } 459 + } 460 + 461 + static struct configfs_item_operations encoder_item_operations = { 462 + .release = &encoder_release, 463 + }; 464 + 465 + static const struct config_item_type encoder_item_type = { 466 + .ct_item_ops = &encoder_item_operations, 467 + .ct_owner = THIS_MODULE, 468 + }; 469 + 470 + static struct config_group *make_encoder_group(struct config_group *group, 471 + const char *name) 472 + { 473 + struct vkms_configfs_device *dev; 474 + struct vkms_configfs_encoder *encoder; 475 + 476 + dev = child_group_to_vkms_configfs_device(group); 477 + 478 + scoped_guard(mutex, &dev->lock) { 479 + if (dev->enabled) 480 + return ERR_PTR(-EBUSY); 481 + 482 + encoder = kzalloc(sizeof(*encoder), GFP_KERNEL); 483 + if (!encoder) 484 + return ERR_PTR(-ENOMEM); 485 + 486 + encoder->dev = dev; 487 + 488 + encoder->config = vkms_config_create_encoder(dev->config); 489 + if (IS_ERR(encoder->config)) { 490 + kfree(encoder); 491 + return ERR_CAST(encoder->config); 492 + } 493 + 494 + config_group_init_type_name(&encoder->group, name, 495 + &encoder_item_type); 496 + 497 + config_group_init_type_name(&encoder->possible_crtcs_group, 498 + "possible_crtcs", 499 + &encoder_possible_crtcs_group_type); 500 + configfs_add_default_group(&encoder->possible_crtcs_group, 501 + &encoder->group); 502 + } 503 + 504 + return &encoder->group; 505 + } 506 + 507 + static struct configfs_group_operations encoders_group_operations = { 508 + .make_group = &make_encoder_group, 509 + }; 510 + 511 + static const struct config_item_type encoder_group_type = { 512 + .ct_group_ops = &encoders_group_operations, 513 + .ct_owner = THIS_MODULE, 514 + }; 515 + 516 + static ssize_t connector_status_show(struct config_item *item, char *page) 517 + { 518 + struct vkms_configfs_connector *connector; 519 + enum drm_connector_status status; 520 + 521 + connector = connector_item_to_vkms_configfs_connector(item); 522 + 523 + scoped_guard(mutex, &connector->dev->lock) 524 + status = vkms_config_connector_get_status(connector->config); 525 + 526 + return sprintf(page, "%u", status); 527 + } 528 + 529 + static ssize_t connector_status_store(struct config_item *item, 530 + const char *page, size_t count) 531 + { 532 + struct vkms_configfs_connector *connector; 533 + enum drm_connector_status status; 534 + 535 + connector = connector_item_to_vkms_configfs_connector(item); 536 + 537 + if (kstrtouint(page, 10, &status)) 538 + return -EINVAL; 539 + 540 + if (status != connector_status_connected && 541 + status != connector_status_disconnected && 542 + status != connector_status_unknown) 543 + return -EINVAL; 544 + 545 + scoped_guard(mutex, &connector->dev->lock) { 546 + vkms_config_connector_set_status(connector->config, status); 547 + 548 + if (connector->dev->enabled) 549 + vkms_trigger_connector_hotplug(connector->dev->config->dev); 550 + } 551 + 552 + return (ssize_t)count; 553 + } 554 + 555 + CONFIGFS_ATTR(connector_, status); 556 + 557 + static struct configfs_attribute *connector_item_attrs[] = { 558 + &connector_attr_status, 559 + NULL, 560 + }; 561 + 562 + static void connector_release(struct config_item *item) 563 + { 564 + struct vkms_configfs_connector *connector; 565 + struct mutex *lock; 566 + 567 + connector = connector_item_to_vkms_configfs_connector(item); 568 + lock = &connector->dev->lock; 569 + 570 + scoped_guard(mutex, lock) { 571 + vkms_config_destroy_connector(connector->config); 572 + kfree(connector); 573 + } 574 + } 575 + 576 + static struct configfs_item_operations connector_item_operations = { 577 + .release = &connector_release, 578 + }; 579 + 580 + static const struct config_item_type connector_item_type = { 581 + .ct_attrs = connector_item_attrs, 582 + .ct_item_ops = &connector_item_operations, 583 + .ct_owner = THIS_MODULE, 584 + }; 585 + 586 + static int connector_possible_encoders_allow_link(struct config_item *src, 587 + struct config_item *target) 588 + { 589 + struct vkms_configfs_connector *connector; 590 + struct vkms_configfs_encoder *encoder; 591 + int ret; 592 + 593 + if (target->ci_type != &encoder_item_type) 594 + return -EINVAL; 595 + 596 + connector = connector_possible_encoders_item_to_vkms_configfs_connector(src); 597 + encoder = encoder_item_to_vkms_configfs_encoder(target); 598 + 599 + scoped_guard(mutex, &connector->dev->lock) { 600 + if (connector->dev->enabled) 601 + return -EBUSY; 602 + 603 + ret = vkms_config_connector_attach_encoder(connector->config, 604 + encoder->config); 605 + } 606 + 607 + return ret; 608 + } 609 + 610 + static void connector_possible_encoders_drop_link(struct config_item *src, 611 + struct config_item *target) 612 + { 613 + struct vkms_configfs_connector *connector; 614 + struct vkms_configfs_encoder *encoder; 615 + 616 + connector = connector_possible_encoders_item_to_vkms_configfs_connector(src); 617 + encoder = encoder_item_to_vkms_configfs_encoder(target); 618 + 619 + scoped_guard(mutex, &connector->dev->lock) { 620 + vkms_config_connector_detach_encoder(connector->config, 621 + encoder->config); 622 + } 623 + } 624 + 625 + static struct configfs_item_operations connector_possible_encoders_item_operations = { 626 + .allow_link = connector_possible_encoders_allow_link, 627 + .drop_link = connector_possible_encoders_drop_link, 628 + }; 629 + 630 + static const struct config_item_type connector_possible_encoders_group_type = { 631 + .ct_item_ops = &connector_possible_encoders_item_operations, 632 + .ct_owner = THIS_MODULE, 633 + }; 634 + 635 + static struct config_group *make_connector_group(struct config_group *group, 636 + const char *name) 637 + { 638 + struct vkms_configfs_device *dev; 639 + struct vkms_configfs_connector *connector; 640 + 641 + dev = child_group_to_vkms_configfs_device(group); 642 + 643 + scoped_guard(mutex, &dev->lock) { 644 + if (dev->enabled) 645 + return ERR_PTR(-EBUSY); 646 + 647 + connector = kzalloc(sizeof(*connector), GFP_KERNEL); 648 + if (!connector) 649 + return ERR_PTR(-ENOMEM); 650 + 651 + connector->dev = dev; 652 + 653 + connector->config = vkms_config_create_connector(dev->config); 654 + if (IS_ERR(connector->config)) { 655 + kfree(connector); 656 + return ERR_CAST(connector->config); 657 + } 658 + 659 + config_group_init_type_name(&connector->group, name, 660 + &connector_item_type); 661 + 662 + config_group_init_type_name(&connector->possible_encoders_group, 663 + "possible_encoders", 664 + &connector_possible_encoders_group_type); 665 + configfs_add_default_group(&connector->possible_encoders_group, 666 + &connector->group); 667 + } 668 + 669 + return &connector->group; 670 + } 671 + 672 + static struct configfs_group_operations connectors_group_operations = { 673 + .make_group = &make_connector_group, 674 + }; 675 + 676 + static const struct config_item_type connector_group_type = { 677 + .ct_group_ops = &connectors_group_operations, 678 + .ct_owner = THIS_MODULE, 679 + }; 680 + 681 + static ssize_t device_enabled_show(struct config_item *item, char *page) 682 + { 683 + struct vkms_configfs_device *dev; 684 + bool enabled; 685 + 686 + dev = device_item_to_vkms_configfs_device(item); 687 + 688 + scoped_guard(mutex, &dev->lock) 689 + enabled = dev->enabled; 690 + 691 + return sprintf(page, "%d\n", enabled); 692 + } 693 + 694 + static ssize_t device_enabled_store(struct config_item *item, const char *page, 695 + size_t count) 696 + { 697 + struct vkms_configfs_device *dev; 698 + bool enabled; 699 + int ret = 0; 700 + 701 + dev = device_item_to_vkms_configfs_device(item); 702 + 703 + if (kstrtobool(page, &enabled)) 704 + return -EINVAL; 705 + 706 + scoped_guard(mutex, &dev->lock) { 707 + if (!dev->enabled && enabled) { 708 + if (!vkms_config_is_valid(dev->config)) 709 + return -EINVAL; 710 + 711 + ret = vkms_create(dev->config); 712 + if (ret) 713 + return ret; 714 + } else if (dev->enabled && !enabled) { 715 + vkms_destroy(dev->config); 716 + } 717 + 718 + dev->enabled = enabled; 719 + } 720 + 721 + return (ssize_t)count; 722 + } 723 + 724 + CONFIGFS_ATTR(device_, enabled); 725 + 726 + static struct configfs_attribute *device_item_attrs[] = { 727 + &device_attr_enabled, 728 + NULL, 729 + }; 730 + 731 + static void device_release(struct config_item *item) 732 + { 733 + struct vkms_configfs_device *dev; 734 + 735 + dev = device_item_to_vkms_configfs_device(item); 736 + 737 + if (dev->enabled) 738 + vkms_destroy(dev->config); 739 + 740 + mutex_destroy(&dev->lock); 741 + vkms_config_destroy(dev->config); 742 + kfree(dev); 743 + } 744 + 745 + static struct configfs_item_operations device_item_operations = { 746 + .release = &device_release, 747 + }; 748 + 749 + static const struct config_item_type device_item_type = { 750 + .ct_attrs = device_item_attrs, 751 + .ct_item_ops = &device_item_operations, 752 + .ct_owner = THIS_MODULE, 753 + }; 754 + 755 + static struct config_group *make_device_group(struct config_group *group, 756 + const char *name) 757 + { 758 + struct vkms_configfs_device *dev; 759 + 760 + if (strcmp(name, DEFAULT_DEVICE_NAME) == 0) 761 + return ERR_PTR(-EINVAL); 762 + 763 + dev = kzalloc(sizeof(*dev), GFP_KERNEL); 764 + if (!dev) 765 + return ERR_PTR(-ENOMEM); 766 + 767 + dev->config = vkms_config_create(name); 768 + if (IS_ERR(dev->config)) { 769 + kfree(dev); 770 + return ERR_CAST(dev->config); 771 + } 772 + 773 + config_group_init_type_name(&dev->group, name, &device_item_type); 774 + mutex_init(&dev->lock); 775 + 776 + config_group_init_type_name(&dev->planes_group, "planes", 777 + &plane_group_type); 778 + configfs_add_default_group(&dev->planes_group, &dev->group); 779 + 780 + config_group_init_type_name(&dev->crtcs_group, "crtcs", 781 + &crtc_group_type); 782 + configfs_add_default_group(&dev->crtcs_group, &dev->group); 783 + 784 + config_group_init_type_name(&dev->encoders_group, "encoders", 785 + &encoder_group_type); 786 + configfs_add_default_group(&dev->encoders_group, &dev->group); 787 + 788 + config_group_init_type_name(&dev->connectors_group, "connectors", 789 + &connector_group_type); 790 + configfs_add_default_group(&dev->connectors_group, &dev->group); 791 + 792 + return &dev->group; 793 + } 794 + 795 + static struct configfs_group_operations device_group_ops = { 796 + .make_group = &make_device_group, 797 + }; 798 + 799 + static const struct config_item_type device_group_type = { 800 + .ct_group_ops = &device_group_ops, 801 + .ct_owner = THIS_MODULE, 802 + }; 803 + 804 + static struct configfs_subsystem vkms_subsys = { 805 + .su_group = { 806 + .cg_item = { 807 + .ci_name = "vkms", 808 + .ci_type = &device_group_type, 809 + }, 810 + }, 811 + .su_mutex = __MUTEX_INITIALIZER(vkms_subsys.su_mutex), 812 + }; 813 + 814 + int vkms_configfs_register(void) 815 + { 816 + int ret; 817 + 818 + if (is_configfs_registered) 819 + return 0; 820 + 821 + config_group_init(&vkms_subsys.su_group); 822 + ret = configfs_register_subsystem(&vkms_subsys); 823 + 824 + is_configfs_registered = ret == 0; 825 + 826 + return ret; 827 + } 828 + 829 + void vkms_configfs_unregister(void) 830 + { 831 + if (is_configfs_registered) 832 + configfs_unregister_subsystem(&vkms_subsys); 833 + }
+8
drivers/gpu/drm/vkms/vkms_configfs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0+ */ 2 + #ifndef _VKMS_CONFIGFS_H_ 3 + #define _VKMS_CONFIGFS_H_ 4 + 5 + int vkms_configfs_register(void); 6 + void vkms_configfs_unregister(void); 7 + 8 + #endif /* _VKMS_CONFIGFS_H_ */
+35
drivers/gpu/drm/vkms/vkms_connector.c
··· 5 5 #include <drm/drm_managed.h> 6 6 #include <drm/drm_probe_helper.h> 7 7 8 + #include "vkms_config.h" 8 9 #include "vkms_connector.h" 9 10 11 + static enum drm_connector_status vkms_connector_detect(struct drm_connector *connector, 12 + bool force) 13 + { 14 + struct drm_device *dev = connector->dev; 15 + struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev); 16 + struct vkms_connector *vkms_connector; 17 + enum drm_connector_status status; 18 + struct vkms_config_connector *connector_cfg; 19 + 20 + vkms_connector = drm_connector_to_vkms_connector(connector); 21 + 22 + /* 23 + * The connector configuration might not exist if its configfs directory 24 + * was deleted. Therefore, use the configuration if present or keep the 25 + * current status if we can not access it anymore. 26 + */ 27 + status = connector->status; 28 + 29 + vkms_config_for_each_connector(vkmsdev->config, connector_cfg) { 30 + if (connector_cfg->connector == vkms_connector) 31 + status = vkms_config_connector_get_status(connector_cfg); 32 + } 33 + 34 + return status; 35 + } 36 + 10 37 static const struct drm_connector_funcs vkms_connector_funcs = { 38 + .detect = vkms_connector_detect, 11 39 .fill_modes = drm_helper_probe_single_connector_modes, 12 40 .reset = drm_atomic_helper_connector_reset, 13 41 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, ··· 86 58 drm_connector_helper_add(&connector->base, &vkms_conn_helper_funcs); 87 59 88 60 return connector; 61 + } 62 + 63 + void vkms_trigger_connector_hotplug(struct vkms_device *vkmsdev) 64 + { 65 + struct drm_device *dev = &vkmsdev->drm; 66 + 67 + drm_kms_helper_hotplug_event(dev); 89 68 }
+9
drivers/gpu/drm/vkms/vkms_connector.h
··· 5 5 6 6 #include "vkms_drv.h" 7 7 8 + #define drm_connector_to_vkms_connector(target) \ 9 + container_of(target, struct vkms_connector, base) 10 + 8 11 /** 9 12 * struct vkms_connector - VKMS custom type wrapping around the DRM connector 10 13 * ··· 25 22 * The connector or an error on failure. 26 23 */ 27 24 struct vkms_connector *vkms_connector_init(struct vkms_device *vkmsdev); 25 + 26 + /** 27 + * vkms_trigger_connector_hotplug() - Update the device's connectors status 28 + * @vkmsdev: VKMS device to update 29 + */ 30 + void vkms_trigger_connector_hotplug(struct vkms_device *vkmsdev); 28 31 29 32 #endif /* _VKMS_CONNECTOR_H_ */
+16 -2
drivers/gpu/drm/vkms/vkms_drv.c
··· 28 28 #include <drm/drm_vblank.h> 29 29 30 30 #include "vkms_config.h" 31 + #include "vkms_configfs.h" 31 32 #include "vkms_drv.h" 32 33 33 34 #define DRIVER_NAME "vkms" ··· 49 48 static bool enable_overlay; 50 49 module_param_named(enable_overlay, enable_overlay, bool, 0444); 51 50 MODULE_PARM_DESC(enable_overlay, "Enable/Disable overlay support"); 51 + 52 + static bool create_default_dev = true; 53 + module_param_named(create_default_dev, create_default_dev, bool, 0444); 54 + MODULE_PARM_DESC(create_default_dev, "Create or not the default VKMS device"); 52 55 53 56 DEFINE_DRM_GEM_FOPS(vkms_driver_fops); 54 57 ··· 151 146 return vkms_output_init(vkmsdev); 152 147 } 153 148 154 - static int vkms_create(struct vkms_config *config) 149 + int vkms_create(struct vkms_config *config) 155 150 { 156 151 int ret; 157 152 struct faux_device *fdev; ··· 219 214 int ret; 220 215 struct vkms_config *config; 221 216 217 + ret = vkms_configfs_register(); 218 + if (ret) 219 + return ret; 220 + 221 + if (!create_default_dev) 222 + return 0; 223 + 222 224 config = vkms_config_default_create(enable_cursor, enable_writeback, enable_overlay); 223 225 if (IS_ERR(config)) 224 226 return PTR_ERR(config); ··· 241 229 return 0; 242 230 } 243 231 244 - static void vkms_destroy(struct vkms_config *config) 232 + void vkms_destroy(struct vkms_config *config) 245 233 { 246 234 struct faux_device *fdev; 247 235 ··· 262 250 263 251 static void __exit vkms_exit(void) 264 252 { 253 + vkms_configfs_unregister(); 254 + 265 255 if (!default_config) 266 256 return; 267 257
+20
drivers/gpu/drm/vkms/vkms_drv.h
··· 257 257 container_of(target, struct vkms_plane_state, base.base) 258 258 259 259 /** 260 + * vkms_create() - Create a device from a configuration 261 + * @config: Config used to configure the new device 262 + * 263 + * A pointer to the created vkms_device is stored in @config 264 + * 265 + * Returns: 266 + * 0 on success or an error. 267 + */ 268 + int vkms_create(struct vkms_config *config); 269 + 270 + /** 271 + * vkms_destroy() - Destroy a device 272 + * @config: Config from which the device was created 273 + * 274 + * The device is completely removed, but the @config is not freed. It can be 275 + * reused or destroyed with vkms_config_destroy(). 276 + */ 277 + void vkms_destroy(struct vkms_config *config); 278 + 279 + /** 260 280 * vkms_crtc_init() - Initialize a CRTC for VKMS 261 281 * @dev: DRM device associated with the VKMS buffer 262 282 * @crtc: uninitialized CRTC device
+10
include/drm/drm_client.h
··· 29 29 struct module *owner; 30 30 31 31 /** 32 + * @free: 33 + * 34 + * Called when the client gets unregistered. Implementations should 35 + * release all client-specific data and free the memory. 36 + * 37 + * This callback is optional. 38 + */ 39 + void (*free)(struct drm_client_dev *client); 40 + 41 + /** 32 42 * @unregister: 33 43 * 34 44 * Called when &drm_device is unregistered. The client should respond by
+261
include/uapi/drm/ethosu_accel.h
··· 1 + /* SPDX-License-Identifier: MIT */ 2 + /* Copyright (C) 2025 Arm, Ltd. */ 3 + #ifndef _ETHOSU_DRM_H_ 4 + #define _ETHOSU_DRM_H_ 5 + 6 + #include "drm.h" 7 + 8 + #if defined(__cplusplus) 9 + extern "C" { 10 + #endif 11 + 12 + /** 13 + * DOC: IOCTL IDs 14 + * 15 + * enum drm_ethosu_ioctl_id - IOCTL IDs 16 + * 17 + * Place new ioctls at the end, don't re-order, don't replace or remove entries. 18 + * 19 + * These IDs are not meant to be used directly. Use the DRM_IOCTL_ETHOSU_xxx 20 + * definitions instead. 21 + */ 22 + enum drm_ethosu_ioctl_id { 23 + /** @DRM_ETHOSU_DEV_QUERY: Query device information. */ 24 + DRM_ETHOSU_DEV_QUERY = 0, 25 + 26 + /** @DRM_ETHOSU_BO_CREATE: Create a buffer object. */ 27 + DRM_ETHOSU_BO_CREATE, 28 + 29 + /** @DRM_ETHOSU_BO_WAIT: Wait on a buffer object's fence. */ 30 + DRM_ETHOSU_BO_WAIT, 31 + 32 + /** 33 + * @DRM_ETHOSU_BO_MMAP_OFFSET: Get the file offset to pass to 34 + * mmap to map a GEM object. 35 + */ 36 + DRM_ETHOSU_BO_MMAP_OFFSET, 37 + 38 + /** 39 + * @DRM_ETHOSU_CMDSTREAM_BO_CREATE: Create a command stream buffer 40 + * object. 41 + */ 42 + DRM_ETHOSU_CMDSTREAM_BO_CREATE, 43 + 44 + /** @DRM_ETHOSU_SUBMIT: Submit a job and BOs to run. */ 45 + DRM_ETHOSU_SUBMIT, 46 + }; 47 + 48 + /** 49 + * DOC: IOCTL arguments 50 + */ 51 + 52 + /** 53 + * enum drm_ethosu_dev_query_type - Query type 54 + * 55 + * Place new types at the end, don't re-order, don't remove or replace. 56 + */ 57 + enum drm_ethosu_dev_query_type { 58 + /** @DRM_ETHOSU_DEV_QUERY_NPU_INFO: Query NPU information. */ 59 + DRM_ETHOSU_DEV_QUERY_NPU_INFO = 0, 60 + }; 61 + 62 + /** 63 + * struct drm_ethosu_gpu_info - NPU information 64 + * 65 + * Structure grouping all queryable information relating to the NPU. 66 + */ 67 + struct drm_ethosu_npu_info { 68 + /** @id : NPU ID. */ 69 + __u32 id; 70 + #define DRM_ETHOSU_ARCH_MAJOR(x) ((x) >> 28) 71 + #define DRM_ETHOSU_ARCH_MINOR(x) (((x) >> 20) & 0xff) 72 + #define DRM_ETHOSU_ARCH_PATCH(x) (((x) >> 16) & 0xf) 73 + #define DRM_ETHOSU_PRODUCT_MAJOR(x) (((x) >> 12) & 0xf) 74 + #define DRM_ETHOSU_VERSION_MAJOR(x) (((x) >> 8) & 0xf) 75 + #define DRM_ETHOSU_VERSION_MINOR(x) (((x) >> 4) & 0xff) 76 + #define DRM_ETHOSU_VERSION_STATUS(x) ((x) & 0xf) 77 + 78 + /** @gpu_rev: GPU revision. */ 79 + __u32 config; 80 + 81 + __u32 sram_size; 82 + }; 83 + 84 + /** 85 + * struct drm_ethosu_dev_query - Arguments passed to DRM_ETHOSU_IOCTL_DEV_QUERY 86 + */ 87 + struct drm_ethosu_dev_query { 88 + /** @type: the query type (see drm_ethosu_dev_query_type). */ 89 + __u32 type; 90 + 91 + /** 92 + * @size: size of the type being queried. 93 + * 94 + * If pointer is NULL, size is updated by the driver to provide the 95 + * output structure size. If pointer is not NULL, the driver will 96 + * only copy min(size, actual_structure_size) bytes to the pointer, 97 + * and update the size accordingly. This allows us to extend query 98 + * types without breaking userspace. 99 + */ 100 + __u32 size; 101 + 102 + /** 103 + * @pointer: user pointer to a query type struct. 104 + * 105 + * Pointer can be NULL, in which case, nothing is copied, but the 106 + * actual structure size is returned. If not NULL, it must point to 107 + * a location that's large enough to hold size bytes. 108 + */ 109 + __u64 pointer; 110 + }; 111 + 112 + /** 113 + * enum drm_ethosu_bo_flags - Buffer object flags, passed at creation time. 114 + */ 115 + enum drm_ethosu_bo_flags { 116 + /** 117 + * @DRM_ETHOSU_BO_NO_MMAP: The buffer object will never be CPU-mapped 118 + * in userspace. 119 + */ 120 + DRM_ETHOSU_BO_NO_MMAP = (1 << 0), 121 + }; 122 + 123 + /** 124 + * struct drm_ethosu_bo_create - Arguments passed to DRM_IOCTL_ETHOSU_BO_CREATE. 125 + */ 126 + struct drm_ethosu_bo_create { 127 + /** 128 + * @size: Requested size for the object 129 + * 130 + * The (page-aligned) allocated size for the object will be returned. 131 + */ 132 + __u64 size; 133 + 134 + /** 135 + * @flags: Flags. Must be a combination of drm_ethosu_bo_flags flags. 136 + */ 137 + __u32 flags; 138 + 139 + /** 140 + * @handle: Returned handle for the object. 141 + * 142 + * Object handles are nonzero. 143 + */ 144 + __u32 handle; 145 + }; 146 + 147 + /** 148 + * struct drm_ethosu_bo_mmap_offset - Arguments passed to DRM_IOCTL_ETHOSU_BO_MMAP_OFFSET. 149 + */ 150 + struct drm_ethosu_bo_mmap_offset { 151 + /** @handle: Handle of the object we want an mmap offset for. */ 152 + __u32 handle; 153 + 154 + /** @pad: MBZ. */ 155 + __u32 pad; 156 + 157 + /** @offset: The fake offset to use for subsequent mmap calls. */ 158 + __u64 offset; 159 + }; 160 + 161 + /** 162 + * struct drm_ethosu_wait_bo - ioctl argument for waiting for 163 + * completion of the last DRM_ETHOSU_SUBMIT on a BO. 164 + * 165 + * This is useful for cases where multiple processes might be 166 + * rendering to a BO and you want to wait for all rendering to be 167 + * completed. 168 + */ 169 + struct drm_ethosu_bo_wait { 170 + __u32 handle; 171 + __u32 pad; 172 + __s64 timeout_ns; /* absolute */ 173 + }; 174 + 175 + struct drm_ethosu_cmdstream_bo_create { 176 + /* Size of the data argument. */ 177 + __u32 size; 178 + 179 + /* Flags, currently must be 0. */ 180 + __u32 flags; 181 + 182 + /* Pointer to the data. */ 183 + __u64 data; 184 + 185 + /** Returned GEM handle for the BO. */ 186 + __u32 handle; 187 + 188 + /* Pad, must be 0. */ 189 + __u32 pad; 190 + }; 191 + 192 + /** 193 + * struct drm_ethosu_job - A job to be run on the NPU 194 + * 195 + * The kernel will schedule the execution of this job taking into account its 196 + * dependencies with other jobs. All tasks in the same job will be executed 197 + * sequentially on the same core, to benefit from memory residency in SRAM. 198 + */ 199 + struct drm_ethosu_job { 200 + /** Input: BO handle for cmdstream. */ 201 + __u32 cmd_bo; 202 + 203 + /** Input: Amount of SRAM to use. */ 204 + __u32 sram_size; 205 + 206 + #define ETHOSU_MAX_REGIONS 8 207 + /** Input: Array of BO handles for each region. */ 208 + __u32 region_bo_handles[ETHOSU_MAX_REGIONS]; 209 + }; 210 + 211 + /** 212 + * struct drm_ethosu_submit - ioctl argument for submitting commands to the NPU. 213 + * 214 + * The kernel will schedule the execution of these jobs in dependency order. 215 + */ 216 + struct drm_ethosu_submit { 217 + /** Input: Pointer to an array of struct drm_ethosu_job. */ 218 + __u64 jobs; 219 + 220 + /** Input: Number of jobs passed in. */ 221 + __u32 job_count; 222 + 223 + /** Reserved, must be zero. */ 224 + __u32 pad; 225 + }; 226 + 227 + /** 228 + * DRM_IOCTL_ETHOSU() - Build a ethosu IOCTL number 229 + * @__access: Access type. Must be R, W or RW. 230 + * @__id: One of the DRM_ETHOSU_xxx id. 231 + * @__type: Suffix of the type being passed to the IOCTL. 232 + * 233 + * Don't use this macro directly, use the DRM_IOCTL_ETHOSU_xxx 234 + * values instead. 235 + * 236 + * Return: An IOCTL number to be passed to ioctl() from userspace. 237 + */ 238 + #define DRM_IOCTL_ETHOSU(__access, __id, __type) \ 239 + DRM_IO ## __access(DRM_COMMAND_BASE + DRM_ETHOSU_ ## __id, \ 240 + struct drm_ethosu_ ## __type) 241 + 242 + enum { 243 + DRM_IOCTL_ETHOSU_DEV_QUERY = 244 + DRM_IOCTL_ETHOSU(WR, DEV_QUERY, dev_query), 245 + DRM_IOCTL_ETHOSU_BO_CREATE = 246 + DRM_IOCTL_ETHOSU(WR, BO_CREATE, bo_create), 247 + DRM_IOCTL_ETHOSU_BO_WAIT = 248 + DRM_IOCTL_ETHOSU(WR, BO_WAIT, bo_wait), 249 + DRM_IOCTL_ETHOSU_BO_MMAP_OFFSET = 250 + DRM_IOCTL_ETHOSU(WR, BO_MMAP_OFFSET, bo_mmap_offset), 251 + DRM_IOCTL_ETHOSU_CMDSTREAM_BO_CREATE = 252 + DRM_IOCTL_ETHOSU(WR, CMDSTREAM_BO_CREATE, cmdstream_bo_create), 253 + DRM_IOCTL_ETHOSU_SUBMIT = 254 + DRM_IOCTL_ETHOSU(WR, SUBMIT, submit), 255 + }; 256 + 257 + #if defined(__cplusplus) 258 + } 259 + #endif 260 + 261 + #endif /* _ETHOSU_DRM_H_ */