summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2026-04-29 12:30:00 +0200
committerThierry Reding <treding@nvidia.com>2026-04-29 12:30:00 +0200
commite48ef8c3e17e4bacdb8a6206fa6d3b33eda74644 (patch)
treef1f08c2518f527d68a7e67f49fe041fb21737d6e
parentf3dc3b95346e4648645c2183fcf5a9abd9dfa244 (diff)
parentc82a16b0caac5799333bd3dc63c12ab4c6ee13b4 (diff)
downloadlinux-next-e48ef8c3e17e4bacdb8a6206fa6d3b33eda74644.tar.gz
linux-next-e48ef8c3e17e4bacdb8a6206fa6d3b33eda74644.zip
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/misc/kernel.git
-rw-r--r--Documentation/accel/amdxdna/amdnpu.rst25
-rw-r--r--Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml9
-rw-r--r--Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml70
-rw-r--r--Documentation/devicetree/bindings/display/panel/himax,hx83102.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml6
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-simple.yaml28
-rw-r--r--Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml2
-rw-r--r--Documentation/gpu/drm-usage-stats.rst1
-rw-r--r--drivers/accel/amdxdna/Makefile10
-rw-r--r--drivers/accel/amdxdna/aie.c119
-rw-r--r--drivers/accel/amdxdna/aie.h111
-rw-r--r--drivers/accel/amdxdna/aie2_ctx.c86
-rw-r--r--drivers/accel/amdxdna/aie2_error.c17
-rw-r--r--drivers/accel/amdxdna/aie2_message.c229
-rw-r--r--drivers/accel/amdxdna/aie2_msg_priv.h41
-rw-r--r--drivers/accel/amdxdna/aie2_pci.c234
-rw-r--r--drivers/accel/amdxdna/aie2_pci.h117
-rw-r--r--drivers/accel/amdxdna/aie2_pm.c22
-rw-r--r--drivers/accel/amdxdna/aie2_psp.c161
-rw-r--r--drivers/accel/amdxdna/aie2_smu.c156
-rw-r--r--drivers/accel/amdxdna/aie2_solver.c10
-rw-r--r--drivers/accel/amdxdna/aie4_message.c27
-rw-r--r--drivers/accel/amdxdna/aie4_msg_priv.h49
-rw-r--r--drivers/accel/amdxdna/aie4_pci.c483
-rw-r--r--drivers/accel/amdxdna/aie4_pci.h53
-rw-r--r--drivers/accel/amdxdna/aie4_sriov.c88
-rw-r--r--drivers/accel/amdxdna/aie_psp.c235
-rw-r--r--drivers/accel/amdxdna/aie_smu.c153
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.c3
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.h1
-rw-r--r--drivers/accel/amdxdna/amdxdna_iommu.c13
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox.c30
-rw-r--r--drivers/accel/amdxdna/amdxdna_mailbox.h8
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.c51
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.h16
-rw-r--r--drivers/accel/amdxdna/amdxdna_sysfs.c5
-rw-r--r--drivers/accel/amdxdna/amdxdna_ubuf.c41
-rw-r--r--drivers/accel/amdxdna/npu1_regs.c30
-rw-r--r--drivers/accel/amdxdna/npu3_regs.c77
-rw-r--r--drivers/accel/amdxdna/npu4_regs.c71
-rw-r--r--drivers/accel/amdxdna/npu5_regs.c10
-rw-r--r--drivers/accel/amdxdna/npu6_regs.c10
-rw-r--r--drivers/accel/ethosu/ethosu_job.c1
-rw-r--r--drivers/accel/ivpu/ivpu_hw_ip.c16
-rw-r--r--drivers/accel/qaic/mhi_controller.c2
-rw-r--r--drivers/accel/qaic/mhi_controller.h9
-rw-r--r--drivers/accel/qaic/qaic.h9
-rw-r--r--drivers/accel/qaic/qaic_control.c2
-rw-r--r--drivers/accel/qaic/qaic_data.c2
-rw-r--r--drivers/accel/qaic/qaic_debugfs.c30
-rw-r--r--drivers/accel/qaic/qaic_debugfs.h2
-rw-r--r--drivers/accel/qaic/qaic_drv.c2
-rw-r--r--drivers/accel/qaic/qaic_ras.c1
-rw-r--r--drivers/accel/qaic/qaic_ras.h1
-rw-r--r--drivers/accel/qaic/qaic_ssr.c2
-rw-r--r--drivers/accel/qaic/qaic_ssr.h9
-rw-r--r--drivers/accel/qaic/qaic_timesync.c3
-rw-r--r--drivers/accel/qaic/qaic_timesync.h7
-rw-r--r--drivers/accel/qaic/sahara.c2
-rw-r--r--drivers/accel/qaic/sahara.h2
-rw-r--r--drivers/accel/rocket/rocket_job.c1
-rw-r--r--drivers/dma-buf/.kunitconfig2
-rw-r--r--drivers/dma-buf/Kconfig11
-rw-r--r--drivers/dma-buf/Makefile5
-rw-r--r--drivers/dma-buf/dma-fence.c3
-rw-r--r--drivers/dma-buf/selftest.c167
-rw-r--r--drivers/dma-buf/selftest.h30
-rw-r--r--drivers/dma-buf/selftests.h16
-rw-r--r--drivers/dma-buf/st-dma-fence-chain.c213
-rw-r--r--drivers/dma-buf/st-dma-fence-unwrap.c292
-rw-r--r--drivers/dma-buf/st-dma-fence.c200
-rw-r--r--drivers/dma-buf/st-dma-resv.c145
-rw-r--r--drivers/dma-buf/udmabuf.c58
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c29
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.h5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c8
-rw-r--r--drivers/gpu/drm/ast/ast_2000.c61
-rw-r--r--drivers/gpu/drm/ast/ast_2100.c292
-rw-r--r--drivers/gpu/drm/ast/ast_2300.c398
-rw-r--r--drivers/gpu/drm/ast/ast_2500.c329
-rw-r--r--drivers/gpu/drm/ast/ast_dp501.c146
-rw-r--r--drivers/gpu/drm/ast/ast_drv.c84
-rw-r--r--drivers/gpu/drm/ast/ast_drv.h60
-rw-r--r--drivers/gpu/drm/ast/ast_mode.c86
-rw-r--r--drivers/gpu/drm/ast/ast_post.c48
-rw-r--r--drivers/gpu/drm/ast/ast_post.h20
-rw-r--r--drivers/gpu/drm/ast/ast_reg.h211
-rw-r--r--drivers/gpu/drm/bridge/Kconfig10
-rw-r--r--drivers/gpu/drm/bridge/Makefile1
-rw-r--r--drivers/gpu/drm/bridge/analogix/Kconfig3
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.c237
-rw-r--r--drivers/gpu/drm/bridge/analogix/analogix_dp_core.h1
-rw-r--r--drivers/gpu/drm/bridge/imx/Kconfig28
-rw-r--r--drivers/gpu/drm/bridge/imx/Makefile3
-rw-r--r--drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c91
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.c75
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.dtso30
-rw-r--r--drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c1
-rw-r--r--drivers/gpu/drm/bridge/of-display-mode-bridge.c93
-rw-r--r--drivers/gpu/drm/bridge/simple-bridge.c5
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-dp.c4
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c49
-rw-r--r--drivers/gpu/drm/bridge/waveshare-dsi.c5
-rw-r--r--drivers/gpu/drm/drm_auth.c2
-rw-r--r--drivers/gpu/drm/drm_bridge.c87
-rw-r--r--drivers/gpu/drm/drm_colorop.c8
-rw-r--r--drivers/gpu/drm/drm_connector.c9
-rw-r--r--drivers/gpu/drm/drm_edid.c465
-rw-r--r--drivers/gpu/drm/drm_encoder.c18
-rw-r--r--drivers/gpu/drm/drm_gem.c10
-rw-r--r--drivers/gpu/drm/drm_gpusvm.c53
-rw-r--r--drivers/gpu/drm/drm_gpuvm.c3
-rw-r--r--drivers/gpu/drm/drm_mipi_dsi.c28
-rw-r--r--drivers/gpu/drm/drm_of.c34
-rw-r--r--drivers/gpu/drm/drm_pagemap.c229
-rw-r--r--drivers/gpu/drm/drm_panel.c23
-rw-r--r--drivers/gpu/drm/drm_vblank.c36
-rw-r--r--drivers/gpu/drm/etnaviv/etnaviv_sched.c1
-rw-r--r--drivers/gpu/drm/exynos/Kconfig3
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp.c110
-rw-r--r--drivers/gpu/drm/hyperv/hyperv_drm_proto.c2
-rw-r--r--drivers/gpu/drm/i915/Kconfig.debug2
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw_trace.c2
-rw-r--r--drivers/gpu/drm/imagination/pvr_job.c8
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c155
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.h2
-rw-r--r--drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h10
-rw-r--r--drivers/gpu/drm/imagination/pvr_sync.c8
-rw-r--r--drivers/gpu/drm/imagination/pvr_sync.h2
-rw-r--r--drivers/gpu/drm/imx/ipuv3/Kconfig4
-rw-r--r--drivers/gpu/drm/imx/ipuv3/imx-ldb.c6
-rw-r--r--drivers/gpu/drm/imx/ipuv3/parallel-display.c5
-rw-r--r--drivers/gpu/drm/kmb/kmb_dsi.c2
-rw-r--r--drivers/gpu/drm/lima/lima_sched.c1
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_g200se.c13
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_mode.c99
-rw-r--r--drivers/gpu/drm/mgag200/mgag200_reg.h1
-rw-r--r--drivers/gpu/drm/msm/msm_gem_vma.c1
-rw-r--r--drivers/gpu/drm/msm/msm_ringbuffer.c1
-rw-r--r--drivers/gpu/drm/mxsfb/Kconfig2
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.c69
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drm.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_sched.c4
-rw-r--r--drivers/gpu/drm/omapdrm/dss/output.c11
-rw-r--r--drivers/gpu/drm/panel/Kconfig13
-rw-r--r--drivers/gpu/drm/panel/Makefile1
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c2
-rw-r--r--drivers/gpu/drm/panel/panel-focaltech-ota7290b.c225
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx83102.c144
-rw-r--r--drivers/gpu/drm/panel/panel-himax-hx8394.c279
-rw-r--r--drivers/gpu/drm/panel/panel-ilitek-ili9881c.c251
-rw-r--r--drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c1421
-rw-r--r--drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c24
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c381
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.c34
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_device.h1
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c1
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_job.c1
-rw-r--r--drivers/gpu/drm/panthor/Kconfig1
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.c11
-rw-r--r--drivers/gpu/drm/panthor/panthor_device.h73
-rw-r--r--drivers/gpu/drm/panthor/panthor_drv.c33
-rw-r--r--drivers/gpu/drm/panthor/panthor_fw.c16
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c1415
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.h136
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c511
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.h8
-rw-r--r--drivers/gpu/drm/panthor/panthor_sched.c85
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c29
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h1
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c2
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c13
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c16
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h2
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c34
-rw-r--r--drivers/gpu/drm/rockchip/Kconfig1
-rw-r--r--drivers/gpu/drm/rockchip/analogix_dp-rockchip.c69
-rw-r--r--drivers/gpu/drm/rockchip/cdn-dp-reg.c2
-rw-r--r--drivers/gpu/drm/rockchip/dw_dp-rockchip.c35
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c13
-rw-r--r--drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c3
-rw-r--r--drivers/gpu/drm/rockchip/rockchip_drm_gem.c2
-rw-r--r--drivers/gpu/drm/scheduler/Makefile2
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c175
-rw-r--r--drivers/gpu/drm/scheduler/sched_fence.c2
-rw-r--r--drivers/gpu/drm/scheduler/sched_internal.h74
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c355
-rw-r--r--drivers/gpu/drm/scheduler/sched_rq.c383
-rw-r--r--drivers/gpu/drm/scheduler/tests/Makefile3
-rw-r--r--drivers/gpu/drm/scheduler/tests/mock_scheduler.c1
-rw-r--r--drivers/gpu/drm/scheduler/tests/tests_scheduler.c882
-rw-r--r--drivers/gpu/drm/tidss/tidss_encoder.c2
-rw-r--r--drivers/gpu/drm/tidss/tidss_kms.c2
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_crtc.c2
-rw-r--r--drivers/gpu/drm/v3d/Makefile1
-rw-r--r--drivers/gpu/drm/v3d/v3d_debugfs.c23
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.c158
-rw-r--r--drivers/gpu/drm/v3d/v3d_drv.h18
-rw-r--r--drivers/gpu/drm/v3d/v3d_gem.c17
-rw-r--r--drivers/gpu/drm/v3d/v3d_irq.c15
-rw-r--r--drivers/gpu/drm/v3d/v3d_mmu.c10
-rw-r--r--drivers/gpu/drm/v3d/v3d_perfmon.c18
-rw-r--r--drivers/gpu/drm/v3d/v3d_power.c87
-rw-r--r--drivers/gpu/drm/v3d/v3d_sched.c1
-rw-r--r--drivers/gpu/drm/v3d/v3d_submit.c19
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_gem.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_irq.c33
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_render_cl.c1
-rw-r--r--drivers/gpu/drm/vc4/vc4_validate.c1
-rw-r--r--drivers/gpu/drm/xe/xe_dep_scheduler.c1
-rw-r--r--drivers/gpu/drm/xe/xe_execlist.c1
-rw-r--r--drivers/gpu/drm/xe/xe_gpu_scheduler.c1
-rw-r--r--drivers/gpu/drm/xe/xe_svm.c1
-rw-r--r--include/drm/bridge/analogix_dp.h8
-rw-r--r--include/drm/bridge/dw_hdmi.h6
-rw-r--r--include/drm/bridge/imx.h17
-rw-r--r--include/drm/bridge/of-display-mode-bridge.h17
-rw-r--r--include/drm/drm_bridge.h73
-rw-r--r--include/drm/drm_connector.h2
-rw-r--r--include/drm/drm_edid.h3
-rw-r--r--include/drm/drm_encoder.h4
-rw-r--r--include/drm/drm_gpusvm.h5
-rw-r--r--include/drm/drm_mipi_dsi.h1
-rw-r--r--include/drm/drm_of.h13
-rw-r--r--include/drm/drm_pagemap.h9
-rw-r--r--include/drm/drm_panel.h1
-rw-r--r--include/drm/gpu_scheduler.h45
-rw-r--r--include/trace/events/amdxdna.h42
-rw-r--r--include/trace/events/dma_fence.h40
-rw-r--r--include/uapi/drm/amdxdna_accel.h3
-rw-r--r--include/uapi/drm/drm.h7
244 files changed, 11868 insertions, 4144 deletions
diff --git a/Documentation/accel/amdxdna/amdnpu.rst b/Documentation/accel/amdxdna/amdnpu.rst
index 42e54904f9a8..064973bf4893 100644
--- a/Documentation/accel/amdxdna/amdnpu.rst
+++ b/Documentation/accel/amdxdna/amdnpu.rst
@@ -270,6 +270,31 @@ MERT can report various kinds of telemetry information like the following:
* Deep Sleep counter
* etc.
+.. _amdxdna-usage-stats:
+
+Amdxdna DRM client usage stats implementation
+=============================================
+
+The amdxdna driver implements the DRM client usage stats specification as
+documented in :ref:`drm-client-usage-stats`.
+
+Example of the output showing the implemented key value pairs:
+
+::
+
+ pos: 0
+ flags: 0100002
+ mnt_id: 29
+ ino: 939
+ drm-driver: amdxdna_accel_driver
+ drm-client-id: 3219
+ drm-pdev: 0000:c5:00.1
+ amdxdna_accel_driver-heap-alloc: 60 KiB
+ amdxdna_accel_driver-internal-alloc: 67588 KiB
+ amdxdna_accel_driver-external-alloc: 0
+ drm-total-memory: 67632 KiB
+ drm-shared-memory: 0
+
References
==========
diff --git a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
index 7586d681bcc6..0363201f0e61 100644
--- a/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/lvds-codec.yaml
@@ -34,6 +34,7 @@ properties:
- items:
- enum:
- doestek,dtc34lm85am # For the Doestek DTC34LM85AM Flat Panel Display (FPD) Transmitter
+ - idt,v103 # For the Triple 10-BIT LVDS Transmitter
- onnn,fin3385 # OnSemi FIN3385
- ti,ds90c185 # For the TI DS90C185 FPD-Link Serializer
- ti,ds90c187 # For the TI DS90C187 FPD-Link Serializer
diff --git a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
index e6808419f625..7636c24906ba 100644
--- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
@@ -30,6 +30,7 @@ properties:
- algoltek,ag6311
- asl-tek,cs5263
- dumb-vga-dac
+ - mstar,tsumu88adt3-lf-1
- parade,ps185hdm
- radxa,ra620
- realtek,rtd2171
diff --git a/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml b/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml
index 3820dd7e11af..4d34a92192bf 100644
--- a/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/waveshare,dsi2dpi.yaml
@@ -10,11 +10,14 @@ maintainers:
- Joseph Guo <qijian.guo@nxp.com>
description:
- Waveshare bridge board is part of Waveshare panel which converts DSI to DPI.
+ Waveshare bridge board is part of Waveshare panel which converts DSI to DPI
+ or LVDS.
properties:
compatible:
- const: waveshare,dsi2dpi
+ enum:
+ - waveshare,dsi2dpi
+ - waveshare,dsi2lvds
reg:
maxItems: 1
@@ -53,7 +56,7 @@ properties:
port@1:
$ref: /schemas/graph.yaml#/properties/port
description:
- Video port for MIPI DPI output panel.
+ Video port for MIPI DPI or LVDS output to the panel.
required:
- port@0
diff --git a/Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml b/Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml
new file mode 100644
index 000000000000..db6775f4d75c
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/focaltech,ota7290b.yaml
@@ -0,0 +1,70 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/focaltech,ota7290b.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Focaltech OTA7290B DSI panels
+
+maintainers:
+ - Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ const: waveshare,8.8-dsi-touch-a
+
+ reg:
+ maxItems: 1
+
+ vdd-supply:
+ description: supply regulator for VDD, usually 3.3V
+
+ vdda-supply:
+ description: supply regulator for VDDA, 7-10V
+
+ vcc-supply:
+ description: supply regulator for VCCIO, usually 1.5V
+
+ reset-gpios: true
+ backlight: true
+ rotation: true
+ port: true
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - vcc-supply
+ - reset-gpios
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ panel@0 {
+ compatible = "waveshare,8.8-dsi-touch-a";
+ reg = <0>;
+ vdd-supply = <&vdd>;
+ vcc-supply = <&vccio>;
+ reset-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ backlight = <&backlight>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&mipi_out_panel>;
+ };
+ };
+ };
+ };
+
+...
+
diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx83102.yaml b/Documentation/devicetree/bindings/display/panel/himax,hx83102.yaml
index 66404b425af3..7667428bf9a8 100644
--- a/Documentation/devicetree/bindings/display/panel/himax,hx83102.yaml
+++ b/Documentation/devicetree/bindings/display/panel/himax,hx83102.yaml
@@ -30,6 +30,8 @@ properties:
- starry,2082109qfh040022-50e
# STARRY himax83102-j02 10.51" WUXGA TFT LCD panel
- starry,himax83102-j02
+ # Waveshare 12.3-DSI-TOUCH-A panel
+ - waveshare,12.3-dsi-touch-a
- const: himax,hx83102
reg:
diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml b/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml
index 84e840e0224f..83c343b02835 100644
--- a/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml
+++ b/Documentation/devicetree/bindings/display/panel/himax,hx8394.yaml
@@ -23,6 +23,8 @@ properties:
- hannstar,hsd060bhw4
- microchip,ac40t08a-mipi-panel
- powkiddy,x55-panel
+ - waveshare,5.0-dsi-touch-a
+ - waveshare,5.5-dsi-touch-a
- const: himax,hx8394
- items:
- enum:
diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml
index d979701a00a8..42e35986fbf6 100644
--- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml
+++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml
@@ -24,6 +24,7 @@ properties:
- raspberrypi,dsi-7inch
- startek,kd050hdfia020
- tdo,tl050hdv35
+ - waveshare,7.0-dsi-touch-a
- wanchanglong,w552946aaa
- wanchanglong,w552946aba
- const: ilitek,ili9881c
@@ -34,6 +35,7 @@ properties:
backlight: true
port: true
power-supply: true
+ iovcc-supply: true
reset-gpios: true
rotation: true
diff --git a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
index e39efb44ed42..4eae802de9fd 100644
--- a/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
+++ b/Documentation/devicetree/bindings/display/panel/jadard,jd9365da-h3.yaml
@@ -24,6 +24,12 @@ properties:
- radxa,display-10hd-ad001
- radxa,display-8hd-ad002
- taiguanck,xti05101-01a
+ - waveshare,3.4-dsi-touch-c
+ - waveshare,4.0-dsi-touch-c
+ - waveshare,8.0-dsi-touch-a
+ - waveshare,9.0-dsi-touch-b
+ - waveshare,10.1-dsi-touch-a
+ - waveshare,10.1-dsi-touch-b
- const: jadard,jd9365da-h3
reg:
diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
index 3e41ed0ef5d5..6e83e03d7456 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
@@ -341,10 +341,38 @@ properties:
- vivax,tpc9150-panel
# VXT 800x480 color TFT LCD panel
- vxt,vl050-8048nt-c01
+ # Waveshare 10.1" WXGA (1280x800) LCD panel
+ - waveshare,10.1inch-c-panel
+ # Waveshare 11.9" (320x1480) LCD panel
+ - waveshare,11.9inch-panel
# Waveshare 13.3" FHD (1920x1080) LCD panel
- waveshare,13.3inch-panel
+ # Waveshare 2.8" VGA (480x640) LCD panel
+ - waveshare,2.8inch-panel
+ # Waveshare 3.4" (800x800) LCD panel
+ - waveshare,3.4inch-c-panel
+ # Waveshare 4.0" WVGA (480x800) LCD panel
+ - waveshare,4.0inch-panel
+ # Waveshare 4.0" (720x720) LCD panel
+ - waveshare,4.0inch-c-panel
+ # Waveshare 5.0" WSVGA (1024x600) LCD panel
+ - waveshare,5.0inch-c-panel
+ # Waveshare 5.0" HD 720p (720x1280) LCD panel
+ - waveshare,5.0inch-d-panel
+ # Waveshare 6.25" (720x1560) LCD panel
+ - waveshare,6.25inch-panel
# Waveshare 7.0" WSVGA (1024x600) LCD panel
- waveshare,7.0inch-c-panel
+ # Waveshare 7.0" WXGA (1280x800) LCD panel
+ - waveshare,7.0inch-e-panel
+ # Waveshare 7.0" HD 720p (720x1280) LCD panel
+ - waveshare,7.0inch-h-panel
+ # Waveshare 7.9" (400x1280) LCD panel
+ - waveshare,7.9inch-panel
+ # Waveshare 8.0" WXGA (1280x800) LCD panel
+ - waveshare,8.0inch-c-panel
+ # Waveshare 8.8" (480x1920) LCD panel
+ - waveshare,8.8inch-panel
# Winstar Display Corporation 3.5" QVGA (320x240) TFT LCD panel
- winstar,wf35ltiacd
# Yes Optoelectronics YTC700TLAG-05-201C 7" TFT LCD panel
diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
index db49b8ff8c74..9db9f84ad964 100644
--- a/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-bifrost.yaml
@@ -26,6 +26,7 @@ properties:
- realtek,rtd1619-mali
- renesas,r9a07g044-mali
- renesas,r9a07g054-mali
+ - renesas,r9a08g046-mali
- renesas,r9a09g047-mali
- renesas,r9a09g056-mali
- renesas,r9a09g057-mali
@@ -150,6 +151,7 @@ allOf:
enum:
- renesas,r9a07g044-mali
- renesas,r9a07g054-mali
+ - renesas,r9a08g046-mali
- renesas,r9a09g047-mali
- renesas,r9a09g056-mali
- renesas,r9a09g057-mali
diff --git a/Documentation/gpu/drm-usage-stats.rst b/Documentation/gpu/drm-usage-stats.rst
index 63d6b2abe5ad..24d3012ca7a6 100644
--- a/Documentation/gpu/drm-usage-stats.rst
+++ b/Documentation/gpu/drm-usage-stats.rst
@@ -215,3 +215,4 @@ Driver specific implementations
* :ref:`panfrost-usage-stats`
* :ref:`panthor-usage-stats`
* :ref:`xe-usage-stats`
+* :ref:`amdxdna-usage-stats`
diff --git a/drivers/accel/amdxdna/Makefile b/drivers/accel/amdxdna/Makefile
index cf9bf19dedb9..79369e497540 100644
--- a/drivers/accel/amdxdna/Makefile
+++ b/drivers/accel/amdxdna/Makefile
@@ -1,14 +1,17 @@
# SPDX-License-Identifier: GPL-2.0-only
amdxdna-y := \
+ aie.o \
+ aie_psp.o \
+ aie_smu.o \
aie2_ctx.o \
aie2_error.o \
aie2_message.o \
aie2_pci.o \
aie2_pm.o \
- aie2_psp.o \
- aie2_smu.o \
aie2_solver.o \
+ aie4_message.o \
+ aie4_pci.o \
amdxdna_ctx.o \
amdxdna_gem.o \
amdxdna_iommu.o \
@@ -19,7 +22,10 @@ amdxdna-y := \
amdxdna_sysfs.o \
amdxdna_ubuf.o \
npu1_regs.o \
+ npu3_regs.o \
npu4_regs.o \
npu5_regs.o \
npu6_regs.o
+
+amdxdna-$(CONFIG_PCI_IOV) += aie4_sriov.o
obj-$(CONFIG_DRM_ACCEL_AMDXDNA) = amdxdna.o
diff --git a/drivers/accel/amdxdna/aie.c b/drivers/accel/amdxdna/aie.c
new file mode 100644
index 000000000000..66849ba9026a
--- /dev/null
+++ b/drivers/accel/amdxdna/aie.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <linux/errno.h>
+
+#include "aie.h"
+#include "amdxdna_mailbox_helper.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_pci_drv.h"
+
+void aie_dump_mgmt_chann_debug(struct aie_device *aie)
+{
+ struct amdxdna_dev *xdna = aie->xdna;
+
+ XDNA_DBG(xdna, "i2x tail 0x%x", aie->mgmt_i2x.mb_tail_ptr_reg);
+ XDNA_DBG(xdna, "i2x head 0x%x", aie->mgmt_i2x.mb_head_ptr_reg);
+ XDNA_DBG(xdna, "i2x ringbuf 0x%x", aie->mgmt_i2x.rb_start_addr);
+ XDNA_DBG(xdna, "i2x rsize 0x%x", aie->mgmt_i2x.rb_size);
+ XDNA_DBG(xdna, "x2i tail 0x%x", aie->mgmt_x2i.mb_tail_ptr_reg);
+ XDNA_DBG(xdna, "x2i head 0x%x", aie->mgmt_x2i.mb_head_ptr_reg);
+ XDNA_DBG(xdna, "x2i ringbuf 0x%x", aie->mgmt_x2i.rb_start_addr);
+ XDNA_DBG(xdna, "x2i rsize 0x%x", aie->mgmt_x2i.rb_size);
+ XDNA_DBG(xdna, "x2i chann index 0x%x", aie->mgmt_chan_idx);
+ XDNA_DBG(xdna, "mailbox protocol major 0x%x", aie->mgmt_prot_major);
+ XDNA_DBG(xdna, "mailbox protocol minor 0x%x", aie->mgmt_prot_minor);
+}
+
+void aie_destroy_chann(struct aie_device *aie, struct mailbox_channel **chann)
+{
+ struct amdxdna_dev *xdna = aie->xdna;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ if (!*chann)
+ return;
+
+ xdna_mailbox_stop_channel(*chann);
+ xdna_mailbox_free_channel(*chann);
+ *chann = NULL;
+}
+
+int aie_send_mgmt_msg_wait(struct aie_device *aie, struct xdna_mailbox_msg *msg)
+{
+ struct amdxdna_dev *xdna = aie->xdna;
+ struct xdna_notify *hdl = msg->handle;
+ int ret;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ if (!aie->mgmt_chann)
+ return -ENODEV;
+
+ ret = xdna_send_msg_wait(xdna, aie->mgmt_chann, msg);
+ if (ret == -ETIME)
+ aie_destroy_chann(aie, &aie->mgmt_chann);
+
+ if (!ret && *hdl->status) {
+ XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
+ msg->opcode, *hdl->data);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int aie_check_protocol(struct aie_device *aie, u32 fw_major, u32 fw_minor)
+{
+ const struct amdxdna_fw_feature_tbl *feature;
+ bool found = false;
+
+ for (feature = aie->xdna->dev_info->fw_feature_tbl;
+ feature->major; feature++) {
+ if (feature->major != fw_major)
+ continue;
+ if (fw_minor < feature->min_minor)
+ continue;
+ if (feature->max_minor > 0 && fw_minor > feature->max_minor)
+ continue;
+
+ aie->feature_mask |= feature->features;
+
+ /* firmware version matches one of the driver support entry */
+ found = true;
+ }
+
+ return found ? 0 : -EOPNOTSUPP;
+}
+
+static void amdxdna_update_vbnv(struct amdxdna_dev *xdna,
+ const struct amdxdna_rev_vbnv *tbl,
+ u32 rev)
+{
+ int i;
+
+ for (i = 0; tbl[i].vbnv; i++) {
+ if (tbl[i].revision == rev) {
+ xdna->vbnv = tbl[i].vbnv;
+ break;
+ }
+ }
+}
+
+void amdxdna_vbnv_init(struct amdxdna_dev *xdna)
+{
+ const struct amdxdna_dev_info *info = xdna->dev_info;
+ u32 rev;
+
+ xdna->vbnv = info->default_vbnv;
+
+ if (!info->ops->get_dev_revision || !info->rev_vbnv_tbl)
+ return;
+
+ if (info->ops->get_dev_revision(xdna, &rev))
+ return;
+
+ amdxdna_update_vbnv(xdna, info->rev_vbnv_tbl, rev);
+}
diff --git a/drivers/accel/amdxdna/aie.h b/drivers/accel/amdxdna/aie.h
new file mode 100644
index 000000000000..7a68b114f235
--- /dev/null
+++ b/drivers/accel/amdxdna/aie.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+#ifndef _AIE_H_
+#define _AIE_H_
+
+#include "amdxdna_pci_drv.h"
+#include "amdxdna_mailbox.h"
+
+#define AIE_INTERVAL 20000 /* us */
+#define AIE_TIMEOUT 1000000 /* us */
+
+struct psp_device;
+struct smu_device;
+
+struct aie_device {
+ struct amdxdna_dev *xdna;
+ struct mailbox_channel *mgmt_chann;
+ struct xdna_mailbox_chann_res mgmt_x2i;
+ struct xdna_mailbox_chann_res mgmt_i2x;
+ u32 mgmt_chan_idx;
+ u32 mgmt_prot_major;
+ u32 mgmt_prot_minor;
+ unsigned long feature_mask;
+
+ struct psp_device *psp_hdl;
+ struct smu_device *smu_hdl;
+};
+
+#define DECLARE_AIE_MSG(name, op) \
+ DECLARE_XDNA_MSG_COMMON(name, op, -1)
+#define AIE_FEATURE_ON(aie, feature) test_bit(feature, &(aie)->feature_mask)
+
+#define PSP_REG_BAR(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].bar_idx)
+#define PSP_REG_OFF(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].offset)
+
+#define SMU_REG_BAR(ndev, idx) ((ndev)->priv->smu_regs_off[(idx)].bar_idx)
+#define SMU_REG_OFF(ndev, idx) ((ndev)->priv->smu_regs_off[(idx)].offset)
+
+#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
+ [reg_name] = {bar##_BAR_INDEX, (reg_addr) - bar##_BAR_BASE}
+
+enum smu_reg_idx {
+ SMU_CMD_REG = 0,
+ SMU_ARG_REG,
+ SMU_INTR_REG,
+ SMU_RESP_REG,
+ SMU_OUT_REG,
+ SMU_MAX_REGS /* Keep this at the end */
+};
+
+enum psp_reg_idx {
+ PSP_CMD_REG = 0,
+ PSP_ARG0_REG,
+ PSP_ARG1_REG,
+ PSP_ARG2_REG,
+ PSP_NUM_IN_REGS, /* number of input registers */
+ PSP_INTR_REG = PSP_NUM_IN_REGS,
+ PSP_STATUS_REG,
+ PSP_RESP_REG,
+ PSP_PWAITMODE_REG,
+ PSP_MAX_REGS /* Keep this at the end */
+};
+
+struct aie_bar_off_pair {
+ int bar_idx;
+ u32 offset;
+};
+
+struct smu_config {
+ void __iomem *smu_regs[SMU_MAX_REGS];
+};
+
+struct psp_config {
+ const void *fw_buf;
+ u32 fw_size;
+ const void *certfw_buf;
+ u32 certfw_size;
+ void __iomem *psp_regs[PSP_MAX_REGS];
+ u32 arg2_mask;
+ u32 notify_val;
+};
+
+/* Device revision to VBNV string mapping table entry */
+struct amdxdna_rev_vbnv {
+ u32 revision;
+ const char *vbnv;
+};
+
+/* aie.c */
+void aie_dump_mgmt_chann_debug(struct aie_device *aie);
+void aie_destroy_chann(struct aie_device *aie, struct mailbox_channel **chann);
+int aie_send_mgmt_msg_wait(struct aie_device *aie, struct xdna_mailbox_msg *msg);
+int aie_check_protocol(struct aie_device *aie, u32 fw_major, u32 fw_minor);
+void amdxdna_vbnv_init(struct amdxdna_dev *xdna);
+
+/* aie_psp.c */
+struct psp_device *aiem_psp_create(struct drm_device *ddev, struct psp_config *conf);
+int aie_psp_start(struct psp_device *psp);
+void aie_psp_stop(struct psp_device *psp);
+int aie_psp_waitmode_poll(struct psp_device *psp);
+
+/* aie_smu.c */
+struct smu_device *aiem_smu_create(struct drm_device *ddev, struct smu_config *conf);
+int aie_smu_init(struct smu_device *smu);
+void aie_smu_fini(struct smu_device *smu);
+int aie_smu_set_clocks(struct smu_device *smu, u32 *npuclk, u32 *hclk);
+int aie_smu_set_dpm(struct smu_device *smu, u32 dpm_level);
+
+#endif /* _AIE_H_ */
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
index 286379d9511d..139825ac8515 100644
--- a/drivers/accel/amdxdna/aie2_ctx.c
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -27,7 +27,9 @@ static bool force_cmdlist = true;
module_param(force_cmdlist, bool, 0600);
MODULE_PARM_DESC(force_cmdlist, "Force use command list (Default true)");
-#define HWCTX_MAX_TIMEOUT 60000 /* milliseconds */
+uint tdr_timeout_ms = 2000;
+module_param(tdr_timeout_ms, int, 0400);
+MODULE_PARM_DESC(tdr_timeout_ms, "TDR (Timeout Detection and Recovery) timeout in milliseconds (0 = disable)");
struct aie2_ctx_health {
struct amdxdna_ctx_health header;
@@ -39,11 +41,30 @@ struct aie2_ctx_health {
u32 fatal_error_app_module;
};
+static inline void aie2_tdr_signal(struct amdxdna_dev *xdna)
+{
+ WRITE_ONCE(xdna->dev_handle->tdr_status, AIE2_TDR_SIGNALED);
+}
+
+static bool aie2_tdr_detect(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+
+ if (READ_ONCE(ndev->tdr_status) == AIE2_TDR_WAIT) {
+ XDNA_ERR(xdna, "TDR timeout detected");
+ return true;
+ }
+
+ WRITE_ONCE(ndev->tdr_status, AIE2_TDR_WAIT);
+ return false;
+}
+
static void aie2_job_release(struct kref *ref)
{
struct amdxdna_sched_job *job;
job = container_of(ref, struct amdxdna_sched_job, refcnt);
+
amdxdna_sched_job_cleanup(job);
atomic64_inc(&job->hwctx->job_free_cnt);
wake_up(&job->hwctx->priv->job_free_wq);
@@ -175,8 +196,10 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
{
struct dma_fence *fence = job->fence;
- trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
+ trace_xdna_job(&job->base, job->hwctx->name, "signaling fence",
+ job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
+ aie2_tdr_signal(job->hwctx->client->xdna);
job->hwctx->priv->completed++;
dma_fence_signal(fence);
@@ -345,6 +368,9 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
struct dma_fence *fence;
int ret;
+ trace_xdna_job(sched_job, hwctx->name, "job run",
+ job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
+
if (!hwctx->priv->mbox_chann)
return NULL;
@@ -360,6 +386,7 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
ret = aie2_sync_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
break;
case ATTACH_DEBUG_BO:
+ case DETACH_DEBUG_BO:
ret = aie2_config_debug_bo(hwctx, job, aie2_sched_drvcmd_resp_handler);
break;
default:
@@ -384,8 +411,11 @@ out:
aie2_job_put(job);
mmput(job->mm);
fence = ERR_PTR(ret);
+ } else {
+ aie2_tdr_signal(hwctx->client->xdna);
}
- trace_xdna_job(sched_job, hwctx->name, "sent to device", job->seq);
+ trace_xdna_job(sched_job, hwctx->name, "sent to device",
+ job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
return fence;
}
@@ -395,7 +425,8 @@ static void aie2_sched_job_free(struct drm_sched_job *sched_job)
struct amdxdna_sched_job *job = drm_job_to_xdna_job(sched_job);
struct amdxdna_hwctx *hwctx = job->hwctx;
- trace_xdna_job(sched_job, hwctx->name, "job free", job->seq);
+ trace_xdna_job(sched_job, hwctx->name, "job free",
+ job->seq, job->drv_cmd ? job->drv_cmd->opcode : DEFAULT_IO);
if (!job->job_done)
up(&hwctx->priv->job_sem);
@@ -413,10 +444,12 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
int ret;
xdna = hwctx->client->xdna;
- trace_xdna_job(sched_job, hwctx->name, "job timedout", job->seq);
- job->job_timeout = true;
- mutex_lock(&xdna->dev_lock);
+ guard(mutex)(&xdna->dev_lock);
+
+ if (!aie2_tdr_detect(xdna))
+ return DRM_GPU_SCHED_STAT_NO_HANG;
+
report = kzalloc_obj(*report);
if (!report)
goto reset_hwctx;
@@ -428,10 +461,10 @@ aie2_sched_job_timedout(struct drm_sched_job *sched_job)
job->aie2_job_health = report;
reset_hwctx:
+ job->job_timeout = true;
aie2_hwctx_stop(xdna, hwctx, sched_job);
aie2_hwctx_restart(xdna, hwctx);
- mutex_unlock(&xdna->dev_lock);
return DRM_GPU_SCHED_STAT_RESET;
}
@@ -513,22 +546,24 @@ static int aie2_alloc_resource(struct amdxdna_hwctx *hwctx)
{
struct amdxdna_dev *xdna = hwctx->client->xdna;
struct alloc_requests *xrs_req;
+ u32 temporal_only_col = 0;
int ret;
- if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) {
- hwctx->num_unused_col = xdna->dev_handle->total_col - hwctx->num_col;
- hwctx->num_col = xdna->dev_handle->total_col;
- return aie2_create_context(xdna->dev_handle, hwctx);
- }
-
xrs_req = kzalloc_obj(*xrs_req);
if (!xrs_req)
return -ENOMEM;
- xrs_req->cdo.start_cols = hwctx->col_list;
- xrs_req->cdo.cols_len = hwctx->col_list_len;
- xrs_req->cdo.ncols = hwctx->num_col;
- xrs_req->cdo.qos_cap.opc = hwctx->max_opc;
+ if (AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_TEMPORAL_ONLY)) {
+ xrs_req->cdo.start_cols = &temporal_only_col;
+ xrs_req->cdo.cols_len = 1;
+ xrs_req->cdo.ncols = xdna->dev_handle->total_col;
+ } else {
+ xrs_req->cdo.start_cols = hwctx->col_list;
+ xrs_req->cdo.cols_len = hwctx->col_list_len;
+ xrs_req->cdo.ncols = hwctx->num_col;
+ }
+ /* Use platform opc */
+ xrs_req->cdo.qos_cap.opc = xdna->dev_handle->priv->col_opc * hwctx->num_col;
xrs_req->rqos.gops = hwctx->qos.gops;
xrs_req->rqos.fps = hwctx->qos.fps;
@@ -552,15 +587,9 @@ static void aie2_release_resource(struct amdxdna_hwctx *hwctx)
struct amdxdna_dev *xdna = hwctx->client->xdna;
int ret;
- if (AIE2_FEATURE_ON(xdna->dev_handle, AIE2_TEMPORAL_ONLY)) {
- ret = aie2_destroy_context(xdna->dev_handle, hwctx);
- if (ret && ret != -ENODEV)
- XDNA_ERR(xdna, "Destroy temporal only context failed, ret %d", ret);
- } else {
- ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
- if (ret)
- XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
- }
+ ret = xrs_release_resource(xdna->xrs_hdl, (uintptr_t)hwctx);
+ if (ret)
+ XDNA_ERR(xdna, "Release AIE resource failed, ret %d", ret);
}
static int aie2_ctx_syncobj_create(struct amdxdna_hwctx *hwctx)
@@ -605,9 +634,8 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
struct amdxdna_dev *xdna = client->xdna;
const struct drm_sched_init_args args = {
.ops = &sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = HWCTX_MAX_CMDS,
- .timeout = msecs_to_jiffies(HWCTX_MAX_TIMEOUT),
+ .timeout = msecs_to_jiffies(tdr_timeout_ms),
.name = "amdxdna_js",
.dev = xdna->ddev.dev,
};
diff --git a/drivers/accel/amdxdna/aie2_error.c b/drivers/accel/amdxdna/aie2_error.c
index 58abb59b6153..70007b4363cd 100644
--- a/drivers/accel/amdxdna/aie2_error.c
+++ b/drivers/accel/amdxdna/aie2_error.c
@@ -249,12 +249,12 @@ static u32 aie2_error_backtrack(struct amdxdna_dev_hdl *ndev, void *err_info, u3
enum aie_error_category cat;
cat = aie_get_error_category(err->row, err->event_id, err->mod_type);
- XDNA_ERR(ndev->xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d",
+ XDNA_ERR(ndev->aie.xdna, "Row: %d, Col: %d, module %d, event ID %d, category %d",
err->row, err->col, err->mod_type,
err->event_id, cat);
if (err->col >= 32) {
- XDNA_WARN(ndev->xdna, "Invalid column number");
+ XDNA_WARN(ndev->aie.xdna, "Invalid column number");
break;
}
@@ -294,7 +294,7 @@ static void aie2_error_worker(struct work_struct *err_work)
e = container_of(err_work, struct async_event, work);
- xdna = e->ndev->xdna;
+ xdna = e->ndev->aie.xdna;
if (e->resp.status == MAX_AIE2_STATUS_CODE)
return;
@@ -329,7 +329,7 @@ static void aie2_error_worker(struct work_struct *err_work)
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
struct async_events *events;
events = ndev->async_events;
@@ -344,7 +344,7 @@ void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev)
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
u32 total_col = ndev->total_col;
u32 total_size = ASYNC_BUF_SIZE * total_col;
struct async_events *events;
@@ -402,12 +402,15 @@ free_events:
int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, struct amdxdna_drm_get_array *args)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+ if (!args->num_element)
+ return -EINVAL;
+
args->num_element = 1;
- args->element_size = sizeof(ndev->last_async_err);
+ args->element_size = min(args->element_size, sizeof(ndev->last_async_err));
if (copy_to_user(u64_to_user_ptr(args->buffer),
&ndev->last_async_err, args->element_size))
return -EFAULT;
diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c
index a1c546c3e81c..6e98af7b74db 100644
--- a/drivers/accel/amdxdna/aie2_message.c
+++ b/drivers/accel/amdxdna/aie2_message.c
@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/xarray.h>
+#include "aie.h"
#include "aie2_msg_priv.h"
#include "aie2_pci.h"
#include "amdxdna_ctx.h"
@@ -24,38 +25,12 @@
#include "amdxdna_mailbox_helper.h"
#include "amdxdna_pci_drv.h"
-#define DECLARE_AIE2_MSG(name, op) \
- DECLARE_XDNA_MSG_COMMON(name, op, MAX_AIE2_STATUS_CODE)
-
#define EXEC_MSG_OPS(xdna) ((xdna)->dev_handle->exec_msg_ops)
-static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev,
- struct xdna_mailbox_msg *msg)
-{
- struct amdxdna_dev *xdna = ndev->xdna;
- struct xdna_notify *hdl = msg->handle;
- int ret;
-
- if (!ndev->mgmt_chann)
- return -ENODEV;
-
- ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg);
- if (ret == -ETIME)
- aie2_destroy_mgmt_chann(ndev);
-
- if (!ret && *hdl->status != AIE2_STATUS_SUCCESS) {
- XDNA_ERR(xdna, "command opcode 0x%x failed, status 0x%x",
- msg->opcode, *hdl->data);
- ret = -EINVAL;
- }
-
- return ret;
-}
-
void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size,
dma_addr_t *dma_addr)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
void *vaddr;
int order;
@@ -79,7 +54,7 @@ void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size,
void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size,
void *cpu_addr, dma_addr_t dma_addr)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
if (amdxdna_iova_on(xdna)) {
amdxdna_iommu_free(xdna, size, cpu_addr, dma_addr);
@@ -91,36 +66,36 @@ void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size,
int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev)
{
- DECLARE_AIE2_MSG(suspend, MSG_OP_SUSPEND);
+ DECLARE_AIE_MSG(suspend, MSG_OP_SUSPEND);
int ret;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
- XDNA_ERR(ndev->xdna, "Failed to suspend fw, ret %d", ret);
+ XDNA_ERR(ndev->aie.xdna, "Failed to suspend fw, ret %d", ret);
return ret;
}
- return aie2_psp_waitmode_poll(ndev->psp_hdl);
+ return aie_psp_waitmode_poll(ndev->aie.psp_hdl);
}
int aie2_resume_fw(struct amdxdna_dev_hdl *ndev)
{
- DECLARE_AIE2_MSG(suspend, MSG_OP_RESUME);
+ DECLARE_AIE_MSG(suspend, MSG_OP_RESUME);
- return aie2_send_mgmt_msg_wait(ndev, &msg);
+ return aie_send_mgmt_msg_wait(&ndev->aie, &msg);
}
int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value)
{
- DECLARE_AIE2_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG);
+ DECLARE_AIE_MSG(set_runtime_cfg, MSG_OP_SET_RUNTIME_CONFIG);
int ret;
req.type = type;
req.value = value;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
- XDNA_ERR(ndev->xdna, "Failed to set runtime config, ret %d", ret);
+ XDNA_ERR(ndev->aie.xdna, "Failed to set runtime config, ret %d", ret);
return ret;
}
@@ -129,13 +104,13 @@ int aie2_set_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 value)
int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value)
{
- DECLARE_AIE2_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG);
+ DECLARE_AIE_MSG(get_runtime_cfg, MSG_OP_GET_RUNTIME_CONFIG);
int ret;
req.type = type;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
- XDNA_ERR(ndev->xdna, "Failed to get runtime config, ret %d", ret);
+ XDNA_ERR(ndev->aie.xdna, "Failed to get runtime config, ret %d", ret);
return ret;
}
@@ -145,20 +120,20 @@ int aie2_get_runtime_cfg(struct amdxdna_dev_hdl *ndev, u32 type, u64 *value)
int aie2_assign_mgmt_pasid(struct amdxdna_dev_hdl *ndev, u16 pasid)
{
- DECLARE_AIE2_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID);
+ DECLARE_AIE_MSG(assign_mgmt_pasid, MSG_OP_ASSIGN_MGMT_PASID);
req.pasid = pasid;
- return aie2_send_mgmt_msg_wait(ndev, &msg);
+ return aie_send_mgmt_msg_wait(&ndev->aie, &msg);
}
int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *version)
{
- DECLARE_AIE2_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION);
- struct amdxdna_dev *xdna = ndev->xdna;
+ DECLARE_AIE_MSG(aie_version_info, MSG_OP_QUERY_AIE_VERSION);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -173,10 +148,10 @@ int aie2_query_aie_version(struct amdxdna_dev_hdl *ndev, struct aie_version *ver
int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *metadata)
{
- DECLARE_AIE2_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO);
+ DECLARE_AIE_MSG(aie_tile_info, MSG_OP_QUERY_AIE_TILE_INFO);
int ret;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -211,10 +186,10 @@ int aie2_query_aie_metadata(struct amdxdna_dev_hdl *ndev, struct aie_metadata *m
int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
struct amdxdna_fw_ver *fw_ver)
{
- DECLARE_AIE2_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION);
+ DECLARE_AIE_MSG(firmware_version, MSG_OP_GET_FIRMWARE_VERSION);
int ret;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -228,12 +203,12 @@ int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id)
{
- DECLARE_AIE2_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
- struct amdxdna_dev *xdna = ndev->xdna;
+ DECLARE_AIE_MSG(destroy_ctx, MSG_OP_DESTROY_CONTEXT);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
req.context_id = id;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret && ret != -ENODEV)
XDNA_WARN(xdna, "Destroy context failed, ret %d", ret);
else if (ret == -ENODEV)
@@ -245,7 +220,7 @@ static int aie2_destroy_context_req(struct amdxdna_dev_hdl *ndev, u32 id)
static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev,
struct amdxdna_hwctx *hwctx)
{
- if (!AIE2_FEATURE_ON(ndev, AIE2_PREEMPT))
+ if (!AIE_FEATURE_ON(&ndev->aie, AIE2_PREEMPT))
return PRIORITY_HIGH;
switch (hwctx->qos.priority) {
@@ -264,8 +239,8 @@ static u32 aie2_get_context_priority(struct amdxdna_dev_hdl *ndev,
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
{
- DECLARE_AIE2_MSG(create_ctx, MSG_OP_CREATE_CONTEXT);
- struct amdxdna_dev *xdna = ndev->xdna;
+ DECLARE_AIE_MSG(create_ctx, MSG_OP_CREATE_CONTEXT);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
struct xdna_mailbox_chann_res x2i;
struct xdna_mailbox_chann_res i2x;
struct cq_pair *cq_pair;
@@ -280,7 +255,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct
req.pasid = amdxdna_pasid_on(hwctx->client) ? hwctx->client->pasid : 0;
req.context_priority = aie2_get_context_priority(ndev, hwctx);
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -344,7 +319,7 @@ del_ctx_req:
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
if (!hwctx->priv->mbox_chann)
@@ -363,14 +338,14 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
{
- DECLARE_AIE2_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER);
- struct amdxdna_dev *xdna = ndev->xdna;
+ DECLARE_AIE_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
int ret;
req.context_id = context_id;
req.buf_addr = addr;
req.buf_size = size;
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret)
return ret;
@@ -392,14 +367,15 @@ static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg)
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
u32 size, u32 *cols_filled)
{
- DECLARE_AIE2_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS);
- struct amdxdna_dev *xdna = ndev->xdna;
- u32 buf_sz = size, aie_bitmap = 0;
+ DECLARE_AIE_MSG(aie_column_info, MSG_OP_QUERY_COL_STATUS);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ u32 buf_sz, aie_bitmap = 0;
struct amdxdna_client *client;
dma_addr_t dma_addr;
u8 *buff_addr;
int ret;
+ buf_sz = ndev->metadata.cols * ndev->metadata.size;
buff_addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr);
if (IS_ERR(buff_addr))
return PTR_ERR(buff_addr);
@@ -415,7 +391,7 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
req.aie_bitmap = aie_bitmap;
drm_clflush_virt_range(buff_addr, size); /* device can access */
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "Error during NPU query, status %d", ret);
goto fail;
@@ -423,13 +399,14 @@ int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf,
XDNA_DBG(xdna, "Query NPU status completed");
- if (size < resp.size) {
+ if (buf_sz < resp.size) {
ret = -EINVAL;
- XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
+ XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size);
goto fail;
}
- if (copy_to_user(buf, buff_addr, resp.size)) {
+ size = min(size, resp.size);
+ if (copy_to_user(buf, buff_addr, size)) {
ret = -EFAULT;
XDNA_ERR(xdna, "Failed to copy NPU status to user space");
goto fail;
@@ -446,16 +423,17 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
char __user *buf, u32 size,
struct amdxdna_drm_query_telemetry_header *header)
{
- DECLARE_AIE2_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
- struct amdxdna_dev *xdna = ndev->xdna;
+ DECLARE_AIE_MSG(get_telemetry, MSG_OP_GET_TELEMETRY);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
dma_addr_t dma_addr;
- u32 buf_sz = size;
+ u32 buf_sz;
u8 *addr;
int ret;
if (header->type >= MAX_TELEMETRY_TYPE)
return -EINVAL;
+ buf_sz = min(size, SZ_4M);
addr = aie2_alloc_msg_buffer(ndev, &buf_sz, &dma_addr);
if (IS_ERR(addr))
return PTR_ERR(addr);
@@ -465,19 +443,20 @@ int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
req.type = header->type;
drm_clflush_virt_range(addr, size); /* device can access */
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "Query telemetry failed, status %d", ret);
goto free_buf;
}
- if (size < resp.size) {
+ if (buf_sz < resp.size) {
ret = -EINVAL;
- XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", size, resp.size);
+ XDNA_ERR(xdna, "Bad buffer size. Available: %u. Needs: %u", buf_sz, resp.size);
goto free_buf;
}
- if (copy_to_user(buf, addr, resp.size)) {
+ size = min(size, resp.size);
+ if (copy_to_user(buf, addr, size)) {
ret = -EFAULT;
XDNA_ERR(xdna, "Failed to copy telemetry to user space");
goto free_buf;
@@ -506,8 +485,8 @@ int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr,
req.buf_addr = addr;
req.buf_size = size;
- XDNA_DBG(ndev->xdna, "Register addr 0x%llx size 0x%x", addr, size);
- return xdna_mailbox_send_msg(ndev->mgmt_chann, &msg, TX_TIMEOUT);
+ XDNA_DBG(ndev->aie.xdna, "Register addr 0x%llx size 0x%x", addr, size);
+ return xdna_mailbox_send_msg(ndev->aie.mgmt_chann, &msg, TX_TIMEOUT);
}
int aie2_config_cu(struct amdxdna_hwctx *hwctx,
@@ -866,7 +845,6 @@ static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo,
int ret;
u32 op;
-
op = amdxdna_cmd_get_op(cmd_abo);
switch (op) {
case ERT_START_CU:
@@ -915,12 +893,12 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
ret = EXEC_MSG_OPS(xdna)->fill_dpu_slot(cmd_abo, slot, size);
break;
case ERT_START_NPU_PREEMPT:
- if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
+ if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT))
return -EOPNOTSUPP;
ret = EXEC_MSG_OPS(xdna)->fill_preempt_slot(cmd_abo, slot, size);
break;
case ERT_START_NPU_PREEMPT_ELF:
- if (!AIE2_FEATURE_ON(xdna->dev_handle, AIE2_PREEMPT))
+ if (!AIE_FEATURE_ON(&xdna->dev_handle->aie, AIE2_PREEMPT))
return -EOPNOTSUPP;
ret = EXEC_MSG_OPS(xdna)->fill_elf_slot(cmd_abo, slot, size);
break;
@@ -935,26 +913,12 @@ aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo,
void aie2_msg_init(struct amdxdna_dev_hdl *ndev)
{
- if (AIE2_FEATURE_ON(ndev, AIE2_NPU_COMMAND))
+ if (AIE_FEATURE_ON(&ndev->aie, AIE2_NPU_COMMAND))
ndev->exec_msg_ops = &npu_exec_message_ops;
else
ndev->exec_msg_ops = &legacy_exec_message_ops;
}
-void aie2_destroy_mgmt_chann(struct amdxdna_dev_hdl *ndev)
-{
- struct amdxdna_dev *xdna = ndev->xdna;
-
- drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
-
- if (!ndev->mgmt_chann)
- return;
-
- xdna_mailbox_stop_channel(ndev->mgmt_chann);
- xdna_mailbox_free_channel(ndev->mgmt_chann);
- ndev->mgmt_chann = NULL;
-}
-
static inline struct amdxdna_gem_obj *
aie2_cmdlist_get_cmd_buf(struct amdxdna_sched_job *job)
{
@@ -1199,14 +1163,14 @@ int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *
int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
struct app_health_report *report)
{
- DECLARE_AIE2_MSG(get_app_health, MSG_OP_GET_APP_HEALTH);
- struct amdxdna_dev *xdna = ndev->xdna;
+ DECLARE_AIE_MSG(get_app_health, MSG_OP_GET_APP_HEALTH);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
struct app_health_report *buf;
dma_addr_t dma_addr;
u32 buf_size;
int ret;
- if (!AIE2_FEATURE_ON(ndev, AIE2_APP_HEALTH)) {
+ if (!AIE_FEATURE_ON(&ndev->aie, AIE2_APP_HEALTH)) {
XDNA_DBG(xdna, "App health feature not supported");
return -EOPNOTSUPP;
}
@@ -1223,7 +1187,7 @@ int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
req.buf_size = buf_size;
drm_clflush_virt_range(buf, sizeof(*report));
- ret = aie2_send_mgmt_msg_wait(ndev, &msg);
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
if (ret) {
XDNA_ERR(xdna, "Get app health failed, ret %d status 0x%x", ret, resp.status);
goto free_buf;
@@ -1236,3 +1200,72 @@ free_buf:
aie2_free_msg_buffer(ndev, buf_size, buf, dma_addr);
return ret;
}
+
+static int
+aie2_runtime_update_ctx_prop(struct amdxdna_dev_hdl *ndev,
+ struct amdxdna_hwctx *ctx, u32 type, u32 value)
+{
+ DECLARE_AIE_MSG(update_property, MSG_OP_UPDATE_PROPERTY);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ int ret;
+
+ if (!AIE_FEATURE_ON(&ndev->aie, AIE2_UPDATE_PROPERTY))
+ return -EOPNOTSUPP;
+
+ if (ctx)
+ req.context_id = ctx->fw_ctx_id;
+ else
+ req.context_id = AIE2_UPDATE_PROPERTY_ALL_CTX;
+
+ req.time_quota_us = value;
+ req.type = type;
+
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret) {
+ XDNA_ERR(xdna, "%s update property failed, type %d ret %d",
+ ctx ? ctx->name : "ctx.all", type, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int aie2_update_prop_time_quota(struct amdxdna_dev_hdl *ndev, u32 us)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ int ret;
+
+ ret = aie2_runtime_update_ctx_prop(ndev, NULL, UPDATE_PROPERTY_TIME_QUOTA, us);
+ if (ret == -EOPNOTSUPP) {
+ XDNA_DBG(xdna, "update time quota not support, skipped");
+ ret = 0;
+ } else if (!ret) {
+ XDNA_DBG(xdna, "Ctx exec time quantum updated to %u us", us);
+ }
+ return ret;
+}
+
+int aie2_get_dev_revision(struct amdxdna_dev_hdl *ndev, enum aie2_dev_revision *rev)
+{
+ DECLARE_AIE_MSG(get_dev_revision, MSG_OP_GET_DEV_REVISION);
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ int ret;
+
+ if (!AIE_FEATURE_ON(&ndev->aie, AIE2_GET_DEV_REVISION))
+ return -EOPNOTSUPP;
+
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret)
+ return ret;
+
+ *rev = resp.rev;
+
+ if (*rev < AIE2_DEV_REVISION_STXA || *rev >= AIE2_DEV_REVISION_UNKN) {
+ XDNA_ERR(xdna, "Unknown device revision: %d (raw fuse: 0x%x)",
+ *rev, resp.raw_fuse_data);
+ return -EINVAL;
+ }
+
+ XDNA_DBG(xdna, "Device revision: %d (raw fuse: 0x%x)", *rev, resp.raw_fuse_data);
+ return 0;
+}
diff --git a/drivers/accel/amdxdna/aie2_msg_priv.h b/drivers/accel/amdxdna/aie2_msg_priv.h
index f18e89a39e35..a41c9797e265 100644
--- a/drivers/accel/amdxdna/aie2_msg_priv.h
+++ b/drivers/accel/amdxdna/aie2_msg_priv.h
@@ -31,7 +31,9 @@ enum aie2_msg_opcode {
MSG_OP_SET_RUNTIME_CONFIG = 0x10A,
MSG_OP_GET_RUNTIME_CONFIG = 0x10B,
MSG_OP_REGISTER_ASYNC_EVENT_MSG = 0x10C,
+ MSG_OP_UPDATE_PROPERTY = 0x113,
MSG_OP_GET_APP_HEALTH = 0x114,
+ MSG_OP_GET_DEV_REVISION = 0x117,
MSG_OP_MAX_DRV_OPCODE,
MSG_OP_GET_PROTOCOL_VERSION = 0x301,
MSG_OP_MAX_OPCODE
@@ -460,7 +462,7 @@ struct fatal_error_info {
__u32 exception_pc; /* Program Counter at the time of the exception */
__u32 app_module; /* Error module name */
__u32 task_index; /* Index of the task in which the error occurred */
- __u32 reserved[128];
+ __u32 reserved[127];
};
struct app_health_report {
@@ -503,4 +505,41 @@ struct get_app_health_resp {
__u32 required_buffer_size;
__u32 reserved[7];
} __packed;
+
+struct update_property_req {
+#define UPDATE_PROPERTY_TIME_QUOTA 0
+ __u32 type;
+#define AIE2_UPDATE_PROPERTY_ALL_CTX 0xFF
+ __u8 context_id;
+ __u8 reserved[7];
+ __u32 time_quota_us;
+ __u32 reserved1;
+} __packed;
+
+struct update_property_resp {
+ enum aie2_msg_status status;
+} __packed;
+
+enum aie2_dev_revision {
+ AIE2_DEV_REVISION_STXA = 1,
+ AIE2_DEV_REVISION_STXB,
+ AIE2_DEV_REVISION_KRK1,
+ AIE2_DEV_REVISION_KRK2,
+ AIE2_DEV_REVISION_HALO,
+ AIE2_DEV_REVISION_GPT1,
+ AIE2_DEV_REVISION_GPT2,
+ AIE2_DEV_REVISION_GPT3,
+ AIE2_DEV_REVISION_UNKN,
+};
+
+struct get_dev_revision_req {
+ __u32 place_holder;
+} __packed;
+
+struct get_dev_revision_resp {
+ enum aie2_msg_status status;
+ enum aie2_dev_revision rev;
+ __u32 raw_fuse_data;
+} __packed;
+
#endif /* _AIE2_MSG_PRIV_H_ */
diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c
index f1ac4e00bd9f..f0ddb843eb21 100644
--- a/drivers/accel/amdxdna/aie2_pci.c
+++ b/drivers/accel/amdxdna/aie2_pci.c
@@ -33,6 +33,8 @@ static int aie2_max_col = XRS_MAX_COL;
module_param(aie2_max_col, uint, 0600);
MODULE_PARM_DESC(aie2_max_col, "Maximum column could be used");
+#define DEFAULT_TIME_QUANTUM 30000 /* microseconds */
+
static char *npu_fw[] = {
"npu_7.sbin",
"npu.sbin"
@@ -60,45 +62,6 @@ struct mgmt_mbox_chann_info {
__u32 rsvd[4];
};
-static int aie2_check_protocol(struct amdxdna_dev_hdl *ndev, u32 fw_major, u32 fw_minor)
-{
- const struct aie2_fw_feature_tbl *feature;
- bool found = false;
-
- for (feature = ndev->priv->fw_feature_tbl; feature->major; feature++) {
- if (feature->major != fw_major)
- continue;
- if (fw_minor < feature->min_minor)
- continue;
- if (feature->max_minor > 0 && fw_minor > feature->max_minor)
- continue;
-
- ndev->feature_mask |= feature->features;
-
- /* firmware version matches one of the driver support entry */
- found = true;
- }
-
- return found ? 0 : -EOPNOTSUPP;
-}
-
-static void aie2_dump_chann_info_debug(struct amdxdna_dev_hdl *ndev)
-{
- struct amdxdna_dev *xdna = ndev->xdna;
-
- XDNA_DBG(xdna, "i2x tail 0x%x", ndev->mgmt_i2x.mb_tail_ptr_reg);
- XDNA_DBG(xdna, "i2x head 0x%x", ndev->mgmt_i2x.mb_head_ptr_reg);
- XDNA_DBG(xdna, "i2x ringbuf 0x%x", ndev->mgmt_i2x.rb_start_addr);
- XDNA_DBG(xdna, "i2x rsize 0x%x", ndev->mgmt_i2x.rb_size);
- XDNA_DBG(xdna, "x2i tail 0x%x", ndev->mgmt_x2i.mb_tail_ptr_reg);
- XDNA_DBG(xdna, "x2i head 0x%x", ndev->mgmt_x2i.mb_head_ptr_reg);
- XDNA_DBG(xdna, "x2i ringbuf 0x%x", ndev->mgmt_x2i.rb_start_addr);
- XDNA_DBG(xdna, "x2i rsize 0x%x", ndev->mgmt_x2i.rb_size);
- XDNA_DBG(xdna, "x2i chann index 0x%x", ndev->mgmt_chan_idx);
- XDNA_DBG(xdna, "mailbox protocol major 0x%x", ndev->mgmt_prot_major);
- XDNA_DBG(xdna, "mailbox protocol minor 0x%x", ndev->mgmt_prot_minor);
-}
-
static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
{
struct mgmt_mbox_chann_info info_regs;
@@ -118,7 +81,7 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
* is alive.
*/
ret = readx_poll_timeout(readl, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF),
- addr, addr, AIE2_INTERVAL, AIE2_TIMEOUT);
+ addr, addr, AIE_INTERVAL, AIE_TIMEOUT);
if (ret || !addr)
return -ETIME;
@@ -128,13 +91,13 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
reg[i] = readl(ndev->sram_base + off + i * sizeof(u32));
if (info_regs.magic != MGMT_MBOX_MAGIC) {
- XDNA_ERR(ndev->xdna, "Invalid mbox magic 0x%x", info_regs.magic);
+ XDNA_ERR(ndev->aie.xdna, "Invalid mbox magic 0x%x", info_regs.magic);
ret = -EINVAL;
goto done;
}
- i2x = &ndev->mgmt_i2x;
- x2i = &ndev->mgmt_x2i;
+ i2x = &ndev->aie.mgmt_i2x;
+ x2i = &ndev->aie.mgmt_x2i;
i2x->mb_head_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_head);
i2x->mb_tail_ptr_reg = AIE2_MBOX_OFF(ndev, info_regs.i2x_tail);
@@ -146,14 +109,15 @@ static int aie2_get_mgmt_chann_info(struct amdxdna_dev_hdl *ndev)
x2i->rb_start_addr = AIE2_SRAM_OFF(ndev, info_regs.x2i_buf);
x2i->rb_size = info_regs.x2i_buf_sz;
- ndev->mgmt_chan_idx = info_regs.msi_id;
- ndev->mgmt_prot_major = info_regs.prot_major;
- ndev->mgmt_prot_minor = info_regs.prot_minor;
+ ndev->aie.mgmt_chan_idx = info_regs.msi_id;
+ ndev->aie.mgmt_prot_major = info_regs.prot_major;
+ ndev->aie.mgmt_prot_minor = info_regs.prot_minor;
- ret = aie2_check_protocol(ndev, ndev->mgmt_prot_major, ndev->mgmt_prot_minor);
+ ret = aie_check_protocol(&ndev->aie, ndev->aie.mgmt_prot_major,
+ ndev->aie.mgmt_prot_minor);
done:
- aie2_dump_chann_info_debug(ndev);
+ aie_dump_mgmt_chann_debug(&ndev->aie);
/* Must clear address at FW_ALIVE_OFF */
writel(0, SRAM_GET_ADDR(ndev, FW_ALIVE_OFF));
@@ -173,13 +137,14 @@ int aie2_runtime_cfg(struct amdxdna_dev_hdl *ndev,
continue;
if (cfg->feature_mask &&
- bitmap_subset(&cfg->feature_mask, &ndev->feature_mask, AIE2_FEATURE_MAX))
+ bitmap_subset(&cfg->feature_mask, &ndev->aie.feature_mask,
+ AIE2_FEATURE_MAX))
continue;
value = val ? *val : cfg->value;
ret = aie2_set_runtime_cfg(ndev, cfg->type, value);
if (ret) {
- XDNA_ERR(ndev->xdna, "Set type %d value %d failed",
+ XDNA_ERR(ndev->aie.xdna, "Set type %d value %d failed",
cfg->type, value);
return ret;
}
@@ -194,13 +159,13 @@ static int aie2_xdna_reset(struct amdxdna_dev_hdl *ndev)
ret = aie2_suspend_fw(ndev);
if (ret) {
- XDNA_ERR(ndev->xdna, "Suspend firmware failed");
+ XDNA_ERR(ndev->aie.xdna, "Suspend firmware failed");
return ret;
}
ret = aie2_resume_fw(ndev);
if (ret) {
- XDNA_ERR(ndev->xdna, "Resume firmware failed");
+ XDNA_ERR(ndev->aie.xdna, "Resume firmware failed");
return ret;
}
@@ -213,19 +178,25 @@ static int aie2_mgmt_fw_init(struct amdxdna_dev_hdl *ndev)
ret = aie2_runtime_cfg(ndev, AIE2_RT_CFG_INIT, NULL);
if (ret) {
- XDNA_ERR(ndev->xdna, "Runtime config failed");
+ XDNA_ERR(ndev->aie.xdna, "Runtime config failed");
return ret;
}
ret = aie2_assign_mgmt_pasid(ndev, 0);
if (ret) {
- XDNA_ERR(ndev->xdna, "Can not assign PASID");
+ XDNA_ERR(ndev->aie.xdna, "Can not assign PASID");
+ return ret;
+ }
+
+ ret = aie2_update_prop_time_quota(ndev, DEFAULT_TIME_QUANTUM);
+ if (ret) {
+ XDNA_ERR(ndev->aie.xdna, "Failed to update execution time quantum");
return ret;
}
ret = aie2_xdna_reset(ndev);
if (ret) {
- XDNA_ERR(ndev->xdna, "Reset firmware failed");
+ XDNA_ERR(ndev->aie.xdna, "Reset firmware failed");
return ret;
}
@@ -236,21 +207,21 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
{
int ret;
- ret = aie2_query_firmware_version(ndev, &ndev->xdna->fw_ver);
+ ret = aie2_query_firmware_version(ndev, &ndev->aie.xdna->fw_ver);
if (ret) {
- XDNA_ERR(ndev->xdna, "query firmware version failed");
+ XDNA_ERR(ndev->aie.xdna, "query firmware version failed");
return ret;
}
ret = aie2_query_aie_version(ndev, &ndev->version);
if (ret) {
- XDNA_ERR(ndev->xdna, "Query AIE version failed");
+ XDNA_ERR(ndev->aie.xdna, "Query AIE version failed");
return ret;
}
ret = aie2_query_aie_metadata(ndev, &ndev->metadata);
if (ret) {
- XDNA_ERR(ndev->xdna, "Query AIE metadata failed");
+ XDNA_ERR(ndev->aie.xdna, "Query AIE metadata failed");
return ret;
}
@@ -262,8 +233,8 @@ static int aie2_mgmt_fw_query(struct amdxdna_dev_hdl *ndev)
static void aie2_mgmt_fw_fini(struct amdxdna_dev_hdl *ndev)
{
if (aie2_suspend_fw(ndev))
- XDNA_ERR(ndev->xdna, "Suspend_fw failed");
- XDNA_DBG(ndev->xdna, "Firmware suspended");
+ XDNA_ERR(ndev->aie.xdna, "Suspend_fw failed");
+ XDNA_DBG(ndev->aie.xdna, "Firmware suspended");
}
static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action)
@@ -275,6 +246,7 @@ static int aie2_xrs_load(void *cb_arg, struct xrs_action_load *action)
xdna = hwctx->client->xdna;
hwctx->start_col = action->part.start_col;
+ hwctx->num_unused_col = action->part.ncols - hwctx->num_col;
hwctx->num_col = action->part.ncols;
ret = aie2_create_context(xdna->dev_handle, hwctx);
if (ret)
@@ -319,6 +291,12 @@ static struct xrs_action_ops aie2_xrs_actions = {
.set_dft_dpm_level = aie2_xrs_set_dft_dpm_level,
};
+static void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
+{
+ ndev->priv->hw_ops->set_dpm(ndev, 0);
+ aie_smu_fini(ndev->aie.smu_hdl);
+}
+
static void aie2_hw_stop(struct amdxdna_dev *xdna)
{
struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
@@ -331,10 +309,10 @@ static void aie2_hw_stop(struct amdxdna_dev *xdna)
aie2_runtime_cfg(ndev, AIE2_RT_CFG_CLK_GATING, NULL);
aie2_mgmt_fw_fini(ndev);
- aie2_destroy_mgmt_chann(ndev);
+ aie_destroy_chann(&ndev->aie, &ndev->aie.mgmt_chann);
drmm_kfree(&xdna->ddev, ndev->mbox);
ndev->mbox = NULL;
- aie2_psp_stop(ndev->psp_hdl);
+ aie_psp_stop(ndev->aie.psp_hdl);
aie2_smu_fini(ndev);
aie2_error_async_events_free(ndev);
pci_disable_device(pdev);
@@ -374,20 +352,20 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
goto disable_dev;
}
- ndev->mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox);
- if (!ndev->mgmt_chann) {
+ ndev->aie.mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox);
+ if (!ndev->aie.mgmt_chann) {
XDNA_ERR(xdna, "failed to alloc channel");
ret = -ENODEV;
goto disable_dev;
}
- ret = aie2_smu_init(ndev);
+ ret = aie_smu_init(ndev->aie.smu_hdl);
if (ret) {
XDNA_ERR(xdna, "failed to init smu, ret %d", ret);
goto free_channel;
}
- ret = aie2_psp_start(ndev->psp_hdl);
+ ret = aie_psp_start(ndev->aie.psp_hdl);
if (ret) {
XDNA_ERR(xdna, "failed to start psp, ret %d", ret);
goto fini_smu;
@@ -399,17 +377,17 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
goto stop_psp;
}
- mgmt_mb_irq = pci_irq_vector(pdev, ndev->mgmt_chan_idx);
+ mgmt_mb_irq = pci_irq_vector(pdev, ndev->aie.mgmt_chan_idx);
if (mgmt_mb_irq < 0) {
ret = mgmt_mb_irq;
XDNA_ERR(xdna, "failed to alloc irq vector, ret %d", ret);
goto stop_psp;
}
- xdna_mailbox_intr_reg = ndev->mgmt_i2x.mb_head_ptr_reg + 4;
- ret = xdna_mailbox_start_channel(ndev->mgmt_chann,
- &ndev->mgmt_x2i,
- &ndev->mgmt_i2x,
+ xdna_mailbox_intr_reg = ndev->aie.mgmt_i2x.mb_head_ptr_reg + 4;
+ ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann,
+ &ndev->aie.mgmt_x2i,
+ &ndev->aie.mgmt_i2x,
xdna_mailbox_intr_reg,
mgmt_mb_irq);
if (ret) {
@@ -448,14 +426,14 @@ static int aie2_hw_start(struct amdxdna_dev *xdna)
stop_fw:
aie2_suspend_fw(ndev);
- xdna_mailbox_stop_channel(ndev->mgmt_chann);
+ xdna_mailbox_stop_channel(ndev->aie.mgmt_chann);
stop_psp:
- aie2_psp_stop(ndev->psp_hdl);
+ aie_psp_stop(ndev->aie.psp_hdl);
fini_smu:
aie2_smu_fini(ndev);
free_channel:
- xdna_mailbox_free_channel(ndev->mgmt_chann);
- ndev->mgmt_chann = NULL;
+ xdna_mailbox_free_channel(ndev->aie.mgmt_chann);
+ ndev->aie.mgmt_chann = NULL;
disable_dev:
pci_disable_device(pdev);
@@ -500,7 +478,8 @@ static int aie2_init(struct amdxdna_dev *xdna)
void __iomem *tbl[PCI_NUM_RESOURCES] = {0};
struct init_config xrs_cfg = { 0 };
struct amdxdna_dev_hdl *ndev;
- struct psp_config psp_conf;
+ struct psp_config psp_conf = { 0 };
+ struct smu_config smu_conf;
const struct firmware *fw;
unsigned long bars = 0;
char *fw_full_path;
@@ -516,7 +495,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
return -ENOMEM;
ndev->priv = xdna->dev_info->dev_priv;
- ndev->xdna = xdna;
+ ndev->aie.xdna = xdna;
for (i = 0; i < ARRAY_SIZE(npu_fw); i++) {
fw_full_path = kasprintf(GFP_KERNEL, "%s%s", ndev->priv->fw_path, npu_fw[i]);
@@ -545,9 +524,10 @@ static int aie2_init(struct amdxdna_dev *xdna)
for (i = 0; i < PSP_MAX_REGS; i++)
set_bit(PSP_REG_BAR(ndev, i), &bars);
+ for (i = 0; i < SMU_MAX_REGS; i++)
+ set_bit(SMU_REG_BAR(ndev, i), &bars);
set_bit(xdna->dev_info->sram_bar, &bars);
- set_bit(xdna->dev_info->smu_bar, &bars);
set_bit(xdna->dev_info->mbox_bar, &bars);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
@@ -562,7 +542,6 @@ static int aie2_init(struct amdxdna_dev *xdna)
}
ndev->sram_base = tbl[xdna->dev_info->sram_bar];
- ndev->smu_base = tbl[xdna->dev_info->smu_bar];
ndev->mbox_base = tbl[xdna->dev_info->mbox_bar];
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
@@ -586,14 +565,25 @@ static int aie2_init(struct amdxdna_dev *xdna)
psp_conf.fw_size = fw->size;
psp_conf.fw_buf = fw->data;
+ psp_conf.arg2_mask = GENMASK(23, 0);
+ psp_conf.notify_val = 1;
for (i = 0; i < PSP_MAX_REGS; i++)
psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(ndev, i)] + PSP_REG_OFF(ndev, i);
- ndev->psp_hdl = aie2m_psp_create(&xdna->ddev, &psp_conf);
- if (!ndev->psp_hdl) {
+ ndev->aie.psp_hdl = aiem_psp_create(&xdna->ddev, &psp_conf);
+ if (!ndev->aie.psp_hdl) {
XDNA_ERR(xdna, "failed to create psp");
ret = -ENOMEM;
goto release_fw;
}
+
+ for (i = 0; i < SMU_MAX_REGS; i++)
+ smu_conf.smu_regs[i] = tbl[SMU_REG_BAR(ndev, i)] + SMU_REG_OFF(ndev, i);
+ ndev->aie.smu_hdl = aiem_smu_create(&xdna->ddev, &smu_conf);
+ if (!ndev->aie.smu_hdl) {
+ XDNA_ERR(xdna, "failed to create smu");
+ ret = -ENOMEM;
+ goto release_fw;
+ }
xdna->dev_handle = ndev;
ret = aie2_hw_start(xdna);
@@ -605,7 +595,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
xrs_cfg.clk_list.num_levels = ndev->max_dpm_level + 1;
for (i = 0; i < xrs_cfg.clk_list.num_levels; i++)
xrs_cfg.clk_list.cu_clk_list[i] = ndev->priv->dpm_clk_tbl[i].hclk;
- xrs_cfg.sys_eff_factor = 1;
+ xrs_cfg.sys_eff_factor = 2;
xrs_cfg.ddev = &xdna->ddev;
xrs_cfg.actions = &aie2_xrs_actions;
xrs_cfg.total_col = ndev->total_col;
@@ -619,6 +609,7 @@ static int aie2_init(struct amdxdna_dev *xdna)
release_firmware(fw);
aie2_msg_init(ndev);
+ amdxdna_vbnv_init(xdna);
amdxdna_pm_init(xdna);
return 0;
@@ -639,23 +630,19 @@ static void aie2_fini(struct amdxdna_dev *xdna)
static int aie2_get_aie_status(struct amdxdna_client *client,
struct amdxdna_drm_get_info *args)
{
- struct amdxdna_drm_query_aie_status status;
+ struct amdxdna_drm_query_aie_status status = {};
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
+ u32 buf_sz;
int ret;
ndev = xdna->dev_handle;
- if (copy_from_user(&status, u64_to_user_ptr(args->buffer), sizeof(status))) {
+ buf_sz = min(args->buffer_size, sizeof(status));
+ if (copy_from_user(&status, u64_to_user_ptr(args->buffer), buf_sz)) {
XDNA_ERR(xdna, "Failed to copy AIE request into kernel");
return -EFAULT;
}
- if (ndev->metadata.cols * ndev->metadata.size < status.buffer_size) {
- XDNA_ERR(xdna, "Invalid buffer size. Given Size: %u. Need Size: %u.",
- status.buffer_size, ndev->metadata.cols * ndev->metadata.size);
- return -EINVAL;
- }
-
ret = aie2_query_status(ndev, u64_to_user_ptr(status.buffer),
status.buffer_size, &status.cols_filled);
if (ret) {
@@ -663,7 +650,7 @@ static int aie2_get_aie_status(struct amdxdna_client *client,
return ret;
}
- if (copy_to_user(u64_to_user_ptr(args->buffer), &status, sizeof(status))) {
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &status, buf_sz)) {
XDNA_ERR(xdna, "Failed to copy AIE request info to user space");
return -EFAULT;
}
@@ -678,6 +665,7 @@ static int aie2_get_aie_metadata(struct amdxdna_client *client,
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
int ret = 0;
+ u32 buf_sz;
ndev = xdna->dev_handle;
meta = kzalloc_obj(*meta);
@@ -709,7 +697,8 @@ static int aie2_get_aie_metadata(struct amdxdna_client *client,
meta->shim.lock_count = ndev->metadata.shim.lock_count;
meta->shim.event_reg_count = ndev->metadata.shim.event_reg_count;
- if (copy_to_user(u64_to_user_ptr(args->buffer), meta, sizeof(*meta)))
+ buf_sz = min(args->buffer_size, sizeof(*meta));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), meta, buf_sz))
ret = -EFAULT;
kfree(meta);
@@ -722,12 +711,14 @@ static int aie2_get_aie_version(struct amdxdna_client *client,
struct amdxdna_drm_query_aie_version version;
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
+ u32 buf_sz;
ndev = xdna->dev_handle;
version.major = ndev->version.major;
version.minor = ndev->version.minor;
- if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version)))
+ buf_sz = min(args->buffer_size, sizeof(version));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz))
return -EFAULT;
return 0;
@@ -738,13 +729,15 @@ static int aie2_get_firmware_version(struct amdxdna_client *client,
{
struct amdxdna_drm_query_firmware_version version;
struct amdxdna_dev *xdna = client->xdna;
+ u32 buf_sz;
version.major = xdna->fw_ver.major;
version.minor = xdna->fw_ver.minor;
version.patch = xdna->fw_ver.sub;
version.build = xdna->fw_ver.build;
- if (copy_to_user(u64_to_user_ptr(args->buffer), &version, sizeof(version)))
+ buf_sz = min(args->buffer_size, sizeof(version));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &version, buf_sz))
return -EFAULT;
return 0;
@@ -756,11 +749,13 @@ static int aie2_get_power_mode(struct amdxdna_client *client,
struct amdxdna_drm_get_power_mode mode = {};
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
+ u32 buf_sz;
ndev = xdna->dev_handle;
mode.power_mode = ndev->pw_mode;
- if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, sizeof(mode)))
+ buf_sz = min(args->buffer_size, sizeof(mode));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &mode, buf_sz))
return -EFAULT;
return 0;
@@ -773,19 +768,22 @@ static int aie2_get_clock_metadata(struct amdxdna_client *client,
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
int ret = 0;
+ u32 buf_sz;
ndev = xdna->dev_handle;
clock = kzalloc_obj(*clock);
if (!clock)
return -ENOMEM;
+ aie2_update_counters(ndev);
snprintf(clock->mp_npu_clock.name, sizeof(clock->mp_npu_clock.name),
"MP-NPU Clock");
clock->mp_npu_clock.freq_mhz = ndev->npuclk_freq;
snprintf(clock->h_clock.name, sizeof(clock->h_clock.name), "H Clock");
clock->h_clock.freq_mhz = ndev->hclk_freq;
- if (copy_to_user(u64_to_user_ptr(args->buffer), clock, sizeof(*clock)))
+ buf_sz = min(args->buffer_size, sizeof(*clock));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), clock, buf_sz))
ret = -EFAULT;
kfree(clock);
@@ -811,12 +809,14 @@ static int aie2_get_sensors(struct amdxdna_client *client,
scnprintf(sensor.label, sizeof(sensor.label), "Total Power");
scnprintf(sensor.units, sizeof(sensor.units), "mW");
+ if (args->buffer_size < sizeof(sensor))
+ goto out;
+
if (copy_to_user(u64_to_user_ptr(args->buffer), &sensor, sizeof(sensor)))
return -EFAULT;
+ args->buffer_size -= sizeof(sensor);
sensors_count++;
- if (args->buffer_size <= sensors_count * sizeof(sensor))
- goto out;
for (i = 0; i < min_t(u32, ndev->total_col, 8); i++) {
memset(&sensor, 0, sizeof(sensor));
@@ -826,13 +826,15 @@ static int aie2_get_sensors(struct amdxdna_client *client,
scnprintf(sensor.label, sizeof(sensor.label), "Column %d Utilization", i);
scnprintf(sensor.units, sizeof(sensor.units), "%%");
+ if (args->buffer_size < sizeof(sensor))
+ goto out;
+
if (copy_to_user(u64_to_user_ptr(args->buffer) + sensors_count * sizeof(sensor),
&sensor, sizeof(sensor)))
return -EFAULT;
+ args->buffer_size -= sizeof(sensor);
sensors_count++;
- if (args->buffer_size <= sensors_count * sizeof(sensor))
- goto out;
}
out:
@@ -928,18 +930,21 @@ static int aie2_query_resource_info(struct amdxdna_client *client,
const struct amdxdna_dev_priv *priv;
struct amdxdna_dev_hdl *ndev;
struct amdxdna_dev *xdna;
+ u32 buf_sz;
xdna = client->xdna;
ndev = xdna->dev_handle;
priv = ndev->priv;
+ aie2_update_counters(ndev);
res_info.npu_clk_max = priv->dpm_clk_tbl[ndev->max_dpm_level].hclk;
res_info.npu_tops_max = ndev->max_tops;
res_info.npu_task_max = priv->hwctx_limit;
res_info.npu_tops_curr = ndev->curr_tops;
res_info.npu_task_curr = ndev->hwctx_num;
- if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, sizeof(res_info)))
+ buf_sz = min(args->buffer_size, sizeof(res_info));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &res_info, buf_sz))
return -EFAULT;
return 0;
@@ -975,12 +980,7 @@ static int aie2_get_telemetry(struct amdxdna_client *client,
XDNA_ERR(xdna, "Invalid buffer size");
return -EINVAL;
}
-
telemetry_data_sz = args->buffer_size - header_sz;
- if (telemetry_data_sz > SZ_4M) {
- XDNA_ERR(xdna, "Buffer size is too big, %d", telemetry_data_sz);
- return -EINVAL;
- }
header = kzalloc(header_sz, GFP_KERNEL);
if (!header)
@@ -1021,6 +1021,7 @@ static int aie2_get_preempt_state(struct amdxdna_client *client,
struct amdxdna_drm_attribute_state state = {};
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_dev_hdl *ndev;
+ u32 buf_sz;
ndev = xdna->dev_handle;
if (args->param == DRM_AMDXDNA_GET_FORCE_PREEMPT_STATE)
@@ -1028,7 +1029,8 @@ static int aie2_get_preempt_state(struct amdxdna_client *client,
else if (args->param == DRM_AMDXDNA_GET_FRAME_BOUNDARY_PREEMPT_STATE)
state.state = ndev->frame_boundary_preempt;
- if (copy_to_user(u64_to_user_ptr(args->buffer), &state, sizeof(state)))
+ buf_sz = min(args->buffer_size, sizeof(state));
+ if (copy_to_user(u64_to_user_ptr(args->buffer), &state, buf_sz))
return -EFAULT;
return 0;
@@ -1255,6 +1257,21 @@ dev_exit:
return ret;
}
+static int aie2_get_dev_rev(struct amdxdna_dev *xdna, u32 *rev)
+{
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ enum aie2_dev_revision aie2_rev;
+ int ret;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+ ret = aie2_get_dev_revision(ndev, &aie2_rev);
+
+ if (!ret)
+ *rev = (u32)aie2_rev;
+
+ return ret;
+}
+
const struct amdxdna_dev_ops aie2_ops = {
.init = aie2_init,
.fini = aie2_fini,
@@ -1269,4 +1286,5 @@ const struct amdxdna_dev_ops aie2_ops = {
.cmd_submit = aie2_cmd_submit,
.hmm_invalidate = aie2_hmm_invalidate,
.get_array = aie2_get_array,
+ .get_dev_revision = aie2_get_dev_rev,
};
diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h
index efcf4be035f0..f12073175676 100644
--- a/drivers/accel/amdxdna/aie2_pci.h
+++ b/drivers/accel/amdxdna/aie2_pci.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (C) 2023-2024, Advanced Micro Devices, Inc.
+ * Copyright (C) 2023-2026, Advanced Micro Devices, Inc.
*/
#ifndef _AIE2_PCI_H_
@@ -10,30 +10,21 @@
#include <linux/limits.h>
#include <linux/semaphore.h>
+#include "aie.h"
#include "aie2_msg_priv.h"
#include "amdxdna_mailbox.h"
-#define AIE2_INTERVAL 20000 /* us */
-#define AIE2_TIMEOUT 1000000 /* us */
-
/* Firmware determines device memory base address and size */
#define AIE2_DEVM_BASE 0x4000000
#define AIE2_DEVM_SIZE SZ_64M
-#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->xdna->ddev.dev))
+#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->aie.xdna->ddev.dev))
#define AIE2_SRAM_OFF(ndev, addr) ((addr) - (ndev)->priv->sram_dev_addr)
#define AIE2_MBOX_OFF(ndev, addr) ((addr) - (ndev)->priv->mbox_dev_addr)
-#define PSP_REG_BAR(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].bar_idx)
-#define PSP_REG_OFF(ndev, idx) ((ndev)->priv->psp_regs_off[(idx)].offset)
#define SRAM_REG_OFF(ndev, idx) ((ndev)->priv->sram_offs[(idx)].offset)
-#define SMU_REG(ndev, idx) \
-({ \
- typeof(ndev) _ndev = ndev; \
- ((_ndev)->smu_base + (_ndev)->priv->smu_regs_off[(idx)].offset); \
-})
#define SRAM_GET_ADDR(ndev, idx) \
({ \
typeof(ndev) _ndev = ndev; \
@@ -45,7 +36,7 @@
({ \
typeof(ndev) _ndev = (ndev); \
((_ndev)->priv->mbox_size) ? (_ndev)->priv->mbox_size : \
- pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->xdna->dev_info->mbox_bar); \
+ pci_resource_len(NDEV2PDEV(_ndev), (_ndev)->aie.xdna->dev_info->mbox_bar); \
})
#if IS_ENABLED(CONFIG_AMD_PMF)
@@ -75,45 +66,17 @@
})
#endif
-enum aie2_smu_reg_idx {
- SMU_CMD_REG = 0,
- SMU_ARG_REG,
- SMU_INTR_REG,
- SMU_RESP_REG,
- SMU_OUT_REG,
- SMU_MAX_REGS /* Keep this at the end */
-};
-
enum aie2_sram_reg_idx {
MBOX_CHANN_OFF = 0,
FW_ALIVE_OFF,
SRAM_MAX_INDEX /* Keep this at the end */
};
-enum psp_reg_idx {
- PSP_CMD_REG = 0,
- PSP_ARG0_REG,
- PSP_ARG1_REG,
- PSP_ARG2_REG,
- PSP_NUM_IN_REGS, /* number of input registers */
- PSP_INTR_REG = PSP_NUM_IN_REGS,
- PSP_STATUS_REG,
- PSP_RESP_REG,
- PSP_PWAITMODE_REG,
- PSP_MAX_REGS /* Keep this at the end */
-};
-
struct amdxdna_client;
struct amdxdna_fw_ver;
struct amdxdna_hwctx;
struct amdxdna_sched_job;
-struct psp_config {
- const void *fw_buf;
- u32 fw_size;
- void __iomem *psp_regs[PSP_MAX_REGS];
-};
-
struct aie_version {
u16 major;
u16 minor;
@@ -202,24 +165,20 @@ struct aie2_exec_msg_ops {
u32 (*get_chain_msg_op)(u32 cmd_op);
};
+enum aie2_tdr_status {
+ AIE2_TDR_WAIT,
+ AIE2_TDR_SIGNALED,
+};
+
struct amdxdna_dev_hdl {
- struct amdxdna_dev *xdna;
+ struct aie_device aie;
const struct amdxdna_dev_priv *priv;
void __iomem *sram_base;
- void __iomem *smu_base;
void __iomem *mbox_base;
- struct psp_device *psp_hdl;
-
- struct xdna_mailbox_chann_res mgmt_x2i;
- struct xdna_mailbox_chann_res mgmt_i2x;
- u32 mgmt_chan_idx;
- u32 mgmt_prot_major;
- u32 mgmt_prot_minor;
u32 total_col;
struct aie_version version;
struct aie_metadata metadata;
- unsigned long feature_mask;
struct aie2_exec_msg_ops *exec_msg_ops;
/* power management and clock*/
@@ -237,63 +196,57 @@ struct amdxdna_dev_hdl {
/* Mailbox and the management channel */
struct mailbox *mbox;
- struct mailbox_channel *mgmt_chann;
struct async_events *async_events;
enum aie2_dev_status dev_status;
u32 hwctx_num;
struct amdxdna_async_error last_async_err;
-};
-
-#define DEFINE_BAR_OFFSET(reg_name, bar, reg_addr) \
- [reg_name] = {bar##_BAR_INDEX, (reg_addr) - bar##_BAR_BASE}
-
-struct aie2_bar_off_pair {
- int bar_idx;
- u32 offset;
+ enum aie2_tdr_status tdr_status;
};
struct aie2_hw_ops {
int (*set_dpm)(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
+ int (*update_counters)(struct amdxdna_dev_hdl *ndev);
};
+#define aie2_update_counters(ndev) \
+({ \
+ typeof(ndev) _ndev = ndev; \
+ if (_ndev->priv->hw_ops->update_counters) \
+ _ndev->priv->hw_ops->update_counters(_ndev); \
+})
+
enum aie2_fw_feature {
AIE2_NPU_COMMAND,
AIE2_PREEMPT,
AIE2_TEMPORAL_ONLY,
AIE2_APP_HEALTH,
+ AIE2_UPDATE_PROPERTY,
+ AIE2_GET_DEV_REVISION,
AIE2_FEATURE_MAX
};
-struct aie2_fw_feature_tbl {
- u64 features;
- u32 major;
- u32 max_minor;
- u32 min_minor;
-};
-
#define AIE2_ALL_FEATURES GENMASK_ULL(AIE2_FEATURE_MAX - 1, AIE2_NPU_COMMAND)
-#define AIE2_FEATURE_ON(ndev, feature) test_bit(feature, &(ndev)->feature_mask)
struct amdxdna_dev_priv {
const char *fw_path;
const struct rt_config *rt_config;
const struct dpm_clk_freq *dpm_clk_tbl;
- const struct aie2_fw_feature_tbl *fw_feature_tbl;
#define COL_ALIGN_NONE 0
#define COL_ALIGN_NATURE 1
u32 col_align;
+ u32 col_opc;
u32 mbox_dev_addr;
/* If mbox_size is 0, use BAR size. See MBOX_SIZE macro */
u32 mbox_size;
u32 hwctx_limit;
u32 sram_dev_addr;
- struct aie2_bar_off_pair sram_offs[SRAM_MAX_INDEX];
- struct aie2_bar_off_pair psp_regs_off[PSP_MAX_REGS];
- struct aie2_bar_off_pair smu_regs_off[SMU_MAX_REGS];
- struct aie2_hw_ops hw_ops;
+ struct aie_bar_off_pair sram_offs[SRAM_MAX_INDEX];
+ struct aie_bar_off_pair psp_regs_off[PSP_MAX_REGS];
+ struct aie_bar_off_pair smu_regs_off[SMU_MAX_REGS];
+ const struct aie2_hw_ops *hw_ops;
};
extern const struct amdxdna_dev_ops aie2_ops;
@@ -306,25 +259,15 @@ extern const struct dpm_clk_freq npu1_dpm_clk_table[];
extern const struct dpm_clk_freq npu4_dpm_clk_table[];
extern const struct rt_config npu1_default_rt_cfg[];
extern const struct rt_config npu4_default_rt_cfg[];
-extern const struct aie2_fw_feature_tbl npu4_fw_feature_table[];
-
-/* aie2_smu.c */
-int aie2_smu_init(struct amdxdna_dev_hdl *ndev);
-void aie2_smu_fini(struct amdxdna_dev_hdl *ndev);
-int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
-int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
+extern const struct amdxdna_fw_feature_tbl npu4_fw_feature_table[];
+extern const struct amdxdna_rev_vbnv npu4_rev_vbnv_tbl[];
+extern const struct aie2_hw_ops npu4_hw_ops;
/* aie2_pm.c */
int aie2_pm_init(struct amdxdna_dev_hdl *ndev);
int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target);
int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level);
-/* aie2_psp.c */
-struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf);
-int aie2_psp_start(struct psp_device *psp);
-void aie2_psp_stop(struct psp_device *psp);
-int aie2_psp_waitmode_poll(struct psp_device *psp);
-
/* aie2_error.c */
int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev);
void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev);
@@ -346,6 +289,7 @@ int aie2_query_firmware_version(struct amdxdna_dev_hdl *ndev,
struct amdxdna_fw_ver *fw_ver);
int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id,
struct app_health_report *report);
+int aie2_get_dev_revision(struct amdxdna_dev_hdl *ndev, enum aie2_dev_revision *rev);
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
@@ -369,6 +313,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, void __iomem *, size_t));
int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job,
int (*notify_cb)(void *, void __iomem *, size_t));
+int aie2_update_prop_time_quota(struct amdxdna_dev_hdl *ndev, u32 us);
void *aie2_alloc_msg_buffer(struct amdxdna_dev_hdl *ndev, u32 *size,
dma_addr_t *dma_addr);
void aie2_free_msg_buffer(struct amdxdna_dev_hdl *ndev, size_t size,
diff --git a/drivers/accel/amdxdna/aie2_pm.c b/drivers/accel/amdxdna/aie2_pm.c
index 29bd4403a94d..4fe6030d2c41 100644
--- a/drivers/accel/amdxdna/aie2_pm.c
+++ b/drivers/accel/amdxdna/aie2_pm.c
@@ -31,14 +31,14 @@ int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
{
int ret;
- ret = amdxdna_pm_resume_get_locked(ndev->xdna);
+ ret = amdxdna_pm_resume_get_locked(ndev->aie.xdna);
if (ret)
return ret;
- ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level);
+ ret = ndev->priv->hw_ops->set_dpm(ndev, dpm_level);
if (!ret)
ndev->dpm_level = dpm_level;
- amdxdna_pm_suspend_put(ndev->xdna);
+ amdxdna_pm_suspend_put(ndev->aie.xdna);
return ret;
}
@@ -49,7 +49,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
if (ndev->dev_status != AIE2_DEV_UNINIT) {
/* Resume device */
- ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->dpm_level);
+ ret = ndev->priv->hw_ops->set_dpm(ndev, ndev->dpm_level);
if (ret)
return ret;
@@ -64,7 +64,7 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
ndev->max_dpm_level++;
ndev->max_dpm_level--;
- ret = ndev->priv->hw_ops.set_dpm(ndev, ndev->max_dpm_level);
+ ret = ndev->priv->hw_ops->set_dpm(ndev, ndev->max_dpm_level);
if (ret)
return ret;
ndev->dpm_level = ndev->max_dpm_level;
@@ -74,14 +74,14 @@ int aie2_pm_init(struct amdxdna_dev_hdl *ndev)
return ret;
ndev->pw_mode = POWER_MODE_DEFAULT;
- ndev->dft_dpm_level = ndev->max_dpm_level;
+ ndev->dft_dpm_level = 0;
return 0;
}
int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target)
{
- struct amdxdna_dev *xdna = ndev->xdna;
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
u32 clk_gating, dpm_level;
int ret;
@@ -108,6 +108,14 @@ int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type
clk_gating = AIE2_CLK_GATING_ENABLE;
dpm_level = ndev->dft_dpm_level;
break;
+ case POWER_MODE_LOW:
+ clk_gating = AIE2_CLK_GATING_ENABLE;
+ dpm_level = 0;
+ break;
+ case POWER_MODE_MEDIUM:
+ clk_gating = AIE2_CLK_GATING_ENABLE;
+ dpm_level = ndev->max_dpm_level / 2;
+ break;
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/accel/amdxdna/aie2_psp.c b/drivers/accel/amdxdna/aie2_psp.c
deleted file mode 100644
index 3a7130577e3e..000000000000
--- a/drivers/accel/amdxdna/aie2_psp.c
+++ /dev/null
@@ -1,161 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
- */
-
-#include <drm/drm_device.h>
-#include <drm/drm_gem_shmem_helper.h>
-#include <drm/drm_managed.h>
-#include <drm/drm_print.h>
-#include <drm/gpu_scheduler.h>
-#include <linux/bitfield.h>
-#include <linux/iopoll.h>
-
-#include "aie2_pci.h"
-#include "amdxdna_mailbox.h"
-#include "amdxdna_pci_drv.h"
-
-#define PSP_STATUS_READY BIT(31)
-
-/* PSP commands */
-#define PSP_VALIDATE 1
-#define PSP_START 2
-#define PSP_RELEASE_TMR 3
-
-/* PSP special arguments */
-#define PSP_START_COPY_FW 1
-
-/* PSP response error code */
-#define PSP_ERROR_CANCEL 0xFFFF0002
-#define PSP_ERROR_BAD_STATE 0xFFFF0007
-
-#define PSP_FW_ALIGN 0x10000
-#define PSP_POLL_INTERVAL 20000 /* us */
-#define PSP_POLL_TIMEOUT 1000000 /* us */
-
-#define PSP_REG(p, reg) ((p)->psp_regs[reg])
-
-struct psp_device {
- struct drm_device *ddev;
- struct psp_config conf;
- u32 fw_buf_sz;
- u64 fw_paddr;
- void *fw_buffer;
- void __iomem *psp_regs[PSP_MAX_REGS];
-};
-
-static int psp_exec(struct psp_device *psp, u32 *reg_vals)
-{
- u32 resp_code;
- int ret, i;
- u32 ready;
-
- /* Write command and argument registers */
- for (i = 0; i < PSP_NUM_IN_REGS; i++)
- writel(reg_vals[i], PSP_REG(psp, i));
-
- /* clear and set PSP INTR register to kick off */
- writel(0, PSP_REG(psp, PSP_INTR_REG));
- writel(1, PSP_REG(psp, PSP_INTR_REG));
-
- /* PSP should be busy. Wait for ready, so we know task is done. */
- ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
- FIELD_GET(PSP_STATUS_READY, ready),
- PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
- if (ret) {
- drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
- return ret;
- }
-
- resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
- if (resp_code) {
- drm_err(psp->ddev, "fw return error 0x%x", resp_code);
- return -EIO;
- }
-
- return 0;
-}
-
-int aie2_psp_waitmode_poll(struct psp_device *psp)
-{
- struct amdxdna_dev *xdna = to_xdna_dev(psp->ddev);
- u32 mode_reg;
- int ret;
-
- ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_PWAITMODE_REG), mode_reg,
- (mode_reg & 0x1) == 1,
- PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
- if (ret)
- XDNA_ERR(xdna, "fw waitmode reg error, ret %d", ret);
-
- return ret;
-}
-
-void aie2_psp_stop(struct psp_device *psp)
-{
- u32 reg_vals[PSP_NUM_IN_REGS] = { PSP_RELEASE_TMR, };
- int ret;
-
- ret = psp_exec(psp, reg_vals);
- if (ret)
- drm_err(psp->ddev, "release tmr failed, ret %d", ret);
-}
-
-int aie2_psp_start(struct psp_device *psp)
-{
- u32 reg_vals[PSP_NUM_IN_REGS];
- int ret;
-
- reg_vals[0] = PSP_VALIDATE;
- reg_vals[1] = lower_32_bits(psp->fw_paddr);
- reg_vals[2] = upper_32_bits(psp->fw_paddr);
- reg_vals[3] = psp->fw_buf_sz;
-
- ret = psp_exec(psp, reg_vals);
- if (ret) {
- drm_err(psp->ddev, "failed to validate fw, ret %d", ret);
- return ret;
- }
-
- memset(reg_vals, 0, sizeof(reg_vals));
- reg_vals[0] = PSP_START;
- reg_vals[1] = PSP_START_COPY_FW;
- ret = psp_exec(psp, reg_vals);
- if (ret) {
- drm_err(psp->ddev, "failed to start fw, ret %d", ret);
- return ret;
- }
-
- return 0;
-}
-
-struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf)
-{
- struct psp_device *psp;
- u64 offset;
-
- psp = drmm_kzalloc(ddev, sizeof(*psp), GFP_KERNEL);
- if (!psp)
- return NULL;
-
- psp->ddev = ddev;
- memcpy(psp->psp_regs, conf->psp_regs, sizeof(psp->psp_regs));
-
- psp->fw_buf_sz = ALIGN(conf->fw_size, PSP_FW_ALIGN);
- psp->fw_buffer = drmm_kmalloc(ddev, psp->fw_buf_sz + PSP_FW_ALIGN, GFP_KERNEL);
- if (!psp->fw_buffer) {
- drm_err(ddev, "no memory for fw buffer");
- return NULL;
- }
-
- /*
- * AMD Platform Security Processor(PSP) requires host physical
- * address to load NPU firmware.
- */
- psp->fw_paddr = virt_to_phys(psp->fw_buffer);
- offset = ALIGN(psp->fw_paddr, PSP_FW_ALIGN) - psp->fw_paddr;
- psp->fw_paddr += offset;
- memcpy(psp->fw_buffer + offset, conf->fw_buf, conf->fw_size);
-
- return psp;
-}
diff --git a/drivers/accel/amdxdna/aie2_smu.c b/drivers/accel/amdxdna/aie2_smu.c
deleted file mode 100644
index d8c31924e501..000000000000
--- a/drivers/accel/amdxdna/aie2_smu.c
+++ /dev/null
@@ -1,156 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
- */
-
-#include <drm/drm_device.h>
-#include <drm/drm_gem_shmem_helper.h>
-#include <drm/drm_print.h>
-#include <drm/gpu_scheduler.h>
-#include <linux/iopoll.h>
-
-#include "aie2_pci.h"
-#include "amdxdna_pci_drv.h"
-
-#define SMU_RESULT_OK 1
-
-/* SMU commands */
-#define AIE2_SMU_POWER_ON 0x3
-#define AIE2_SMU_POWER_OFF 0x4
-#define AIE2_SMU_SET_MPNPUCLK_FREQ 0x5
-#define AIE2_SMU_SET_HCLK_FREQ 0x6
-#define AIE2_SMU_SET_SOFT_DPMLEVEL 0x7
-#define AIE2_SMU_SET_HARD_DPMLEVEL 0x8
-
-#define NPU4_DPM_TOPS(ndev, dpm_level) \
-({ \
- typeof(ndev) _ndev = ndev; \
- (4096 * (_ndev)->total_col * \
- (_ndev)->priv->dpm_clk_tbl[dpm_level].hclk / 1000000); \
-})
-
-static int aie2_smu_exec(struct amdxdna_dev_hdl *ndev, u32 reg_cmd,
- u32 reg_arg, u32 *out)
-{
- u32 resp;
- int ret;
-
- writel(0, SMU_REG(ndev, SMU_RESP_REG));
- writel(reg_arg, SMU_REG(ndev, SMU_ARG_REG));
- writel(reg_cmd, SMU_REG(ndev, SMU_CMD_REG));
-
- /* Clear and set SMU_INTR_REG to kick off */
- writel(0, SMU_REG(ndev, SMU_INTR_REG));
- writel(1, SMU_REG(ndev, SMU_INTR_REG));
-
- ret = readx_poll_timeout(readl, SMU_REG(ndev, SMU_RESP_REG), resp,
- resp, AIE2_INTERVAL, AIE2_TIMEOUT);
- if (ret) {
- XDNA_ERR(ndev->xdna, "smu cmd %d timed out", reg_cmd);
- return ret;
- }
-
- if (out)
- *out = readl(SMU_REG(ndev, SMU_OUT_REG));
-
- if (resp != SMU_RESULT_OK) {
- XDNA_ERR(ndev->xdna, "smu cmd %d failed, 0x%x", reg_cmd, resp);
- return -EINVAL;
- }
-
- return 0;
-}
-
-int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
-{
- u32 freq;
- int ret;
-
- ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ,
- ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n",
- ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret);
- return ret;
- }
- ndev->npuclk_freq = freq;
-
- ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HCLK_FREQ,
- ndev->priv->dpm_clk_tbl[dpm_level].hclk, &freq);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n",
- ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret);
- return ret;
- }
-
- ndev->hclk_freq = freq;
- ndev->max_tops = 2 * ndev->total_col;
- ndev->curr_tops = ndev->max_tops * freq / 1028;
-
- XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
- ndev->npuclk_freq, ndev->hclk_freq);
-
- return 0;
-}
-
-int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
-{
- int ret;
-
- ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ",
- dpm_level, ret);
- return ret;
- }
-
- ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d",
- dpm_level, ret);
- return ret;
- }
-
- ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
- ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
- ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->max_dpm_level);
- ndev->curr_tops = NPU4_DPM_TOPS(ndev, dpm_level);
-
- XDNA_DBG(ndev->xdna, "MP-NPU clock %d, H clock %d\n",
- ndev->npuclk_freq, ndev->hclk_freq);
-
- return 0;
-}
-
-int aie2_smu_init(struct amdxdna_dev_hdl *ndev)
-{
- int ret;
-
- /*
- * Failing to set power off indicates an unrecoverable hardware or
- * firmware error.
- */
- ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Access power failed, ret %d", ret);
- return ret;
- }
-
- ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_ON, 0, NULL);
- if (ret) {
- XDNA_ERR(ndev->xdna, "Power on failed, ret %d", ret);
- return ret;
- }
-
- return 0;
-}
-
-void aie2_smu_fini(struct amdxdna_dev_hdl *ndev)
-{
- int ret;
-
- ndev->priv->hw_ops.set_dpm(ndev, 0);
- ret = aie2_smu_exec(ndev, AIE2_SMU_POWER_OFF, 0, NULL);
- if (ret)
- XDNA_ERR(ndev->xdna, "Power off failed, ret %d", ret);
-}
diff --git a/drivers/accel/amdxdna/aie2_solver.c b/drivers/accel/amdxdna/aie2_solver.c
index 3611e3268d79..6f3ee77d5264 100644
--- a/drivers/accel/amdxdna/aie2_solver.c
+++ b/drivers/accel/amdxdna/aie2_solver.c
@@ -52,7 +52,7 @@ static u32 calculate_gops(struct aie_qos *rqos)
u32 service_rate = 0;
if (rqos->latency)
- service_rate = (1000 / rqos->latency);
+ service_rate = max_t(u32, 1000 / rqos->latency, 1);
if (rqos->fps > service_rate)
return rqos->fps * rqos->gops;
@@ -348,6 +348,7 @@ int xrs_release_resource(void *hdl, u64 rid)
{
struct solver_state *xrs = hdl;
struct solver_node *node;
+ u32 level = 0;
node = rg_search_node(&xrs->rgp, rid);
if (!node) {
@@ -358,6 +359,13 @@ int xrs_release_resource(void *hdl, u64 rid)
xrs->cfg.actions->unload(node->cb_arg);
remove_solver_node(&xrs->rgp, node);
+ /* set the dpm level which fits all the sessions */
+ list_for_each_entry(node, &xrs->rgp.node_list, list) {
+ if (node->dpm_level > level)
+ level = node->dpm_level;
+ }
+ xrs->cfg.actions->set_dft_dpm_level(xrs->cfg.ddev, level);
+
return 0;
}
diff --git a/drivers/accel/amdxdna/aie4_message.c b/drivers/accel/amdxdna/aie4_message.c
new file mode 100644
index 000000000000..d621dd32ac40
--- /dev/null
+++ b/drivers/accel/amdxdna/aie4_message.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_print.h>
+#include <linux/mutex.h>
+
+#include "aie.h"
+#include "aie4_msg_priv.h"
+#include "aie4_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_mailbox_helper.h"
+#include "amdxdna_pci_drv.h"
+
+int aie4_suspend_fw(struct amdxdna_dev_hdl *ndev)
+{
+ DECLARE_AIE_MSG(aie4_msg_suspend, AIE4_MSG_OP_SUSPEND);
+ int ret;
+
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret)
+ XDNA_ERR(ndev->aie.xdna, "Failed to suspend fw, ret %d", ret);
+
+ return ret;
+}
diff --git a/drivers/accel/amdxdna/aie4_msg_priv.h b/drivers/accel/amdxdna/aie4_msg_priv.h
new file mode 100644
index 000000000000..88463cc3a98a
--- /dev/null
+++ b/drivers/accel/amdxdna/aie4_msg_priv.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AIE4_MSG_PRIV_H_
+#define _AIE4_MSG_PRIV_H_
+
+#include <linux/types.h>
+
+enum aie4_msg_opcode {
+ AIE4_MSG_OP_SUSPEND = 0x10003,
+
+ AIE4_MSG_OP_CREATE_VFS = 0x20001,
+ AIE4_MSG_OP_DESTROY_VFS = 0x20002,
+};
+
+enum aie4_msg_status {
+ AIE4_MSG_STATUS_SUCCESS = 0x0,
+ AIE4_MSG_STATUS_ERROR = 0x1,
+ AIE4_MSG_STATUS_NOTSUPP = 0x2,
+ MAX_AIE4_MSG_STATUS_CODE = 0x4,
+};
+
+struct aie4_msg_suspend_req {
+ __u32 rsvd;
+} __packed;
+
+struct aie4_msg_suspend_resp {
+ enum aie4_msg_status status;
+} __packed;
+
+struct aie4_msg_create_vfs_req {
+ __u32 vf_cnt;
+} __packed;
+
+struct aie4_msg_create_vfs_resp {
+ enum aie4_msg_status status;
+} __packed;
+
+struct aie4_msg_destroy_vfs_req {
+ __u32 rsvd;
+} __packed;
+
+struct aie4_msg_destroy_vfs_resp {
+ enum aie4_msg_status status;
+} __packed;
+
+#endif /* _AIE4_MSG_PRIV_H_ */
diff --git a/drivers/accel/amdxdna/aie4_pci.c b/drivers/accel/amdxdna/aie4_pci.c
new file mode 100644
index 000000000000..87f80f804f91
--- /dev/null
+++ b/drivers/accel/amdxdna/aie4_pci.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <linux/firmware.h>
+#include <linux/sizes.h>
+
+#include "aie4_pci.h"
+#include "amdxdna_pci_drv.h"
+
+#define NO_IOHUB 0
+#define PSP_NOTIFY_INTR 0xD007BE11
+
+/*
+ * The management mailbox channel is allocated by firmware.
+ * The related register and ring buffer information is on SRAM BAR.
+ * This struct is the register layout.
+ */
+struct mailbox_info {
+ __u32 valid;
+ __u32 protocol_major;
+ __u32 protocol_minor;
+ __u32 x2i_tail_offset;
+ __u32 x2i_head_offset;
+ __u32 x2i_buffer_addr;
+ __u32 x2i_buffer_size;
+ __u32 i2x_tail_offset;
+ __u32 i2x_head_offset;
+ __u32 i2x_buffer_addr;
+ __u32 i2x_buffer_size;
+ __u32 i2x_msi_idx;
+ __u32 reserved[4];
+};
+
+static int aie4_fw_is_alive(struct amdxdna_dev *xdna)
+{
+ const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ u32 __iomem *src;
+ u32 fw_is_valid;
+ int ret;
+
+ src = ndev->rbuf_base + npriv->mbox_info_off;
+
+ ret = readx_poll_timeout(readl, src + offsetof(struct mailbox_info, valid),
+ fw_is_valid, (fw_is_valid == 0x1),
+ AIE_INTERVAL, AIE_TIMEOUT);
+ if (ret)
+ XDNA_ERR(xdna, "fw_is_valid=%d after %d ms",
+ fw_is_valid, DIV_ROUND_CLOSEST(AIE_TIMEOUT, 1000000));
+
+ return ret;
+}
+
+static void aie4_read_mbox_info(struct amdxdna_dev *xdna,
+ struct mailbox_info *mbox_info)
+{
+ const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ u32 *dst = (u32 *)mbox_info;
+ u32 __iomem *src;
+ int i;
+
+ src = ndev->rbuf_base + npriv->mbox_info_off;
+
+ for (i = 0; i < sizeof(*mbox_info) / sizeof(u32); i++)
+ dst[i] = readl(&src[i]);
+}
+
+static int aie4_mailbox_info(struct amdxdna_dev *xdna,
+ struct mailbox_info *mbox_info)
+{
+ int ret;
+
+ ret = aie4_fw_is_alive(xdna);
+ if (ret)
+ return ret;
+
+ aie4_read_mbox_info(xdna, mbox_info);
+
+ ret = aie_check_protocol(&xdna->dev_handle->aie,
+ mbox_info->protocol_major,
+ mbox_info->protocol_minor);
+ if (ret)
+ XDNA_ERR(xdna, "mailbox major.minor %d.%d is not supported",
+ mbox_info->protocol_major, mbox_info->protocol_minor);
+
+ return ret;
+}
+
+static void aie4_mailbox_fini(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+
+ aie_destroy_chann(&ndev->aie, &ndev->aie.mgmt_chann);
+ drmm_kfree(&xdna->ddev, ndev->mbox);
+ ndev->mbox = NULL;
+}
+
+static int aie4_irq_init(struct amdxdna_dev *xdna)
+{
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ int ret, nvec;
+
+ nvec = pci_msix_vec_count(pdev);
+ XDNA_DBG(xdna, "irq vectors:%d", nvec);
+ if (nvec <= 0) {
+ XDNA_ERR(xdna, "does not get number of interrupt vector");
+ return -EINVAL;
+ }
+
+ ret = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
+ if (ret < 0) {
+ XDNA_ERR(xdna, "failed to alloc irq vector, ret: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aie4_mailbox_start(struct amdxdna_dev *xdna,
+ struct mailbox_info *mbi)
+{
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ const struct amdxdna_dev_priv *npriv = xdna->dev_info->dev_priv;
+ struct xdna_mailbox_chann_res *i2x;
+ struct xdna_mailbox_chann_res *x2i;
+ int mgmt_mb_irq;
+ int ret;
+
+ struct xdna_mailbox_res mbox_res = {
+ .ringbuf_base = ndev->rbuf_base,
+ .ringbuf_size = pci_resource_len(pdev, npriv->mbox_rbuf_bar),
+ .mbox_base = ndev->mbox_base,
+ .mbox_size = pci_resource_len(pdev, npriv->mbox_bar),
+ .name = "xdna_aie4_mailbox",
+ };
+
+ i2x = &ndev->aie.mgmt_i2x;
+ x2i = &ndev->aie.mgmt_x2i;
+
+ x2i->mb_head_ptr_reg = mbi->x2i_head_offset;
+ x2i->mb_tail_ptr_reg = mbi->x2i_tail_offset;
+ x2i->rb_start_addr = mbi->x2i_buffer_addr;
+ x2i->rb_size = mbi->x2i_buffer_size;
+
+ i2x->rb_start_addr = mbi->i2x_buffer_addr;
+ i2x->rb_size = mbi->i2x_buffer_size;
+ i2x->mb_head_ptr_reg = mbi->i2x_head_offset;
+ i2x->mb_tail_ptr_reg = mbi->i2x_tail_offset;
+
+ ndev->aie.mgmt_chan_idx = mbi->i2x_msi_idx;
+ aie_dump_mgmt_chann_debug(&ndev->aie);
+
+ ndev->mbox = xdnam_mailbox_create(&xdna->ddev, &mbox_res);
+ if (!ndev->mbox) {
+ XDNA_ERR(xdna, "failed to create mailbox device");
+ return -ENODEV;
+ }
+
+ ndev->aie.mgmt_chann = xdna_mailbox_alloc_channel(ndev->mbox);
+ if (!ndev->aie.mgmt_chann) {
+ XDNA_ERR(xdna, "failed to alloc mailbox channel");
+ return -ENODEV;
+ }
+
+ mgmt_mb_irq = pci_irq_vector(pdev, ndev->aie.mgmt_chan_idx);
+ if (mgmt_mb_irq < 0) {
+ XDNA_ERR(xdna, "failed to alloc irq vector, return %d", mgmt_mb_irq);
+ ret = mgmt_mb_irq;
+ goto free_channel;
+ }
+
+ ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann,
+ &ndev->aie.mgmt_x2i,
+ &ndev->aie.mgmt_i2x,
+ NO_IOHUB,
+ mgmt_mb_irq);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to start management mailbox channel");
+ ret = -EINVAL;
+ goto free_channel;
+ }
+
+ XDNA_DBG(xdna, "Mailbox management channel created");
+ return 0;
+
+free_channel:
+ xdna_mailbox_free_channel(ndev->aie.mgmt_chann);
+ ndev->aie.mgmt_chann = NULL;
+ return ret;
+}
+
+static int aie4_mailbox_init(struct amdxdna_dev *xdna)
+{
+ struct mailbox_info mbox_info;
+ int ret;
+
+ ret = aie4_mailbox_info(xdna, &mbox_info);
+ if (ret)
+ return ret;
+
+ return aie4_mailbox_start(xdna, &mbox_info);
+}
+
+static void aie4_fw_unload(struct amdxdna_dev_hdl *ndev)
+{
+ aie_psp_stop(ndev->aie.psp_hdl);
+ aie_smu_fini(ndev->aie.smu_hdl);
+}
+
+static int aie4_fw_load(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ ret = aie_smu_init(ndev->aie.smu_hdl);
+ if (ret) {
+ XDNA_ERR(ndev->aie.xdna, "failed to init smu, ret %d", ret);
+ return ret;
+ }
+
+ ret = aie_psp_start(ndev->aie.psp_hdl);
+ if (ret) {
+ XDNA_ERR(ndev->aie.xdna, "failed to start psp, ret %d", ret);
+ aie_smu_fini(ndev->aie.smu_hdl);
+ }
+
+ return ret;
+}
+
+static int aie4_hw_start(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+ int ret;
+
+ ret = aie4_fw_load(ndev);
+ if (ret)
+ return ret;
+
+ ret = aie4_mailbox_init(xdna);
+ if (ret)
+ goto fw_unload;
+
+ return 0;
+
+fw_unload:
+ aie4_fw_unload(ndev);
+
+ return ret;
+}
+
+static void aie4_mgmt_fw_fini(struct amdxdna_dev_hdl *ndev)
+{
+ int ret;
+
+ /* No paired resume needed, fw is stateless */
+ ret = aie4_suspend_fw(ndev);
+ if (ret)
+ XDNA_ERR(ndev->aie.xdna, "suspend_fw failed, ret %d", ret);
+ else
+ XDNA_DBG(ndev->aie.xdna, "npu firmware suspended");
+}
+
+static void aie4_hw_stop(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ aie4_mgmt_fw_fini(ndev);
+ aie4_mailbox_fini(ndev);
+
+ aie4_fw_unload(ndev);
+}
+
+static int aie4_request_firmware(struct amdxdna_dev_hdl *ndev,
+ const struct firmware **npufw,
+ const struct firmware **certfw)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ char fw_name[128];
+ int ret;
+
+ ret = snprintf(fw_name, sizeof(fw_name), "amdnpu/%04x_%02x/%s",
+ pdev->device, pdev->revision, ndev->priv->npufw_path);
+ if (ret >= sizeof(fw_name)) {
+ XDNA_ERR(xdna, "npu firmware path is truncated");
+ return -EINVAL;
+ }
+
+ ret = request_firmware(npufw, fw_name, &pdev->dev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to request_firmware %s, ret %d", fw_name, ret);
+ return ret;
+ }
+
+ ret = snprintf(fw_name, sizeof(fw_name), "amdnpu/%04x_%02x/%s",
+ pdev->device, pdev->revision, ndev->priv->certfw_path);
+ if (ret >= sizeof(fw_name)) {
+ XDNA_ERR(xdna, "cert firmware path is truncated");
+ ret = -EINVAL;
+ goto release_npufw;
+ }
+
+ ret = request_firmware(certfw, fw_name, &pdev->dev);
+ if (ret) {
+ XDNA_ERR(xdna, "failed to request_firmware %s, ret %d", fw_name, ret);
+ goto release_npufw;
+ }
+
+ return 0;
+
+release_npufw:
+ release_firmware(*npufw);
+
+ return ret;
+}
+
+static void aie4_release_firmware(struct amdxdna_dev_hdl *ndev,
+ const struct firmware *npufw,
+ const struct firmware *certfw)
+{
+ release_firmware(certfw);
+ release_firmware(npufw);
+}
+
+static int aie4_prepare_firmware(struct amdxdna_dev_hdl *ndev,
+ const struct firmware *npufw,
+ const struct firmware *certfw,
+ void __iomem *tbl[PCI_NUM_RESOURCES])
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ struct psp_config psp_conf;
+ struct smu_config smu_conf;
+ int i;
+
+ psp_conf.fw_size = npufw->size;
+ psp_conf.fw_buf = npufw->data;
+ psp_conf.certfw_size = certfw->size;
+ psp_conf.certfw_buf = certfw->data;
+ psp_conf.arg2_mask = ~0;
+ psp_conf.notify_val = PSP_NOTIFY_INTR;
+ for (i = 0; i < PSP_MAX_REGS; i++)
+ psp_conf.psp_regs[i] = tbl[PSP_REG_BAR(ndev, i)] + PSP_REG_OFF(ndev, i);
+ ndev->aie.psp_hdl = aiem_psp_create(&xdna->ddev, &psp_conf);
+ if (!ndev->aie.psp_hdl) {
+ XDNA_ERR(xdna, "failed to create psp");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < SMU_MAX_REGS; i++)
+ smu_conf.smu_regs[i] = tbl[SMU_REG_BAR(ndev, i)] + SMU_REG_OFF(ndev, i);
+ ndev->aie.smu_hdl = aiem_smu_create(&xdna->ddev, &smu_conf);
+ if (!ndev->aie.smu_hdl) {
+ XDNA_ERR(xdna, "failed to create smu");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int aie4_pcidev_init(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ void __iomem *tbl[PCI_NUM_RESOURCES] = {0};
+ const struct firmware *npufw, *certfw;
+ unsigned long bars = 0;
+ int ret, i;
+
+ /* Enable managed PCI device */
+ ret = pcim_enable_device(pdev);
+ if (ret) {
+ XDNA_ERR(xdna, "pcim enable device failed, ret %d", ret);
+ return ret;
+ }
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (ret) {
+ XDNA_ERR(xdna, "failed to set DMA mask to 64:%d", ret);
+ return ret;
+ }
+
+ for (i = 0; i < PSP_MAX_REGS; i++)
+ set_bit(PSP_REG_BAR(ndev, i), &bars);
+ for (i = 0; i < SMU_MAX_REGS; i++)
+ set_bit(SMU_REG_BAR(ndev, i), &bars);
+ set_bit(xdna->dev_info->mbox_bar, &bars);
+ set_bit(xdna->dev_info->sram_bar, &bars);
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ if (!test_bit(i, &bars))
+ continue;
+ tbl[i] = pcim_iomap(pdev, i, 0);
+ if (!tbl[i]) {
+ XDNA_ERR(xdna, "map bar %d failed", i);
+ return -ENOMEM;
+ }
+ }
+
+ ndev->mbox_base = tbl[xdna->dev_info->mbox_bar];
+ ndev->rbuf_base = tbl[xdna->dev_info->sram_bar];
+
+ pci_set_master(pdev);
+
+ ret = aie4_request_firmware(ndev, &npufw, &certfw);
+ if (ret)
+ goto clear_master;
+
+ ret = aie4_prepare_firmware(ndev, npufw, certfw, tbl);
+ aie4_release_firmware(ndev, npufw, certfw);
+ if (ret)
+ goto clear_master;
+
+ ret = aie4_irq_init(xdna);
+ if (ret)
+ goto clear_master;
+
+ ret = aie4_hw_start(xdna);
+ if (ret)
+ goto clear_master;
+
+ return 0;
+
+clear_master:
+ pci_clear_master(pdev);
+
+ return ret;
+}
+
+static void aie4_pcidev_fini(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+
+ aie4_hw_stop(xdna);
+
+ pci_clear_master(pdev);
+}
+
+static void aie4_fini(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+
+ aie4_sriov_stop(ndev);
+ aie4_pcidev_fini(ndev);
+}
+
+static int aie4_init(struct amdxdna_dev *xdna)
+{
+ struct amdxdna_dev_hdl *ndev;
+ int ret;
+
+ ndev = drmm_kzalloc(&xdna->ddev, sizeof(*ndev), GFP_KERNEL);
+ if (!ndev)
+ return -ENOMEM;
+
+ ndev->priv = xdna->dev_info->dev_priv;
+ ndev->aie.xdna = xdna;
+ xdna->dev_handle = ndev;
+
+ ret = aie4_pcidev_init(ndev);
+ if (ret) {
+ XDNA_ERR(xdna, "Setup PCI device failed, ret %d", ret);
+ return ret;
+ }
+
+ amdxdna_vbnv_init(xdna);
+ XDNA_DBG(xdna, "aie4 init finished");
+ return 0;
+}
+
+const struct amdxdna_dev_ops aie4_ops = {
+ .init = aie4_init,
+ .fini = aie4_fini,
+ .sriov_configure = aie4_sriov_configure,
+};
diff --git a/drivers/accel/amdxdna/aie4_pci.h b/drivers/accel/amdxdna/aie4_pci.h
new file mode 100644
index 000000000000..aa1495c3370b
--- /dev/null
+++ b/drivers/accel/amdxdna/aie4_pci.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#ifndef _AIE4_PCI_H_
+#define _AIE4_PCI_H_
+
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+
+#include "aie.h"
+#include "amdxdna_mailbox.h"
+
+struct amdxdna_dev_priv {
+ const char *npufw_path;
+ const char *certfw_path;
+ u32 mbox_bar;
+ u32 mbox_rbuf_bar;
+ u64 mbox_info_off;
+
+ struct aie_bar_off_pair psp_regs_off[PSP_MAX_REGS];
+ struct aie_bar_off_pair smu_regs_off[SMU_MAX_REGS];
+};
+
+struct amdxdna_dev_hdl {
+ struct aie_device aie;
+ const struct amdxdna_dev_priv *priv;
+ void __iomem *mbox_base;
+ void __iomem *rbuf_base;
+
+ struct mailbox *mbox;
+};
+
+/* aie4_message.c */
+int aie4_suspend_fw(struct amdxdna_dev_hdl *ndev);
+
+/* aie4_sriov.c */
+#if IS_ENABLED(CONFIG_PCI_IOV)
+int aie4_sriov_configure(struct amdxdna_dev *xdna, int num_vfs);
+int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev);
+#else
+#define aie4_sriov_configure NULL
+static inline int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev)
+{
+ return 0;
+}
+#endif
+
+extern const struct amdxdna_dev_ops aie4_ops;
+
+#endif /* _AIE4_PCI_H_ */
diff --git a/drivers/accel/amdxdna/aie4_sriov.c b/drivers/accel/amdxdna/aie4_sriov.c
new file mode 100644
index 000000000000..e1ce633768a5
--- /dev/null
+++ b/drivers/accel/amdxdna/aie4_sriov.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_print.h>
+#include <linux/pci.h>
+
+#include "aie.h"
+#include "aie4_msg_priv.h"
+#include "aie4_pci.h"
+#include "amdxdna_mailbox.h"
+#include "amdxdna_mailbox_helper.h"
+#include "amdxdna_pci_drv.h"
+
+static int aie4_destroy_vfs(struct amdxdna_dev_hdl *ndev)
+{
+ DECLARE_AIE_MSG(aie4_msg_destroy_vfs, AIE4_MSG_OP_DESTROY_VFS);
+ int ret;
+
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret)
+ XDNA_ERR(ndev->aie.xdna, "destroy vfs op failed: %d", ret);
+
+ return ret;
+}
+
+static int aie4_create_vfs(struct amdxdna_dev_hdl *ndev, int num_vfs)
+{
+ DECLARE_AIE_MSG(aie4_msg_create_vfs, AIE4_MSG_OP_CREATE_VFS);
+ int ret;
+
+ req.vf_cnt = num_vfs;
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret)
+ XDNA_ERR(ndev->aie.xdna, "create vfs op failed: %d", ret);
+
+ return ret;
+}
+
+int aie4_sriov_stop(struct amdxdna_dev_hdl *ndev)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ int ret;
+
+ if (!pci_num_vf(pdev))
+ return 0;
+
+ ret = pci_vfs_assigned(pdev);
+ if (ret) {
+ XDNA_ERR(xdna, "VFs are still assigned to VMs");
+ return -EPERM;
+ }
+
+ pci_disable_sriov(pdev);
+ return aie4_destroy_vfs(ndev);
+}
+
+static int aie4_sriov_start(struct amdxdna_dev_hdl *ndev, int num_vfs)
+{
+ struct amdxdna_dev *xdna = ndev->aie.xdna;
+ struct pci_dev *pdev = to_pci_dev(xdna->ddev.dev);
+ int ret;
+
+ ret = aie4_create_vfs(ndev, num_vfs);
+ if (ret)
+ return ret;
+
+ ret = pci_enable_sriov(pdev, num_vfs);
+ if (ret) {
+ XDNA_ERR(xdna, "configure VFs failed, ret: %d", ret);
+ aie4_destroy_vfs(ndev);
+ return ret;
+ }
+
+ return num_vfs;
+}
+
+int aie4_sriov_configure(struct amdxdna_dev *xdna, int num_vfs)
+{
+ struct amdxdna_dev_hdl *ndev = xdna->dev_handle;
+
+ drm_WARN_ON(&xdna->ddev, !mutex_is_locked(&xdna->dev_lock));
+
+ return (num_vfs) ? aie4_sriov_start(ndev, num_vfs) : aie4_sriov_stop(ndev);
+}
diff --git a/drivers/accel/amdxdna/aie_psp.c b/drivers/accel/amdxdna/aie_psp.c
new file mode 100644
index 000000000000..458dca7cc5a0
--- /dev/null
+++ b/drivers/accel/amdxdna/aie_psp.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/drm_device.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <linux/slab.h>
+
+#include "aie.h"
+
+#define PSP_STATUS_READY BIT(31)
+
+/* PSP commands */
+#define PSP_VALIDATE 1
+#define PSP_START 2
+#define PSP_RELEASE_TMR 3
+#define PSP_VALIDATE_CERT 4
+
+/* PSP special arguments */
+#define PSP_START_COPY_FW 1
+
+/* PSP response error code */
+#define PSP_ERROR_CANCEL 0xFFFF0002
+#define PSP_ERROR_BAD_STATE 0xFFFF0007
+
+#define PSP_FW_ALIGN 0x10000
+#define PSP_CFW_ALIGN 0x8000
+#define PSP_POLL_INTERVAL 20000 /* us */
+#define PSP_POLL_TIMEOUT 1000000 /* us */
+
+#define PSP_REG(p, reg) ((p)->conf.psp_regs[reg])
+#define PSP_SET_CMD(psp, reg_vals, cmd, arg0, arg1, arg2) \
+({ \
+ u32 *_regs = reg_vals; \
+ u32 _cmd = cmd; \
+ _regs[0] = _cmd; \
+ _regs[1] = arg0; \
+ _regs[2] = arg1; \
+ _regs[3] = ((arg2) | ((_cmd) << 24)) & (psp)->conf.arg2_mask; \
+})
+
+struct psp_device {
+ struct drm_device *ddev;
+ struct psp_config conf;
+ u32 fw_buf_sz;
+ u64 fw_paddr;
+ void *fw_buffer;
+ u32 certfw_buf_sz;
+ u64 certfw_paddr;
+ void *certfw_buffer;
+};
+
+static int psp_exec(struct psp_device *psp, u32 *reg_vals)
+{
+ u32 resp_code;
+ int ret, i;
+ u32 ready;
+
+ /* Check for PSP ready before any write */
+ ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
+ FIELD_GET(PSP_STATUS_READY, ready),
+ PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
+ if (ret) {
+ drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
+ return ret;
+ }
+
+ /* Write command and argument registers */
+ for (i = 0; i < PSP_NUM_IN_REGS; i++)
+ writel(reg_vals[i], PSP_REG(psp, i));
+
+ /* clear and set PSP INTR register to kick off */
+ writel(0, PSP_REG(psp, PSP_INTR_REG));
+ writel(psp->conf.notify_val, PSP_REG(psp, PSP_INTR_REG));
+
+ /* PSP should be busy. Wait for ready, so we know task is done. */
+ ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_STATUS_REG), ready,
+ FIELD_GET(PSP_STATUS_READY, ready),
+ PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
+ if (ret) {
+ drm_err(psp->ddev, "PSP is not ready, ret 0x%x", ret);
+ return ret;
+ }
+
+ resp_code = readl(PSP_REG(psp, PSP_RESP_REG));
+ if (resp_code) {
+ drm_err(psp->ddev, "fw return error 0x%x", resp_code);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int aie_psp_waitmode_poll(struct psp_device *psp)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(psp->ddev);
+ u32 mode_reg;
+ int ret;
+
+ ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_PWAITMODE_REG), mode_reg,
+ (mode_reg & 0x1) == 1,
+ PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT);
+ if (ret)
+ XDNA_ERR(xdna, "fw waitmode reg error, ret %d", ret);
+
+ return ret;
+}
+
+void aie_psp_stop(struct psp_device *psp)
+{
+ u32 reg_vals[PSP_NUM_IN_REGS];
+ int ret;
+
+ PSP_SET_CMD(psp, reg_vals, PSP_RELEASE_TMR, 0, 0, 0);
+
+ ret = psp_exec(psp, reg_vals);
+ if (ret)
+ drm_err(psp->ddev, "release tmr failed, ret %d", ret);
+}
+
+static int psp_validate_fw(struct psp_device *psp, u8 cmd, u64 paddr, u32 buf_sz)
+{
+ u32 reg_vals[PSP_NUM_IN_REGS];
+ int ret;
+
+ PSP_SET_CMD(psp, reg_vals, cmd, lower_32_bits(paddr),
+ upper_32_bits(paddr), buf_sz);
+
+ ret = psp_exec(psp, reg_vals);
+ if (ret)
+ drm_err(psp->ddev, "failed to validate fw, ret %d", ret);
+
+ return ret;
+}
+
+static int psp_start(struct psp_device *psp)
+{
+ u32 reg_vals[PSP_NUM_IN_REGS];
+ int ret;
+
+ PSP_SET_CMD(psp, reg_vals, PSP_START, PSP_START_COPY_FW, 0, 0);
+
+ ret = psp_exec(psp, reg_vals);
+ if (ret)
+ drm_err(psp->ddev, "failed to start fw, ret %d", ret);
+
+ return ret;
+}
+
+int aie_psp_start(struct psp_device *psp)
+{
+ int ret;
+
+ ret = psp_validate_fw(psp, PSP_VALIDATE,
+ psp->fw_paddr, psp->fw_buf_sz);
+ if (ret)
+ return ret;
+
+ if (!psp->certfw_buf_sz)
+ goto psp_start;
+
+ ret = psp_validate_fw(psp, PSP_VALIDATE_CERT,
+ psp->certfw_paddr, psp->certfw_buf_sz);
+ if (ret)
+ return ret;
+psp_start:
+ return psp_start(psp);
+}
+
+/*
+ * PSP requires host physical address to load firmware.
+ * Allocate a buffer, obtain its physical address, align, and copy data in.
+ */
+static void *psp_alloc_fw_buf(struct psp_device *psp, const void *fw_data,
+ u32 fw_size, u32 align, u32 *buf_sz,
+ u64 *paddr)
+{
+ u32 alloc_sz;
+ void *buffer;
+ u64 offset;
+
+ *buf_sz = ALIGN(fw_size, align);
+ alloc_sz = *buf_sz + align;
+
+ buffer = drmm_kmalloc(psp->ddev, alloc_sz, GFP_KERNEL);
+ if (!buffer)
+ return NULL;
+
+ *paddr = virt_to_phys(buffer);
+ offset = ALIGN(*paddr, align) - *paddr;
+ *paddr += offset;
+ memcpy(buffer + offset, fw_data, fw_size);
+
+ return buffer;
+}
+
+struct psp_device *aiem_psp_create(struct drm_device *ddev, struct psp_config *conf)
+{
+ struct psp_device *psp;
+
+ psp = drmm_kzalloc(ddev, sizeof(*psp), GFP_KERNEL);
+ if (!psp)
+ return NULL;
+
+ psp->ddev = ddev;
+ psp->fw_buffer = psp_alloc_fw_buf(psp, conf->fw_buf, conf->fw_size,
+ PSP_FW_ALIGN, &psp->fw_buf_sz,
+ &psp->fw_paddr);
+ if (!psp->fw_buffer)
+ return NULL;
+
+ if (!conf->certfw_size) {
+ drm_dbg(ddev, "no cert fw");
+ goto done;
+ }
+
+ /* CERT firmware */
+ psp->certfw_buffer = psp_alloc_fw_buf(psp, conf->certfw_buf,
+ conf->certfw_size, PSP_CFW_ALIGN,
+ &psp->certfw_buf_sz,
+ &psp->certfw_paddr);
+ if (!psp->certfw_buffer) {
+ drm_err(ddev, "no memory for cert fw buffer");
+ return NULL;
+ }
+
+done:
+ memcpy(&psp->conf, conf, sizeof(psp->conf));
+
+ return psp;
+}
diff --git a/drivers/accel/amdxdna/aie_smu.c b/drivers/accel/amdxdna/aie_smu.c
new file mode 100644
index 000000000000..62aea550aabc
--- /dev/null
+++ b/drivers/accel/amdxdna/aie_smu.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include "drm/amdxdna_accel.h"
+#include <drm/drm_device.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+#include <linux/iopoll.h>
+
+#include "aie.h"
+
+#define SMU_RESULT_OK 1
+
+/* SMU commands */
+#define AIE_SMU_POWER_ON 0x3
+#define AIE_SMU_POWER_OFF 0x4
+#define AIE_SMU_SET_MPNPUCLK_FREQ 0x5
+#define AIE_SMU_SET_HCLK_FREQ 0x6
+#define AIE_SMU_SET_SOFT_DPMLEVEL 0x7
+#define AIE_SMU_SET_HARD_DPMLEVEL 0x8
+
+#define SMU_REG(s, reg) ((s)->smu_regs[reg])
+
+struct smu_device {
+ struct drm_device *ddev;
+ struct smu_config conf;
+ void __iomem *smu_regs[SMU_MAX_REGS];
+};
+
+static int aie_smu_exec(struct smu_device *smu, u32 reg_cmd, u32 reg_arg, u32 *out)
+{
+ u32 resp;
+ int ret;
+
+ writel(0, SMU_REG(smu, SMU_RESP_REG));
+ writel(reg_arg, SMU_REG(smu, SMU_ARG_REG));
+ writel(reg_cmd, SMU_REG(smu, SMU_CMD_REG));
+
+ /* Clear and set SMU_INTR_REG to kick off */
+ writel(0, SMU_REG(smu, SMU_INTR_REG));
+ writel(1, SMU_REG(smu, SMU_INTR_REG));
+
+ ret = readx_poll_timeout(readl, SMU_REG(smu, SMU_RESP_REG), resp,
+ resp, AIE_INTERVAL, AIE_TIMEOUT);
+ if (ret) {
+ drm_err(smu->ddev, "smu cmd %d timed out", reg_cmd);
+ return ret;
+ }
+
+ if (out)
+ *out = readl(SMU_REG(smu, SMU_OUT_REG));
+
+ if (resp != SMU_RESULT_OK) {
+ drm_err(smu->ddev, "smu cmd %d failed, 0x%x", reg_cmd, resp);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int aie_smu_init(struct smu_device *smu)
+{
+ int ret;
+
+ /*
+ * Failing to set power off indicates an unrecoverable hardware or
+ * firmware error.
+ */
+ ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL);
+ if (ret) {
+ drm_err(smu->ddev, "Access power failed, ret %d", ret);
+ return ret;
+ }
+
+ ret = aie_smu_exec(smu, AIE_SMU_POWER_ON, 0, NULL);
+ if (ret) {
+ drm_err(smu->ddev, "Power on failed, ret %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void aie_smu_fini(struct smu_device *smu)
+{
+ int ret;
+
+ ret = aie_smu_exec(smu, AIE_SMU_POWER_OFF, 0, NULL);
+ if (ret)
+ drm_err(smu->ddev, "Power off failed, ret %d", ret);
+}
+
+int aie_smu_set_clocks(struct smu_device *smu, u32 *npuclk, u32 *hclk)
+{
+ int ret;
+
+ if (npuclk) {
+ ret = aie_smu_exec(smu, AIE_SMU_SET_MPNPUCLK_FREQ, *npuclk, npuclk);
+ if (ret) {
+ drm_err(smu->ddev, "Set mpnpu clock to %d failed, ret %d", *npuclk, ret);
+ return ret;
+ }
+ }
+
+ if (hclk) {
+ ret = aie_smu_exec(smu, AIE_SMU_SET_HCLK_FREQ, *hclk, hclk);
+ if (ret) {
+ drm_err(smu->ddev, "Set hclock to %d failed, ret %d",
+ *hclk, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int aie_smu_set_dpm(struct smu_device *smu, u32 dpm_level)
+{
+ int ret;
+
+ ret = aie_smu_exec(smu, AIE_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL);
+ if (ret) {
+ drm_err(smu->ddev, "Set hard dpm level %d failed, ret %d",
+ dpm_level, ret);
+ return ret;
+ }
+
+ ret = aie_smu_exec(smu, AIE_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL);
+ if (ret) {
+ drm_err(smu->ddev, "Set soft dpm level %d failed, ret %d",
+ dpm_level, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct smu_device *aiem_smu_create(struct drm_device *ddev, struct smu_config *conf)
+{
+ struct smu_device *smu;
+
+ smu = drmm_kzalloc(ddev, sizeof(*smu), GFP_KERNEL);
+ if (!smu)
+ return NULL;
+
+ smu->ddev = ddev;
+ memcpy(smu->smu_regs, conf->smu_regs, sizeof(smu->smu_regs));
+
+ return smu;
+}
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c
index ff6c3e8e5a15..2c2c21992c87 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.c
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -514,7 +514,6 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
goto unlock_srcu;
}
-
job->hwctx = hwctx;
job->mm = current->mm;
@@ -612,6 +611,8 @@ int amdxdna_drm_submit_cmd_ioctl(struct drm_device *dev, void *data, struct drm_
if (args->ext || args->ext_flags)
return -EINVAL;
+ trace_amdxdna_debug_point(current->comm, args->type, "job received");
+
switch (args->type) {
case AMDXDNA_CMD_SUBMIT_EXEC_BUF:
return amdxdna_drm_submit_execbuf(client, args);
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h
index a8557d7e8923..355798687376 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.h
+++ b/drivers/accel/amdxdna/amdxdna_ctx.h
@@ -119,6 +119,7 @@ struct amdxdna_hwctx {
container_of(j, struct amdxdna_sched_job, base)
enum amdxdna_job_opcode {
+ DEFAULT_IO,
SYNC_DEBUG_BO,
ATTACH_DEBUG_BO,
DETACH_DEBUG_BO,
diff --git a/drivers/accel/amdxdna/amdxdna_iommu.c b/drivers/accel/amdxdna/amdxdna_iommu.c
index 4626434d4180..5a9f06183487 100644
--- a/drivers/accel/amdxdna/amdxdna_iommu.c
+++ b/drivers/accel/amdxdna/amdxdna_iommu.c
@@ -40,7 +40,7 @@ int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
struct sg_table *sgt;
dma_addr_t dma_addr;
struct iova *iova;
- size_t size;
+ ssize_t size;
if (abo->type != AMDXDNA_BO_DEV_HEAP && abo->type != AMDXDNA_BO_SHMEM)
return 0;
@@ -65,7 +65,14 @@ int amdxdna_iommu_map_bo(struct amdxdna_dev *xdna, struct amdxdna_gem_obj *abo)
size = iommu_map_sgtable(xdna->domain, dma_addr, sgt,
IOMMU_READ | IOMMU_WRITE);
+ if (size < 0) {
+ XDNA_ERR(xdna, "iommu_map_sgtable failed: %zd", size);
+ __free_iova(&xdna->iovad, iova);
+ return size;
+ }
+
if (size < abo->mem.size) {
+ iommu_unmap(xdna->domain, dma_addr, size);
__free_iova(&xdna->iovad, iova);
return -ENXIO;
}
@@ -110,10 +117,12 @@ void *amdxdna_iommu_alloc(struct amdxdna_dev *xdna, size_t size, dma_addr_t *dma
iova_align(&xdna->iovad, size),
IOMMU_READ | IOMMU_WRITE, GFP_KERNEL);
if (ret)
- goto free_iova;
+ goto free_cpu_addr;
return cpu_addr;
+free_cpu_addr:
+ free_pages((unsigned long)cpu_addr, get_order(size));
free_iova:
__free_iova(&xdna->iovad, iova);
return ERR_PTR(ret);
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c
index e681a090752d..cc8865f4e79c 100644
--- a/drivers/accel/amdxdna/amdxdna_mailbox.c
+++ b/drivers/accel/amdxdna/amdxdna_mailbox.c
@@ -112,6 +112,18 @@ static u32 mailbox_reg_read(struct mailbox_channel *mb_chann, u32 mbox_reg)
return readl(ringbuf_addr);
}
+static inline void mailbox_irq_acknowledge(struct mailbox_channel *mb_chann)
+{
+ if (mb_chann->iohub_int_addr)
+ mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
+}
+
+static inline u32 mailbox_irq_status(struct mailbox_channel *mb_chann)
+{
+ return (mb_chann->iohub_int_addr) ?
+ mailbox_reg_read(mb_chann, mb_chann->iohub_int_addr) : 0;
+}
+
static inline void
mailbox_set_headptr(struct mailbox_channel *mb_chann, u32 headptr_val)
{
@@ -199,7 +211,6 @@ mailbox_send_msg(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg)
start_addr = mb_chann->res[CHAN_RES_X2I].rb_start_addr;
tmp_tail = tail + mb_msg->pkg_size;
-
check_again:
if (tail >= head && tmp_tail > ringbuf_size) {
write_addr = mb_chann->mb->res.ringbuf_base + start_addr + tail;
@@ -350,6 +361,7 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
int ret;
mb_chann = container_of(rx_work, struct mailbox_channel, rx_work);
+ trace_mbox_rx_worker(MAILBOX_NAME, mb_chann->msix_irq);
if (READ_ONCE(mb_chann->bad_state)) {
MB_ERR(mb_chann, "Channel in bad state, work aborted");
@@ -357,7 +369,7 @@ static void mailbox_rx_worker(struct work_struct *rx_work)
}
again:
- mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
+ mailbox_irq_acknowledge(mb_chann);
while (1) {
/*
@@ -382,7 +394,7 @@ again:
* the interrupt register to make sure there is not any new response
* before exiting.
*/
- if (mailbox_reg_read(mb_chann, mb_chann->iohub_int_addr))
+ if (mailbox_irq_status(mb_chann))
goto again;
}
@@ -485,6 +497,9 @@ free_chann:
void xdna_mailbox_free_channel(struct mailbox_channel *mb_chann)
{
+ if (!mb_chann)
+ return;
+
destroy_workqueue(mb_chann->work_q);
kfree(mb_chann);
}
@@ -520,7 +535,7 @@ xdna_mailbox_start_channel(struct mailbox_channel *mb_chann,
}
mb_chann->bad_state = false;
- mailbox_reg_write(mb_chann, mb_chann->iohub_int_addr, 0);
+ mailbox_irq_acknowledge(mb_chann);
MB_DBG(mb_chann, "Mailbox channel started (irq: %d)", mb_chann->msix_irq);
return 0;
@@ -531,6 +546,9 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann)
struct mailbox_msg *mb_msg;
unsigned long msg_id;
+ if (!mb_chann)
+ return;
+
/* Disable an irq and wait. This might sleep. */
free_irq(mb_chann->msix_irq, mb_chann);
@@ -538,7 +556,9 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann)
drain_workqueue(mb_chann->work_q);
/* We can clean up and release resources */
- xa_for_each(&mb_chann->chan_xa, msg_id, mb_msg)
+ xa_for_each_start(&mb_chann->chan_xa, msg_id, mb_msg, mb_chann->next_msgid)
+ mailbox_release_msg(mb_chann, mb_msg);
+ xa_for_each_range(&mb_chann->chan_xa, msg_id, mb_msg, 0, mb_chann->next_msgid - 1)
mailbox_release_msg(mb_chann, mb_msg);
xa_destroy(&mb_chann->chan_xa);
diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.h b/drivers/accel/amdxdna/amdxdna_mailbox.h
index 8b1e00945da4..2908404303ae 100644
--- a/drivers/accel/amdxdna/amdxdna_mailbox.h
+++ b/drivers/accel/amdxdna/amdxdna_mailbox.h
@@ -1,10 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Copyright (C) 2022-2024, Advanced Micro Devices, Inc.
+ * Copyright (C) 2022-2026, Advanced Micro Devices, Inc.
*/
-#ifndef _AIE2_MAILBOX_H_
-#define _AIE2_MAILBOX_H_
+#ifndef _AIE_MAILBOX_H_
+#define _AIE_MAILBOX_H_
struct mailbox;
struct mailbox_channel;
@@ -124,4 +124,4 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mailbox_chann);
int xdna_mailbox_send_msg(struct mailbox_channel *mailbox_chann,
const struct xdna_mailbox_msg *msg, u64 tx_timeout);
-#endif /* _AIE2_MAILBOX_ */
+#endif /* _AIE_MAILBOX_ */
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c
index b50a7d1f8a11..21eddfc538d0 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.c
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c
@@ -37,9 +37,10 @@ MODULE_FIRMWARE("amdnpu/17f0_11/npu_7.sbin");
* 0.6: Support preemption
* 0.7: Support getting power and utilization data
* 0.8: Support BO usage query
+ * 0.9: Add new device type AMDXDNA_DEV_TYPE_PF
*/
#define AMDXDNA_DRIVER_MAJOR 0
-#define AMDXDNA_DRIVER_MINOR 8
+#define AMDXDNA_DRIVER_MINOR 9
/*
* Bind the driver base on (vendor_id, device_id) pair and later use the
@@ -49,6 +50,8 @@ MODULE_FIRMWARE("amdnpu/17f0_11/npu_7.sbin");
static const struct pci_device_id pci_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1502) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x17f0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x17f2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1B0B) },
{0}
};
@@ -59,6 +62,8 @@ static const struct amdxdna_device_id amdxdna_ids[] = {
{ 0x17f0, 0x10, &dev_npu4_info },
{ 0x17f0, 0x11, &dev_npu5_info },
{ 0x17f0, 0x20, &dev_npu6_info },
+ { 0x17f2, 0x10, &dev_npu3_pf_info },
+ { 0x1B0B, 0x10, &dev_npu3_pf_info },
{0}
};
@@ -221,6 +226,35 @@ static const struct drm_ioctl_desc amdxdna_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(AMDXDNA_SET_STATE, amdxdna_drm_set_state_ioctl, DRM_ROOT_ONLY),
};
+static void amdxdna_show_fdinfo(struct drm_printer *p, struct drm_file *filp)
+{
+ struct amdxdna_client *client = filp->driver_priv;
+ size_t heap_usage, external_usage, internal_usage;
+ char *drv_name = filp->minor->dev->driver->name;
+
+ mutex_lock(&client->mm_lock);
+
+ heap_usage = client->heap_usage;
+ internal_usage = client->total_int_bo_usage;
+ external_usage = client->total_bo_usage - internal_usage;
+
+ mutex_unlock(&client->mm_lock);
+
+ /*
+ * Note for driver specific BO memory usage stat.
+ * Total memory alloc = amdxdna-internal-alloc + amdxdna-external-alloc
+ */
+ drm_fdinfo_print_size(p, drv_name, "heap", "alloc", heap_usage);
+ drm_fdinfo_print_size(p, drv_name, "internal", "alloc", internal_usage);
+ drm_fdinfo_print_size(p, drv_name, "external", "alloc", external_usage);
+ /*
+ * Note for DRM standard BO memory stat.
+ * drm-total-memory counts both DEV BO and HEAP BO
+ * drm-shared-memory counts BO imported
+ */
+ drm_show_memory_stats(p, filp);
+}
+
static const struct file_operations amdxdna_fops = {
.owner = THIS_MODULE,
.open = accel_open,
@@ -231,6 +265,7 @@ static const struct file_operations amdxdna_fops = {
.read = drm_read,
.llseek = noop_llseek,
.mmap = drm_gem_mmap,
+ .show_fdinfo = drm_show_fdinfo,
.fop_flags = FOP_UNSIGNED_OFFSET,
};
@@ -246,7 +281,7 @@ const struct drm_driver amdxdna_drm_drv = {
.postclose = amdxdna_drm_close,
.ioctls = amdxdna_drm_ioctls,
.num_ioctls = ARRAY_SIZE(amdxdna_drm_ioctls),
-
+ .show_fdinfo = amdxdna_show_fdinfo,
.gem_create_object = amdxdna_gem_create_shmem_object_cb,
.gem_prime_import = amdxdna_gem_prime_import,
};
@@ -365,12 +400,24 @@ static const struct dev_pm_ops amdxdna_pm_ops = {
RUNTIME_PM_OPS(amdxdna_pm_suspend, amdxdna_pm_resume, NULL)
};
+static int amdxdna_sriov_configure(struct pci_dev *pdev, int num_vfs)
+{
+ struct amdxdna_dev *xdna = pci_get_drvdata(pdev);
+
+ guard(mutex)(&xdna->dev_lock);
+ if (xdna->dev_info->ops->sriov_configure)
+ return xdna->dev_info->ops->sriov_configure(xdna, num_vfs);
+
+ return -ENOENT;
+}
+
static struct pci_driver amdxdna_pci_driver = {
.name = KBUILD_MODNAME,
.id_table = pci_ids,
.probe = amdxdna_probe,
.remove = amdxdna_remove,
.driver.pm = &amdxdna_pm_ops,
+ .sriov_configure = amdxdna_sriov_configure,
};
module_pci_driver(amdxdna_pci_driver);
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h
index 0661749917d6..bdd0dc83f92e 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.h
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h
@@ -55,6 +55,7 @@ struct amdxdna_dev_ops {
void (*fini)(struct amdxdna_dev *xdna);
int (*resume)(struct amdxdna_dev *xdna);
int (*suspend)(struct amdxdna_dev *xdna);
+ int (*sriov_configure)(struct amdxdna_dev *xdna, int num_vfs);
int (*hwctx_init)(struct amdxdna_hwctx *hwctx);
void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
@@ -64,6 +65,14 @@ struct amdxdna_dev_ops {
int (*get_aie_info)(struct amdxdna_client *client, struct amdxdna_drm_get_info *args);
int (*set_aie_state)(struct amdxdna_client *client, struct amdxdna_drm_set_state *args);
int (*get_array)(struct amdxdna_client *client, struct amdxdna_drm_get_array *args);
+ int (*get_dev_revision)(struct amdxdna_dev *xdna, u32 *rev);
+};
+
+struct amdxdna_fw_feature_tbl {
+ u64 features;
+ u32 major;
+ u32 max_minor;
+ u32 min_minor;
};
/*
@@ -81,8 +90,10 @@ struct amdxdna_dev_info {
u32 dev_mem_buf_shift;
u64 dev_mem_base;
size_t dev_mem_size;
- char *vbnv;
+ const char *default_vbnv;
+ const struct amdxdna_rev_vbnv *rev_vbnv_tbl;
const struct amdxdna_dev_priv *dev_priv;
+ const struct amdxdna_fw_feature_tbl *fw_feature_tbl;
const struct amdxdna_dev_ops *ops;
};
@@ -108,6 +119,8 @@ struct amdxdna_dev {
struct iommu_group *group;
struct iommu_domain *domain;
struct iova_domain iovad;
+ /* Accurate board name queried from firmware, or default_vbnv as fallback */
+ const char *vbnv;
};
/*
@@ -149,6 +162,7 @@ struct amdxdna_client {
/* Add device info below */
extern const struct amdxdna_dev_info dev_npu1_info;
+extern const struct amdxdna_dev_info dev_npu3_pf_info;
extern const struct amdxdna_dev_info dev_npu4_info;
extern const struct amdxdna_dev_info dev_npu5_info;
extern const struct amdxdna_dev_info dev_npu6_info;
diff --git a/drivers/accel/amdxdna/amdxdna_sysfs.c b/drivers/accel/amdxdna/amdxdna_sysfs.c
index f27e4ee960a0..d9e359ee8182 100644
--- a/drivers/accel/amdxdna/amdxdna_sysfs.c
+++ b/drivers/accel/amdxdna/amdxdna_sysfs.c
@@ -17,7 +17,10 @@ static ssize_t vbnv_show(struct device *dev, struct device_attribute *attr, char
{
struct amdxdna_dev *xdna = dev_get_drvdata(dev);
- return sprintf(buf, "%s\n", xdna->dev_info->vbnv);
+ if (!xdna->vbnv)
+ return sprintf(buf, "\n");
+
+ return sprintf(buf, "%s\n", xdna->vbnv);
}
static DEVICE_ATTR_RO(vbnv);
diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
index fb999aa25318..3769210c55cc 100644
--- a/drivers/accel/amdxdna/amdxdna_ubuf.c
+++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
@@ -125,6 +125,26 @@ static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = {
.vunmap = amdxdna_ubuf_vunmap,
};
+static int readonly_va_entry(struct amdxdna_drm_va_entry *va_ent)
+{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ int ret;
+
+ mmap_read_lock(mm);
+
+ vma = find_vma(mm, va_ent->vaddr);
+ if (!vma ||
+ vma->vm_start > va_ent->vaddr ||
+ vma->vm_end - va_ent->vaddr < va_ent->len)
+ ret = -ENOENT;
+ else
+ ret = vma->vm_flags & VM_WRITE ? 0 : 1;
+
+ mmap_read_unlock(mm);
+ return ret;
+}
+
struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
u32 num_entries, void __user *va_entries)
{
@@ -134,6 +154,7 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
struct amdxdna_ubuf_priv *ubuf;
u32 npages, start = 0;
struct dma_buf *dbuf;
+ bool readonly = true;
int i, ret;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
@@ -172,6 +193,10 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
ret = -EINVAL;
goto free_ent;
}
+
+ /* Pin pages as writable as long as not all entries are read-only. */
+ if (readonly && readonly_va_entry(&va_ent[i]) != 1)
+ readonly = false;
}
ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
@@ -194,20 +219,24 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
npages = va_ent[i].len >> PAGE_SHIFT;
ret = pin_user_pages_fast(va_ent[i].vaddr, npages,
- FOLL_WRITE | FOLL_LONGTERM,
+ (readonly ? 0 : FOLL_WRITE) | FOLL_LONGTERM,
&ubuf->pages[start]);
- if (ret < 0 || ret != npages) {
- ret = -ENOMEM;
+ if (ret >= 0) {
+ start += ret;
+ if (ret != npages) {
+ XDNA_ERR(xdna, "Partially pinned pages %d/%u", ret, npages);
+ ret = -ENOMEM;
+ goto destroy_pages;
+ }
+ } else {
XDNA_ERR(xdna, "Failed to pin pages ret %d", ret);
goto destroy_pages;
}
-
- start += ret;
}
exp_info.ops = &amdxdna_ubuf_dmabuf_ops;
exp_info.priv = ubuf;
- exp_info.flags = O_RDWR | O_CLOEXEC;
+ exp_info.flags = (readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC;
dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) {
diff --git a/drivers/accel/amdxdna/npu1_regs.c b/drivers/accel/amdxdna/npu1_regs.c
index 1320e924e548..4e48c030a69f 100644
--- a/drivers/accel/amdxdna/npu1_regs.c
+++ b/drivers/accel/amdxdna/npu1_regs.c
@@ -65,18 +65,39 @@ const struct dpm_clk_freq npu1_dpm_clk_table[] = {
{ 0 }
};
-static const struct aie2_fw_feature_tbl npu1_fw_feature_table[] = {
+static const struct amdxdna_fw_feature_tbl npu1_fw_feature_table[] = {
{ .major = 5, .min_minor = 7 },
{ .features = BIT_U64(AIE2_NPU_COMMAND), .major = 5, .min_minor = 8 },
{ 0 }
};
+static int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
+{
+ u32 npuclk, hclk;
+ int ret;
+
+ npuclk = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
+ hclk = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
+ ret = aie_smu_set_clocks(ndev->aie.smu_hdl, &npuclk, &hclk);
+ if (ret)
+ return ret;
+
+ ndev->npuclk_freq = npuclk;
+ ndev->hclk_freq = hclk;
+ ndev->max_tops = 2 * ndev->total_col;
+ ndev->curr_tops = ndev->max_tops * hclk / 1028;
+
+ XDNA_DBG(ndev->aie.xdna, "MP-NPU clock %d, H clock %d\n",
+ ndev->npuclk_freq, ndev->hclk_freq);
+ return 0;
+}
+
static const struct amdxdna_dev_priv npu1_dev_priv = {
.fw_path = "amdnpu/1502_00/",
.rt_config = npu1_default_rt_cfg,
.dpm_clk_tbl = npu1_dpm_clk_table,
- .fw_feature_tbl = npu1_fw_feature_table,
.col_align = COL_ALIGN_NONE,
+ .col_opc = 2048,
.mbox_dev_addr = NPU1_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU1_SRAM_BAR_BASE,
@@ -102,7 +123,7 @@ static const struct amdxdna_dev_priv npu1_dev_priv = {
DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU1_SMU, MPNPU_PUB_SCRATCH6),
DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU1_SMU, MPNPU_PUB_SCRATCH7),
},
- .hw_ops = {
+ .hw_ops = &(const struct aie2_hw_ops) {
.set_dpm = npu1_set_dpm,
},
};
@@ -117,8 +138,9 @@ const struct amdxdna_dev_info dev_npu1_info = {
.dev_mem_buf_shift = 15, /* 32 KiB aligned */
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
- .vbnv = "RyzenAI-npu1",
+ .default_vbnv = "RyzenAI-npu1",
.device_type = AMDXDNA_DEV_TYPE_KMQ,
.dev_priv = &npu1_dev_priv,
+ .fw_feature_tbl = npu1_fw_feature_table,
.ops = &aie2_ops,
};
diff --git a/drivers/accel/amdxdna/npu3_regs.c b/drivers/accel/amdxdna/npu3_regs.c
new file mode 100644
index 000000000000..acece0faddf2
--- /dev/null
+++ b/drivers/accel/amdxdna/npu3_regs.c
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026, Advanced Micro Devices, Inc.
+ */
+
+#include <drm/amdxdna_accel.h>
+#include <drm/drm_device.h>
+
+#include "aie4_pci.h"
+#include "amdxdna_pci_drv.h"
+
+#define NPU3_MBOX_BAR 0
+
+#define NPU3_MBOX_BUFFER_BAR 2
+#define NPU3_MBOX_INFO_OFF 0x0
+
+/* PCIe BAR Index for NPU3 */
+#define NPU3_REG_BAR_INDEX 0
+#define NPU3_PSP_BAR_INDEX 4
+#define NPU3_SMU_BAR_INDEX 5
+
+#define MMNPU_APERTURE3_BASE 0x3810000
+#define MMNPU_APERTURE4_BASE 0x3B10000
+
+#define NPU3_PSP_BAR_BASE MMNPU_APERTURE3_BASE
+#define NPU3_SMU_BAR_BASE MMNPU_APERTURE4_BASE
+
+#define MPASP_C2PMSG_123_ALT_1 0x3810AEC
+#define MPASP_C2PMSG_156_ALT_1 0x3810B70
+#define MPASP_C2PMSG_157_ALT_1 0x3810B74
+#define MPASP_C2PMSG_73_ALT_1 0x3810A24
+
+#define MP1_C2PMSG_59_ALT_1 0x3B109EC
+#define MP1_C2PMSG_61_ALT_1 0x3B109F4
+#define MP1_C2PMSG_60_ALT_1 0x3B109F0
+
+static const struct amdxdna_fw_feature_tbl npu3_fw_feature_table[] = {
+ { .major = 5, .min_minor = 10 },
+ { 0 }
+};
+
+static const struct amdxdna_dev_priv npu3_dev_priv = {
+ .npufw_path = "npu.dev.sbin",
+ .certfw_path = "cert.dev.sbin",
+ .mbox_bar = NPU3_MBOX_BAR,
+ .mbox_rbuf_bar = NPU3_MBOX_BUFFER_BAR,
+ .mbox_info_off = NPU3_MBOX_INFO_OFF,
+ .psp_regs_off = {
+ DEFINE_BAR_OFFSET(PSP_CMD_REG, NPU3_PSP, MPASP_C2PMSG_123_ALT_1),
+ DEFINE_BAR_OFFSET(PSP_ARG0_REG, NPU3_PSP, MPASP_C2PMSG_156_ALT_1),
+ DEFINE_BAR_OFFSET(PSP_ARG1_REG, NPU3_PSP, MPASP_C2PMSG_157_ALT_1),
+ DEFINE_BAR_OFFSET(PSP_ARG2_REG, NPU3_PSP, MPASP_C2PMSG_123_ALT_1),
+ DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU3_PSP, MPASP_C2PMSG_73_ALT_1),
+ DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU3_PSP, MPASP_C2PMSG_123_ALT_1),
+ DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU3_PSP, MPASP_C2PMSG_156_ALT_1),
+ /* npu3 doesn't use 8th pwaitmode register */
+ },
+ .smu_regs_off = {
+ DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU3_SMU, MP1_C2PMSG_59_ALT_1),
+ DEFINE_BAR_OFFSET(SMU_ARG_REG, NPU3_SMU, MP1_C2PMSG_61_ALT_1),
+ DEFINE_BAR_OFFSET(SMU_INTR_REG, NPU3_SMU, MMNPU_APERTURE4_BASE),
+ DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU3_SMU, MP1_C2PMSG_60_ALT_1),
+ DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU3_SMU, MP1_C2PMSG_61_ALT_1),
+ },
+};
+
+const struct amdxdna_dev_info dev_npu3_pf_info = {
+ .mbox_bar = NPU3_MBOX_BAR,
+ .sram_bar = NPU3_MBOX_BUFFER_BAR,
+ .psp_bar = NPU3_PSP_BAR_INDEX,
+ .smu_bar = NPU3_SMU_BAR_INDEX,
+ .default_vbnv = "RyzenAI-npu3-pf",
+ .device_type = AMDXDNA_DEV_TYPE_PF,
+ .dev_priv = &npu3_dev_priv,
+ .fw_feature_tbl = npu3_fw_feature_table,
+ .ops = &aie4_ops,
+};
diff --git a/drivers/accel/amdxdna/npu4_regs.c b/drivers/accel/amdxdna/npu4_regs.c
index 619bff042e52..eddc31803a50 100644
--- a/drivers/accel/amdxdna/npu4_regs.c
+++ b/drivers/accel/amdxdna/npu4_regs.c
@@ -6,6 +6,7 @@
#include <drm/amdxdna_accel.h>
#include <drm/drm_device.h>
#include <drm/gpu_scheduler.h>
+#include <linux/amd-pmf-io.h>
#include <linux/bits.h>
#include <linux/sizes.h>
@@ -63,6 +64,8 @@
#define NPU4_SMU_BAR_BASE MMNPU_APERTURE4_BASE
#define NPU4_SRAM_BAR_BASE MMNPU_APERTURE1_BASE
+#define NPU4_DPM_TOPS(ndev, hclk) (4096 * (ndev)->total_col * (hclk) / 1000000)
+
const struct rt_config npu4_default_rt_cfg[] = {
{ 5, 1, AIE2_RT_CFG_INIT }, /* PDI APP LOAD MODE */
{ 10, 1, AIE2_RT_CFG_INIT }, /* DEBUG BUF */
@@ -88,22 +91,76 @@ const struct dpm_clk_freq npu4_dpm_clk_table[] = {
{ 0 }
};
-const struct aie2_fw_feature_tbl npu4_fw_feature_table[] = {
+const struct amdxdna_fw_feature_tbl npu4_fw_feature_table[] = {
{ .major = 6, .min_minor = 12 },
- { .features = BIT_U64(AIE2_NPU_COMMAND), .major = 6, .min_minor = 15 },
{ .features = BIT_U64(AIE2_PREEMPT), .major = 6, .min_minor = 12 },
{ .features = BIT_U64(AIE2_TEMPORAL_ONLY), .major = 6, .min_minor = 12 },
+ { .features = BIT_U64(AIE2_NPU_COMMAND), .major = 6, .min_minor = 15 },
+ { .features = BIT_U64(AIE2_UPDATE_PROPERTY), .major = 6, .min_minor = 15 },
{ .features = BIT_U64(AIE2_APP_HEALTH), .major = 6, .min_minor = 18 },
+ { .features = BIT_U64(AIE2_GET_DEV_REVISION), .major = 6, .min_minor = 24 },
{ .features = AIE2_ALL_FEATURES, .major = 7 },
{ 0 }
};
+static int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level)
+{
+ int ret;
+
+ ret = aie_smu_set_dpm(ndev->aie.smu_hdl, dpm_level);
+ if (ret)
+ return ret;
+
+ ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk;
+ ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk;
+ ndev->max_tops = NPU4_DPM_TOPS(ndev, ndev->priv->dpm_clk_tbl[ndev->max_dpm_level].hclk);
+ ndev->curr_tops = NPU4_DPM_TOPS(ndev, ndev->hclk_freq);
+
+ XDNA_DBG(ndev->aie.xdna, "MP-NPU clock %d, H clock %d\n",
+ ndev->npuclk_freq, ndev->hclk_freq);
+
+ return 0;
+}
+
+static int npu4_update_counters(struct amdxdna_dev_hdl *ndev)
+{
+ struct amd_pmf_npu_metrics npu_metrics;
+ int ret;
+
+ ret = AIE2_GET_PMF_NPU_METRICS(&npu_metrics);
+ if (ret)
+ return ret;
+
+ ndev->npuclk_freq = npu_metrics.mpnpuclk_freq;
+ ndev->hclk_freq = npu_metrics.npuclk_freq;
+ ndev->curr_tops = NPU4_DPM_TOPS(ndev, ndev->hclk_freq);
+
+ return 0;
+}
+
+const struct aie2_hw_ops npu4_hw_ops = {
+ .set_dpm = npu4_set_dpm,
+ .update_counters = npu4_update_counters,
+};
+
+const struct amdxdna_rev_vbnv npu4_rev_vbnv_tbl[] = {
+ { AIE2_DEV_REVISION_STXA, "NPU Strix" },
+ { AIE2_DEV_REVISION_STXB, "NPU Strix" },
+ { AIE2_DEV_REVISION_KRK1, "NPU Krackan 1" },
+ { AIE2_DEV_REVISION_KRK2, "NPU Krackan 2" },
+ { AIE2_DEV_REVISION_HALO, "NPU Strix Halo" },
+ { AIE2_DEV_REVISION_GPT1, "NPU Gorgon Point 1" },
+ { AIE2_DEV_REVISION_GPT2, "NPU Gorgon Point 2" },
+ { AIE2_DEV_REVISION_GPT3, "NPU Gorgon Point 3" },
+ { 0 }
+};
+
static const struct amdxdna_dev_priv npu4_dev_priv = {
.fw_path = "amdnpu/17f0_10/",
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
- .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
+ .col_opc = 4096,
.mbox_dev_addr = NPU4_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU4_SRAM_BAR_BASE,
@@ -129,9 +186,7 @@ static const struct amdxdna_dev_priv npu4_dev_priv = {
DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU4_SMU, MP1_C2PMSG_61),
DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU4_SMU, MP1_C2PMSG_60),
},
- .hw_ops = {
- .set_dpm = npu4_set_dpm,
- },
+ .hw_ops = &npu4_hw_ops
};
const struct amdxdna_dev_info dev_npu4_info = {
@@ -144,8 +199,10 @@ const struct amdxdna_dev_info dev_npu4_info = {
.dev_mem_buf_shift = 15, /* 32 KiB aligned */
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
- .vbnv = "RyzenAI-npu4",
+ .default_vbnv = "RyzenAI-npu4",
.device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .rev_vbnv_tbl = npu4_rev_vbnv_tbl,
.dev_priv = &npu4_dev_priv,
+ .fw_feature_tbl = npu4_fw_feature_table,
.ops = &aie2_ops, /* NPU4 can share NPU1's callback */
};
diff --git a/drivers/accel/amdxdna/npu5_regs.c b/drivers/accel/amdxdna/npu5_regs.c
index c0ac5daf32ee..a9102978e4a8 100644
--- a/drivers/accel/amdxdna/npu5_regs.c
+++ b/drivers/accel/amdxdna/npu5_regs.c
@@ -66,8 +66,8 @@ static const struct amdxdna_dev_priv npu5_dev_priv = {
.fw_path = "amdnpu/17f0_11/",
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
- .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
+ .col_opc = 4096,
.mbox_dev_addr = NPU5_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU5_SRAM_BAR_BASE,
@@ -93,9 +93,7 @@ static const struct amdxdna_dev_priv npu5_dev_priv = {
DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU5_SMU, MP1_C2PMSG_61),
DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU5_SMU, MP1_C2PMSG_60),
},
- .hw_ops = {
- .set_dpm = npu4_set_dpm,
- },
+ .hw_ops = &npu4_hw_ops
};
const struct amdxdna_dev_info dev_npu5_info = {
@@ -108,8 +106,10 @@ const struct amdxdna_dev_info dev_npu5_info = {
.dev_mem_buf_shift = 15, /* 32 KiB aligned */
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
- .vbnv = "RyzenAI-npu5",
+ .default_vbnv = "RyzenAI-npu5",
.device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .rev_vbnv_tbl = npu4_rev_vbnv_tbl,
.dev_priv = &npu5_dev_priv,
+ .fw_feature_tbl = npu4_fw_feature_table,
.ops = &aie2_ops,
};
diff --git a/drivers/accel/amdxdna/npu6_regs.c b/drivers/accel/amdxdna/npu6_regs.c
index ce591ed0d483..e0db3a09740b 100644
--- a/drivers/accel/amdxdna/npu6_regs.c
+++ b/drivers/accel/amdxdna/npu6_regs.c
@@ -66,8 +66,8 @@ static const struct amdxdna_dev_priv npu6_dev_priv = {
.fw_path = "amdnpu/17f0_10/",
.rt_config = npu4_default_rt_cfg,
.dpm_clk_tbl = npu4_dpm_clk_table,
- .fw_feature_tbl = npu4_fw_feature_table,
.col_align = COL_ALIGN_NATURE,
+ .col_opc = 4096,
.mbox_dev_addr = NPU6_MBOX_BAR_BASE,
.mbox_size = 0, /* Use BAR size */
.sram_dev_addr = NPU6_SRAM_BAR_BASE,
@@ -93,9 +93,7 @@ static const struct amdxdna_dev_priv npu6_dev_priv = {
DEFINE_BAR_OFFSET(SMU_RESP_REG, NPU6_SMU, MP1_C2PMSG_61),
DEFINE_BAR_OFFSET(SMU_OUT_REG, NPU6_SMU, MP1_C2PMSG_60),
},
- .hw_ops = {
- .set_dpm = npu4_set_dpm,
- },
+ .hw_ops = &npu4_hw_ops
};
@@ -109,8 +107,10 @@ const struct amdxdna_dev_info dev_npu6_info = {
.dev_mem_buf_shift = 15, /* 32 KiB aligned */
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
- .vbnv = "RyzenAI-npu6",
+ .default_vbnv = "RyzenAI-npu6",
.device_type = AMDXDNA_DEV_TYPE_KMQ,
+ .rev_vbnv_tbl = npu4_rev_vbnv_tbl,
.dev_priv = &npu6_dev_priv,
+ .fw_feature_tbl = npu4_fw_feature_table,
.ops = &aie2_ops,
};
diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c
index ec85f4156744..418463c03bfb 100644
--- a/drivers/accel/ethosu/ethosu_job.c
+++ b/drivers/accel/ethosu/ethosu_job.c
@@ -296,7 +296,6 @@ int ethosu_job_init(struct ethosu_device *edev)
struct device *dev = edev->base.dev;
struct drm_sched_init_args args = {
.ops = &ethosu_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
.name = dev_name(dev),
diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c
index 37f95a0551ed..81f0b1f8f5a6 100644
--- a/drivers/accel/ivpu/ivpu_hw_ip.c
+++ b/drivers/accel/ivpu/ivpu_hw_ip.c
@@ -308,26 +308,26 @@ static void pwr_island_trickle_drive_40xx(struct ivpu_device *vdev, bool enable)
static void pwr_island_drive_37xx(struct ivpu_device *vdev, bool enable)
{
- u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0);
+ u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0);
if (enable)
- val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
+ val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, MSS_CPU, val);
else
- val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
+ val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, MSS_CPU, val);
- REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
+ REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
}
static void pwr_island_drive_40xx(struct ivpu_device *vdev, bool enable)
{
- u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0);
+ u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0);
if (enable)
- val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, MSS_CPU, val);
+ val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
else
- val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, MSS_CPU, val);
+ val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val);
- REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
+ REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val);
}
static void pwr_island_enable(struct ivpu_device *vdev)
diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c
index 4d787f77ce41..40e6d262ef21 100644
--- a/drivers/accel/qaic/mhi_controller.c
+++ b/drivers/accel/qaic/mhi_controller.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/delay.h>
#include <linux/err.h>
diff --git a/drivers/accel/qaic/mhi_controller.h b/drivers/accel/qaic/mhi_controller.h
index 8939f6ae185e..c1940c839246 100644
--- a/drivers/accel/qaic/mhi_controller.h
+++ b/drivers/accel/qaic/mhi_controller.h
@@ -1,8 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#ifndef MHICONTROLLERQAIC_H_
#define MHICONTROLLERQAIC_H_
diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h
index fa7a8155658c..83948358ada1 100644
--- a/drivers/accel/qaic/qaic.h
+++ b/drivers/accel/qaic/qaic.h
@@ -1,8 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#ifndef _QAIC_H_
#define _QAIC_H_
diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c
index 43f84d438960..bb94d3556904 100644
--- a/drivers/accel/qaic/qaic_control.c
+++ b/drivers/accel/qaic/qaic_control.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <asm/byteorder.h>
#include <linux/completion.h>
diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c
index 95300c2f7d8a..b27c232021bd 100644
--- a/drivers/accel/qaic/qaic_data.c
+++ b/drivers/accel/qaic/qaic_data.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/bitfield.h>
#include <linux/bits.h>
diff --git a/drivers/accel/qaic/qaic_debugfs.c b/drivers/accel/qaic/qaic_debugfs.c
index 8dc4fe5bb560..95c78e12dd61 100644
--- a/drivers/accel/qaic/qaic_debugfs.c
+++ b/drivers/accel/qaic/qaic_debugfs.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/debugfs.h>
#include <linux/device.h>
@@ -27,6 +27,8 @@
struct bootlog_msg {
/* Buffer for bootlog messages */
char str[BOOTLOG_MSG_SIZE];
+ /* Length of bootlog message */
+ size_t len;
/* Root struct of device, used to access device resources */
struct qaic_device *qdev;
/* Work struct to schedule work coming on QAIC_LOGGING channel */
@@ -46,18 +48,15 @@ static int bootlog_show(struct seq_file *s, void *unused)
{
struct bootlog_page *page;
struct qaic_device *qdev;
- void *page_end;
+ size_t len;
void *log;
qdev = s->private;
mutex_lock(&qdev->bootlog_mutex);
list_for_each_entry(page, &qdev->bootlog, node) {
log = page + 1;
- page_end = (void *)page + page->offset;
- while (log < page_end) {
- seq_printf(s, "%s", (char *)log);
- log += strlen(log) + 1;
- }
+ len = page->offset - sizeof(*page);
+ seq_write(s, log, len);
}
mutex_unlock(&qdev->bootlog_mutex);
@@ -182,15 +181,14 @@ static void bootlog_commit(struct qaic_device *qdev, unsigned int size)
static void bootlog_log(struct work_struct *work)
{
struct bootlog_msg *msg = container_of(work, struct bootlog_msg, work);
- unsigned int len = strlen(msg->str) + 1;
struct qaic_device *qdev = msg->qdev;
void *log;
mutex_lock(&qdev->bootlog_mutex);
- log = bootlog_get_space(qdev, len);
+ log = bootlog_get_space(qdev, msg->len);
if (log) {
- memcpy(log, msg, len);
- bootlog_commit(qdev, len);
+ memcpy(log, msg, msg->len);
+ bootlog_commit(qdev, msg->len);
}
mutex_unlock(&qdev->bootlog_mutex);
@@ -265,14 +263,18 @@ static void qaic_bootlog_mhi_dl_xfer_cb(struct mhi_device *mhi_dev, struct mhi_r
{
struct qaic_device *qdev = dev_get_drvdata(&mhi_dev->dev);
struct bootlog_msg *msg = mhi_result->buf_addr;
+ int status = mhi_result->transaction_status;
- if (mhi_result->transaction_status) {
+ if (status && status != -EOVERFLOW) {
devm_kfree(&qdev->pdev->dev, msg);
return;
}
- /* Force a null at the end of the transferred string */
- msg->str[mhi_result->bytes_xferd - 1] = 0;
+ msg->len = mhi_result->bytes_xferd;
+
+ /* Exclude trailing null to normalize AIC100/AIC200 line endings */
+ if (msg->len && msg->str[msg->len - 1] == '\0')
+ msg->len--;
queue_work(qdev->bootlog_wq, &msg->work);
}
diff --git a/drivers/accel/qaic/qaic_debugfs.h b/drivers/accel/qaic/qaic_debugfs.h
index 05e74f84cf9f..59a002bab07c 100644
--- a/drivers/accel/qaic/qaic_debugfs.h
+++ b/drivers/accel/qaic/qaic_debugfs.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#ifndef __QAIC_DEBUGFS_H__
#define __QAIC_DEBUGFS_H__
diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c
index 63fb8c7b4abc..1c7c57dabcd6 100644
--- a/drivers/accel/qaic/qaic_drv.c
+++ b/drivers/accel/qaic/qaic_drv.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/delay.h>
#include <linux/dma-mapping.h>
diff --git a/drivers/accel/qaic/qaic_ras.c b/drivers/accel/qaic/qaic_ras.c
index cc0b75461e1a..6a962c5cf048 100644
--- a/drivers/accel/qaic/qaic_ras.c
+++ b/drivers/accel/qaic/qaic_ras.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <asm/byteorder.h>
diff --git a/drivers/accel/qaic/qaic_ras.h b/drivers/accel/qaic/qaic_ras.h
index d44a4eeeb060..7b3fe9585ed9 100644
--- a/drivers/accel/qaic/qaic_ras.h
+++ b/drivers/accel/qaic/qaic_ras.h
@@ -1,4 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
+
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
#ifndef __QAIC_RAS_H__
diff --git a/drivers/accel/qaic/qaic_ssr.c b/drivers/accel/qaic/qaic_ssr.c
index a5bb6078824b..a98928654959 100644
--- a/drivers/accel/qaic/qaic_ssr.c
+++ b/drivers/accel/qaic/qaic_ssr.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */
-/* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <asm/byteorder.h>
#include <drm/drm_file.h>
diff --git a/drivers/accel/qaic/qaic_ssr.h b/drivers/accel/qaic/qaic_ssr.h
index 97ccff305750..af074edbf967 100644
--- a/drivers/accel/qaic/qaic_ssr.h
+++ b/drivers/accel/qaic/qaic_ssr.h
@@ -1,8 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (c) 2020, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#ifndef __QAIC_SSR_H__
#define __QAIC_SSR_H__
diff --git a/drivers/accel/qaic/qaic_timesync.c b/drivers/accel/qaic/qaic_timesync.c
index 939462b9958d..9faf71f47bdc 100644
--- a/drivers/accel/qaic/qaic_timesync.c
+++ b/drivers/accel/qaic/qaic_timesync.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */
+
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/io.h>
#include <linux/kernel.h>
diff --git a/drivers/accel/qaic/qaic_timesync.h b/drivers/accel/qaic/qaic_timesync.h
index 77b9c2b55057..6aeda1d62a35 100644
--- a/drivers/accel/qaic/qaic_timesync.h
+++ b/drivers/accel/qaic/qaic_timesync.h
@@ -1,7 +1,6 @@
-/* SPDX-License-Identifier: GPL-2.0-only
- *
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#ifndef __QAIC_TIMESYNC_H__
#define __QAIC_TIMESYNC_H__
diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c
index fd3c3b2d1fd3..9fea294e1d7b 100644
--- a/drivers/accel/qaic/sahara.c
+++ b/drivers/accel/qaic/sahara.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#include <linux/devcoredump.h>
#include <linux/firmware.h>
diff --git a/drivers/accel/qaic/sahara.h b/drivers/accel/qaic/sahara.h
index 640208acc0d1..08037281c80e 100644
--- a/drivers/accel/qaic/sahara.h
+++ b/drivers/accel/qaic/sahara.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. */
+/* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */
#ifndef __SAHARA_H__
#define __SAHARA_H__
diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c
index ac51bff39833..2f1861f960cc 100644
--- a/drivers/accel/rocket/rocket_job.c
+++ b/drivers/accel/rocket/rocket_job.c
@@ -437,7 +437,6 @@ int rocket_job_init(struct rocket_core *core)
{
struct drm_sched_init_args args = {
.ops = &rocket_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
.name = dev_name(core->dev),
diff --git a/drivers/dma-buf/.kunitconfig b/drivers/dma-buf/.kunitconfig
new file mode 100644
index 000000000000..1ce5fb7e6cf9
--- /dev/null
+++ b/drivers/dma-buf/.kunitconfig
@@ -0,0 +1,2 @@
+CONFIG_KUNIT=y
+CONFIG_DMABUF_KUNIT_TEST=y
diff --git a/drivers/dma-buf/Kconfig b/drivers/dma-buf/Kconfig
index 8d4f2f89f24e..7efc0f0d0712 100644
--- a/drivers/dma-buf/Kconfig
+++ b/drivers/dma-buf/Kconfig
@@ -49,10 +49,13 @@ config DMABUF_DEBUG
exporters. Specifically it validates that importers do not peek at the
underlying struct page when they import a buffer.
-config DMABUF_SELFTESTS
- tristate "Selftests for the dma-buf interfaces"
- default n
- depends on DMA_SHARED_BUFFER
+config DMABUF_KUNIT_TEST
+ tristate "KUnit tests for DMA-BUF" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ select DMA_SHARED_BUFFER
+ default KUNIT_ALL_TESTS
+ help
+ Enable kunit tests for DMA-BUF
menuconfig DMABUF_HEAPS
bool "DMA-BUF Userland Memory Heaps"
diff --git a/drivers/dma-buf/Makefile b/drivers/dma-buf/Makefile
index 7a85565d906b..b25d7550bacf 100644
--- a/drivers/dma-buf/Makefile
+++ b/drivers/dma-buf/Makefile
@@ -7,11 +7,10 @@ obj-$(CONFIG_SYNC_FILE) += sync_file.o
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
obj-$(CONFIG_UDMABUF) += udmabuf.o
-dmabuf_selftests-y := \
- selftest.o \
+dmabuf_kunit-y := \
st-dma-fence.o \
st-dma-fence-chain.o \
st-dma-fence-unwrap.o \
st-dma-resv.o
-obj-$(CONFIG_DMABUF_SELFTESTS) += dmabuf_selftests.o
+obj-$(CONFIG_DMABUF_KUNIT_TEST) += dmabuf_kunit.o
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index a2aa82f4eedd..b3bfa6943a8e 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -363,6 +363,8 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
&fence->flags)))
return;
+ trace_dma_fence_signaled(fence);
+
/*
* When neither a release nor a wait operation is specified set the ops
* pointer to NULL to allow the fence structure to become independent
@@ -377,7 +379,6 @@ void dma_fence_signal_timestamp_locked(struct dma_fence *fence,
fence->timestamp = timestamp;
set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags);
- trace_dma_fence_signaled(fence);
list_for_each_entry_safe(cur, tmp, &cb_list, node) {
INIT_LIST_HEAD(&cur->node);
diff --git a/drivers/dma-buf/selftest.c b/drivers/dma-buf/selftest.c
deleted file mode 100644
index c60b6944b4bd..000000000000
--- a/drivers/dma-buf/selftest.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-
-/*
- * Copyright © 2019 Intel Corporation
- */
-
-#include <linux/compiler.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched/signal.h>
-#include <linux/slab.h>
-
-#include "selftest.h"
-
-enum {
-#define selftest(n, func) __idx_##n,
-#include "selftests.h"
-#undef selftest
-};
-
-#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f },
-static struct selftest {
- bool enabled;
- const char *name;
- int (*func)(void);
-} selftests[] = {
-#include "selftests.h"
-};
-#undef selftest
-
-/* Embed the line number into the parameter name so that we can order tests */
-#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n))
-#define selftest_0(n, func, id) \
-module_param_named(id, selftests[__idx_##n].enabled, bool, 0400);
-#define selftest(n, func) selftest_0(n, func, param(n))
-#include "selftests.h"
-#undef selftest
-
-int __sanitycheck__(void)
-{
- pr_debug("Hello World!\n");
- return 0;
-}
-
-static char *__st_filter;
-
-static bool apply_subtest_filter(const char *caller, const char *name)
-{
- char *filter, *sep, *tok;
- bool result = true;
-
- filter = kstrdup(__st_filter, GFP_KERNEL);
- for (sep = filter; (tok = strsep(&sep, ","));) {
- bool allow = true;
- char *sl;
-
- if (*tok == '!') {
- allow = false;
- tok++;
- }
-
- if (*tok == '\0')
- continue;
-
- sl = strchr(tok, '/');
- if (sl) {
- *sl++ = '\0';
- if (strcmp(tok, caller)) {
- if (allow)
- result = false;
- continue;
- }
- tok = sl;
- }
-
- if (strcmp(tok, name)) {
- if (allow)
- result = false;
- continue;
- }
-
- result = allow;
- break;
- }
- kfree(filter);
-
- return result;
-}
-
-int
-__subtests(const char *caller, const struct subtest *st, int count, void *data)
-{
- int err;
-
- for (; count--; st++) {
- cond_resched();
- if (signal_pending(current))
- return -EINTR;
-
- if (!apply_subtest_filter(caller, st->name))
- continue;
-
- pr_info("dma-buf: Running %s/%s\n", caller, st->name);
-
- err = st->func(data);
- if (err && err != -EINTR) {
- pr_err("dma-buf/%s: %s failed with error %d\n",
- caller, st->name, err);
- return err;
- }
- }
-
- return 0;
-}
-
-static void set_default_test_all(struct selftest *st, unsigned long count)
-{
- unsigned long i;
-
- for (i = 0; i < count; i++)
- if (st[i].enabled)
- return;
-
- for (i = 0; i < count; i++)
- st[i].enabled = true;
-}
-
-static int run_selftests(struct selftest *st, unsigned long count)
-{
- int err = 0;
-
- set_default_test_all(st, count);
-
- /* Tests are listed in natural order in selftests.h */
- for (; count--; st++) {
- if (!st->enabled)
- continue;
-
- pr_info("dma-buf: Running %s\n", st->name);
- err = st->func();
- if (err)
- break;
- }
-
- if (WARN(err > 0 || err == -ENOTTY,
- "%s returned %d, conflicting with selftest's magic values!\n",
- st->name, err))
- err = -1;
-
- return err;
-}
-
-static int __init st_init(void)
-{
- return run_selftests(selftests, ARRAY_SIZE(selftests));
-}
-
-static void __exit st_exit(void)
-{
-}
-
-module_param_named(st_filter, __st_filter, charp, 0400);
-module_init(st_init);
-module_exit(st_exit);
-
-MODULE_DESCRIPTION("Self-test harness for dma-buf");
-MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/dma-buf/selftest.h b/drivers/dma-buf/selftest.h
deleted file mode 100644
index 45793aff6142..000000000000
--- a/drivers/dma-buf/selftest.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-/*
- * Copyright © 2019 Intel Corporation
- */
-
-#ifndef __SELFTEST_H__
-#define __SELFTEST_H__
-
-#include <linux/compiler.h>
-
-#define selftest(name, func) int func(void);
-#include "selftests.h"
-#undef selftest
-
-struct subtest {
- int (*func)(void *data);
- const char *name;
-};
-
-int __subtests(const char *caller,
- const struct subtest *st,
- int count,
- void *data);
-#define subtests(T, data) \
- __subtests(__func__, T, ARRAY_SIZE(T), data)
-
-#define SUBTEST(x) { x, #x }
-
-#endif /* __SELFTEST_H__ */
diff --git a/drivers/dma-buf/selftests.h b/drivers/dma-buf/selftests.h
deleted file mode 100644
index 851965867d9c..000000000000
--- a/drivers/dma-buf/selftests.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* List each unit test as selftest(name, function)
- *
- * The name is used as both an enum and expanded as subtest__name to create
- * a module parameter. It must be unique and legal for a C identifier.
- *
- * The function should be of type int function(void). It may be conditionally
- * compiled using #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST).
- *
- * Tests are executed in order by igt/dmabuf_selftest
- */
-selftest(sanitycheck, __sanitycheck__) /* keep first (igt selfcheck) */
-selftest(dma_fence, dma_fence)
-selftest(dma_fence_chain, dma_fence_chain)
-selftest(dma_fence_unwrap, dma_fence_unwrap)
-selftest(dma_resv, dma_resv)
diff --git a/drivers/dma-buf/st-dma-fence-chain.c b/drivers/dma-buf/st-dma-fence-chain.c
index 821023dd34df..a3023d3fedc9 100644
--- a/drivers/dma-buf/st-dma-fence-chain.c
+++ b/drivers/dma-buf/st-dma-fence-chain.c
@@ -4,6 +4,7 @@
* Copyright © 2019 Intel Corporation
*/
+#include <kunit/test.h>
#include <linux/delay.h>
#include <linux/dma-fence.h>
#include <linux/dma-fence-chain.h>
@@ -15,8 +16,6 @@
#include <linux/spinlock.h>
#include <linux/random.h>
-#include "selftest.h"
-
#define CHAIN_SZ (4 << 10)
static struct kmem_cache *slab_fences;
@@ -74,27 +73,23 @@ static struct dma_fence *mock_chain(struct dma_fence *prev,
return &f->base;
}
-static int sanitycheck(void *arg)
+static void test_sanitycheck(struct kunit *test)
{
struct dma_fence *f, *chain;
- int err = 0;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
chain = mock_chain(NULL, f, 1);
if (chain)
dma_fence_enable_sw_signaling(chain);
else
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to create chain");
dma_fence_signal(f);
dma_fence_put(f);
dma_fence_put(chain);
-
- return err;
}
struct fence_chains {
@@ -176,7 +171,7 @@ static void fence_chains_fini(struct fence_chains *fc)
kvfree(fc->chains);
}
-static int find_seqno(void *arg)
+static void test_find_seqno(struct kunit *test)
{
struct fence_chains fc;
struct dma_fence *fence;
@@ -184,14 +179,13 @@ static int find_seqno(void *arg)
int i;
err = fence_chains_init(&fc, 64, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
fence = dma_fence_get(fc.tail);
err = dma_fence_chain_find_seqno(&fence, 0);
dma_fence_put(fence);
if (err) {
- pr_err("Reported %d for find_seqno(0)!\n", err);
+ KUNIT_FAIL(test, "Reported %d for find_seqno(0)!", err);
goto err;
}
@@ -200,14 +194,13 @@ static int find_seqno(void *arg)
err = dma_fence_chain_find_seqno(&fence, i + 1);
dma_fence_put(fence);
if (err) {
- pr_err("Reported %d for find_seqno(%d:%d)!\n",
- err, fc.chain_length + 1, i + 1);
+ KUNIT_FAIL(test, "Reported %d for find_seqno(%d:%d)!",
+ err, fc.chain_length + 1, i + 1);
goto err;
}
if (fence != fc.chains[i]) {
- pr_err("Incorrect fence reported by find_seqno(%d:%d)\n",
- fc.chain_length + 1, i + 1);
- err = -EINVAL;
+ KUNIT_FAIL(test, "Incorrect fence reported by find_seqno(%d:%d)",
+ fc.chain_length + 1, i + 1);
goto err;
}
@@ -215,12 +208,11 @@ static int find_seqno(void *arg)
err = dma_fence_chain_find_seqno(&fence, i + 1);
dma_fence_put(fence);
if (err) {
- pr_err("Error reported for finding self\n");
+ KUNIT_FAIL(test, "Error reported for finding self");
goto err;
}
if (fence != fc.chains[i]) {
- pr_err("Incorrect fence reported by find self\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Incorrect fence reported by find self");
goto err;
}
@@ -228,9 +220,8 @@ static int find_seqno(void *arg)
err = dma_fence_chain_find_seqno(&fence, i + 2);
dma_fence_put(fence);
if (!err) {
- pr_err("Error not reported for future fence: find_seqno(%d:%d)!\n",
- i + 1, i + 2);
- err = -EINVAL;
+ KUNIT_FAIL(test, "Error not reported for future fence: find_seqno(%d:%d)!",
+ i + 1, i + 2);
goto err;
}
@@ -238,31 +229,28 @@ static int find_seqno(void *arg)
err = dma_fence_chain_find_seqno(&fence, i);
dma_fence_put(fence);
if (err) {
- pr_err("Error reported for previous fence!\n");
+ KUNIT_FAIL(test, "Error reported for previous fence!");
goto err;
}
if (i > 0 && fence != fc.chains[i - 1]) {
- pr_err("Incorrect fence reported by find_seqno(%d:%d)\n",
- i + 1, i);
- err = -EINVAL;
+ KUNIT_FAIL(test, "Incorrect fence reported by find_seqno(%d:%d)",
+ i + 1, i);
goto err;
}
}
err:
fence_chains_fini(&fc);
- return err;
}
-static int find_signaled(void *arg)
+static void test_find_signaled(struct kunit *test)
{
struct fence_chains fc;
struct dma_fence *fence;
int err;
err = fence_chains_init(&fc, 2, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
dma_fence_signal(fc.fences[0]);
@@ -270,37 +258,33 @@ static int find_signaled(void *arg)
err = dma_fence_chain_find_seqno(&fence, 1);
dma_fence_put(fence);
if (err) {
- pr_err("Reported %d for find_seqno()!\n", err);
+ KUNIT_FAIL(test, "Reported %d for find_seqno()!", err);
goto err;
}
if (fence && fence != fc.chains[0]) {
- pr_err("Incorrect chain-fence.seqno:%lld reported for completed seqno:1\n",
- fence->seqno);
+ KUNIT_FAIL(test, "Incorrect chain-fence.seqno:%lld reported for completed seqno:1",
+ fence->seqno);
dma_fence_get(fence);
err = dma_fence_chain_find_seqno(&fence, 1);
dma_fence_put(fence);
if (err)
- pr_err("Reported %d for finding self!\n", err);
-
- err = -EINVAL;
+ KUNIT_FAIL(test, "Reported %d for finding self!", err);
}
err:
fence_chains_fini(&fc);
- return err;
}
-static int find_out_of_order(void *arg)
+static void test_find_out_of_order(struct kunit *test)
{
struct fence_chains fc;
struct dma_fence *fence;
int err;
err = fence_chains_init(&fc, 3, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
dma_fence_signal(fc.fences[1]);
@@ -308,7 +292,7 @@ static int find_out_of_order(void *arg)
err = dma_fence_chain_find_seqno(&fence, 2);
dma_fence_put(fence);
if (err) {
- pr_err("Reported %d for find_seqno()!\n", err);
+ KUNIT_FAIL(test, "Reported %d for find_seqno()!", err);
goto err;
}
@@ -319,16 +303,12 @@ static int find_out_of_order(void *arg)
* we should get as fence to wait upon (fence 2 being garbage
* collected during the traversal of the chain).
*/
- if (fence != fc.chains[0]) {
- pr_err("Incorrect chain-fence.seqno:%lld reported for completed seqno:2\n",
- fence ? fence->seqno : 0);
-
- err = -EINVAL;
- }
+ if (fence != fc.chains[0])
+ KUNIT_FAIL(test, "Incorrect chain-fence.seqno:%lld reported for completed seqno:2",
+ fence ? fence->seqno : 0);
err:
fence_chains_fini(&fc);
- return err;
}
static uint64_t seqno_inc2(unsigned int i)
@@ -336,7 +316,7 @@ static uint64_t seqno_inc2(unsigned int i)
return 2 * i + 2;
}
-static int find_gap(void *arg)
+static void test_find_gap(struct kunit *test)
{
struct fence_chains fc;
struct dma_fence *fence;
@@ -344,24 +324,22 @@ static int find_gap(void *arg)
int i;
err = fence_chains_init(&fc, 64, seqno_inc2);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
for (i = 0; i < fc.chain_length; i++) {
fence = dma_fence_get(fc.tail);
err = dma_fence_chain_find_seqno(&fence, 2 * i + 1);
dma_fence_put(fence);
if (err) {
- pr_err("Reported %d for find_seqno(%d:%d)!\n",
- err, fc.chain_length + 1, 2 * i + 1);
+ KUNIT_FAIL(test, "Reported %d for find_seqno(%d:%d)!",
+ err, fc.chain_length + 1, 2 * i + 1);
goto err;
}
if (fence != fc.chains[i]) {
- pr_err("Incorrect fence.seqno:%lld reported by find_seqno(%d:%d)\n",
- fence->seqno,
- fc.chain_length + 1,
- 2 * i + 1);
- err = -EINVAL;
+ KUNIT_FAIL(test, "Incorrect fence.seqno:%lld reported by find_seqno(%d:%d)",
+ fence->seqno,
+ fc.chain_length + 1,
+ 2 * i + 1);
goto err;
}
@@ -369,19 +347,17 @@ static int find_gap(void *arg)
err = dma_fence_chain_find_seqno(&fence, 2 * i + 2);
dma_fence_put(fence);
if (err) {
- pr_err("Error reported for finding self\n");
+ KUNIT_FAIL(test, "Error reported for finding self");
goto err;
}
if (fence != fc.chains[i]) {
- pr_err("Incorrect fence reported by find self\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Incorrect fence reported by find self");
goto err;
}
}
err:
fence_chains_fini(&fc);
- return err;
}
struct find_race {
@@ -437,7 +413,7 @@ signal:
return err;
}
-static int find_race(void *arg)
+static void test_find_race(struct kunit *test)
{
struct find_race data;
int ncpus = num_online_cpus();
@@ -447,12 +423,11 @@ static int find_race(void *arg)
int i;
err = fence_chains_init(&data.fc, CHAIN_SZ, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
threads = kmalloc_objs(*threads, ncpus);
if (!threads) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to allocate threads array");
goto err;
}
@@ -486,74 +461,67 @@ static int find_race(void *arg)
count++;
pr_info("Completed %lu cycles\n", count);
+ KUNIT_EXPECT_EQ(test, err, 0);
+
err:
fence_chains_fini(&data.fc);
- return err;
}
-static int signal_forward(void *arg)
+static void test_signal_forward(struct kunit *test)
{
struct fence_chains fc;
int err;
int i;
err = fence_chains_init(&fc, 64, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
for (i = 0; i < fc.chain_length; i++) {
dma_fence_signal(fc.fences[i]);
if (!dma_fence_is_signaled(fc.chains[i])) {
- pr_err("chain[%d] not signaled!\n", i);
- err = -EINVAL;
+ KUNIT_FAIL(test, "chain[%d] not signaled!", i);
goto err;
}
if (i + 1 < fc.chain_length &&
dma_fence_is_signaled(fc.chains[i + 1])) {
- pr_err("chain[%d] is signaled!\n", i);
- err = -EINVAL;
+ KUNIT_FAIL(test, "chain[%d] is signaled!", i);
goto err;
}
}
err:
fence_chains_fini(&fc);
- return err;
}
-static int signal_backward(void *arg)
+static void test_signal_backward(struct kunit *test)
{
struct fence_chains fc;
int err;
int i;
err = fence_chains_init(&fc, 64, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
for (i = fc.chain_length; i--; ) {
dma_fence_signal(fc.fences[i]);
if (i > 0 && dma_fence_is_signaled(fc.chains[i])) {
- pr_err("chain[%d] is signaled!\n", i);
- err = -EINVAL;
+ KUNIT_FAIL(test, "chain[%d] is signaled!", i);
goto err;
}
}
for (i = 0; i < fc.chain_length; i++) {
if (!dma_fence_is_signaled(fc.chains[i])) {
- pr_err("chain[%d] was not signaled!\n", i);
- err = -EINVAL;
+ KUNIT_FAIL(test, "chain[%d] was not signaled!", i);
goto err;
}
}
err:
fence_chains_fini(&fc);
- return err;
}
static int __wait_fence_chains(void *arg)
@@ -566,7 +534,7 @@ static int __wait_fence_chains(void *arg)
return 0;
}
-static int wait_forward(void *arg)
+static void test_wait_forward(struct kunit *test)
{
struct fence_chains fc;
struct task_struct *tsk;
@@ -574,12 +542,11 @@ static int wait_forward(void *arg)
int i;
err = fence_chains_init(&fc, CHAIN_SZ, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
tsk = kthread_run(__wait_fence_chains, &fc, "dmabuf/wait");
if (IS_ERR(tsk)) {
- err = PTR_ERR(tsk);
+ KUNIT_FAIL(test, "Failed to create kthread");
goto err;
}
get_task_struct(tsk);
@@ -589,13 +556,13 @@ static int wait_forward(void *arg)
dma_fence_signal(fc.fences[i]);
err = kthread_stop_put(tsk);
+ KUNIT_EXPECT_EQ(test, err, 0);
err:
fence_chains_fini(&fc);
- return err;
}
-static int wait_backward(void *arg)
+static void test_wait_backward(struct kunit *test)
{
struct fence_chains fc;
struct task_struct *tsk;
@@ -603,12 +570,11 @@ static int wait_backward(void *arg)
int i;
err = fence_chains_init(&fc, CHAIN_SZ, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
tsk = kthread_run(__wait_fence_chains, &fc, "dmabuf/wait");
if (IS_ERR(tsk)) {
- err = PTR_ERR(tsk);
+ KUNIT_FAIL(test, "Failed to create kthread");
goto err;
}
get_task_struct(tsk);
@@ -618,10 +584,10 @@ static int wait_backward(void *arg)
dma_fence_signal(fc.fences[i]);
err = kthread_stop_put(tsk);
+ KUNIT_EXPECT_EQ(test, err, 0);
err:
fence_chains_fini(&fc);
- return err;
}
static void randomise_fences(struct fence_chains *fc)
@@ -640,7 +606,7 @@ static void randomise_fences(struct fence_chains *fc)
}
}
-static int wait_random(void *arg)
+static void test_wait_random(struct kunit *test)
{
struct fence_chains fc;
struct task_struct *tsk;
@@ -648,14 +614,13 @@ static int wait_random(void *arg)
int i;
err = fence_chains_init(&fc, CHAIN_SZ, seqno_inc);
- if (err)
- return err;
+ KUNIT_ASSERT_EQ_MSG(test, err, 0, "Failed to init fence chains");
randomise_fences(&fc);
tsk = kthread_run(__wait_fence_chains, &fc, "dmabuf/wait");
if (IS_ERR(tsk)) {
- err = PTR_ERR(tsk);
+ KUNIT_FAIL(test, "Failed to create kthread");
goto err;
}
get_task_struct(tsk);
@@ -665,29 +630,14 @@ static int wait_random(void *arg)
dma_fence_signal(fc.fences[i]);
err = kthread_stop_put(tsk);
+ KUNIT_EXPECT_EQ(test, err, 0);
err:
fence_chains_fini(&fc);
- return err;
}
-int dma_fence_chain(void)
+static int dma_fence_chain_suite_init(struct kunit_suite *suite)
{
- static const struct subtest tests[] = {
- SUBTEST(sanitycheck),
- SUBTEST(find_seqno),
- SUBTEST(find_signaled),
- SUBTEST(find_out_of_order),
- SUBTEST(find_gap),
- SUBTEST(find_race),
- SUBTEST(signal_forward),
- SUBTEST(signal_backward),
- SUBTEST(wait_forward),
- SUBTEST(wait_backward),
- SUBTEST(wait_random),
- };
- int ret;
-
pr_info("sizeof(dma_fence_chain)=%zu\n",
sizeof(struct dma_fence_chain));
@@ -696,9 +646,34 @@ int dma_fence_chain(void)
SLAB_HWCACHE_ALIGN);
if (!slab_fences)
return -ENOMEM;
+ return 0;
+}
- ret = subtests(tests, NULL);
-
+static void dma_fence_chain_suite_exit(struct kunit_suite *suite)
+{
kmem_cache_destroy(slab_fences);
- return ret;
}
+
+static struct kunit_case dma_fence_chain_cases[] = {
+ KUNIT_CASE(test_sanitycheck),
+ KUNIT_CASE(test_find_seqno),
+ KUNIT_CASE(test_find_signaled),
+ KUNIT_CASE(test_find_out_of_order),
+ KUNIT_CASE(test_find_gap),
+ KUNIT_CASE(test_find_race),
+ KUNIT_CASE(test_signal_forward),
+ KUNIT_CASE(test_signal_backward),
+ KUNIT_CASE(test_wait_forward),
+ KUNIT_CASE(test_wait_backward),
+ KUNIT_CASE(test_wait_random),
+ {}
+};
+
+static struct kunit_suite dma_fence_chain_test_suite = {
+ .name = "dma-buf-fence-chain",
+ .suite_init = dma_fence_chain_suite_init,
+ .suite_exit = dma_fence_chain_suite_exit,
+ .test_cases = dma_fence_chain_cases,
+};
+
+kunit_test_suite(dma_fence_chain_test_suite);
diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c
index 9c74195f47fd..51c87869b7b8 100644
--- a/drivers/dma-buf/st-dma-fence-unwrap.c
+++ b/drivers/dma-buf/st-dma-fence-unwrap.c
@@ -4,13 +4,12 @@
* Copyright (C) 2022 Advanced Micro Devices, Inc.
*/
+#include <kunit/test.h>
#include <linux/dma-fence.h>
#include <linux/dma-fence-array.h>
#include <linux/dma-fence-chain.h>
#include <linux/dma-fence-unwrap.h>
-#include "selftest.h"
-
#define CHAIN_SZ (4 << 10)
struct mock_fence {
@@ -97,52 +96,45 @@ static struct dma_fence *mock_chain(struct dma_fence *prev,
return &f->base;
}
-static int sanitycheck(void *arg)
+static void test_sanitycheck(struct kunit *test)
{
struct dma_fence *f, *chain, *array;
- int err = 0;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
array = mock_array(1, f);
- if (!array)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, array);
chain = mock_chain(NULL, array);
- if (!chain)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, chain);
dma_fence_put(chain);
- return err;
}
-static int unwrap_array(void *arg)
+static void test_unwrap_array(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *array;
struct dma_fence_unwrap iter;
- int err = 0;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
if (!f2) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
dma_fence_put(f1);
- return -ENOMEM;
+ return;
}
dma_fence_enable_sw_signaling(f2);
array = mock_array(2, f1, f2);
- if (!array)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, array);
dma_fence_unwrap_for_each(fence, &iter, array) {
if (fence == f1) {
@@ -150,43 +142,37 @@ static int unwrap_array(void *arg)
} else if (fence == f2) {
f2 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f2) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f2)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(array);
- return err;
}
-static int unwrap_chain(void *arg)
+static void test_unwrap_chain(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *chain;
struct dma_fence_unwrap iter;
- int err = 0;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
if (!f2) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
dma_fence_put(f1);
- return -ENOMEM;
+ return;
}
dma_fence_enable_sw_signaling(f2);
chain = mock_chain(f1, f2);
- if (!chain)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, chain);
dma_fence_unwrap_for_each(fence, &iter, chain) {
if (fence == f1) {
@@ -194,47 +180,40 @@ static int unwrap_chain(void *arg)
} else if (fence == f2) {
f2 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f2) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f2)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(chain);
- return err;
}
-static int unwrap_chain_array(void *arg)
+static void test_unwrap_chain_array(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *array, *chain;
struct dma_fence_unwrap iter;
- int err = 0;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
if (!f2) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
dma_fence_put(f1);
- return -ENOMEM;
+ return;
}
dma_fence_enable_sw_signaling(f2);
array = mock_array(2, f1, f2);
- if (!array)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, array);
chain = mock_chain(NULL, array);
- if (!chain)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, chain);
dma_fence_unwrap_for_each(fence, &iter, chain) {
if (fence == f1) {
@@ -242,35 +221,29 @@ static int unwrap_chain_array(void *arg)
} else if (fence == f2) {
f2 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f2) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f2)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(chain);
- return err;
}
-static int unwrap_merge(void *arg)
+static void test_unwrap_merge(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *f3;
struct dma_fence_unwrap iter;
- int err = 0;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
if (!f2) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f1;
}
@@ -278,7 +251,7 @@ static int unwrap_merge(void *arg)
f3 = dma_fence_unwrap_merge(f1, f2);
if (!f3) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to merge fences");
goto error_put_f2;
}
@@ -290,39 +263,33 @@ static int unwrap_merge(void *arg)
dma_fence_put(f2);
f2 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f2) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f2)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(f3);
error_put_f2:
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
- return err;
}
-static int unwrap_merge_duplicate(void *arg)
+static void test_unwrap_merge_duplicate(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2;
struct dma_fence_unwrap iter;
- int err = 0;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = dma_fence_unwrap_merge(f1, f1);
if (!f2) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to merge fences");
goto error_put_f1;
}
@@ -331,41 +298,35 @@ static int unwrap_merge_duplicate(void *arg)
dma_fence_put(f1);
f1 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
- return err;
}
-static int unwrap_merge_seqno(void *arg)
+static void test_unwrap_merge_seqno(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *f3, *f4;
struct dma_fence_unwrap iter;
- int err = 0;
u64 ctx[2];
ctx[0] = dma_fence_context_alloc(1);
ctx[1] = dma_fence_context_alloc(1);
f1 = __mock_fence(ctx[1], 1);
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = __mock_fence(ctx[1], 2);
if (!f2) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f1;
}
@@ -373,7 +334,7 @@ static int unwrap_merge_seqno(void *arg)
f3 = __mock_fence(ctx[0], 1);
if (!f3) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f2;
}
@@ -381,7 +342,7 @@ static int unwrap_merge_seqno(void *arg)
f4 = dma_fence_unwrap_merge(f1, f2, f3);
if (!f4) {
- err = -ENOMEM;
+ KUNIT_FAIL(test, "Failed to merge fences");
goto error_put_f3;
}
@@ -393,15 +354,12 @@ static int unwrap_merge_seqno(void *arg)
dma_fence_put(f2);
f2 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f2 || f3) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f2 || f3)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(f4);
error_put_f3:
@@ -410,40 +368,41 @@ error_put_f2:
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
- return err;
}
-static int unwrap_merge_order(void *arg)
+static void test_unwrap_merge_order(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *a1, *a2, *c1, *c2;
struct dma_fence_unwrap iter;
- int err = 0;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
if (!f2) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
dma_fence_put(f1);
- return -ENOMEM;
+ return;
}
dma_fence_enable_sw_signaling(f2);
a1 = mock_array(2, f1, f2);
- if (!a1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, a1);
c1 = mock_chain(NULL, dma_fence_get(f1));
- if (!c1)
+ if (!c1) {
+ KUNIT_FAIL(test, "Failed to create chain");
goto error_put_a1;
+ }
c2 = mock_chain(c1, dma_fence_get(f2));
- if (!c2)
+ if (!c2) {
+ KUNIT_FAIL(test, "Failed to create chain");
goto error_put_a1;
+ }
/*
* The fences in the chain are the same as in a1 but in oposite order,
@@ -455,63 +414,64 @@ static int unwrap_merge_order(void *arg)
if (fence == f1) {
f1 = NULL;
if (!f2)
- pr_err("Unexpected order!\n");
+ KUNIT_FAIL(test, "Unexpected order!");
} else if (fence == f2) {
f2 = NULL;
if (f1)
- pr_err("Unexpected order!\n");
+ KUNIT_FAIL(test, "Unexpected order!");
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f2) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f2)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(a2);
- return err;
+ return;
error_put_a1:
dma_fence_put(a1);
- return -ENOMEM;
}
-static int unwrap_merge_complex(void *arg)
+static void test_unwrap_merge_complex(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5;
struct dma_fence_unwrap iter;
- int err = -ENOMEM;
f1 = mock_fence();
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = mock_fence();
- if (!f2)
+ if (!f2) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f1;
+ }
dma_fence_enable_sw_signaling(f2);
f3 = dma_fence_unwrap_merge(f1, f2);
- if (!f3)
+ if (!f3) {
+ KUNIT_FAIL(test, "Failed to merge fences");
goto error_put_f2;
+ }
/* The resulting array has the fences in reverse */
f4 = mock_array(2, dma_fence_get(f2), dma_fence_get(f1));
- if (!f4)
+ if (!f4) {
+ KUNIT_FAIL(test, "Failed to create array");
goto error_put_f3;
+ }
/* Signaled fences should be filtered, the two arrays merged. */
f5 = dma_fence_unwrap_merge(f3, f4, dma_fence_get_stub());
- if (!f5)
+ if (!f5) {
+ KUNIT_FAIL(test, "Failed to merge fences");
goto error_put_f4;
+ }
- err = 0;
dma_fence_unwrap_for_each(fence, &iter, f5) {
if (fence == f1) {
dma_fence_put(f1);
@@ -520,15 +480,12 @@ static int unwrap_merge_complex(void *arg)
dma_fence_put(f2);
f2 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f2) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f2)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(f5);
error_put_f4:
@@ -539,56 +496,64 @@ error_put_f2:
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
- return err;
}
-static int unwrap_merge_complex_seqno(void *arg)
+static void test_unwrap_merge_complex_seqno(struct kunit *test)
{
struct dma_fence *fence, *f1, *f2, *f3, *f4, *f5, *f6, *f7;
struct dma_fence_unwrap iter;
- int err = -ENOMEM;
u64 ctx[2];
ctx[0] = dma_fence_context_alloc(1);
ctx[1] = dma_fence_context_alloc(1);
f1 = __mock_fence(ctx[0], 2);
- if (!f1)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f1);
dma_fence_enable_sw_signaling(f1);
f2 = __mock_fence(ctx[1], 1);
- if (!f2)
+ if (!f2) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f1;
+ }
dma_fence_enable_sw_signaling(f2);
f3 = __mock_fence(ctx[0], 1);
- if (!f3)
+ if (!f3) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f2;
+ }
dma_fence_enable_sw_signaling(f3);
f4 = __mock_fence(ctx[1], 2);
- if (!f4)
+ if (!f4) {
+ KUNIT_FAIL(test, "Failed to create mock fence");
goto error_put_f3;
+ }
dma_fence_enable_sw_signaling(f4);
f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2));
- if (!f5)
+ if (!f5) {
+ KUNIT_FAIL(test, "Failed to create array");
goto error_put_f4;
+ }
f6 = mock_array(2, dma_fence_get(f3), dma_fence_get(f4));
- if (!f6)
+ if (!f6) {
+ KUNIT_FAIL(test, "Failed to create array");
goto error_put_f5;
+ }
f7 = dma_fence_unwrap_merge(f5, f6);
- if (!f7)
+ if (!f7) {
+ KUNIT_FAIL(test, "Failed to merge fences");
goto error_put_f6;
+ }
- err = 0;
dma_fence_unwrap_for_each(fence, &iter, f7) {
if (fence == f1 && f4) {
dma_fence_put(f1);
@@ -597,15 +562,12 @@ static int unwrap_merge_complex_seqno(void *arg)
dma_fence_put(f4);
f4 = NULL;
} else {
- pr_err("Unexpected fence!\n");
- err = -EINVAL;
+ KUNIT_FAIL(test, "Unexpected fence!");
}
}
- if (f1 || f4) {
- pr_err("Not all fences seen!\n");
- err = -EINVAL;
- }
+ if (f1 || f4)
+ KUNIT_FAIL(test, "Not all fences seen!");
dma_fence_put(f7);
error_put_f6:
@@ -620,23 +582,25 @@ error_put_f2:
dma_fence_put(f2);
error_put_f1:
dma_fence_put(f1);
- return err;
}
-int dma_fence_unwrap(void)
-{
- static const struct subtest tests[] = {
- SUBTEST(sanitycheck),
- SUBTEST(unwrap_array),
- SUBTEST(unwrap_chain),
- SUBTEST(unwrap_chain_array),
- SUBTEST(unwrap_merge),
- SUBTEST(unwrap_merge_duplicate),
- SUBTEST(unwrap_merge_seqno),
- SUBTEST(unwrap_merge_order),
- SUBTEST(unwrap_merge_complex),
- SUBTEST(unwrap_merge_complex_seqno),
- };
-
- return subtests(tests, NULL);
-}
+static struct kunit_case dma_fence_unwrap_cases[] = {
+ KUNIT_CASE(test_sanitycheck),
+ KUNIT_CASE(test_unwrap_array),
+ KUNIT_CASE(test_unwrap_chain),
+ KUNIT_CASE(test_unwrap_chain_array),
+ KUNIT_CASE(test_unwrap_merge),
+ KUNIT_CASE(test_unwrap_merge_duplicate),
+ KUNIT_CASE(test_unwrap_merge_seqno),
+ KUNIT_CASE(test_unwrap_merge_order),
+ KUNIT_CASE(test_unwrap_merge_complex),
+ KUNIT_CASE(test_unwrap_merge_complex_seqno),
+ {}
+};
+
+static struct kunit_suite dma_fence_unwrap_test_suite = {
+ .name = "dma-buf-fence-unwrap",
+ .test_cases = dma_fence_unwrap_cases,
+};
+
+kunit_test_suite(dma_fence_unwrap_test_suite);
diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c
index 0d9d524d79b6..499272229696 100644
--- a/drivers/dma-buf/st-dma-fence.c
+++ b/drivers/dma-buf/st-dma-fence.c
@@ -4,6 +4,7 @@
* Copyright © 2019 Intel Corporation
*/
+#include <kunit/test.h>
#include <linux/delay.h>
#include <linux/dma-fence.h>
#include <linux/kernel.h>
@@ -12,8 +13,6 @@
#include <linux/slab.h>
#include <linux/spinlock.h>
-#include "selftest.h"
-
static const char *mock_name(struct dma_fence *f)
{
return "mock";
@@ -36,62 +35,55 @@ static struct dma_fence *mock_fence(void)
return f;
}
-static int sanitycheck(void *arg)
+static void test_sanitycheck(struct kunit *test)
{
struct dma_fence *f;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_signal(f);
dma_fence_put(f);
-
- return 0;
}
-static int test_signaling(void *arg)
+static void test_signaling(struct kunit *test)
{
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
if (dma_fence_is_signaled(f)) {
- pr_err("Fence unexpectedly signaled on creation\n");
+ KUNIT_FAIL(test, "Fence unexpectedly signaled on creation");
goto err_free;
}
if (dma_fence_check_and_signal(f)) {
- pr_err("Fence reported being already signaled\n");
+ KUNIT_FAIL(test, "Fence reported being already signaled");
goto err_free;
}
if (!dma_fence_is_signaled(f)) {
- pr_err("Fence not reporting signaled\n");
+ KUNIT_FAIL(test, "Fence not reporting signaled");
goto err_free;
}
if (!dma_fence_test_signaled_flag(f)) {
- pr_err("Fence reported not being already signaled\n");
+ KUNIT_FAIL(test, "Fence reported not being already signaled");
goto err_free;
}
if (rcu_dereference_protected(f->ops, true)) {
- pr_err("Fence ops not cleared on signal\n");
+ KUNIT_FAIL(test, "Fence ops not cleared on signal");
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
struct simple_cb {
@@ -104,215 +96,187 @@ static void simple_callback(struct dma_fence *f, struct dma_fence_cb *cb)
smp_store_mb(container_of(cb, struct simple_cb, cb)->seen, true);
}
-static int test_add_callback(void *arg)
+static void test_add_callback(struct kunit *test)
{
struct simple_cb cb = {};
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
- pr_err("Failed to add callback, fence already signaled!\n");
+ KUNIT_FAIL(test, "Failed to add callback, fence already signaled!");
goto err_free;
}
dma_fence_signal(f);
if (!cb.seen) {
- pr_err("Callback failed!\n");
+ KUNIT_FAIL(test, "Callback failed!");
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
-static int test_late_add_callback(void *arg)
+static void test_late_add_callback(struct kunit *test)
{
struct simple_cb cb = {};
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_signal(f);
if (!dma_fence_add_callback(f, &cb.cb, simple_callback)) {
- pr_err("Added callback, but fence was already signaled!\n");
+ KUNIT_FAIL(test, "Added callback, but fence was already signaled!");
goto err_free;
}
dma_fence_signal(f);
if (cb.seen) {
- pr_err("Callback called after failed attachment !\n");
+ KUNIT_FAIL(test, "Callback called after failed attachment!");
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
-static int test_rm_callback(void *arg)
+static void test_rm_callback(struct kunit *test)
{
struct simple_cb cb = {};
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
- pr_err("Failed to add callback, fence already signaled!\n");
+ KUNIT_FAIL(test, "Failed to add callback, fence already signaled!");
goto err_free;
}
if (!dma_fence_remove_callback(f, &cb.cb)) {
- pr_err("Failed to remove callback!\n");
+ KUNIT_FAIL(test, "Failed to remove callback!");
goto err_free;
}
dma_fence_signal(f);
if (cb.seen) {
- pr_err("Callback still signaled after removal!\n");
+ KUNIT_FAIL(test, "Callback still signaled after removal!");
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
-static int test_late_rm_callback(void *arg)
+static void test_late_rm_callback(struct kunit *test)
{
struct simple_cb cb = {};
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
if (dma_fence_add_callback(f, &cb.cb, simple_callback)) {
- pr_err("Failed to add callback, fence already signaled!\n");
+ KUNIT_FAIL(test, "Failed to add callback, fence already signaled!");
goto err_free;
}
dma_fence_signal(f);
if (!cb.seen) {
- pr_err("Callback failed!\n");
+ KUNIT_FAIL(test, "Callback failed!");
goto err_free;
}
if (dma_fence_remove_callback(f, &cb.cb)) {
- pr_err("Callback removal succeed after being executed!\n");
+ KUNIT_FAIL(test, "Callback removal succeeded after being executed!");
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
-static int test_status(void *arg)
+static void test_status(struct kunit *test)
{
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
if (dma_fence_get_status(f)) {
- pr_err("Fence unexpectedly has signaled status on creation\n");
+ KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation");
goto err_free;
}
dma_fence_signal(f);
if (!dma_fence_get_status(f)) {
- pr_err("Fence not reporting signaled status\n");
+ KUNIT_FAIL(test, "Fence not reporting signaled status");
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
-static int test_error(void *arg)
+static void test_error(struct kunit *test)
{
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_fence_set_error(f, -EIO);
if (dma_fence_get_status(f)) {
- pr_err("Fence unexpectedly has error status before signal\n");
+ KUNIT_FAIL(test, "Fence unexpectedly has error status before signal");
goto err_free;
}
dma_fence_signal(f);
if (dma_fence_get_status(f) != -EIO) {
- pr_err("Fence not reporting error status, got %d\n",
- dma_fence_get_status(f));
+ KUNIT_FAIL(test, "Fence not reporting error status, got %d",
+ dma_fence_get_status(f));
goto err_free;
}
- err = 0;
err_free:
dma_fence_put(f);
- return err;
}
-static int test_wait(void *arg)
+static void test_wait(struct kunit *test)
{
struct dma_fence *f;
- int err = -EINVAL;
f = mock_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
if (dma_fence_wait_timeout(f, false, 0) != 0) {
- pr_err("Wait reported complete before being signaled\n");
+ KUNIT_FAIL(test, "Wait reported complete before being signaled");
goto err_free;
}
dma_fence_signal(f);
if (dma_fence_wait_timeout(f, false, 0) != 1) {
- pr_err("Wait reported incomplete after being signaled\n");
+ KUNIT_FAIL(test, "Wait reported incomplete after being signaled");
goto err_free;
}
- err = 0;
err_free:
dma_fence_signal(f);
dma_fence_put(f);
- return err;
}
struct wait_timer {
@@ -327,21 +291,19 @@ static void wait_timer(struct timer_list *timer)
dma_fence_signal(wt->f);
}
-static int test_wait_timeout(void *arg)
+static void test_wait_timeout(struct kunit *test)
{
struct wait_timer wt;
- int err = -EINVAL;
timer_setup_on_stack(&wt.timer, wait_timer, 0);
wt.f = mock_fence();
- if (!wt.f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, wt.f);
dma_fence_enable_sw_signaling(wt.f);
if (dma_fence_wait_timeout(wt.f, false, 1) != 0) {
- pr_err("Wait reported complete before being signaled\n");
+ KUNIT_FAIL(test, "Wait reported complete before being signaled");
goto err_free;
}
@@ -349,42 +311,38 @@ static int test_wait_timeout(void *arg)
if (dma_fence_wait_timeout(wt.f, false, HZ) == 0) {
if (timer_pending(&wt.timer)) {
- pr_notice("Timer did not fire within one HZ!\n");
- err = 0; /* not our fault! */
+ kunit_mark_skipped(
+ test, "Timer did not fire within on HZ!\n");
} else {
- pr_err("Wait reported incomplete after timeout\n");
+ KUNIT_FAIL(test,
+ "Wait reported incomplete after timeout");
}
goto err_free;
}
- err = 0;
err_free:
timer_delete_sync(&wt.timer);
timer_destroy_on_stack(&wt.timer);
dma_fence_signal(wt.f);
dma_fence_put(wt.f);
- return err;
}
-static int test_stub(void *arg)
+static void test_stub(struct kunit *test)
{
struct dma_fence *f[64];
- int err = -EINVAL;
int i;
for (i = 0; i < ARRAY_SIZE(f); i++) {
f[i] = dma_fence_get_stub();
if (!dma_fence_is_signaled(f[i])) {
- pr_err("Obtained unsignaled stub fence!\n");
+ KUNIT_FAIL(test, "Obtained unsignaled stub fence!");
goto err;
}
}
- err = 0;
err:
while (i--)
dma_fence_put(f[i]);
- return err;
}
/* Now off to the races! */
@@ -473,12 +431,19 @@ static int thread_signal_callback(void *arg)
return err;
}
-static int race_signal_callback(void *arg)
+static void test_race_signal_callback(struct kunit *test)
{
struct dma_fence __rcu *f[2] = {};
int ret = 0;
int pass;
+ /*
+ * thread_signal_callback() spins under RCU and it cannot make forward
+ * progress unless the threads are truly running concurrently.
+ */
+ if (num_online_cpus() < 2)
+ kunit_skip(test, "requires at least 2 CPUs");
+
for (pass = 0; !ret && pass <= 1; pass++) {
struct race_thread t[2];
int i;
@@ -490,10 +455,10 @@ static int race_signal_callback(void *arg)
t[i].task = kthread_run(thread_signal_callback, &t[i],
"dma-fence:%d", i);
if (IS_ERR(t[i].task)) {
- ret = PTR_ERR(t[i].task);
+ KUNIT_FAIL(test, "Failed to create kthread");
while (--i >= 0)
kthread_stop_put(t[i].task);
- return ret;
+ return;
}
get_task_struct(t[i].task);
}
@@ -509,26 +474,35 @@ static int race_signal_callback(void *arg)
}
}
- return ret;
+ KUNIT_EXPECT_EQ(test, ret, 0);
}
-int dma_fence(void)
+static int dma_fence_suite_init(struct kunit_suite *suite)
{
- static const struct subtest tests[] = {
- SUBTEST(sanitycheck),
- SUBTEST(test_signaling),
- SUBTEST(test_add_callback),
- SUBTEST(test_late_add_callback),
- SUBTEST(test_rm_callback),
- SUBTEST(test_late_rm_callback),
- SUBTEST(test_status),
- SUBTEST(test_error),
- SUBTEST(test_wait),
- SUBTEST(test_wait_timeout),
- SUBTEST(test_stub),
- SUBTEST(race_signal_callback),
- };
-
pr_info("sizeof(dma_fence)=%zu\n", sizeof(struct dma_fence));
- return subtests(tests, NULL);
+ return 0;
}
+
+static struct kunit_case dma_fence_cases[] = {
+ KUNIT_CASE(test_sanitycheck),
+ KUNIT_CASE(test_signaling),
+ KUNIT_CASE(test_add_callback),
+ KUNIT_CASE(test_late_add_callback),
+ KUNIT_CASE(test_rm_callback),
+ KUNIT_CASE(test_late_rm_callback),
+ KUNIT_CASE(test_status),
+ KUNIT_CASE(test_error),
+ KUNIT_CASE(test_wait),
+ KUNIT_CASE(test_wait_timeout),
+ KUNIT_CASE(test_stub),
+ KUNIT_CASE(test_race_signal_callback),
+ {}
+};
+
+static struct kunit_suite dma_fence_test_suite = {
+ .name = "dma-buf-fence",
+ .suite_init = dma_fence_suite_init,
+ .test_cases = dma_fence_cases,
+};
+
+kunit_test_suite(dma_fence_test_suite);
diff --git a/drivers/dma-buf/st-dma-resv.c b/drivers/dma-buf/st-dma-resv.c
index ad4dfb49dcd9..95a4becdb892 100644
--- a/drivers/dma-buf/st-dma-resv.c
+++ b/drivers/dma-buf/st-dma-resv.c
@@ -5,13 +5,17 @@
* Copyright © 2021 Advanced Micro Devices, Inc.
*/
+#include <kunit/test.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/dma-resv.h>
-#include "selftest.h"
+static DEFINE_SPINLOCK(fence_lock);
-static struct spinlock fence_lock;
+struct dma_resv_usage_param {
+ enum dma_resv_usage usage;
+ const char *desc;
+};
static const char *fence_name(struct dma_fence *f)
{
@@ -35,15 +39,14 @@ static struct dma_fence *alloc_fence(void)
return f;
}
-static int sanitycheck(void *arg)
+static void test_sanitycheck(struct kunit *test)
{
struct dma_resv resv;
struct dma_fence *f;
int r;
f = alloc_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
@@ -53,49 +56,46 @@ static int sanitycheck(void *arg)
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
if (r)
- pr_err("Resv locking failed\n");
+ KUNIT_FAIL(test, "Resv locking failed\n");
else
dma_resv_unlock(&resv);
dma_resv_fini(&resv);
- return r;
}
-static int test_signaling(void *arg)
+static void test_signaling(struct kunit *test)
{
- enum dma_resv_usage usage = (unsigned long)arg;
+ const struct dma_resv_usage_param *param = test->param_value;
+ enum dma_resv_usage usage = param->usage;
struct dma_resv resv;
struct dma_fence *f;
int r;
f = alloc_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
if (r) {
- pr_err("Resv locking failed\n");
+ KUNIT_FAIL(test, "Resv locking failed");
goto err_free;
}
r = dma_resv_reserve_fences(&resv, 1);
if (r) {
- pr_err("Resv shared slot allocation failed\n");
+ KUNIT_FAIL(test, "Resv shared slot allocation failed");
goto err_unlock;
}
dma_resv_add_fence(&resv, f, usage);
if (dma_resv_test_signaled(&resv, usage)) {
- pr_err("Resv unexpectedly signaled\n");
- r = -EINVAL;
+ KUNIT_FAIL(test, "Resv unexpectedly signaled");
goto err_unlock;
}
dma_fence_signal(f);
if (!dma_resv_test_signaled(&resv, usage)) {
- pr_err("Resv not reporting signaled\n");
- r = -EINVAL;
+ KUNIT_FAIL(test, "Resv not reporting signaled");
goto err_unlock;
}
err_unlock:
@@ -103,33 +103,32 @@ err_unlock:
err_free:
dma_resv_fini(&resv);
dma_fence_put(f);
- return r;
}
-static int test_for_each(void *arg)
+static void test_for_each(struct kunit *test)
{
- enum dma_resv_usage usage = (unsigned long)arg;
+ const struct dma_resv_usage_param *param = test->param_value;
+ enum dma_resv_usage usage = param->usage;
struct dma_resv_iter cursor;
struct dma_fence *f, *fence;
struct dma_resv resv;
int r;
f = alloc_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
if (r) {
- pr_err("Resv locking failed\n");
+ KUNIT_FAIL(test, "Resv locking failed");
goto err_free;
}
r = dma_resv_reserve_fences(&resv, 1);
if (r) {
- pr_err("Resv shared slot allocation failed\n");
+ KUNIT_FAIL(test, "Resv shared slot allocation failed");
goto err_unlock;
}
@@ -138,24 +137,23 @@ static int test_for_each(void *arg)
r = -ENOENT;
dma_resv_for_each_fence(&cursor, &resv, usage, fence) {
if (!r) {
- pr_err("More than one fence found\n");
- r = -EINVAL;
+ KUNIT_FAIL(test, "More than one fence found");
goto err_unlock;
}
if (f != fence) {
- pr_err("Unexpected fence\n");
+ KUNIT_FAIL(test, "Unexpected fence");
r = -EINVAL;
goto err_unlock;
}
if (dma_resv_iter_usage(&cursor) != usage) {
- pr_err("Unexpected fence usage\n");
+ KUNIT_FAIL(test, "Unexpected fence usage");
r = -EINVAL;
goto err_unlock;
}
r = 0;
}
if (r) {
- pr_err("No fence found\n");
+ KUNIT_FAIL(test, "No fence found");
goto err_unlock;
}
dma_fence_signal(f);
@@ -164,33 +162,32 @@ err_unlock:
err_free:
dma_resv_fini(&resv);
dma_fence_put(f);
- return r;
}
-static int test_for_each_unlocked(void *arg)
+static void test_for_each_unlocked(struct kunit *test)
{
- enum dma_resv_usage usage = (unsigned long)arg;
+ const struct dma_resv_usage_param *param = test->param_value;
+ enum dma_resv_usage usage = param->usage;
struct dma_resv_iter cursor;
struct dma_fence *f, *fence;
struct dma_resv resv;
int r;
f = alloc_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
if (r) {
- pr_err("Resv locking failed\n");
+ KUNIT_FAIL(test, "Resv locking failed");
goto err_free;
}
r = dma_resv_reserve_fences(&resv, 1);
if (r) {
- pr_err("Resv shared slot allocation failed\n");
+ KUNIT_FAIL(test, "Resv shared slot allocation failed");
dma_resv_unlock(&resv);
goto err_free;
}
@@ -202,21 +199,20 @@ static int test_for_each_unlocked(void *arg)
dma_resv_iter_begin(&cursor, &resv, usage);
dma_resv_for_each_fence_unlocked(&cursor, fence) {
if (!r) {
- pr_err("More than one fence found\n");
- r = -EINVAL;
+ KUNIT_FAIL(test, "More than one fence found");
goto err_iter_end;
}
if (!dma_resv_iter_is_restarted(&cursor)) {
- pr_err("No restart flag\n");
+ KUNIT_FAIL(test, "No restart flag");
goto err_iter_end;
}
if (f != fence) {
- pr_err("Unexpected fence\n");
+ KUNIT_FAIL(test, "Unexpected fence");
r = -EINVAL;
goto err_iter_end;
}
if (dma_resv_iter_usage(&cursor) != usage) {
- pr_err("Unexpected fence usage\n");
+ KUNIT_FAIL(test, "Unexpected fence usage");
r = -EINVAL;
goto err_iter_end;
}
@@ -230,40 +226,38 @@ static int test_for_each_unlocked(void *arg)
r = 0;
}
}
- if (r)
- pr_err("No fence found\n");
+ KUNIT_EXPECT_EQ(test, r, 0);
err_iter_end:
dma_resv_iter_end(&cursor);
dma_fence_signal(f);
err_free:
dma_resv_fini(&resv);
dma_fence_put(f);
- return r;
}
-static int test_get_fences(void *arg)
+static void test_get_fences(struct kunit *test)
{
- enum dma_resv_usage usage = (unsigned long)arg;
+ const struct dma_resv_usage_param *param = test->param_value;
+ enum dma_resv_usage usage = param->usage;
struct dma_fence *f, **fences = NULL;
struct dma_resv resv;
int r, i;
f = alloc_fence();
- if (!f)
- return -ENOMEM;
+ KUNIT_ASSERT_NOT_NULL(test, f);
dma_fence_enable_sw_signaling(f);
dma_resv_init(&resv);
r = dma_resv_lock(&resv, NULL);
if (r) {
- pr_err("Resv locking failed\n");
+ KUNIT_FAIL(test, "Resv locking failed");
goto err_resv;
}
r = dma_resv_reserve_fences(&resv, 1);
if (r) {
- pr_err("Resv shared slot allocation failed\n");
+ KUNIT_FAIL(test, "Resv shared slot allocation failed");
dma_resv_unlock(&resv);
goto err_resv;
}
@@ -273,12 +267,12 @@ static int test_get_fences(void *arg)
r = dma_resv_get_fences(&resv, usage, &i, &fences);
if (r) {
- pr_err("get_fences failed\n");
+ KUNIT_FAIL(test, "get_fences failed");
goto err_free;
}
if (i != 1 || fences[0] != f) {
- pr_err("get_fences returned unexpected fence\n");
+ KUNIT_FAIL(test, "get_fences returned unexpected fence");
goto err_free;
}
@@ -290,27 +284,32 @@ err_free:
err_resv:
dma_resv_fini(&resv);
dma_fence_put(f);
- return r;
}
-int dma_resv(void)
-{
- static const struct subtest tests[] = {
- SUBTEST(sanitycheck),
- SUBTEST(test_signaling),
- SUBTEST(test_for_each),
- SUBTEST(test_for_each_unlocked),
- SUBTEST(test_get_fences),
- };
- enum dma_resv_usage usage;
- int r;
+static const struct dma_resv_usage_param dma_resv_usage_params[] = {
+ { DMA_RESV_USAGE_KERNEL, "kernel" },
+ { DMA_RESV_USAGE_WRITE, "write" },
+ { DMA_RESV_USAGE_READ, "read" },
+ { DMA_RESV_USAGE_BOOKKEEP, "bookkeep" },
+};
- spin_lock_init(&fence_lock);
- for (usage = DMA_RESV_USAGE_KERNEL; usage <= DMA_RESV_USAGE_BOOKKEEP;
- ++usage) {
- r = subtests(tests, (void *)(unsigned long)usage);
- if (r)
- return r;
- }
- return 0;
-}
+KUNIT_ARRAY_PARAM_DESC(dma_resv_usage, dma_resv_usage_params, desc);
+
+static struct kunit_case dma_resv_cases[] = {
+ KUNIT_CASE(test_sanitycheck),
+ KUNIT_CASE_PARAM(test_signaling, dma_resv_usage_gen_params),
+ KUNIT_CASE_PARAM(test_for_each, dma_resv_usage_gen_params),
+ KUNIT_CASE_PARAM(test_for_each_unlocked, dma_resv_usage_gen_params),
+ KUNIT_CASE_PARAM(test_get_fences, dma_resv_usage_gen_params),
+ {}
+};
+
+static struct kunit_suite dma_resv_test_suite = {
+ .name = "dma-buf-resv",
+ .test_cases = dma_resv_cases,
+};
+
+kunit_test_suite(dma_resv_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for DMA-BUF");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
index 94b8ecb892bb..bced421c0d65 100644
--- a/drivers/dma-buf/udmabuf.c
+++ b/drivers/dma-buf/udmabuf.c
@@ -26,10 +26,10 @@ MODULE_PARM_DESC(size_limit_mb, "Max size of a dmabuf, in megabytes. Default is
struct udmabuf {
pgoff_t pagecount;
- struct folio **folios;
+ struct page **pages;
/**
- * Unlike folios, pinned_folios is only used for unpin.
+ * Unlike pages, pinned_folios is only used for unpin.
* So, nr_pinned is not the same to pagecount, the pinned_folios
* only set each folio which already pinned when udmabuf_create.
* Note that, since a folio may be pinned multiple times, each folio
@@ -40,8 +40,8 @@ struct udmabuf {
struct folio **pinned_folios;
struct sg_table *sg;
+ enum dma_data_direction sg_dir;
struct miscdevice *device;
- pgoff_t *offsets;
};
static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf)
@@ -55,8 +55,7 @@ static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf)
if (pgoff >= ubuf->pagecount)
return VM_FAULT_SIGBUS;
- pfn = folio_pfn(ubuf->folios[pgoff]);
- pfn += ubuf->offsets[pgoff] >> PAGE_SHIFT;
+ pfn = page_to_pfn(ubuf->pages[pgoff]);
ret = vmf_insert_pfn(vma, vmf->address, pfn);
if (ret & VM_FAULT_ERROR)
@@ -73,8 +72,7 @@ static vm_fault_t udmabuf_vm_fault(struct vm_fault *vmf)
if (WARN_ON(pgoff >= ubuf->pagecount))
break;
- pfn = folio_pfn(ubuf->folios[pgoff]);
- pfn += ubuf->offsets[pgoff] >> PAGE_SHIFT;
+ pfn = page_to_pfn(ubuf->pages[pgoff]);
/**
* If the below vmf_insert_pfn() fails, we do not return an
@@ -109,22 +107,11 @@ static int mmap_udmabuf(struct dma_buf *buf, struct vm_area_struct *vma)
static int vmap_udmabuf(struct dma_buf *buf, struct iosys_map *map)
{
struct udmabuf *ubuf = buf->priv;
- struct page **pages;
void *vaddr;
- pgoff_t pg;
dma_resv_assert_held(buf->resv);
- pages = kvmalloc_objs(*pages, ubuf->pagecount);
- if (!pages)
- return -ENOMEM;
-
- for (pg = 0; pg < ubuf->pagecount; pg++)
- pages[pg] = folio_page(ubuf->folios[pg],
- ubuf->offsets[pg] >> PAGE_SHIFT);
-
- vaddr = vm_map_ram(pages, ubuf->pagecount, -1);
- kvfree(pages);
+ vaddr = vm_map_ram(ubuf->pages, ubuf->pagecount, -1);
if (!vaddr)
return -EINVAL;
@@ -146,23 +133,19 @@ static struct sg_table *get_sg_table(struct device *dev, struct dma_buf *buf,
{
struct udmabuf *ubuf = buf->priv;
struct sg_table *sg;
- struct scatterlist *sgl;
- unsigned int i = 0;
int ret;
sg = kzalloc_obj(*sg);
if (!sg)
return ERR_PTR(-ENOMEM);
- ret = sg_alloc_table(sg, ubuf->pagecount, GFP_KERNEL);
+ ret = sg_alloc_table_from_pages(sg, ubuf->pages, ubuf->pagecount, 0,
+ ubuf->pagecount << PAGE_SHIFT,
+ GFP_KERNEL);
if (ret < 0)
goto err_alloc;
- for_each_sg(sg->sgl, sgl, ubuf->pagecount, i)
- sg_set_folio(sgl, ubuf->folios[i], PAGE_SIZE,
- ubuf->offsets[i]);
-
- ret = dma_map_sgtable(dev, sg, direction, 0);
+ ret = dma_map_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC);
if (ret < 0)
goto err_map;
return sg;
@@ -177,7 +160,7 @@ err_alloc:
static void put_sg_table(struct device *dev, struct sg_table *sg,
enum dma_data_direction direction)
{
- dma_unmap_sgtable(dev, sg, direction, 0);
+ dma_unmap_sgtable(dev, sg, direction, DMA_ATTR_SKIP_CPU_SYNC);
sg_free_table(sg);
kfree(sg);
}
@@ -207,12 +190,8 @@ static void unpin_all_folios(struct udmabuf *ubuf)
static __always_inline int init_udmabuf(struct udmabuf *ubuf, pgoff_t pgcnt)
{
- ubuf->folios = kvmalloc_objs(*ubuf->folios, pgcnt);
- if (!ubuf->folios)
- return -ENOMEM;
-
- ubuf->offsets = kvzalloc_objs(*ubuf->offsets, pgcnt);
- if (!ubuf->offsets)
+ ubuf->pages = kvmalloc_objs(*ubuf->pages, pgcnt);
+ if (!ubuf->pages)
return -ENOMEM;
ubuf->pinned_folios = kvmalloc_objs(*ubuf->pinned_folios, pgcnt);
@@ -225,8 +204,7 @@ static __always_inline int init_udmabuf(struct udmabuf *ubuf, pgoff_t pgcnt)
static __always_inline void deinit_udmabuf(struct udmabuf *ubuf)
{
unpin_all_folios(ubuf);
- kvfree(ubuf->offsets);
- kvfree(ubuf->folios);
+ kvfree(ubuf->pages);
}
static void release_udmabuf(struct dma_buf *buf)
@@ -235,7 +213,7 @@ static void release_udmabuf(struct dma_buf *buf)
struct device *dev = ubuf->device->this_device;
if (ubuf->sg)
- put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL);
+ put_sg_table(dev, ubuf->sg, ubuf->sg_dir);
deinit_udmabuf(ubuf);
kfree(ubuf);
@@ -253,6 +231,8 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
if (IS_ERR(ubuf->sg)) {
ret = PTR_ERR(ubuf->sg);
ubuf->sg = NULL;
+ } else {
+ ubuf->sg_dir = direction;
}
} else {
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
@@ -344,8 +324,8 @@ static long udmabuf_pin_folios(struct udmabuf *ubuf, struct file *memfd,
ubuf->pinned_folios[nr_pinned++] = folios[cur_folio];
for (; subpgoff < fsize; subpgoff += PAGE_SIZE) {
- ubuf->folios[upgcnt] = folios[cur_folio];
- ubuf->offsets[upgcnt] = subpgoff;
+ ubuf->pages[upgcnt] = folio_page(folios[cur_folio],
+ subpgoff >> PAGE_SHIFT);
++upgcnt;
if (++cur_pgcnt >= pgcnt)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index b24d5d21be5f..32af8cce3df8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -1100,7 +1100,8 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
if (p->gang_size > 1 && !adev->vm_manager.concurrent_flush) {
for (i = 0; i < p->gang_size; ++i) {
struct drm_sched_entity *entity = p->entities[i];
- struct drm_gpu_scheduler *sched = entity->rq->sched;
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->rq, typeof(*sched), rq);
struct amdgpu_ring *ring = to_amdgpu_ring(sched);
if (amdgpu_vmid_uses_reserved(vm, ring->vm_hub))
@@ -1231,7 +1232,8 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
return r;
}
- sched = p->gang_leader->base.entity->rq->sched;
+ sched = container_of(p->gang_leader->base.entity->rq, typeof(*sched),
+ rq);
while ((fence = amdgpu_sync_get_fence(&p->sync))) {
struct drm_sched_fence *s_fence = to_drm_sched_fence(fence);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 737ef1ef96a5..20fcfa09e9e3 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -2221,7 +2221,6 @@ static int amdgpu_device_init_schedulers(struct amdgpu_device *adev)
{
struct drm_sched_init_args args = {
.ops = &amdgpu_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.timeout_wq = adev->reset_domain->wq,
.dev = adev->dev,
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 07771721af9d..9ecc6387c1eb 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -388,7 +388,9 @@ static struct dma_fence *
amdgpu_job_prepare_job(struct drm_sched_job *sched_job,
struct drm_sched_entity *s_entity)
{
- struct amdgpu_ring *ring = to_amdgpu_ring(s_entity->rq->sched);
+ struct drm_gpu_scheduler *sched =
+ container_of(s_entity->rq, typeof(*sched), rq);
+ struct amdgpu_ring *ring = to_amdgpu_ring(sched);
struct amdgpu_job *job = to_amdgpu_job(sched_job);
struct dma_fence *fence;
int r;
@@ -481,25 +483,22 @@ drm_sched_entity_queue_pop(struct drm_sched_entity *entity)
void amdgpu_job_stop_all_jobs_on_sched(struct drm_gpu_scheduler *sched)
{
+ struct drm_sched_rq *rq = &sched->rq;
+ struct drm_sched_entity *s_entity;
struct drm_sched_job *s_job;
- struct drm_sched_entity *s_entity = NULL;
- int i;
/* Signal all jobs not yet scheduled */
- for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
- struct drm_sched_rq *rq = sched->sched_rq[i];
- spin_lock(&rq->lock);
- list_for_each_entry(s_entity, &rq->entities, list) {
- while ((s_job = drm_sched_entity_queue_pop(s_entity))) {
- struct drm_sched_fence *s_fence = s_job->s_fence;
-
- dma_fence_signal(&s_fence->scheduled);
- dma_fence_set_error(&s_fence->finished, -EHWPOISON);
- dma_fence_signal(&s_fence->finished);
- }
+ spin_lock(&rq->lock);
+ list_for_each_entry(s_entity, &rq->entities, list) {
+ while ((s_job = drm_sched_entity_queue_pop(s_entity))) {
+ struct drm_sched_fence *s_fence = s_job->s_fence;
+
+ dma_fence_signal(&s_fence->scheduled);
+ dma_fence_set_error(&s_fence->finished, -EHWPOISON);
+ dma_fence_signal(&s_fence->finished);
}
- spin_unlock(&rq->lock);
}
+ spin_unlock(&rq->lock);
/* Signal all jobs already scheduled to HW */
list_for_each_entry(s_job, &sched->pending_list, list) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
index 56a88e14a044..e70a1117b812 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h
@@ -107,7 +107,10 @@ struct amdgpu_job {
static inline struct amdgpu_ring *amdgpu_job_ring(struct amdgpu_job *job)
{
- return to_amdgpu_ring(job->base.entity->rq->sched);
+ struct drm_gpu_scheduler *sched =
+ container_of(job->base.entity->rq, typeof(*sched), rq);
+
+ return to_amdgpu_ring(sched);
}
int amdgpu_job_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
index d13e64a69e25..85724ec6aaf8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
@@ -145,6 +145,7 @@ TRACE_EVENT(amdgpu_cs,
struct amdgpu_ib *ib),
TP_ARGS(p, job, ib),
TP_STRUCT__entry(
+ __field(struct drm_gpu_scheduler *, sched)
__field(struct amdgpu_bo_list *, bo_list)
__field(u32, ring)
__field(u32, dw)
@@ -152,11 +153,14 @@ TRACE_EVENT(amdgpu_cs,
),
TP_fast_assign(
+ __entry->sched = container_of(job->base.entity->rq,
+ typeof(*__entry->sched),
+ rq);
__entry->bo_list = p->bo_list;
- __entry->ring = to_amdgpu_ring(job->base.entity->rq->sched)->idx;
+ __entry->ring = to_amdgpu_ring(__entry->sched)->idx;
__entry->dw = ib->length_dw;
__entry->fences = amdgpu_fence_count_emitted(
- to_amdgpu_ring(job->base.entity->rq->sched));
+ to_amdgpu_ring(__entry->sched));
),
TP_printk("bo_list=%p, ring=%u, dw=%u, fences=%u",
__entry->bo_list, __entry->ring, __entry->dw,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
index 36805dcfa159..4ccd2e769799 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c
@@ -106,13 +106,13 @@ static int amdgpu_vm_sdma_prepare(struct amdgpu_vm_update_params *p,
static int amdgpu_vm_sdma_commit(struct amdgpu_vm_update_params *p,
struct dma_fence **fence)
{
+ struct drm_gpu_scheduler *sched =
+ container_of(p->vm->delayed.rq, typeof(*sched), rq);
+ struct amdgpu_ring *ring =
+ container_of(sched, struct amdgpu_ring, sched);
struct amdgpu_ib *ib = p->job->ibs;
- struct amdgpu_ring *ring;
struct dma_fence *f;
- ring = container_of(p->vm->delayed.rq->sched, struct amdgpu_ring,
- sched);
-
WARN_ON(ib->length_dw == 0);
amdgpu_ring_pad_ib(ring, ib);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
index 42be8ee155dd..409e103ffe8c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c
@@ -466,15 +466,15 @@ int amdgpu_xcp_open_device(struct amdgpu_device *adev,
void amdgpu_xcp_release_sched(struct amdgpu_device *adev,
struct amdgpu_ctx_entity *entity)
{
- struct drm_gpu_scheduler *sched;
- struct amdgpu_ring *ring;
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->entity.rq, typeof(*sched), rq);
if (!adev->xcp_mgr)
return;
- sched = entity->entity.rq->sched;
if (drm_sched_wqueue_ready(sched)) {
- ring = to_amdgpu_ring(entity->entity.rq->sched);
+ struct amdgpu_ring *ring = to_amdgpu_ring(sched);
+
atomic_dec(&adev->xcp_mgr->xcp[ring->xcp_id].ref_cnt);
}
}
diff --git a/drivers/gpu/drm/ast/ast_2000.c b/drivers/gpu/drm/ast/ast_2000.c
index fa3bc23ce098..4cf951b3533d 100644
--- a/drivers/gpu/drm/ast/ast_2000.c
+++ b/drivers/gpu/drm/ast/ast_2000.c
@@ -69,64 +69,59 @@ void ast_2000_set_def_ext_reg(struct ast_device *ast)
}
static const struct ast_dramstruct ast2000_dram_table_data[] = {
- { 0x0108, 0x00000000 },
- { 0x0120, 0x00004a21 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR108, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR120, 0x00004a21),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x0000, 0xFFFFFFFF },
- AST_DRAMSTRUCT_INIT(DRAM_TYPE, 0x00000089),
- { 0x0008, 0x22331353 },
- { 0x000C, 0x0d07000b },
- { 0x0010, 0x11113333 },
- { 0x0020, 0x00110350 },
- { 0x0028, 0x1e0828f0 },
- { 0x0024, 0x00000001 },
- { 0x001C, 0x00000000 },
- { 0x0014, 0x00000003 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR00, 0xffffffff), /* FIXME: This locks the MCR registers. */
+ AST_DRAMSTRUCT_REG(AST_REG_MCR04, 0x00000089), /* DRAM type */
+ AST_DRAMSTRUCT_REG(AST_REG_MCR08, 0x22331353),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x0d07000b),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR10, 0x11113333),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR20, 0x00110350),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x1e0828f0),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR1C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x00000003),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x0018, 0x00000131 },
- { 0x0014, 0x00000001 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x00000131),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x00000001),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x0018, 0x00000031 },
- { 0x0014, 0x00000001 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x00000031),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x00000001),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x0028, 0x1e0828f1 },
- { 0x0024, 0x00000003 },
- { 0x002C, 0x1f0f28fb },
- { 0x0030, 0xFFFFFE01 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x1e0828f1),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x1f0f28fb),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0xfffffe01),
AST_DRAMSTRUCT_INVALID,
};
static void ast_post_chip_2000(struct ast_device *ast)
{
u8 j;
- u32 temp, i;
- const struct ast_dramstruct *dram_reg_info;
+ u32 i;
j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
if ((j & 0x80) == 0) { /* VGA only */
- dram_reg_info = ast2000_dram_table_data;
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
- ast_write32(ast, 0x10100, 0xa8);
+ const struct ast_dramstruct *dram_reg_info = ast2000_dram_table_data;
+ u32 mcr140;
- do {
- ;
- } while (ast_read32(ast, 0x10100) != 0xa8);
+ ast_moutdwm_poll(ast, AST_REG_MCR100, 0xa8, 0xa8);
while (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) {
if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) {
for (i = 0; i < 15; i++)
udelay(dram_reg_info->data);
} else {
- ast_write32(ast, 0x10000 + dram_reg_info->index,
- dram_reg_info->data);
+ ast_moutdwm(ast, dram_reg_info->index, dram_reg_info->data);
}
dram_reg_info++;
}
- temp = ast_read32(ast, 0x10140);
- ast_write32(ast, 0x10140, temp | 0x40);
+ mcr140 = ast_mindwm(ast, AST_REG_MCR140);
+ mcr140 |= 0x00000040;
+ ast_moutdwm(ast, AST_REG_MCR140, mcr140);
}
/* wait ready */
diff --git a/drivers/gpu/drm/ast/ast_2100.c b/drivers/gpu/drm/ast/ast_2100.c
index 05aeb0624d41..5cf8ad9c3446 100644
--- a/drivers/gpu/drm/ast/ast_2100.c
+++ b/drivers/gpu/drm/ast/ast_2100.c
@@ -40,21 +40,19 @@
static enum ast_dram_layout ast_2100_get_dram_layout_p2a(struct ast_device *ast)
{
- u32 mcr_cfg;
+ u32 mcr04;
enum ast_dram_layout dram_layout;
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
- mcr_cfg = ast_read32(ast, 0x10004);
+ mcr04 = ast_mindwm(ast, AST_REG_MCR04);
- switch (mcr_cfg & 0x0c) {
+ switch (mcr04 & GENMASK(3, 2)) {
case 0:
case 4:
default:
dram_layout = AST_DRAM_512Mx16;
break;
case 8:
- if (mcr_cfg & 0x40)
+ if (mcr04 & 0x40)
dram_layout = AST_DRAM_1Gx16;
else
dram_layout = AST_DRAM_512Mx32;
@@ -72,108 +70,108 @@ static enum ast_dram_layout ast_2100_get_dram_layout_p2a(struct ast_device *ast)
*/
static const struct ast_dramstruct ast1100_dram_table_data[] = {
- { 0x2000, 0x1688a8a8 },
- { 0x2020, 0x000041f0 },
+ AST_DRAMSTRUCT_REG(AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_SCU020, 0x000041f0),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x0000, 0xfc600309 },
- { 0x006C, 0x00909090 },
- { 0x0064, 0x00050000 },
- AST_DRAMSTRUCT_INIT(DRAM_TYPE, 0x00000585),
- { 0x0008, 0x0011030f },
- { 0x0010, 0x22201724 },
- { 0x0018, 0x1e29011a },
- { 0x0020, 0x00c82222 },
- { 0x0014, 0x01001523 },
- { 0x001C, 0x1024010d },
- { 0x0024, 0x00cb2522 },
- { 0x0038, 0xffffff82 },
- { 0x003C, 0x00000000 },
- { 0x0040, 0x00000000 },
- { 0x0044, 0x00000000 },
- { 0x0048, 0x00000000 },
- { 0x004C, 0x00000000 },
- { 0x0050, 0x00000000 },
- { 0x0054, 0x00000000 },
- { 0x0058, 0x00000000 },
- { 0x005C, 0x00000000 },
- { 0x0060, 0x032aa02a },
- { 0x0064, 0x002d3000 },
- { 0x0068, 0x00000000 },
- { 0x0070, 0x00000000 },
- { 0x0074, 0x00000000 },
- { 0x0078, 0x00000000 },
- { 0x007C, 0x00000000 },
- { 0x0034, 0x00000001 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR6C, 0x00909090),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x00050000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR04, 0x00000585), // DRAM type
+ AST_DRAMSTRUCT_REG(AST_REG_MCR08, 0x0011030f),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR10, 0x22201724),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x1e29011a),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR20, 0x00c82222),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x01001523),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR1C, 0x1024010d),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00cb2522),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR38, 0xffffff82),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR3C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR40, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR44, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR48, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR4C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR50, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR54, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR58, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR5C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR60, 0x032aa02a),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x002d3000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR68, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR70, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR74, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR78, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR7C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00000001),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x002C, 0x00000732 },
- { 0x0030, 0x00000040 },
- { 0x0028, 0x00000005 },
- { 0x0028, 0x00000007 },
- { 0x0028, 0x00000003 },
- { 0x0028, 0x00000001 },
- { 0x000C, 0x00005a08 },
- { 0x002C, 0x00000632 },
- { 0x0028, 0x00000001 },
- { 0x0030, 0x000003c0 },
- { 0x0028, 0x00000003 },
- { 0x0030, 0x00000040 },
- { 0x0028, 0x00000003 },
- { 0x000C, 0x00005a21 },
- { 0x0034, 0x00007c03 },
- { 0x0120, 0x00004c41 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000732),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000005),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000007),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a08),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000632),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x000003c0),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a21),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00007c03),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR120, 0x00004c41),
AST_DRAMSTRUCT_INVALID,
};
static const struct ast_dramstruct ast2100_dram_table_data[] = {
- { 0x2000, 0x1688a8a8 },
- { 0x2020, 0x00004120 },
+ AST_DRAMSTRUCT_REG(AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_SCU020, 0x00004120),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x0000, 0xfc600309 },
- { 0x006C, 0x00909090 },
- { 0x0064, 0x00070000 },
- AST_DRAMSTRUCT_INIT(DRAM_TYPE, 0x00000489),
- { 0x0008, 0x0011030f },
- { 0x0010, 0x32302926 },
- { 0x0018, 0x274c0122 },
- { 0x0020, 0x00ce2222 },
- { 0x0014, 0x01001523 },
- { 0x001C, 0x1024010d },
- { 0x0024, 0x00cb2522 },
- { 0x0038, 0xffffff82 },
- { 0x003C, 0x00000000 },
- { 0x0040, 0x00000000 },
- { 0x0044, 0x00000000 },
- { 0x0048, 0x00000000 },
- { 0x004C, 0x00000000 },
- { 0x0050, 0x00000000 },
- { 0x0054, 0x00000000 },
- { 0x0058, 0x00000000 },
- { 0x005C, 0x00000000 },
- { 0x0060, 0x0f2aa02a },
- { 0x0064, 0x003f3005 },
- { 0x0068, 0x02020202 },
- { 0x0070, 0x00000000 },
- { 0x0074, 0x00000000 },
- { 0x0078, 0x00000000 },
- { 0x007C, 0x00000000 },
- { 0x0034, 0x00000001 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR6C, 0x00909090),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x00070000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR04, 0x00000489), // DRAM type
+ AST_DRAMSTRUCT_REG(AST_REG_MCR08, 0x0011030f),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR10, 0x32302926),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR18, 0x274c0122),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR20, 0x00ce2222),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR14, 0x01001523),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR1C, 0x1024010d),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR24, 0x00cb2522),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR38, 0xffffff82),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR3C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR40, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR44, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR48, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR4C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR50, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR54, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR58, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR5C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR60, 0x0f2aa02a),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR64, 0x003f3005),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR68, 0x02020202),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR70, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR74, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR78, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR7C, 0x00000000),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00000001),
AST_DRAMSTRUCT_UDELAY(67u),
- { 0x002C, 0x00000942 },
- { 0x0030, 0x00000040 },
- { 0x0028, 0x00000005 },
- { 0x0028, 0x00000007 },
- { 0x0028, 0x00000003 },
- { 0x0028, 0x00000001 },
- { 0x000C, 0x00005a08 },
- { 0x002C, 0x00000842 },
- { 0x0028, 0x00000001 },
- { 0x0030, 0x000003c0 },
- { 0x0028, 0x00000003 },
- { 0x0030, 0x00000040 },
- { 0x0028, 0x00000003 },
- { 0x000C, 0x00005a21 },
- { 0x0034, 0x00007c03 },
- { 0x0120, 0x00005061 },
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000942),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000005),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000007),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a08),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR2C, 0x00000842),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000001),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x000003c0),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR30, 0x00000040),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR28, 0x00000003),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR0C, 0x00005a21),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR34, 0x00007c03),
+ AST_DRAMSTRUCT_REG(AST_REG_MCR120, 0x00005061),
AST_DRAMSTRUCT_INVALID,
};
@@ -209,28 +207,28 @@ static u32 mmctestburst2_ast2150(struct ast_device *ast, u32 datagen)
{
u32 data, timeout;
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
- ast_moutdwm(ast, 0x1e6e0070, 0x00000001 | (datagen << 3));
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000001 | (datagen << 3));
timeout = 0;
do {
- data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
return 0xffffffff;
}
} while (!data);
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
- ast_moutdwm(ast, 0x1e6e0070, 0x00000003 | (datagen << 3));
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000003 | (datagen << 3));
timeout = 0;
do {
- data = ast_mindwm(ast, 0x1e6e0070) & 0x40;
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x40;
if (++timeout > TIMEOUT_AST2150) {
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
return 0xffffffff;
}
} while (!data);
- data = (ast_mindwm(ast, 0x1e6e0070) & 0x80) >> 7;
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ data = (ast_mindwm(ast, AST_REG_MCR70) & 0x80) >> 7;
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
return data;
}
@@ -249,7 +247,7 @@ static int cbrscan_ast2150(struct ast_device *ast, int busw)
u32 patcnt, loop;
for (patcnt = 0; patcnt < CBR_PATNUM_AST2150; patcnt++) {
- ast_moutdwm(ast, 0x1e6e007c, pattern_AST2150[patcnt]);
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern_AST2150[patcnt]);
for (loop = 0; loop < CBR_PASSNUM_AST2150; loop++) {
if (cbrtest_ast2150(ast))
break;
@@ -276,7 +274,7 @@ cbr_start:
passcnt = 0;
for (dlli = 0; dlli < 100; dlli++) {
- ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR68, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
data = cbrscan_ast2150(ast, busw);
if (data != 0) {
if (data & 0x1) {
@@ -294,78 +292,82 @@ cbr_start:
goto cbr_start;
dlli = dll_min[0] + (((dll_max[0] - dll_min[0]) * 7) >> 4);
- ast_moutdwm(ast, 0x1e6e0068, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR68, dlli | (dlli << 8) | (dlli << 16) | (dlli << 24));
}
static void ast_post_chip_2100(struct ast_device *ast)
{
u8 j;
- u32 data, temp, i;
- const struct ast_dramstruct *dram_reg_info;
- enum ast_dram_layout dram_layout = ast_2100_get_dram_layout_p2a(ast);
+ u32 i;
+ enum ast_dram_layout dram_layout = ast_2100_get_dram_layout_p2a(ast);
+ u32 mcr120;
+ u32 scu00c, scu040;
j = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
if ((j & 0x80) == 0) { /* VGA only */
+ const struct ast_dramstruct *dram_reg_info;
+
if (ast->chip == AST2100 || ast->chip == AST2200)
dram_reg_info = ast2100_dram_table_data;
else
dram_reg_info = ast1100_dram_table_data;
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
- ast_write32(ast, 0x12000, 0x1688A8A8);
- do {
- ;
- } while (ast_read32(ast, 0x12000) != 0x01);
-
- ast_write32(ast, 0x10000, 0xfc600309);
- do {
- ;
- } while (ast_read32(ast, 0x10000) != 0x01);
+ ast_moutdwm_poll(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY, 0x01);
+ ast_moutdwm_poll(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY, 0x01);
while (!AST_DRAMSTRUCT_IS(dram_reg_info, INVALID)) {
if (AST_DRAMSTRUCT_IS(dram_reg_info, UDELAY)) {
for (i = 0; i < 15; i++)
udelay(dram_reg_info->data);
- } else if (AST_DRAMSTRUCT_IS(dram_reg_info, DRAM_TYPE)) {
+ } else if (AST_DRAMSTRUCT_IS_REG(dram_reg_info, AST_REG_MCR04)) {
+ u32 mcr04;
+ u32 scu070;
+
switch (dram_layout) {
case AST_DRAM_1Gx16:
- data = 0x00000d89;
+ mcr04 = 0x00000d89;
break;
case AST_DRAM_1Gx32:
- data = 0x00000c8d;
+ mcr04 = 0x00000c8d;
break;
default:
- data = dram_reg_info->data;
+ mcr04 = dram_reg_info->data;
break;
}
- temp = ast_read32(ast, 0x12070);
- temp &= 0xc;
- temp <<= 2;
- ast_write32(ast, 0x10000 + dram_reg_info->index, data | temp);
+ /*
+ * FIXME: There might be bits already in MCR04[5:4]. Should
+ * we only do this in the default case?
+ */
+ scu070 = ast_mindwm(ast, AST_REG_SCU070);
+ mcr04 |= (scu070 & GENMASK(3, 2)) << 2;
+
+ ast_moutdwm(ast, dram_reg_info->index, mcr04);
} else {
- ast_write32(ast, 0x10000 + dram_reg_info->index,
- dram_reg_info->data);
+ ast_moutdwm(ast, dram_reg_info->index, dram_reg_info->data);
}
dram_reg_info++;
}
/* AST 2100/2150 DRAM calibration */
- data = ast_read32(ast, 0x10120);
- if (data == 0x5061) { /* 266Mhz */
- data = ast_read32(ast, 0x10004);
- if (data & 0x40)
+ mcr120 = ast_mindwm(ast, AST_REG_MCR120);
+ if (mcr120 == 0x5061) { /* 266Mhz */
+ u32 mcr04 = ast_mindwm(ast, AST_REG_MCR04);
+
+ if (mcr04 & 0x40)
cbrdlli_ast2150(ast, 16); /* 16 bits */
else
cbrdlli_ast2150(ast, 32); /* 32 bits */
}
- temp = ast_read32(ast, 0x1200c);
- ast_write32(ast, 0x1200c, temp & 0xfffffffd);
- temp = ast_read32(ast, 0x12040);
- ast_write32(ast, 0x12040, temp | 0x40);
+ scu00c = ast_mindwm(ast, AST_REG_SCU00C);
+ scu00c &= 0xfffffffd;
+ ast_moutdwm(ast, AST_REG_SCU00C, scu00c);
+
+ scu040 = ast_mindwm(ast, AST_REG_SCU040);
+ scu040 |= 0x00000040;
+ ast_moutdwm(ast, AST_REG_SCU040, scu040);
}
/* wait ready */
diff --git a/drivers/gpu/drm/ast/ast_2300.c b/drivers/gpu/drm/ast/ast_2300.c
index 5f50d9f91ffd..cbeac6990c27 100644
--- a/drivers/gpu/drm/ast/ast_2300.c
+++ b/drivers/gpu/drm/ast/ast_2300.c
@@ -129,19 +129,19 @@ static u32 mmc_test2(struct ast_device *ast, u32 datagen, u8 test_ctl)
{
u32 data, timeout;
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
- ast_moutdwm(ast, 0x1e6e0070, (datagen << 3) | test_ctl);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, (datagen << 3) | test_ctl);
timeout = 0;
do {
- data = ast_mindwm(ast, 0x1e6e0070) & 0x1000;
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x1000;
if (++timeout > TIMEOUT) {
- ast_moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x0);
return 0xffffffff;
}
} while (!data);
- data = ast_mindwm(ast, 0x1e6e0078);
+ data = ast_mindwm(ast, AST_REG_MCR78);
data = (data | (data >> 16)) & 0xffff;
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
return data;
}
@@ -186,7 +186,7 @@ static int cbr_scan(struct ast_device *ast)
data2 = 3;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
- ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern[patcnt]);
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
data = cbr_test(ast);
if (data != 0) {
@@ -222,7 +222,7 @@ static u32 cbr_scan2(struct ast_device *ast)
data2 = 0xffff;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
- ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern[patcnt]);
for (loop = 0; loop < CBR_PASSNUM2; loop++) {
data = cbr_test2(ast);
if (data != 0) {
@@ -252,7 +252,7 @@ static bool cbr_scan3(struct ast_device *ast)
u32 patcnt, loop;
for (patcnt = 0; patcnt < CBR_PATNUM; patcnt++) {
- ast_moutdwm(ast, 0x1e6e007c, pattern[patcnt]);
+ ast_moutdwm(ast, AST_REG_MCR7C, pattern[patcnt]);
for (loop = 0; loop < 2; loop++) {
if (cbr_test3(ast))
break;
@@ -274,8 +274,8 @@ FINETUNE_START:
}
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
- ast_moutdwm(ast, 0x1E6E0068, 0x00001400 | (dlli << 16) | (dlli << 24));
- ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE1);
+ ast_moutdwm(ast, AST_REG_MCR68, 0x00001400 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR74, CBR_SIZE1);
data = cbr_scan2(ast);
if (data != 0) {
mask = 0x00010001;
@@ -330,7 +330,7 @@ FINETUNE_DONE:
data |= dlli << 21;
}
}
- ast_moutdwm(ast, 0x1E6E0080, data);
+ ast_moutdwm(ast, AST_REG_MCR80, data);
data = 0;
for (cnt = 8; cnt < 16; cnt++) {
@@ -354,7 +354,7 @@ FINETUNE_DONE:
data |= dlli << 21;
}
}
- ast_moutdwm(ast, 0x1E6E0084, data);
+ ast_moutdwm(ast, AST_REG_MCR84, data);
return status;
} /* finetuneDQI_L */
@@ -367,10 +367,10 @@ static void finetuneDQSI(struct ast_device *ast)
char tag[2][76];
/* Disable DQI CBR */
- reg_mcr0c = ast_mindwm(ast, 0x1E6E000C);
- reg_mcr18 = ast_mindwm(ast, 0x1E6E0018);
+ reg_mcr0c = ast_mindwm(ast, AST_REG_MCR0C);
+ reg_mcr18 = ast_mindwm(ast, AST_REG_MCR18);
reg_mcr18 &= 0x0000ffff;
- ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
+ ast_moutdwm(ast, AST_REG_MCR18, reg_mcr18);
for (dlli = 0; dlli < 76; dlli++) {
tag[0][dlli] = 0x0;
@@ -386,14 +386,14 @@ static void finetuneDQSI(struct ast_device *ast)
passcnt[0] = 0;
passcnt[1] = 0;
for (dqsip = 0; dqsip < 2; dqsip++) {
- ast_moutdwm(ast, 0x1E6E000C, 0);
- ast_moutdwm(ast, 0x1E6E0018, reg_mcr18 | (dqidly << 16) | (dqsip << 23));
- ast_moutdwm(ast, 0x1E6E000C, reg_mcr0c);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0);
+ ast_moutdwm(ast, AST_REG_MCR18, reg_mcr18 | (dqidly << 16) | (dqsip << 23));
+ ast_moutdwm(ast, AST_REG_MCR0C, reg_mcr0c);
for (dlli = 0; dlli < 76; dlli++) {
- ast_moutdwm(ast, 0x1E6E0068,
+ ast_moutdwm(ast, AST_REG_MCR68,
0x00001300 | (dlli << 16) | (dlli << 24));
- ast_moutdwm(ast, 0x1E6E0070, 0);
- ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE0);
+ ast_moutdwm(ast, AST_REG_MCR70, 0);
+ ast_moutdwm(ast, AST_REG_MCR74, CBR_SIZE0);
if (cbr_scan3(ast)) {
if (dlli == 0)
break;
@@ -457,7 +457,7 @@ static void finetuneDQSI(struct ast_device *ast)
}
}
reg_mcr18 = reg_mcr18 | (g_dqidly << 16) | (g_dqsip << 23);
- ast_moutdwm(ast, 0x1E6E0018, reg_mcr18);
+ ast_moutdwm(ast, AST_REG_MCR18, reg_mcr18);
}
static bool cbr_dll2(struct ast_device *ast, struct ast2300_dram_param *param)
@@ -476,8 +476,8 @@ CBR_START2:
dllmax[1] = 0x0;
passcnt = 0;
for (dlli = 0; dlli < 76; dlli++) {
- ast_moutdwm(ast, 0x1E6E0068, 0x00001300 | (dlli << 16) | (dlli << 24));
- ast_moutdwm(ast, 0x1E6E0074, CBR_SIZE2);
+ ast_moutdwm(ast, AST_REG_MCR68, 0x00001300 | (dlli << 16) | (dlli << 24));
+ ast_moutdwm(ast, AST_REG_MCR74, CBR_SIZE2);
data = cbr_scan(ast);
if (data != 0) {
if (data & 0x1) {
@@ -508,7 +508,7 @@ CBR_DONE2:
dlli = (dllmin[1] + dllmax[1]) >> 1;
dlli <<= 8;
dlli += (dllmin[0] + dllmax[0]) >> 1;
- ast_moutdwm(ast, 0x1E6E0068, ast_mindwm(ast, 0x1E720058) | (dlli << 16));
+ ast_moutdwm(ast, AST_REG_MCR68, ast_mindwm(ast, AST_REG_A2P58) | (dlli << 16));
return status;
} /* CBRDLL2 */
@@ -516,10 +516,10 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
{
u32 trap, trap_AC2, trap_MRS;
- ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
/* Ger trap info */
- trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
+ trap = (ast_mindwm(ast, AST_REG_SCU070) >> 25) & 0x3;
trap_AC2 = 0x00020000 + (trap << 16);
trap_AC2 |= 0x00300000 + ((trap & 0x2) << 19);
trap_MRS = 0x00000010 + (trap << 4);
@@ -533,7 +533,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
switch (param->dram_freq) {
case 336:
- ast_moutdwm(ast, 0x1E6E2020, 0x0190);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0190);
param->wodt = 0;
param->reg_AC1 = 0x22202725;
param->reg_AC2 = 0xAA007613 | trap_AC2;
@@ -561,7 +561,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
break;
default:
case 396:
- ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x03F1);
param->wodt = 1;
param->reg_AC1 = 0x33302825;
param->reg_AC2 = 0xCC009617 | trap_AC2;
@@ -591,7 +591,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
break;
case 408:
- ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x01F0);
param->wodt = 1;
param->reg_AC1 = 0x33302825;
param->reg_AC2 = 0xCC009617 | trap_AC2;
@@ -621,7 +621,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
break;
case 456:
- ast_moutdwm(ast, 0x1E6E2020, 0x0230);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0230);
param->wodt = 0;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xCD44961A;
@@ -635,7 +635,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 4;
break;
case 504:
- ast_moutdwm(ast, 0x1E6E2020, 0x0270);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0270);
param->wodt = 1;
param->reg_AC1 = 0x33302926;
param->reg_AC2 = 0xDE44A61D;
@@ -649,7 +649,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 4;
break;
case 528:
- ast_moutdwm(ast, 0x1E6E2020, 0x0290);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0290);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302926;
@@ -665,7 +665,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 576:
- ast_moutdwm(ast, 0x1E6E2020, 0x0140);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0140);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
@@ -683,7 +683,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 600:
- ast_moutdwm(ast, 0x1E6E2020, 0x02E1);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x02E1);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
@@ -701,7 +701,7 @@ static void get_ddr3_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 624:
- ast_moutdwm(ast, 0x1E6E2020, 0x0160);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0160);
param->reg_MADJ = 0x00136868;
param->reg_SADJ = 0x00004534;
param->wodt = 1;
@@ -758,115 +758,115 @@ static void ddr3_init(struct ast_device *ast, struct ast2300_dram_param *param)
u32 data, data2, retry = 0;
ddr3_init_start:
- ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
- ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
- ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0034, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00000100);
+ ast_moutdwm(ast, AST_REG_MCR24, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00000000);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
- ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ);
+ ast_moutdwm(ast, AST_REG_MCR68, param->reg_SADJ);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ | 0xC0000);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
- ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
- ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
- ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
- ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
- ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
- ast_moutdwm(ast, 0x1E6E0018, 0x4000A170);
- ast_moutdwm(ast, 0x1E6E0018, 0x00002370);
- ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0040, 0xFF444444);
- ast_moutdwm(ast, 0x1E6E0044, 0x22222222);
- ast_moutdwm(ast, 0x1E6E0048, 0x22222222);
- ast_moutdwm(ast, 0x1E6E004C, 0x00000002);
- ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0054, 0);
- ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
- ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
- ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
- ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR04, param->dram_config);
+ ast_moutdwm(ast, AST_REG_MCR08, 0x90040f);
+ ast_moutdwm(ast, AST_REG_MCR10, param->reg_AC1);
+ ast_moutdwm(ast, AST_REG_MCR14, param->reg_AC2);
+ ast_moutdwm(ast, AST_REG_MCR20, param->reg_DQSIC);
+ ast_moutdwm(ast, AST_REG_MCR80, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR84, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR88, param->reg_DQIDLY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x4000A170);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00002370);
+ ast_moutdwm(ast, AST_REG_MCR38, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR40, 0xFF444444);
+ ast_moutdwm(ast, AST_REG_MCR44, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR48, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR4C, 0x00000002);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR54, 0);
+ ast_moutdwm(ast, AST_REG_MCR60, param->reg_DRV);
+ ast_moutdwm(ast, AST_REG_MCR6C, param->reg_IOZ);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR74, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR78, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
/* Wait MCLK2X lock to MCLK */
do {
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
} while (!(data & 0x08000000));
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
data = (data >> 8) & 0xff;
while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
- data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
+ data2 = (ast_mindwm(ast, AST_REG_MCR64) & 0xfff3ffff) + 4;
if ((data2 & 0xff) > param->madj_max)
break;
- ast_moutdwm(ast, 0x1E6E0064, data2);
+ ast_moutdwm(ast, AST_REG_MCR64, data2);
if (data2 & 0x00100000)
data2 = ((data2 & 0xff) >> 3) + 3;
else
data2 = ((data2 & 0xff) >> 2) + 5;
- data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
+ data = ast_mindwm(ast, AST_REG_MCR68) & 0xffff00ff;
data2 += data & 0xff;
data = data | (data2 << 8);
- ast_moutdwm(ast, 0x1E6E0068, data);
+ ast_moutdwm(ast, AST_REG_MCR68, data);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
+ ast_moutdwm(ast, AST_REG_MCR64, ast_mindwm(ast, AST_REG_MCR64) | 0xC0000);
udelay(10);
- data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
- ast_moutdwm(ast, 0x1E6E0018, data);
+ data = ast_mindwm(ast, AST_REG_MCR18) & 0xfffff1ff;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
data = data | 0x200;
- ast_moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, AST_REG_MCR18, data);
do {
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
} while (!(data & 0x08000000));
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
data = (data >> 8) & 0xff;
}
- ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0068) & 0xffff);
- data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
- ast_moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, AST_REG_A2P58, ast_mindwm(ast, AST_REG_MCR68) & 0xffff);
+ data = ast_mindwm(ast, AST_REG_MCR18) | 0xC00;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
- ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
- ast_moutdwm(ast, 0x1E6E000C, 0x00000040);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000040);
udelay(50);
/* Mode Register Setting */
- ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
- ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
- ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
- ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
-
- ast_moutdwm(ast, 0x1E6E000C, 0x00005C01);
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS | 0x100);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000005);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000007);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C08);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01);
data = 0;
if (param->wodt)
data = 0x300;
if (param->rodt)
data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
- ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
+ ast_moutdwm(ast, AST_REG_MCR34, data | 0x3);
/* Calibrate the DQSI delay */
if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
goto ddr3_init_start;
- ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
+ ast_moutdwm(ast, AST_REG_MCR120, param->reg_FREQ);
/* ECC Memory Initialization */
#ifdef ECC
- ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0070, 0x221);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x221);
do {
- data = ast_mindwm(ast, 0x1E6E0070);
+ data = ast_mindwm(ast, AST_REG_MCR70);
} while (!(data & 0x00001000));
- ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
#endif
}
@@ -874,10 +874,10 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
{
u32 trap, trap_AC2, trap_MRS;
- ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
/* Ger trap info */
- trap = (ast_mindwm(ast, 0x1E6E2070) >> 25) & 0x3;
+ trap = (ast_mindwm(ast, AST_REG_SCU070) >> 25) & 0x3;
trap_AC2 = (trap << 20) | (trap << 16);
trap_AC2 += 0x00110000;
trap_MRS = 0x00000040 | (trap << 4);
@@ -890,7 +890,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
switch (param->dram_freq) {
case 264:
- ast_moutdwm(ast, 0x1E6E2020, 0x0130);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0130);
param->wodt = 0;
param->reg_AC1 = 0x11101513;
param->reg_AC2 = 0x78117011;
@@ -905,7 +905,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 336:
- ast_moutdwm(ast, 0x1E6E2020, 0x0190);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0190);
param->wodt = 1;
param->reg_AC1 = 0x22202613;
param->reg_AC2 = 0xAA009016 | trap_AC2;
@@ -936,7 +936,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
break;
default:
case 396:
- ast_moutdwm(ast, 0x1E6E2020, 0x03F1);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x03F1);
param->wodt = 1;
param->rodt = 0;
param->reg_AC1 = 0x33302714;
@@ -970,7 +970,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
break;
case 408:
- ast_moutdwm(ast, 0x1E6E2020, 0x01F0);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x01F0);
param->wodt = 1;
param->rodt = 0;
param->reg_AC1 = 0x33302714;
@@ -1003,7 +1003,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
break;
case 456:
- ast_moutdwm(ast, 0x1E6E2020, 0x0230);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0230);
param->wodt = 0;
param->reg_AC1 = 0x33302815;
param->reg_AC2 = 0xCD44B01E;
@@ -1018,7 +1018,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 504:
- ast_moutdwm(ast, 0x1E6E2020, 0x0261);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0261);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302815;
@@ -1034,7 +1034,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 528:
- ast_moutdwm(ast, 0x1E6E2020, 0x0120);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0120);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x33302815;
@@ -1050,7 +1050,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 552:
- ast_moutdwm(ast, 0x1E6E2020, 0x02A1);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x02A1);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x43402915;
@@ -1066,7 +1066,7 @@ static void get_ddr2_info(struct ast_device *ast, struct ast2300_dram_param *par
param->dll2_finetune_step = 3;
break;
case 576:
- ast_moutdwm(ast, 0x1E6E2020, 0x0140);
+ ast_moutdwm(ast, AST_REG_SCU020, 0x0140);
param->wodt = 1;
param->rodt = 1;
param->reg_AC1 = 0x43402915;
@@ -1121,104 +1121,104 @@ static void ddr2_init(struct ast_device *ast, struct ast2300_dram_param *param)
u32 data, data2, retry = 0;
ddr2_init_start:
- ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
- ast_moutdwm(ast, 0x1E6E0018, 0x00000100);
- ast_moutdwm(ast, 0x1E6E0024, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ);
- ast_moutdwm(ast, 0x1E6E0068, param->reg_SADJ);
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00000100);
+ ast_moutdwm(ast, AST_REG_MCR24, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ);
+ ast_moutdwm(ast, AST_REG_MCR68, param->reg_SADJ);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0064, param->reg_MADJ | 0xC0000);
+ ast_moutdwm(ast, AST_REG_MCR64, param->reg_MADJ | 0xC0000);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0004, param->dram_config);
- ast_moutdwm(ast, 0x1E6E0008, 0x90040f);
- ast_moutdwm(ast, 0x1E6E0010, param->reg_AC1);
- ast_moutdwm(ast, 0x1E6E0014, param->reg_AC2);
- ast_moutdwm(ast, 0x1E6E0020, param->reg_DQSIC);
- ast_moutdwm(ast, 0x1E6E0080, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0084, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0088, param->reg_DQIDLY);
- ast_moutdwm(ast, 0x1E6E0018, 0x4000A130);
- ast_moutdwm(ast, 0x1E6E0018, 0x00002330);
- ast_moutdwm(ast, 0x1E6E0038, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0040, 0xFF808000);
- ast_moutdwm(ast, 0x1E6E0044, 0x88848466);
- ast_moutdwm(ast, 0x1E6E0048, 0x44440008);
- ast_moutdwm(ast, 0x1E6E004C, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0054, 0);
- ast_moutdwm(ast, 0x1E6E0060, param->reg_DRV);
- ast_moutdwm(ast, 0x1E6E006C, param->reg_IOZ);
- ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0074, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0078, 0x00000000);
- ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR04, param->dram_config);
+ ast_moutdwm(ast, AST_REG_MCR08, 0x90040f);
+ ast_moutdwm(ast, AST_REG_MCR10, param->reg_AC1);
+ ast_moutdwm(ast, AST_REG_MCR14, param->reg_AC2);
+ ast_moutdwm(ast, AST_REG_MCR20, param->reg_DQSIC);
+ ast_moutdwm(ast, AST_REG_MCR80, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR84, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR88, param->reg_DQIDLY);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x4000A130);
+ ast_moutdwm(ast, AST_REG_MCR18, 0x00002330);
+ ast_moutdwm(ast, AST_REG_MCR38, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR40, 0xFF808000);
+ ast_moutdwm(ast, AST_REG_MCR44, 0x88848466);
+ ast_moutdwm(ast, AST_REG_MCR48, 0x44440008);
+ ast_moutdwm(ast, AST_REG_MCR4C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR54, 0);
+ ast_moutdwm(ast, AST_REG_MCR60, param->reg_DRV);
+ ast_moutdwm(ast, AST_REG_MCR6C, param->reg_IOZ);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR74, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR78, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
/* Wait MCLK2X lock to MCLK */
do {
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
} while (!(data & 0x08000000));
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
data = (data >> 8) & 0xff;
while ((data & 0x08) || ((data & 0x7) < 2) || (data < 4)) {
- data2 = (ast_mindwm(ast, 0x1E6E0064) & 0xfff3ffff) + 4;
+ data2 = (ast_mindwm(ast, AST_REG_MCR64) & 0xfff3ffff) + 4;
if ((data2 & 0xff) > param->madj_max)
break;
- ast_moutdwm(ast, 0x1E6E0064, data2);
+ ast_moutdwm(ast, AST_REG_MCR64, data2);
if (data2 & 0x00100000)
data2 = ((data2 & 0xff) >> 3) + 3;
else
data2 = ((data2 & 0xff) >> 2) + 5;
- data = ast_mindwm(ast, 0x1E6E0068) & 0xffff00ff;
+ data = ast_mindwm(ast, AST_REG_MCR68) & 0xffff00ff;
data2 += data & 0xff;
data = data | (data2 << 8);
- ast_moutdwm(ast, 0x1E6E0068, data);
+ ast_moutdwm(ast, AST_REG_MCR68, data);
udelay(10);
- ast_moutdwm(ast, 0x1E6E0064, ast_mindwm(ast, 0x1E6E0064) | 0xC0000);
+ ast_moutdwm(ast, AST_REG_MCR64, ast_mindwm(ast, AST_REG_MCR64) | 0xC0000);
udelay(10);
- data = ast_mindwm(ast, 0x1E6E0018) & 0xfffff1ff;
- ast_moutdwm(ast, 0x1E6E0018, data);
+ data = ast_mindwm(ast, AST_REG_MCR18) & 0xfffff1ff;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
data = data | 0x200;
- ast_moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, AST_REG_MCR18, data);
do {
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
} while (!(data & 0x08000000));
- data = ast_mindwm(ast, 0x1E6E001C);
+ data = ast_mindwm(ast, AST_REG_MCR1C);
data = (data >> 8) & 0xff;
}
- ast_moutdwm(ast, 0x1E720058, ast_mindwm(ast, 0x1E6E0008) & 0xffff);
- data = ast_mindwm(ast, 0x1E6E0018) | 0xC00;
- ast_moutdwm(ast, 0x1E6E0018, data);
+ ast_moutdwm(ast, AST_REG_A2P58, ast_mindwm(ast, AST_REG_MCR08) & 0xffff);
+ data = ast_mindwm(ast, AST_REG_MCR18) | 0xC00;
+ ast_moutdwm(ast, AST_REG_MCR18, data);
- ast_moutdwm(ast, 0x1E6E0034, 0x00000001);
- ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
udelay(50);
/* Mode Register Setting */
- ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS | 0x100);
- ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000005);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000007);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
-
- ast_moutdwm(ast, 0x1E6E000C, 0x00005C08);
- ast_moutdwm(ast, 0x1E6E002C, param->reg_MRS);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000001);
- ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS | 0x380);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
- ast_moutdwm(ast, 0x1E6E0030, param->reg_EMRS);
- ast_moutdwm(ast, 0x1E6E0028, 0x00000003);
-
- ast_moutdwm(ast, 0x1E6E000C, 0x7FFF5C01);
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS | 0x100);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000005);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000007);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C08);
+ ast_moutdwm(ast, AST_REG_MCR2C, param->reg_MRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000001);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS | 0x380);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+ ast_moutdwm(ast, AST_REG_MCR30, param->reg_EMRS);
+ ast_moutdwm(ast, AST_REG_MCR28, 0x00000003);
+
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x7FFF5C01);
data = 0;
if (param->wodt)
data = 0x500;
if (param->rodt)
data = data | 0x3000 | ((param->reg_AC2 & 0x60000) >> 3);
- ast_moutdwm(ast, 0x1E6E0034, data | 0x3);
- ast_moutdwm(ast, 0x1E6E0120, param->reg_FREQ);
+ ast_moutdwm(ast, AST_REG_MCR34, data | 0x3);
+ ast_moutdwm(ast, AST_REG_MCR120, param->reg_FREQ);
/* Calibrate the DQSI delay */
if ((cbr_dll2(ast, param) == false) && (retry++ < 10))
@@ -1226,14 +1226,14 @@ ddr2_init_start:
/* ECC Memory Initialization */
#ifdef ECC
- ast_moutdwm(ast, 0x1E6E007C, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0070, 0x221);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x221);
do {
- data = ast_mindwm(ast, 0x1E6E0070);
+ data = ast_mindwm(ast, AST_REG_MCR70);
} while (!(data & 0x00001000));
- ast_moutdwm(ast, 0x1E6E0070, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
- ast_moutdwm(ast, 0x1E6E0050, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x00000000);
#endif
}
@@ -1245,26 +1245,19 @@ static void ast_post_chip_2300(struct ast_device *ast)
reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
if ((reg & 0x80) == 0) {/* vga only */
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
- ast_write32(ast, 0x12000, 0x1688a8a8);
- do {
- ;
- } while (ast_read32(ast, 0x12000) != 0x1);
+ u32 scu008, scu040;
- ast_write32(ast, 0x10000, 0xfc600309);
- do {
- ;
- } while (ast_read32(ast, 0x10000) != 0x1);
+ ast_moutdwm_poll(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY, 0x01);
+ ast_moutdwm_poll(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY, 0x01);
/* Slow down CPU/AHB CLK in VGA only mode */
- temp = ast_read32(ast, 0x12008);
- temp |= 0x73;
- ast_write32(ast, 0x12008, temp);
+ scu008 = ast_mindwm(ast, AST_REG_SCU008);
+ scu008 |= 0x00000073;
+ ast_moutdwm(ast, AST_REG_SCU008, scu008);
param.dram_freq = 396;
param.dram_type = AST_DDR3;
- temp = ast_mindwm(ast, 0x1e6e2070);
+ temp = ast_mindwm(ast, AST_REG_SCU070);
if (temp & 0x01000000)
param.dram_type = AST_DDR2;
switch (temp & 0x18000000) {
@@ -1306,8 +1299,9 @@ static void ast_post_chip_2300(struct ast_device *ast)
ddr2_init(ast, &param);
}
- temp = ast_mindwm(ast, 0x1e6e2040);
- ast_moutdwm(ast, 0x1e6e2040, temp | 0x40);
+ scu040 = ast_mindwm(ast, AST_REG_SCU040);
+ scu040 |= 0x00000040;
+ ast_moutdwm(ast, AST_REG_SCU040, scu040);
}
/* wait ready */
diff --git a/drivers/gpu/drm/ast/ast_2500.c b/drivers/gpu/drm/ast/ast_2500.c
index 2a52af0ded56..03a67ec9684b 100644
--- a/drivers/gpu/drm/ast/ast_2500.c
+++ b/drivers/gpu/drm/ast/ast_2500.c
@@ -109,12 +109,12 @@ void ast_2500_patch_ahb(void __iomem *regs)
u32 data;
/* Clear bus lock condition */
- __ast_moutdwm(regs, 0x1e600000, 0xAEED1A03);
- __ast_moutdwm(regs, 0x1e600084, 0x00010000);
- __ast_moutdwm(regs, 0x1e600088, 0x00000000);
- __ast_moutdwm(regs, 0x1e6e2000, 0x1688A8A8);
+ __ast_moutdwm(regs, AST_REG_AHBC00, AST_REG_AHBC00_PROTECT_KEY);
+ __ast_moutdwm(regs, AST_REG_AHBC84, 0x00010000);
+ __ast_moutdwm(regs, AST_REG_AHBC88, 0x00000000);
+ __ast_moutdwm(regs, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
- data = __ast_mindwm(regs, 0x1e6e2070);
+ data = __ast_mindwm(regs, AST_REG_SCU070);
if (data & 0x08000000) { /* check fast reset */
/*
* If "Fast restet" is enabled for ARM-ICE debugger,
@@ -127,18 +127,18 @@ void ast_2500_patch_ahb(void __iomem *regs)
* [1]:= 1:WDT will be cleeared and disabled after timeout occurs
* [0]:= 1:WDT enable
*/
- __ast_moutdwm(regs, 0x1E785004, 0x00000010);
- __ast_moutdwm(regs, 0x1E785008, 0x00004755);
- __ast_moutdwm(regs, 0x1E78500c, 0x00000033);
+ __ast_moutdwm(regs, AST_REG_WDT04(0), 0x00000010);
+ __ast_moutdwm(regs, AST_REG_WDT08(0), 0x00004755);
+ __ast_moutdwm(regs, AST_REG_WDT0C(0), 0x00000033);
udelay(1000);
}
do {
- __ast_moutdwm(regs, 0x1e6e2000, 0x1688A8A8);
- data = __ast_mindwm(regs, 0x1e6e2000);
+ __ast_moutdwm(regs, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+ data = __ast_mindwm(regs, AST_REG_SCU000);
} while (data != 1);
- __ast_moutdwm(regs, 0x1e6e207c, 0x08000000); /* clear fast reset */
+ __ast_moutdwm(regs, AST_REG_SCU07C, 0x08000000); /* clear fast reset */
}
static bool mmc_test_single_2500(struct ast_device *ast, u32 datagen)
@@ -148,8 +148,8 @@ static bool mmc_test_single_2500(struct ast_device *ast, u32 datagen)
static bool cbr_test_2500(struct ast_device *ast)
{
- ast_moutdwm(ast, 0x1E6E0074, 0x0000FFFF);
- ast_moutdwm(ast, 0x1E6E007C, 0xFF00FF00);
+ ast_moutdwm(ast, AST_REG_MCR74, 0x0000FFFF);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0xFF00FF00);
if (!mmc_test_burst(ast, 0))
return false;
if (!mmc_test_single_2500(ast, 0))
@@ -159,8 +159,8 @@ static bool cbr_test_2500(struct ast_device *ast)
static bool ddr_test_2500(struct ast_device *ast)
{
- ast_moutdwm(ast, 0x1E6E0074, 0x0000FFFF);
- ast_moutdwm(ast, 0x1E6E007C, 0xFF00FF00);
+ ast_moutdwm(ast, AST_REG_MCR74, 0x0000FFFF);
+ ast_moutdwm(ast, AST_REG_MCR7C, 0xFF00FF00);
if (!mmc_test_burst(ast, 0))
return false;
if (!mmc_test_burst(ast, 1))
@@ -176,25 +176,25 @@ static bool ddr_test_2500(struct ast_device *ast)
static void ddr_init_common_2500(struct ast_device *ast)
{
- ast_moutdwm(ast, 0x1E6E0034, 0x00020080);
- ast_moutdwm(ast, 0x1E6E0008, 0x2003000F);
- ast_moutdwm(ast, 0x1E6E0038, 0x00000FFF);
- ast_moutdwm(ast, 0x1E6E0040, 0x88448844);
- ast_moutdwm(ast, 0x1E6E0044, 0x24422288);
- ast_moutdwm(ast, 0x1E6E0048, 0x22222222);
- ast_moutdwm(ast, 0x1E6E004C, 0x22222222);
- ast_moutdwm(ast, 0x1E6E0050, 0x80000000);
- ast_moutdwm(ast, 0x1E6E0208, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0218, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0220, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0228, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0230, 0x00000000);
- ast_moutdwm(ast, 0x1E6E02A8, 0x00000000);
- ast_moutdwm(ast, 0x1E6E02B0, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0240, 0x86000000);
- ast_moutdwm(ast, 0x1E6E0244, 0x00008600);
- ast_moutdwm(ast, 0x1E6E0248, 0x80000000);
- ast_moutdwm(ast, 0x1E6E024C, 0x80808080);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020080);
+ ast_moutdwm(ast, AST_REG_MCR08, 0x2003000F);
+ ast_moutdwm(ast, AST_REG_MCR38, 0x00000FFF);
+ ast_moutdwm(ast, AST_REG_MCR40, 0x88448844);
+ ast_moutdwm(ast, AST_REG_MCR44, 0x24422288);
+ ast_moutdwm(ast, AST_REG_MCR48, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR4C, 0x22222222);
+ ast_moutdwm(ast, AST_REG_MCR50, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR208, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR218, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR220, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR228, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR230, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2A8, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2B0, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR240, 0x86000000);
+ ast_moutdwm(ast, AST_REG_MCR244, 0x00008600);
+ ast_moutdwm(ast, AST_REG_MCR248, 0x80000000);
+ ast_moutdwm(ast, AST_REG_MCR24C, 0x80808080);
}
static void ddr_phy_init_2500(struct ast_device *ast)
@@ -202,29 +202,32 @@ static void ddr_phy_init_2500(struct ast_device *ast)
u32 data, pass, timecnt;
pass = 0;
- ast_moutdwm(ast, 0x1E6E0060, 0x00000005);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000005);
while (!pass) {
for (timecnt = 0; timecnt < TIMEOUT; timecnt++) {
- data = ast_mindwm(ast, 0x1E6E0060) & 0x1;
+ data = ast_mindwm(ast, AST_REG_MCR60) & 0x1;
if (!data)
break;
}
if (timecnt != TIMEOUT) {
- data = ast_mindwm(ast, 0x1E6E0300) & 0x000A0000;
+ data = ast_mindwm(ast, AST_REG_MCR300) & 0x000A0000;
if (!data)
pass = 1;
}
if (!pass) {
- ast_moutdwm(ast, 0x1E6E0060, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
udelay(10); /* delay 10 us */
- ast_moutdwm(ast, 0x1E6E0060, 0x00000005);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000005);
}
}
- ast_moutdwm(ast, 0x1E6E0060, 0x00000006);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000006);
}
/*
+ * TODO: Review and fix the comments. The function below only detects
+ * up to 1 GiB of SDRAM.
+ *
* Check DRAM Size
* 1Gb : 0x80000000 ~ 0x87FFFFFF
* 2Gb : 0x80000000 ~ 0x8FFFFFFF
@@ -235,125 +238,123 @@ static void check_dram_size_2500(struct ast_device *ast, u32 tRFC)
{
u32 reg_04, reg_14;
- reg_04 = ast_mindwm(ast, 0x1E6E0004) & 0xfffffffc;
- reg_14 = ast_mindwm(ast, 0x1E6E0014) & 0xffffff00;
+ reg_04 = ast_mindwm(ast, AST_REG_MCR04) & 0xfffffffc;
+ reg_14 = ast_mindwm(ast, AST_REG_MCR14) & 0xffffff00;
- ast_moutdwm(ast, 0xA0100000, 0x41424344);
- ast_moutdwm(ast, 0x90100000, 0x35363738);
- ast_moutdwm(ast, 0x88100000, 0x292A2B2C);
- ast_moutdwm(ast, 0x80100000, 0x1D1E1F10);
+ ast_moutdwm(ast, AST_SDRAM(0x20100000), 0x41424344);
+ ast_moutdwm(ast, AST_SDRAM(0x10100000), 0x35363738);
+ ast_moutdwm(ast, AST_SDRAM(0x08100000), 0x292A2B2C);
+ ast_moutdwm(ast, AST_SDRAM(0x00100000), 0x1D1E1F10);
/* Check 8Gbit */
- if (ast_mindwm(ast, 0xA0100000) == 0x41424344) {
+ if (ast_mindwm(ast, AST_SDRAM(0x20100000)) == 0x41424344) {
reg_04 |= 0x03;
reg_14 |= (tRFC >> 24) & 0xFF;
/* Check 4Gbit */
- } else if (ast_mindwm(ast, 0x90100000) == 0x35363738) {
+ } else if (ast_mindwm(ast, AST_SDRAM(0x10100000)) == 0x35363738) {
reg_04 |= 0x02;
reg_14 |= (tRFC >> 16) & 0xFF;
/* Check 2Gbit */
- } else if (ast_mindwm(ast, 0x88100000) == 0x292A2B2C) {
+ } else if (ast_mindwm(ast, AST_SDRAM(0x08100000)) == 0x292A2B2C) {
reg_04 |= 0x01;
reg_14 |= (tRFC >> 8) & 0xFF;
} else {
reg_14 |= tRFC & 0xFF;
}
- ast_moutdwm(ast, 0x1E6E0004, reg_04);
- ast_moutdwm(ast, 0x1E6E0014, reg_14);
+ ast_moutdwm(ast, AST_REG_MCR04, reg_04);
+ ast_moutdwm(ast, AST_REG_MCR14, reg_14);
}
static void enable_cache_2500(struct ast_device *ast)
{
u32 reg_04, data;
- reg_04 = ast_mindwm(ast, 0x1E6E0004);
- ast_moutdwm(ast, 0x1E6E0004, reg_04 | 0x1000);
+ reg_04 = ast_mindwm(ast, AST_REG_MCR04);
+ ast_moutdwm(ast, AST_REG_MCR04, reg_04 | 0x1000);
do
- data = ast_mindwm(ast, 0x1E6E0004);
+ data = ast_mindwm(ast, AST_REG_MCR04);
while (!(data & 0x80000));
- ast_moutdwm(ast, 0x1E6E0004, reg_04 | 0x400);
+ ast_moutdwm(ast, AST_REG_MCR04, reg_04 | 0x400);
}
static void set_mpll_2500(struct ast_device *ast)
{
- u32 addr, data, param;
+ u32 mcr, data, param;
/* Reset MMC */
- ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
- ast_moutdwm(ast, 0x1E6E0034, 0x00020080);
- for (addr = 0x1e6e0004; addr < 0x1e6e0090;) {
- ast_moutdwm(ast, addr, 0x0);
- addr += 4;
- }
- ast_moutdwm(ast, 0x1E6E0034, 0x00020000);
-
- ast_moutdwm(ast, 0x1E6E2000, 0x1688A8A8);
- data = ast_mindwm(ast, 0x1E6E2070) & 0x00800000;
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020080);
+ for (mcr = AST_REG_MCR04; mcr <= AST_REG_MCR8C; mcr += 4)
+ ast_moutdwm(ast, mcr, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020000);
+
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+ data = ast_mindwm(ast, AST_REG_SCU070) & 0x00800000;
if (data) {
/* CLKIN = 25MHz */
param = 0x930023E0;
- ast_moutdwm(ast, 0x1E6E2160, 0x00011320);
+ ast_moutdwm(ast, AST_REG_SCU160, 0x00011320);
} else {
/* CLKIN = 24MHz */
param = 0x93002400;
}
- ast_moutdwm(ast, 0x1E6E2020, param);
+ ast_moutdwm(ast, AST_REG_SCU020, param);
udelay(100);
}
static void reset_mmc_2500(struct ast_device *ast)
{
- ast_moutdwm(ast, 0x1E78505C, 0x00000004);
- ast_moutdwm(ast, 0x1E785044, 0x00000001);
- ast_moutdwm(ast, 0x1E785048, 0x00004755);
- ast_moutdwm(ast, 0x1E78504C, 0x00000013);
+ ast_moutdwm(ast, AST_REG_WDT1C(1), 0x00000004);
+ ast_moutdwm(ast, AST_REG_WDT04(1), 0x00000001);
+ ast_moutdwm(ast, AST_REG_WDT08(1), 0x00004755);
+ ast_moutdwm(ast, AST_REG_WDT0C(1), 0x00000013);
mdelay(100);
- ast_moutdwm(ast, 0x1E785054, 0x00000077);
- ast_moutdwm(ast, 0x1E6E0000, 0xFC600309);
+ ast_moutdwm(ast, AST_REG_WDT14(1), 0x00000077);
+ ast_moutdwm(ast, AST_REG_MCR00, AST_REG_MCR00_PROTECTION_KEY);
}
static void ddr3_init_2500(struct ast_device *ast, const u32 *ddr_table)
{
- ast_moutdwm(ast, 0x1E6E0004, 0x00000303);
- ast_moutdwm(ast, 0x1E6E0010, ddr_table[REGIDX_010]);
- ast_moutdwm(ast, 0x1E6E0014, ddr_table[REGIDX_014]);
- ast_moutdwm(ast, 0x1E6E0018, ddr_table[REGIDX_018]);
- ast_moutdwm(ast, 0x1E6E0020, ddr_table[REGIDX_020]); /* MODEREG4/6 */
- ast_moutdwm(ast, 0x1E6E0024, ddr_table[REGIDX_024]); /* MODEREG5 */
- ast_moutdwm(ast, 0x1E6E002C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */
- ast_moutdwm(ast, 0x1E6E0030, ddr_table[REGIDX_030]); /* MODEREG1/3 */
+ ast_moutdwm(ast, AST_REG_MCR04, 0x00000303);
+ ast_moutdwm(ast, AST_REG_MCR10, ddr_table[REGIDX_010]);
+ ast_moutdwm(ast, AST_REG_MCR14, ddr_table[REGIDX_014]);
+ ast_moutdwm(ast, AST_REG_MCR18, ddr_table[REGIDX_018]);
+ ast_moutdwm(ast, AST_REG_MCR20, ddr_table[REGIDX_020]); /* MODEREG4/6 */
+ ast_moutdwm(ast, AST_REG_MCR24, ddr_table[REGIDX_024]); /* MODEREG5 */
+ ast_moutdwm(ast, AST_REG_MCR2C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */
+ ast_moutdwm(ast, AST_REG_MCR30, ddr_table[REGIDX_030]); /* MODEREG1/3 */
/* DDR PHY Setting */
- ast_moutdwm(ast, 0x1E6E0200, 0x02492AAE);
- ast_moutdwm(ast, 0x1E6E0204, 0x00001001);
- ast_moutdwm(ast, 0x1E6E020C, 0x55E00B0B);
- ast_moutdwm(ast, 0x1E6E0210, 0x20000000);
- ast_moutdwm(ast, 0x1E6E0214, ddr_table[REGIDX_214]);
- ast_moutdwm(ast, 0x1E6E02E0, ddr_table[REGIDX_2E0]);
- ast_moutdwm(ast, 0x1E6E02E4, ddr_table[REGIDX_2E4]);
- ast_moutdwm(ast, 0x1E6E02E8, ddr_table[REGIDX_2E8]);
- ast_moutdwm(ast, 0x1E6E02EC, ddr_table[REGIDX_2EC]);
- ast_moutdwm(ast, 0x1E6E02F0, ddr_table[REGIDX_2F0]);
- ast_moutdwm(ast, 0x1E6E02F4, ddr_table[REGIDX_2F4]);
- ast_moutdwm(ast, 0x1E6E02F8, ddr_table[REGIDX_2F8]);
- ast_moutdwm(ast, 0x1E6E0290, 0x00100008);
- ast_moutdwm(ast, 0x1E6E02C0, 0x00000006);
+ ast_moutdwm(ast, AST_REG_MCR200, 0x02492AAE);
+ ast_moutdwm(ast, AST_REG_MCR204, 0x00001001);
+ ast_moutdwm(ast, AST_REG_MCR20C, 0x55E00B0B);
+ ast_moutdwm(ast, AST_REG_MCR210, 0x20000000);
+ ast_moutdwm(ast, AST_REG_MCR214, ddr_table[REGIDX_214]);
+ ast_moutdwm(ast, AST_REG_MCR2E0, ddr_table[REGIDX_2E0]);
+ ast_moutdwm(ast, AST_REG_MCR2E4, ddr_table[REGIDX_2E4]);
+ ast_moutdwm(ast, AST_REG_MCR2E8, ddr_table[REGIDX_2E8]);
+ ast_moutdwm(ast, AST_REG_MCR2EC, ddr_table[REGIDX_2EC]);
+ ast_moutdwm(ast, AST_REG_MCR2F0, ddr_table[REGIDX_2F0]);
+ ast_moutdwm(ast, AST_REG_MCR2F4, ddr_table[REGIDX_2F4]);
+ ast_moutdwm(ast, AST_REG_MCR2F8, ddr_table[REGIDX_2F8]);
+ ast_moutdwm(ast, AST_REG_MCR290, 0x00100008);
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006);
/* Controller Setting */
- ast_moutdwm(ast, 0x1E6E0034, 0x00020091);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x00020091);
/* Wait DDR PHY init done */
ddr_phy_init_2500(ast);
- ast_moutdwm(ast, 0x1E6E0120, ddr_table[REGIDX_PLL]);
- ast_moutdwm(ast, 0x1E6E000C, 0x42AA5C81);
- ast_moutdwm(ast, 0x1E6E0034, 0x0001AF93);
+ ast_moutdwm(ast, AST_REG_MCR120, ddr_table[REGIDX_PLL]);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x42AA5C81);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x0001AF93);
check_dram_size_2500(ast, ddr_table[REGIDX_RFC]);
enable_cache_2500(ast);
- ast_moutdwm(ast, 0x1E6E001C, 0x00000008);
- ast_moutdwm(ast, 0x1E6E0038, 0xFFFFFF00);
+ ast_moutdwm(ast, AST_REG_MCR1C, 0x00000008);
+ ast_moutdwm(ast, AST_REG_MCR38, 0xFFFFFF00);
}
static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
@@ -363,34 +364,34 @@ static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
u32 min_ddr_vref = 0, min_phy_vref = 0;
u32 max_ddr_vref = 0, max_phy_vref = 0;
- ast_moutdwm(ast, 0x1E6E0004, 0x00000313);
- ast_moutdwm(ast, 0x1E6E0010, ddr_table[REGIDX_010]);
- ast_moutdwm(ast, 0x1E6E0014, ddr_table[REGIDX_014]);
- ast_moutdwm(ast, 0x1E6E0018, ddr_table[REGIDX_018]);
- ast_moutdwm(ast, 0x1E6E0020, ddr_table[REGIDX_020]); /* MODEREG4/6 */
- ast_moutdwm(ast, 0x1E6E0024, ddr_table[REGIDX_024]); /* MODEREG5 */
- ast_moutdwm(ast, 0x1E6E002C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */
- ast_moutdwm(ast, 0x1E6E0030, ddr_table[REGIDX_030]); /* MODEREG1/3 */
+ ast_moutdwm(ast, AST_REG_MCR04, 0x00000313);
+ ast_moutdwm(ast, AST_REG_MCR10, ddr_table[REGIDX_010]);
+ ast_moutdwm(ast, AST_REG_MCR14, ddr_table[REGIDX_014]);
+ ast_moutdwm(ast, AST_REG_MCR18, ddr_table[REGIDX_018]);
+ ast_moutdwm(ast, AST_REG_MCR20, ddr_table[REGIDX_020]); /* MODEREG4/6 */
+ ast_moutdwm(ast, AST_REG_MCR24, ddr_table[REGIDX_024]); /* MODEREG5 */
+ ast_moutdwm(ast, AST_REG_MCR2C, ddr_table[REGIDX_02C] | 0x100); /* MODEREG0/2 */
+ ast_moutdwm(ast, AST_REG_MCR30, ddr_table[REGIDX_030]); /* MODEREG1/3 */
/* DDR PHY Setting */
- ast_moutdwm(ast, 0x1E6E0200, 0x42492AAE);
- ast_moutdwm(ast, 0x1E6E0204, 0x09002000);
- ast_moutdwm(ast, 0x1E6E020C, 0x55E00B0B);
- ast_moutdwm(ast, 0x1E6E0210, 0x20000000);
- ast_moutdwm(ast, 0x1E6E0214, ddr_table[REGIDX_214]);
- ast_moutdwm(ast, 0x1E6E02E0, ddr_table[REGIDX_2E0]);
- ast_moutdwm(ast, 0x1E6E02E4, ddr_table[REGIDX_2E4]);
- ast_moutdwm(ast, 0x1E6E02E8, ddr_table[REGIDX_2E8]);
- ast_moutdwm(ast, 0x1E6E02EC, ddr_table[REGIDX_2EC]);
- ast_moutdwm(ast, 0x1E6E02F0, ddr_table[REGIDX_2F0]);
- ast_moutdwm(ast, 0x1E6E02F4, ddr_table[REGIDX_2F4]);
- ast_moutdwm(ast, 0x1E6E02F8, ddr_table[REGIDX_2F8]);
- ast_moutdwm(ast, 0x1E6E0290, 0x00100008);
- ast_moutdwm(ast, 0x1E6E02C4, 0x3C183C3C);
- ast_moutdwm(ast, 0x1E6E02C8, 0x00631E0E);
+ ast_moutdwm(ast, AST_REG_MCR200, 0x42492AAE);
+ ast_moutdwm(ast, AST_REG_MCR204, 0x09002000);
+ ast_moutdwm(ast, AST_REG_MCR20C, 0x55E00B0B);
+ ast_moutdwm(ast, AST_REG_MCR210, 0x20000000);
+ ast_moutdwm(ast, AST_REG_MCR214, ddr_table[REGIDX_214]);
+ ast_moutdwm(ast, AST_REG_MCR2E0, ddr_table[REGIDX_2E0]);
+ ast_moutdwm(ast, AST_REG_MCR2E4, ddr_table[REGIDX_2E4]);
+ ast_moutdwm(ast, AST_REG_MCR2E8, ddr_table[REGIDX_2E8]);
+ ast_moutdwm(ast, AST_REG_MCR2EC, ddr_table[REGIDX_2EC]);
+ ast_moutdwm(ast, AST_REG_MCR2F0, ddr_table[REGIDX_2F0]);
+ ast_moutdwm(ast, AST_REG_MCR2F4, ddr_table[REGIDX_2F4]);
+ ast_moutdwm(ast, AST_REG_MCR2F8, ddr_table[REGIDX_2F8]);
+ ast_moutdwm(ast, AST_REG_MCR290, 0x00100008);
+ ast_moutdwm(ast, AST_REG_MCR2C4, 0x3C183C3C);
+ ast_moutdwm(ast, AST_REG_MCR2C8, 0x00631E0E);
/* Controller Setting */
- ast_moutdwm(ast, 0x1E6E0034, 0x0001A991);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x0001A991);
/* Train PHY Vref first */
pass = 0;
@@ -398,17 +399,17 @@ static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
for (retrycnt = 0; retrycnt < 4 && pass == 0; retrycnt++) {
max_phy_vref = 0x0;
pass = 0;
- ast_moutdwm(ast, 0x1E6E02C0, 0x00001C06);
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00001C06);
for (phy_vref = 0x40; phy_vref < 0x80; phy_vref++) {
- ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0060, 0x00000000);
- ast_moutdwm(ast, 0x1E6E02CC, phy_vref | (phy_vref << 8));
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2CC, phy_vref | (phy_vref << 8));
/* Fire DFI Init */
ddr_phy_init_2500(ast);
- ast_moutdwm(ast, 0x1E6E000C, 0x00005C01);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01);
if (cbr_test_2500(ast)) {
pass++;
- data = ast_mindwm(ast, 0x1E6E03D0);
+ data = ast_mindwm(ast, AST_REG_MCR3D0);
data2 = data >> 8;
data = data & 0xff;
if (data > data2)
@@ -422,7 +423,7 @@ static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
}
}
}
- ast_moutdwm(ast, 0x1E6E02CC, min_phy_vref | (min_phy_vref << 8));
+ ast_moutdwm(ast, AST_REG_MCR2CC, min_phy_vref | (min_phy_vref << 8));
/* Train DDR Vref next */
pass = 0;
@@ -432,12 +433,12 @@ static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
max_ddr_vref = 0x0;
pass = 0;
for (ddr_vref = 0x00; ddr_vref < 0x40; ddr_vref++) {
- ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0060, 0x00000000);
- ast_moutdwm(ast, 0x1E6E02C0, 0x00000006 | (ddr_vref << 8));
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006 | (ddr_vref << 8));
/* Fire DFI Init */
ddr_phy_init_2500(ast);
- ast_moutdwm(ast, 0x1E6E000C, 0x00005C01);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00005C01);
if (cbr_test_2500(ast)) {
pass++;
if (min_ddr_vref > ddr_vref)
@@ -450,22 +451,22 @@ static void ddr4_init_2500(struct ast_device *ast, const u32 *ddr_table)
}
}
- ast_moutdwm(ast, 0x1E6E000C, 0x00000000);
- ast_moutdwm(ast, 0x1E6E0060, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR60, 0x00000000);
ddr_vref = (min_ddr_vref + max_ddr_vref + 1) >> 1;
- ast_moutdwm(ast, 0x1E6E02C0, 0x00000006 | (ddr_vref << 8));
+ ast_moutdwm(ast, AST_REG_MCR2C0, 0x00000006 | (ddr_vref << 8));
/* Wait DDR PHY init done */
ddr_phy_init_2500(ast);
- ast_moutdwm(ast, 0x1E6E0120, ddr_table[REGIDX_PLL]);
- ast_moutdwm(ast, 0x1E6E000C, 0x42AA5C81);
- ast_moutdwm(ast, 0x1E6E0034, 0x0001AF93);
+ ast_moutdwm(ast, AST_REG_MCR120, ddr_table[REGIDX_PLL]);
+ ast_moutdwm(ast, AST_REG_MCR0C, 0x42AA5C81);
+ ast_moutdwm(ast, AST_REG_MCR34, 0x0001AF93);
check_dram_size_2500(ast, ddr_table[REGIDX_RFC]);
enable_cache_2500(ast);
- ast_moutdwm(ast, 0x1E6E001C, 0x00000008);
- ast_moutdwm(ast, 0x1E6E0038, 0xFFFFFF00);
+ ast_moutdwm(ast, AST_REG_MCR1C, 0x00000008);
+ ast_moutdwm(ast, AST_REG_MCR38, 0xFFFFFF00);
}
static bool ast_dram_init_2500(struct ast_device *ast)
@@ -480,18 +481,18 @@ static bool ast_dram_init_2500(struct ast_device *ast)
reset_mmc_2500(ast);
ddr_init_common_2500(ast);
- data = ast_mindwm(ast, 0x1E6E2070);
+ data = ast_mindwm(ast, AST_REG_SCU070);
if (data & 0x01000000)
ddr4_init_2500(ast, ast2500_ddr4_1600_timing_table);
else
ddr3_init_2500(ast, ast2500_ddr3_1600_timing_table);
} while (!ddr_test_2500(ast));
- ast_moutdwm(ast, 0x1E6E2040, ast_mindwm(ast, 0x1E6E2040) | 0x41);
+ ast_moutdwm(ast, AST_REG_SCU040, ast_mindwm(ast, AST_REG_SCU040) | 0x41);
/* Patch code */
- data = ast_mindwm(ast, 0x1E6E200C) & 0xF9FFFFFF;
- ast_moutdwm(ast, 0x1E6E200C, data | 0x10000000);
+ data = ast_mindwm(ast, AST_REG_SCU00C) & 0xF9FFFFFF;
+ ast_moutdwm(ast, AST_REG_SCU00C, data | 0x10000000);
return true;
}
@@ -504,12 +505,14 @@ static void ast_post_chip_2500(struct ast_device *ast)
reg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
if ((reg & AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK) == 0) {/* vga only */
+ u32 scu008;
+
/* Clear bus lock condition */
ast_2500_patch_ahb(ast->regs);
/* Disable watchdog */
- ast_moutdwm(ast, 0x1E78502C, 0x00000000);
- ast_moutdwm(ast, 0x1E78504C, 0x00000000);
+ ast_moutdwm(ast, AST_REG_WDT2C(0), 0x00000000);
+ ast_moutdwm(ast, AST_REG_WDT0C(1), 0x00000000);
/*
* Reset USB port to patch USB unknown device issue
@@ -524,28 +527,28 @@ static void ast_post_chip_2500(struct ast_device *ast)
* SCU7C is Write clear reg to SCU70
* [23]:= write 1 and then SCU70[23] will be clear as 0b.
*/
- ast_moutdwm(ast, 0x1E6E2090, 0x20000000);
- ast_moutdwm(ast, 0x1E6E2094, 0x00004000);
- if (ast_mindwm(ast, 0x1E6E2070) & 0x00800000) {
- ast_moutdwm(ast, 0x1E6E207C, 0x00800000);
+ ast_moutdwm(ast, AST_REG_SCU090, 0x20000000);
+ ast_moutdwm(ast, AST_REG_SCU094, 0x00004000);
+ if (ast_mindwm(ast, AST_REG_SCU070) & 0x00800000) {
+ ast_moutdwm(ast, AST_REG_SCU07C, 0x00800000);
mdelay(100);
- ast_moutdwm(ast, 0x1E6E2070, 0x00800000);
+ ast_moutdwm(ast, AST_REG_SCU070, 0x00800000);
}
/* Modify eSPI reset pin */
- temp = ast_mindwm(ast, 0x1E6E2070);
+ temp = ast_mindwm(ast, AST_REG_SCU070);
if (temp & 0x02000000)
- ast_moutdwm(ast, 0x1E6E207C, 0x00004000);
+ ast_moutdwm(ast, AST_REG_SCU07C, 0x00004000);
/* Slow down CPU/AHB CLK in VGA only mode */
- temp = ast_read32(ast, 0x12008);
- temp |= 0x73;
- ast_write32(ast, 0x12008, temp);
+ scu008 = ast_mindwm(ast, AST_REG_SCU008);
+ scu008 |= 0x00000073;
+ ast_moutdwm(ast, AST_REG_SCU008, scu008);
if (!ast_dram_init_2500(ast))
drm_err(dev, "DRAM init failed !\n");
- temp = ast_mindwm(ast, 0x1e6e2040);
- ast_moutdwm(ast, 0x1e6e2040, temp | 0x40);
+ temp = ast_mindwm(ast, AST_REG_SCU040);
+ ast_moutdwm(ast, AST_REG_SCU040, temp | 0x40);
}
/* wait ready */
diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c
index 677c52c0d99a..98d8491ce6c9 100644
--- a/drivers/gpu/drm/ast/ast_dp501.c
+++ b/drivers/gpu/drm/ast/ast_dp501.c
@@ -183,7 +183,7 @@ static void ast_set_dp501_video_output(struct ast_device *ast, u8 mode)
static u32 get_fw_base(struct ast_device *ast)
{
- return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff;
+ return ast_mindwm(ast, AST_REG_SCU104) & 0x7fffffff;
}
bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size)
@@ -194,7 +194,7 @@ bool ast_backup_fw(struct ast_device *ast, u8 *addr, u32 size)
if (ast->config_mode != ast_use_p2a)
return false;
- data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
+ data = ast_mindwm(ast, AST_REG_SCU100) & 0x01;
if (data) {
boot_address = get_fw_base(ast);
for (i = 0; i < size; i += 4)
@@ -214,7 +214,7 @@ static bool ast_launch_m68k(struct ast_device *ast)
if (ast->config_mode != ast_use_p2a)
return false;
- data = ast_mindwm(ast, 0x1e6e2100) & 0x01;
+ data = ast_mindwm(ast, AST_REG_SCU100) & 0x01;
if (!data) {
if (ast->dp501_fw_addr) {
@@ -229,8 +229,8 @@ static bool ast_launch_m68k(struct ast_device *ast)
len = ast->dp501_fw->size;
}
/* Get BootAddress */
- ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
- data = ast_mindwm(ast, 0x1e6e0004);
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
+ data = ast_mindwm(ast, AST_REG_MCR04);
switch (data & 0x03) {
case 0:
boot_address = 0x44000000;
@@ -255,16 +255,16 @@ static bool ast_launch_m68k(struct ast_device *ast)
}
/* Init SCU */
- ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8);
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
/* Launch FW */
- ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address);
- ast_moutdwm(ast, 0x1e6e2100, 1);
+ ast_moutdwm(ast, AST_REG_SCU104, 0x80000000 + boot_address);
+ ast_moutdwm(ast, AST_REG_SCU100, 1);
/* Update Scratch */
- data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff; /* D[11:9] = 100b: UEFI handling */
- data |= 0x800;
- ast_moutdwm(ast, 0x1e6e2040, data);
+ data = ast_mindwm(ast, AST_REG_SCU040) & 0xfffff1ff;
+ data |= 0x800; /* D[11:9] = 100b: UEFI handling */
+ ast_moutdwm(ast, AST_REG_SCU040, data);
jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */
jreg |= 0x02;
@@ -347,68 +347,65 @@ static int ast_dp512_read_edid_block(void *data, u8 *buf, unsigned int block, si
static bool ast_init_dvo(struct ast_device *ast)
{
u8 jreg;
- u32 data;
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
- ast_write32(ast, 0x12000, 0x1688a8a8);
+ u32 scu02c;
+
+ ast_moutdwm(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY);
jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff);
if (!(jreg & 0x80)) {
+ u32 scu008;
+
/* Init SCU DVO Settings */
- data = ast_read32(ast, 0x12008);
- /* delay phase */
- data &= 0xfffff8ff;
- data |= 0x00000500;
- ast_write32(ast, 0x12008, data);
+
+ scu008 = ast_mindwm(ast, AST_REG_SCU008);
+ scu008 &= 0xfffff8ff;
+ scu008 |= 0x00000500; /* delay phase */
+ ast_moutdwm(ast, AST_REG_SCU008, scu008);
if (IS_AST_GEN4(ast)) {
- data = ast_read32(ast, 0x12084);
- /* multi-pins for DVO single-edge */
- data |= 0xfffe0000;
- ast_write32(ast, 0x12084, data);
-
- data = ast_read32(ast, 0x12088);
- /* multi-pins for DVO single-edge */
- data |= 0x000fffff;
- ast_write32(ast, 0x12088, data);
-
- data = ast_read32(ast, 0x12090);
- /* multi-pins for DVO single-edge */
- data &= 0xffffffcf;
- data |= 0x00000020;
- ast_write32(ast, 0x12090, data);
+ u32 scu084, scu088, scu090;
+
+ scu084 = ast_mindwm(ast, AST_REG_SCU084);
+ scu084 |= 0xfffe0000; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU084, scu084);
+
+ scu088 = ast_mindwm(ast, AST_REG_SCU088);
+ scu088 |= 0x000fffff; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU088, scu088);
+
+ scu090 = ast_mindwm(ast, AST_REG_SCU090);
+ scu090 &= 0xffffffcf;
+ scu090 |= 0x00000020; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU090, scu090);
} else { /* AST GEN5+ */
- data = ast_read32(ast, 0x12088);
- /* multi-pins for DVO single-edge */
- data |= 0x30000000;
- ast_write32(ast, 0x12088, data);
-
- data = ast_read32(ast, 0x1208c);
- /* multi-pins for DVO single-edge */
- data |= 0x000000cf;
- ast_write32(ast, 0x1208c, data);
-
- data = ast_read32(ast, 0x120a4);
- /* multi-pins for DVO single-edge */
- data |= 0xffff0000;
- ast_write32(ast, 0x120a4, data);
-
- data = ast_read32(ast, 0x120a8);
- /* multi-pins for DVO single-edge */
- data |= 0x0000000f;
- ast_write32(ast, 0x120a8, data);
-
- data = ast_read32(ast, 0x12094);
- /* multi-pins for DVO single-edge */
- data |= 0x00000002;
- ast_write32(ast, 0x12094, data);
+ u32 scu088, scu08c, scu0a4, scu0a8, scu094;
+
+ scu088 = ast_mindwm(ast, AST_REG_SCU088);
+ scu088 |= 0x30000000; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU088, scu088);
+
+ scu08c = ast_mindwm(ast, AST_REG_SCU08C);
+ scu08c |= 0x000000cf; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU08C, scu08c);
+
+ scu0a4 = ast_mindwm(ast, AST_REG_SCU0A4);
+ scu0a4 |= 0xffff0000; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU0A4, scu0a4);
+
+ scu0a8 = ast_mindwm(ast, AST_REG_SCU0A8);
+ scu0a8 |= 0x0000000f; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU0A8, scu0a8);
+
+ scu094 = ast_mindwm(ast, AST_REG_SCU094);
+ scu094 |= 0x00000002; /* multi-pins for DVO single-edge */
+ ast_moutdwm(ast, AST_REG_SCU094, scu094);
}
}
/* Force to DVO */
- data = ast_read32(ast, 0x1202c);
- data &= 0xfffbffff;
- ast_write32(ast, 0x1202c, data);
+ scu02c = ast_mindwm(ast, AST_REG_SCU02C);
+ scu02c &= 0xfffbffff;
+ ast_moutdwm(ast, AST_REG_SCU02C, scu02c);
/* Init VGA DVO Settings */
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80);
@@ -418,25 +415,20 @@ static bool ast_init_dvo(struct ast_device *ast)
static void ast_init_analog(struct ast_device *ast)
{
- u32 data;
+ u32 scu02c;
/*
* Set DAC source to VGA mode in SCU2C via the P2A
- * bridge. First configure the P2U to target the SCU
- * in case it isn't at this stage.
+ * bridge.
*/
- ast_write32(ast, 0xf004, 0x1e6e0000);
- ast_write32(ast, 0xf000, 0x1);
-
- /* Then unlock the SCU with the magic password */
- ast_write32(ast, 0x12000, 0x1688a8a8);
- ast_write32(ast, 0x12000, 0x1688a8a8);
- ast_write32(ast, 0x12000, 0x1688a8a8);
-
- /* Finally, clear bits [17:16] of SCU2c */
- data = ast_read32(ast, 0x1202c);
- data &= 0xfffcffff;
- ast_write32(ast, 0x1202c, data);
+
+ /* Unlock the SCU with the magic password */
+ ast_moutdwm_poll(ast, AST_REG_SCU000, AST_REG_SCU000_PROTECTION_KEY, 0x01);
+
+ /* Clear bits [17:16] of SCU2C */
+ scu02c = ast_mindwm(ast, AST_REG_SCU02C);
+ scu02c &= 0xfffcffff;
+ ast_moutdwm(ast, AST_REG_SCU02C, scu02c);
/* Disable DVO */
ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00);
diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c
index b9a9b050b546..ba6cf5fa901c 100644
--- a/drivers/gpu/drm/ast/ast_drv.c
+++ b/drivers/gpu/drm/ast/ast_drv.c
@@ -47,6 +47,77 @@ static int ast_modeset = -1;
MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
module_param_named(modeset, ast_modeset, int, 0400);
+/*
+ * Register access
+ */
+
+/* Select R/W segment */
+static void __ast_selseg(void __iomem *regs, u32 r)
+{
+ u32 p2a04, p2a04_base;
+
+ p2a04 = r & AST_REG_P2A04_BASE_MASK;
+ __ast_write32(regs, AST_REG_P2A04, p2a04);
+ __ast_write32(regs, AST_REG_P2A00, AST_REG_P2A00_PROTECTION_KEY);
+
+ do {
+ cpu_relax();
+ p2a04_base = __ast_read32(regs, AST_REG_P2A04);
+ p2a04_base &= AST_REG_P2A04_BASE_MASK;
+ } while (p2a04_base != p2a04);
+}
+
+/* Read within segment */
+static u32 __ast_rdseg32(void __iomem *regs, u32 r)
+{
+ return __ast_read32(regs, AST_REG_P2A_ADDR(r));
+}
+
+/* Write within segment */
+static void __ast_wrseg32(void __iomem *regs, u32 r, u32 v)
+{
+ __ast_write32(regs, AST_REG_P2A_ADDR(r), v);
+}
+
+u32 __ast_mindwm(void __iomem *regs, u32 r)
+{
+ __ast_selseg(regs, r);
+
+ return __ast_rdseg32(regs, r);
+}
+
+void __ast_moutdwm(void __iomem *regs, u32 r, u32 v)
+{
+ __ast_selseg(regs, r);
+ __ast_wrseg32(regs, r, v);
+}
+
+u32 ast_mindwm(struct ast_device *ast, u32 r)
+{
+ return __ast_mindwm(ast->regs, r);
+}
+
+void ast_moutdwm(struct ast_device *ast, u32 r, u32 v)
+{
+ __ast_moutdwm(ast->regs, r, v);
+}
+
+void ast_moutdwm_poll(struct ast_device *ast, u32 r, u32 v, u32 res)
+{
+ void __iomem *regs = ast->regs;
+
+ __ast_selseg(regs, r);
+ __ast_wrseg32(regs, r, v);
+
+ do {
+ cpu_relax();
+ } while (__ast_rdseg32(regs, r) != res);
+}
+
+/*
+ * AST device
+ */
+
void ast_device_init(struct ast_device *ast,
enum ast_chip chip,
enum ast_config_mode config_mode,
@@ -171,7 +242,7 @@ static int ast_detect_chip(struct pci_dev *pdev,
enum ast_config_mode config_mode = ast_use_defaults;
uint32_t scu_rev = 0xffffffff;
enum ast_chip chip;
- u32 data;
+ u32 data, p2a04, scu07c;
u8 vgacrd0, vgacrd1;
/*
@@ -204,14 +275,13 @@ static int ast_detect_chip(struct pci_dev *pdev,
}
/* Double check that it's actually working */
- data = __ast_read32(regs, 0xf004);
- if ((data != 0xffffffff) && (data != 0x00)) {
+ p2a04 = __ast_read32(regs, AST_REG_P2A04);
+ if (p2a04 != 0xffffffff && p2a04 != 0x00000000) {
config_mode = ast_use_p2a;
- /* Read SCU7c (silicon revision register) */
- __ast_write32(regs, 0xf004, 0x1e6e0000);
- __ast_write32(regs, 0xf000, 0x1);
- scu_rev = __ast_read32(regs, 0x1207c);
+ /* Read SCU7C (silicon revision register) */
+ scu07c = __ast_mindwm(regs, AST_REG_SCU07C);
+ scu_rev = scu07c & AST_REG_SCU07C_CHIP_BONDING_MASK;
}
}
}
diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h
index 787e38c6c17d..4f221b848d68 100644
--- a/drivers/gpu/drm/ast/ast_drv.h
+++ b/drivers/gpu/drm/ast/ast_drv.h
@@ -259,26 +259,20 @@ static inline bool __ast_gen_is_eq(struct ast_device *ast, unsigned long gen)
#define IS_AST_GEN6(__ast) __ast_gen_is_eq(__ast, 6)
#define IS_AST_GEN7(__ast) __ast_gen_is_eq(__ast, 7)
+/*
+ * MMIO access
+ */
+
static inline u8 __ast_read8(const void __iomem *addr, u32 reg)
{
return ioread8(addr + reg);
}
-static inline u32 __ast_read32(const void __iomem *addr, u32 reg)
-{
- return ioread32(addr + reg);
-}
-
static inline void __ast_write8(void __iomem *addr, u32 reg, u8 val)
{
iowrite8(val, addr + reg);
}
-static inline void __ast_write32(void __iomem *addr, u32 reg, u32 val)
-{
- iowrite32(val, addr + reg);
-}
-
static inline u8 __ast_read8_i(void __iomem *addr, u32 reg, u8 index)
{
__ast_write8(addr, reg, index);
@@ -307,16 +301,6 @@ static inline void __ast_write8_i_masked(void __iomem *addr, u32 reg, u8 index,
__ast_write8_i(addr, reg, index, tmp | val);
}
-static inline u32 ast_read32(struct ast_device *ast, u32 reg)
-{
- return __ast_read32(ast->regs, reg);
-}
-
-static inline void ast_write32(struct ast_device *ast, u32 reg, u32 val)
-{
- __ast_write32(ast->regs, reg, val);
-}
-
static inline u8 ast_io_read8(struct ast_device *ast, u32 reg)
{
return __ast_read8(ast->ioregs, reg);
@@ -349,6 +333,40 @@ static inline void ast_set_index_reg_mask(struct ast_device *ast, u32 base, u8 i
__ast_write8_i_masked(ast->ioregs, base, index, preserve_mask, val);
}
+/*
+ * Register access
+ */
+
+static inline u32 __ast_read32(const void __iomem *addr, u32 reg)
+{
+ return ioread32(addr + reg);
+}
+
+static inline void __ast_write32(void __iomem *addr, u32 reg, u32 val)
+{
+ iowrite32(val, addr + reg);
+}
+
+static inline u32 ast_read32(struct ast_device *ast, u32 reg)
+{
+ return __ast_read32(ast->regs, reg);
+}
+
+static inline void ast_write32(struct ast_device *ast, u32 reg, u32 val)
+{
+ __ast_write32(ast->regs, reg, val);
+}
+
+u32 __ast_mindwm(void __iomem *regs, u32 r);
+void __ast_moutdwm(void __iomem *regs, u32 r, u32 v);
+u32 ast_mindwm(struct ast_device *ast, u32 r);
+void ast_moutdwm(struct ast_device *ast, u32 r, u32 v);
+void ast_moutdwm_poll(struct ast_device *ast, u32 r, u32 v, u32 res);
+
+/*
+ * VBIOS
+ */
+
struct ast_vbios_stdtable {
u8 misc;
u8 seq[4];
@@ -517,8 +535,6 @@ struct drm_device *ast_2600_device_create(struct pci_dev *pdev,
/* ast post */
int ast_post_gpu(struct ast_device *ast);
-u32 ast_mindwm(struct ast_device *ast, u32 r);
-void ast_moutdwm(struct ast_device *ast, u32 r, u32 v);
int ast_vga_output_init(struct ast_device *ast);
int ast_sil164_output_init(struct ast_device *ast);
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 21abb6a6d8bc..91b83ec00e9c 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -105,8 +105,9 @@ static void ast_crtc_fill_gamma(struct ast_device *ast,
/* gamma table is used as color palette */
drm_crtc_fill_palette_8(crtc, ast_set_gamma_lut);
break;
+ case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
- /* also uses 8-bit gamma ramp on low-color modes */
+ /* also uses 24-bit gamma correction on high-color modes */
fallthrough;
case DRM_FORMAT_XRGB8888:
drm_crtc_fill_gamma_888(crtc, ast_set_gamma_lut);
@@ -129,8 +130,9 @@ static void ast_crtc_load_gamma(struct ast_device *ast,
/* gamma table is used as color palette */
drm_crtc_load_palette_8(crtc, lut, ast_set_gamma_lut);
break;
+ case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
- /* also uses 8-bit gamma ramp on low-color modes */
+ /* also uses 24-bit gamma correction on high-color modes */
fallthrough;
case DRM_FORMAT_XRGB8888:
drm_crtc_load_gamma_888(crtc, lut, ast_set_gamma_lut);
@@ -146,30 +148,35 @@ static void ast_set_vbios_color_reg(struct ast_device *ast,
const struct drm_format_info *format,
const struct ast_vbios_enhtable *vmode)
{
- u32 color_index;
+ u8 vgacr8c = 0x00;
+ u8 vgacr92 = 0x00;
- switch (format->cpp[0]) {
- case 1:
- color_index = VGAModeIndex - 1;
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_VGA;
+ vgacr92 = 8;
+ break;
+ case DRM_FORMAT_XRGB1555:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_15_BPP;
+ vgacr92 = 15;
break;
- case 2:
- color_index = HiCModeIndex;
+ case DRM_FORMAT_RGB565:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_16_BPP;
+ vgacr92 = 16;
break;
- case 3:
- case 4:
- color_index = TrueCModeIndex;
+ case DRM_FORMAT_XRGB8888:
+ vgacr8c |= AST_IO_VGACR8C_CUR_MODE_32_BPP;
+ vgacr92 = 32;
break;
- default:
- return;
}
- ast_set_index_reg(ast, AST_IO_VGACRI, 0x8c, (u8)((color_index & 0x0f) << 4));
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x8c, vgacr8c);
ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0x00);
if (vmode->flags & NewModeInfo) {
- ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0xa8);
- ast_set_index_reg(ast, AST_IO_VGACRI, 0x92, format->cpp[0] * 8);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, AST_IO_VGACR91_PASSWORD);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x92, vgacr92);
}
}
@@ -188,7 +195,7 @@ static void ast_set_vbios_mode_reg(struct ast_device *ast,
ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0x00);
if (vmode->flags & NewModeInfo) {
- ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, 0xa8);
+ ast_set_index_reg(ast, AST_IO_VGACRI, 0x91, AST_IO_VGACR91_PASSWORD);
ast_set_index_reg(ast, AST_IO_VGACRI, 0x93, adjusted_mode->clock / 1000);
ast_set_index_reg(ast, AST_IO_VGACRI, 0x94, adjusted_mode->crtc_hdisplay);
ast_set_index_reg(ast, AST_IO_VGACRI, 0x95, adjusted_mode->crtc_hdisplay >> 8);
@@ -381,30 +388,36 @@ static void ast_set_dclk_reg(struct ast_device *ast,
static void ast_set_color_reg(struct ast_device *ast,
const struct drm_format_info *format)
{
- u8 jregA0 = 0, jregA3 = 0, jregA8 = 0;
+ u8 vgacra0 = 0x00;
+ u8 vgacra3 = 0x00;
+ u8 vgacra8 = 0x00;
- switch (format->cpp[0] * 8) {
- case 8:
- jregA0 = 0x70;
- jregA3 = 0x01;
- jregA8 = 0x00;
+ vgacra0 |= AST_IO_VGACRA0_MEMORY_CHAIN4_MODE |
+ AST_IO_VGACRA0_LINEAR_EXT_ACCESS |
+ AST_IO_VGACRA0_SEGMENTED_EXT_ACCESS;
+
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ vgacra3 |= AST_IO_VGACRA3_256_COLORS;
+ vgacra8 &= ~AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
break;
- case 15:
- case 16:
- jregA0 = 0x70;
- jregA3 = 0x04;
- jregA8 = 0x02;
+ case DRM_FORMAT_XRGB1555:
+ vgacra3 |= AST_IO_VGACRA3_15_BPP;
+ vgacra8 |= AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
break;
- case 32:
- jregA0 = 0x70;
- jregA3 = 0x08;
- jregA8 = 0x02;
+ case DRM_FORMAT_RGB565:
+ vgacra3 |= AST_IO_VGACRA3_16_BPP;
+ vgacra8 |= AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ vgacra3 |= AST_IO_VGACRA3_32_BPP;
+ vgacra8 |= AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED;
break;
}
- ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa0, 0x8f, jregA0);
- ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xf0, jregA3);
- ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa8, 0xfd, jregA8);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa0, 0x8f, vgacra0);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xf0, vgacra3);
+ ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa8, 0xfd, vgacra8);
}
static void ast_set_crtthd_reg(struct ast_device *ast)
@@ -489,6 +502,7 @@ void __iomem *ast_plane_vaddr(struct ast_plane *ast_plane)
static const uint32_t ast_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB1555,
DRM_FORMAT_C8,
};
@@ -762,10 +776,10 @@ static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
case DRM_FORMAT_C8:
ast_state->std_table = &vbios_stdtable[VGAModeIndex];
break;
+ case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
ast_state->std_table = &vbios_stdtable[HiCModeIndex];
break;
- case DRM_FORMAT_RGB888:
case DRM_FORMAT_XRGB8888:
ast_state->std_table = &vbios_stdtable[TrueCModeIndex];
break;
diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c
index b72914dbed38..297230f1efaf 100644
--- a/drivers/gpu/drm/ast/ast_post.c
+++ b/drivers/gpu/drm/ast/ast_post.c
@@ -34,44 +34,6 @@
#include "ast_drv.h"
#include "ast_post.h"
-u32 __ast_mindwm(void __iomem *regs, u32 r)
-{
- u32 data;
-
- __ast_write32(regs, 0xf004, r & 0xffff0000);
- __ast_write32(regs, 0xf000, 0x1);
-
- do {
- data = __ast_read32(regs, 0xf004) & 0xffff0000;
- } while (data != (r & 0xffff0000));
-
- return __ast_read32(regs, 0x10000 + (r & 0x0000ffff));
-}
-
-void __ast_moutdwm(void __iomem *regs, u32 r, u32 v)
-{
- u32 data;
-
- __ast_write32(regs, 0xf004, r & 0xffff0000);
- __ast_write32(regs, 0xf000, 0x1);
-
- do {
- data = __ast_read32(regs, 0xf004) & 0xffff0000;
- } while (data != (r & 0xffff0000));
-
- __ast_write32(regs, 0x10000 + (r & 0x0000ffff), v);
-}
-
-u32 ast_mindwm(struct ast_device *ast, u32 r)
-{
- return __ast_mindwm(ast->regs, r);
-}
-
-void ast_moutdwm(struct ast_device *ast, u32 r, u32 v)
-{
- __ast_moutdwm(ast->regs, r, v);
-}
-
int ast_post_gpu(struct ast_device *ast)
{
int ret;
@@ -107,19 +69,19 @@ bool mmc_test(struct ast_device *ast, u32 datagen, u8 test_ctl)
{
u32 data, timeout;
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
- ast_moutdwm(ast, 0x1e6e0070, (datagen << 3) | test_ctl);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, (datagen << 3) | test_ctl);
timeout = 0;
do {
- data = ast_mindwm(ast, 0x1e6e0070) & 0x3000;
+ data = ast_mindwm(ast, AST_REG_MCR70) & 0x3000;
if (data & 0x2000)
return false;
if (++timeout > TIMEOUT) {
- ast_moutdwm(ast, 0x1e6e0070, 0x00000000);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x00000000);
return false;
}
} while (!data);
- ast_moutdwm(ast, 0x1e6e0070, 0x0);
+ ast_moutdwm(ast, AST_REG_MCR70, 0x0);
return true;
}
diff --git a/drivers/gpu/drm/ast/ast_post.h b/drivers/gpu/drm/ast/ast_post.h
index aa5d247bebe8..41b2db08a870 100644
--- a/drivers/gpu/drm/ast/ast_post.h
+++ b/drivers/gpu/drm/ast/ast_post.h
@@ -10,13 +10,10 @@ struct ast_device;
/* DRAM timing tables */
struct ast_dramstruct {
- u16 index;
+ u32 index;
u32 data;
};
-/* hardware fields */
-#define __AST_DRAMSTRUCT_DRAM_TYPE 0x0004
-
/* control commands */
#define __AST_DRAMSTRUCT_UDELAY 0xff00
#define __AST_DRAMSTRUCT_INVALID 0xffff
@@ -24,20 +21,21 @@ struct ast_dramstruct {
#define __AST_DRAMSTRUCT_INDEX(_name) \
(__AST_DRAMSTRUCT_ ## _name)
-#define AST_DRAMSTRUCT_INIT(_name, _value) \
- { __AST_DRAMSTRUCT_INDEX(_name), (_value) }
+#define __AST_DRAMSTRUCT_INIT(_index, _value) \
+ { (_index), (_value) }
+#define AST_DRAMSTRUCT_REG(_reg, _value) \
+ __AST_DRAMSTRUCT_INIT(_reg, _value)
#define AST_DRAMSTRUCT_UDELAY(_usecs) \
- AST_DRAMSTRUCT_INIT(UDELAY, _usecs)
+ __AST_DRAMSTRUCT_INIT(__AST_DRAMSTRUCT_UDELAY, _usecs)
#define AST_DRAMSTRUCT_INVALID \
- AST_DRAMSTRUCT_INIT(INVALID, U32_MAX)
+ __AST_DRAMSTRUCT_INIT(__AST_DRAMSTRUCT_INVALID, U32_MAX)
+#define AST_DRAMSTRUCT_IS_REG(_entry, _reg) \
+ ((_entry)->index == (_reg))
#define AST_DRAMSTRUCT_IS(_entry, _name) \
((_entry)->index == __AST_DRAMSTRUCT_INDEX(_name))
-u32 __ast_mindwm(void __iomem *regs, u32 r);
-void __ast_moutdwm(void __iomem *regs, u32 r, u32 v);
-
bool mmc_test(struct ast_device *ast, u32 datagen, u8 test_ctl);
bool mmc_test_burst(struct ast_device *ast, u32 datagen);
diff --git a/drivers/gpu/drm/ast/ast_reg.h b/drivers/gpu/drm/ast/ast_reg.h
index 30578e3b07e4..b808b0a02ba8 100644
--- a/drivers/gpu/drm/ast/ast_reg.h
+++ b/drivers/gpu/drm/ast/ast_reg.h
@@ -28,18 +28,49 @@
#define AST_IO_VGAPDR (0x49)
#define AST_IO_VGAGRI (0x4E)
-#define AST_IO_VGACRI (0x54)
-#define AST_IO_VGACR17_SYNC_ENABLE BIT(7) /* called "Hardware reset" in docs */
-#define AST_IO_VGACR80_PASSWORD (0xa8)
-#define AST_IO_VGACR99_VGAMEM_RSRV_MASK GENMASK(1, 0)
-#define AST_IO_VGACRA1_VGAIO_DISABLED BIT(1)
-#define AST_IO_VGACRA1_MMIO_ENABLED BIT(2)
-#define AST_IO_VGACRA3_DVO_ENABLED BIT(7)
-#define AST_IO_VGACRAA_VGAMEM_SIZE_MASK GENMASK(1, 0)
-#define AST_IO_VGACRB6_HSYNC_OFF BIT(0)
-#define AST_IO_VGACRB6_VSYNC_OFF BIT(1)
-#define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */
-#define AST_IO_VGACRCB_HWC_ENABLED BIT(1)
+#define AST_IO_VGACRI (0x54)
+#define AST_IO_VGACR17_SYNC_ENABLE BIT(7) /* called "Hardware reset" in docs */
+#define AST_IO_VGACR80_PASSWORD (0xa8)
+
+#define AST_IO_VGACR8C_NEW_MODE_MASK GENMASK(3, 0)
+#define AST_IO_VGACR8C_NEW_MODE_EGA (0x00)
+#define AST_IO_VGACR8C_NEW_MODE_VGA (0x01)
+#define AST_IO_VGACR8C_NEW_MODE_15_BPP (0x02)
+#define AST_IO_VGACR8C_NEW_MODE_16_BPP (0x03)
+#define AST_IO_VGACR8C_NEW_MODE_32_BPP (0x04)
+#define AST_IO_VGACR8C_NEW_MODE_CGA (0x0f)
+#define AST_IO_VGACR8C_NEW_MODE_TEXT (0x0e)
+#define AST_IO_VGACR8C_CUR_MODE_MASK GENMASK(7, 4)
+#define AST_IO_VGACR8C_CUR_MODE_EGA (0x00)
+#define AST_IO_VGACR8C_CUR_MODE_VGA (0x10)
+#define AST_IO_VGACR8C_CUR_MODE_15_BPP (0x20)
+#define AST_IO_VGACR8C_CUR_MODE_16_BPP (0x30)
+#define AST_IO_VGACR8C_CUR_MODE_32_BPP (0x40)
+#define AST_IO_VGACR8C_CUR_MODE_CGA (0xf0)
+#define AST_IO_VGACR8C_CUR_MODE_TEXT (0xe0)
+
+#define AST_IO_VGACR91_PASSWORD (0xa8)
+#define AST_IO_VGACR99_VGAMEM_RSRV_MASK GENMASK(1, 0)
+
+#define AST_IO_VGACRA0_MEMORY_CHAIN4_MODE BIT(6)
+#define AST_IO_VGACRA0_LINEAR_EXT_ACCESS BIT(5)
+#define AST_IO_VGACRA0_SEGMENTED_EXT_ACCESS BIT(4)
+
+#define AST_IO_VGACRA1_VGAIO_DISABLED BIT(1)
+#define AST_IO_VGACRA1_MMIO_ENABLED BIT(2)
+
+#define AST_IO_VGACRA3_DVO_ENABLED BIT(7)
+#define AST_IO_VGACRA3_32_BPP BIT(3)
+#define AST_IO_VGACRA3_16_BPP BIT(2)
+#define AST_IO_VGACRA3_15_BPP BIT(1)
+#define AST_IO_VGACRA3_256_COLORS BIT(0)
+
+#define AST_IO_VGACRA8_GAMMA_CORRECTION_ENABLED BIT(1)
+#define AST_IO_VGACRAA_VGAMEM_SIZE_MASK GENMASK(1, 0)
+#define AST_IO_VGACRB6_HSYNC_OFF BIT(0)
+#define AST_IO_VGACRB6_VSYNC_OFF BIT(1)
+#define AST_IO_VGACRCB_HWC_16BPP BIT(0) /* set: ARGB4444, cleared: 2bpp palette */
+#define AST_IO_VGACRCB_HWC_ENABLED BIT(1)
/* mirrors SCU100[7:0] */
#define AST_IO_VGACRD0_VRAM_INIT_STATUS_MASK GENMASK(7, 6)
@@ -75,4 +106,160 @@
#define AST_IO_VGAIR1_R (0x5A)
#define AST_IO_VGAIR1_VREFRESH BIT(3)
+/*
+ * P-Bus to AHB Bridge (0x00000000 - 0x0001ffff)
+ */
+
+#define AST_REG_P2A_BASE (0x00000000)
+#define AST_REG_P2A(__offset) (AST_REG_P2A_BASE + (__offset))
+#define AST_REG_P2A_ADDR(__addr) AST_REG_P2A(0x10000 + ((__addr) & GENMASK(15, 0)))
+#define AST_REG_P2A00 AST_REG_P2A(0xf000)
+#define AST_REG_P2A00_PROTECTION_KEY (0x01)
+#define AST_REG_P2A04 AST_REG_P2A(0xf004)
+#define AST_REG_P2A04_BASE_MASK GENMASK(31, 16)
+
+/*
+ * AHB Controller (0x1e600000 - 0x1e61ffff)
+ */
+
+#define AST_REG_AHBC_BASE (0x1e600000)
+#define AST_REG_AHBC(__offset) (AST_REG_AHBC_BASE + (__offset))
+#define AST_REG_AHBC00 AST_REG_AHBC(0x00)
+#define AST_REG_AHBC00_PROTECT_KEY (0xaeed1a03)
+#define AST_REG_AHBC84 AST_REG_AHBC(0x84)
+#define AST_REG_AHBC88 AST_REG_AHBC(0x88)
+
+/*
+ * SDRAM Memory Controller (0x1e6e0000 - 0x1e6e0fff)
+ */
+
+#define AST_REG_MCR_BASE (0x1e6e0000)
+#define AST_REG_MCR(__offset) (AST_REG_MCR_BASE + (__offset))
+#define AST_REG_MCR00 AST_REG_MCR(0x00)
+#define AST_REG_MCR00_PROTECTION_KEY (0xfc600309)
+#define AST_REG_MCR04 AST_REG_MCR(0x04)
+#define AST_REG_MCR08 AST_REG_MCR(0x08)
+#define AST_REG_MCR0C AST_REG_MCR(0x0c)
+#define AST_REG_MCR10 AST_REG_MCR(0x10)
+#define AST_REG_MCR14 AST_REG_MCR(0x14)
+#define AST_REG_MCR18 AST_REG_MCR(0x18)
+#define AST_REG_MCR1C AST_REG_MCR(0x1c)
+#define AST_REG_MCR20 AST_REG_MCR(0x20)
+#define AST_REG_MCR24 AST_REG_MCR(0x24)
+#define AST_REG_MCR28 AST_REG_MCR(0x28)
+#define AST_REG_MCR2C AST_REG_MCR(0x2C)
+#define AST_REG_MCR30 AST_REG_MCR(0x30)
+#define AST_REG_MCR34 AST_REG_MCR(0x34)
+#define AST_REG_MCR38 AST_REG_MCR(0x38)
+#define AST_REG_MCR3C AST_REG_MCR(0x3c)
+#define AST_REG_MCR40 AST_REG_MCR(0x40)
+#define AST_REG_MCR44 AST_REG_MCR(0x44)
+#define AST_REG_MCR48 AST_REG_MCR(0x48)
+#define AST_REG_MCR4C AST_REG_MCR(0x4C)
+#define AST_REG_MCR50 AST_REG_MCR(0x50)
+#define AST_REG_MCR54 AST_REG_MCR(0x54)
+#define AST_REG_MCR58 AST_REG_MCR(0x58)
+#define AST_REG_MCR5C AST_REG_MCR(0x5c)
+#define AST_REG_MCR60 AST_REG_MCR(0x60)
+#define AST_REG_MCR64 AST_REG_MCR(0x64)
+#define AST_REG_MCR68 AST_REG_MCR(0x68)
+#define AST_REG_MCR6C AST_REG_MCR(0x6c)
+#define AST_REG_MCR70 AST_REG_MCR(0x70)
+#define AST_REG_MCR74 AST_REG_MCR(0x74)
+#define AST_REG_MCR78 AST_REG_MCR(0x78)
+#define AST_REG_MCR7C AST_REG_MCR(0x7c)
+#define AST_REG_MCR80 AST_REG_MCR(0x80)
+#define AST_REG_MCR84 AST_REG_MCR(0x84)
+#define AST_REG_MCR88 AST_REG_MCR(0x88)
+#define AST_REG_MCR8C AST_REG_MCR(0x8c)
+#define AST_REG_MCR100 AST_REG_MCR(0x100)
+#define AST_REG_MCR108 AST_REG_MCR(0x108)
+#define AST_REG_MCR120 AST_REG_MCR(0x120)
+#define AST_REG_MCR140 AST_REG_MCR(0x140)
+#define AST_REG_MCR200 AST_REG_MCR(0x200)
+#define AST_REG_MCR204 AST_REG_MCR(0x204)
+#define AST_REG_MCR208 AST_REG_MCR(0x208)
+#define AST_REG_MCR20C AST_REG_MCR(0x20C)
+#define AST_REG_MCR210 AST_REG_MCR(0x210)
+#define AST_REG_MCR214 AST_REG_MCR(0x214)
+#define AST_REG_MCR218 AST_REG_MCR(0x218)
+#define AST_REG_MCR220 AST_REG_MCR(0x220)
+#define AST_REG_MCR228 AST_REG_MCR(0x228)
+#define AST_REG_MCR230 AST_REG_MCR(0x230)
+#define AST_REG_MCR2A8 AST_REG_MCR(0x2a8)
+#define AST_REG_MCR2B0 AST_REG_MCR(0x2b0)
+#define AST_REG_MCR240 AST_REG_MCR(0x240)
+#define AST_REG_MCR244 AST_REG_MCR(0x244)
+#define AST_REG_MCR248 AST_REG_MCR(0x248)
+#define AST_REG_MCR24C AST_REG_MCR(0x24c)
+#define AST_REG_MCR290 AST_REG_MCR(0x290)
+#define AST_REG_MCR2C0 AST_REG_MCR(0x2c0)
+#define AST_REG_MCR2C4 AST_REG_MCR(0x2c4)
+#define AST_REG_MCR2C8 AST_REG_MCR(0x2c8)
+#define AST_REG_MCR2CC AST_REG_MCR(0x2cc)
+#define AST_REG_MCR2E0 AST_REG_MCR(0x2e0)
+#define AST_REG_MCR2E4 AST_REG_MCR(0x2e4)
+#define AST_REG_MCR2E8 AST_REG_MCR(0x2e8)
+#define AST_REG_MCR2EC AST_REG_MCR(0x2ec)
+#define AST_REG_MCR2F0 AST_REG_MCR(0x2f0)
+#define AST_REG_MCR2F4 AST_REG_MCR(0x2f4)
+#define AST_REG_MCR2F8 AST_REG_MCR(0x2f8)
+#define AST_REG_MCR300 AST_REG_MCR(0x300)
+#define AST_REG_MCR3D0 AST_REG_MCR(0x3d0)
+
+/*
+ * System Control Unit (0x1e6e2000 - 0x1e6e2fff)
+ */
+
+#define AST_REG_SCU_BASE (0x1e6e2000)
+#define AST_REG_SCU(__offset) (AST_REG_SCU_BASE + (__offset))
+#define AST_REG_SCU000 AST_REG_SCU(0x000)
+#define AST_REG_SCU000_PROTECTION_KEY (0x1688a8a8)
+#define AST_REG_SCU008 AST_REG_SCU(0x008)
+#define AST_REG_SCU00C AST_REG_SCU(0x00c)
+#define AST_REG_SCU020 AST_REG_SCU(0x020)
+#define AST_REG_SCU02C AST_REG_SCU(0x02c)
+#define AST_REG_SCU040 AST_REG_SCU(0x040)
+#define AST_REG_SCU070 AST_REG_SCU(0x070)
+#define AST_REG_SCU07C AST_REG_SCU(0x07c)
+#define AST_REG_SCU07C_CHIP_BONDING_MASK GENMASK(15, 8)
+#define AST_REG_SCU084 AST_REG_SCU(0x084)
+#define AST_REG_SCU088 AST_REG_SCU(0x088)
+#define AST_REG_SCU08C AST_REG_SCU(0x08c)
+#define AST_REG_SCU090 AST_REG_SCU(0x090)
+#define AST_REG_SCU094 AST_REG_SCU(0x094)
+#define AST_REG_SCU0A4 AST_REG_SCU(0x0a4)
+#define AST_REG_SCU0A8 AST_REG_SCU(0x0a8)
+#define AST_REG_SCU100 AST_REG_SCU(0x100)
+#define AST_REG_SCU104 AST_REG_SCU(0x104)
+#define AST_REG_SCU160 AST_REG_SCU(0x160)
+
+/*
+ * AHB-to-P Bus Bridge (0x1e720000 - 0x1e73ffff)
+ */
+
+#define AST_REG_A2P_BASE (0x1e720000)
+#define AST_REG_A2P(__offset) (AST_REG_A2P_BASE + (__offset))
+#define AST_REG_A2P58 AST_REG_A2P(0x58)
+
+/*
+ * Watchdog timer (0x1e785000 - 0x1e785fff)
+ */
+
+#define AST_REG_WDT_BASE(__n) (0x1e785000 + (__n) * 0x40)
+#define AST_REG_WDT(__n, __offset) (AST_REG_WDT_BASE((__n)) + (__offset))
+#define AST_REG_WDT04(__n) AST_REG_WDT((__n), 0x04)
+#define AST_REG_WDT08(__n) AST_REG_WDT((__n), 0x08)
+#define AST_REG_WDT0C(__n) AST_REG_WDT((__n), 0x0c)
+#define AST_REG_WDT14(__n) AST_REG_WDT((__n), 0x14)
+#define AST_REG_WDT1C(__n) AST_REG_WDT((__n), 0x1c)
+#define AST_REG_WDT2C(__n) AST_REG_WDT((__n), 0x2c)
+
+/*
+ * SDRAM (0x80000000 - 0xffffffff)
+ */
+
+#define AST_SDRAM_BASE (0x80000000)
+#define AST_SDRAM(__offset) (AST_SDRAM_BASE + (__offset))
+
#endif
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index c3209b0f4678..f81b566c82a1 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -262,6 +262,16 @@ config DRM_NXP_PTN3460
help
NXP PTN3460 eDP-LVDS bridge chip driver.
+config DRM_OF_DISPLAY_MODE_BRIDGE
+ tristate
+ depends on DRM_BRIDGE && OF
+ help
+ This is a DRM bridge implementation that uses of_get_drm_display_mode
+ to acquire display mode.
+
+ It exists for compatibility with legacy display mode parsing, in order
+ to conform to the panel-bridge framework.
+
config DRM_PARADE_PS8622
tristate "Parade eDP/LVDS bridge"
depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index beab5b695a6e..15cc821d85b7 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
obj-$(CONFIG_DRM_MICROCHIP_LVDS_SERIALIZER) += microchip-lvds.o
obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
+obj-$(CONFIG_DRM_OF_DISPLAY_MODE_BRIDGE) += of-display-mode-bridge.o
obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
obj-$(CONFIG_DRM_SAMSUNG_DSIM) += samsung-dsim.o
diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig
index 03dc7ffe824a..57bb2daa5aaf 100644
--- a/drivers/gpu/drm/bridge/analogix/Kconfig
+++ b/drivers/gpu/drm/bridge/analogix/Kconfig
@@ -15,6 +15,7 @@ config DRM_ANALOGIX_ANX6345
config DRM_ANALOGIX_ANX78XX
tristate "Analogix ANX78XX bridge"
+ depends on OF
select DRM_ANALOGIX_DP
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_HELPER
@@ -29,6 +30,8 @@ config DRM_ANALOGIX_ANX78XX
config DRM_ANALOGIX_DP
tristate
depends on DRM
+ depends on OF
+ select DRM_DISPLAY_DP_AUX_BUS
config DRM_ANALOGIX_ANX7625
tristate "Analogix Anx7625 MIPI to DP interface support"
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 8dee5f2fbde5..460729fdcecd 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -6,6 +6,7 @@
* Author: Jingoo Han <jg1.han@samsung.com>
*/
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/err.h>
@@ -20,12 +21,14 @@
#include <linux/platform_device.h>
#include <drm/bridge/analogix_dp.h>
+#include <drm/display/drm_dp_aux_bus.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -747,9 +750,6 @@ static int analogix_dp_commit(struct analogix_dp_device *dp)
{
int ret;
- /* Keep the panel disabled while we configure video */
- drm_panel_disable(dp->plat_data->panel);
-
ret = analogix_dp_train_link(dp);
if (ret) {
dev_err(dp->dev, "unable to do link train, ret=%d\n", ret);
@@ -769,9 +769,6 @@ static int analogix_dp_commit(struct analogix_dp_device *dp)
return ret;
}
- /* Safe to enable the panel now */
- drm_panel_enable(dp->plat_data->panel);
-
/* Check whether panel supports fast training */
ret = analogix_dp_fast_link_train_detection(dp);
if (ret)
@@ -856,79 +853,47 @@ static int analogix_dp_disable_psr(struct analogix_dp_device *dp)
return analogix_dp_send_psr_spd(dp, &psr_vsc, true);
}
-static int analogix_dp_get_modes(struct drm_connector *connector)
-{
- struct analogix_dp_device *dp = to_dp(connector);
- const struct drm_edid *drm_edid;
- int num_modes = 0;
-
- if (dp->plat_data->panel) {
- num_modes += drm_panel_get_modes(dp->plat_data->panel, connector);
- } else {
- drm_edid = drm_edid_read_ddc(connector, &dp->aux.ddc);
-
- drm_edid_connector_update(&dp->connector, drm_edid);
-
- if (drm_edid) {
- num_modes += drm_edid_connector_add_modes(&dp->connector);
- drm_edid_free(drm_edid);
- }
- }
-
- if (dp->plat_data->get_modes)
- num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
-
- return num_modes;
-}
-
-static struct drm_encoder *
-analogix_dp_best_encoder(struct drm_connector *connector)
+static const struct drm_edid *analogix_dp_bridge_edid_read(struct drm_bridge *bridge,
+ struct drm_connector *connector)
{
- struct analogix_dp_device *dp = to_dp(connector);
+ struct analogix_dp_device *dp = to_dp(bridge);
- return dp->encoder;
+ return drm_edid_read_ddc(connector, &dp->aux.ddc);
}
-
-static int analogix_dp_atomic_check(struct drm_connector *connector,
- struct drm_atomic_state *state)
+static int analogix_dp_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
- struct analogix_dp_device *dp = to_dp(connector);
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state;
-
- conn_state = drm_atomic_get_new_connector_state(state, connector);
- if (WARN_ON(!conn_state))
- return -ENODEV;
+ struct analogix_dp_device *dp = to_dp(bridge);
+ struct drm_display_info *di = &conn_state->connector->display_info;
+ u32 mask = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422);
+
+ if (is_rockchip(dp->plat_data->dev_type)) {
+ if ((di->color_formats & mask)) {
+ DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
+ di->color_formats &= ~mask;
+ di->color_formats |= BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444);
+ di->bpc = 8;
+ }
+ }
conn_state->self_refresh_aware = true;
- if (!conn_state->crtc)
- return 0;
-
- crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
- if (!crtc_state)
- return 0;
-
if (crtc_state->self_refresh_active && !dp->psr_supported)
return -EINVAL;
return 0;
}
-static const struct drm_connector_helper_funcs analogix_dp_connector_helper_funcs = {
- .get_modes = analogix_dp_get_modes,
- .best_encoder = analogix_dp_best_encoder,
- .atomic_check = analogix_dp_atomic_check,
-};
-
static enum drm_connector_status
-analogix_dp_detect(struct drm_connector *connector, bool force)
+analogix_dp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
- struct analogix_dp_device *dp = to_dp(connector);
+ struct analogix_dp_device *dp = to_dp(bridge);
enum drm_connector_status status = connector_status_disconnected;
- if (dp->plat_data->panel)
+ if (dp->plat_data->next_bridge)
return connector_status_connected;
if (!analogix_dp_detect_hpd(dp))
@@ -937,55 +902,23 @@ analogix_dp_detect(struct drm_connector *connector, bool force)
return status;
}
-static const struct drm_connector_funcs analogix_dp_connector_funcs = {
- .fill_modes = drm_helper_probe_single_connector_modes,
- .detect = analogix_dp_detect,
- .destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
enum drm_bridge_attach_flags flags)
{
struct analogix_dp_device *dp = to_dp(bridge);
- struct drm_connector *connector = NULL;
int ret = 0;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
- DRM_ERROR("Fix bridge driver to make connector optional!");
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+ DRM_ERROR("Unsupported connector creation\n");
return -EINVAL;
}
- if (!dp->plat_data->skip_connector) {
- connector = &dp->connector;
- connector->polled = DRM_CONNECTOR_POLL_HPD;
-
- ret = drm_connector_init(dp->drm_dev, connector,
- &analogix_dp_connector_funcs,
- DRM_MODE_CONNECTOR_eDP);
+ if (dp->plat_data->next_bridge) {
+ ret = drm_bridge_attach(dp->encoder, dp->plat_data->next_bridge, bridge,
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
- DRM_ERROR("Failed to initialize connector with drm\n");
- return ret;
- }
-
- drm_connector_helper_add(connector,
- &analogix_dp_connector_helper_funcs);
- drm_connector_attach_encoder(connector, encoder);
- }
-
- /*
- * NOTE: the connector registration is implemented in analogix
- * platform driver, that to say connector would be exist after
- * plat_data->attch return, that's why we record the connector
- * point after plat attached.
- */
- if (dp->plat_data->attach) {
- ret = dp->plat_data->attach(dp->plat_data, bridge, connector);
- if (ret) {
- DRM_ERROR("Failed at platform attach func\n");
+ dev_err(dp->dev, "failed to attach following panel or bridge (%d)\n", ret);
return ret;
}
}
@@ -1046,8 +979,6 @@ static void analogix_dp_bridge_atomic_pre_enable(struct drm_bridge *bridge,
/* Don't touch the panel if we're coming back from PSR */
if (old_crtc_state && old_crtc_state->self_refresh_active)
return;
-
- drm_panel_prepare(dp->plat_data->panel);
}
static int analogix_dp_set_bridge(struct analogix_dp_device *dp)
@@ -1087,14 +1018,21 @@ out_dp_init:
}
static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
+ struct drm_atomic_state *state,
const struct drm_display_mode *mode)
{
struct analogix_dp_device *dp = to_dp(bridge);
- struct drm_display_info *display_info = &dp->connector.display_info;
struct video_info *video = &dp->video_info;
struct device_node *dp_node = dp->dev->of_node;
+ struct drm_connector *connector;
+ struct drm_display_info *display_info;
int vic;
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (!connector)
+ return;
+ display_info = &connector->display_info;
+
/* Input video interlaces & hsync pol & vsync pol */
video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
@@ -1178,7 +1116,7 @@ static void analogix_dp_bridge_atomic_enable(struct drm_bridge *bridge,
new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
if (!new_crtc_state)
return;
- analogix_dp_bridge_mode_set(bridge, &new_crtc_state->adjusted_mode);
+ analogix_dp_bridge_mode_set(bridge, old_state, &new_crtc_state->adjusted_mode);
old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
/* Not a full enable, just disable PSR and continue */
@@ -1212,16 +1150,12 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
if (dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
- drm_panel_disable(dp->plat_data->panel);
-
disable_irq(dp->irq);
analogix_dp_set_analog_power_down(dp, POWER_ALL, 1);
pm_runtime_put_sync(dp->dev);
- drm_panel_unprepare(dp->plat_data->panel);
-
dp->fast_train_enable = false;
dp->psr_supported = false;
dp->dpms_mode = DRM_MODE_DPMS_OFF;
@@ -1294,7 +1228,10 @@ static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
.atomic_enable = analogix_dp_bridge_atomic_enable,
.atomic_disable = analogix_dp_bridge_atomic_disable,
.atomic_post_disable = analogix_dp_bridge_atomic_post_disable,
+ .atomic_check = analogix_dp_bridge_atomic_check,
.attach = analogix_dp_bridge_attach,
+ .edid_read = analogix_dp_bridge_edid_read,
+ .detect = analogix_dp_bridge_detect,
};
static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
@@ -1524,6 +1461,7 @@ EXPORT_SYMBOL_GPL(analogix_dp_resume);
int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev)
{
+ struct drm_bridge *bridge = &dp->bridge;
int ret;
dp->drm_dev = drm_dev;
@@ -1537,7 +1475,23 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev)
return ret;
}
- ret = drm_bridge_attach(dp->encoder, &dp->bridge, NULL, 0);
+ bridge->ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
+ bridge->of_node = dp->dev->of_node;
+ bridge->type = DRM_MODE_CONNECTOR_eDP;
+ ret = devm_drm_bridge_add(dp->dev, &dp->bridge);
+ if (ret)
+ goto err_unregister_aux;
+
+ if (dp->plat_data->panel) {
+ dp->plat_data->next_bridge = devm_drm_panel_bridge_add(dp->dev,
+ dp->plat_data->panel);
+ if (IS_ERR(dp->plat_data->next_bridge)) {
+ ret = PTR_ERR(bridge);
+ goto err_unregister_aux;
+ }
+ }
+
+ ret = drm_bridge_attach(dp->encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret) {
DRM_ERROR("failed to create bridge (%d)\n", ret);
goto err_unregister_aux;
@@ -1554,18 +1508,15 @@ EXPORT_SYMBOL_GPL(analogix_dp_bind);
void analogix_dp_unbind(struct analogix_dp_device *dp)
{
- analogix_dp_bridge_disable(&dp->bridge);
- dp->connector.funcs->destroy(&dp->connector);
-
- drm_panel_unprepare(dp->plat_data->panel);
-
drm_dp_aux_unregister(&dp->aux);
}
EXPORT_SYMBOL_GPL(analogix_dp_unbind);
int analogix_dp_start_crc(struct drm_connector *connector)
{
- struct analogix_dp_device *dp = to_dp(connector);
+ struct analogix_dp_device *dp;
+ struct drm_bridge *bridge __free(drm_bridge_put) =
+ drm_bridge_chain_get_first_bridge(connector->encoder);
if (!connector->state->crtc) {
DRM_ERROR("Connector %s doesn't currently have a CRTC.\n",
@@ -1573,13 +1524,25 @@ int analogix_dp_start_crc(struct drm_connector *connector)
return -EINVAL;
}
+ if (!bridge || bridge->type != DRM_MODE_CONNECTOR_eDP)
+ return -EINVAL;
+
+ dp = to_dp(bridge);
+
return drm_dp_start_crc(&dp->aux, connector->state->crtc);
}
EXPORT_SYMBOL_GPL(analogix_dp_start_crc);
int analogix_dp_stop_crc(struct drm_connector *connector)
{
- struct analogix_dp_device *dp = to_dp(connector);
+ struct analogix_dp_device *dp;
+ struct drm_bridge *bridge __free(drm_bridge_put) =
+ drm_bridge_chain_get_first_bridge(connector->encoder);
+
+ if (!bridge || bridge->type != DRM_MODE_CONNECTOR_eDP)
+ return -EINVAL;
+
+ dp = to_dp(bridge);
return drm_dp_stop_crc(&dp->aux);
}
@@ -1599,6 +1562,50 @@ struct drm_dp_aux *analogix_dp_get_aux(struct analogix_dp_device *dp)
}
EXPORT_SYMBOL_GPL(analogix_dp_get_aux);
+static int analogix_dp_aux_done_probing(struct drm_dp_aux *aux)
+{
+ struct analogix_dp_device *dp = to_dp(aux);
+ struct analogix_dp_plat_data *plat_data = dp->plat_data;
+ int port = plat_data->dev_type == EXYNOS_DP ? 0 : 1;
+ int ret;
+
+ /*
+ * If drm_of_find_panel_or_bridge() returns -ENODEV, there may be no valid panel
+ * or bridge nodes. The driver should go on for the driver-free bridge or the DP
+ * mode applications.
+ */
+ ret = drm_of_find_panel_or_bridge(dp->dev->of_node, port, 0,
+ &plat_data->panel, &plat_data->next_bridge);
+ if (ret && ret != -ENODEV)
+ return ret;
+
+ return component_add(dp->dev, plat_data->ops);
+}
+
+int analogix_dp_finish_probe(struct analogix_dp_device *dp)
+{
+ int ret;
+
+ ret = devm_of_dp_aux_populate_bus(&dp->aux, analogix_dp_aux_done_probing);
+ if (ret) {
+ /*
+ * If devm_of_dp_aux_populate_bus() returns -ENODEV, the done_probing() will
+ * not be called because there are no EP devices. Then the callback function
+ * analogix_dp_aux_done_probing() will be called directly in order to support
+ * the other valid DT configurations.
+ *
+ * NOTE: The devm_of_dp_aux_populate_bus() is allowed to return -EPROBE_DEFER.
+ */
+ if (ret != -ENODEV)
+ return dev_err_probe(dp->dev, ret, "failed to populate aux bus\n");
+
+ return analogix_dp_aux_done_probing(&dp->aux);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_finish_probe);
+
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Analogix DP Core Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index 91b215c6a0cf..17347448c6b0 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -154,7 +154,6 @@ struct analogix_dp_device {
struct drm_encoder *encoder;
struct device *dev;
struct drm_device *drm_dev;
- struct drm_connector connector;
struct drm_bridge bridge;
struct drm_dp_aux aux;
struct clk *clock;
diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig
index b9028a5e5a06..005a745e5c47 100644
--- a/drivers/gpu/drm/bridge/imx/Kconfig
+++ b/drivers/gpu/drm/bridge/imx/Kconfig
@@ -3,21 +3,13 @@ if ARCH_MXC || COMPILE_TEST
config DRM_IMX_LDB_HELPER
tristate
-config DRM_IMX_LEGACY_BRIDGE
- tristate
- depends on DRM_IMX
- help
- This is a DRM bridge implementation for the DRM i.MX IPUv3 driver,
- that uses of_get_drm_display_mode to acquire display mode.
-
- Newer designs should not use this bridge and should use proper panel
- driver instead.
-
config DRM_IMX8MP_DW_HDMI_BRIDGE
tristate "Freescale i.MX8MP HDMI-TX bridge support"
depends on OF
depends on COMMON_CLK
select DRM_DW_HDMI
+ select OF_OVERLAY
+ select DRM_DISPLAY_CONNECTOR
imply DRM_IMX8MP_HDMI_PAI
imply DRM_IMX8MP_HDMI_PVI
imply PHY_FSL_SAMSUNG_HDMI_PHY
@@ -25,6 +17,22 @@ config DRM_IMX8MP_DW_HDMI_BRIDGE
Choose this to enable support for the internal HDMI encoder found
on the i.MX8MP SoC.
+config DRM_IMX8MP_DW_HDMI_BRIDGE_CONNECTOR_FIXUP
+ bool
+ default y
+ depends on DRM_IMX_LCDIF
+ depends on DRM_IMX8MP_DW_HDMI_BRIDGE
+ depends on OF
+ help
+ Modifies at early boot the live device tree of boards using the
+ i.MX8MP fsl,imx8mp-hdmi-tx adding a hdmi-connector node linked to
+ the hdmi-tx. This is needed to support bridge-connector usage in
+ the i.MX8MP LCDIF driver.
+
+ You need this if you use the i.MX8MP HDMI output and your board
+ device tree file does not have an hdmi-connector node connected
+ to it.
+
config DRM_IMX8MP_HDMI_PAI
tristate "Freescale i.MX8MP HDMI PAI bridge support"
depends on OF
diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile
index 8d01fda25451..94ac8c40ebe9 100644
--- a/drivers/gpu/drm/bridge/imx/Makefile
+++ b/drivers/gpu/drm/bridge/imx/Makefile
@@ -1,6 +1,7 @@
obj-$(CONFIG_DRM_IMX_LDB_HELPER) += imx-ldb-helper.o
-obj-$(CONFIG_DRM_IMX_LEGACY_BRIDGE) += imx-legacy-bridge.o
obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE) += imx8mp-hdmi-tx.o
+obj-$(CONFIG_DRM_IMX8MP_DW_HDMI_BRIDGE_CONNECTOR_FIXUP) += imx8mp-hdmi-tx-connector-fixup.o \
+ imx8mp-hdmi-tx-connector-fixup.dtbo.o
obj-$(CONFIG_DRM_IMX8MP_HDMI_PAI) += imx8mp-hdmi-pai.o
obj-$(CONFIG_DRM_IMX8MP_HDMI_PVI) += imx8mp-hdmi-pvi.o
obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o
diff --git a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c b/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
deleted file mode 100644
index 0e31d5000e7c..000000000000
--- a/drivers/gpu/drm/bridge/imx/imx-legacy-bridge.c
+++ /dev/null
@@ -1,91 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Freescale i.MX drm driver
- *
- * bridge driver for legacy DT bindings, utilizing display-timings node
- */
-
-#include <linux/export.h>
-
-#include <drm/drm_bridge.h>
-#include <drm/drm_modes.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/bridge/imx.h>
-
-#include <video/of_display_timing.h>
-#include <video/of_videomode.h>
-
-struct imx_legacy_bridge {
- struct drm_bridge base;
-
- struct drm_display_mode mode;
- u32 bus_flags;
-};
-
-#define to_imx_legacy_bridge(bridge) container_of(bridge, struct imx_legacy_bridge, base)
-
-static int imx_legacy_bridge_attach(struct drm_bridge *bridge,
- struct drm_encoder *encoder,
- enum drm_bridge_attach_flags flags)
-{
- if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
- return -EINVAL;
-
- return 0;
-}
-
-static int imx_legacy_bridge_get_modes(struct drm_bridge *bridge,
- struct drm_connector *connector)
-{
- struct imx_legacy_bridge *imx_bridge = to_imx_legacy_bridge(bridge);
- int ret;
-
- ret = drm_connector_helper_get_modes_fixed(connector, &imx_bridge->mode);
- if (ret)
- return ret;
-
- connector->display_info.bus_flags = imx_bridge->bus_flags;
-
- return 0;
-}
-
-struct drm_bridge_funcs imx_legacy_bridge_funcs = {
- .attach = imx_legacy_bridge_attach,
- .get_modes = imx_legacy_bridge_get_modes,
-};
-
-struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev,
- struct device_node *np,
- int type)
-{
- struct imx_legacy_bridge *imx_bridge;
- int ret;
-
- imx_bridge = devm_drm_bridge_alloc(dev, struct imx_legacy_bridge,
- base, &imx_legacy_bridge_funcs);
- if (IS_ERR(imx_bridge))
- return ERR_CAST(imx_bridge);
-
- ret = of_get_drm_display_mode(np,
- &imx_bridge->mode,
- &imx_bridge->bus_flags,
- OF_USE_NATIVE_MODE);
- if (ret)
- return ERR_PTR(ret);
-
- imx_bridge->mode.type |= DRM_MODE_TYPE_DRIVER;
-
- imx_bridge->base.of_node = np;
- imx_bridge->base.ops = DRM_BRIDGE_OP_MODES;
- imx_bridge->base.type = type;
-
- ret = devm_drm_bridge_add(dev, &imx_bridge->base);
- if (ret)
- return ERR_PTR(ret);
-
- return &imx_bridge->base;
-}
-EXPORT_SYMBOL_GPL(devm_imx_drm_legacy_bridge);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Freescale i.MX DRM bridge driver for legacy DT bindings");
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.c
new file mode 100644
index 000000000000..aaac27d00590
--- /dev/null
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Add an hdmi-connector node to boards using the imx8mp hdmi_tx which
+ * don't have one. This is needed for the i.MX LCDIF to work with
+ * DRM_BRIDGE_ATTACH_NO_CONNECTOR.
+ *
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+
+/* Embedded dtbo symbols created by cmd_wrap_S_dtb in scripts/Makefile.dtbs */
+extern char __dtbo_imx8mp_hdmi_tx_connector_fixup_begin[];
+extern char __dtbo_imx8mp_hdmi_tx_connector_fixup_end[];
+
+static int __init imx8mp_hdmi_tx_connector_fixup_init(void)
+{
+ struct device_node *soc __free(device_node) = NULL;
+ struct device_node *hdmi_tx __free(device_node) = NULL;
+ struct device_node *endpoint __free(device_node) = NULL;
+ void *dtbo_start;
+ u32 dtbo_size;
+ int ovcs_id;
+ int err;
+
+ soc = of_find_node_by_path("/soc@0");
+ if (!soc)
+ return 0;
+
+ /* This applies to i.MX8MP only, do nothing on other systems */
+ if (!of_device_is_compatible(soc, "fsl,imx8mp-soc"))
+ return 0;
+
+ hdmi_tx = of_find_node_by_path("/soc@0/bus@32c00000/hdmi@32fd8000");
+ if (!of_device_is_available(hdmi_tx))
+ return 0;
+
+ /* If endpoint exists, assume an hdmi-connector exists already */
+ endpoint = of_graph_get_endpoint_by_regs(hdmi_tx, 1, -1);
+ if (endpoint)
+ return 0;
+
+ /*
+ * Boards with an HDMI connector should describe it in a device
+ * tree node with compatible = "hdmi-connector".
+ *
+ * If you see this warning, it means such a node was not found and
+ * a fallback one is added using a device tree overlay. Please add
+ * one in your device tree, also describing the exact connector
+ * type (the added overlay assumes Type A as a fallback, but it
+ * might be wrong).
+ *
+ * This node is necessary for modern DRM, where bridge drivers do
+ * not create a connector (see the DRM_BRIDGE_ATTACH_NO_CONNECTOR
+ * flag). See https://docs.kernel.org/gpu/drm-kms-helpers.html for
+ * more info.
+ */
+ pr_warn("Please add a hdmi-connector DT node for imx8mp-hdmi-tx.\n");
+
+ dtbo_start = __dtbo_imx8mp_hdmi_tx_connector_fixup_begin;
+ dtbo_size = __dtbo_imx8mp_hdmi_tx_connector_fixup_end -
+ __dtbo_imx8mp_hdmi_tx_connector_fixup_begin;
+
+ err = of_overlay_fdt_apply(dtbo_start, dtbo_size, &ovcs_id, NULL);
+ if (err)
+ err = of_overlay_remove(&ovcs_id);
+
+ return err;
+}
+
+subsys_initcall(imx8mp_hdmi_tx_connector_fixup_init);
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.dtso b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.dtso
new file mode 100644
index 000000000000..6ba1c1854aee
--- /dev/null
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx-connector-fixup.dtso
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DTS overlay adding an hdmi-connector node to boards using the imx8mp hdmi_tx
+ *
+ * Copyright (C) 2026 GE HealthCare
+ * Author: Luca Ceresoli <luca.ceresoli@bootlin.com>
+ */
+
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ fixup-hdmi-connector {
+ compatible = "hdmi-connector";
+ label = "HDMI";
+ type = "a";
+
+ port {
+ fixup_hdmi_connector_in: endpoint {
+ remote-endpoint = <&hdmi_tx_out>;
+ };
+ };
+ };
+};
+
+&{/soc@0/bus@32c00000/hdmi@32fd8000/ports/port@1} {
+ hdmi_tx_out: endpoint {
+ remote-endpoint = <&fixup_hdmi_connector_in>;
+ };
+};
diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c
index 32fd3554e267..8e8cfd66f23b 100644
--- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c
+++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c
@@ -138,6 +138,7 @@ static int imx8mp_dw_hdmi_probe(struct platform_device *pdev)
plat_data->phy_name = "SAMSUNG HDMI TX PHY";
plat_data->priv_data = hdmi;
plat_data->phy_force_vendor = true;
+ plat_data->output_port = 1;
platform_set_drvdata(pdev, hdmi);
diff --git a/drivers/gpu/drm/bridge/of-display-mode-bridge.c b/drivers/gpu/drm/bridge/of-display-mode-bridge.c
new file mode 100644
index 000000000000..cb15713f3a79
--- /dev/null
+++ b/drivers/gpu/drm/bridge/of-display-mode-bridge.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * bridge driver for legacy DT bindings, utilizing display-timings node
+ *
+ * Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
+ */
+
+#include <linux/export.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/bridge/of-display-mode-bridge.h>
+
+#include <video/of_display_timing.h>
+#include <video/of_videomode.h>
+
+struct of_display_mode_bridge {
+ struct drm_bridge base;
+
+ struct drm_display_mode mode;
+ u32 bus_flags;
+};
+
+#define to_of_display_mode_bridge(bridge) container_of(bridge, struct of_display_mode_bridge, base)
+
+static int of_display_mode_bridge_attach(struct drm_bridge *bridge,
+ struct drm_encoder *encoder,
+ enum drm_bridge_attach_flags flags)
+{
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int of_display_mode_bridge_get_modes(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct of_display_mode_bridge *of_bridge = to_of_display_mode_bridge(bridge);
+ int ret;
+
+ ret = drm_connector_helper_get_modes_fixed(connector, &of_bridge->mode);
+ if (ret)
+ return ret;
+
+ connector->display_info.bus_flags = of_bridge->bus_flags;
+
+ return 0;
+}
+
+struct drm_bridge_funcs of_display_mode_bridge_funcs = {
+ .attach = of_display_mode_bridge_attach,
+ .get_modes = of_display_mode_bridge_get_modes,
+};
+
+struct drm_bridge *devm_drm_of_display_mode_bridge(struct device *dev,
+ struct device_node *np,
+ int type)
+{
+ struct of_display_mode_bridge *of_bridge;
+ int ret;
+
+ of_bridge = devm_drm_bridge_alloc(dev, struct of_display_mode_bridge,
+ base, &of_display_mode_bridge_funcs);
+ if (IS_ERR(of_bridge))
+ return ERR_CAST(of_bridge);
+
+ ret = of_get_drm_display_mode(np,
+ &of_bridge->mode,
+ &of_bridge->bus_flags,
+ OF_USE_NATIVE_MODE);
+ if (ret)
+ return ERR_PTR(ret);
+
+ of_bridge->mode.type |= DRM_MODE_TYPE_DRIVER;
+
+ of_bridge->base.of_node = np;
+ of_bridge->base.ops = DRM_BRIDGE_OP_MODES;
+ of_bridge->base.type = type;
+
+ ret = devm_drm_bridge_add(dev, &of_bridge->base);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &of_bridge->base;
+}
+EXPORT_SYMBOL_GPL(devm_drm_of_display_mode_bridge);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DRM bridge driver for legacy DT bindings");
diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c
index 8aa31ca3c72d..cc13c98f9be6 100644
--- a/drivers/gpu/drm/bridge/simple-bridge.c
+++ b/drivers/gpu/drm/bridge/simple-bridge.c
@@ -271,6 +271,11 @@ static const struct of_device_id simple_bridge_match[] = {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
},
}, {
+ .compatible = "mstar,tsumu88adt3-lf-1",
+ .data = &(const struct simple_bridge_info) {
+ .connector_type = DRM_MODE_CONNECTOR_HDMIA,
+ },
+ }, {
.compatible = "parade,ps185hdm",
.data = &(const struct simple_bridge_info) {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
index 45b37885d719..3f4530c117c7 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c
@@ -1970,10 +1970,6 @@ struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder,
void __iomem *res;
int ret;
- dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
- if (!dp)
- return ERR_PTR(-ENOMEM);
-
dp = devm_drm_bridge_alloc(dev, struct dw_dp, bridge, &dw_dp_bridge_funcs);
if (IS_ERR(dp))
return ERR_CAST(dp);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 0296e110ce65..f4a1ebb79716 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -6,6 +6,8 @@
* Copyright (C) 2011-2013 Freescale Semiconductor, Inc.
* Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
*/
+
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
@@ -2910,9 +2912,24 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
{
struct dw_hdmi *hdmi = bridge->driver_private;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
- return drm_bridge_attach(encoder, hdmi->bridge.next_bridge,
- bridge, flags);
+ /* DRM_BRIDGE_ATTACH_NO_CONNECTOR requires a remote-endpoint to the next bridge */
+ if (WARN_ON((flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) && !hdmi->plat_data->output_port))
+ return -EINVAL;
+
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) {
+ struct device_node *remote __free(device_node) =
+ of_graph_get_remote_node(hdmi->dev->of_node,
+ hdmi->plat_data->output_port, -1);
+ if (!remote)
+ return -ENODEV;
+
+ struct drm_bridge *next_bridge __free(drm_bridge_put) =
+ of_drm_find_and_get_bridge(remote);
+ if (!next_bridge)
+ return -EPROBE_DEFER;
+
+ return drm_bridge_attach(encoder, next_bridge, bridge, flags);
+ }
return dw_hdmi_connector_create(hdmi);
}
@@ -3303,28 +3320,6 @@ static void dw_hdmi_init_hw(struct dw_hdmi *hdmi)
* Probe/remove API, used from platforms based on the DRM bridge API.
*/
-static int dw_hdmi_parse_dt(struct dw_hdmi *hdmi)
-{
- struct device_node *remote;
-
- if (!hdmi->plat_data->output_port)
- return 0;
-
-
- remote = of_graph_get_remote_node(hdmi->dev->of_node,
- hdmi->plat_data->output_port,
- -1);
- if (!remote)
- return -ENODEV;
-
- hdmi->bridge.next_bridge = of_drm_find_and_get_bridge(remote);
- of_node_put(remote);
- if (!hdmi->bridge.next_bridge)
- return -EPROBE_DEFER;
-
- return 0;
-}
-
bool dw_hdmi_bus_fmt_is_420(struct dw_hdmi *hdmi)
{
return hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_out_bus_format);
@@ -3369,10 +3364,6 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
mutex_init(&hdmi->cec_notifier_mutex);
spin_lock_init(&hdmi->audio_lock);
- ret = dw_hdmi_parse_dt(hdmi);
- if (ret < 0)
- return ERR_PTR(ret);
-
ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
if (ddc_node) {
hdmi->ddc = of_get_i2c_adapter_by_node(ddc_node);
diff --git a/drivers/gpu/drm/bridge/waveshare-dsi.c b/drivers/gpu/drm/bridge/waveshare-dsi.c
index 32d40414adb9..ded57f298d64 100644
--- a/drivers/gpu/drm/bridge/waveshare-dsi.c
+++ b/drivers/gpu/drm/bridge/waveshare-dsi.c
@@ -177,7 +177,7 @@ static int ws_bridge_probe(struct i2c_client *i2c)
regmap_write(ws->reg_map, 0xc2, 0x01);
regmap_write(ws->reg_map, 0xac, 0x01);
- ws->bridge.type = DRM_MODE_CONNECTOR_DPI;
+ ws->bridge.type = (uintptr_t)i2c_get_match_data(i2c);
ws->bridge.of_node = dev->of_node;
devm_drm_bridge_add(dev, &ws->bridge);
@@ -185,7 +185,8 @@ static int ws_bridge_probe(struct i2c_client *i2c)
}
static const struct of_device_id ws_bridge_of_ids[] = {
- {.compatible = "waveshare,dsi2dpi",},
+ {.compatible = "waveshare,dsi2dpi", .data = (void *)DRM_MODE_CONNECTOR_DPI, },
+ {.compatible = "waveshare,dsi2lvds", .data = (void *)DRM_MODE_CONNECTOR_LVDS, },
{ }
};
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index e17bb0f1f9e0..e5013b870ba0 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -347,7 +347,7 @@ void drm_master_release(struct drm_file *file_priv)
if (!drm_is_current_master_locked(file_priv))
goto out;
- if (dev->master == file_priv->master)
+ if (dev->master && dev->master == file_priv->master)
drm_drop_master(dev, file_priv);
out:
if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index d6f512b73389..986e4c79a4e0 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -282,7 +282,7 @@ static void __drm_bridge_free(struct kref *kref)
/**
* drm_bridge_get - Acquire a bridge reference
- * @bridge: DRM bridge
+ * @bridge: DRM bridge; if NULL this function does nothing
*
* This function increments the bridge's refcount.
*
@@ -300,7 +300,7 @@ EXPORT_SYMBOL(drm_bridge_get);
/**
* drm_bridge_put - Release a bridge reference
- * @bridge: DRM bridge
+ * @bridge: DRM bridge; if NULL this function does nothing
*
* This function decrements the bridge's reference count and frees the
* object if the reference count drops to zero.
@@ -574,10 +574,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
bridge->dev = encoder->dev;
bridge->encoder = encoder;
+ mutex_lock(&encoder->bridge_chain_mutex);
if (previous)
list_add(&bridge->chain_node, &previous->chain_node);
else
list_add(&bridge->chain_node, &encoder->bridge_chain);
+ mutex_unlock(&encoder->bridge_chain_mutex);
if (bridge->funcs->attach) {
ret = bridge->funcs->attach(bridge, encoder, flags);
@@ -594,7 +596,9 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
err_reset_bridge:
bridge->dev = NULL;
bridge->encoder = NULL;
+ mutex_lock(&encoder->bridge_chain_mutex);
list_del(&bridge->chain_node);
+ mutex_unlock(&encoder->bridge_chain_mutex);
if (ret != -EPROBE_DEFER)
DRM_ERROR("failed to attach bridge %pOF to encoder %s: %d\n",
@@ -721,7 +725,7 @@ void drm_bridge_detach(struct drm_bridge *bridge)
/**
* drm_bridge_chain_mode_valid - validate the mode against all bridges in the
* encoder chain.
- * @bridge: bridge control structure
+ * @first_bridge: bridge control structure
* @info: display info against which the mode shall be validated
* @mode: desired mode to be validated
*
@@ -735,17 +739,14 @@ void drm_bridge_detach(struct drm_bridge *bridge)
* MODE_OK on success, drm_mode_status Enum error code on failure
*/
enum drm_mode_status
-drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
+drm_bridge_chain_mode_valid(struct drm_bridge *first_bridge,
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
- struct drm_encoder *encoder;
-
- if (!bridge)
+ if (!first_bridge)
return MODE_OK;
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+ drm_for_each_bridge_in_chain_from(first_bridge, bridge) {
enum drm_mode_status ret;
if (!bridge->funcs->mode_valid)
@@ -763,7 +764,7 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
/**
* drm_bridge_chain_mode_set - set proposed mode for all bridges in the
* encoder chain
- * @bridge: bridge control structure
+ * @first_bridge: bridge control structure
* @mode: desired mode to be set for the encoder chain
* @adjusted_mode: updated mode that works for this encoder chain
*
@@ -772,20 +773,16 @@ EXPORT_SYMBOL(drm_bridge_chain_mode_valid);
*
* Note: the bridge passed should be the one closest to the encoder
*/
-void drm_bridge_chain_mode_set(struct drm_bridge *bridge,
+void drm_bridge_chain_mode_set(struct drm_bridge *first_bridge,
const struct drm_display_mode *mode,
const struct drm_display_mode *adjusted_mode)
{
- struct drm_encoder *encoder;
-
- if (!bridge)
+ if (!first_bridge)
return;
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+ drm_for_each_bridge_in_chain_from(first_bridge, bridge)
if (bridge->funcs->mode_set)
bridge->funcs->mode_set(bridge, mode, adjusted_mode);
- }
}
EXPORT_SYMBOL(drm_bridge_chain_mode_set);
@@ -811,6 +808,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
return;
encoder = bridge->encoder;
+ mutex_lock(&encoder->bridge_chain_mutex);
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->funcs->atomic_disable) {
iter->funcs->atomic_disable(iter, state);
@@ -821,6 +819,7 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
if (iter == bridge)
break;
}
+ mutex_unlock(&encoder->bridge_chain_mutex);
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
@@ -868,6 +867,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
encoder = bridge->encoder;
+ mutex_lock(&encoder->bridge_chain_mutex);
list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
limit = NULL;
@@ -916,6 +916,7 @@ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge,
/* Jump all bridges that we have already post_disabled */
bridge = limit;
}
+ mutex_unlock(&encoder->bridge_chain_mutex);
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
@@ -962,6 +963,7 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
encoder = bridge->encoder;
+ mutex_lock(&encoder->bridge_chain_mutex);
list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
if (iter->pre_enable_prev_first) {
next = iter;
@@ -1004,12 +1006,13 @@ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge,
if (iter == bridge)
break;
}
+ mutex_unlock(&encoder->bridge_chain_mutex);
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
/**
* drm_atomic_bridge_chain_enable - enables all bridges in the encoder chain
- * @bridge: bridge control structure
+ * @first_bridge: bridge control structure
* @state: atomic state being committed
*
* Calls &drm_bridge_funcs.atomic_enable (falls back on
@@ -1019,22 +1022,18 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_pre_enable);
*
* Note: the bridge passed should be the one closest to the encoder
*/
-void drm_atomic_bridge_chain_enable(struct drm_bridge *bridge,
+void drm_atomic_bridge_chain_enable(struct drm_bridge *first_bridge,
struct drm_atomic_state *state)
{
- struct drm_encoder *encoder;
-
- if (!bridge)
+ if (!first_bridge)
return;
- encoder = bridge->encoder;
- list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) {
+ drm_for_each_bridge_in_chain_from(first_bridge, bridge)
if (bridge->funcs->atomic_enable) {
bridge->funcs->atomic_enable(bridge, state);
} else if (bridge->funcs->enable) {
bridge->funcs->enable(bridge);
}
- }
}
EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
@@ -1329,25 +1328,27 @@ int drm_atomic_bridge_chain_check(struct drm_bridge *bridge,
return ret;
encoder = bridge->encoder;
- list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
- int ret;
-
- /*
- * Bus flags are propagated by default. If a bridge needs to
- * tweak the input bus flags for any reason, it should happen
- * in its &drm_bridge_funcs.atomic_check() implementation such
- * that preceding bridges in the chain can propagate the new
- * bus flags.
- */
- drm_atomic_bridge_propagate_bus_flags(iter, conn,
- crtc_state->state);
-
- ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
- if (ret)
- return ret;
+ scoped_guard(mutex, &encoder->bridge_chain_mutex) {
+ list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
+ int ret;
+
+ /*
+ * Bus flags are propagated by default. If a bridge needs to
+ * tweak the input bus flags for any reason, it should happen
+ * in its &drm_bridge_funcs.atomic_check() implementation such
+ * that preceding bridges in the chain can propagate the new
+ * bus flags.
+ */
+ drm_atomic_bridge_propagate_bus_flags(iter, conn,
+ crtc_state->state);
+
+ ret = drm_atomic_bridge_check(iter, crtc_state, conn_state);
+ if (ret)
+ return ret;
- if (iter == bridge)
- break;
+ if (iter == bridge)
+ break;
+ }
}
return 0;
diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c
index 566816e3c6f0..764d12060666 100644
--- a/drivers/gpu/drm/drm_colorop.c
+++ b/drivers/gpu/drm/drm_colorop.c
@@ -516,10 +516,10 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state,
colorop_state->bypass = true;
if (colorop->curve_1d_type_property) {
- drm_object_property_get_default_value(&colorop->base,
- colorop->curve_1d_type_property,
- &val);
- colorop_state->curve_1d_type = val;
+ if (!drm_object_property_get_default_value(&colorop->base,
+ colorop->curve_1d_type_property,
+ &val))
+ colorop_state->curve_1d_type = val;
}
}
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 47dc53c4a738..3fa4d2082cd7 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2867,23 +2867,18 @@ int drm_connector_attach_max_bpc_property(struct drm_connector *connector,
EXPORT_SYMBOL(drm_connector_attach_max_bpc_property);
/**
- * drm_connector_attach_hdr_output_metadata_property - attach "HDR_OUTPUT_METADA" property
+ * drm_connector_attach_hdr_output_metadata_property - attach "HDR_OUTPUT_METADATA" property
* @connector: connector to attach the property on.
*
* This is used to allow the userspace to send HDR Metadata to the
* driver.
- *
- * Returns:
- * Zero on success, negative errno on failure.
*/
-int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector)
+void drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_property *prop = dev->mode_config.hdr_output_metadata_property;
drm_object_attach_property(&connector->base, prop, 0);
-
- return 0;
}
EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property);
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 404208bf23a6..63814af7f1c1 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -31,6 +31,7 @@
#include <linux/bitfield.h>
#include <linux/byteorder/generic.h>
#include <linux/cec.h>
+#include <linux/gcd.h>
#include <linux/export.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
@@ -778,6 +779,87 @@ static const struct minimode extra_modes[] = {
{ 2048, 1536, 60, 0 },
};
+struct cta_rid {
+ u16 hactive;
+ u16 vactive;
+ u8 hratio;
+ u8 vratio;
+};
+
+/* CTA-861-I Table 11 - Resolution Identification (RID) */
+static const struct cta_rid rids[] = {
+ [0] = { 0, 0, 0, 0 },
+ [1] = { 1280, 720, 16, 9 },
+ [2] = { 1280, 720, 64, 27 },
+ [3] = { 1680, 720, 64, 27 },
+ [4] = { 1920, 1080, 16, 9 },
+ [5] = { 1920, 1080, 64, 27 },
+ [6] = { 2560, 1080, 64, 27 },
+ [7] = { 3840, 1080, 32, 9 },
+ [8] = { 2560, 1440, 16, 9 },
+ [9] = { 3440, 1440, 64, 27 },
+ [10] = { 5120, 1440, 32, 9 },
+ [11] = { 3840, 2160, 16, 9 },
+ [12] = { 3840, 2160, 64, 27 },
+ [13] = { 5120, 2160, 64, 27 },
+ [14] = { 7680, 2160, 32, 9 },
+ [15] = { 5120, 2880, 16, 9 },
+ [16] = { 5120, 2880, 64, 27 },
+ [17] = { 6880, 2880, 64, 27 },
+ [18] = { 10240, 2880, 32, 9 },
+ [19] = { 7680, 4320, 16, 9 },
+ [20] = { 7680, 4320, 64, 27 },
+ [21] = { 10240, 4320, 64, 27 },
+ [22] = { 15360, 4320, 32, 9 },
+ [23] = { 11520, 6480, 16, 9 },
+ [24] = { 11520, 6480, 64, 27 },
+ [25] = { 15360, 6480, 64, 27 },
+ [26] = { 15360, 8640, 16, 9 },
+ [27] = { 15360, 8640, 64, 27 },
+ [28] = { 20480, 8640, 64, 27 },
+};
+
+/* CTA-861-I Table 12 - AVI InfoFrame Video Format Frame Rate */
+static const u16 video_format_frame_rates[] = {
+ /* Frame Rate 0-7 */
+ 0, 24, 25, 30, 48, 50, 60, 100,
+ /* Frame Rate 8-15 */
+ 120, 144, 200, 240, 300, 360, 400, 480,
+};
+
+/* CTA-861-I Table 13 - RID To VIC Mapping */
+static const u8 rid_to_vic[][8] = {
+ [0] = {},
+ [1] = { 60, 61, 62, 108, 19, 4, 41, 47 },
+ [2] = { 65, 66, 67, 109, 68, 69, 70, 71 },
+ [3] = { 79, 80, 81, 110, 82, 83, 84, 85 },
+ [4] = { 32, 33, 34, 111, 31, 16, 64, 63 },
+ [5] = { 72, 73, 74, 112, 75, 76, 77, 78 },
+ [6] = { 86, 87, 88, 113, 89, 90, 91, 92 },
+ [7] = {},
+ [8] = {},
+ [9] = {},
+ [10] = {},
+ [11] = { 93, 94, 95, 114, 96, 97, 117, 118 },
+ [12] = { 103, 104, 105, 116, 106, 107, 119, 120 },
+ [13] = { 121, 122, 123, 124, 125, 126, 127, 193 },
+ [14] = {},
+ [15] = {},
+ [16] = {},
+ [17] = {},
+ [18] = {},
+ [19] = { 194, 195, 196, 197, 198, 199, 200, 201 },
+ [20] = { 202, 203, 204, 205, 206, 207, 208, 209 },
+ [21] = { 210, 211, 212, 213, 214, 215, 216, 217 },
+ [22] = {},
+ [23] = {},
+ [24] = {},
+ [25] = {},
+ [26] = {},
+ [27] = {},
+ [28] = {},
+};
+
/*
* From CEA/CTA-861 spec.
*
@@ -4178,6 +4260,7 @@ static int add_detailed_modes(struct drm_connector *connector,
#define CTA_DB_VIDEO 2
#define CTA_DB_VENDOR 3
#define CTA_DB_SPEAKER 4
+#define CTA_DB_VIDEO_FORMAT 6
#define CTA_DB_EXTENDED_TAG 7
/* CTA-861-H Table 62 - CTA Extended Tag Codes */
@@ -5019,6 +5102,16 @@ struct cea_db {
u8 data[];
} __packed;
+struct cta_vfd {
+ u8 rid;
+ u8 fr_fact;
+ bool bfr50;
+ bool fr24;
+ bool bfr60;
+ bool fr144;
+ bool fr48;
+};
+
static int cea_db_tag(const struct cea_db *db)
{
return db->tag_length >> 5;
@@ -5304,6 +5397,376 @@ static int edid_hfeeodb_extension_block_count(const struct edid *edid)
return cta[4 + 2];
}
+/* CTA-861 Video Format Descriptor (CTA VFD) */
+static void parse_cta_vfd(struct cta_vfd *vfd, const u8 *data, int vfd_len)
+{
+ vfd->rid = data[0] & 0x3f;
+ vfd->bfr50 = data[0] & 0x80;
+ vfd->fr24 = data[0] & 0x40;
+ vfd->bfr60 = vfd_len > 1 ? (data[1] & 0x80) : true;
+ vfd->fr144 = vfd_len > 1 ? (data[1] & 0x40) : false;
+ vfd->fr_fact = vfd_len > 1 ? (data[1] & 0x3f) : 0x3;
+ vfd->fr48 = vfd_len > 2 ? (data[2] & 0x1) : false;
+}
+
+static bool vfd_has_fr(const struct cta_vfd *vfd, int rate)
+{
+ static const u8 factors[] = {
+ 1, 2, 4, 8, 12, 16
+ };
+ int factor = 0;
+ int i;
+
+ switch (rate) {
+ case 24:
+ return vfd->fr24;
+ case 48:
+ return vfd->fr48;
+ case 144:
+ return vfd->fr144;
+ }
+
+ if (!(rate % 25)) {
+ if (!vfd->bfr50)
+ return false;
+
+ factor = rate / 25;
+ } else if (!(rate % 30)) {
+ if (!vfd->bfr60)
+ return false;
+
+ factor = rate / 30;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(factors); i++)
+ if (factor == factors[i] && (vfd->fr_fact & (1 << i)))
+ return true;
+
+ return false;
+}
+
+#define OVT_PIXEL_CLOCK_GRANULARITY 1000 /* Hz */
+#define OVT_MIN_HTOTAL_GRANULARITY 8 /* pixels */
+#define OVT_MIN_VBLANK_DURATION 460000000 /* ps */
+#define OVT_MIN_VBLANK_LINES 20
+#define OVT_MIN_VSYNC_LEADING_EDGE 400 /* us */
+#define OVT_MIN_VSYNC_LE_LINES 14
+#define OVT_MIN_CLOCK_RATE_420 590000000 /* Hz */
+#define OVT_PIXEL_FACTOR_420 2
+#define OVT_MIN_HBLANK_444 80 /* pixels */
+#define OVT_MIN_HBLANK_420 128 /* pixels */
+#define OVT_MAX_CHUNK_RATE 650000000 /* Hz */
+#define OVT_AUDIO_PACKET_RATE 195000 /* Hz */
+#define OVT_AUDIO_PACKET_SIZE 32
+#define OVT_LINE_OVERHEAD 32
+#define OVT_HSYNC_WIDTH 32
+#define OVT_VSYNC_WIDTH 8
+
+static u32 calculate_ovt_min_vtotal(const struct cta_rid *rid, u64 max_vrate,
+ u32 vtotal_granularity)
+{
+ u64 max_active_time;
+ u32 min_line_time;
+ u32 min_vblank;
+ u32 min_vtotal;
+
+ /* step 2 */
+ max_active_time = div64_u64(1000000000000, max_vrate) -
+ (u64)OVT_MIN_VBLANK_DURATION;
+
+ min_line_time = div_u64(max_active_time, rid->vactive);
+
+ min_vblank = max_t(u64, (u64)OVT_MIN_VBLANK_LINES,
+ DIV64_U64_ROUND_UP(OVT_MIN_VBLANK_DURATION,
+ min_line_time));
+
+ min_vtotal = rid->vactive + min_vblank;
+
+ if (min_vtotal % vtotal_granularity)
+ min_vtotal += vtotal_granularity - (min_vtotal %
+ vtotal_granularity);
+
+ return min_vtotal;
+}
+
+static u32 calculate_ovt_min_htotal(const struct cta_rid *rid,
+ const u32 max_vrate,
+ const u32 min_vtotal,
+ u32 *min_hblank,
+ u32 *htotal_granularity)
+{
+ u32 max_audio_packets_per_line;
+ u32 htotal_granularity_chunk;
+ u64 min_pixel_clock_rate;
+ u32 min_line_rate;
+ u32 min_htotal;
+
+ /* step 3 */
+ min_line_rate = max_vrate * min_vtotal;
+
+ max_audio_packets_per_line = DIV_ROUND_UP(OVT_AUDIO_PACKET_RATE,
+ min_line_rate);
+
+ /* step 4 */
+ *min_hblank = OVT_LINE_OVERHEAD + OVT_AUDIO_PACKET_SIZE *
+ max_audio_packets_per_line;
+
+ min_htotal = rid->hactive + max(OVT_MIN_HBLANK_444, *min_hblank);
+
+ min_pixel_clock_rate = max_vrate * min_htotal * min_vtotal;
+
+ htotal_granularity_chunk =
+ roundup_pow_of_two(DIV64_U64_ROUND_UP(min_pixel_clock_rate,
+ OVT_MAX_CHUNK_RATE));
+
+ *htotal_granularity = max(OVT_MIN_HTOTAL_GRANULARITY,
+ htotal_granularity_chunk);
+
+ if (min_htotal % *htotal_granularity)
+ min_htotal += *htotal_granularity - (min_htotal %
+ *htotal_granularity);
+
+ return min_htotal;
+}
+
+static u64 calculate_ovt_pixel_clock_rate(const struct cta_rid *rid,
+ const u32 max_vrate,
+ const u32 min_hblank,
+ u32 min_htotal,
+ u32 min_vtotal,
+ const u32 htotal_granularity,
+ const u32 vtotal_granularity,
+ u32 *htotal, u32 *vtotal)
+{
+ u32 resolution_granularity;
+ u64 pixel_clock_rate;
+ u64 min_resolution;
+ u64 rem;
+ u32 h;
+ u64 r;
+ u32 v;
+
+ resolution_granularity = OVT_PIXEL_CLOCK_GRANULARITY /
+ gcd(OVT_PIXEL_CLOCK_GRANULARITY, max_vrate);
+
+ do {
+ /* step 5 */
+ min_resolution = 0;
+ v = min_vtotal;
+
+ goto loop_end;
+
+ while (!min_resolution || r <= min_resolution) {
+ goto inner_loop_end;
+
+ while (rem || div64_u64(max_vrate * r, (h & ~(h - 1))) >
+ OVT_MAX_CHUNK_RATE) {
+ h += htotal_granularity;
+ r = (u64)h * (u64)v;
+inner_loop_end:
+ div64_u64_rem(r, resolution_granularity, &rem);
+ }
+
+ if (!min_resolution || r < min_resolution) {
+ *htotal = h;
+ *vtotal = v;
+ min_resolution = r;
+ }
+
+ v += vtotal_granularity;
+
+loop_end:
+ h = min_htotal;
+ r = (u64)h * (u64)v;
+ }
+
+ pixel_clock_rate = max_vrate * min_resolution;
+
+ /* step 6 */
+ min_htotal = rid->hactive + max(OVT_MIN_HBLANK_420,
+ OVT_PIXEL_FACTOR_420 *
+ min_hblank);
+
+ } while (pixel_clock_rate >= OVT_MIN_CLOCK_RATE_420 &&
+ *htotal < min_htotal);
+
+ return pixel_clock_rate;
+}
+
+static const struct cta_rid *find_rid(u8 rid)
+{
+ if (!rid || rid >= ARRAY_SIZE(rids))
+ return NULL;
+
+ return &rids[rid];
+}
+
+/* OVT Algorthim as specified in CTA-861-I */
+struct drm_display_mode *drm_ovt_mode(struct drm_device *dev, int r_id,
+ int vrefresh)
+{
+ const struct cta_rid *rid = find_rid(r_id);
+ struct drm_display_mode *mode;
+ u32 vtotal_granularity = 1;
+ u32 htotal_granularity;
+ u32 max_vrate = vrefresh;
+ u64 pixel_clock_rate;
+ u32 vsync_position;
+ u32 min_hblank;
+ u32 min_htotal;
+ u32 min_vtotal;
+ u32 htotal;
+ u32 vtotal;
+
+ if (!rid)
+ return NULL;
+
+ /* step 1 */
+ switch (vrefresh) {
+ case 24:
+ case 25:
+ max_vrate = 30;
+ fallthrough;
+ case 30:
+ vtotal_granularity = 20;
+ break;
+ case 48:
+ case 50:
+ max_vrate = 60;
+ fallthrough;
+ case 60:
+ vtotal_granularity = 20;
+ break;
+ case 100:
+ max_vrate = 120;
+ fallthrough;
+ case 120:
+ vtotal_granularity = 5;
+ break;
+ case 200:
+ max_vrate = 240;
+ fallthrough;
+ case 240:
+ vtotal_granularity = 5;
+ break;
+ case 300:
+ max_vrate = 360;
+ fallthrough;
+ case 360:
+ vtotal_granularity = 5;
+ break;
+ case 400:
+ max_vrate = 480;
+ fallthrough;
+ case 480:
+ vtotal_granularity = 5;
+ break;
+ }
+
+ min_vtotal = calculate_ovt_min_vtotal(rid, max_vrate,
+ vtotal_granularity);
+
+ min_htotal = calculate_ovt_min_htotal(rid, max_vrate, min_vtotal,
+ &min_hblank, &htotal_granularity);
+
+ pixel_clock_rate = calculate_ovt_pixel_clock_rate(rid, max_vrate,
+ min_hblank,
+ min_htotal,
+ min_vtotal,
+ htotal_granularity,
+ vtotal_granularity,
+ &htotal, &vtotal);
+
+ /* step 7 */
+ vtotal = vtotal * max_vrate / (u32)vrefresh;
+
+ /* step 8 */
+ vsync_position = max(OVT_MIN_VSYNC_LE_LINES,
+ DIV64_U64_ROUND_UP((u64)OVT_MIN_VSYNC_LE_LINES *
+ pixel_clock_rate,
+ (u64)htotal * (u64)1000000));
+
+ mode = drm_mode_create(dev);
+
+ if (!mode)
+ return NULL;
+
+ /* step 10 */
+ mode->clock = div_u64(pixel_clock_rate, 1000);
+ mode->hdisplay = rid->hactive;
+ mode->hsync_start = htotal - OVT_HSYNC_WIDTH * 2;
+ mode->hsync_end = mode->hsync_start + OVT_HSYNC_WIDTH;
+ mode->htotal = htotal;
+
+ mode->vdisplay = rid->vactive;
+ mode->vsync_start = vtotal - vsync_position;
+ mode->vsync_end = mode->vsync_start + OVT_VSYNC_WIDTH;
+ mode->vtotal = vtotal;
+
+ return mode;
+}
+
+static u8 find_vic(u8 rid, int rate_idx)
+{
+ if (video_format_frame_rates[rate_idx] > 120 || !find_rid(rid))
+ return 0;
+
+ return rid_to_vic[rid][rate_idx - 1];
+}
+
+/* CTA-861 Video Format Data Block (CTA VFDB) */
+static int add_modes_from_vfdb(struct drm_connector *connector,
+ const struct cea_db *db)
+{
+ const struct drm_display_info *info = &connector->display_info;
+ int vfdb_len = cea_db_payload_len(db);
+ struct drm_display_mode *mode;
+ struct cta_vfd vfd;
+ int num_modes = 0;
+ int rate_idx;
+ int vfd_len;
+ int rate;
+ int i;
+
+ if (!vfdb_len)
+ return 0;
+
+ vfd_len = (db->data[0] & 0x3);
+
+ if (!vfd_len)
+ return 0;
+
+ vfd_len++;
+ vfdb_len--;
+ vfdb_len -= (vfdb_len % vfd_len);
+
+ for (i = 0; i < vfdb_len; i += vfd_len) {
+ parse_cta_vfd(&vfd, &db->data[i + 1], vfd_len);
+
+ for (rate_idx = 1; rate_idx <
+ ARRAY_SIZE(video_format_frame_rates); rate_idx++) {
+ rate = video_format_frame_rates[rate_idx];
+
+ if (!vfd_has_fr(&vfd, rate) || find_vic(vfd.rid,
+ rate_idx))
+ continue;
+
+ mode = drm_ovt_mode(connector->dev, vfd.rid, rate);
+
+ if (!mode)
+ continue;
+
+ mode->height_mm = info->height_mm;
+ mode->width_mm = info->width_mm;
+
+ drm_mode_probed_add(connector, mode);
+ num_modes++;
+ }
+ }
+
+ return num_modes;
+}
+
/*
* CTA-861 YCbCr 4:2:0 Capability Map Data Block (CTA Y420CMDB)
*
@@ -5372,6 +5835,8 @@ static int add_cea_modes(struct drm_connector *connector,
/* Add 4:2:0(only) modes present in EDID */
modes += do_y420vdb_modes(connector, vdb420,
cea_db_payload_len(db) - 1);
+ } else if (cea_db_tag(db) == CTA_DB_VIDEO_FORMAT) {
+ modes += add_modes_from_vfdb(connector, db);
}
}
cea_db_iter_end(&iter);
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index 8f2bc6a28482..0d5dbed06db4 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -129,6 +129,7 @@ static int __drm_encoder_init(struct drm_device *dev,
}
INIT_LIST_HEAD(&encoder->bridge_chain);
+ mutex_init(&encoder->bridge_chain_mutex);
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
encoder->index = dev->mode_config.num_encoder++;
@@ -188,20 +189,33 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_bridge *bridge, *next;
+ LIST_HEAD(tmplist);
/* Note that the encoder_list is considered to be static; should we
* remove the drm_encoder at runtime we would have to decrement all
* the indices on the drm_encoder after us in the encoder_list.
*/
- list_for_each_entry_safe(bridge, next, &encoder->bridge_chain,
- chain_node)
+ /*
+ * We need the bridge_chain_mutex to modify the chain, but
+ * drm_bridge_detach() will call DRM_MODESET_LOCK_ALL_BEGIN() (in
+ * drm_modeset_lock_fini()), resulting in a possible ABBA circular
+ * deadlock. Avoid it by first moving all the bridges to a
+ * temporary list holding the lock, and then calling
+ * drm_bridge_detach() without the lock.
+ */
+ mutex_lock(&encoder->bridge_chain_mutex);
+ list_cut_before(&tmplist, &encoder->bridge_chain, &encoder->bridge_chain);
+ mutex_unlock(&encoder->bridge_chain_mutex);
+
+ list_for_each_entry_safe(bridge, next, &tmplist, chain_node)
drm_bridge_detach(bridge);
drm_mode_object_unregister(dev, &encoder->base);
kfree(encoder->name);
list_del(&encoder->head);
dev->mode_config.num_encoder--;
+ mutex_destroy(&encoder->bridge_chain_mutex);
memset(encoder, 0, sizeof(*encoder));
}
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index d6424267260b..fca42949eb2b 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -1700,6 +1700,16 @@ drm_gem_lru_scan(struct drm_gem_lru *lru,
*/
WARN_ON(obj->lru == &still_in_lru);
WARN_ON(obj->lru == lru);
+ } else if (obj->lru == &still_in_lru) {
+ /*
+ * If the object wasn't moved and wasn't shrunk either,
+ * it's still remaining as reclaimable. Note that
+ * obj->lru is supposed to be checked with the LRU lock
+ * held for an accurate result, but we don't care about
+ * accuracy here. Worst thing that could happen is an
+ * extra scan.
+ */
+ *remaining += obj->size >> PAGE_SHIFT;
}
dma_resv_unlock(obj->resv);
diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c
index 4b928fda5b12..8526d6d3c828 100644
--- a/drivers/gpu/drm/drm_gpusvm.c
+++ b/drivers/gpu/drm/drm_gpusvm.c
@@ -1139,11 +1139,17 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm,
struct drm_gpusvm_pages_flags flags = {
.__flags = svm_pages->flags.__flags,
};
+ bool use_iova = dma_use_iova(&svm_pages->state);
+
+ if (use_iova)
+ dma_iova_destroy(dev, &svm_pages->state,
+ svm_pages->state_offset,
+ svm_pages->dma_addr[0].dir, 0);
for (i = 0, j = 0; i < npages; j++) {
struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j];
- if (addr->proto == DRM_INTERCONNECT_SYSTEM)
+ if (!use_iova && addr->proto == DRM_INTERCONNECT_SYSTEM)
dma_unmap_page(dev,
addr->addr,
PAGE_SIZE << addr->order,
@@ -1408,6 +1414,7 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
struct drm_gpusvm_pages_flags flags;
enum dma_data_direction dma_dir = ctx->read_only ? DMA_TO_DEVICE :
DMA_BIDIRECTIONAL;
+ struct dma_iova_state *state = &svm_pages->state;
retry:
if (time_after(jiffies, timeout))
@@ -1446,6 +1453,9 @@ retry:
if (err)
goto err_free;
+ *state = (struct dma_iova_state){};
+ svm_pages->state_offset = 0;
+
map_pages:
/*
* Perform all dma mappings under the notifier lock to not
@@ -1539,13 +1549,33 @@ map_pages:
goto err_unmap;
}
- addr = dma_map_page(gpusvm->drm->dev,
- page, 0,
- PAGE_SIZE << order,
- dma_dir);
- if (dma_mapping_error(gpusvm->drm->dev, addr)) {
- err = -EFAULT;
- goto err_unmap;
+ if (!i)
+ dma_iova_try_alloc(gpusvm->drm->dev, state,
+ npages * PAGE_SIZE >=
+ HPAGE_PMD_SIZE ?
+ HPAGE_PMD_SIZE : 0,
+ npages * PAGE_SIZE);
+
+ if (dma_use_iova(state)) {
+ err = dma_iova_link(gpusvm->drm->dev, state,
+ hmm_pfn_to_phys(pfns[i]),
+ svm_pages->state_offset,
+ PAGE_SIZE << order,
+ dma_dir, 0);
+ if (err)
+ goto err_unmap;
+
+ addr = state->addr + svm_pages->state_offset;
+ svm_pages->state_offset += PAGE_SIZE << order;
+ } else {
+ addr = dma_map_page(gpusvm->drm->dev,
+ page, 0,
+ PAGE_SIZE << order,
+ dma_dir);
+ if (dma_mapping_error(gpusvm->drm->dev, addr)) {
+ err = -EFAULT;
+ goto err_unmap;
+ }
}
svm_pages->dma_addr[j] = drm_pagemap_addr_encode
@@ -1557,6 +1587,13 @@ map_pages:
flags.has_dma_mapping = true;
}
+ if (dma_use_iova(state)) {
+ err = dma_iova_sync(gpusvm->drm->dev, state, 0,
+ svm_pages->state_offset);
+ if (err)
+ goto err_unmap;
+ }
+
if (pagemap) {
flags.has_devmem_pages = true;
drm_pagemap_get(dpagemap);
diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c
index 44acfe4120d2..f56719e9f435 100644
--- a/drivers/gpu/drm/drm_gpuvm.c
+++ b/drivers/gpu/drm/drm_gpuvm.c
@@ -1322,6 +1322,9 @@ drm_gpuvm_prepare_range(struct drm_gpuvm *gpuvm, struct drm_exec *exec,
drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) {
struct drm_gem_object *obj = va->gem.obj;
+ if (unlikely(!obj))
+ continue;
+
ret = exec_prepare_obj(exec, obj, num_fences);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 0390e14d3157..3ac1dd5ad640 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -587,6 +587,9 @@ EXPORT_SYMBOL(mipi_dsi_create_packet);
* mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
* @dsi: DSI peripheral device
*
+ * This function is deprecated. Use mipi_dsi_shutdown_peripheral_multi()
+ * instead.
+ *
* Return: 0 on success or a negative error code on failure.
*/
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
@@ -1980,6 +1983,31 @@ void mipi_dsi_dcs_set_tear_scanline_multi(struct mipi_dsi_multi_context *ctx,
}
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline_multi);
+/**
+ * mipi_dsi_shutdown_peripheral_multi() - sends a Shutdown Peripheral command
+ * @ctx: Context for multiple DSI transactions
+ *
+ * Like mipi_dsi_shutdown_peripheral() but deals with errors in a way that
+ * makes it convenient to make several calls in a row.
+ */
+void mipi_dsi_shutdown_peripheral_multi(struct mipi_dsi_multi_context *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ if (ctx->accum_err)
+ return;
+
+ ret = mipi_dsi_shutdown_peripheral(dsi);
+ if (ret < 0) {
+ ctx->accum_err = ret;
+ dev_err(dev, "Failed to shutdown peripheral: %d\n",
+ ctx->accum_err);
+ }
+}
+EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral_multi);
+
static int mipi_dsi_drv_probe(struct device *dev)
{
struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index 4f65ce729a47..ef6b09316963 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -558,6 +558,40 @@ int drm_of_get_data_lanes_count_ep(const struct device_node *port,
}
EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count_ep);
+/**
+ * drm_of_get_data_lanes_count_remote - Get DSI/(e)DP data lane count by endpoint
+ * @port: DT port node of the DSI/(e)DP source or sink
+ * @port_reg: identifier (value of reg property) of the parent port node
+ * @reg: identifier (value of reg property) of the endpoint node
+ * @min: minimum supported number of data lanes
+ * @max: maximum supported number of data lanes
+ *
+ * Count DT "data-lanes" property elements in the remote endpoint and check for
+ * validity. This variant uses endpoint specifier.
+ *
+ * Return:
+ * * min..max - positive integer count of "data-lanes" elements
+ * * -EINVAL - the "data-lanes" property is unsupported
+ * * -ENODEV - the "data-lanes" property is missing
+ */
+int drm_of_get_data_lanes_count_remote(const struct device_node *port,
+ int port_reg, int reg,
+ const unsigned int min,
+ const unsigned int max)
+{
+ struct device_node *endpoint, *remote;
+ int ret;
+
+ endpoint = of_graph_get_endpoint_by_regs(port, port_reg, reg);
+ remote = of_graph_get_remote_endpoint(endpoint);
+ of_node_put(endpoint);
+ ret = drm_of_get_data_lanes_count(remote, min, max);
+ of_node_put(remote);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(drm_of_get_data_lanes_count_remote);
+
#if IS_ENABLED(CONFIG_DRM_MIPI_DSI)
/**
diff --git a/drivers/gpu/drm/drm_pagemap.c b/drivers/gpu/drm/drm_pagemap.c
index 5002049e0198..d82ea7ccb8da 100644
--- a/drivers/gpu/drm/drm_pagemap.c
+++ b/drivers/gpu/drm/drm_pagemap.c
@@ -216,7 +216,8 @@ static void drm_pagemap_get_devmem_page(struct page *page,
}
/**
- * drm_pagemap_migrate_map_pages() - Map migration pages for GPU SVM migration
+ * drm_pagemap_migrate_map_device_private_pages() - Map device private migration
+ * pages for GPU SVM migration
* @dev: The device performing the migration.
* @local_dpagemap: The drm_pagemap local to the migrating device.
* @pagemap_addr: Array to store DMA information corresponding to mapped pages.
@@ -232,58 +233,50 @@ static void drm_pagemap_get_devmem_page(struct page *page,
*
* Returns: 0 on success, -EFAULT if an error occurs during mapping.
*/
-static int drm_pagemap_migrate_map_pages(struct device *dev,
- struct drm_pagemap *local_dpagemap,
- struct drm_pagemap_addr *pagemap_addr,
- unsigned long *migrate_pfn,
- unsigned long npages,
- enum dma_data_direction dir,
- const struct drm_pagemap_migrate_details *mdetails)
+static int
+drm_pagemap_migrate_map_device_private_pages(struct device *dev,
+ struct drm_pagemap *local_dpagemap,
+ struct drm_pagemap_addr *pagemap_addr,
+ unsigned long *migrate_pfn,
+ unsigned long npages,
+ enum dma_data_direction dir,
+ const struct drm_pagemap_migrate_details *mdetails)
{
unsigned long num_peer_pages = 0, num_local_pages = 0, i;
for (i = 0; i < npages;) {
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
- dma_addr_t dma_addr;
+ struct drm_pagemap_zdd *zdd;
+ struct drm_pagemap *dpagemap;
+ struct drm_pagemap_addr addr;
struct folio *folio;
unsigned int order = 0;
if (!page)
goto next;
+ WARN_ON_ONCE(!is_device_private_page(page));
folio = page_folio(page);
order = folio_order(folio);
- if (is_device_private_page(page)) {
- struct drm_pagemap_zdd *zdd = drm_pagemap_page_zone_device_data(page);
- struct drm_pagemap *dpagemap = zdd->dpagemap;
- struct drm_pagemap_addr addr;
-
- if (dpagemap == local_dpagemap) {
- if (!mdetails->can_migrate_same_pagemap)
- goto next;
+ zdd = drm_pagemap_page_zone_device_data(page);
+ dpagemap = zdd->dpagemap;
- num_local_pages += NR_PAGES(order);
- } else {
- num_peer_pages += NR_PAGES(order);
- }
-
- addr = dpagemap->ops->device_map(dpagemap, dev, page, order, dir);
- if (dma_mapping_error(dev, addr.addr))
- return -EFAULT;
+ if (dpagemap == local_dpagemap) {
+ if (!mdetails->can_migrate_same_pagemap)
+ goto next;
- pagemap_addr[i] = addr;
+ num_local_pages += NR_PAGES(order);
} else {
- dma_addr = dma_map_page(dev, page, 0, page_size(page), dir);
- if (dma_mapping_error(dev, dma_addr))
- return -EFAULT;
-
- pagemap_addr[i] =
- drm_pagemap_addr_encode(dma_addr,
- DRM_INTERCONNECT_SYSTEM,
- order, dir);
+ num_peer_pages += NR_PAGES(order);
}
+ addr = dpagemap->ops->device_map(dpagemap, dev, page, order, dir);
+ if (dma_mapping_error(dev, addr.addr))
+ return -EFAULT;
+
+ pagemap_addr[i] = addr;
+
next:
i += NR_PAGES(order);
}
@@ -299,6 +292,101 @@ next:
}
/**
+ * struct drm_pagemap_iova_state - DRM pagemap IOVA state
+ * @dma_state: DMA IOVA state.
+ * @offset: Current offset in IOVA.
+ *
+ * This structure acts as an iterator for packing all IOVA addresses within a
+ * contiguous range.
+ */
+struct drm_pagemap_iova_state {
+ struct dma_iova_state dma_state;
+ unsigned long offset;
+};
+
+/**
+ * drm_pagemap_migrate_map_system_pages() - Map system or device coherent
+ * migration pages for GPU SVM migration
+ * @dev: The device performing the migration.
+ * @pagemap_addr: Array to store DMA information corresponding to mapped pages.
+ * @migrate_pfn: Array of page frame numbers of system pages or peer pages to map.
+ * @npages: Number of system or device coherent pages to map.
+ * @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
+ * @state: DMA IOVA state for mapping.
+ *
+ * This function maps pages of memory for migration usage in GPU SVM. It
+ * iterates over each page frame number provided in @migrate_pfn, maps the
+ * corresponding page, and stores the DMA address in the provided @dma_addr
+ * array.
+ *
+ * Returns: 0 on success, negative error code on failure.
+ */
+static int
+drm_pagemap_migrate_map_system_pages(struct device *dev,
+ struct drm_pagemap_addr *pagemap_addr,
+ unsigned long *migrate_pfn,
+ unsigned long npages,
+ enum dma_data_direction dir,
+ struct drm_pagemap_iova_state *state)
+{
+ unsigned long i;
+ bool try_alloc = false;
+
+ for (i = 0; i < npages;) {
+ struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
+ dma_addr_t dma_addr;
+ struct folio *folio;
+ unsigned int order = 0;
+
+ if (!page)
+ goto next;
+
+ WARN_ON_ONCE(is_device_private_page(page));
+ folio = page_folio(page);
+ order = folio_order(folio);
+
+ if (!try_alloc) {
+ dma_iova_try_alloc(dev, &state->dma_state,
+ (npages - i) * PAGE_SIZE >=
+ HPAGE_PMD_SIZE ?
+ HPAGE_PMD_SIZE : 0,
+ npages * PAGE_SIZE);
+ try_alloc = true;
+ }
+
+ if (dma_use_iova(&state->dma_state)) {
+ int err = dma_iova_link(dev, &state->dma_state,
+ page_to_phys(page),
+ state->offset, page_size(page),
+ dir, 0);
+ if (err)
+ return err;
+
+ dma_addr = state->dma_state.addr + state->offset;
+ state->offset += page_size(page);
+ } else {
+ dma_addr = dma_map_page(dev, page, 0, page_size(page),
+ dir);
+ if (dma_mapping_error(dev, dma_addr))
+ return -EFAULT;
+ }
+
+ pagemap_addr[i] =
+ drm_pagemap_addr_encode(dma_addr,
+ DRM_INTERCONNECT_SYSTEM,
+ order, dir);
+
+next:
+ i += NR_PAGES(order);
+ }
+
+ if (dma_use_iova(&state->dma_state))
+ return dma_iova_sync(dev, &state->dma_state, 0, state->offset);
+
+ return 0;
+}
+
+/**
* drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
* @dev: The device for which the pages were mapped
* @migrate_pfn: Array of migrate pfns set up for the mapped pages. Used to
@@ -306,19 +394,27 @@ next:
* @pagemap_addr: Array of DMA information corresponding to mapped pages
* @npages: Number of pages to unmap
* @dir: Direction of data transfer (e.g., DMA_BIDIRECTIONAL)
+ * @state: DMA IOVA state for mapping.
*
* This function unmaps previously mapped pages of memory for GPU Shared Virtual
- * Memory (SVM). It iterates over each DMA address provided in @dma_addr, checks
- * if it's valid and not already unmapped, and unmaps the corresponding page.
+ * Memory (SVM). It iterates over each DMA address provided in @pagemap_addr,
+ * checks if it's valid and not already unmapped, and unmaps the corresponding
+ * page.
*/
static void drm_pagemap_migrate_unmap_pages(struct device *dev,
struct drm_pagemap_addr *pagemap_addr,
unsigned long *migrate_pfn,
unsigned long npages,
- enum dma_data_direction dir)
+ enum dma_data_direction dir,
+ struct drm_pagemap_iova_state *state)
{
unsigned long i;
+ if (state && dma_use_iova(&state->dma_state)) {
+ dma_iova_destroy(dev, &state->dma_state, state->offset, dir, 0);
+ return;
+ }
+
for (i = 0; i < npages;) {
struct page *page = migrate_pfn_to_page(migrate_pfn[i]);
@@ -358,9 +454,13 @@ drm_pagemap_migrate_remote_to_local(struct drm_pagemap_devmem *devmem,
const struct drm_pagemap_migrate_details *mdetails)
{
- int err = drm_pagemap_migrate_map_pages(remote_device, remote_dpagemap,
- pagemap_addr, local_pfns,
- npages, DMA_FROM_DEVICE, mdetails);
+ int err = drm_pagemap_migrate_map_device_private_pages(remote_device,
+ remote_dpagemap,
+ pagemap_addr,
+ local_pfns,
+ npages,
+ DMA_FROM_DEVICE,
+ mdetails);
if (err)
goto out;
@@ -369,7 +469,7 @@ drm_pagemap_migrate_remote_to_local(struct drm_pagemap_devmem *devmem,
devmem->pre_migrate_fence);
out:
drm_pagemap_migrate_unmap_pages(remote_device, pagemap_addr, local_pfns,
- npages, DMA_FROM_DEVICE);
+ npages, DMA_FROM_DEVICE, NULL);
return err;
}
@@ -380,11 +480,12 @@ drm_pagemap_migrate_sys_to_dev(struct drm_pagemap_devmem *devmem,
struct drm_pagemap_addr pagemap_addr[],
unsigned long npages,
const struct drm_pagemap_devmem_ops *ops,
- const struct drm_pagemap_migrate_details *mdetails)
+ struct drm_pagemap_iova_state *state)
{
- int err = drm_pagemap_migrate_map_pages(devmem->dev, devmem->dpagemap,
- pagemap_addr, sys_pfns, npages,
- DMA_TO_DEVICE, mdetails);
+ int err = drm_pagemap_migrate_map_system_pages(devmem->dev,
+ pagemap_addr, sys_pfns,
+ npages, DMA_TO_DEVICE,
+ state);
if (err)
goto out;
@@ -393,7 +494,7 @@ drm_pagemap_migrate_sys_to_dev(struct drm_pagemap_devmem *devmem,
devmem->pre_migrate_fence);
out:
drm_pagemap_migrate_unmap_pages(devmem->dev, pagemap_addr, sys_pfns, npages,
- DMA_TO_DEVICE);
+ DMA_TO_DEVICE, state);
return err;
}
@@ -421,6 +522,7 @@ static int drm_pagemap_migrate_range(struct drm_pagemap_devmem *devmem,
const struct migrate_range_loc *cur,
const struct drm_pagemap_migrate_details *mdetails)
{
+ struct drm_pagemap_iova_state state = {};
int ret = 0;
if (cur->start == 0)
@@ -448,7 +550,7 @@ static int drm_pagemap_migrate_range(struct drm_pagemap_devmem *devmem,
&pages[last->start],
&pagemap_addr[last->start],
cur->start - last->start,
- last->ops, mdetails);
+ last->ops, &state);
out:
*last = *cur;
@@ -651,12 +753,10 @@ int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
own_pages++;
goto next;
}
- if (mdetails->source_peer_migrates) {
- cur.dpagemap = src_zdd->dpagemap;
- cur.ops = src_zdd->devmem_allocation->ops;
- cur.device = cur.dpagemap->drm->dev;
- pages[i] = src_page;
- }
+ cur.dpagemap = src_zdd->dpagemap;
+ cur.ops = src_zdd->devmem_allocation->ops;
+ cur.device = cur.dpagemap->drm->dev;
+ pages[i] = src_page;
}
if (!pages[i]) {
cur.dpagemap = NULL;
@@ -1012,7 +1112,7 @@ EXPORT_SYMBOL(drm_pagemap_put);
int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
{
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
- struct drm_pagemap_migrate_details mdetails = {};
+ struct drm_pagemap_iova_state state = {};
unsigned long npages, mpages = 0;
struct page **pages;
unsigned long *src, *dst;
@@ -1051,10 +1151,10 @@ retry:
if (err || !mpages)
goto err_finalize;
- err = drm_pagemap_migrate_map_pages(devmem_allocation->dev,
- devmem_allocation->dpagemap, pagemap_addr,
- dst, npages, DMA_FROM_DEVICE,
- &mdetails);
+ err = drm_pagemap_migrate_map_system_pages(devmem_allocation->dev,
+ pagemap_addr,
+ dst, npages,
+ DMA_FROM_DEVICE, &state);
if (err)
goto err_finalize;
@@ -1078,7 +1178,7 @@ err_finalize:
migrate_device_pages(src, dst, npages);
migrate_device_finalize(src, dst, npages);
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages,
- DMA_FROM_DEVICE);
+ DMA_FROM_DEVICE, &state);
err_free:
kvfree(buf);
@@ -1090,6 +1190,7 @@ err_out:
if (retry_count--) {
cond_resched();
+ state = (struct drm_pagemap_iova_state){};
goto retry;
}
@@ -1123,7 +1224,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
MIGRATE_VMA_SELECT_COMPOUND,
.fault_page = page,
};
- struct drm_pagemap_migrate_details mdetails = {};
+ struct drm_pagemap_iova_state state = {};
struct drm_pagemap_zdd *zdd;
const struct drm_pagemap_devmem_ops *ops;
struct device *dev = NULL;
@@ -1181,8 +1282,9 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
if (err)
goto err_finalize;
- err = drm_pagemap_migrate_map_pages(dev, zdd->dpagemap, pagemap_addr, migrate.dst, npages,
- DMA_FROM_DEVICE, &mdetails);
+ err = drm_pagemap_migrate_map_system_pages(dev, pagemap_addr,
+ migrate.dst, npages,
+ DMA_FROM_DEVICE, &state);
if (err)
goto err_finalize;
@@ -1207,7 +1309,8 @@ err_finalize:
migrate_vma_finalize(&migrate);
if (dev)
drm_pagemap_migrate_unmap_pages(dev, pagemap_addr, migrate.dst,
- npages, DMA_FROM_DEVICE);
+ npages, DMA_FROM_DEVICE,
+ &state);
err_free:
kvfree(buf);
err_out:
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index d1e6598ea3bc..a6029b699b73 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -101,6 +101,29 @@ void drm_panel_remove(struct drm_panel *panel)
}
EXPORT_SYMBOL(drm_panel_remove);
+static void drm_panel_add_release(void *data)
+{
+ drm_panel_remove(data);
+}
+
+/**
+ * devm_drm_panel_add - add a panel to the global registry using devres
+ * @panel: panel to add
+ *
+ * Add a panel to the global registry so that it can be looked
+ * up by display drivers. The panel to be added must have been
+ * allocated by devm_drm_panel_alloc(). Unlike drm_panel_add() with this
+ * function there is no need to call drm_panel_remove(), it will be called
+ * automatically.
+ */
+int devm_drm_panel_add(struct device *dev, struct drm_panel *panel)
+{
+ drm_panel_add(panel);
+
+ return devm_add_action_or_reset(dev, drm_panel_add_release, panel);
+}
+EXPORT_SYMBOL(devm_drm_panel_add);
+
/**
* drm_panel_prepare - power on a panel
* @panel: DRM panel
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index f78bf37f1e0a..f90fb2d13e42 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -236,6 +236,21 @@ static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe)
return drm_vblank_no_hw_counter(dev, pipe);
}
+static bool get_vblank_counter_and_timestamp(struct drm_device *dev, unsigned int pipe,
+ u32 *cur_vblank, ktime_t *t_vblank,
+ bool in_vblank_irq)
+{
+ int count = DRM_TIMESTAMP_MAXRETRIES;
+ bool rc;
+
+ do {
+ *cur_vblank = __get_vblank_counter(dev, pipe);
+ rc = drm_get_last_vbltimestamp(dev, pipe, t_vblank, in_vblank_irq);
+ } while (*cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+
+ return rc;
+}
+
/*
* Reset the stored timestamp for the current vblank count to correspond
* to the last vblank occurred.
@@ -250,7 +265,6 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
u32 cur_vblank;
bool rc;
ktime_t t_vblank;
- int count = DRM_TIMESTAMP_MAXRETRIES;
spin_lock(&dev->vblank_time_lock);
@@ -258,10 +272,8 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
* sample the current counter to avoid random jumps
* when drm_vblank_enable() applies the diff
*/
- do {
- cur_vblank = __get_vblank_counter(dev, pipe);
- rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
- } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+ rc = get_vblank_counter_and_timestamp(dev, pipe, &cur_vblank,
+ &t_vblank, false);
/*
* Only reinitialize corresponding vblank timestamp if high-precision query
@@ -299,7 +311,6 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
u32 cur_vblank, diff;
bool rc;
ktime_t t_vblank;
- int count = DRM_TIMESTAMP_MAXRETRIES;
int framedur_ns = vblank->framedur_ns;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
@@ -315,10 +326,8 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
* updating its hardware counter while we are retrieving the
* corresponding vblank timestamp.
*/
- do {
- cur_vblank = __get_vblank_counter(dev, pipe);
- rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
- } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+ rc = get_vblank_counter_and_timestamp(dev, pipe, &cur_vblank,
+ &t_vblank, in_vblank_irq);
if (max_vblank_count) {
/* trust the hw counter when it's around */
@@ -1543,7 +1552,6 @@ static void drm_vblank_restore(struct drm_device *dev, unsigned int pipe)
int framedur_ns;
u64 diff_ns;
u32 cur_vblank, diff = 1;
- int count = DRM_TIMESTAMP_MAXRETRIES;
u32 max_vblank_count = drm_max_vblank_count(dev, pipe);
if (drm_WARN_ON(dev, pipe >= dev->num_crtcs))
@@ -1558,10 +1566,8 @@ static void drm_vblank_restore(struct drm_device *dev, unsigned int pipe)
"Cannot compute missed vblanks without frame duration\n");
framedur_ns = vblank->framedur_ns;
- do {
- cur_vblank = __get_vblank_counter(dev, pipe);
- drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
- } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
+ get_vblank_counter_and_timestamp(dev, pipe, &cur_vblank,
+ &t_vblank, false);
diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
if (framedur_ns)
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_sched.c b/drivers/gpu/drm/etnaviv/etnaviv_sched.c
index df4232d7e135..63f672536516 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_sched.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_sched.c
@@ -142,7 +142,6 @@ int etnaviv_sched_init(struct etnaviv_gpu *gpu)
{
const struct drm_sched_init_args args = {
.ops = &etnaviv_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = etnaviv_hw_jobs_limit,
.hang_limit = etnaviv_job_hang_limit,
.timeout = msecs_to_jiffies(500),
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index 0d13828e7d9e..a59131ae122c 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -69,9 +69,12 @@ config DRM_EXYNOS_DSI
config DRM_EXYNOS_DP
bool "Exynos specific extensions for Analogix DP driver"
depends on DRM_EXYNOS_FIMD || DRM_EXYNOS7_DECON
+ depends on OF
select DRM_ANALOGIX_DP
+ select DRM_BRIDGE_CONNECTOR
select DRM_DISPLAY_DP_HELPER
default DRM_EXYNOS
+ select DRM_OF_DISPLAY_MODE_BRIDGE
select DRM_PANEL
help
This enables support for DP device.
diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c
index 5bcf41e0bd04..6884ea6d04eb 100644
--- a/drivers/gpu/drm/exynos/exynos_dp.c
+++ b/drivers/gpu/drm/exynos/exynos_dp.c
@@ -19,8 +19,10 @@
#include <video/videomode.h>
#include <drm/bridge/analogix_dp.h>
+#include <drm/bridge/of-display-mode-bridge.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
@@ -35,12 +37,9 @@
struct exynos_dp_device {
struct drm_encoder encoder;
- struct drm_connector *connector;
- struct drm_bridge *ptn_bridge;
struct drm_device *drm_dev;
struct device *dev;
- struct videomode vm;
struct analogix_dp_device *adp;
struct analogix_dp_plat_data plat_data;
};
@@ -69,53 +68,6 @@ static int exynos_dp_poweroff(struct analogix_dp_plat_data *plat_data)
return exynos_dp_crtc_clock_enable(plat_data, false);
}
-static int exynos_dp_get_modes(struct analogix_dp_plat_data *plat_data,
- struct drm_connector *connector)
-{
- struct exynos_dp_device *dp = to_dp(plat_data);
- struct drm_display_mode *mode;
-
- if (dp->plat_data.panel)
- return 0;
-
- mode = drm_mode_create(connector->dev);
- if (!mode) {
- DRM_DEV_ERROR(dp->dev,
- "failed to create a new display mode.\n");
- return 0;
- }
-
- drm_display_mode_from_videomode(&dp->vm, mode);
- connector->display_info.width_mm = mode->width_mm;
- connector->display_info.height_mm = mode->height_mm;
-
- mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
- drm_mode_set_name(mode);
- drm_mode_probed_add(connector, mode);
-
- return 1;
-}
-
-static int exynos_dp_bridge_attach(struct analogix_dp_plat_data *plat_data,
- struct drm_bridge *bridge,
- struct drm_connector *connector)
-{
- struct exynos_dp_device *dp = to_dp(plat_data);
- int ret;
-
- dp->connector = connector;
-
- /* Pre-empt DP connector creation if there's a bridge */
- if (dp->ptn_bridge) {
- ret = drm_bridge_attach(&dp->encoder, dp->ptn_bridge, bridge,
- 0);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
static void exynos_dp_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -133,34 +85,16 @@ static const struct drm_encoder_helper_funcs exynos_dp_encoder_helper_funcs = {
.disable = exynos_dp_nop,
};
-static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp)
-{
- int ret;
-
- ret = of_get_videomode(dp->dev->of_node, &dp->vm, OF_USE_NATIVE_MODE);
- if (ret) {
- DRM_DEV_ERROR(dp->dev,
- "failed: of_get_videomode() : %d\n", ret);
- return ret;
- }
- return 0;
-}
-
static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
{
struct exynos_dp_device *dp = dev_get_drvdata(dev);
struct drm_encoder *encoder = &dp->encoder;
struct drm_device *drm_dev = data;
+ struct drm_connector *connector;
int ret;
dp->drm_dev = drm_dev;
- if (!dp->plat_data.panel && !dp->ptn_bridge) {
- ret = exynos_dp_dt_parse_panel(dp);
- if (ret)
- return ret;
- }
-
drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(encoder, &exynos_dp_encoder_helper_funcs);
@@ -172,10 +106,19 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
dp->plat_data.encoder = encoder;
ret = analogix_dp_bind(dp->adp, dp->drm_dev);
- if (ret)
+ if (ret) {
dp->encoder.funcs->destroy(&dp->encoder);
+ return ret;
+ }
+
+ connector = drm_bridge_connector_init(dp->drm_dev, dp->plat_data.encoder);
+ if (IS_ERR(connector)) {
+ ret = PTR_ERR(connector);
+ dev_err(dp->dev, "Failed to initialize bridge_connector\n");
+ return ret;
+ }
- return ret;
+ return drm_connector_attach_encoder(connector, dp->plat_data.encoder);
}
static void exynos_dp_unbind(struct device *dev, struct device *master,
@@ -197,9 +140,6 @@ static int exynos_dp_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *np;
struct exynos_dp_device *dp;
- struct drm_panel *panel;
- struct drm_bridge *bridge;
- int ret;
dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device),
GFP_KERNEL);
@@ -226,27 +166,29 @@ static int exynos_dp_probe(struct platform_device *pdev)
goto out;
}
- ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &bridge);
- if (ret)
- return ret;
+ if (of_get_display_timings(dev->of_node)) {
+ dp->plat_data.next_bridge = devm_drm_of_display_mode_bridge(dp->dev,
+ dp->dev->of_node,
+ DRM_MODE_CONNECTOR_eDP);
+ if (IS_ERR(dp->plat_data.next_bridge))
+ return PTR_ERR(dp->plat_data.next_bridge);
+ }
/* The remote port can be either a panel or a bridge */
- dp->plat_data.panel = panel;
dp->plat_data.dev_type = EXYNOS_DP;
dp->plat_data.power_on = exynos_dp_poweron;
dp->plat_data.power_off = exynos_dp_poweroff;
- dp->plat_data.attach = exynos_dp_bridge_attach;
- dp->plat_data.get_modes = exynos_dp_get_modes;
- dp->plat_data.skip_connector = !!bridge;
-
- dp->ptn_bridge = bridge;
+ dp->plat_data.ops = &exynos_dp_ops;
out:
dp->adp = analogix_dp_probe(dev, &dp->plat_data);
if (IS_ERR(dp->adp))
return PTR_ERR(dp->adp);
- return component_add(&pdev->dev, &exynos_dp_ops);
+ if (dp->plat_data.panel || dp->plat_data.next_bridge)
+ return component_add(&pdev->dev, &exynos_dp_ops);
+ else
+ return analogix_dp_finish_probe(dp->adp);
}
static void exynos_dp_remove(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
index 051ecc526832..753d97bff76f 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c
@@ -10,7 +10,7 @@
#include "hyperv_drm.h"
-#define VMBUS_RING_BUFSIZE (256 * 1024)
+#define VMBUS_RING_BUFSIZE VMBUS_RING_SIZE(256 * 1024)
#define VMBUS_VSP_TIMEOUT (10 * HZ)
#define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug
index 3562a02ef7ad..52a3a59b4ba2 100644
--- a/drivers/gpu/drm/i915/Kconfig.debug
+++ b/drivers/gpu/drm/i915/Kconfig.debug
@@ -51,7 +51,7 @@ config DRM_I915_DEBUG
select DRM_DEBUG_MM if DRM=y
select DRM_EXPORT_FOR_TESTS if m
select DRM_KUNIT_TEST if KUNIT
- select DMABUF_SELFTESTS
+ select DMABUF_KUNIT_TEST if KUNIT
select SW_SYNC # signaling validation framework (igt/syncobj*)
select DRM_I915_WERROR
select DRM_I915_DEBUG_GEM
diff --git a/drivers/gpu/drm/imagination/pvr_fw_trace.c b/drivers/gpu/drm/imagination/pvr_fw_trace.c
index 6193811ef7be..6bb5baa6c41b 100644
--- a/drivers/gpu/drm/imagination/pvr_fw_trace.c
+++ b/drivers/gpu/drm/imagination/pvr_fw_trace.c
@@ -77,7 +77,7 @@ const struct kernel_param_ops pvr_fw_trace_init_mask_ops = {
};
param_check_hexint(init_fw_trace_mask, &pvr_fw_trace_init_mask);
-module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0600);
+module_param_cb(init_fw_trace_mask, &pvr_fw_trace_init_mask_ops, &pvr_fw_trace_init_mask, 0400);
__MODULE_PARM_TYPE(init_fw_trace_mask, "hexint");
MODULE_PARM_DESC(init_fw_trace_mask,
"Enable FW trace for the specified groups at device init time");
diff --git a/drivers/gpu/drm/imagination/pvr_job.c b/drivers/gpu/drm/imagination/pvr_job.c
index 0c2f511a6178..dd9f5df01e08 100644
--- a/drivers/gpu/drm/imagination/pvr_job.c
+++ b/drivers/gpu/drm/imagination/pvr_job.c
@@ -326,7 +326,7 @@ prepare_job_syncs(struct pvr_file *pvr_file,
struct pvr_job_data *job_data,
struct xarray *signal_array)
{
- struct dma_fence *done_fence;
+ struct dma_fence *finished_fence;
int err = pvr_sync_signal_array_collect_ops(signal_array,
from_pvr_file(pvr_file),
job_data->sync_op_count,
@@ -359,13 +359,13 @@ prepare_job_syncs(struct pvr_file *pvr_file,
return err;
}
- /* We need to arm the job to get the job done fence. */
- done_fence = pvr_queue_job_arm(job_data->job);
+ /* We need to arm the job to get the job finished fence. */
+ finished_fence = pvr_queue_job_arm(job_data->job);
err = pvr_sync_signal_array_update_fences(signal_array,
job_data->sync_op_count,
job_data->sync_ops,
- done_fence);
+ finished_fence);
return err;
}
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index dd88949f6194..d10a13173f0f 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -178,10 +178,35 @@ static const struct dma_fence_ops pvr_queue_job_fence_ops = {
};
/**
+ * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO.
+ * @f: The dma_fence to check.
+ *
+ * Return:
+ * * true if the dma_fence is backed by a UFO, or
+ * * false otherwise.
+ */
+static inline bool
+pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
+{
+ /*
+ * Currently the only dma_fence backed by a UFO object is the job fence,
+ * e.g. pvr_job::done_fence, wrapped by a pvr_queue_fence object.
+ */
+ return f && f->ops == &pvr_queue_job_fence_ops;
+}
+
+/**
* to_pvr_queue_job_fence() - Return a pvr_queue_fence object if the fence is
- * backed by a UFO.
+ * already backed by a UFO.
* @f: The dma_fence to turn into a pvr_queue_fence.
*
+ * This could be called on:
+ * - a job fence directly, in which case it simply returns the containing pvr_queue_fence;
+ * - a drm_sched_fence's scheduled or finished fence, in which case it will first try to follow
+ * the parent pointer to find the job fence (note that the parent pointer is initialized
+ * only after the run_job() callback is called on the drm_sched_fence's owning job);
+ * - any other dma_fence, in which case it will return NULL.
+ *
* Return:
* * A non-NULL pvr_queue_fence object if the dma_fence is backed by a UFO, or
* * NULL otherwise.
@@ -194,7 +219,7 @@ to_pvr_queue_job_fence(struct dma_fence *f)
if (sched_fence)
f = sched_fence->parent;
- if (f && f->ops == &pvr_queue_job_fence_ops)
+ if (pvr_queue_fence_is_ufo_backed(f))
return container_of(f, struct pvr_queue_fence, base);
return NULL;
@@ -349,11 +374,23 @@ static u32 ufo_cmds_size(u32 elem_count)
static u32 job_cmds_size(struct pvr_job *job, u32 ufo_wait_count)
{
- /* One UFO cmd for the fence signaling, one UFO cmd per native fence native,
- * and a command for the job itself.
+ /*
+ * One UFO command per native fence this job will be waiting on (unless any are
+ * signaled by the time the job is submitted), plus a command for the job itself,
+ * plus one UFO command for the fence signaling.
*/
- return ufo_cmds_size(1) + ufo_cmds_size(ufo_wait_count) +
- pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len);
+ return ufo_cmds_size(ufo_wait_count) +
+ pvr_cccb_get_size_of_cmd_with_hdr(job->cmd_len) +
+ ufo_cmds_size(1);
+}
+
+static bool
+is_paired_job_fence(struct dma_fence *fence, struct pvr_job *job)
+{
+ /* This assumes "fence" is one of "job"'s drm_sched_job::dependencies */
+ return job->type == DRM_PVR_JOB_TYPE_FRAGMENT &&
+ job->paired_job &&
+ &job->paired_job->base.s_fence->scheduled == fence;
}
/**
@@ -371,6 +408,17 @@ static unsigned long job_count_remaining_native_deps(struct pvr_job *job)
xa_for_each(&job->base.dependencies, index, fence) {
struct pvr_queue_fence *jfence;
+ if (is_paired_job_fence(fence, job)) {
+ /*
+ * A fence between paired jobs won't resolve to a pvr_queue_fence (i.e.
+ * be backed by a UFO) until the jobs have been submitted, together.
+ * The submitting code will insert a partial render fence command for this.
+ */
+ WARN_ON(dma_fence_is_signaled(fence));
+ remaining_count++;
+ continue;
+ }
+
jfence = to_pvr_queue_job_fence(fence);
if (!jfence)
continue;
@@ -468,29 +516,37 @@ pvr_queue_get_job_kccb_fence(struct pvr_queue *queue, struct pvr_job *job)
}
static struct dma_fence *
-pvr_queue_get_paired_frag_job_dep(struct pvr_queue *queue, struct pvr_job *job)
+pvr_queue_get_paired_frag_job_dep(struct pvr_job *job)
{
struct pvr_job *frag_job = job->type == DRM_PVR_JOB_TYPE_GEOMETRY ?
job->paired_job : NULL;
+ struct pvr_queue *frag_queue = frag_job ? frag_job->ctx->queues.fragment : NULL;
struct dma_fence *f;
unsigned long index;
if (!frag_job)
return NULL;
+ /* Have the geometry job wait on the paired fragment job's dependencies as well. */
xa_for_each(&frag_job->base.dependencies, index, f) {
/* Skip already signaled fences. */
if (dma_fence_is_signaled(f))
continue;
- /* Skip our own fence. */
+ /*
+ * The paired job fence won't be signaled until both jobs have
+ * been submitted, so we can't wait on it to schedule them.
+ */
if (f == &job->base.s_fence->scheduled)
continue;
return dma_fence_get(f);
}
- return frag_job->base.sched->ops->prepare_job(&frag_job->base, &queue->entity);
+ /* Initialize the paired fragment job's done_fence, so we can signal it. */
+ pvr_queue_job_fence_init(frag_job->done_fence, frag_queue);
+
+ return pvr_queue_get_job_cccb_fence(frag_queue, frag_job);
}
/**
@@ -509,32 +565,25 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,
struct pvr_queue *queue = container_of(s_entity, struct pvr_queue, entity);
struct dma_fence *internal_dep = NULL;
- /*
- * Initialize the done_fence, so we can signal it. This must be done
- * here because otherwise by the time of run_job() the job will end up
- * in the pending list without a valid fence.
- */
if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
/*
- * This will be called on a paired fragment job after being
- * submitted to firmware. We can tell if this is the case and
- * bail early from whether run_job() has been called on the
- * geometry job, which would issue a pm ref.
+ * This will be called on a paired fragment job after being submitted
+ * to the firmware as part of the paired geometry job's submission.
+ * We can tell if this is the case and bail early from whether run_job()
+ * has been called on the geometry job, which would issue a pm ref on
+ * this job as well.
*/
- if (job->paired_job->has_pm_ref)
+ if (job->has_pm_ref)
return NULL;
-
- /*
- * In this case we need to use the job's own ctx to initialise
- * the done_fence. The other steps are done in the ctx of the
- * paired geometry job.
- */
- pvr_queue_job_fence_init(job->done_fence,
- job->ctx->queues.fragment);
- } else {
- pvr_queue_job_fence_init(job->done_fence, queue);
}
+ /*
+ * Initialize the done_fence, so we can signal it. This must be done
+ * here because otherwise by the time of run_job() the job will end up
+ * in the pending list without a valid fence.
+ */
+ pvr_queue_job_fence_init(job->done_fence, queue);
+
/* CCCB fence is used to make sure we have enough space in the CCCB to
* submit our commands.
*/
@@ -555,7 +604,7 @@ pvr_queue_prepare_job(struct drm_sched_job *sched_job,
/* The paired job fence should come last, when everything else is ready. */
if (!internal_dep)
- internal_dep = pvr_queue_get_paired_frag_job_dep(queue, job);
+ internal_dep = pvr_queue_get_paired_frag_job_dep(job);
return internal_dep;
}
@@ -630,11 +679,7 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
if (!jfence)
continue;
- /* Skip the partial render fence, we will place it at the end. */
- if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job &&
- &job->paired_job->base.s_fence->scheduled == fence)
- continue;
-
+ /* Some dependencies might have been signaled since prepare_job() */
if (dma_fence_is_signaled(&jfence->base))
continue;
@@ -649,8 +694,13 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
}
}
- /* Partial render fence goes last. */
if (job->type == DRM_PVR_JOB_TYPE_FRAGMENT && job->paired_job) {
+ /*
+ * The loop above will only process dependencies backed by a UFO i.e. with
+ * a valid parent fence assigned, but the paired job dependency won't have
+ * one until both jobs have been submitted. Access the parent fence directly
+ * here instead, submitting it last as partial render fence.
+ */
jfence = to_pvr_queue_job_fence(job->paired_job->done_fence);
if (!WARN_ON(!jfence)) {
pvr_fw_object_get_fw_addr(jfence->queue->timeline_ufo.fw_obj,
@@ -679,7 +729,7 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd,
job->id, job->id);
- /* Signal the job fence. */
+ /* Update command to signal the job fence. */
pvr_fw_object_get_fw_addr(queue->timeline_ufo.fw_obj, &ufos[0].addr);
ufos[0].value = job->done_fence->seqno;
pvr_cccb_write_command_with_header(cccb, ROGUE_FWIF_CCB_CMD_TYPE_UPDATE,
@@ -709,10 +759,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job)
}
/* The only kind of jobs that can be paired are geometry and fragment, and
- * we bail out early if we see a fragment job that's paired with a geomtry
- * job.
- * Paired jobs must also target the same context and point to the same
- * HWRT.
+ * we bail out early if we see a fragment job that's paired with a geometry job.
+ * Paired jobs must also target the same context and point to the same HWRT.
*/
if (WARN_ON(job->paired_job &&
(job->type != DRM_PVR_JOB_TYPE_GEOMETRY ||
@@ -882,16 +930,16 @@ static const struct drm_sched_backend_ops pvr_queue_sched_ops = {
};
/**
- * pvr_queue_fence_is_ufo_backed() - Check if a dma_fence is backed by a UFO object
+ * pvr_queue_fence_is_native() - Check if a dma_fence is native to this driver.
* @f: Fence to test.
*
- * A UFO-backed fence is a fence that can be signaled or waited upon FW-side.
- * pvr_job::done_fence objects are backed by the timeline UFO attached to the queue
- * they are pushed to, but those fences are not directly exposed to the outside
- * world, so we also need to check if the fence we're being passed is a
- * drm_sched_fence that was coming from our driver.
+ * Check if the fence we're being passed is a drm_sched_fence that is coming from this driver.
+ *
+ * It may be a UFO-backed fence i.e. a fence that can be signaled or waited upon FW-side,
+ * such as pvr_job::done_fence objects that are backed by the timeline UFO attached to the queue
+ * they are pushed to.
*/
-bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
+bool pvr_queue_fence_is_native(struct dma_fence *f)
{
struct drm_sched_fence *sched_fence = f ? to_drm_sched_fence(f) : NULL;
@@ -899,10 +947,7 @@ bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f)
sched_fence->sched->ops == &pvr_queue_sched_ops)
return true;
- if (f && f->ops == &pvr_queue_job_fence_ops)
- return true;
-
- return false;
+ return pvr_queue_fence_is_ufo_backed(f);
}
/**
@@ -934,9 +979,8 @@ pvr_queue_signal_done_fences(struct pvr_queue *queue)
}
/**
- * pvr_queue_check_job_waiting_for_cccb_space() - Check if the job waiting for CCCB space
- * can be unblocked
- * pushed to the CCCB
+ * pvr_queue_check_job_waiting_for_cccb_space() - Check if a job waiting for CCCB space
+ * can be unblocked and pushed to the CCCB.
* @queue: Queue to check
*
* If we have a job waiting for CCCB, and this job now fits in the CCCB, we signal
@@ -1228,7 +1272,6 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx,
const struct drm_sched_init_args sched_args = {
.ops = &pvr_queue_sched_ops,
.submit_wq = pvr_dev->sched_wq,
- .num_rqs = 1,
.credit_limit = 64 * 1024,
.hang_limit = 1,
.timeout = msecs_to_jiffies(500),
diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h
index fc1986d73fc8..4aa72665ce25 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.h
+++ b/drivers/gpu/drm/imagination/pvr_queue.h
@@ -141,7 +141,7 @@ struct pvr_queue {
u64 callstack_addr;
};
-bool pvr_queue_fence_is_ufo_backed(struct dma_fence *f);
+bool pvr_queue_fence_is_native(struct dma_fence *f);
int pvr_queue_job_init(struct pvr_job *job, u64 drm_client_id);
diff --git a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h
index 869d904e3649..fe54c1cad7a9 100644
--- a/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h
+++ b/drivers/gpu/drm/imagination/pvr_rogue_fwif_shared.h
@@ -14,15 +14,7 @@
#define ROGUE_NUM_GEOM_CORES_SIZE 2U
-/*
- * Maximum number of UFOs in a CCB command.
- * The number is based on having 32 sync prims (as originally), plus 32 sync
- * checkpoints.
- * Once the use of sync prims is no longer supported, we will retain
- * the same total (64) as the number of sync checkpoints which may be
- * supporting a fence is not visible to the client driver and has to
- * allow for the number of different timelines involved in fence merges.
- */
+/* Maximum number of UFOs in a CCB command. */
#define ROGUE_FWIF_CCB_CMD_MAX_UFOS (32U + 32U)
/*
diff --git a/drivers/gpu/drm/imagination/pvr_sync.c b/drivers/gpu/drm/imagination/pvr_sync.c
index 3582616ff722..936f840a5221 100644
--- a/drivers/gpu/drm/imagination/pvr_sync.c
+++ b/drivers/gpu/drm/imagination/pvr_sync.c
@@ -160,7 +160,7 @@ int
pvr_sync_signal_array_update_fences(struct xarray *array,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops,
- struct dma_fence *done_fence)
+ struct dma_fence *finished_fence)
{
for (u32 i = 0; i < sync_op_count; i++) {
struct dma_fence *old_fence;
@@ -175,7 +175,7 @@ pvr_sync_signal_array_update_fences(struct xarray *array,
return -EINVAL;
old_fence = sig_sync->fence;
- sig_sync->fence = dma_fence_get(done_fence);
+ sig_sync->fence = dma_fence_get(finished_fence);
dma_fence_put(old_fence);
if (WARN_ON(!sig_sync->fence))
@@ -211,7 +211,7 @@ pvr_sync_add_dep_to_job(struct drm_sched_job *job, struct dma_fence *f)
int err = 0;
dma_fence_unwrap_for_each(uf, &iter, f) {
- if (pvr_queue_fence_is_ufo_backed(uf))
+ if (pvr_queue_fence_is_native(uf))
native_fence_count++;
}
@@ -227,7 +227,7 @@ pvr_sync_add_dep_to_job(struct drm_sched_job *job, struct dma_fence *f)
if (err)
continue;
- if (pvr_queue_fence_is_ufo_backed(uf)) {
+ if (pvr_queue_fence_is_native(uf)) {
struct drm_sched_fence *s_fence = to_drm_sched_fence(uf);
/* If this is a native dependency, we wait for the scheduled fence,
diff --git a/drivers/gpu/drm/imagination/pvr_sync.h b/drivers/gpu/drm/imagination/pvr_sync.h
index db6ccfda104a..48501ad27794 100644
--- a/drivers/gpu/drm/imagination/pvr_sync.h
+++ b/drivers/gpu/drm/imagination/pvr_sync.h
@@ -70,7 +70,7 @@ int
pvr_sync_signal_array_update_fences(struct xarray *array,
u32 sync_op_count,
const struct drm_pvr_sync_op *sync_ops,
- struct dma_fence *done_fence);
+ struct dma_fence *finished_fence);
void
pvr_sync_signal_array_push_fences(struct xarray *array);
diff --git a/drivers/gpu/drm/imx/ipuv3/Kconfig b/drivers/gpu/drm/imx/ipuv3/Kconfig
index b2240998df4f..351dc65913eb 100644
--- a/drivers/gpu/drm/imx/ipuv3/Kconfig
+++ b/drivers/gpu/drm/imx/ipuv3/Kconfig
@@ -16,7 +16,7 @@ config DRM_IMX_PARALLEL_DISPLAY
select DRM_BRIDGE
select DRM_BRIDGE_CONNECTOR
select DRM_DISPLAY_HELPER
- select DRM_IMX_LEGACY_BRIDGE
+ select DRM_OF_DISPLAY_MODE_BRIDGE
select DRM_PANEL_BRIDGE
select VIDEOMODE_HELPERS
@@ -37,7 +37,7 @@ config DRM_IMX_LDB
select DRM_BRIDGE
select DRM_BRIDGE_CONNECTOR
select DRM_PANEL_BRIDGE
- select DRM_IMX_LEGACY_BRIDGE
+ select DRM_OF_DISPLAY_MODE_BRIDGE
help
Choose this to enable the internal LVDS Display Bridge (LDB)
found on i.MX53 and i.MX6 processors.
diff --git a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c
index 626d410d9150..730caf883e83 100644
--- a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c
+++ b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c
@@ -28,7 +28,7 @@
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
-#include <drm/bridge/imx.h>
+#include <drm/bridge/of-display-mode-bridge.h>
#include "imx-drm.h"
@@ -605,8 +605,8 @@ static int imx_ldb_probe(struct platform_device *pdev)
* checking the bus_format property.
*/
if (!channel->bridge) {
- channel->bridge = devm_imx_drm_legacy_bridge(dev, child,
- DRM_MODE_CONNECTOR_LVDS);
+ channel->bridge = devm_drm_of_display_mode_bridge(dev, child,
+ DRM_MODE_CONNECTOR_LVDS);
if (IS_ERR(channel->bridge)) {
ret = PTR_ERR(channel->bridge);
goto free_child;
diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
index a356f0b764cb..6c505becb31d 100644
--- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c
+++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c
@@ -19,7 +19,7 @@
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
-#include <drm/bridge/imx.h>
+#include <drm/bridge/of-display-mode-bridge.h>
#include "imx-drm.h"
@@ -242,7 +242,8 @@ static int imx_pd_probe(struct platform_device *pdev)
/* port@1 is the output port */
imxpd->next_bridge = devm_drm_of_get_bridge(dev, np, 1, 0);
if (imxpd->next_bridge == ERR_PTR(-ENODEV))
- imxpd->next_bridge = devm_imx_drm_legacy_bridge(dev, np, DRM_MODE_CONNECTOR_DPI);
+ imxpd->next_bridge = devm_drm_of_display_mode_bridge(dev, np,
+ DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(imxpd->next_bridge)) {
ret = PTR_ERR(imxpd->next_bridge);
return ret;
diff --git a/drivers/gpu/drm/kmb/kmb_dsi.c b/drivers/gpu/drm/kmb/kmb_dsi.c
index aeb2f9f98f23..febca939bd01 100644
--- a/drivers/gpu/drm/kmb/kmb_dsi.c
+++ b/drivers/gpu/drm/kmb/kmb_dsi.c
@@ -251,7 +251,7 @@ int kmb_dsi_host_bridge_init(struct device *dev)
return -EINVAL;
}
/* Locate drm bridge from the hdmi encoder DT node */
- adv_bridge = of_drm_find_bridge(encoder_node);
+ adv_bridge = of_drm_find_and_get_bridge(encoder_node);
of_node_put(dsi_out);
of_node_put(encoder_node);
if (!adv_bridge) {
diff --git a/drivers/gpu/drm/lima/lima_sched.c b/drivers/gpu/drm/lima/lima_sched.c
index 9a1e6b9ecbe5..0a01213c4878 100644
--- a/drivers/gpu/drm/lima/lima_sched.c
+++ b/drivers/gpu/drm/lima/lima_sched.c
@@ -521,7 +521,6 @@ int lima_sched_pipe_init(struct lima_sched_pipe *pipe, const char *name)
lima_sched_timeout_ms : 10000;
const struct drm_sched_init_args args = {
.ops = &lima_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.hang_limit = lima_job_hang_limit,
.timeout = msecs_to_jiffies(timeout),
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c
index a0ac19ee0353..5c2185d6b707 100644
--- a/drivers/gpu/drm/mgag200/mgag200_g200se.c
+++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c
@@ -76,12 +76,19 @@ static void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
unsigned int bpp;
unsigned long mb;
- if (format->cpp[0] * 8 > 16)
+ switch (format->format) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_RGB888:
bpp = 32;
- else if (format->cpp[0] * 8 > 8)
+ break;
+ case DRM_FORMAT_RGB565:
+ case DRM_FORMAT_XRGB1555:
bpp = 16;
- else
+ break;
+ case DRM_FORMAT_C8:
bpp = 8;
+ break;
+ }
mb = (mode->clock * bpp) / 1000;
if (mb > 3100)
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 8894a063b1a1..bd346b828773 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -56,6 +56,12 @@ void mgag200_crtc_fill_gamma(struct mga_device *mdev,
struct drm_crtc *crtc = &mdev->crtc;
switch (format->format) {
+ case DRM_FORMAT_C8:
+ drm_crtc_fill_palette_8(crtc, mgag200_set_gamma_lut);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ drm_crtc_fill_gamma_555(crtc, mgag200_set_gamma_lut);
+ break;
case DRM_FORMAT_RGB565:
drm_crtc_fill_gamma_565(crtc, mgag200_set_gamma_lut);
break;
@@ -77,6 +83,12 @@ void mgag200_crtc_load_gamma(struct mga_device *mdev,
struct drm_crtc *crtc = &mdev->crtc;
switch (format->format) {
+ case DRM_FORMAT_C8:
+ drm_crtc_load_palette_8(crtc, lut, mgag200_set_gamma_lut);
+ break;
+ case DRM_FORMAT_XRGB1555:
+ drm_crtc_load_gamma_555_from_888(crtc, lut, mgag200_set_gamma_lut);
+ break;
case DRM_FORMAT_RGB565:
drm_crtc_load_gamma_565_from_888(crtc, lut, mgag200_set_gamma_lut);
break;
@@ -174,6 +186,7 @@ static void mgag200_set_datasiz(struct mga_device *mdev, u32 format)
/* Big-endian byte-swapping */
switch (format) {
+ case DRM_FORMAT_XRGB1555:
case DRM_FORMAT_RGB565:
opmode |= 0x10100;
break;
@@ -298,37 +311,11 @@ void mgag200_set_mode_regs(struct mga_device *mdev, const struct drm_display_mod
WREG8(MGA_MISC_OUT, misc);
}
-static u8 mgag200_get_bpp_shift(const struct drm_format_info *format)
-{
- static const u8 bpp_shift[] = {0, 1, 0, 2};
-
- return bpp_shift[format->cpp[0] - 1];
-}
-
-/*
- * Calculates the HW offset value from the framebuffer's pitch. The
- * offset is a multiple of the pixel size and depends on the display
- * format.
- */
-static u32 mgag200_calculate_offset(struct mga_device *mdev,
- const struct drm_framebuffer *fb)
-{
- u32 offset = fb->pitches[0] / fb->format->cpp[0];
- u8 bppshift = mgag200_get_bpp_shift(fb->format);
-
- if (fb->format->cpp[0] * 8 == 24)
- offset = (offset * 3) >> (4 - bppshift);
- else
- offset = offset >> (4 - bppshift);
-
- return offset;
-}
-
static void mgag200_set_offset(struct mga_device *mdev,
const struct drm_framebuffer *fb)
{
u8 crtc13, crtcext0;
- u32 offset = mgag200_calculate_offset(mdev, fb);
+ u32 offset = fb->pitches[0] / 16;
RREG_ECRT(0, crtcext0);
@@ -343,49 +330,49 @@ static void mgag200_set_offset(struct mga_device *mdev,
void mgag200_set_format_regs(struct mga_device *mdev, const struct drm_format_info *format)
{
- struct drm_device *dev = &mdev->base;
- unsigned int bpp, bppshift, scale;
- u8 crtcext3, xmulctrl;
+ u8 xmulctrl = 0;
+ u8 crtcext3;
- bpp = format->cpp[0] * 8;
+ RREG_ECRT(3, crtcext3);
- bppshift = mgag200_get_bpp_shift(format);
- switch (bpp) {
- case 24:
- scale = ((1 << bppshift) * 3) - 1;
+ switch (format->format) {
+ case DRM_FORMAT_C8:
+ crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
+ crtcext3 |= 0x0;
break;
- default:
- scale = (1 << bppshift) - 1;
+ case DRM_FORMAT_XRGB1555:
+ case DRM_FORMAT_RGB565:
+ crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
+ crtcext3 |= 0x01;
+ break;
+ case DRM_FORMAT_RGB888:
+ crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
+ crtcext3 |= 0x02;
+ break;
+ case DRM_FORMAT_XRGB8888:
+ crtcext3 &= ~MGAREG_CRTCEXT3_SCALE_MASK;
+ crtcext3 |= 0x03;
break;
}
- RREG_ECRT(3, crtcext3);
-
- switch (bpp) {
- case 8:
+ switch (format->format) {
+ case DRM_FORMAT_C8:
xmulctrl = MGA1064_MUL_CTL_8bits;
break;
- case 16:
- if (format->depth == 15)
- xmulctrl = MGA1064_MUL_CTL_15bits;
- else
- xmulctrl = MGA1064_MUL_CTL_16bits;
+ case DRM_FORMAT_XRGB1555:
+ xmulctrl = MGA1064_MUL_CTL_15bits;
+ break;
+ case DRM_FORMAT_RGB565:
+ xmulctrl = MGA1064_MUL_CTL_16bits;
break;
- case 24:
+ case DRM_FORMAT_RGB888:
xmulctrl = MGA1064_MUL_CTL_24bits;
break;
- case 32:
+ case DRM_FORMAT_XRGB8888:
xmulctrl = MGA1064_MUL_CTL_32_24bits;
break;
- default:
- /* BUG: We should have caught this problem already. */
- drm_WARN_ON(dev, "invalid format depth\n");
- return;
}
- crtcext3 &= ~GENMASK(2, 0);
- crtcext3 |= scale;
-
WREG_DAC(MGA1064_MUL_CTL, xmulctrl);
WREG_GFX(0, 0x00);
@@ -463,7 +450,9 @@ static void mgag200_handle_damage(struct mga_device *mdev, const struct iosys_ma
const uint32_t mgag200_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_RGB565,
+ DRM_FORMAT_XRGB1555,
DRM_FORMAT_RGB888,
+ DRM_FORMAT_C8,
};
const size_t mgag200_primary_plane_formats_size = ARRAY_SIZE(mgag200_primary_plane_formats);
diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h
index d4fef8f25871..088b6c16f7f6 100644
--- a/drivers/gpu/drm/mgag200/mgag200_reg.h
+++ b/drivers/gpu/drm/mgag200/mgag200_reg.h
@@ -265,6 +265,7 @@
#define MGAREG_CRTCEXT1_HRSTEN BIT(3)
#define MGAREG_CRTCEXT3_MGAMODE BIT(7)
+#define MGAREG_CRTCEXT3_SCALE_MASK GENMASK(2, 0)
/* Cursor X and Y position */
#define MGA_CURPOSXL 0x3c0c
diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c
index 1a952b171ed7..271691ae32c3 100644
--- a/drivers/gpu/drm/msm/msm_gem_vma.c
+++ b/drivers/gpu/drm/msm/msm_gem_vma.c
@@ -841,7 +841,6 @@ msm_gem_vm_create(struct drm_device *drm, struct msm_mmu *mmu, const char *name,
if (!managed) {
struct drm_sched_init_args args = {
.ops = &msm_vm_bind_ops,
- .num_rqs = 1,
.credit_limit = 1,
.timeout = MAX_SCHEDULE_TIMEOUT,
.name = "msm-vm-bind",
diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c
index 30ddb5351e98..a7dafa7ab4b1 100644
--- a/drivers/gpu/drm/msm/msm_ringbuffer.c
+++ b/drivers/gpu/drm/msm/msm_ringbuffer.c
@@ -67,7 +67,6 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id,
{
struct drm_sched_init_args args = {
.ops = &msm_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = num_hw_submissions,
.timeout = MAX_SCHEDULE_TIMEOUT,
.dev = gpu->dev->dev,
diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig
index 264e74f45554..31db7a824a93 100644
--- a/drivers/gpu/drm/mxsfb/Kconfig
+++ b/drivers/gpu/drm/mxsfb/Kconfig
@@ -33,6 +33,8 @@ config DRM_IMX_LCDIF
select DRM_GEM_DMA_HELPER
select DRM_PANEL
select DRM_PANEL_BRIDGE
+ select DRM_DISPLAY_HELPER
+ select DRM_BRIDGE_CONNECTOR
help
Choose this option if you have an LCDIFv3 LCD controller.
Those devices are found in various i.MX SoC (i.MX8MP,
diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c
index 47da1d9336b9..7f07ae24e0dc 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_drv.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c
@@ -5,6 +5,7 @@
* This code is based on drivers/gpu/drm/mxsfb/mxsfb*
*/
+#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
@@ -17,6 +18,7 @@
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_encoder.h>
#include <drm/drm_fbdev_dma.h>
@@ -48,62 +50,61 @@ static const struct drm_encoder_funcs lcdif_encoder_funcs = {
static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
{
struct device *dev = lcdif->drm->dev;
- struct device_node *ep;
- struct drm_bridge *bridge;
- int ret;
+ struct device_node *ep __free(device_node) = NULL;
for_each_endpoint_of_node(dev->of_node, ep) {
- struct device_node *remote;
+ struct device_node *remote __free(device_node) =
+ of_graph_get_remote_port_parent(ep);
struct of_endpoint of_ep;
+ struct drm_bridge *bridge;
struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int ret;
- remote = of_graph_get_remote_port_parent(ep);
- if (!of_device_is_available(remote)) {
- of_node_put(remote);
+ if (!of_device_is_available(remote))
continue;
- }
- of_node_put(remote);
ret = of_graph_parse_endpoint(ep, &of_ep);
- if (ret < 0) {
- dev_err(dev, "Failed to parse endpoint %pOF\n", ep);
- of_node_put(ep);
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to parse endpoint %pOF\n", ep);
bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, of_ep.id);
- if (IS_ERR(bridge)) {
- of_node_put(ep);
+ if (IS_ERR(bridge))
return dev_err_probe(dev, PTR_ERR(bridge),
"Failed to get bridge for endpoint%u\n",
of_ep.id);
- }
encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL);
- if (!encoder) {
- dev_err(dev, "Failed to allocate encoder for endpoint%u\n",
- of_ep.id);
- of_node_put(ep);
- return -ENOMEM;
- }
+ if (!encoder)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed to allocate encoder for endpoint%u\n",
+ of_ep.id);
encoder->possible_crtcs = drm_crtc_mask(&lcdif->crtc);
ret = drm_encoder_init(lcdif->drm, encoder, &lcdif_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
- if (ret) {
- dev_err(dev, "Failed to initialize encoder for endpoint%u: %d\n",
- of_ep.id, ret);
- of_node_put(ep);
- return ret;
- }
-
- ret = drm_bridge_attach(encoder, bridge, NULL, 0);
- if (ret) {
- of_node_put(ep);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to initialize encoder for endpoint%u\n",
+ of_ep.id);
+
+ ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+ if (ret)
return dev_err_probe(dev, ret,
"Failed to attach bridge for endpoint%u\n",
of_ep.id);
- }
+
+ connector = drm_bridge_connector_init(lcdif->drm, encoder);
+ if (IS_ERR(connector))
+ return dev_err_probe(dev, PTR_ERR(connector),
+ "Failed to init bridge_connector for endpoint%u\n",
+ of_ep.id);
+
+ ret = drm_connector_attach_encoder(connector, encoder);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to attach connector for endpoint%u\n",
+ of_ep.id);
}
return 0;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index 6c3a8712d38a..04b6fa3ee2ae 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -2474,7 +2474,7 @@ nv50_disp_atomic_commit(struct drm_device *dev,
pm_runtime_get_noresume(dev->dev);
if (nonblock)
- queue_work(system_unbound_wq, &state->commit_work);
+ queue_work(system_dfl_wq, &state->commit_work);
else
nv50_disp_atomic_commit_tail(state);
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index 7860877d909b..291203121f0c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -176,7 +176,7 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,
/* Cancel all jobs from the entity's queue. */
if (chan->sched)
- drm_sched_entity_fini(&chan->sched->entity);
+ drm_sched_entity_kill(&chan->sched->entity);
if (chan->chan)
nouveau_channel_idle(chan->chan);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 517ff2c31dce..e16f59b00f6f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -632,7 +632,7 @@ nouveau_drm_device_init(struct nouveau_drm *drm)
struct drm_device *dev = drm->dev;
int ret;
- drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", 0,
+ drm->sched_wq = alloc_workqueue("nouveau_sched_wq_shared", WQ_PERCPU,
WQ_MAX_ACTIVE);
if (!drm->sched_wq)
return -ENOMEM;
diff --git a/drivers/gpu/drm/nouveau/nouveau_sched.c b/drivers/gpu/drm/nouveau/nouveau_sched.c
index 5546be488795..2cbae003d6de 100644
--- a/drivers/gpu/drm/nouveau/nouveau_sched.c
+++ b/drivers/gpu/drm/nouveau/nouveau_sched.c
@@ -405,7 +405,6 @@ nouveau_sched_init(struct nouveau_sched *sched, struct nouveau_drm *drm,
struct drm_sched_entity *entity = &sched->entity;
struct drm_sched_init_args args = {
.ops = &nouveau_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = credit_limit,
.timeout = msecs_to_jiffies(NOUVEAU_SCHED_JOB_TIMEOUT_MS),
.name = "nouveau_sched",
@@ -414,7 +413,8 @@ nouveau_sched_init(struct nouveau_sched *sched, struct nouveau_drm *drm,
int ret;
if (!wq) {
- wq = alloc_workqueue("nouveau_sched_wq_%d", 0, WQ_MAX_ACTIVE,
+ wq = alloc_workqueue("nouveau_sched_wq_%d", WQ_PERCPU,
+ WQ_MAX_ACTIVE,
current->pid);
if (!wq)
return -ENOMEM;
diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c
index 7378e855c278..ca891aba3820 100644
--- a/drivers/gpu/drm/omapdrm/dss/output.c
+++ b/drivers/gpu/drm/omapdrm/dss/output.c
@@ -30,11 +30,13 @@ int omapdss_device_init_output(struct omap_dss_device *out,
return 0;
}
- out->bridge = of_drm_find_bridge(remote_node);
out->panel = of_drm_find_panel(remote_node);
if (IS_ERR(out->panel))
out->panel = NULL;
+ if (!out->panel)
+ out->bridge = of_drm_find_and_get_bridge(remote_node);
+
of_node_put(remote_node);
if (out->panel) {
@@ -49,7 +51,7 @@ int omapdss_device_init_output(struct omap_dss_device *out,
goto error;
}
- out->bridge = bridge;
+ out->bridge = drm_bridge_get(bridge);
}
if (local_bridge) {
@@ -59,7 +61,7 @@ int omapdss_device_init_output(struct omap_dss_device *out,
}
out->next_bridge = out->bridge;
- out->bridge = local_bridge;
+ out->bridge = drm_bridge_get(local_bridge);
}
if (!out->bridge) {
@@ -79,6 +81,9 @@ void omapdss_device_cleanup_output(struct omap_dss_device *out)
if (out->bridge && out->panel)
drm_panel_bridge_remove(out->next_bridge ?
out->next_bridge : out->bridge);
+
+ drm_bridge_put(out->next_bridge);
+ drm_bridge_put(out->bridge);
}
void dss_mgr_set_timings(struct omap_dss_device *dssdev,
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index d6863b28ddc5..979109c27b9b 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -144,6 +144,18 @@ config DRM_PANEL_FEIYANG_FY07024DI26A30D
Say Y if you want to enable support for panels based on the
Feiyang FY07024DI26A30-D MIPI-DSI interface.
+config DRM_PANEL_FOCALTECH_OTA7290B
+ tristate "Focaltech OTA7290B"
+ depends on DRM_MIPI_DSI
+ depends on I2C
+ depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_KMS_HELPER
+ help
+ Enable support for panels using OTA7290B as a controller (for
+ example, Waveshare 12.3" DSI TOUCH-A panel). Say Y here if you want
+ to enable support for this panel. To compile this driver as a module,
+ choose M here.
+
config DRM_PANEL_DSI_CM
tristate "Generic DSI command mode panels"
depends on OF
@@ -337,6 +349,7 @@ config DRM_PANEL_JADARD_JD9365DA_H3
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
+ select DRM_KMS_HELPER
help
Say Y here if you want to enable support for Jadard JD9365DA-H3
WXGA MIPI DSI panel. The panel support TFT dot matrix LCD with
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index a4291dc3905b..0d694acbfbb6 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_DRM_PANEL_EBBG_FT8719) += panel-ebbg-ft8719.o
obj-$(CONFIG_DRM_PANEL_ELIDA_KD35T133) += panel-elida-kd35t133.o
obj-$(CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02) += panel-feixin-k101-im2ba02.o
obj-$(CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D) += panel-feiyang-fy07024di26a30d.o
+obj-$(CONFIG_DRM_PANEL_FOCALTECH_OTA7290B) += panel-focaltech-ota7290b.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX8279) += panel-himax-hx8279.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83102) += panel-himax-hx83102.o
obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112A) += panel-himax-hx83112a.o
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 497dcd48f57b..f3cdaea89b29 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -2072,6 +2072,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1100, &delay_200_500_e80_d50, "MNB601LS1-1"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1103, &delay_200_500_e80_d50, "MNB601LS1-3"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1104, &delay_200_500_e50_d100, "MNB601LS1-4"),
+ EDP_PANEL_ENTRY('C', 'S', 'W', 0x110a, &delay_200_500_e50, "PNB601LS1-2"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x143f, &delay_200_500_e50, "MNE007QS3-6"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x1448, &delay_200_500_e50, "MNE007QS3-7"),
EDP_PANEL_ENTRY('C', 'S', 'W', 0x144b, &delay_200_500_e80, "MNE001BS1-4"),
@@ -2119,6 +2120,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05af, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05f1, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0778, &delay_200_500_e200_d200, "134WT1"),
+ EDP_PANEL_ENTRY('L', 'G', 'D', 0x07fe, &delay_200_500_e200_d200, "LP116WHA-SPB1"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1511, &delay_200_500_e50, "LQ140M1JW48"),
EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &delay_80_500_e50, "LQ140M1JW46"),
diff --git a/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c b/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c
new file mode 100644
index 000000000000..dd420bb19440
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-focaltech-ota7290b.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024 Waveshare International Limited
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+struct ota7290b {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+
+ struct regulator *power;
+ struct gpio_desc *reset;
+ struct regulator *avdd;
+ struct regulator *vdd;
+ struct regulator *vcc;
+
+ enum drm_panel_orientation orientation;
+};
+
+static inline struct ota7290b *panel_to_ota(struct drm_panel *panel)
+{
+ return container_of(panel, struct ota7290b, panel);
+}
+
+static int ota7290b_prepare(struct drm_panel *panel)
+{
+ struct ota7290b *ctx = panel_to_ota(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+ int ret;
+
+ if (ctx->vcc) {
+ ret = regulator_enable(ctx->vcc);
+ if (ret)
+ dev_err(panel->dev, "failed to enable VCC regulator: %d\n", ret);
+ }
+
+ if (ctx->reset) {
+ gpiod_set_value_cansleep(ctx->reset, 0);
+ msleep(60);
+ gpiod_set_value_cansleep(ctx->reset, 1);
+ msleep(60);
+ }
+
+ if (ctx->vdd) {
+ ret = regulator_enable(ctx->vdd);
+ if (ret)
+ dev_err(panel->dev, "failed to enable VDD regulator: %d\n", ret);
+ }
+
+ if (ctx->reset) {
+ gpiod_set_value_cansleep(ctx->reset, 0);
+ msleep(60);
+ }
+
+ if (ctx->avdd) {
+ ret = regulator_enable(ctx->avdd);
+ if (ret)
+ dev_err(panel->dev, "failed to enable AVDD regulator: %d\n", ret);
+ }
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_msleep(&dsi_ctx, 50);
+
+ if (dsi_ctx.accum_err < 0)
+ dev_err(panel->dev, "failed to init panel: %d\n", dsi_ctx.accum_err);
+
+ return dsi_ctx.accum_err;
+}
+
+static int ota7290b_unprepare(struct drm_panel *panel)
+{
+ struct ota7290b *ctx = panel_to_ota(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+
+ if (ctx->avdd)
+ regulator_disable(ctx->avdd);
+
+ if (ctx->reset) {
+ gpiod_set_value_cansleep(ctx->reset, 1);
+ msleep(5);
+ }
+
+ if (ctx->vdd)
+ regulator_disable(ctx->vdd);
+
+ if (ctx->vcc)
+ regulator_disable(ctx->vcc);
+
+ return 0;
+}
+
+static const struct drm_display_mode waveshare_dsi_touch_8_8_a_mode = {
+ .clock = 75000,
+
+ .hdisplay = 480,
+ .hsync_start = 480 + 50,
+ .hsync_end = 480 + 50 + 50,
+ .htotal = 480 + 50 + 50 + 50,
+
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 20,
+ .vsync_end = 1920 + 20 + 20,
+ .vtotal = 1920 + 20 + 20 + 20,
+
+ .width_mm = 68,
+ .height_mm = 219,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+static int ota7290b_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &waveshare_dsi_touch_8_8_a_mode);
+}
+
+static enum drm_panel_orientation ota7290b_get_orientation(struct drm_panel *panel)
+{
+ struct ota7290b *ctx = panel_to_ota(panel);
+
+ return ctx->orientation;
+}
+
+static const struct drm_panel_funcs ota7290b_funcs = {
+ .prepare = ota7290b_prepare,
+ .unprepare = ota7290b_unprepare,
+ .get_modes = ota7290b_get_modes,
+ .get_orientation = ota7290b_get_orientation,
+};
+
+static int ota7290b_probe(struct mipi_dsi_device *dsi)
+{
+ struct ota7290b *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(&dsi->dev, struct ota7290b, panel,
+ &ota7290b_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (!ctx)
+ return -ENOMEM;
+ mipi_dsi_set_drvdata(dsi, ctx);
+ ctx->dsi = dsi;
+
+ ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset))
+ return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
+ "Couldn't get our reset GPIO\n");
+
+ ctx->vcc = devm_regulator_get_optional(&dsi->dev, "vcc");
+ if (IS_ERR(ctx->vcc))
+ return dev_err_probe(&dsi->dev, PTR_ERR(ctx->vcc),
+ "Couldn't get our VCC supply\n");
+
+ ctx->avdd = devm_regulator_get_optional(&dsi->dev, "avdd");
+ if (IS_ERR(ctx->avdd))
+ return dev_err_probe(&dsi->dev, PTR_ERR(ctx->avdd),
+ "Couldn't get our AVDD supply\n");
+
+ ctx->vdd = devm_regulator_get_optional(&dsi->dev, "vdd");
+ if (IS_ERR(ctx->vdd))
+ return dev_err_probe(&dsi->dev, PTR_ERR(ctx->vdd),
+ "Couldn't get our VDD supply\n");
+
+ ret = of_drm_get_panel_orientation(dsi->dev.of_node, &ctx->orientation);
+ if (ret) {
+ dev_err(&dsi->dev, "%pOF: failed to get orientation: %d\n",
+ dsi->dev.of_node, ret);
+ return ret;
+ }
+
+ ret = drm_panel_of_backlight(&ctx->panel);
+ if (ret)
+ return ret;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ret = devm_drm_panel_add(&dsi->dev, &ctx->panel);
+ if (ret)
+ return ret;
+
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ dsi->format = MIPI_DSI_FMT_RGB888,
+ dsi->lanes = 2;
+
+ return devm_mipi_dsi_attach(&dsi->dev, dsi);
+}
+
+static const struct of_device_id ota7290b_of_match[] = {
+ { .compatible = "waveshare,8.8-dsi-touch-a", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ota7290b_of_match);
+
+static struct mipi_dsi_driver ota7290b_driver = {
+ .probe = ota7290b_probe,
+ .driver = {
+ .name = "focaltech-ota7290b",
+ .of_match_table = ota7290b_of_match,
+ },
+};
+module_mipi_dsi_driver(ota7290b_driver);
+
+MODULE_DESCRIPTION("Panel driver for Focaltech OTA7290B panels");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/panel/panel-himax-hx83102.c b/drivers/gpu/drm/panel/panel-himax-hx83102.c
index 8b2a68ee851e..eab67893da86 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx83102.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx83102.c
@@ -29,11 +29,14 @@
#define HX83102_UNKNOWN_B8 0xb8
#define HX83102_SETEXTC 0xb9
#define HX83102_SETMIPI 0xba
+#define HX83102_UNKNOWN_BB 0xbb
#define HX83102_SETVDC 0xbc
#define HX83102_SETBANK 0xbd
#define HX83102_UNKNOWN_BE 0xbe
#define HX83102_SETPTBA 0xbf
#define HX83102_SETSTBA 0xc0
+#define HX83102_UNKNOWN_C2 0xc2
+#define HX83102_UNKNOWN_C6 0xc6
#define HX83102_SETTCON 0xc7
#define HX83102_SETRAMDMY 0xc8
#define HX83102_SETPWM 0xc9
@@ -78,6 +81,7 @@ struct hx83102_panel_desc {
} size;
bool has_backlight;
+ unsigned long mode_flags;
int (*init)(struct hx83102 *ctx);
};
@@ -765,6 +769,111 @@ static int holitech_htf065h045_init(struct hx83102 *ctx)
return dsi_ctx.accum_err;
}
+/* This is HX83102-E, assuming commands are the same as the normal HX83102 */
+static int waveshare_12_3_a_init(struct hx83102 *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETEXTC, 0x83, 0x10, 0x2e);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETSPCCMD, 0xcd);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_BB, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETSPCCMD, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETPCTRL, 0x67, 0x2c, 0xff, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_BE, 0x11, 0x96, 0x89);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_D9, 0x04, 0x03, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETPOWER,
+ 0x10, 0xfa, 0xaf, 0xaf, 0x33, 0x33, 0xb1, 0x4d, 0x2f, 0x36,
+ 0x36, 0x36, 0x36, 0x22, 0x21, 0x15, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETDISP,
+ 0x00, 0xd0, 0x27, 0x80, 0x00, 0x14, 0x40, 0x2c, 0x32, 0x02,
+ 0x00, 0x00, 0x15, 0x20, 0xd7, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETCYC,
+ 0x98, 0xa0, 0x01, 0x01, 0x98, 0xa0, 0x68, 0x50, 0x01, 0xc7,
+ 0x01, 0x58, 0x00, 0xff, 0x00, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_B6, 0x4d, 0x4d, 0xe3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETPTBA, 0xfc, 0x85, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_D2, 0x33, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP0,
+ 0x00, 0x00, 0x00, 0x00, 0x64, 0x04, 0x00, 0x08, 0x08, 0x27,
+ 0x27, 0x22, 0x2f, 0x15, 0x15, 0x04, 0x04, 0x32, 0x10, 0x13,
+ 0x00, 0x13, 0x32, 0x10, 0x1f, 0x00,
+ 0x02, 0x32, 0x17, 0xfd, 0x00, 0x10, 0x00, 0x00, 0x20,
+ 0x30, 0x01, 0x55, 0x21, 0x38, 0x01, 0x55, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGMA,
+ 0x00, 0x0c, 0x1a, 0x23, 0x2b, 0x4f, 0x64, 0x69, 0x6c, 0x64,
+ 0x77, 0x77, 0x76, 0x80, 0x79, 0x7e, 0x85, 0x9a, 0x97, 0x4d,
+ 0x56, 0x64, 0x70, 0x00, 0x0c, 0x1a, 0x23, 0x2b, 0x4f, 0x64,
+ 0x69, 0x6c, 0x64, 0x77, 0x77, 0x76, 0x80, 0x79, 0x7e, 0x85,
+ 0x9a, 0x97, 0x4d, 0x56, 0x64, 0x76);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETPOWER, 0x01, 0x9b, 0x01, 0x31);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETCLOCK,
+ 0x80, 0x36, 0x12, 0x16, 0xc0, 0x28, 0x40, 0x84, 0x22);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP0,
+ 0x01, 0x00, 0xfc, 0x00, 0x00, 0x11, 0x10, 0x00, 0x0e, 0x00,
+ 0x01);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETCYC, 0x4e, 0x00, 0x33, 0x11, 0x33, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETPTBA, 0xf2, 0x00, 0x02);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETSTBA,
+ 0x23, 0x23, 0x22, 0x11, 0xa2, 0x17, 0x00, 0x80, 0x00, 0x00,
+ 0x08, 0x00, 0x63, 0x63);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_C6, 0xf9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETTCON, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETRAMDMY,
+ 0x00, 0x04, 0x04, 0x00, 0x00, 0x82, 0x13, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETCASCADE, 0x07, 0x04, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP1,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x21, 0x20, 0x21, 0x20,
+ 0x01, 0x00, 0x03, 0x02, 0x05, 0x04, 0x07, 0x06, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x9a, 0x9a, 0x9a, 0x9a, 0x18, 0x18, 0x18, 0x18,
+ 0x21, 0x20, 0x21, 0x20, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP2,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x20, 0x21, 0x20, 0x21,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x1a, 0x18, 0x18, 0x18, 0x18,
+ 0x20, 0x21, 0x20, 0x21, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x98, 0x98, 0x98, 0x98);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETTP1,
+ 0x00, 0x34, 0x01, 0x88, 0x0e, 0xbe, 0x0f);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_UNKNOWN_C2, 0x43, 0xff, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETPANEL, 0x02);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETDISP, 0x80);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP3,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0x80, 0x2a, 0xaa, 0xaa, 0xaa, 0xaa, 0x80,
+ 0x2a, 0xaa, 0xaa, 0xaa);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP3,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETGIP3,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf0, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf0);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, HX83102_SETBANK, 0x00);
+
+ return dsi_ctx.accum_err;
+};
+
static const struct drm_display_mode starry_mode = {
.clock = 162680,
.hdisplay = 1200,
@@ -920,6 +1029,30 @@ static const struct hx83102_panel_desc holitech_htf065h045_desc = {
.init = holitech_htf065h045_init,
};
+static const struct drm_display_mode waveshare_12_3_a_mode = {
+ .clock = 95000,
+ .hdisplay = 720,
+ .hsync_start = 720 + 10,
+ .hsync_end = 720 + 10 + 10,
+ .htotal = 720 + 10 + 10 + 12,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 64,
+ .vsync_end = 1920 + 64 + 18,
+ .vtotal = 1920 + 64 + 18 + 4,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct hx83102_panel_desc waveshare_12_3_inch_a_desc = {
+ .modes = &waveshare_12_3_a_mode,
+ .size = {
+ .width_mm = 109,
+ .height_mm = 292,
+ },
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .init = waveshare_12_3_a_init,
+};
+
static int hx83102_enable(struct drm_panel *panel)
{
msleep(130);
@@ -1168,8 +1301,12 @@ static int hx83102_probe(struct mipi_dsi_device *dsi)
desc = of_device_get_match_data(&dsi->dev);
dsi->lanes = 4;
dsi->format = MIPI_DSI_FMT_RGB888;
- dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
- MIPI_DSI_MODE_LPM;
+ if (desc->mode_flags)
+ dsi->mode_flags = desc->mode_flags;
+ else
+ dsi->mode_flags = MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+ MIPI_DSI_MODE_LPM;
ctx->desc = desc;
ctx->dsi = dsi;
ret = hx83102_panel_add(ctx);
@@ -1220,6 +1357,9 @@ static const struct of_device_id hx83102_of_match[] = {
{ .compatible = "holitech,htf065h045",
.data = &holitech_htf065h045_desc
},
+ { .compatible = "waveshare,12.3-dsi-touch-a",
+ .data = &waveshare_12_3_inch_a_desc
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hx83102_of_match);
diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c
index c4d3e09a228d..bf80354567df 100644
--- a/drivers/gpu/drm/panel/panel-himax-hx8394.c
+++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c
@@ -44,6 +44,7 @@
#define HX8394_CMD_SETID 0xc3
#define HX8394_CMD_SETDDB 0xc4
#define HX8394_CMD_UNKNOWN2 0xc6
+#define HX8394_CMD_UNKNOWN6 0xc7
#define HX8394_CMD_SETCABC 0xc9
#define HX8394_CMD_SETCABCGAIN 0xca
#define HX8394_CMD_SETPANEL 0xcc
@@ -618,38 +619,246 @@ static const struct hx8394_panel_desc hl055fhav028c_desc = {
.init_sequence = hl055fhav028c_init_sequence,
};
-static int hx8394_enable(struct drm_panel *panel)
+static void waveshare_5_0_inch_a_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
{
- struct hx8394 *ctx = panel_to_hx8394(panel);
- struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
- struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
- int ret;
+ /* 5.19.8 SETEXTC: Set extension command (B9h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC,
+ 0xff, 0x83, 0x94);
- ctx->desc->init_sequence(&dsi_ctx);
+ /* 5.19.2 SETPOWER: Set power (B1h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
+ 0x48, 0x0a, 0x6a, 0x09, 0x33, 0x54, 0x71, 0x71, 0x2e, 0x45);
- mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ /* 5.19.9 SETMIPI: Set MIPI control (BAh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETMIPI,
+ 0x61, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
- if (dsi_ctx.accum_err)
- return dsi_ctx.accum_err;
- /* Panel is operational 120 msec after reset */
- msleep(120);
+ /* 5.19.3 SETDISP: Set display related register (B2h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP,
+ 0x00, 0x80, 0x64, 0x0c, 0x06, 0x2f);
- mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
- if (dsi_ctx.accum_err)
- goto sleep_in;
+ /* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC,
+ 0x1c, 0x78, 0x1c, 0x78, 0x1c, 0x78, 0x01, 0x0c, 0x86, 0x75,
+ 0x00, 0x3f, 0x1c, 0x78, 0x1c, 0x78, 0x1c, 0x78, 0x01, 0x0c,
+ 0x86);
- return 0;
+ /* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x32, 0x10,
+ 0x05, 0x00, 0x05, 0x32, 0x13, 0xc1, 0x00, 0x01, 0x32, 0x10,
+ 0x08, 0x00, 0x00, 0x37, 0x03, 0x07, 0x07, 0x37, 0x05, 0x05,
+ 0x37, 0x0c, 0x40);
-sleep_in:
- ret = dsi_ctx.accum_err;
- dsi_ctx.accum_err = 0;
+ /* 5.19.20 Set GIP Option1 (D5h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1,
+ 0x18, 0x18, 0x18, 0x18, 0x22, 0x23, 0x20, 0x21, 0x04, 0x05,
+ 0x06, 0x07, 0x00, 0x01, 0x02, 0x03, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x19, 0x19, 0x19, 0x19);
- /* This will probably fail, but let's try orderly power off anyway. */
- mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
- mipi_dsi_msleep(&dsi_ctx, 50);
+ /* 5.19.21 Set GIP Option2 (D6h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2,
+ 0x18, 0x18, 0x19, 0x19, 0x21, 0x20, 0x23, 0x22, 0x03, 0x02,
+ 0x01, 0x00, 0x07, 0x06, 0x05, 0x04, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x19, 0x19, 0x18, 0x18);
- return ret;
-}
+ /* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA,
+ 0x07, 0x08, 0x09, 0x0d, 0x10, 0x14, 0x16, 0x13, 0x24, 0x36,
+ 0x48, 0x4a, 0x58, 0x6f, 0x76, 0x80, 0x97, 0xa5, 0xa8, 0xb5,
+ 0xc6, 0x62, 0x63, 0x68, 0x6f, 0x72, 0x78, 0x7f, 0x7f, 0x00,
+ 0x02, 0x08, 0x0d, 0x0c, 0x0e, 0x0f, 0x10, 0x24, 0x36, 0x48,
+ 0x4a, 0x58, 0x6f, 0x78, 0x82, 0x99, 0xa4, 0xa0, 0xb1, 0xc0,
+ 0x5e, 0x5e, 0x64, 0x6b, 0x6c, 0x73, 0x7f, 0x7f);
+
+ /* 5.19.17 SETPANEL (CCh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL,
+ 0x0b);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN1,
+ 0x1f, 0x73);
+
+ /* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM,
+ 0x6b, 0x6b);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN3,
+ 0x02);
+
+ /* 5.19.11 Set register bank (BDh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
+ 0x01);
+
+ /* 5.19.2 SETPOWER: Set power (B1h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
+ 0x00);
+
+ /* 5.19.11 Set register bank (BDh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
+ 0x00);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN5,
+ 0x40, 0x81, 0x50, 0x00, 0x1a, 0xfc, 0x01);
+};
+
+static const struct drm_display_mode waveshare_5_0_inch_a_mode = {
+ .clock = 70000,
+ .hdisplay = 720,
+ .hsync_start = 720 + 40,
+ .hsync_end = 720 + 40 + 20,
+ .htotal = 720 + 40 + 20 + 20,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 30,
+ .vsync_end = 1280 + 30 + 10,
+ .vtotal = 1280 + 30 + 10 + 4,
+ .width_mm = 62,
+ .height_mm = 110,
+};
+
+static const struct hx8394_panel_desc waveshare_5_0_inch_a_desc = {
+ .mode = &waveshare_5_0_inch_a_mode,
+ .lanes = 2,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init_sequence = waveshare_5_0_inch_a_init_sequence,
+};
+
+static const struct drm_display_mode waveshare_5_5_inch_a_mode = {
+ .clock = 65000,
+ .hdisplay = 720,
+ .hsync_start = 720 + 50,
+ .hsync_end = 720 + 50 + 50,
+ .htotal = 720 + 50 + 50 + 10,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 15,
+ .vsync_end = 1280 + 15 + 12,
+ .vtotal = 1280 + 15 + 12 + 4,
+ .width_mm = 62,
+ .height_mm = 110,
+};
+
+static void waveshare_5_5_inch_a_init_sequence(struct mipi_dsi_multi_context *dsi_ctx)
+{
+ /* 5.19.8 SETEXTC: Set extension command (B9h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETEXTC,
+ 0xff, 0x83, 0x94);
+
+ /* 5.19.9 SETMIPI: Set MIPI control (BAh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETMIPI,
+ 0x61, 0x03, 0x68, 0x6b, 0xb2, 0xc0);
+
+ /* 5.19.2 SETPOWER: Set power (B1h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
+ 0x48, 0x12, 0x72, 0x09, 0x32, 0x54, 0x71, 0x71, 0x57, 0x47);
+
+ /* 5.19.3 SETDISP: Set display related register (B2h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETDISP,
+ 0x00, 0x80, 0x64, 0x0c, 0x0d, 0x2f);
+
+ /* 5.19.4 SETCYC: Set display waveform cycles (B4h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETCYC,
+ 0x73, 0x74, 0x73, 0x74, 0x73, 0x74, 0x01, 0x0c, 0x86, 0x75,
+ 0x00, 0x3f, 0x73, 0x74, 0x73, 0x74, 0x73, 0x74, 0x01, 0x0c,
+ 0x86);
+
+ /* 5.19.5 SETVCOM: Set VCOM voltage (B6h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETVCOM,
+ 0x86, 0x86);
+
+ /* 5.19.19 SETGIP0: Set GIP Option0 (D3h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP0,
+ 0x00, 0x00, 0x07, 0x07, 0x40, 0x07, 0x0c, 0x00, 0x08, 0x10,
+ 0x08, 0x00, 0x08, 0x54, 0x15, 0x0a, 0x05, 0x0a, 0x02, 0x15,
+ 0x06, 0x05, 0x06, 0x47, 0x44, 0x0a, 0x0a, 0x4b, 0x10, 0x07,
+ 0x07, 0x0c, 0x40);
+
+ /* 5.19.20 Set GIP Option1 (D5h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP1,
+ 0x1c, 0x1c, 0x1d, 0x1d, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x24, 0x25, 0x18, 0x18,
+ 0x26, 0x27, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x20, 0x21,
+ 0x18, 0x18, 0x18, 0x18);
+
+ /* 5.19.21 Set GIP Option2 (D6h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGIP2,
+ 0x1c, 0x1c, 0x1d, 0x1d, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
+ 0x01, 0x00, 0x0b, 0x0a, 0x09, 0x08, 0x21, 0x20, 0x18, 0x18,
+ 0x27, 0x26, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x25, 0x24,
+ 0x18, 0x18, 0x18, 0x18);
+
+ /* 5.19.25 SETGAMMA: Set gamma curve related setting (E0h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETGAMMA,
+ 0x00, 0x13, 0x21, 0x28, 0x2b, 0x2e, 0x32, 0x2f, 0x61, 0x6e,
+ 0x7e, 0x7b, 0x80, 0x8f, 0x91, 0x93, 0x9d, 0x9d, 0x97, 0xa4,
+ 0xb1, 0x57, 0x55, 0x58, 0x5d, 0x60, 0x67, 0x74, 0x7f, 0x00,
+ 0x13, 0x21, 0x28, 0x2b, 0x2e, 0x32, 0x2f, 0x61, 0x6e, 0x7d,
+ 0x7b, 0x7f, 0x8e, 0x90, 0x93, 0x9c, 0x9d, 0x98, 0xa4, 0xb1,
+ 0x58, 0x55, 0x59, 0x5e, 0x61, 0x68, 0x76, 0x7f);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN1,
+ 0x1f, 0x31);
+
+ /* 5.19.17 SETPANEL (CCh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPANEL,
+ 0x07);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN3,
+ 0x02);
+
+ /* 5.19.11 Set register bank (BDh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
+ 0x02);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN4,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff);
+
+ /* 5.19.11 Set register bank (BDh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
+ 0x00);
+
+ /* 5.19.11 Set register bank (BDh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
+ 0x01);
+
+ /* 5.19.2 SETPOWER: Set power (B1h) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETPOWER,
+ 0x00);
+
+ /* 5.19.11 Set register bank (BDh) */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_SETREGBANK,
+ 0x00);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN2,
+ 0xed);
+
+ /* Unknown command, not listed in the HX8394-F datasheet */
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, HX8394_CMD_UNKNOWN6,
+ 0x00, 0xc0);
+};
+
+static const struct hx8394_panel_desc waveshare_5_5_inch_a_desc = {
+ .mode = &waveshare_5_5_inch_a_mode,
+ .lanes = 2,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init_sequence = waveshare_5_5_inch_a_init_sequence,
+};
static int hx8394_disable(struct drm_panel *panel)
{
@@ -663,6 +872,26 @@ static int hx8394_disable(struct drm_panel *panel)
return dsi_ctx.accum_err;
}
+static int hx8394_enable(struct drm_panel *panel)
+{
+ struct hx8394 *ctx = panel_to_hx8394(panel);
+ struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
+
+ ctx->desc->init_sequence(&dsi_ctx);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+
+ /* Panel is operational 120 msec after reset */
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ if (dsi_ctx.accum_err)
+ hx8394_disable(panel);
+
+ return dsi_ctx.accum_err;
+}
+
static int hx8394_unprepare(struct drm_panel *panel)
{
struct hx8394 *ctx = panel_to_hx8394(panel);
@@ -792,6 +1021,8 @@ static int hx8394_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
+ ctx->panel.prepare_prev_first = true;
+
drm_panel_add(&ctx->panel);
ret = mipi_dsi_attach(dsi);
@@ -826,6 +1057,8 @@ static const struct of_device_id hx8394_of_match[] = {
{ .compatible = "huiling,hl055fhav028c", .data = &hl055fhav028c_desc },
{ .compatible = "powkiddy,x55-panel", .data = &powkiddy_x55_desc },
{ .compatible = "microchip,ac40t08a-mipi-panel", .data = &mchp_ac40t08a_desc },
+ { .compatible = "waveshare,5.0-dsi-touch-a", .data = &waveshare_5_0_inch_a_desc },
+ { .compatible = "waveshare,5.5-dsi-touch-a", .data = &waveshare_5_5_inch_a_desc },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, hx8394_of_match);
diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
index 947b47841b01..0652cdb57d11 100644
--- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
+++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
@@ -52,6 +52,7 @@ struct ili9881c {
const struct ili9881c_desc *desc;
struct regulator *power;
+ struct regulator *iovcc;
struct gpio_desc *reset;
enum drm_panel_orientation orientation;
@@ -1997,6 +1998,205 @@ static const struct ili9881c_instr bsd1218_a101kl68_init[] = {
ILI9881C_COMMAND_INSTR(0xd3, 0x3f),
};
+static const struct ili9881c_instr waveshare_7inch_a_init[] = {
+ ILI9881C_SWITCH_PAGE_INSTR(3),
+ ILI9881C_COMMAND_INSTR(0x01, 0x00),
+ ILI9881C_COMMAND_INSTR(0x02, 0x00),
+ ILI9881C_COMMAND_INSTR(0x03, 0x73),
+ ILI9881C_COMMAND_INSTR(0x04, 0x00),
+ ILI9881C_COMMAND_INSTR(0x05, 0x00),
+ ILI9881C_COMMAND_INSTR(0x06, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x07, 0x00),
+ ILI9881C_COMMAND_INSTR(0x08, 0x00),
+ ILI9881C_COMMAND_INSTR(0x09, 0x61),
+ ILI9881C_COMMAND_INSTR(0x0a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0c, 0x01),
+ ILI9881C_COMMAND_INSTR(0x0d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x0f, 0x61),
+ ILI9881C_COMMAND_INSTR(0x10, 0x61),
+ ILI9881C_COMMAND_INSTR(0x11, 0x00),
+ ILI9881C_COMMAND_INSTR(0x12, 0x00),
+ ILI9881C_COMMAND_INSTR(0x13, 0x00),
+ ILI9881C_COMMAND_INSTR(0x14, 0x00),
+ ILI9881C_COMMAND_INSTR(0x15, 0x00),
+ ILI9881C_COMMAND_INSTR(0x16, 0x00),
+ ILI9881C_COMMAND_INSTR(0x17, 0x00),
+ ILI9881C_COMMAND_INSTR(0x18, 0x00),
+ ILI9881C_COMMAND_INSTR(0x19, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x1e, 0x40),
+ ILI9881C_COMMAND_INSTR(0x1f, 0x80),
+ ILI9881C_COMMAND_INSTR(0x20, 0x06),
+ ILI9881C_COMMAND_INSTR(0x21, 0x01),
+ ILI9881C_COMMAND_INSTR(0x22, 0x00),
+ ILI9881C_COMMAND_INSTR(0x23, 0x00),
+ ILI9881C_COMMAND_INSTR(0x24, 0x00),
+ ILI9881C_COMMAND_INSTR(0x25, 0x00),
+ ILI9881C_COMMAND_INSTR(0x26, 0x00),
+ ILI9881C_COMMAND_INSTR(0x27, 0x00),
+ ILI9881C_COMMAND_INSTR(0x28, 0x33),
+ ILI9881C_COMMAND_INSTR(0x29, 0x03),
+ ILI9881C_COMMAND_INSTR(0x2a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x2f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x30, 0x00),
+ ILI9881C_COMMAND_INSTR(0x31, 0x00),
+ ILI9881C_COMMAND_INSTR(0x32, 0x00),
+ ILI9881C_COMMAND_INSTR(0x33, 0x00),
+ ILI9881C_COMMAND_INSTR(0x34, 0x04),
+ ILI9881C_COMMAND_INSTR(0x35, 0x00),
+ ILI9881C_COMMAND_INSTR(0x36, 0x00),
+ ILI9881C_COMMAND_INSTR(0x37, 0x00),
+ ILI9881C_COMMAND_INSTR(0x38, 0x3c),
+ ILI9881C_COMMAND_INSTR(0x39, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3c, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3d, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x3f, 0x00),
+ ILI9881C_COMMAND_INSTR(0x40, 0x00),
+ ILI9881C_COMMAND_INSTR(0x41, 0x00),
+ ILI9881C_COMMAND_INSTR(0x42, 0x00),
+ ILI9881C_COMMAND_INSTR(0x43, 0x00),
+ ILI9881C_COMMAND_INSTR(0x44, 0x00),
+ ILI9881C_COMMAND_INSTR(0x50, 0x10),
+ ILI9881C_COMMAND_INSTR(0x51, 0x32),
+ ILI9881C_COMMAND_INSTR(0x52, 0x54),
+ ILI9881C_COMMAND_INSTR(0x53, 0x76),
+ ILI9881C_COMMAND_INSTR(0x54, 0x98),
+ ILI9881C_COMMAND_INSTR(0x55, 0xba),
+ ILI9881C_COMMAND_INSTR(0x56, 0x10),
+ ILI9881C_COMMAND_INSTR(0x57, 0x32),
+ ILI9881C_COMMAND_INSTR(0x58, 0x54),
+ ILI9881C_COMMAND_INSTR(0x59, 0x76),
+ ILI9881C_COMMAND_INSTR(0x5a, 0x98),
+ ILI9881C_COMMAND_INSTR(0x5b, 0xba),
+ ILI9881C_COMMAND_INSTR(0x5c, 0xdc),
+ ILI9881C_COMMAND_INSTR(0x5d, 0xfe),
+ ILI9881C_COMMAND_INSTR(0x5e, 0x00),
+ ILI9881C_COMMAND_INSTR(0x5f, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x60, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x61, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x62, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x63, 0x06),
+ ILI9881C_COMMAND_INSTR(0x64, 0x07),
+ ILI9881C_COMMAND_INSTR(0x65, 0x02),
+ ILI9881C_COMMAND_INSTR(0x66, 0x02),
+ ILI9881C_COMMAND_INSTR(0x67, 0x02),
+ ILI9881C_COMMAND_INSTR(0x68, 0x02),
+ ILI9881C_COMMAND_INSTR(0x69, 0x01),
+ ILI9881C_COMMAND_INSTR(0x6a, 0x00),
+ ILI9881C_COMMAND_INSTR(0x6b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15),
+ ILI9881C_COMMAND_INSTR(0x6d, 0x14),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x02),
+ ILI9881C_COMMAND_INSTR(0x70, 0x02),
+ ILI9881C_COMMAND_INSTR(0x71, 0x02),
+ ILI9881C_COMMAND_INSTR(0x72, 0x02),
+ ILI9881C_COMMAND_INSTR(0x73, 0x02),
+ ILI9881C_COMMAND_INSTR(0x74, 0x02),
+ ILI9881C_COMMAND_INSTR(0x75, 0x0e),
+ ILI9881C_COMMAND_INSTR(0x76, 0x0f),
+ ILI9881C_COMMAND_INSTR(0x77, 0x0c),
+ ILI9881C_COMMAND_INSTR(0x78, 0x0d),
+ ILI9881C_COMMAND_INSTR(0x79, 0x06),
+ ILI9881C_COMMAND_INSTR(0x7a, 0x07),
+ ILI9881C_COMMAND_INSTR(0x7b, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7c, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7d, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7e, 0x02),
+ ILI9881C_COMMAND_INSTR(0x7f, 0x01),
+ ILI9881C_COMMAND_INSTR(0x80, 0x00),
+ ILI9881C_COMMAND_INSTR(0x81, 0x02),
+ ILI9881C_COMMAND_INSTR(0x82, 0x14),
+ ILI9881C_COMMAND_INSTR(0x83, 0x15),
+ ILI9881C_COMMAND_INSTR(0x84, 0x02),
+ ILI9881C_COMMAND_INSTR(0x85, 0x02),
+ ILI9881C_COMMAND_INSTR(0x86, 0x02),
+ ILI9881C_COMMAND_INSTR(0x87, 0x02),
+ ILI9881C_COMMAND_INSTR(0x88, 0x02),
+ ILI9881C_COMMAND_INSTR(0x89, 0x02),
+ ILI9881C_COMMAND_INSTR(0x8a, 0x02),
+
+ ILI9881C_SWITCH_PAGE_INSTR(4),
+ ILI9881C_COMMAND_INSTR(0x38, 0x01),
+ ILI9881C_COMMAND_INSTR(0x39, 0x00),
+ ILI9881C_COMMAND_INSTR(0x6c, 0x15),
+ ILI9881C_COMMAND_INSTR(0x6e, 0x2a),
+ ILI9881C_COMMAND_INSTR(0x6f, 0x33),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x94),
+ ILI9881C_COMMAND_INSTR(0x8d, 0x14),
+ ILI9881C_COMMAND_INSTR(0x87, 0xba),
+ ILI9881C_COMMAND_INSTR(0x26, 0x76),
+ ILI9881C_COMMAND_INSTR(0xb2, 0xd1),
+ ILI9881C_COMMAND_INSTR(0xb5, 0x06),
+ ILI9881C_COMMAND_INSTR(0x3b, 0x98),
+
+ ILI9881C_SWITCH_PAGE_INSTR(1),
+ ILI9881C_COMMAND_INSTR(0x22, 0x0a),
+ ILI9881C_COMMAND_INSTR(0x31, 0x00),
+ ILI9881C_COMMAND_INSTR(0x53, 0x71),
+ ILI9881C_COMMAND_INSTR(0x55, 0x8f),
+ ILI9881C_COMMAND_INSTR(0x40, 0x33),
+ ILI9881C_COMMAND_INSTR(0x50, 0x96),
+ ILI9881C_COMMAND_INSTR(0x51, 0x96),
+ ILI9881C_COMMAND_INSTR(0x60, 0x23),
+ ILI9881C_COMMAND_INSTR(0xa0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xa1, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa2, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xa3, 0x10),
+ ILI9881C_COMMAND_INSTR(0xa4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xa5, 0x28),
+ ILI9881C_COMMAND_INSTR(0xa6, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xa7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xa8, 0x7e),
+ ILI9881C_COMMAND_INSTR(0xa9, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xaa, 0x29),
+ ILI9881C_COMMAND_INSTR(0xab, 0x6b),
+ ILI9881C_COMMAND_INSTR(0xac, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xad, 0x18),
+ ILI9881C_COMMAND_INSTR(0xae, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xaf, 0x20),
+ ILI9881C_COMMAND_INSTR(0xb0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xb1, 0x50),
+ ILI9881C_COMMAND_INSTR(0xb2, 0x64),
+ ILI9881C_COMMAND_INSTR(0xb3, 0x39),
+ ILI9881C_COMMAND_INSTR(0xc0, 0x08),
+ ILI9881C_COMMAND_INSTR(0xc1, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc2, 0x2a),
+ ILI9881C_COMMAND_INSTR(0xc3, 0x10),
+ ILI9881C_COMMAND_INSTR(0xc4, 0x15),
+ ILI9881C_COMMAND_INSTR(0xc5, 0x28),
+ ILI9881C_COMMAND_INSTR(0xc6, 0x1c),
+ ILI9881C_COMMAND_INSTR(0xc7, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xc8, 0x7e),
+ ILI9881C_COMMAND_INSTR(0xc9, 0x1d),
+ ILI9881C_COMMAND_INSTR(0xca, 0x29),
+ ILI9881C_COMMAND_INSTR(0xcb, 0x6b),
+ ILI9881C_COMMAND_INSTR(0xcc, 0x1a),
+ ILI9881C_COMMAND_INSTR(0xcd, 0x18),
+ ILI9881C_COMMAND_INSTR(0xce, 0x4b),
+ ILI9881C_COMMAND_INSTR(0xcf, 0x20),
+ ILI9881C_COMMAND_INSTR(0xd0, 0x27),
+ ILI9881C_COMMAND_INSTR(0xd1, 0x50),
+ ILI9881C_COMMAND_INSTR(0xd2, 0x64),
+ ILI9881C_COMMAND_INSTR(0xd3, 0x39),
+
+ ILI9881C_SWITCH_PAGE_INSTR(0),
+ ILI9881C_COMMAND_INSTR(0x3a, 0x77),
+ ILI9881C_COMMAND_INSTR(0x36, 0x00),
+};
+
static inline struct ili9881c *panel_to_ili9881c(struct drm_panel *panel)
{
return container_of(panel, struct ili9881c, panel);
@@ -2035,9 +2235,19 @@ static int ili9881c_prepare(struct drm_panel *panel)
int ret;
/* Power the panel */
+ if (ctx->iovcc) {
+ ret = regulator_enable(ctx->iovcc);
+ if (ret)
+ return ret;
+ }
+
+ msleep(5);
ret = regulator_enable(ctx->power);
- if (ret)
- return ret;
+ if (ret) {
+ mctx.accum_err = ret;
+ goto disable_iovcc;
+ }
+
msleep(5);
/* And reset it */
@@ -2074,6 +2284,9 @@ static int ili9881c_prepare(struct drm_panel *panel)
disable_power:
regulator_disable(ctx->power);
+disable_iovcc:
+ if (ctx->iovcc)
+ regulator_disable(ctx->iovcc);
return mctx.accum_err;
}
@@ -2085,6 +2298,8 @@ static int ili9881c_unprepare(struct drm_panel *panel)
mipi_dsi_dcs_set_display_off_multi(&mctx);
mipi_dsi_dcs_enter_sleep_mode_multi(&mctx);
regulator_disable(ctx->power);
+ if (ctx->iovcc)
+ regulator_disable(ctx->iovcc);
gpiod_set_value_cansleep(ctx->reset, 1);
return 0;
@@ -2260,6 +2475,23 @@ static const struct drm_display_mode bsd1218_a101kl68_default_mode = {
.height_mm = 170,
};
+static const struct drm_display_mode waveshare_7inch_a_mode = {
+ .clock = 83333,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 120,
+ .hsync_end = 720 + 120 + 100,
+ .htotal = 720 + 120 + 100 + 100,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 10,
+ .vsync_end = 1280 + 10 + 10,
+ .vtotal = 1280 + 10 + 10 + 10,
+
+ .width_mm = 85,
+ .height_mm = 154,
+};
+
static int ili9881c_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
@@ -2329,6 +2561,11 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power),
"Couldn't get our power regulator\n");
+ ctx->iovcc = devm_regulator_get_optional(&dsi->dev, "iovcc");
+ if (IS_ERR(ctx->iovcc))
+ return dev_err_probe(&dsi->dev, PTR_ERR(ctx->iovcc),
+ "Couldn't get our iovcc regulator\n");
+
ctx->reset = devm_gpiod_get_optional(&dsi->dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ctx->reset))
return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
@@ -2454,6 +2691,15 @@ static const struct ili9881c_desc bsd1218_a101kl68_desc = {
.lanes = 4,
};
+static const struct ili9881c_desc waveshare_7inch_a_desc = {
+ .init = waveshare_7inch_a_init,
+ .init_length = ARRAY_SIZE(waveshare_7inch_a_init),
+ .mode = &waveshare_7inch_a_mode,
+ .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_HSE |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+ .lanes = 2,
+};
+
static const struct of_device_id ili9881c_of_match[] = {
{ .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc },
{ .compatible = "bestar,bsd1218-a101kl68", .data = &bsd1218_a101kl68_desc },
@@ -2462,6 +2708,7 @@ static const struct of_device_id ili9881c_of_match[] = {
{ .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc },
{ .compatible = "wanchanglong,w552946aaa", .data = &w552946aaa_desc },
{ .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc },
+ { .compatible = "waveshare,7.0-dsi-touch-a", .data = &waveshare_7inch_a_desc },
{ .compatible = "ampire,am8001280g", .data = &am8001280g_desc },
{ .compatible = "raspberrypi,dsi-5inch", &rpi_5inch_desc },
{ .compatible = "raspberrypi,dsi-7inch", &rpi_7inch_desc },
diff --git a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
index c33c611e03c0..7157b1299bfd 100644
--- a/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
+++ b/drivers/gpu/drm/panel/panel-jadard-jd9365da-h3.c
@@ -10,8 +10,10 @@
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_modes.h>
+#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
@@ -19,10 +21,13 @@
#include <linux/of.h>
#include <linux/regulator/consumer.h>
+#include <video/mipi_display.h>
+
struct jadard;
struct jadard_panel_desc {
- const struct drm_display_mode mode;
+ const struct drm_display_mode *mode_4ln;
+ const struct drm_display_mode *mode_2ln;
unsigned int lanes;
enum mipi_dsi_pixel_format format;
int (*init)(struct jadard *jadard);
@@ -56,7 +61,10 @@ static void jadard_enable_standard_cmds(struct mipi_dsi_multi_context *dsi_ctx)
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe1, 0x93);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe2, 0x65);
mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xe3, 0xf8);
- mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x03);
+ if (dsi_ctx->dsi->lanes == 2)
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x01);
+ else
+ mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x03);
}
static inline struct jadard *panel_to_jadard(struct drm_panel *panel)
@@ -149,24 +157,11 @@ static int jadard_get_modes(struct drm_panel *panel,
struct drm_connector *connector)
{
struct jadard *jadard = panel_to_jadard(panel);
- const struct drm_display_mode *desc_mode = &jadard->desc->mode;
- struct drm_display_mode *mode;
-
- mode = drm_mode_duplicate(connector->dev, desc_mode);
- if (!mode) {
- DRM_DEV_ERROR(&jadard->dsi->dev, "failed to add mode %ux%ux@%u\n",
- desc_mode->hdisplay, desc_mode->vdisplay,
- drm_mode_vrefresh(desc_mode));
- return -ENOMEM;
- }
-
- drm_mode_set_name(mode);
- drm_mode_probed_add(connector, mode);
-
- connector->display_info.width_mm = mode->width_mm;
- connector->display_info.height_mm = mode->height_mm;
- return 1;
+ if (jadard->dsi->lanes == 2)
+ return drm_connector_helper_get_modes_fixed(connector, jadard->desc->mode_2ln);
+ else
+ return drm_connector_helper_get_modes_fixed(connector, jadard->desc->mode_4ln);
}
static enum drm_panel_orientation jadard_panel_get_orientation(struct drm_panel *panel)
@@ -369,7 +364,7 @@ static int radxa_display_8hd_ad002_init_cmds(struct jadard *jadard)
};
static const struct jadard_panel_desc radxa_display_8hd_ad002_desc = {
- .mode = {
+ .mode_4ln = &(const struct drm_display_mode) {
.clock = 70000,
.hdisplay = 800,
@@ -601,7 +596,7 @@ static int cz101b4001_init_cmds(struct jadard *jadard)
};
static const struct jadard_panel_desc cz101b4001_desc = {
- .mode = {
+ .mode_4ln = &(const struct drm_display_mode) {
.clock = 70000,
.hdisplay = 800,
@@ -834,7 +829,7 @@ static int kingdisplay_kd101ne3_init_cmds(struct jadard *jadard)
};
static const struct jadard_panel_desc kingdisplay_kd101ne3_40ti_desc = {
- .mode = {
+ .mode_4ln = &(const struct drm_display_mode) {
.clock = (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
.hdisplay = 800,
@@ -1085,7 +1080,7 @@ static int melfas_lmfbx101117480_init_cmds(struct jadard *jadard)
};
static const struct jadard_panel_desc melfas_lmfbx101117480_desc = {
- .mode = {
+ .mode_4ln = &(const struct drm_display_mode) {
.clock = (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
.hdisplay = 800,
@@ -1341,7 +1336,7 @@ static int anbernic_rgds_init_cmds(struct jadard *jadard)
};
static const struct jadard_panel_desc anbernic_rgds_display_desc = {
- .mode = {
+ .mode_4ln = &(const struct drm_display_mode) {
.clock = (640 + 260 + 220 + 260) * (480 + 10 + 2 + 16) * 60 / 1000,
.hdisplay = 640,
@@ -1577,7 +1572,7 @@ static int taiguan_xti05101_01a_init_cmds(struct jadard *jadard)
};
static const struct jadard_panel_desc taiguan_xti05101_01a_desc = {
- .mode = {
+ .mode_4ln = &(const struct drm_display_mode) {
.clock = (800 + 24 + 24 + 24) * (1280 + 30 + 4 + 8) * 60 / 1000,
.hdisplay = 800,
@@ -1606,6 +1601,1336 @@ static const struct jadard_panel_desc taiguan_xti05101_01a_desc = {
.enter_sleep_to_reset_down_delay_ms = 100,
};
+static int waveshare_3_4_c_init(struct jadard *jadard)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ jadard_enable_standard_cmds(&dsi_ctx);
+
+ jd9365da_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x26);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x64);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0xc7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x1b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x56);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x25);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x16);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x2f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x32);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x31);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x56);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x25);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x16);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x2f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x32);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x31);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x0f);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x42);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0xa6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xd9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x43);
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ msleep(120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ msleep(5);
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct jadard_panel_desc waveshare_3_4_inch_c_desc = {
+ .mode_2ln = &(const struct drm_display_mode) {
+ .clock = (800 + 40 + 20 + 20) * (800 + 24 + 4 + 12) * 60 / 1000,
+
+ .hdisplay = 800,
+ .hsync_start = 800 + 40,
+ .hsync_end = 800 + 40 + 20,
+ .htotal = 800 + 40 + 20 + 20,
+
+ .vdisplay = 800,
+ .vsync_start = 800 + 24,
+ .vsync_end = 800 + 24 + 4,
+ .vtotal = 800 + 24 + 4 + 12,
+
+ .width_mm = 88,
+ .height_mm = 88,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .lanes = 2,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = waveshare_3_4_c_init,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+};
+
+static int waveshare_4_0_c_init(struct jadard *jadard)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ jadard_enable_standard_cmds(&dsi_ctx);
+
+ jd9365da_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xd0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x26);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x64);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0xc7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x1b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x56);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x25);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x16);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x2f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x32);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x31);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x56);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x25);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x16);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x2f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x32);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x53);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x31);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x0f);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x42);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0xa6);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xd9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x33);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x43);
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ msleep(120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ msleep(5);
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct jadard_panel_desc waveshare_4_0_inch_c_desc = {
+ .mode_2ln = &(const struct drm_display_mode) {
+ .clock = (720 + 40 + 20 + 20) * (720 + 24 + 4 + 12) * 60 / 1000,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 40,
+ .hsync_end = 720 + 40 + 20,
+ .htotal = 720 + 40 + 20 + 20,
+
+ .vdisplay = 720,
+ .vsync_start = 720 + 24,
+ .vsync_end = 720 + 24 + 4,
+ .vtotal = 720 + 24 + 4 + 12,
+
+ .width_mm = 88,
+ .height_mm = 88,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .lanes = 2,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = waveshare_4_0_c_init,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+};
+
+static int waveshare_8_0_a_init(struct jadard *jadard)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ jadard_enable_standard_cmds(&dsi_ctx);
+
+ jd9365da_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
+ if (jadard->dsi->lanes == 4)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x7e);
+ else
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x4e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x65);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xb7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xb7);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x70);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xa0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0xa9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x63);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x54);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x62);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x63);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x54);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x62);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x10);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x35);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x35);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x30);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x0c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x56);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x7b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xf8);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xd5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x12);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x7b);
+
+ jd9365da_switch_page(&dsi_ctx, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x59);
+ if (jadard->dsi->lanes != 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0f);
+ }
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ msleep(120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ msleep(60);
+
+ return 0;
+}
+
+static const struct drm_display_mode waveshare_8_0_a_mode = {
+ .clock = (800 + 40 + 20 + 20) * (1280 + 30 + 12 + 4) * 60 / 1000,
+
+ .hdisplay = 800,
+ .hsync_start = 800 + 40,
+ .hsync_end = 800 + 40 + 20,
+ .htotal = 800 + 40 + 20 + 20,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 30,
+ .vsync_end = 1280 + 30 + 12,
+ .vtotal = 1280 + 30 + 12 + 4,
+
+ .width_mm = 107,
+ .height_mm = 172,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static const struct jadard_panel_desc waveshare_8_0_inch_a_desc = {
+ .mode_4ln = &waveshare_8_0_a_mode,
+ .mode_2ln = &waveshare_8_0_a_mode,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = waveshare_8_0_a_init,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+};
+
+static int waveshare_10_1_b_init(struct jadard *jadard);
+
+static const struct jadard_panel_desc waveshare_9_0_inch_b_desc = {
+ .mode_4ln = &(const struct drm_display_mode) {
+ .clock = (720 + 60 + 60 + 4) * (1280 + 16 + 12 + 4) * 60 / 1000,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 60,
+ .hsync_end = 720 + 60 + 60,
+ .htotal = 720 + 60 + 60 + 4,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 16,
+ .vsync_end = 1280 + 16 + 12,
+ .vtotal = 1280 + 16 + 12 + 4,
+
+ .width_mm = 114,
+ .height_mm = 196,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .mode_2ln = &(const struct drm_display_mode) {
+ .clock = (720 + 50 + 50 + 50) * (1280 + 26 + 12 + 4) * 60 / 1000,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 50,
+ .hsync_end = 720 + 50 + 50,
+ .htotal = 720 + 50 + 50 + 50,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 26,
+ .vsync_end = 1280 + 26 + 12,
+ .vtotal = 1280 + 26 + 12 + 4,
+
+ .width_mm = 114,
+ .height_mm = 196,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = waveshare_10_1_b_init,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+};
+
+static const struct drm_display_mode waveshare_10_1_a_mode = {
+ .clock = (800 + 40 + 20 + 20) * (1280 + 20 + 20 + 4) * 60 / 1000,
+
+ .hdisplay = 800,
+ .hsync_start = 800 + 40,
+ .hsync_end = 800 + 40 + 20,
+ .htotal = 800 + 40 + 20 + 20,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 20,
+ .vsync_end = 1280 + 20 + 20,
+ .vtotal = 1280 + 20 + 20 + 4,
+
+ .width_mm = 135,
+ .height_mm = 216,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+};
+
+static int waveshare_10_1_a_init(struct jadard *jadard)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ jadard_enable_standard_cmds(&dsi_ctx);
+
+ jd9365da_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x3b);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x38);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xaf);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xaf);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x26);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x78);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xa0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x81);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x23);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0d);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x69);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x6b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x5c);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x6a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x5b);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x4f);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x4d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x3f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x42);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x2b);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x2a);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x43);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x63);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x62);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x52);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x4f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x4e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x0f);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x59);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x3a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x26);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x6b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x5c);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x6a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x5b);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x4f);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x4d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x3f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x42);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x2b);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x2a);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x43);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x43);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x63);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x62);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x52);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x4f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x4e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0f);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x59);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x4c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x3a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x26);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x37);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x42);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x42);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x77);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x49);
+ if (jadard->dsi->lanes == 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x37);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x5e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x77);
+ }
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x48);
+ if (jadard->dsi->lanes == 4)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x1f);
+ else
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x1e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x37);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x30);
+ if (jadard->dsi->lanes == 4)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x16);
+ else
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x34);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x6a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
+ if (jadard->dsi->lanes == 4)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x1d);
+ else
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x6a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xdd);
+ if (jadard->dsi->lanes == 4)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x3f);
+ else
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2c);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x15);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x82);
+
+ jd9365da_switch_page(&dsi_ctx, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x61);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x48);
+ if (jadard->dsi->lanes != 4) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0f);
+ }
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe6, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe7, 0x0c);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ msleep(120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ msleep(60);
+
+ return dsi_ctx.accum_err;
+}
+
+static const struct jadard_panel_desc waveshare_10_1_inch_a_desc = {
+ .mode_4ln = &waveshare_10_1_a_mode,
+ .mode_2ln = &waveshare_10_1_a_mode,
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = waveshare_10_1_a_init,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+};
+
+static int waveshare_10_1_b_init(struct jadard *jadard)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = jadard->dsi };
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ jadard_enable_standard_cmds(&dsi_ctx);
+
+ jd9365da_switch_page(&dsi_ctx, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x3f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0xbf);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0xbf);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0xfe);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x74);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0xff);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0xa0);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x7e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x0f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x24);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0xa9);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5a, 0x38);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x1a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x65);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x25);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x3f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x34);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x27);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x24);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x65);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x52);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x3d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x14);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x28);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x25);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x23);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x3f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0x2d);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x34);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x27);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x24);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0x18);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x00);
+
+ jd9365da_switch_page(&dsi_ctx, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x51);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x01, 0x55);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x51);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x05, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x06, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x07, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x08, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0a, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0b, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0c, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0d, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0f, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x10, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x11, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x12, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x13, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x14, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x16, 0x51);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x17, 0x55);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x18, 0x50);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x19, 0x51);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x77);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1c, 0x57);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1d, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1e, 0x47);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1f, 0x46);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x20, 0x45);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x21, 0x44);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x22, 0x4b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x23, 0x4a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x24, 0x49);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x25, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x26, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x27, 0x41);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x28, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x29, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2a, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x5f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2c, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2d, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2e, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x30, 0x15);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x31, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x32, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x33, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x34, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x38, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3c, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3e, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x15);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x17);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4c, 0x0a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x0b);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x52, 0x11);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x1f);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5b, 0x10);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5c, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x40);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x02);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x66);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x55);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x13);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x73);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x09);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x66);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x08);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x88);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xe3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0xd5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x2a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x21);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0x00);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0x66);
+
+ jd9365da_switch_page(&dsi_ctx, 0x04);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x0e);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0xb3);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x09, 0x60);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x0e, 0x48);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x37, 0x58);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2b, 0x0f);
+
+ jd9365da_switch_page(&dsi_ctx, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x15, 0x1d);
+
+ jd9365da_switch_page(&dsi_ctx, 0x00);
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ msleep(120);
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ msleep(5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_TEAR_ON);
+
+ return 0;
+}
+
+static const struct jadard_panel_desc waveshare_10_1_inch_b_desc = {
+ .mode_4ln = &(const struct drm_display_mode) {
+ .clock = (720 + 60 + 60 + 4) * (1280 + 16 + 12 + 4) * 60 / 1000,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 60,
+ .hsync_end = 720 + 60 + 60,
+ .htotal = 720 + 60 + 60 + 4,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 16,
+ .vsync_end = 1280 + 16 + 12,
+ .vtotal = 1280 + 16 + 12 + 4,
+
+ .width_mm = 125,
+ .height_mm = 222,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .mode_2ln = &(const struct drm_display_mode) {
+ .clock = (720 + 50 + 50 + 50) * (1280 + 26 + 12 + 4) * 60 / 1000,
+
+ .hdisplay = 720,
+ .hsync_start = 720 + 50,
+ .hsync_end = 720 + 50 + 50,
+ .htotal = 720 + 50 + 50 + 50,
+
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 26,
+ .vsync_end = 1280 + 26 + 12,
+ .vtotal = 1280 + 26 + 12 + 4,
+
+ .width_mm = 125,
+ .height_mm = 222,
+ .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ },
+ .format = MIPI_DSI_FMT_RGB888,
+ .init = waveshare_10_1_b_init,
+ .mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+ MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS,
+};
+
static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
@@ -1629,6 +2954,26 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
dsi->format = desc->format;
dsi->lanes = desc->lanes;
+ if (!dsi->lanes) {
+ dsi->lanes = drm_of_get_data_lanes_count_remote(dsi->dev.of_node, 0, -1, 2, 4);
+ if (dsi->lanes < 0)
+ return dsi->lanes;
+ if (dsi->lanes == 4) {
+ if (!desc->mode_4ln) {
+ dev_err(&dsi->dev, "4-lane config is not supported\n");
+ return -EINVAL;
+ }
+ } else if (dsi->lanes == 2) {
+ if (!desc->mode_2ln) {
+ dev_err(&dsi->dev, "2-lane config is not supported\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&dsi->dev, "Unsupported number of lanes, %d\n", dsi->lanes);
+ return -ENODEV;
+ }
+ }
+ dev_dbg(&dsi->dev, "lanes: %d\n", dsi->lanes);
jadard->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(jadard->reset))
@@ -1653,6 +2998,8 @@ static int jadard_dsi_probe(struct mipi_dsi_device *dsi)
if (ret)
return ret;
+ jadard->panel.prepare_prev_first = true;
+
drm_panel_add(&jadard->panel);
mipi_dsi_set_drvdata(dsi, jadard);
@@ -1707,6 +3054,30 @@ static const struct of_device_id jadard_of_match[] = {
.compatible = "taiguanck,xti05101-01a",
.data = &taiguan_xti05101_01a_desc
},
+ {
+ .compatible = "waveshare,3.4-dsi-touch-c",
+ .data = &waveshare_3_4_inch_c_desc
+ },
+ {
+ .compatible = "waveshare,4.0-dsi-touch-c",
+ .data = &waveshare_4_0_inch_c_desc
+ },
+ {
+ .compatible = "waveshare,8.0-dsi-touch-a",
+ .data = &waveshare_8_0_inch_a_desc
+ },
+ {
+ .compatible = "waveshare,9.0-dsi-touch-b",
+ .data = &waveshare_9_0_inch_b_desc
+ },
+ {
+ .compatible = "waveshare,10.1-dsi-touch-a",
+ .data = &waveshare_10_1_inch_a_desc
+ },
+ {
+ .compatible = "waveshare,10.1-dsi-touch-b",
+ .data = &waveshare_10_1_inch_b_desc
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, jadard_of_match);
diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
index 3c3308fc55df..d21d93a0700e 100644
--- a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
+++ b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c
@@ -44,14 +44,23 @@ static inline struct wuxga_nt_panel *to_wuxga_nt_panel(struct drm_panel *panel)
static int wuxga_nt_panel_on(struct wuxga_nt_panel *wuxga_nt)
{
- return mipi_dsi_turn_on_peripheral(wuxga_nt->dsi);
+ struct mipi_dsi_multi_context dsi_ctx = {
+ .dsi = wuxga_nt->dsi
+ };
+
+ mipi_dsi_turn_on_peripheral_multi(&dsi_ctx);
+ return dsi_ctx.accum_err;
}
static int wuxga_nt_panel_disable(struct drm_panel *panel)
{
struct wuxga_nt_panel *wuxga_nt = to_wuxga_nt_panel(panel);
+ struct mipi_dsi_multi_context dsi_ctx = {
+ .dsi = wuxga_nt->dsi
+ };
- return mipi_dsi_shutdown_peripheral(wuxga_nt->dsi);
+ mipi_dsi_shutdown_peripheral_multi(&dsi_ctx);
+ return dsi_ctx.accum_err;
}
static int wuxga_nt_panel_unprepare(struct drm_panel *panel)
@@ -94,15 +103,8 @@ static int wuxga_nt_panel_prepare(struct drm_panel *panel)
msleep(250);
ret = wuxga_nt_panel_on(wuxga_nt);
- if (ret < 0) {
- dev_err(panel->dev, "failed to set panel on: %d\n", ret);
- goto poweroff;
- }
-
- return 0;
-
-poweroff:
- regulator_disable(wuxga_nt->supply);
+ if (ret < 0)
+ regulator_disable(wuxga_nt->supply);
return ret;
}
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 236bd56208cc..b2708a1fe464 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -5061,6 +5061,342 @@ static const struct panel_desc vl050_8048nt_c01 = {
.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE,
};
+static const struct drm_display_mode waveshare_28_lcd_mode = {
+ .clock = 50000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 150,
+ .hsync_end = 480 + 150 + 50,
+ .htotal = 480 + 150 + 50 + 150,
+ .vdisplay = 640,
+ .vsync_start = 640 + 150,
+ .vsync_end = 640 + 150 + 50,
+ .vtotal = 640 + 150 + 50 + 150,
+ .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc waveshare_28_lcd_panel = {
+ .modes = &waveshare_28_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 44,
+ .height = 58,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE,
+};
+
+static const struct drm_display_mode waveshare_34_lcd_c_mode = {
+ .clock = 50000,
+ .hdisplay = 800,
+ .hsync_start = 800 + 32,
+ .hsync_end = 800 + 32 + 6,
+ .htotal = 800 + 32 + 6 + 120,
+ .vdisplay = 800,
+ .vsync_start = 800 + 8,
+ .vsync_end = 800 + 8 + 4,
+ .vtotal = 800 + 8 + 4 + 16,
+};
+
+static const struct panel_desc waveshare_34_lcd_c_panel = {
+ .modes = &waveshare_34_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 88,
+ .height = 88,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_40_lcd_mode = {
+ .clock = 50000,
+ .hdisplay = 480,
+ .hsync_start = 480 + 150,
+ .hsync_end = 480 + 150 + 100,
+ .htotal = 480 + 150 + 100 + 150,
+ .vdisplay = 800,
+ .vsync_start = 800 + 20,
+ .vsync_end = 800 + 20 + 100,
+ .vtotal = 800 + 20 + 100 + 20,
+ .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc waveshare_40_lcd_panel = {
+ .modes = &waveshare_40_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 52,
+ .height = 87,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE,
+};
+
+static const struct drm_display_mode waveshare_40_lcd_c_mode = {
+ .clock = 50000,
+ .hdisplay = 720,
+ .hsync_start = 720 + 32,
+ .hsync_end = 720 + 32 + 200,
+ .htotal = 720 + 32 + 200 + 120,
+ .vdisplay = 720,
+ .vsync_start = 720 + 8,
+ .vsync_end = 720 + 8 + 4,
+ .vtotal = 720 + 8 + 4 + 16,
+};
+
+static const struct panel_desc waveshare_40_lcd_c_panel = {
+ .modes = &waveshare_40_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 102,
+ .height = 102,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_50_lcd_c_mode = {
+ .clock = 50000,
+ .hdisplay = 1024,
+ .hsync_start = 1024 + 100,
+ .hsync_end = 1024 + 100 + 100,
+ .htotal = 1024 + 100 + 100 + 100,
+ .vdisplay = 600,
+ .vsync_start = 600 + 10,
+ .vsync_end = 600 + 10 + 10,
+ .vtotal = 600 + 10 + 10 + 10,
+ .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc waveshare_50_lcd_c_panel = {
+ .modes = &waveshare_50_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 109,
+ .height = 66,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE,
+};
+
+static const struct drm_display_mode waveshare_50_lcd_d_mode = {
+ .clock = 83333,
+ .hdisplay = 720,
+ .hsync_start = 720 + 100,
+ .hsync_end = 720 + 100 + 80,
+ .htotal = 720 + 100 + 80 + 100,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 20,
+ .vsync_end = 1280 + 20 + 20,
+ .vtotal = 1280 + 20 + 20 + 20,
+};
+
+static const struct panel_desc waveshare_50_lcd_d_panel = {
+ .modes = &waveshare_50_lcd_d_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 62,
+ .height = 110,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_625_lcd_mode = {
+ .clock = 83333,
+ .hdisplay = 720,
+ .hsync_start = 720 + 50,
+ .hsync_end = 720 + 50 + 50,
+ .htotal = 720 + 50 + 50 + 50,
+ .vdisplay = 1560,
+ .vsync_start = 1560 + 20,
+ .vsync_end = 1560 + 20 + 20,
+ .vtotal = 1560 + 20 + 20 + 20,
+};
+
+static const struct panel_desc waveshare_625_lcd_panel = {
+ .modes = &waveshare_625_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 66,
+ .height = 144,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct panel_desc waveshare_70_lcd_c_panel = {
+ .modes = &waveshare_50_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 155,
+ .height = 87,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+ .connector_type = DRM_MODE_CONNECTOR_DPI,
+ .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE |
+ DRM_BUS_FLAG_SYNC_SAMPLE_POSEDGE,
+};
+
+static const struct drm_display_mode waveshare_80_lcd_c_mode;
+static const struct panel_desc waveshare_70_lcd_e_panel = {
+ .modes = &waveshare_80_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 152,
+ .height = 95,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_70_lcd_h_mode = {
+ .clock = 83333,
+ .hdisplay = 1280,
+ .hsync_start = 1280 + 64,
+ .hsync_end = 1280 + 64 + 64,
+ .htotal = 1280 + 64 + 64 + 64,
+ .vdisplay = 720,
+ .vsync_start = 720 + 64,
+ .vsync_end = 720 + 64 + 64,
+ .vtotal = 720 + 64 + 64 + 64,
+};
+
+static const struct panel_desc waveshare_70_lcd_h_panel = {
+ .modes = &waveshare_70_lcd_h_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 155,
+ .height = 88,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_79_lcd_mode = {
+ .clock = 50000,
+ .hdisplay = 400,
+ .hsync_start = 400 + 40,
+ .hsync_end = 400 + 40 + 30,
+ .htotal = 400 + 40 + 30 + 40,
+ .vdisplay = 1280,
+ .vsync_start = 1280 + 20,
+ .vsync_end = 1280 + 20 + 10,
+ .vtotal = 1280 + 20 + 10 + 20,
+};
+
+static const struct panel_desc waveshare_79_lcd_panel = {
+ .modes = &waveshare_79_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 60,
+ .height = 191,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_80_lcd_c_mode = {
+ .clock = 83333,
+ .hdisplay = 1280,
+ .hsync_start = 1280 + 156,
+ .hsync_end = 1280 + 156 + 20,
+ .htotal = 1280 + 156 + 20 + 40,
+ .vdisplay = 800,
+ .vsync_start = 800 + 40,
+ .vsync_end = 800 + 40 + 48,
+ .vtotal = 800 + 40 + 48 + 40,
+};
+
+static const struct panel_desc waveshare_80_lcd_c_panel = {
+ .modes = &waveshare_80_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 173,
+ .height = 108,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_88_lcd_mode = {
+ .clock = 83333,
+ .hdisplay = 480,
+ .hsync_start = 480 + 50,
+ .hsync_end = 480 + 50 + 50,
+ .htotal = 480 + 50 + 50 + 50,
+ .vdisplay = 1920,
+ .vsync_start = 1920 + 20,
+ .vsync_end = 1920 + 20 + 20,
+ .vtotal = 1920 + 20 + 20 + 20,
+};
+
+static const struct panel_desc waveshare_88_lcd_panel = {
+ .modes = &waveshare_88_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 56,
+ .height = 220,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct panel_desc waveshare_101_lcd_c_panel = {
+ .modes = &waveshare_80_lcd_c_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 217,
+ .height = 136,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
+static const struct drm_display_mode waveshare_119_lcd_mode = {
+ .clock = 50000,
+ .hdisplay = 320,
+ .hsync_start = 320 + 60,
+ .hsync_end = 320 + 60 + 60,
+ .htotal = 320 + 60 + 60 + 60,
+ .vdisplay = 1480,
+ .vsync_start = 1480 + 60,
+ .vsync_end = 1480 + 60 + 60,
+ .vtotal = 1480 + 60 + 60 + 60,
+};
+
+static const struct panel_desc waveshare_119_lcd_panel = {
+ .modes = &waveshare_119_lcd_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 58,
+ .height = 268,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode waveshare_133inch_mode = {
.clock = 148500,
.hdisplay = 1920,
@@ -5698,6 +6034,51 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "vxt,vl050-8048nt-c01",
.data = &vl050_8048nt_c01,
}, {
+ .compatible = "waveshare,2.8inch-panel",
+ .data = &waveshare_28_lcd_panel
+ }, {
+ .compatible = "waveshare,3.4inch-c-panel",
+ .data = &waveshare_34_lcd_c_panel
+ }, {
+ .compatible = "waveshare,4.0inch-panel",
+ .data = &waveshare_40_lcd_panel
+ }, {
+ .compatible = "waveshare,4.0inch-c-panel",
+ .data = &waveshare_40_lcd_c_panel
+ }, {
+ .compatible = "waveshare,5.0inch-c-panel",
+ .data = &waveshare_50_lcd_c_panel
+ }, {
+ .compatible = "waveshare,5.0inch-d-panel",
+ .data = &waveshare_50_lcd_d_panel
+ }, {
+ .compatible = "waveshare,6.25inch-panel",
+ .data = &waveshare_625_lcd_panel
+ }, {
+ .compatible = "waveshare,7.0inch-c-panel",
+ .data = &waveshare_70_lcd_c_panel
+ }, {
+ .compatible = "waveshare,7.0inch-e-panel",
+ .data = &waveshare_70_lcd_e_panel
+ }, {
+ .compatible = "waveshare,7.0inch-h-panel",
+ .data = &waveshare_70_lcd_h_panel
+ }, {
+ .compatible = "waveshare,7.9inch-panel",
+ .data = &waveshare_79_lcd_panel
+ }, {
+ .compatible = "waveshare,8.0inch-c-panel",
+ .data = &waveshare_80_lcd_c_panel
+ }, {
+ .compatible = "waveshare,8.8inch-panel",
+ .data = &waveshare_88_lcd_panel
+ }, {
+ .compatible = "waveshare,10.1inch-c-panel",
+ .data = &waveshare_101_lcd_c_panel
+ }, {
+ .compatible = "waveshare,11.9inch-panel",
+ .data = &waveshare_119_lcd_panel
+ }, {
.compatible = "waveshare,13.3inch-panel",
.data = &waveshare_133inch,
}, {
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index dedc13e56631..7fed22d555a5 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -70,8 +70,21 @@ static int panfrost_clk_init(struct panfrost_device *pfdev)
goto disable_clock;
}
+ pfdev->bus_ace_clock = devm_clk_get_optional(pfdev->base.dev, "bus_ace");
+ if (IS_ERR(pfdev->bus_ace_clock)) {
+ err = PTR_ERR(pfdev->bus_ace_clock);
+ dev_err(pfdev->base.dev, "get bus_ace_clock failed %d\n", err);
+ goto disable_bus_clock;
+ }
+
+ err = clk_prepare_enable(pfdev->bus_ace_clock);
+ if (err)
+ goto disable_bus_clock;
+
return 0;
+disable_bus_clock:
+ clk_disable_unprepare(pfdev->bus_clock);
disable_clock:
clk_disable_unprepare(pfdev->clock);
@@ -80,6 +93,7 @@ disable_clock:
static void panfrost_clk_fini(struct panfrost_device *pfdev)
{
+ clk_disable_unprepare(pfdev->bus_ace_clock);
clk_disable_unprepare(pfdev->bus_clock);
clk_disable_unprepare(pfdev->clock);
}
@@ -429,11 +443,13 @@ static int panfrost_device_runtime_resume(struct device *dev)
if (ret)
goto err_clk;
- if (pfdev->bus_clock) {
- ret = clk_enable(pfdev->bus_clock);
- if (ret)
- goto err_bus_clk;
- }
+ ret = clk_enable(pfdev->bus_clock);
+ if (ret)
+ goto err_bus_clk;
+
+ ret = clk_enable(pfdev->bus_ace_clock);
+ if (ret)
+ goto err_bus_ace_clk;
}
panfrost_device_reset(pfdev, true);
@@ -441,6 +457,9 @@ static int panfrost_device_runtime_resume(struct device *dev)
return 0;
+err_bus_ace_clk:
+ if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
+ clk_disable(pfdev->bus_clock);
err_bus_clk:
if (pfdev->comp->pm_features & BIT(GPU_PM_RT))
clk_disable(pfdev->clock);
@@ -464,9 +483,8 @@ static int panfrost_device_runtime_suspend(struct device *dev)
panfrost_gpu_power_off(pfdev);
if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) {
- if (pfdev->bus_clock)
- clk_disable(pfdev->bus_clock);
-
+ clk_disable(pfdev->bus_ace_clock);
+ clk_disable(pfdev->bus_clock);
clk_disable(pfdev->clock);
reset_control_assert(pfdev->rstc);
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 0f3992412205..ec55c136b1b6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -136,6 +136,7 @@ struct panfrost_device {
void __iomem *iomem;
struct clk *clock;
struct clk *bus_clock;
+ struct clk *bus_ace_clock;
struct regulator_bulk_data *regulators;
struct reset_control *rstc;
/* pm_domains for devices with more than one. */
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 711f5101aa04..3d0bdba2a474 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -1156,6 +1156,7 @@ static const struct of_device_id dt_match[] = {
.data = &amlogic_data, },
{ .compatible = "amlogic,meson-g12a-mali",
.data = &amlogic_data, },
+ { .compatible = "renesas,r9a08g046-mali", .data = &default_pm_rt_data },
{ .compatible = "renesas,r9a09g047-mali", .data = &default_pm_rt_data },
{ .compatible = "arm,mali-t604", .data = &default_data, },
{ .compatible = "arm,mali-t624", .data = &default_data, },
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index d59b4863b8ad..2d12b83e900a 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -850,7 +850,6 @@ int panfrost_jm_init(struct panfrost_device *pfdev)
{
struct drm_sched_init_args args = {
.ops = &panfrost_sched_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 2,
.timeout = msecs_to_jiffies(JOB_TIMEOUT_MS),
.dev = pfdev->base.dev,
diff --git a/drivers/gpu/drm/panthor/Kconfig b/drivers/gpu/drm/panthor/Kconfig
index 55b40ad07f3b..911e7f4810c3 100644
--- a/drivers/gpu/drm/panthor/Kconfig
+++ b/drivers/gpu/drm/panthor/Kconfig
@@ -8,7 +8,6 @@ config DRM_PANTHOR
depends on MMU
select DEVFREQ_GOV_SIMPLE_ONDEMAND
select DRM_EXEC
- select DRM_GEM_SHMEM_HELPER
select DRM_GPUVM
select DRM_SCHED
select IOMMU_IO_PGTABLE_LPAE
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 54fbb1aa07c5..bc62a498a8a8 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -2,6 +2,7 @@
/* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
/* Copyright 2023 Collabora ltd. */
+/* Copyright 2025 ARM Limited. All rights reserved. */
#include <linux/clk.h>
#include <linux/mm.h>
@@ -122,6 +123,7 @@ void panthor_device_unplug(struct panthor_device *ptdev)
panthor_sched_unplug(ptdev);
panthor_fw_unplug(ptdev);
panthor_mmu_unplug(ptdev);
+ panthor_gem_shrinker_unplug(ptdev);
panthor_gpu_unplug(ptdev);
panthor_pwr_unplug(ptdev);
@@ -291,10 +293,14 @@ int panthor_device_init(struct panthor_device *ptdev)
if (ret)
goto err_unplug_gpu;
- ret = panthor_mmu_init(ptdev);
+ ret = panthor_gem_shrinker_init(ptdev);
if (ret)
goto err_unplug_gpu;
+ ret = panthor_mmu_init(ptdev);
+ if (ret)
+ goto err_unplug_shrinker;
+
ret = panthor_fw_init(ptdev);
if (ret)
goto err_unplug_mmu;
@@ -326,6 +332,9 @@ err_unplug_fw:
err_unplug_mmu:
panthor_mmu_unplug(ptdev);
+err_unplug_shrinker:
+ panthor_gem_shrinker_unplug(ptdev);
+
err_unplug_gpu:
panthor_gpu_unplug(ptdev);
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index b6696f73a536..5cba272f9b4d 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -14,6 +14,7 @@
#include <linux/spinlock.h>
#include <drm/drm_device.h>
+#include <drm/drm_gem.h>
#include <drm/drm_mm.h>
#include <drm/gpu_scheduler.h>
#include <drm/panthor_drm.h>
@@ -178,6 +179,78 @@ struct panthor_device {
/** @devfreq: Device frequency scaling management data. */
struct panthor_devfreq *devfreq;
+ /** @reclaim: Reclaim related stuff */
+ struct {
+ /** @reclaim.shrinker: Shrinker instance */
+ struct shrinker *shrinker;
+
+ /** @reclaim.lock: Lock protecting all LRUs */
+ struct mutex lock;
+
+ /**
+ * @reclaim.unused: BOs with unused pages
+ *
+ * Basically all buffers that got mmapped, vmapped or GPU mapped and
+ * then unmapped. There should be no contention on these buffers,
+ * making them ideal to reclaim.
+ */
+ struct drm_gem_lru unused;
+
+ /**
+ * @reclaim.mmapped: mmap()-ed buffers
+ *
+ * Those are relatively easy to reclaim since we don't need user
+ * agreement, we can simply teardown the mapping and let it fault on
+ * the next access.
+ */
+ struct drm_gem_lru mmapped;
+
+ /**
+ * @reclaim.gpu_mapped_shared: shared BO LRU list
+ *
+ * That's the most tricky BO type to reclaim, because it involves
+ * tearing down all mappings in all VMs where this BO is mapped,
+ * which increases the risk of contention and thus decreases the
+ * likeliness of success.
+ */
+ struct drm_gem_lru gpu_mapped_shared;
+
+ /**
+ * @reclaim.vms: VM LRU list
+ *
+ * VMs that have reclaimable BOs only mapped to a single VM are placed
+ * in this LRU. Reclaiming such BOs implies waiting for VM idleness
+ * (no in-flight GPU jobs targeting this VM), meaning we can't reclaim
+ * those if we're in a context where we can't block/sleep.
+ */
+ struct list_head vms;
+
+ /**
+ * @reclaim.gpu_mapped_count: Global counter of pages that are GPU mapped
+ *
+ * Allows us to get the number of reclaimable pages without walking
+ * the vms and gpu_mapped_shared LRUs.
+ */
+ long gpu_mapped_count;
+
+ /**
+ * @reclaim.retry_count: Number of times we ran the shrinker without being
+ * able to reclaim stuff
+ *
+ * Used to stop scanning GEMs when too many attempts were made
+ * without progress.
+ */
+ atomic_t retry_count;
+
+#ifdef CONFIG_DEBUG_FS
+ /**
+ * @reclaim.nr_pages_reclaimed_on_last_scan: Number of pages reclaimed on the last
+ * shrinker scan
+ */
+ unsigned long nr_pages_reclaimed_on_last_scan;
+#endif
+ } reclaim;
+
/** @unplug: Device unplug related fields. */
struct {
/** @lock: Lock used to serialize unplug operations. */
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 87d27c3c1456..73fc983dc9b4 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -21,6 +21,7 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_exec.h>
+#include <drm/drm_file.h>
#include <drm/drm_ioctl.h>
#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
@@ -1578,7 +1579,7 @@ static int panthor_ioctl_bo_query_info(struct drm_device *ddev, void *data,
args->create_flags = bo->flags;
args->extra_flags = 0;
- if (drm_gem_is_imported(&bo->base.base))
+ if (drm_gem_is_imported(&bo->base))
args->extra_flags |= DRM_PANTHOR_BO_IS_IMPORTED;
drm_gem_object_put(obj);
@@ -1756,34 +1757,10 @@ static const struct file_operations panthor_drm_driver_fops = {
};
#ifdef CONFIG_DEBUG_FS
-static int panthor_gems_show(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
- struct panthor_device *ptdev = container_of(dev, struct panthor_device, base);
-
- panthor_gem_debugfs_print_bos(ptdev, m);
-
- return 0;
-}
-
-static struct drm_info_list panthor_debugfs_list[] = {
- {"gems", panthor_gems_show, 0, NULL},
-};
-
-static int panthor_gems_debugfs_init(struct drm_minor *minor)
-{
- drm_debugfs_create_files(panthor_debugfs_list,
- ARRAY_SIZE(panthor_debugfs_list),
- minor->debugfs_root, minor);
-
- return 0;
-}
-
static void panthor_debugfs_init(struct drm_minor *minor)
{
panthor_mmu_debugfs_init(minor);
- panthor_gems_debugfs_init(minor);
+ panthor_gem_debugfs_init(minor);
}
#endif
@@ -1817,8 +1794,7 @@ static const struct drm_driver panthor_drm_driver = {
.major = 1,
.minor = 8,
- .gem_create_object = panthor_gem_create_object,
- .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
+ .gem_prime_import_sg_table = panthor_gem_prime_import_sg_table,
.gem_prime_import = panthor_gem_prime_import,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = panthor_debugfs_init,
@@ -1968,3 +1944,4 @@ module_exit(panthor_exit);
MODULE_AUTHOR("Panthor Project Developers");
MODULE_DESCRIPTION("Panthor DRM Driver");
MODULE_LICENSE("Dual MIT/GPL");
+MODULE_IMPORT_NS("DMA_BUF");
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 8886002e1d31..be0da5b1f3ab 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -628,7 +628,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
struct panthor_gem_object *bo;
u32 vm_map_flags = 0;
- struct sg_table *sgt;
u64 va = hdr.va.start;
if (!(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_WR))
@@ -666,11 +665,12 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
panthor_fw_init_section_mem(ptdev, section);
bo = to_panthor_bo(section->mem->obj);
- sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
- if (IS_ERR(sgt))
- return PTR_ERR(sgt);
- dma_sync_sgtable_for_device(ptdev->base.dev, sgt, DMA_TO_DEVICE);
+ /* An sgt should have been requested when the kernel BO was GPU-mapped. */
+ if (drm_WARN_ON_ONCE(&ptdev->base, !bo->dmap.sgt))
+ return -EINVAL;
+
+ dma_sync_sgtable_for_device(ptdev->base.dev, bo->dmap.sgt, DMA_TO_DEVICE);
}
if (hdr.va.start == CSF_MCU_SHARED_REGION_START)
@@ -730,8 +730,10 @@ panthor_reload_fw_sections(struct panthor_device *ptdev, bool full_reload)
continue;
panthor_fw_init_section_mem(ptdev, section);
- sgt = drm_gem_shmem_get_pages_sgt(&to_panthor_bo(section->mem->obj)->base);
- if (!drm_WARN_ON(&ptdev->base, IS_ERR_OR_NULL(sgt)))
+
+ /* An sgt should have been requested when the kernel BO was GPU-mapped. */
+ sgt = to_panthor_bo(section->mem->obj)->dmap.sgt;
+ if (!drm_WARN_ON_ONCE(&ptdev->base, !sgt))
dma_sync_sgtable_for_device(ptdev->base.dev, sgt, DMA_TO_DEVICE);
}
}
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index cd49859da89b..13295d7a593d 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -2,13 +2,21 @@
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
/* Copyright 2023 Collabora ltd. */
/* Copyright 2025 Amazon.com, Inc. or its affiliates */
+/* Copyright 2025 ARM Limited. All rights reserved. */
#include <linux/cleanup.h>
+#include <linux/debugfs.h>
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_file.h>
+#include <drm/drm_gpuvm.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_prime.h>
#include <drm/drm_print.h>
#include <drm/panthor_drm.h>
@@ -42,7 +50,7 @@ static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo)
static void panthor_gem_debugfs_bo_add(struct panthor_gem_object *bo)
{
- struct panthor_device *ptdev = container_of(bo->base.base.dev,
+ struct panthor_device *ptdev = container_of(bo->base.dev,
struct panthor_device, base);
bo->debugfs.creator.tgid = current->tgid;
@@ -55,7 +63,7 @@ static void panthor_gem_debugfs_bo_add(struct panthor_gem_object *bo)
static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo)
{
- struct panthor_device *ptdev = container_of(bo->base.base.dev,
+ struct panthor_device *ptdev = container_of(bo->base.dev,
struct panthor_device, base);
if (list_empty(&bo->debugfs.node))
@@ -78,9 +86,9 @@ static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo) {}
#endif
static bool
-should_map_wc(struct panthor_gem_object *bo, struct panthor_vm *exclusive_vm)
+should_map_wc(struct panthor_gem_object *bo)
{
- struct panthor_device *ptdev = container_of(bo->base.base.dev, struct panthor_device, base);
+ struct panthor_device *ptdev = container_of(bo->base.dev, struct panthor_device, base);
/* We can't do uncached mappings if the device is coherent,
* because the zeroing done by the shmem layer at page allocation
@@ -110,132 +118,359 @@ should_map_wc(struct panthor_gem_object *bo, struct panthor_vm *exclusive_vm)
return true;
}
-static void panthor_gem_free_object(struct drm_gem_object *obj)
+static bool is_gpu_mapped(struct panthor_gem_object *bo,
+ enum panthor_gem_reclaim_state *state)
{
- struct panthor_gem_object *bo = to_panthor_bo(obj);
- struct drm_gem_object *vm_root_gem = bo->exclusive_vm_root_gem;
+ struct drm_gpuvm_bo *vm_bo;
+ u32 vm_count = 0;
- panthor_gem_debugfs_bo_rm(bo);
+ drm_gem_for_each_gpuvm_bo(vm_bo, &bo->base) {
+ /* Skip evicted GPU mappings. */
+ if (vm_bo->evicted)
+ continue;
- /*
- * Label might have been allocated with kstrdup_const(),
- * we need to take that into account when freeing the memory
- */
- kfree_const(bo->label.str);
+ if (vm_count++) {
+ *state = PANTHOR_GEM_GPU_MAPPED_MULTI_VM;
+ break;
+ }
- mutex_destroy(&bo->label.lock);
+ *state = PANTHOR_GEM_GPU_MAPPED_SINGLE_VM;
+ }
- drm_gem_free_mmap_offset(&bo->base.base);
- drm_gem_shmem_free(&bo->base);
- drm_gem_object_put(vm_root_gem);
+ return vm_count > 0;
}
-/**
- * panthor_kernel_bo_destroy() - Destroy a kernel buffer object
- * @bo: Kernel buffer object to destroy. If NULL or an ERR_PTR(), the destruction
- * is skipped.
- */
-void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
+static enum panthor_gem_reclaim_state
+panthor_gem_evaluate_reclaim_state_locked(struct panthor_gem_object *bo)
{
- struct panthor_vm *vm;
+ enum panthor_gem_reclaim_state gpu_mapped_state;
- if (IS_ERR_OR_NULL(bo))
+ dma_resv_assert_held(bo->base.resv);
+ lockdep_assert_held(&bo->base.gpuva.lock);
+
+ /* If pages have not been allocated, there's nothing to reclaim. */
+ if (!bo->backing.pages)
+ return PANTHOR_GEM_UNRECLAIMABLE;
+
+ /* If memory is pinned, we prevent reclaim. */
+ if (refcount_read(&bo->backing.pin_count))
+ return PANTHOR_GEM_UNRECLAIMABLE;
+
+ if (is_gpu_mapped(bo, &gpu_mapped_state))
+ return gpu_mapped_state;
+
+ if (refcount_read(&bo->cmap.mmap_count))
+ return PANTHOR_GEM_MMAPPED;
+
+ return PANTHOR_GEM_UNUSED;
+}
+
+void panthor_gem_update_reclaim_state_locked(struct panthor_gem_object *bo,
+ enum panthor_gem_reclaim_state *old_statep)
+{
+ struct panthor_device *ptdev = container_of(bo->base.dev, struct panthor_device, base);
+ enum panthor_gem_reclaim_state old_state = bo->reclaim_state;
+ enum panthor_gem_reclaim_state new_state;
+ bool was_gpu_mapped, is_gpu_mapped;
+
+ if (old_statep)
+ *old_statep = old_state;
+
+ new_state = panthor_gem_evaluate_reclaim_state_locked(bo);
+ if (new_state == old_state)
return;
- vm = bo->vm;
- panthor_kernel_bo_vunmap(bo);
+ was_gpu_mapped = old_state == PANTHOR_GEM_GPU_MAPPED_MULTI_VM ||
+ old_state == PANTHOR_GEM_GPU_MAPPED_SINGLE_VM;
+ is_gpu_mapped = new_state == PANTHOR_GEM_GPU_MAPPED_MULTI_VM ||
+ new_state == PANTHOR_GEM_GPU_MAPPED_SINGLE_VM;
- drm_WARN_ON(bo->obj->dev,
- to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm));
- panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size);
- panthor_vm_free_va(vm, &bo->va_node);
- drm_gem_object_put(bo->obj);
- panthor_vm_put(vm);
- kfree(bo);
+ if (is_gpu_mapped && !was_gpu_mapped)
+ ptdev->reclaim.gpu_mapped_count += bo->base.size >> PAGE_SHIFT;
+ else if (!is_gpu_mapped && was_gpu_mapped)
+ ptdev->reclaim.gpu_mapped_count -= bo->base.size >> PAGE_SHIFT;
+
+ switch (new_state) {
+ case PANTHOR_GEM_UNUSED:
+ drm_gem_lru_move_tail(&ptdev->reclaim.unused, &bo->base);
+ break;
+ case PANTHOR_GEM_MMAPPED:
+ drm_gem_lru_move_tail(&ptdev->reclaim.mmapped, &bo->base);
+ break;
+ case PANTHOR_GEM_GPU_MAPPED_SINGLE_VM:
+ panthor_vm_update_bo_reclaim_lru_locked(bo);
+ break;
+ case PANTHOR_GEM_GPU_MAPPED_MULTI_VM:
+ drm_gem_lru_move_tail(&ptdev->reclaim.gpu_mapped_shared, &bo->base);
+ break;
+ case PANTHOR_GEM_UNRECLAIMABLE:
+ drm_gem_lru_remove(&bo->base);
+ break;
+ default:
+ drm_WARN(&ptdev->base, true, "invalid GEM reclaim state (%d)\n", new_state);
+ break;
+ }
+
+ bo->reclaim_state = new_state;
}
-/**
- * panthor_kernel_bo_create() - Create and map a GEM object to a VM
- * @ptdev: Device.
- * @vm: VM to map the GEM to.
- * @size: Size of the buffer object.
- * @bo_flags: Combination of drm_panthor_bo_flags flags.
- * @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those
- * that are related to map operations).
- * @gpu_va: GPU address assigned when mapping to the VM.
- * If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be
- * automatically allocated.
- * @name: Descriptive label of the BO's contents
- *
- * Return: A valid pointer in case of success, an ERR_PTR() otherwise.
- */
-struct panthor_kernel_bo *
-panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
- size_t size, u32 bo_flags, u32 vm_map_flags,
- u64 gpu_va, const char *name)
+static void
+bo_assert_locked_or_gone(struct panthor_gem_object *bo)
+{
+ /* If the refcount is zero, the BO is being freed, and we
+ * allow the lock to not be held in that particular case.
+ */
+ if (kref_read(&bo->base.refcount))
+ dma_resv_assert_held(bo->base.resv);
+}
+
+static void
+panthor_gem_backing_cleanup_locked(struct panthor_gem_object *bo)
+{
+ bo_assert_locked_or_gone(bo);
+
+ if (!bo->backing.pages)
+ return;
+
+ drm_gem_put_pages(&bo->base, bo->backing.pages, true, false);
+ bo->backing.pages = NULL;
+}
+
+static int
+panthor_gem_backing_get_pages_locked(struct panthor_gem_object *bo)
+{
+ struct page **pages;
+
+ dma_resv_assert_held(bo->base.resv);
+
+ if (bo->backing.pages)
+ return 0;
+
+ pages = drm_gem_get_pages(&bo->base);
+ if (IS_ERR(pages)) {
+ drm_dbg_driver(bo->base.dev, "Failed to get pages (%pe)\n", pages);
+ return PTR_ERR(pages);
+ }
+
+ bo->backing.pages = pages;
+ return 0;
+}
+
+static int panthor_gem_backing_pin_locked(struct panthor_gem_object *bo)
{
- struct drm_gem_shmem_object *obj;
- struct panthor_kernel_bo *kbo;
- struct panthor_gem_object *bo;
- u32 debug_flags = PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL;
int ret;
- if (drm_WARN_ON(&ptdev->base, !vm))
- return ERR_PTR(-EINVAL);
+ dma_resv_assert_held(bo->base.resv);
+ drm_WARN_ON_ONCE(bo->base.dev, drm_gem_is_imported(&bo->base));
- kbo = kzalloc_obj(*kbo);
- if (!kbo)
- return ERR_PTR(-ENOMEM);
+ if (refcount_inc_not_zero(&bo->backing.pin_count))
+ return 0;
- obj = drm_gem_shmem_create(&ptdev->base, size);
- if (IS_ERR(obj)) {
- ret = PTR_ERR(obj);
- goto err_free_bo;
+ ret = panthor_gem_backing_get_pages_locked(bo);
+ if (!ret) {
+ refcount_set(&bo->backing.pin_count, 1);
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
}
- bo = to_panthor_bo(&obj->base);
- kbo->obj = &obj->base;
- bo->flags = bo_flags;
- bo->base.map_wc = should_map_wc(bo, vm);
- bo->exclusive_vm_root_gem = panthor_vm_root_gem(vm);
- drm_gem_object_get(bo->exclusive_vm_root_gem);
- bo->base.base.resv = bo->exclusive_vm_root_gem->resv;
+ return ret;
+}
- if (vm == panthor_fw_vm(ptdev))
- debug_flags |= PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED;
+static void panthor_gem_backing_unpin_locked(struct panthor_gem_object *bo)
+{
+ bo_assert_locked_or_gone(bo);
+ drm_WARN_ON_ONCE(bo->base.dev, drm_gem_is_imported(&bo->base));
- panthor_gem_kernel_bo_set_label(kbo, name);
- panthor_gem_debugfs_set_usage_flags(to_panthor_bo(kbo->obj), debug_flags);
+ if (refcount_dec_and_test(&bo->backing.pin_count)) {
+ /* We don't release anything when pin_count drops to zero.
+ * Pages stay there until an explicit cleanup is requested.
+ */
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+ }
+}
- /* The system and GPU MMU page size might differ, which becomes a
- * problem for FW sections that need to be mapped at explicit address
- * since our PAGE_SIZE alignment might cover a VA range that's
- * expected to be used for another section.
- * Make sure we never map more than we need.
- */
- size = ALIGN(size, panthor_vm_page_size(vm));
- ret = panthor_vm_alloc_va(vm, gpu_va, size, &kbo->va_node);
+static void
+panthor_gem_dev_map_cleanup_locked(struct panthor_gem_object *bo)
+{
+ bo_assert_locked_or_gone(bo);
+
+ if (!bo->dmap.sgt)
+ return;
+
+ dma_unmap_sgtable(drm_dev_dma_dev(bo->base.dev), bo->dmap.sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(bo->dmap.sgt);
+ kfree(bo->dmap.sgt);
+ bo->dmap.sgt = NULL;
+}
+
+static struct sg_table *
+panthor_gem_dev_map_get_sgt_locked(struct panthor_gem_object *bo)
+{
+ struct sg_table *sgt;
+ int ret;
+
+ dma_resv_assert_held(bo->base.resv);
+
+ if (bo->dmap.sgt)
+ return bo->dmap.sgt;
+
+ ret = panthor_gem_backing_get_pages_locked(bo);
if (ret)
- goto err_put_obj;
+ return ERR_PTR(ret);
- ret = panthor_vm_map_bo_range(vm, bo, 0, size, kbo->va_node.start, vm_map_flags);
+ sgt = drm_prime_pages_to_sg(bo->base.dev, bo->backing.pages,
+ bo->base.size >> PAGE_SHIFT);
+ if (IS_ERR(sgt))
+ return sgt;
+
+ /* Map the pages for use by the h/w. */
+ ret = dma_map_sgtable(drm_dev_dma_dev(bo->base.dev), sgt, DMA_BIDIRECTIONAL, 0);
if (ret)
- goto err_free_va;
+ goto err_free_sgt;
- kbo->vm = panthor_vm_get(vm);
- return kbo;
+ bo->dmap.sgt = sgt;
+ return sgt;
-err_free_va:
- panthor_vm_free_va(vm, &kbo->va_node);
+err_free_sgt:
+ sg_free_table(sgt);
+ kfree(sgt);
+ return ERR_PTR(ret);
+}
-err_put_obj:
- drm_gem_object_put(&obj->base);
+struct sg_table *
+panthor_gem_get_dev_sgt(struct panthor_gem_object *bo)
+{
+ struct sg_table *sgt;
-err_free_bo:
- kfree(kbo);
+ dma_resv_lock(bo->base.resv, NULL);
+ sgt = panthor_gem_dev_map_get_sgt_locked(bo);
+ dma_resv_unlock(bo->base.resv);
+
+ return sgt;
+}
+
+static void
+panthor_gem_vmap_cleanup_locked(struct panthor_gem_object *bo)
+{
+ if (!bo->cmap.vaddr)
+ return;
+
+ vunmap(bo->cmap.vaddr);
+ bo->cmap.vaddr = NULL;
+ panthor_gem_backing_unpin_locked(bo);
+}
+
+static int
+panthor_gem_prep_for_cpu_map_locked(struct panthor_gem_object *bo)
+{
+ if (should_map_wc(bo)) {
+ struct sg_table *sgt;
+
+ sgt = panthor_gem_dev_map_get_sgt_locked(bo);
+ if (IS_ERR(sgt))
+ return PTR_ERR(sgt);
+ }
+
+ return 0;
+}
+
+static void *
+panthor_gem_vmap_get_locked(struct panthor_gem_object *bo)
+{
+ pgprot_t prot = PAGE_KERNEL;
+ void *vaddr;
+ int ret;
+
+ dma_resv_assert_held(bo->base.resv);
+
+ if (drm_WARN_ON_ONCE(bo->base.dev, drm_gem_is_imported(&bo->base)))
+ return ERR_PTR(-EINVAL);
+
+ if (refcount_inc_not_zero(&bo->cmap.vaddr_use_count)) {
+ drm_WARN_ON_ONCE(bo->base.dev, !bo->cmap.vaddr);
+ return bo->cmap.vaddr;
+ }
+
+ ret = panthor_gem_backing_pin_locked(bo);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = panthor_gem_prep_for_cpu_map_locked(bo);
+ if (ret)
+ goto err_unpin;
+
+ if (should_map_wc(bo))
+ prot = pgprot_writecombine(prot);
+
+ vaddr = vmap(bo->backing.pages, bo->base.size >> PAGE_SHIFT, VM_MAP, prot);
+ if (!vaddr) {
+ ret = -ENOMEM;
+ goto err_unpin;
+ }
+
+ bo->cmap.vaddr = vaddr;
+ refcount_set(&bo->cmap.vaddr_use_count, 1);
+ return vaddr;
+
+err_unpin:
+ panthor_gem_backing_unpin_locked(bo);
return ERR_PTR(ret);
}
+static void
+panthor_gem_vmap_put_locked(struct panthor_gem_object *bo)
+{
+ dma_resv_assert_held(bo->base.resv);
+
+ if (drm_WARN_ON_ONCE(bo->base.dev, drm_gem_is_imported(&bo->base)))
+ return;
+
+ if (refcount_dec_and_test(&bo->cmap.vaddr_use_count))
+ panthor_gem_vmap_cleanup_locked(bo);
+}
+
+static void panthor_gem_free_object(struct drm_gem_object *obj)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+ struct drm_gem_object *vm_root_gem = bo->exclusive_vm_root_gem;
+
+ panthor_gem_debugfs_bo_rm(bo);
+
+ /*
+ * Label might have been allocated with kstrdup_const(),
+ * we need to take that into account when freeing the memory
+ */
+ kfree_const(bo->label.str);
+
+ mutex_destroy(&bo->label.lock);
+
+ if (drm_gem_is_imported(obj)) {
+ drm_prime_gem_destroy(obj, bo->dmap.sgt);
+ } else {
+ /* The last ref on the GEM object can be released
+ * by the shrinker, which can't block on the resv
+ * lock acquisition. In practice, even if we were
+ * taking the lock, it wouldn't block because we're
+ * the last piece of code having a visibility on
+ * this GEM, but lockdep can't see that, so we've
+ * just tought the _cleanup_locked() helpers about
+ * this "being freed" exception, and we call those
+ * without the lock held here.
+ */
+ panthor_gem_vmap_cleanup_locked(bo);
+ panthor_gem_dev_map_cleanup_locked(bo);
+ panthor_gem_backing_cleanup_locked(bo);
+ }
+
+ drm_gem_object_release(obj);
+
+ kfree(bo);
+ drm_gem_object_put(vm_root_gem);
+}
+
static struct sg_table *
panthor_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
enum dma_data_direction dir)
@@ -263,15 +498,15 @@ panthor_gem_prime_begin_cpu_access(struct dma_buf *dma_buf,
{
struct drm_gem_object *obj = dma_buf->priv;
struct drm_device *dev = obj->dev;
- struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
struct dma_buf_attachment *attach;
dma_resv_lock(obj->resv, NULL);
- if (shmem->sgt)
- dma_sync_sgtable_for_cpu(dev->dev, shmem->sgt, dir);
+ if (bo->dmap.sgt)
+ dma_sync_sgtable_for_cpu(drm_dev_dma_dev(dev), bo->dmap.sgt, dir);
- if (shmem->vaddr)
- invalidate_kernel_vmap_range(shmem->vaddr, shmem->base.size);
+ if (bo->cmap.vaddr)
+ invalidate_kernel_vmap_range(bo->cmap.vaddr, bo->base.size);
list_for_each_entry(attach, &dma_buf->attachments, node) {
struct sg_table *sgt = attach->priv;
@@ -290,7 +525,7 @@ panthor_gem_prime_end_cpu_access(struct dma_buf *dma_buf,
{
struct drm_gem_object *obj = dma_buf->priv;
struct drm_device *dev = obj->dev;
- struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
struct dma_buf_attachment *attach;
dma_resv_lock(obj->resv, NULL);
@@ -301,11 +536,11 @@ panthor_gem_prime_end_cpu_access(struct dma_buf *dma_buf,
dma_sync_sgtable_for_device(attach->dev, sgt, dir);
}
- if (shmem->vaddr)
- flush_kernel_vmap_range(shmem->vaddr, shmem->base.size);
+ if (bo->cmap.vaddr)
+ flush_kernel_vmap_range(bo->cmap.vaddr, bo->base.size);
- if (shmem->sgt)
- dma_sync_sgtable_for_device(dev->dev, shmem->sgt, dir);
+ if (bo->dmap.sgt)
+ dma_sync_sgtable_for_device(drm_dev_dma_dev(dev), bo->dmap.sgt, dir);
dma_resv_unlock(obj->resv);
return 0;
@@ -362,53 +597,466 @@ panthor_gem_prime_import(struct drm_device *dev,
return drm_gem_prime_import(dev, dma_buf);
}
+static void panthor_gem_print_info(struct drm_printer *p, unsigned int indent,
+ const struct drm_gem_object *obj)
+{
+ const struct panthor_gem_object *bo = to_panthor_bo(obj);
+
+ if (drm_gem_is_imported(&bo->base))
+ return;
+
+ drm_printf_indent(p, indent, "resident=%s\n", str_true_false(bo->backing.pages));
+ drm_printf_indent(p, indent, "pages_pin_count=%u\n", refcount_read(&bo->backing.pin_count));
+ drm_printf_indent(p, indent, "vmap_use_count=%u\n",
+ refcount_read(&bo->cmap.vaddr_use_count));
+ drm_printf_indent(p, indent, "vaddr=%p\n", bo->cmap.vaddr);
+ drm_printf_indent(p, indent, "mmap_count=%u\n", refcount_read(&bo->cmap.mmap_count));
+}
+
+static int panthor_gem_pin_locked(struct drm_gem_object *obj)
+{
+ if (!drm_gem_is_imported(obj))
+ return panthor_gem_backing_pin_locked(to_panthor_bo(obj));
+
+ return 0;
+}
+
+static void panthor_gem_unpin_locked(struct drm_gem_object *obj)
+{
+ if (!drm_gem_is_imported(obj))
+ panthor_gem_backing_unpin_locked(to_panthor_bo(obj));
+}
+
+int panthor_gem_pin(struct panthor_gem_object *bo)
+{
+ int ret = 0;
+
+ if (drm_gem_is_imported(&bo->base))
+ return 0;
+
+ if (refcount_inc_not_zero(&bo->backing.pin_count))
+ return 0;
+
+ dma_resv_lock(bo->base.resv, NULL);
+ ret = panthor_gem_backing_pin_locked(bo);
+ dma_resv_unlock(bo->base.resv);
+
+ return ret;
+}
+
+void panthor_gem_unpin(struct panthor_gem_object *bo)
+{
+ if (drm_gem_is_imported(&bo->base))
+ return;
+
+ if (refcount_dec_not_one(&bo->backing.pin_count))
+ return;
+
+ dma_resv_lock(bo->base.resv, NULL);
+ panthor_gem_backing_unpin_locked(bo);
+ dma_resv_unlock(bo->base.resv);
+}
+
+int panthor_gem_swapin_locked(struct panthor_gem_object *bo)
+{
+ struct sg_table *sgt;
+
+ dma_resv_assert_held(bo->base.resv);
+
+ if (drm_WARN_ON_ONCE(bo->base.dev, drm_gem_is_imported(&bo->base)))
+ return -EINVAL;
+
+ sgt = panthor_gem_dev_map_get_sgt_locked(bo);
+ if (IS_ERR(sgt))
+ return PTR_ERR(sgt);
+
+ return 0;
+}
+
+static void panthor_gem_evict_locked(struct panthor_gem_object *bo)
+{
+ dma_resv_assert_held(bo->base.resv);
+ lockdep_assert_held(&bo->base.gpuva.lock);
+
+ if (drm_WARN_ON_ONCE(bo->base.dev, drm_gem_is_imported(&bo->base)))
+ return;
+
+ if (drm_WARN_ON_ONCE(bo->base.dev, refcount_read(&bo->backing.pin_count)))
+ return;
+
+ if (drm_WARN_ON_ONCE(bo->base.dev, !bo->backing.pages))
+ return;
+
+ panthor_gem_dev_map_cleanup_locked(bo);
+ panthor_gem_backing_cleanup_locked(bo);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+}
+
+static struct sg_table *panthor_gem_get_sg_table(struct drm_gem_object *obj)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+
+ drm_WARN_ON_ONCE(obj->dev, drm_gem_is_imported(obj));
+ drm_WARN_ON_ONCE(obj->dev, !bo->backing.pages);
+ drm_WARN_ON_ONCE(obj->dev, !refcount_read(&bo->backing.pin_count));
+
+ return drm_prime_pages_to_sg(obj->dev, bo->backing.pages, obj->size >> PAGE_SHIFT);
+}
+
+static int panthor_gem_vmap_locked(struct drm_gem_object *obj,
+ struct iosys_map *map)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+ void *vaddr;
+
+ dma_resv_assert_held(obj->resv);
+
+ if (drm_gem_is_imported(obj))
+ return dma_buf_vmap(obj->import_attach->dmabuf, map);
+
+ vaddr = panthor_gem_vmap_get_locked(bo);
+ if (IS_ERR(vaddr))
+ return PTR_ERR(vaddr);
+
+ iosys_map_set_vaddr(map, vaddr);
+ return 0;
+}
+
+static void panthor_gem_vunmap_locked(struct drm_gem_object *obj,
+ struct iosys_map *map)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+
+ dma_resv_assert_held(obj->resv);
+
+ if (drm_gem_is_imported(obj)) {
+ dma_buf_vunmap(obj->import_attach->dmabuf, map);
+ } else {
+ drm_WARN_ON_ONCE(obj->dev, bo->cmap.vaddr != map->vaddr);
+ panthor_gem_vmap_put_locked(bo);
+ }
+}
+
+static int panthor_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+ int ret;
+
+ if (drm_gem_is_imported(obj)) {
+ /* Reset both vm_ops and vm_private_data, so we don't end up with
+ * vm_ops pointing to our implementation if the dma-buf backend
+ * doesn't set those fields.
+ */
+ vma->vm_private_data = NULL;
+ vma->vm_ops = NULL;
+
+ ret = dma_buf_mmap(obj->dma_buf, vma, 0);
+
+ /* Drop the reference drm_gem_mmap_obj() acquired.*/
+ if (!ret)
+ drm_gem_object_put(obj);
+
+ return ret;
+ }
+
+ if (is_cow_mapping(vma->vm_flags))
+ return -EINVAL;
+
+ if (!refcount_inc_not_zero(&bo->cmap.mmap_count)) {
+ dma_resv_lock(obj->resv, NULL);
+ if (!refcount_inc_not_zero(&bo->cmap.mmap_count)) {
+ refcount_set(&bo->cmap.mmap_count, 1);
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+ }
+ dma_resv_unlock(obj->resv);
+ }
+
+ vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+ if (should_map_wc(bo))
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return 0;
+}
+
static enum drm_gem_object_status panthor_gem_status(struct drm_gem_object *obj)
{
struct panthor_gem_object *bo = to_panthor_bo(obj);
enum drm_gem_object_status res = 0;
- if (drm_gem_is_imported(&bo->base.base) || bo->base.pages)
+ if (drm_gem_is_imported(&bo->base) || bo->backing.pages)
res |= DRM_GEM_OBJECT_RESIDENT;
return res;
}
+static vm_fault_t insert_page(struct vm_fault *vmf, unsigned int order, struct page *page)
+{
+ if (!order) {
+ return vmf_insert_pfn(vmf->vma, vmf->address, page_to_pfn(page));
+#ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP
+ } else if (order == PMD_ORDER) {
+ unsigned long pfn = page_to_pfn(page);
+ unsigned long paddr = pfn << PAGE_SHIFT;
+ bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK);
+
+ if (aligned &&
+ folio_test_pmd_mappable(page_folio(page))) {
+ pfn &= PMD_MASK >> PAGE_SHIFT;
+ return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
+ }
+#endif
+ }
+
+ return VM_FAULT_FALLBACK;
+}
+
+static vm_fault_t nonblocking_page_setup(struct vm_fault *vmf,
+ unsigned int order,
+ pgoff_t page_offset)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
+ vm_fault_t ret;
+
+ if (!dma_resv_trylock(bo->base.resv))
+ return VM_FAULT_RETRY;
+
+ if (bo->backing.pages)
+ ret = insert_page(vmf, order, bo->backing.pages[page_offset]);
+ else
+ ret = VM_FAULT_RETRY;
+
+ dma_resv_unlock(bo->base.resv);
+ return ret;
+}
+
+static vm_fault_t blocking_page_setup(struct vm_fault *vmf, unsigned int order,
+ struct panthor_gem_object *bo,
+ pgoff_t page_offset, bool mmap_lock_held)
+{
+ vm_fault_t ret;
+ int err;
+
+ err = dma_resv_lock_interruptible(bo->base.resv, NULL);
+ if (err)
+ return mmap_lock_held ? VM_FAULT_NOPAGE : VM_FAULT_RETRY;
+
+ err = panthor_gem_backing_get_pages_locked(bo);
+ if (!err)
+ err = panthor_gem_prep_for_cpu_map_locked(bo);
+
+ if (err) {
+ ret = mmap_lock_held ? VM_FAULT_SIGBUS : VM_FAULT_RETRY;
+ } else {
+ struct page *page = bo->backing.pages[page_offset];
+
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+
+ if (mmap_lock_held)
+ ret = insert_page(vmf, order, page);
+ else
+ ret = VM_FAULT_RETRY;
+ }
+
+ dma_resv_unlock(bo->base.resv);
+
+ return ret;
+}
+
+static vm_fault_t panthor_gem_any_fault(struct vm_fault *vmf, unsigned int order)
+{
+ struct vm_area_struct *vma = vmf->vma;
+ struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
+ loff_t num_pages = bo->base.size >> PAGE_SHIFT;
+ pgoff_t page_offset;
+ vm_fault_t ret;
+
+ if (order && order != PMD_ORDER)
+ return VM_FAULT_FALLBACK;
+
+ /* Offset to faulty address in the VMA. */
+ page_offset = vmf->pgoff - vma->vm_pgoff;
+ if (page_offset >= num_pages)
+ return VM_FAULT_SIGBUS;
+
+ ret = nonblocking_page_setup(vmf, order, page_offset);
+ if (ret != VM_FAULT_RETRY)
+ return ret;
+
+ /* Check if we're allowed to retry. */
+ if (fault_flag_allow_retry_first(vmf->flags)) {
+ /* If we're allowed to retry but not wait here, return
+ * immediately, the wait will be done when the fault
+ * handler is called again, with the mmap_lock held.
+ */
+ if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT)
+ return VM_FAULT_RETRY;
+
+ /* Wait with the mmap lock released, if we're allowed to. */
+ drm_gem_object_get(&bo->base);
+
+ if (vmf->flags & FAULT_FLAG_VMA_LOCK)
+ vma_end_read(vmf->vma);
+ else
+ mmap_read_unlock(vmf->vma->vm_mm);
+
+ ret = blocking_page_setup(vmf, order, bo, page_offset, false);
+ drm_gem_object_put(&bo->base);
+ return ret;
+ }
+
+ return blocking_page_setup(vmf, order, bo, page_offset, true);
+}
+
+static vm_fault_t panthor_gem_fault(struct vm_fault *vmf)
+{
+ return panthor_gem_any_fault(vmf, 0);
+}
+
+static void panthor_gem_vm_open(struct vm_area_struct *vma)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
+
+ drm_WARN_ON(bo->base.dev, drm_gem_is_imported(&bo->base));
+ drm_WARN_ON(bo->base.dev, !refcount_inc_not_zero(&bo->cmap.mmap_count));
+
+ drm_gem_vm_open(vma);
+}
+
+static void panthor_gem_vm_close(struct vm_area_struct *vma)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
+
+ if (drm_gem_is_imported(&bo->base))
+ goto out;
+
+ if (refcount_dec_not_one(&bo->cmap.mmap_count))
+ goto out;
+
+ dma_resv_lock(bo->base.resv, NULL);
+ if (refcount_dec_and_test(&bo->cmap.mmap_count)) {
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+ }
+ dma_resv_unlock(bo->base.resv);
+
+out:
+ drm_gem_object_put(&bo->base);
+}
+
+static const struct vm_operations_struct panthor_gem_vm_ops = {
+ .fault = panthor_gem_fault,
+#ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP
+ .huge_fault = panthor_gem_any_fault,
+#endif
+ .open = panthor_gem_vm_open,
+ .close = panthor_gem_vm_close,
+};
+
static const struct drm_gem_object_funcs panthor_gem_funcs = {
.free = panthor_gem_free_object,
- .print_info = drm_gem_shmem_object_print_info,
- .pin = drm_gem_shmem_object_pin,
- .unpin = drm_gem_shmem_object_unpin,
- .get_sg_table = drm_gem_shmem_object_get_sg_table,
- .vmap = drm_gem_shmem_object_vmap,
- .vunmap = drm_gem_shmem_object_vunmap,
- .mmap = drm_gem_shmem_object_mmap,
+ .print_info = panthor_gem_print_info,
+ .pin = panthor_gem_pin_locked,
+ .unpin = panthor_gem_unpin_locked,
+ .get_sg_table = panthor_gem_get_sg_table,
+ .vmap = panthor_gem_vmap_locked,
+ .vunmap = panthor_gem_vunmap_locked,
+ .mmap = panthor_gem_mmap,
.status = panthor_gem_status,
.export = panthor_gem_prime_export,
- .vm_ops = &drm_gem_shmem_vm_ops,
+ .vm_ops = &panthor_gem_vm_ops,
};
-/**
- * panthor_gem_create_object - Implementation of driver->gem_create_object.
- * @ddev: DRM device
- * @size: Size in bytes of the memory the object will reference
- *
- * This lets the GEM helpers allocate object structs for us, and keep
- * our BO stats correct.
- */
-struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t size)
+static struct panthor_gem_object *
+panthor_gem_alloc_object(u32 flags)
{
- struct panthor_gem_object *obj;
+ struct panthor_gem_object *bo;
- obj = kzalloc_obj(*obj);
- if (!obj)
+ bo = kzalloc_obj(*bo);
+ if (!bo)
return ERR_PTR(-ENOMEM);
- obj->base.base.funcs = &panthor_gem_funcs;
- mutex_init(&obj->label.lock);
+ bo->reclaim_state = PANTHOR_GEM_UNRECLAIMABLE;
+ bo->base.funcs = &panthor_gem_funcs;
+ bo->flags = flags;
+ mutex_init(&bo->label.lock);
+ panthor_gem_debugfs_bo_init(bo);
+ return bo;
+}
- panthor_gem_debugfs_bo_init(obj);
+static struct panthor_gem_object *
+panthor_gem_create(struct drm_device *dev, size_t size, uint32_t flags,
+ struct panthor_vm *exclusive_vm, u32 usage_flags)
+{
+ struct panthor_gem_object *bo;
+ int ret;
+
+ bo = panthor_gem_alloc_object(flags);
+ if (IS_ERR(bo))
+ return bo;
+
+ size = PAGE_ALIGN(size);
+ ret = drm_gem_object_init(dev, &bo->base, size);
+ if (ret)
+ goto err_put;
+
+ /* Our buffers are kept pinned, so allocating them
+ * from the MOVABLE zone is a really bad idea, and
+ * conflicts with CMA. See comments above new_inode()
+ * why this is required _and_ expected if you're
+ * going to pin these pages.
+ */
+ mapping_set_gfp_mask(bo->base.filp->f_mapping,
+ GFP_HIGHUSER | __GFP_RETRY_MAYFAIL | __GFP_NOWARN);
+
+ ret = drm_gem_create_mmap_offset(&bo->base);
+ if (ret)
+ goto err_put;
- return &obj->base.base;
+ if (exclusive_vm) {
+ bo->exclusive_vm_root_gem = panthor_vm_root_gem(exclusive_vm);
+ drm_gem_object_get(bo->exclusive_vm_root_gem);
+ bo->base.resv = bo->exclusive_vm_root_gem->resv;
+ }
+
+ panthor_gem_debugfs_set_usage_flags(bo, usage_flags);
+ return bo;
+
+err_put:
+ drm_gem_object_put(&bo->base);
+ return ERR_PTR(ret);
+}
+
+struct drm_gem_object *
+panthor_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt)
+{
+ struct panthor_gem_object *bo;
+ int ret;
+
+ bo = panthor_gem_alloc_object(0);
+ if (IS_ERR(bo))
+ return ERR_CAST(bo);
+
+ drm_gem_private_object_init(dev, &bo->base, attach->dmabuf->size);
+
+ ret = drm_gem_create_mmap_offset(&bo->base);
+ if (ret)
+ goto err_put;
+
+ bo->dmap.sgt = sgt;
+ return &bo->base;
+
+err_put:
+ drm_gem_object_put(&bo->base);
+ return ERR_PTR(ret);
}
/**
@@ -429,54 +1077,22 @@ panthor_gem_create_with_handle(struct drm_file *file,
u64 *size, u32 flags, u32 *handle)
{
int ret;
- struct drm_gem_shmem_object *shmem;
struct panthor_gem_object *bo;
- shmem = drm_gem_shmem_create(ddev, *size);
- if (IS_ERR(shmem))
- return PTR_ERR(shmem);
-
- bo = to_panthor_bo(&shmem->base);
- bo->flags = flags;
- bo->base.map_wc = should_map_wc(bo, exclusive_vm);
-
- if (exclusive_vm) {
- bo->exclusive_vm_root_gem = panthor_vm_root_gem(exclusive_vm);
- drm_gem_object_get(bo->exclusive_vm_root_gem);
- bo->base.base.resv = bo->exclusive_vm_root_gem->resv;
- }
-
- panthor_gem_debugfs_set_usage_flags(bo, 0);
-
- /* If this is a write-combine mapping, we query the sgt to force a CPU
- * cache flush (dma_map_sgtable() is called when the sgt is created).
- * This ensures the zero-ing is visible to any uncached mapping created
- * by vmap/mmap.
- * FIXME: Ideally this should be done when pages are allocated, not at
- * BO creation time.
- */
- if (shmem->map_wc) {
- struct sg_table *sgt;
-
- sgt = drm_gem_shmem_get_pages_sgt(shmem);
- if (IS_ERR(sgt)) {
- ret = PTR_ERR(sgt);
- goto out_put_gem;
- }
- }
+ bo = panthor_gem_create(ddev, *size, flags, exclusive_vm, 0);
+ if (IS_ERR(bo))
+ return PTR_ERR(bo);
/*
* Allocate an id of idr table where the obj is registered
* and handle has the id what user can see.
*/
- ret = drm_gem_handle_create(file, &shmem->base, handle);
+ ret = drm_gem_handle_create(file, &bo->base, handle);
if (!ret)
- *size = bo->base.base.size;
+ *size = bo->base.size;
-out_put_gem:
/* drop reference from allocate - handle holds it now. */
- drm_gem_object_put(&shmem->base);
-
+ drm_gem_object_put(&bo->base);
return ret;
}
@@ -521,18 +1137,18 @@ panthor_gem_sync(struct drm_gem_object *obj, u32 type,
u64 offset, u64 size)
{
struct panthor_gem_object *bo = to_panthor_bo(obj);
- struct drm_gem_shmem_object *shmem = &bo->base;
- const struct drm_device *dev = shmem->base.dev;
+ struct device *dma_dev = drm_dev_dma_dev(bo->base.dev);
struct sg_table *sgt;
struct scatterlist *sgl;
unsigned int count;
+ int ret;
/* Make sure the range is in bounds. */
- if (offset + size < offset || offset + size > shmem->base.size)
+ if (offset + size < offset || offset + size > bo->base.size)
return -EINVAL;
/* Disallow CPU-cache maintenance on imported buffers. */
- if (drm_gem_is_imported(&shmem->base))
+ if (drm_gem_is_imported(&bo->base))
return -EINVAL;
switch (type) {
@@ -545,16 +1161,28 @@ panthor_gem_sync(struct drm_gem_object *obj, u32 type,
}
/* Don't bother if it's WC-mapped */
- if (shmem->map_wc)
+ if (should_map_wc(bo))
return 0;
/* Nothing to do if the size is zero. */
if (size == 0)
return 0;
- sgt = drm_gem_shmem_get_pages_sgt(shmem);
- if (IS_ERR(sgt))
- return PTR_ERR(sgt);
+ ret = dma_resv_lock_interruptible(bo->base.resv, NULL);
+ if (ret)
+ return ret;
+
+ /* If there's no pages, there's no point pulling those back, bail out early. */
+ if (!bo->backing.pages) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ sgt = panthor_gem_dev_map_get_sgt_locked(bo);
+ if (IS_ERR(sgt)) {
+ ret = PTR_ERR(sgt);
+ goto out_unlock;
+ }
for_each_sgtable_dma_sg(sgt, sgl, count) {
if (size == 0)
@@ -593,14 +1221,365 @@ panthor_gem_sync(struct drm_gem_object *obj, u32 type,
*
* for the flush+invalidate case.
*/
- dma_sync_single_for_device(dev->dev, paddr, len, DMA_TO_DEVICE);
+ dma_sync_single_for_device(dma_dev, paddr, len, DMA_TO_DEVICE);
if (type == DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE)
- dma_sync_single_for_cpu(dev->dev, paddr, len, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(dma_dev, paddr, len, DMA_FROM_DEVICE);
}
+ ret = 0;
+
+out_unlock:
+ dma_resv_unlock(bo->base.resv);
+ return ret;
+}
+
+/**
+ * panthor_kernel_bo_destroy() - Destroy a kernel buffer object
+ * @bo: Kernel buffer object to destroy. If NULL or an ERR_PTR(), the destruction
+ * is skipped.
+ */
+void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
+{
+ struct panthor_device *ptdev;
+ struct panthor_vm *vm;
+
+ if (IS_ERR_OR_NULL(bo))
+ return;
+
+ ptdev = container_of(bo->obj->dev, struct panthor_device, base);
+ vm = bo->vm;
+ panthor_kernel_bo_vunmap(bo);
+
+ drm_WARN_ON(bo->obj->dev,
+ to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm));
+ panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size);
+ panthor_vm_free_va(vm, &bo->va_node);
+ if (vm == panthor_fw_vm(ptdev))
+ panthor_gem_unpin(to_panthor_bo(bo->obj));
+ drm_gem_object_put(bo->obj);
+ panthor_vm_put(vm);
+ kfree(bo);
+}
+
+/**
+ * panthor_kernel_bo_create() - Create and map a GEM object to a VM
+ * @ptdev: Device.
+ * @vm: VM to map the GEM to.
+ * @size: Size of the buffer object.
+ * @bo_flags: Combination of drm_panthor_bo_flags flags.
+ * @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those
+ * that are related to map operations).
+ * @gpu_va: GPU address assigned when mapping to the VM.
+ * If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be
+ * automatically allocated.
+ * @name: Descriptive label of the BO's contents
+ *
+ * Return: A valid pointer in case of success, an ERR_PTR() otherwise.
+ */
+struct panthor_kernel_bo *
+panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
+ size_t size, u32 bo_flags, u32 vm_map_flags,
+ u64 gpu_va, const char *name)
+{
+ struct panthor_kernel_bo *kbo;
+ struct panthor_gem_object *bo;
+ u32 debug_flags = PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL;
+ int ret;
+
+ if (drm_WARN_ON(&ptdev->base, !vm))
+ return ERR_PTR(-EINVAL);
+
+ kbo = kzalloc_obj(*kbo);
+ if (!kbo)
+ return ERR_PTR(-ENOMEM);
+
+ if (vm == panthor_fw_vm(ptdev))
+ debug_flags |= PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED;
+
+ bo = panthor_gem_create(&ptdev->base, size, bo_flags, vm, debug_flags);
+ if (IS_ERR(bo)) {
+ ret = PTR_ERR(bo);
+ goto err_free_kbo;
+ }
+
+ kbo->obj = &bo->base;
+
+ if (vm == panthor_fw_vm(ptdev)) {
+ ret = panthor_gem_pin(bo);
+ if (ret)
+ goto err_put_obj;
+ }
+
+ panthor_gem_kernel_bo_set_label(kbo, name);
+
+ /* The system and GPU MMU page size might differ, which becomes a
+ * problem for FW sections that need to be mapped at explicit address
+ * since our PAGE_SIZE alignment might cover a VA range that's
+ * expected to be used for another section.
+ * Make sure we never map more than we need.
+ */
+ size = ALIGN(size, panthor_vm_page_size(vm));
+ ret = panthor_vm_alloc_va(vm, gpu_va, size, &kbo->va_node);
+ if (ret)
+ goto err_unpin;
+
+ ret = panthor_vm_map_bo_range(vm, bo, 0, size, kbo->va_node.start, vm_map_flags);
+ if (ret)
+ goto err_free_va;
+
+ kbo->vm = panthor_vm_get(vm);
+ return kbo;
+
+err_free_va:
+ panthor_vm_free_va(vm, &kbo->va_node);
+
+err_unpin:
+ if (vm == panthor_fw_vm(ptdev))
+ panthor_gem_unpin(bo);
+
+err_put_obj:
+ drm_gem_object_put(&bo->base);
+
+err_free_kbo:
+ kfree(kbo);
+ return ERR_PTR(ret);
+}
+
+static bool can_swap(void)
+{
+ return get_nr_swap_pages() > 0;
+}
+
+static bool can_block(struct shrink_control *sc)
+{
+ /* If direct reclaim is allowed, we can always block.
+ * If kswapd reclaim is allowed, we can block, but only if we're called
+ * by the kswapd thread.
+ */
+ return (sc->gfp_mask & __GFP_DIRECT_RECLAIM) ||
+ ((sc->gfp_mask & __GFP_KSWAPD_RECLAIM) && current_is_kswapd());
+}
+
+static unsigned long
+panthor_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
+{
+ struct panthor_device *ptdev = shrinker->private_data;
+ unsigned long count;
+
+ /* We currently don't have a flag to tell when the content of a
+ * BO can be discarded.
+ */
+ if (!can_swap())
+ return 0;
+
+ /* This is racy, but that's okay because the returned count is just a
+ * hint. That's also what MSM is doing (no atomic var, it's relying on
+ * the fact unsigned long access is usually atomic), so if it's good
+ * enough for them, it's good enough for us too.
+ */
+ count = ptdev->reclaim.unused.count;
+ count += ptdev->reclaim.mmapped.count;
+
+ if (can_block(sc))
+ count += ptdev->reclaim.gpu_mapped_count;
+
+ return count ? count : SHRINK_EMPTY;
+}
+
+static bool panthor_gem_try_evict_no_resv_wait(struct drm_gem_object *obj,
+ struct ww_acquire_ctx *ticket)
+{
+ /*
+ * Track last locked entry for unwinding locks in error and
+ * success paths
+ */
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+ struct drm_gpuvm_bo *vm_bo, *last_locked = NULL;
+ enum panthor_gem_reclaim_state old_state;
+ int ret = 0;
+
+ /* To avoid potential lock ordering issue between bo_gpuva and
+ * mapping->i_mmap_rwsem, unmap the pages from CPU side before
+ * acquring the bo_gpuva lock. As the bo_resv lock is held, CPU
+ * page fault handler won't be able to map in the pages whilst
+ * eviction is in progress.
+ */
+ drm_vma_node_unmap(&bo->base.vma_node, bo->base.dev->anon_inode->i_mapping);
+
+ /* We take this lock when walking the list to prevent
+ * insertion/deletion.
+ */
+ /* We can only trylock in that path, because
+ * - allocation might happen while some of these locks are held
+ * - lock ordering is different in other paths
+ * vm_resv -> bo_resv -> bo_gpuva
+ * vs
+ * bo_resv -> bo_gpuva -> vm_resv
+ *
+ * If we fail to lock that's fine, we back off and will get
+ * back to it later.
+ */
+ if (!mutex_trylock(&bo->base.gpuva.lock))
+ return false;
+
+ drm_gem_for_each_gpuvm_bo(vm_bo, obj) {
+ struct dma_resv *resv = drm_gpuvm_resv(vm_bo->vm);
+
+ if (resv == obj->resv)
+ continue;
+
+ if (!dma_resv_trylock(resv)) {
+ ret = -EDEADLK;
+ goto out_unlock;
+ }
+
+ last_locked = vm_bo;
+ }
+
+ /* Update the state before trying to evict the buffer, if the state was
+ * updated to something that's harder to reclaim (higher value in the
+ * enum), skip it (will be processed when the relevant LRU is).
+ */
+ panthor_gem_update_reclaim_state_locked(bo, &old_state);
+ if (old_state < bo->reclaim_state) {
+ ret = -EAGAIN;
+ goto out_unlock;
+ }
+
+ /* Couldn't teardown the GPU mappings? Skip. */
+ ret = panthor_vm_evict_bo_mappings_locked(bo);
+ if (ret)
+ goto out_unlock;
+
+ /* If everything went fine, evict the object. */
+ panthor_gem_evict_locked(bo);
+
+out_unlock:
+ if (last_locked) {
+ drm_gem_for_each_gpuvm_bo(vm_bo, obj) {
+ struct dma_resv *resv = drm_gpuvm_resv(vm_bo->vm);
+
+ if (resv == obj->resv)
+ continue;
+
+ dma_resv_unlock(resv);
+
+ if (last_locked == vm_bo)
+ break;
+ }
+ }
+ mutex_unlock(&bo->base.gpuva.lock);
+
+ return ret == 0;
+}
+
+static bool panthor_gem_try_evict(struct drm_gem_object *obj,
+ struct ww_acquire_ctx *ticket)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(obj);
+
+ /* Wait was too long, skip. */
+ if (dma_resv_wait_timeout(obj->resv, DMA_RESV_USAGE_BOOKKEEP, false, 10) <= 0)
+ return false;
+
+ return panthor_gem_try_evict_no_resv_wait(&bo->base, ticket);
+}
+
+static unsigned long
+panthor_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc)
+{
+ struct panthor_device *ptdev = shrinker->private_data;
+ unsigned long remaining = 0;
+ unsigned long freed = 0;
+
+ if (!can_swap())
+ goto out;
+
+ freed += drm_gem_lru_scan(&ptdev->reclaim.unused,
+ sc->nr_to_scan - freed, &remaining,
+ panthor_gem_try_evict_no_resv_wait, NULL);
+ if (freed >= sc->nr_to_scan)
+ goto out;
+
+ freed += drm_gem_lru_scan(&ptdev->reclaim.mmapped,
+ sc->nr_to_scan - freed, &remaining,
+ panthor_gem_try_evict_no_resv_wait, NULL);
+ if (freed >= sc->nr_to_scan)
+ goto out;
+
+ if (!can_block(sc))
+ goto out;
+
+ freed += panthor_mmu_reclaim_priv_bos(ptdev, sc->nr_to_scan - freed,
+ &remaining, panthor_gem_try_evict);
+ if (freed >= sc->nr_to_scan)
+ goto out;
+
+ freed += drm_gem_lru_scan(&ptdev->reclaim.gpu_mapped_shared,
+ sc->nr_to_scan - freed, &remaining,
+ panthor_gem_try_evict, NULL);
+
+out:
+#ifdef CONFIG_DEBUG_FS
+ /* This is racy, but that's okay, because this is just debugfs
+ * reporting and doesn't need to be accurate.
+ */
+ ptdev->reclaim.nr_pages_reclaimed_on_last_scan = freed;
+#endif
+
+ /* If there are things to reclaim, try a couple times before giving up. */
+ if (!freed && remaining > 0 &&
+ atomic_inc_return(&ptdev->reclaim.retry_count) < 2)
+ return 0;
+
+ atomic_set(&ptdev->reclaim.retry_count, 0);
+
+ if (freed)
+ return freed;
+
+ /* There's nothing left to reclaim, or the resources are contended. Give up now. */
+ return SHRINK_STOP;
+}
+
+int panthor_gem_shrinker_init(struct panthor_device *ptdev)
+{
+ struct shrinker *shrinker;
+ int ret;
+
+ ret = drmm_mutex_init(&ptdev->base, &ptdev->reclaim.lock);
+ if (ret)
+ return ret;
+
+ INIT_LIST_HEAD(&ptdev->reclaim.vms);
+ drm_gem_lru_init(&ptdev->reclaim.unused, &ptdev->reclaim.lock);
+ drm_gem_lru_init(&ptdev->reclaim.mmapped, &ptdev->reclaim.lock);
+ drm_gem_lru_init(&ptdev->reclaim.gpu_mapped_shared, &ptdev->reclaim.lock);
+ ptdev->reclaim.gpu_mapped_count = 0;
+
+ /* Teach lockdep about lock ordering wrt. shrinker: */
+ fs_reclaim_acquire(GFP_KERNEL);
+ might_lock(&ptdev->reclaim.lock);
+ fs_reclaim_release(GFP_KERNEL);
+
+ shrinker = shrinker_alloc(0, "drm-panthor-gem");
+ if (!shrinker)
+ return -ENOMEM;
+
+ shrinker->count_objects = panthor_gem_shrinker_count;
+ shrinker->scan_objects = panthor_gem_shrinker_scan;
+ shrinker->private_data = ptdev;
+ ptdev->reclaim.shrinker = shrinker;
+
+ shrinker_register(shrinker);
return 0;
}
+void panthor_gem_shrinker_unplug(struct panthor_device *ptdev)
+{
+ if (ptdev->reclaim.shrinker)
+ shrinker_free(ptdev->reclaim.shrinker);
+}
+
#ifdef CONFIG_DEBUG_FS
struct gem_size_totals {
size_t size;
@@ -644,7 +1623,8 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
struct seq_file *m,
struct gem_size_totals *totals)
{
- unsigned int refcount = kref_read(&bo->base.base.refcount);
+ enum panthor_gem_reclaim_state reclaim_state = bo->reclaim_state;
+ unsigned int refcount = kref_read(&bo->base.refcount);
char creator_info[32] = {};
size_t resident_size;
u32 gem_usage_flags = bo->debugfs.flags;
@@ -654,21 +1634,21 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
if (!refcount)
return;
- resident_size = bo->base.pages ? bo->base.base.size : 0;
+ resident_size = bo->backing.pages ? bo->base.size : 0;
snprintf(creator_info, sizeof(creator_info),
"%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid);
seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd0x%-16lx",
creator_info,
- bo->base.base.name,
+ bo->base.name,
refcount,
- bo->base.base.size,
+ bo->base.size,
resident_size,
- drm_vma_node_start(&bo->base.base.vma_node));
+ drm_vma_node_start(&bo->base.vma_node));
- if (drm_gem_is_imported(&bo->base.base))
+ if (drm_gem_is_imported(&bo->base))
gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED;
- if (bo->base.base.dma_buf)
+ if (bo->base.dma_buf)
gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED;
seq_printf(m, "0x%-8x 0x%-10x", gem_state_flags, gem_usage_flags);
@@ -677,14 +1657,14 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
seq_printf(m, "%s\n", bo->label.str ? : "");
}
- totals->size += bo->base.base.size;
+ totals->size += bo->base.size;
totals->resident += resident_size;
- if (bo->base.madv > 0)
+ if (reclaim_state != PANTHOR_GEM_UNRECLAIMABLE)
totals->reclaimable += resident_size;
}
-void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev,
- struct seq_file *m)
+static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev,
+ struct seq_file *m)
{
struct gem_size_totals totals = {0};
struct panthor_gem_object *bo;
@@ -704,4 +1684,59 @@ void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev,
seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
totals.size, totals.resident, totals.reclaimable);
}
+
+static int panthor_gem_show_bos(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct panthor_device *ptdev =
+ container_of(dev, struct panthor_device, base);
+
+ panthor_gem_debugfs_print_bos(ptdev, m);
+
+ return 0;
+}
+
+static struct drm_info_list panthor_gem_debugfs_list[] = {
+ { "gems", panthor_gem_show_bos, 0, NULL },
+};
+
+static int shrink_get(void *data, u64 *val)
+{
+ struct panthor_device *ptdev =
+ container_of(data, struct panthor_device, base);
+
+ *val = ptdev->reclaim.nr_pages_reclaimed_on_last_scan;
+ return 0;
+}
+
+static int shrink_set(void *data, u64 val)
+{
+ struct panthor_device *ptdev =
+ container_of(data, struct panthor_device, base);
+ struct shrink_control sc = {
+ .gfp_mask = GFP_KERNEL,
+ .nr_to_scan = val,
+ };
+
+ fs_reclaim_acquire(GFP_KERNEL);
+ if (ptdev->reclaim.shrinker)
+ panthor_gem_shrinker_scan(ptdev->reclaim.shrinker, &sc);
+ fs_reclaim_release(GFP_KERNEL);
+
+ return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(panthor_gem_debugfs_shrink_fops,
+ shrink_get, shrink_set,
+ "0x%08llx\n");
+
+void panthor_gem_debugfs_init(struct drm_minor *minor)
+{
+ drm_debugfs_create_files(panthor_gem_debugfs_list,
+ ARRAY_SIZE(panthor_gem_debugfs_list),
+ minor->debugfs_root, minor);
+ debugfs_create_file("shrink", 0600, minor->debugfs_root,
+ minor->dev, &panthor_gem_debugfs_shrink_fops);
+}
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index 22519c570b5a..ae0491d0b121 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -1,11 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 or MIT */
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
/* Copyright 2023 Collabora ltd. */
+/* Copyright 2025 ARM Limited. All rights reserved. */
#ifndef __PANTHOR_GEM_H__
#define __PANTHOR_GEM_H__
-#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_gem.h>
#include <drm/drm_mm.h>
#include <linux/iosys-map.h>
@@ -61,11 +62,115 @@ struct panthor_gem_debugfs {
};
/**
+ * struct panthor_gem_backing - GEM memory backing related data
+ */
+struct panthor_gem_backing {
+ /** @pages: Pages requested with drm_gem_get_pages() */
+ struct page **pages;
+
+ /** @pin_count: Number of active pin requests on this GEM */
+ refcount_t pin_count;
+};
+
+/**
+ * struct panthor_gem_cpu_map - GEM CPU mapping related data
+ */
+struct panthor_gem_cpu_map {
+ /** @vaddr: Address returned by vmap() */
+ void *vaddr;
+
+ /** @vaddr_use_count: Number of active vmap() requests on this GEM */
+ refcount_t vaddr_use_count;
+
+ /** @mmap_count: Number of active mmap() requests on this GEM */
+ refcount_t mmap_count;
+};
+
+/**
+ * struct panthor_gem_dev_map - GEM device mapping related data
+ */
+struct panthor_gem_dev_map {
+ /** @sgt: Device mapped sg_table for this GEM */
+ struct sg_table *sgt;
+};
+
+/**
+ * enum panthor_gem_reclaim_state - Reclaim state of a GEM object
+ *
+ * This is defined in descending reclaimability order and some part
+ * of the code depends on that.
+ */
+enum panthor_gem_reclaim_state {
+ /**
+ * @PANTHOR_GEM_UNUSED: GEM is currently unused
+ *
+ * This can happen when the GEM was previously vmap-ed, mmap-ed,
+ * and/or GPU mapped and got unmapped. Because pages are lazily
+ * returned to the shmem layer, we want to keep a list of such
+ * BOs, because they should be fairly easy to reclaim (no need
+ * to wait for GPU to be done, and no need to tear down user
+ * mappings either).
+ */
+ PANTHOR_GEM_UNUSED,
+
+ /**
+ * @PANTHOR_GEM_MMAPPED: GEM is currently mmap-ed
+ *
+ * When a GEM has pages allocated and the mmap_count is > 0, the
+ * GEM is placed in the mmapped list. This comes right after
+ * unused because we can relatively easily tear down user mappings.
+ */
+ PANTHOR_GEM_MMAPPED,
+
+ /**
+ * @PANTHOR_GEM_GPU_MAPPED_SINGLE_VM: GEM is GPU mapped to only one VM
+ *
+ * When a GEM is mapped to a single VM, reclaim requests have more
+ * chances to succeed, because we only need to synchronize against
+ * a single GPU context. This is more annoying than reclaiming
+ * mmap-ed pages still, because we have to wait for in-flight jobs
+ * to land, and we might not be able to acquire all necessary locks
+ * at reclaim time either.
+ */
+ PANTHOR_GEM_GPU_MAPPED_SINGLE_VM,
+
+ /**
+ * @PANTHOR_GEM_GPU_MAPPED_MULTI_VM: GEM is GPU mapped to multiple VMs
+ *
+ * Like PANTHOR_GEM_GPU_MAPPED_SINGLE_VM, but the synchronization across
+ * VMs makes such BOs harder to reclaim.
+ */
+ PANTHOR_GEM_GPU_MAPPED_MULTI_VM,
+
+ /**
+ * @PANTHOR_GEM_UNRECLAIMABLE: GEM can't be reclaimed
+ *
+ * Happens when the GEM memory is pinned. It's also the state all GEM
+ * objects start in, because no memory is allocated until explicitly
+ * requested by a CPU or GPU map, meaning there's nothing to reclaim
+ * until such an allocation happens.
+ */
+ PANTHOR_GEM_UNRECLAIMABLE,
+};
+
+/**
* struct panthor_gem_object - Driver specific GEM object.
*/
struct panthor_gem_object {
- /** @base: Inherit from drm_gem_shmem_object. */
- struct drm_gem_shmem_object base;
+ /** @base: Inherit from drm_gem_object. */
+ struct drm_gem_object base;
+
+ /** @backing: Memory backing state */
+ struct panthor_gem_backing backing;
+
+ /** @cmap: CPU mapping state */
+ struct panthor_gem_cpu_map cmap;
+
+ /** @dmap: Device mapping state */
+ struct panthor_gem_dev_map dmap;
+
+ /** @reclaim_state: Cached reclaim state */
+ enum panthor_gem_reclaim_state reclaim_state;
/**
* @exclusive_vm_root_gem: Root GEM of the exclusive VM this GEM object
@@ -130,22 +235,30 @@ struct panthor_kernel_bo {
void *kmap;
};
-static inline
-struct panthor_gem_object *to_panthor_bo(struct drm_gem_object *obj)
-{
- return container_of(to_drm_gem_shmem_obj(obj), struct panthor_gem_object, base);
-}
+#define to_panthor_bo(obj) container_of_const(obj, struct panthor_gem_object, base)
void panthor_gem_init(struct panthor_device *ptdev);
-struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t size);
-
+struct drm_gem_object *
+panthor_gem_prime_import_sg_table(struct drm_device *dev,
+ struct dma_buf_attachment *attach,
+ struct sg_table *sgt);
int
panthor_gem_create_with_handle(struct drm_file *file,
struct drm_device *ddev,
struct panthor_vm *exclusive_vm,
u64 *size, u32 flags, uint32_t *handle);
+struct sg_table *
+panthor_gem_get_dev_sgt(struct panthor_gem_object *bo);
+int panthor_gem_pin(struct panthor_gem_object *bo);
+void panthor_gem_unpin(struct panthor_gem_object *bo);
+int panthor_gem_swapin_locked(struct panthor_gem_object *bo);
+void panthor_gem_update_reclaim_state_locked(struct panthor_gem_object *bo,
+ enum panthor_gem_reclaim_state *old_state);
+int panthor_gem_shrinker_init(struct panthor_device *ptdev);
+void panthor_gem_shrinker_unplug(struct panthor_device *ptdev);
+
void panthor_gem_bo_set_label(struct drm_gem_object *obj, const char *label);
void panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label);
int panthor_gem_sync(struct drm_gem_object *obj,
@@ -203,8 +316,7 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo);
#ifdef CONFIG_DEBUG_FS
-void panthor_gem_debugfs_print_bos(struct panthor_device *pfdev,
- struct seq_file *m);
+void panthor_gem_debugfs_init(struct drm_minor *minor);
#endif
#endif /* __PANTHOR_GEM_H__ */
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 75d98dad7b1d..fc930ee158a5 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: GPL-2.0 or MIT
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
/* Copyright 2023 Collabora ltd. */
+/* Copyright 2025 ARM Limited. All rights reserved. */
#include <drm/drm_debugfs.h>
#include <drm/drm_drv.h>
#include <drm/drm_exec.h>
+#include <drm/drm_file.h>
#include <drm/drm_gpuvm.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
@@ -130,6 +132,9 @@ struct panthor_vma {
* Only map related flags are accepted.
*/
u32 flags;
+
+ /** @evicted: True if the VMA has been evicted. */
+ bool evicted;
};
/**
@@ -198,10 +203,8 @@ struct panthor_vm_op_ctx {
*/
struct sg_table *sgt;
- /**
- * @map.new_vma: The new VMA object that will be inserted to the VA tree.
- */
- struct panthor_vma *new_vma;
+ /** @map.bo: the BO being mapped. */
+ struct panthor_gem_object *bo;
} map;
};
@@ -384,6 +387,18 @@ struct panthor_vm {
/** @locked_region.size: Size of the locked region. */
u64 size;
} locked_region;
+
+ /** @reclaim: Fields related to BO reclaim. */
+ struct {
+ /** @reclaim.lru: LRU of BOs that are only mapped to this VM. */
+ struct drm_gem_lru lru;
+
+ /**
+ * @reclaim.lru_node: Node used to insert the VM in
+ * panthor_device::reclaim::vms.
+ */
+ struct list_head lru_node;
+ } reclaim;
};
/**
@@ -688,6 +703,16 @@ int panthor_vm_active(struct panthor_vm *vm)
if (refcount_inc_not_zero(&vm->as.active_cnt))
goto out_dev_exit;
+ /* As soon as active is called, we place the VM at the end of the VM LRU.
+ * If something fails after that, the only downside is that this VM that
+ * never became active in the first place will be reclaimed last, but
+ * that's an acceptable trade-off.
+ */
+ mutex_lock(&ptdev->reclaim.lock);
+ if (vm->reclaim.lru.count)
+ list_move_tail(&vm->reclaim.lru_node, &ptdev->reclaim.vms);
+ mutex_unlock(&ptdev->reclaim.lock);
+
/* Make sure we don't race with lock/unlock_region() calls
* happening around VM bind operations.
*/
@@ -1083,8 +1108,15 @@ static void panthor_vm_bo_free(struct drm_gpuvm_bo *vm_bo)
{
struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj);
- if (!drm_gem_is_imported(&bo->base.base))
- drm_gem_shmem_unpin(&bo->base);
+ /* We couldn't call this when we unlinked, because the resv lock can't
+ * be taken in the dma signalling path, so call it now.
+ */
+ dma_resv_lock(bo->base.resv, NULL);
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+ dma_resv_unlock(bo->base.resv);
+
kfree(vm_bo);
}
@@ -1093,6 +1125,16 @@ static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
{
u32 remaining_pt_count = op_ctx->rsvd_page_tables.count -
op_ctx->rsvd_page_tables.ptr;
+ u32 op_type = op_ctx->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK;
+
+ /* If this is a map operation and no BO is attached, we're being called
+ * from vm_bo_validate() and we can't acquire the VM lock because it's
+ * already held. In that case, we just skip the deferred vm_bo cleanup,
+ * which is fine, because the vm_bo validation is not calling
+ * drm_gpuvm_bo_put_deferred().
+ */
+ bool skip_deferred_cleanup = op_type == DRM_PANTHOR_VM_BIND_OP_TYPE_MAP &&
+ !op_ctx->map.bo;
if (remaining_pt_count) {
kmem_cache_free_bulk(pt_cache, remaining_pt_count,
@@ -1105,10 +1147,16 @@ static void panthor_vm_cleanup_op_ctx(struct panthor_vm_op_ctx *op_ctx,
if (op_ctx->map.vm_bo)
drm_gpuvm_bo_put_deferred(op_ctx->map.vm_bo);
+ if (op_ctx->map.bo) {
+ panthor_gem_unpin(op_ctx->map.bo);
+ drm_gem_object_put(&op_ctx->map.bo->base);
+ }
+
for (u32 i = 0; i < ARRAY_SIZE(op_ctx->preallocated_vmas); i++)
kfree(op_ctx->preallocated_vmas[i]);
- drm_gpuvm_bo_deferred_cleanup(&vm->base);
+ if (!skip_deferred_cleanup)
+ drm_gpuvm_bo_deferred_cleanup(&vm->base);
}
static void
@@ -1180,6 +1228,44 @@ panthor_vm_op_ctx_prealloc_vmas(struct panthor_vm_op_ctx *op_ctx)
return 0;
}
+static void panthor_vm_init_op_ctx(struct panthor_vm_op_ctx *op_ctx,
+ u64 size, u64 va, u32 flags)
+{
+ memset(op_ctx, 0, sizeof(*op_ctx));
+ op_ctx->flags = flags;
+ op_ctx->va.range = size;
+ op_ctx->va.addr = va;
+}
+
+static int panthor_vm_op_ctx_prealloc_pts(struct panthor_vm_op_ctx *op_ctx)
+{
+ u64 size = op_ctx->va.range;
+ u64 va = op_ctx->va.addr;
+ int ret;
+
+ /* L1, L2 and L3 page tables.
+ * We could optimize L3 allocation by iterating over the sgt and merging
+ * 2M contiguous blocks, but it's simpler to over-provision and return
+ * the pages if they're not used.
+ */
+ u64 pt_count = ((ALIGN(va + size, 1ull << 39) - ALIGN_DOWN(va, 1ull << 39)) >> 39) +
+ ((ALIGN(va + size, 1ull << 30) - ALIGN_DOWN(va, 1ull << 30)) >> 30) +
+ ((ALIGN(va + size, 1ull << 21) - ALIGN_DOWN(va, 1ull << 21)) >> 21);
+
+ op_ctx->rsvd_page_tables.pages = kzalloc_objs(*op_ctx->rsvd_page_tables.pages,
+ pt_count);
+ if (!op_ctx->rsvd_page_tables.pages)
+ return -ENOMEM;
+
+ ret = kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, pt_count,
+ op_ctx->rsvd_page_tables.pages);
+ op_ctx->rsvd_page_tables.count = ret;
+ if (ret != pt_count)
+ return -ENOMEM;
+
+ return 0;
+}
+
#define PANTHOR_VM_BIND_OP_MAP_FLAGS \
(DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \
DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \
@@ -1195,7 +1281,6 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
{
struct drm_gpuvm_bo *preallocated_vm_bo;
struct sg_table *sgt = NULL;
- u64 pt_count;
int ret;
if (!bo)
@@ -1206,7 +1291,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
return -EINVAL;
/* Make sure the VA and size are in-bounds. */
- if (size > bo->base.base.size || offset > bo->base.base.size - size)
+ if (size > bo->base.size || offset > bo->base.size - size)
return -EINVAL;
/* If the BO has an exclusive VM attached, it can't be mapped to other VMs. */
@@ -1214,78 +1299,54 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx,
bo->exclusive_vm_root_gem != panthor_vm_root_gem(vm))
return -EINVAL;
- memset(op_ctx, 0, sizeof(*op_ctx));
- op_ctx->flags = flags;
- op_ctx->va.range = size;
- op_ctx->va.addr = va;
+ panthor_vm_init_op_ctx(op_ctx, size, va, flags);
ret = panthor_vm_op_ctx_prealloc_vmas(op_ctx);
if (ret)
goto err_cleanup;
- if (!drm_gem_is_imported(&bo->base.base)) {
- /* Pre-reserve the BO pages, so the map operation doesn't have to
- * allocate. This pin is dropped in panthor_vm_bo_free(), so
- * once we have successfully called drm_gpuvm_bo_create(),
- * GPUVM will take care of dropping the pin for us.
- */
- ret = drm_gem_shmem_pin(&bo->base);
- if (ret)
- goto err_cleanup;
- }
+ /* Pre-reserve the BO pages, so the map operation doesn't have to
+ * allocate.
+ */
+ ret = panthor_gem_pin(bo);
+ if (ret)
+ goto err_cleanup;
- sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
- if (IS_ERR(sgt)) {
- if (!drm_gem_is_imported(&bo->base.base))
- drm_gem_shmem_unpin(&bo->base);
+ drm_gem_object_get(&bo->base);
+ op_ctx->map.bo = bo;
+ sgt = panthor_gem_get_dev_sgt(bo);
+ if (IS_ERR(sgt)) {
ret = PTR_ERR(sgt);
goto err_cleanup;
}
- op_ctx->map.sgt = sgt;
-
- preallocated_vm_bo = drm_gpuvm_bo_create(&vm->base, &bo->base.base);
+ preallocated_vm_bo = drm_gpuvm_bo_create(&vm->base, &bo->base);
if (!preallocated_vm_bo) {
- if (!drm_gem_is_imported(&bo->base.base))
- drm_gem_shmem_unpin(&bo->base);
-
ret = -ENOMEM;
goto err_cleanup;
}
op_ctx->map.vm_bo = drm_gpuvm_bo_obtain_prealloc(preallocated_vm_bo);
-
op_ctx->map.bo_offset = offset;
- /* L1, L2 and L3 page tables.
- * We could optimize L3 allocation by iterating over the sgt and merging
- * 2M contiguous blocks, but it's simpler to over-provision and return
- * the pages if they're not used.
- */
- pt_count = ((ALIGN(va + size, 1ull << 39) - ALIGN_DOWN(va, 1ull << 39)) >> 39) +
- ((ALIGN(va + size, 1ull << 30) - ALIGN_DOWN(va, 1ull << 30)) >> 30) +
- ((ALIGN(va + size, 1ull << 21) - ALIGN_DOWN(va, 1ull << 21)) >> 21);
-
- op_ctx->rsvd_page_tables.pages = kzalloc_objs(*op_ctx->rsvd_page_tables.pages,
- pt_count);
- if (!op_ctx->rsvd_page_tables.pages) {
- ret = -ENOMEM;
+ ret = panthor_vm_op_ctx_prealloc_pts(op_ctx);
+ if (ret)
goto err_cleanup;
- }
- ret = kmem_cache_alloc_bulk(pt_cache, GFP_KERNEL, pt_count,
- op_ctx->rsvd_page_tables.pages);
- op_ctx->rsvd_page_tables.count = ret;
- if (ret != pt_count) {
- ret = -ENOMEM;
- goto err_cleanup;
+ /* Insert BO into the extobj list last, when we know nothing can fail. */
+ if (bo->base.resv != panthor_vm_resv(vm)) {
+ dma_resv_lock(panthor_vm_resv(vm), NULL);
+ drm_gpuvm_bo_extobj_add(op_ctx->map.vm_bo);
+ dma_resv_unlock(panthor_vm_resv(vm));
}
- /* Insert BO into the extobj list last, when we know nothing can fail. */
- dma_resv_lock(panthor_vm_resv(vm), NULL);
- drm_gpuvm_bo_extobj_add(op_ctx->map.vm_bo);
- dma_resv_unlock(panthor_vm_resv(vm));
+ /* And finally update the BO state. */
+ dma_resv_lock(bo->base.resv, NULL);
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+ dma_resv_unlock(bo->base.resv);
return 0;
@@ -1893,6 +1954,10 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm)
struct panthor_vm *vm = container_of(gpuvm, struct panthor_vm, base);
struct panthor_device *ptdev = vm->ptdev;
+ mutex_lock(&ptdev->reclaim.lock);
+ list_del_init(&vm->reclaim.lru_node);
+ mutex_unlock(&ptdev->reclaim.lock);
+
mutex_lock(&vm->heaps.lock);
if (drm_WARN_ON(&ptdev->base, vm->heaps.pool))
panthor_heap_pool_destroy(vm->heaps.pool);
@@ -2071,9 +2136,9 @@ static void panthor_vma_link(struct panthor_vm *vm,
{
struct panthor_gem_object *bo = to_panthor_bo(vma->base.gem.obj);
- mutex_lock(&bo->base.base.gpuva.lock);
+ mutex_lock(&bo->base.gpuva.lock);
drm_gpuva_link(&vma->base, vm_bo);
- mutex_unlock(&bo->base.base.gpuva.lock);
+ mutex_unlock(&bo->base.gpuva.lock);
}
static void panthor_vma_unlink(struct panthor_vma *vma)
@@ -2106,7 +2171,7 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
panthor_vma_init(vma, op_ctx->flags & PANTHOR_VM_MAP_FLAGS);
ret = panthor_vm_map_pages(vm, op->map.va.addr, flags_to_prot(vma->flags),
- op_ctx->map.sgt, op->map.gem.offset,
+ op_ctx->map.bo->dmap.sgt, op->map.gem.offset,
op->map.va.range);
if (ret) {
panthor_vm_op_ctx_return_vma(op_ctx, vma);
@@ -2125,11 +2190,12 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv)
static bool
iova_mapped_as_huge_page(struct drm_gpuva_op_map *op, u64 addr)
{
+ struct panthor_gem_object *bo = to_panthor_bo(op->gem.obj);
const struct page *pg;
pgoff_t bo_offset;
bo_offset = addr - op->va.addr + op->gem.offset;
- pg = to_panthor_bo(op->gem.obj)->base.pages[bo_offset >> PAGE_SHIFT];
+ pg = bo->backing.pages[bo_offset >> PAGE_SHIFT];
return folio_size(page_folio(pg)) >= SZ_2M;
}
@@ -2189,21 +2255,27 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
* atomicity. panthor_vm_lock_region() bails out early if the new region
* is already part of the locked region, so no need to do this check here.
*/
- panthor_vm_lock_region(vm, unmap_start, unmap_range);
- panthor_vm_unmap_pages(vm, unmap_start, unmap_range);
+ if (!unmap_vma->evicted) {
+ panthor_vm_lock_region(vm, unmap_start, unmap_range);
+ panthor_vm_unmap_pages(vm, unmap_start, unmap_range);
+ }
if (op->remap.prev) {
struct panthor_gem_object *bo = to_panthor_bo(op->remap.prev->gem.obj);
u64 offset = op->remap.prev->gem.offset + unmap_start - op->remap.prev->va.addr;
u64 size = op->remap.prev->va.addr + op->remap.prev->va.range - unmap_start;
- ret = panthor_vm_map_pages(vm, unmap_start, flags_to_prot(unmap_vma->flags),
- bo->base.sgt, offset, size);
- if (ret)
- return ret;
+ if (!unmap_vma->evicted) {
+ ret = panthor_vm_map_pages(vm, unmap_start,
+ flags_to_prot(unmap_vma->flags),
+ bo->dmap.sgt, offset, size);
+ if (ret)
+ return ret;
+ }
prev_vma = panthor_vm_op_ctx_get_vma(op_ctx);
panthor_vma_init(prev_vma, unmap_vma->flags);
+ prev_vma->evicted = unmap_vma->evicted;
}
if (op->remap.next) {
@@ -2211,13 +2283,17 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op,
u64 addr = op->remap.next->va.addr;
u64 size = unmap_start + unmap_range - op->remap.next->va.addr;
- ret = panthor_vm_map_pages(vm, addr, flags_to_prot(unmap_vma->flags),
- bo->base.sgt, op->remap.next->gem.offset, size);
- if (ret)
- return ret;
+ if (!unmap_vma->evicted) {
+ ret = panthor_vm_map_pages(vm, addr, flags_to_prot(unmap_vma->flags),
+ bo->dmap.sgt, op->remap.next->gem.offset,
+ size);
+ if (ret)
+ return ret;
+ }
next_vma = panthor_vm_op_ctx_get_vma(op_ctx);
panthor_vma_init(next_vma, unmap_vma->flags);
+ next_vma->evicted = unmap_vma->evicted;
}
drm_gpuva_remap(prev_vma ? &prev_vma->base : NULL,
@@ -2247,19 +2323,230 @@ static int panthor_gpuva_sm_step_unmap(struct drm_gpuva_op *op,
struct panthor_vma *unmap_vma = container_of(op->unmap.va, struct panthor_vma, base);
struct panthor_vm *vm = priv;
- panthor_vm_unmap_pages(vm, unmap_vma->base.va.addr,
- unmap_vma->base.va.range);
+ if (!unmap_vma->evicted) {
+ panthor_vm_unmap_pages(vm, unmap_vma->base.va.addr,
+ unmap_vma->base.va.range);
+ }
+
drm_gpuva_unmap(&op->unmap);
panthor_vma_unlink(unmap_vma);
return 0;
}
+void panthor_vm_update_bo_reclaim_lru_locked(struct panthor_gem_object *bo)
+{
+ struct panthor_device *ptdev = container_of(bo->base.dev, struct panthor_device, base);
+ struct panthor_vm *vm = NULL;
+ struct drm_gpuvm_bo *vm_bo;
+
+ dma_resv_assert_held(bo->base.resv);
+ lockdep_assert_held(&bo->base.gpuva.lock);
+
+ drm_gem_for_each_gpuvm_bo(vm_bo, &bo->base) {
+ if (vm_bo->evicted)
+ continue;
+
+ /* We're only supposed to have one non-evicted vm_bo in the list if we get
+ * there.
+ */
+ drm_WARN_ON(&ptdev->base, vm);
+ vm = container_of(vm_bo->vm, struct panthor_vm, base);
+
+ mutex_lock(&ptdev->reclaim.lock);
+ drm_gem_lru_move_tail_locked(&vm->reclaim.lru, &bo->base);
+ if (list_empty(&vm->reclaim.lru_node))
+ list_move(&vm->reclaim.lru_node, &ptdev->reclaim.vms);
+ mutex_unlock(&ptdev->reclaim.lock);
+ }
+}
+
+int panthor_vm_evict_bo_mappings_locked(struct panthor_gem_object *bo)
+{
+ struct drm_gpuvm_bo *vm_bo;
+ int ret = 0;
+
+ drm_gem_for_each_gpuvm_bo(vm_bo, &bo->base) {
+ struct panthor_vm *vm = container_of(vm_bo->vm, struct panthor_vm, base);
+ struct drm_gpuva *va;
+
+ /* Skip already evicted GPU mappings. */
+ if (vm_bo->evicted)
+ continue;
+
+ if (!mutex_trylock(&vm->op_lock))
+ return -EDEADLK;
+
+ drm_gpuvm_bo_evict(vm_bo, true);
+ drm_gpuvm_bo_for_each_va(va, vm_bo) {
+ struct panthor_vma *vma = container_of(va, struct panthor_vma, base);
+
+ if (vma->evicted)
+ continue;
+
+ /* If something fail in the middle of a VM_BO eviction, the VM_BO
+ * is considered fully evicted, but some of its VMAs might still be
+ * active. That's okay because the pages won't be released if this
+ * function returns an error.
+ *
+ * On the next job targeting this VM, the partially evicted VM_BO
+ * will be validated, causing all its evicted VMAs to be repopulated
+ * before the job runs. So no GPU fault expected.
+ */
+ ret = panthor_vm_lock_region(vm, va->va.addr, va->va.range);
+ if (ret)
+ break;
+
+ panthor_vm_unmap_pages(vm, va->va.addr, va->va.range);
+ panthor_vm_unlock_region(vm);
+ vma->evicted = true;
+ }
+
+ mutex_unlock(&vm->op_lock);
+
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+static struct panthor_vma *select_evicted_vma(struct drm_gpuvm_bo *vm_bo,
+ struct panthor_vm_op_ctx *op_ctx)
+{
+ struct panthor_vm *vm = container_of(vm_bo->vm, struct panthor_vm, base);
+ struct panthor_vma *first_evicted_vma = NULL;
+ struct drm_gpuva *va;
+
+ /* Take op_lock to protect against va insertion/removal. */
+ mutex_lock(&vm->op_lock);
+ drm_gpuvm_bo_for_each_va(va, vm_bo) {
+ struct panthor_vma *vma = container_of(va, struct panthor_vma, base);
+
+ if (vma->evicted) {
+ first_evicted_vma = vma;
+ panthor_vm_init_op_ctx(op_ctx, va->va.range, va->va.addr, vma->flags);
+ op_ctx->map.bo_offset = va->gem.offset;
+ break;
+ }
+ }
+ mutex_unlock(&vm->op_lock);
+
+ return first_evicted_vma;
+}
+
+static int remap_evicted_vma(struct drm_gpuvm_bo *vm_bo,
+ struct panthor_vma *evicted_vma,
+ struct panthor_vm_op_ctx *op_ctx)
+{
+ struct panthor_vm *vm = container_of(vm_bo->vm, struct panthor_vm, base);
+ struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj);
+ struct drm_gpuva *va;
+ bool found = false;
+ int ret;
+
+ ret = panthor_vm_op_ctx_prealloc_pts(op_ctx);
+ if (ret)
+ goto out_cleanup;
+
+ /* Take op_lock to protect against va insertion/removal. Note that the
+ * evicted_vma selection was done with the same lock held, but we had
+ * to release it so we can allocate PTs, because this very same lock
+ * is taken in a DMA-signalling path.
+ */
+ mutex_lock(&vm->op_lock);
+ drm_gpuvm_bo_for_each_va(va, vm_bo) {
+ struct panthor_vma *vma = container_of(va, struct panthor_vma, base);
+
+ if (vma != evicted_vma)
+ continue;
+
+ /* Because we had to release the lock between the evicted_vma selection
+ * and its repopulation, we can't rely solely on pointer equality (the
+ * VMA might have been freed and a new one allocated at the same address).
+ * If the evicted bit is still set, we're sure it's our VMA, because
+ * population/eviction is serialized with the BO resv lock.
+ */
+ if (vma->evicted)
+ found = true;
+
+ break;
+ }
+
+ if (found) {
+ vm->op_ctx = op_ctx;
+ ret = panthor_vm_lock_region(vm, evicted_vma->base.va.addr,
+ evicted_vma->base.va.range);
+ if (!ret) {
+ ret = panthor_vm_map_pages(vm, evicted_vma->base.va.addr,
+ flags_to_prot(evicted_vma->flags),
+ bo->dmap.sgt,
+ evicted_vma->base.gem.offset,
+ evicted_vma->base.va.range);
+ if (!ret)
+ evicted_vma->evicted = false;
+
+ panthor_vm_unlock_region(vm);
+ }
+
+ vm->op_ctx = NULL;
+ }
+
+ mutex_unlock(&vm->op_lock);
+
+out_cleanup:
+ panthor_vm_cleanup_op_ctx(op_ctx, vm);
+ return ret;
+}
+
+static int panthor_vm_restore_vmas(struct drm_gpuvm_bo *vm_bo)
+{
+ struct panthor_vm *vm = container_of(vm_bo->vm, struct panthor_vm, base);
+ struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj);
+ struct panthor_vm_op_ctx op_ctx;
+
+ if (drm_WARN_ON_ONCE(&vm->ptdev->base, !bo->dmap.sgt))
+ return -EINVAL;
+
+ for (struct panthor_vma *vma = select_evicted_vma(vm_bo, &op_ctx);
+ vma; vma = select_evicted_vma(vm_bo, &op_ctx)) {
+ int ret;
+
+ ret = remap_evicted_vma(vm_bo, vma, &op_ctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int panthor_vm_bo_validate(struct drm_gpuvm_bo *vm_bo,
+ struct drm_exec *exec)
+{
+ struct panthor_gem_object *bo = to_panthor_bo(vm_bo->obj);
+ int ret;
+
+ ret = panthor_gem_swapin_locked(bo);
+ if (ret)
+ return ret;
+
+ ret = panthor_vm_restore_vmas(vm_bo);
+ if (ret)
+ return ret;
+
+ drm_gpuvm_bo_evict(vm_bo, false);
+ mutex_lock(&bo->base.gpuva.lock);
+ panthor_gem_update_reclaim_state_locked(bo, NULL);
+ mutex_unlock(&bo->base.gpuva.lock);
+ return 0;
+}
+
static const struct drm_gpuvm_ops panthor_gpuvm_ops = {
.vm_free = panthor_vm_free,
.vm_bo_free = panthor_vm_bo_free,
.sm_step_map = panthor_gpuva_sm_step_map,
.sm_step_remap = panthor_gpuva_sm_step_remap,
.sm_step_unmap = panthor_gpuva_sm_step_unmap,
+ .vm_bo_validate = panthor_vm_bo_validate,
};
/**
@@ -2432,7 +2719,6 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
const struct drm_sched_init_args sched_args = {
.ops = &panthor_vm_bind_ops,
.submit_wq = ptdev->mmu->vm.wq,
- .num_rqs = 1,
.credit_limit = 1,
/* Bind operations are synchronous for now, no timeout needed. */
.timeout = MAX_SCHEDULE_TIMEOUT,
@@ -2474,6 +2760,8 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
vm->kernel_auto_va.start = auto_kernel_va_start;
vm->kernel_auto_va.end = vm->kernel_auto_va.start + auto_kernel_va_size - 1;
+ drm_gem_lru_init(&vm->reclaim.lru, &ptdev->reclaim.lock);
+ INIT_LIST_HEAD(&vm->reclaim.lru_node);
INIT_LIST_HEAD(&vm->node);
INIT_LIST_HEAD(&vm->as.lru_node);
vm->as.id = -1;
@@ -2821,7 +3109,78 @@ int panthor_vm_prepare_mapped_bos_resvs(struct drm_exec *exec, struct panthor_vm
if (ret)
return ret;
- return drm_gpuvm_prepare_objects(&vm->base, exec, slot_count);
+ ret = drm_gpuvm_prepare_objects(&vm->base, exec, slot_count);
+ if (ret)
+ return ret;
+
+ return drm_gpuvm_validate(&vm->base, exec);
+}
+
+unsigned long
+panthor_mmu_reclaim_priv_bos(struct panthor_device *ptdev,
+ unsigned int nr_to_scan, unsigned long *remaining,
+ bool (*shrink)(struct drm_gem_object *,
+ struct ww_acquire_ctx *))
+{
+ unsigned long freed = 0;
+ LIST_HEAD(remaining_vms);
+ LIST_HEAD(vms);
+
+ mutex_lock(&ptdev->reclaim.lock);
+ list_splice_init(&ptdev->reclaim.vms, &vms);
+
+ while (freed < nr_to_scan) {
+ struct panthor_vm *vm;
+
+ vm = list_first_entry_or_null(&vms, typeof(*vm),
+ reclaim.lru_node);
+ if (!vm)
+ break;
+
+ if (!kref_get_unless_zero(&vm->base.kref)) {
+ list_del_init(&vm->reclaim.lru_node);
+ continue;
+ }
+
+ mutex_unlock(&ptdev->reclaim.lock);
+
+ freed += drm_gem_lru_scan(&vm->reclaim.lru, nr_to_scan - freed,
+ remaining, shrink, NULL);
+
+ mutex_lock(&ptdev->reclaim.lock);
+
+ /* If the VM is still in the temporary list, remove it so we
+ * can proceed with the next VM.
+ */
+ if (vm == list_first_entry_or_null(&vms, typeof(*vm), reclaim.lru_node)) {
+ list_del_init(&vm->reclaim.lru_node);
+
+ /* Keep the VM around if there are still things to
+ * reclaim, so we can preserve the LRU order when
+ * re-inserting in ptdev->reclaim.vms at the end.
+ */
+ if (vm->reclaim.lru.count > 0)
+ list_add_tail(&vm->reclaim.lru_node, &remaining_vms);
+ }
+
+ mutex_unlock(&ptdev->reclaim.lock);
+
+ panthor_vm_put(vm);
+
+ mutex_lock(&ptdev->reclaim.lock);
+ }
+
+ /* Re-insert VMs with remaining data to reclaim at the beginning of
+ * the LRU. Note that any activeness change on the VM that happened
+ * while we were reclaiming would have moved the VM out of our
+ * temporary [remaining_]vms list, meaning anything we re-insert here
+ * preserves the LRU order.
+ */
+ list_splice_tail(&vms, &remaining_vms);
+ list_splice(&remaining_vms, &ptdev->reclaim.vms);
+ mutex_unlock(&ptdev->reclaim.lock);
+
+ return freed;
}
/**
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h
index 0e268fdfdb2f..3522fbbce369 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.h
+++ b/drivers/gpu/drm/panthor/panthor_mmu.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 or MIT */
/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
/* Copyright 2023 Collabora ltd. */
+/* Copyright 2025 ARM Limited. All rights reserved. */
#ifndef __PANTHOR_MMU_H__
#define __PANTHOR_MMU_H__
@@ -46,6 +47,13 @@ struct panthor_vm *panthor_vm_create(struct panthor_device *ptdev, bool for_mcu,
u64 kernel_auto_va_start,
u64 kernel_auto_va_size);
+void panthor_vm_update_bo_reclaim_lru_locked(struct panthor_gem_object *bo);
+int panthor_vm_evict_bo_mappings_locked(struct panthor_gem_object *bo);
+unsigned long
+panthor_mmu_reclaim_priv_bos(struct panthor_device *ptdev,
+ unsigned int nr_to_scan, unsigned long *remaining,
+ bool (*shrink)(struct drm_gem_object *,
+ struct ww_acquire_ctx *));
int panthor_vm_prepare_mapped_bos_resvs(struct drm_exec *exec,
struct panthor_vm *vm,
u32 slot_count);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 2fe04d0f0e3a..41d6369fa9c0 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3,7 +3,7 @@
#include <drm/drm_drv.h>
#include <drm/drm_exec.h>
-#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_file.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
#include <drm/gpu_scheduler.h>
@@ -221,7 +221,7 @@ struct panthor_scheduler {
/** @groups: Various lists used to classify groups. */
struct {
/**
- * @runnable: Runnable group lists.
+ * @groups.runnable: Runnable group lists.
*
* When a group has queues that want to execute something,
* its panthor_group::run_node should be inserted here.
@@ -231,7 +231,7 @@ struct panthor_scheduler {
struct list_head runnable[PANTHOR_CSG_PRIORITY_COUNT];
/**
- * @idle: Idle group lists.
+ * @groups.idle: Idle group lists.
*
* When all queues of a group are idle (either because they
* have nothing to execute, or because they are blocked), the
@@ -242,7 +242,7 @@ struct panthor_scheduler {
struct list_head idle[PANTHOR_CSG_PRIORITY_COUNT];
/**
- * @waiting: List of groups whose queues are blocked on a
+ * @groups.waiting: List of groups whose queues are blocked on a
* synchronization object.
*
* Insert panthor_group::wait_node here when a group is waiting
@@ -283,17 +283,17 @@ struct panthor_scheduler {
/** @pm: Power management related fields. */
struct {
- /** @has_ref: True if the scheduler owns a runtime PM reference. */
+ /** @pm.has_ref: True if the scheduler owns a runtime PM reference. */
bool has_ref;
} pm;
/** @reset: Reset related fields. */
struct {
- /** @lock: Lock protecting the other reset fields. */
+ /** @reset.lock: Lock protecting the other reset fields. */
struct mutex lock;
/**
- * @in_progress: True if a reset is in progress.
+ * @reset.in_progress: True if a reset is in progress.
*
* Set to true in panthor_sched_pre_reset() and back to false in
* panthor_sched_post_reset().
@@ -301,7 +301,7 @@ struct panthor_scheduler {
atomic_t in_progress;
/**
- * @stopped_groups: List containing all groups that were stopped
+ * @reset.stopped_groups: List containing all groups that were stopped
* before a reset.
*
* Insert panthor_group::run_node in the pre_reset path.
@@ -395,19 +395,19 @@ struct panthor_queue {
/** @iface: Firmware interface. */
struct {
- /** @mem: FW memory allocated for this interface. */
+ /** @iface.mem: FW memory allocated for this interface. */
struct panthor_kernel_bo *mem;
- /** @input: Input interface. */
+ /** @iface.input: Input interface. */
struct panthor_fw_ringbuf_input_iface *input;
- /** @output: Output interface. */
+ /** @iface.output: Output interface. */
const struct panthor_fw_ringbuf_output_iface *output;
- /** @input_fw_va: FW virtual address of the input interface buffer. */
+ /** @iface.input_fw_va: FW virtual address of the input interface buffer. */
u32 input_fw_va;
- /** @output_fw_va: FW virtual address of the output interface buffer. */
+ /** @iface.output_fw_va: FW virtual address of the output interface buffer. */
u32 output_fw_va;
} iface;
@@ -416,26 +416,26 @@ struct panthor_queue {
* queue is waiting on.
*/
struct {
- /** @gpu_va: GPU address of the synchronization object. */
+ /** @syncwait.gpu_va: GPU address of the synchronization object. */
u64 gpu_va;
- /** @ref: Reference value to compare against. */
+ /** @syncwait.ref: Reference value to compare against. */
u64 ref;
- /** @gt: True if this is a greater-than test. */
+ /** @syncwait.gt: True if this is a greater-than test. */
bool gt;
- /** @sync64: True if this is a 64-bit sync object. */
+ /** @syncwait.sync64: True if this is a 64-bit sync object. */
bool sync64;
- /** @bo: Buffer object holding the synchronization object. */
+ /** @syncwait.obj: Buffer object holding the synchronization object. */
struct drm_gem_object *obj;
- /** @offset: Offset of the synchronization object inside @bo. */
+ /** @syncwait.offset: Offset of the synchronization object inside @bo. */
u64 offset;
/**
- * @kmap: Kernel mapping of the buffer object holding the
+ * @syncwait.kmap: Kernel mapping of the buffer object holding the
* synchronization object.
*/
void *kmap;
@@ -443,21 +443,21 @@ struct panthor_queue {
/** @fence_ctx: Fence context fields. */
struct {
- /** @lock: Used to protect access to all fences allocated by this context. */
+ /** @fence_ctx.lock: Used to protect access to all fences allocated by this context. */
spinlock_t lock;
/**
- * @id: Fence context ID.
+ * @fence_ctx.id: Fence context ID.
*
* Allocated with dma_fence_context_alloc().
*/
u64 id;
- /** @seqno: Sequence number of the last initialized fence. */
+ /** @fence_ctx.seqno: Sequence number of the last initialized fence. */
atomic64_t seqno;
/**
- * @last_fence: Fence of the last submitted job.
+ * @fence_ctx.last_fence: Fence of the last submitted job.
*
* We return this fence when we get an empty command stream.
* This way, we are guaranteed that all earlier jobs have completed
@@ -467,7 +467,7 @@ struct panthor_queue {
struct dma_fence *last_fence;
/**
- * @in_flight_jobs: List containing all in-flight jobs.
+ * @fence_ctx.in_flight_jobs: List containing all in-flight jobs.
*
* Used to keep track and signal panthor_job::done_fence when the
* synchronization object attached to the queue is signaled.
@@ -477,13 +477,13 @@ struct panthor_queue {
/** @profiling: Job profiling data slots and access information. */
struct {
- /** @slots: Kernel BO holding the slots. */
+ /** @profiling.slots: Kernel BO holding the slots. */
struct panthor_kernel_bo *slots;
- /** @slot_count: Number of jobs ringbuffer can hold at once. */
+ /** @profiling.slot_count: Number of jobs ringbuffer can hold at once. */
u32 slot_count;
- /** @seqno: Index of the next available profiling information slot. */
+ /** @profiling.seqno: Index of the next available profiling information slot. */
u32 seqno;
} profiling;
};
@@ -627,7 +627,7 @@ struct panthor_group {
/** @fdinfo: Per-file info exposed through /proc/<process>/fdinfo */
struct {
- /** @data: Total sampled values for jobs in queues from this group. */
+ /** @fdinfo.data: Total sampled values for jobs in queues from this group. */
struct panthor_gpu_usage data;
/**
@@ -805,15 +805,15 @@ struct panthor_job {
/** @call_info: Information about the userspace command stream call. */
struct {
- /** @start: GPU address of the userspace command stream. */
+ /** @call_info.start: GPU address of the userspace command stream. */
u64 start;
- /** @size: Size of the userspace command stream. */
+ /** @call_info.size: Size of the userspace command stream. */
u32 size;
/**
- * @latest_flush: Flush ID at the time the userspace command
- * stream was built.
+ * @call_info.latest_flush: Flush ID at the time the userspace
+ * command stream was built.
*
* Needed for the flush reduction mechanism.
*/
@@ -822,10 +822,10 @@ struct panthor_job {
/** @ringbuf: Position of this job is in the ring buffer. */
struct {
- /** @start: Start offset. */
+ /** @ringbuf.start: Start offset. */
u64 start;
- /** @end: End offset. */
+ /** @ringbuf.end: End offset. */
u64 end;
} ringbuf;
@@ -840,10 +840,10 @@ struct panthor_job {
/** @profiling: Job profiling information. */
struct {
- /** @mask: Current device job profiling enablement bitmask. */
+ /** @profiling.mask: Current device job profiling enablement bitmask. */
u32 mask;
- /** @slot: Job index in the profiling slots BO. */
+ /** @profiling.slot: Job index in the profiling slots BO. */
u32 slot;
} profiling;
};
@@ -871,8 +871,7 @@ panthor_queue_get_syncwait_obj(struct panthor_group *group, struct panthor_queue
int ret;
if (queue->syncwait.kmap) {
- bo = container_of(queue->syncwait.obj,
- struct panthor_gem_object, base.base);
+ bo = to_panthor_bo(queue->syncwait.obj);
goto out_sync;
}
@@ -882,7 +881,7 @@ panthor_queue_get_syncwait_obj(struct panthor_group *group, struct panthor_queue
if (drm_WARN_ON(&ptdev->base, IS_ERR_OR_NULL(bo)))
goto err_put_syncwait_obj;
- queue->syncwait.obj = &bo->base.base;
+ queue->syncwait.obj = &bo->base;
ret = drm_gem_vmap(queue->syncwait.obj, &map);
if (drm_WARN_ON(&ptdev->base, ret))
goto err_put_syncwait_obj;
@@ -896,7 +895,7 @@ out_sync:
* panthor_gem_sync() is a NOP if map_wc=true, so no need to check
* it here.
*/
- panthor_gem_sync(&bo->base.base,
+ panthor_gem_sync(&bo->base,
DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE,
queue->syncwait.offset,
queue->syncwait.sync64 ?
@@ -1894,6 +1893,8 @@ static void process_fw_events_work(struct work_struct *work)
/**
* panthor_sched_report_fw_events() - Report FW events to the scheduler.
+ * @ptdev: Device.
+ * @events: Bitmask of pending FW events to report.
*/
void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events)
{
@@ -2779,6 +2780,7 @@ static void panthor_group_start(struct panthor_group *group)
/**
* panthor_sched_report_mmu_fault() - Report MMU faults to the scheduler.
+ * @ptdev: Device.
*/
void panthor_sched_report_mmu_fault(struct panthor_device *ptdev)
{
@@ -3487,7 +3489,6 @@ group_create_queue(struct panthor_group *group,
struct drm_sched_init_args sched_args = {
.ops = &panthor_queue_sched_ops,
.submit_wq = group->ptdev->scheduler->wq,
- .num_rqs = 1,
/*
* The credit limit argument tells us the total number of
* instructions across all CS slots in the ringbuffer, with
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
index 7ecec7b04a8d..32ea09d65d76 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.c
@@ -51,7 +51,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
{
struct rcar_du_encoder *renc;
struct drm_connector *connector;
- struct drm_bridge *bridge;
+ struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
int ret;
/*
@@ -70,19 +70,28 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(bridge))
- return PTR_ERR(bridge);
+ return PTR_ERR(no_free_ptr(bridge));
+
+ /*
+ * The reference taken by devm_drm_panel_bridge_add_typed() is
+ * released automatically. Take a second one for the __free()
+ * when this function will return.
+ */
+ drm_bridge_get(bridge);
} else {
- bridge = of_drm_find_bridge(enc_node);
+ bridge = of_drm_find_and_get_bridge(enc_node);
if (!bridge)
return -EPROBE_DEFER;
if (output == RCAR_DU_OUTPUT_LVDS0 ||
output == RCAR_DU_OUTPUT_LVDS1)
- rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] = bridge;
+ rcdu->lvds[output - RCAR_DU_OUTPUT_LVDS0] =
+ drm_bridge_get(bridge);
if (output == RCAR_DU_OUTPUT_DSI0 ||
output == RCAR_DU_OUTPUT_DSI1)
- rcdu->dsi[output - RCAR_DU_OUTPUT_DSI0] = bridge;
+ rcdu->dsi[output - RCAR_DU_OUTPUT_DSI0] =
+ drm_bridge_get(bridge);
}
/*
@@ -135,3 +144,13 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
return drm_connector_attach_encoder(connector, &renc->base);
}
+
+void rcar_du_encoder_cleanup(struct rcar_du_device *rcdu)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rcdu->lvds); i++)
+ drm_bridge_put(rcdu->lvds[i]);
+ for (i = 0; i < ARRAY_SIZE(rcdu->dsi); i++)
+ drm_bridge_put(rcdu->dsi[i]);
+}
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h
index e5ec8fbb3979..b2b5e93f30f8 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_encoder.h
@@ -25,5 +25,6 @@ struct rcar_du_encoder {
int rcar_du_encoder_init(struct rcar_du_device *rcdu,
enum rcar_du_output output,
struct device_node *enc_node);
+void rcar_du_encoder_cleanup(struct rcar_du_device *rcdu);
#endif /* __RCAR_DU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
index b2d0e4651e35..1119c84e5fe9 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_kms.c
@@ -832,6 +832,8 @@ static void rcar_du_modeset_cleanup(struct drm_device *dev, void *res)
put_device(cmm->dev);
}
+
+ rcar_du_encoder_cleanup(rcdu);
}
int rcar_du_modeset_init(struct rcar_du_device *rcdu)
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
index 5e6dd16705e6..d53068733c66 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
@@ -67,7 +67,7 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
{
struct rzg2l_du_encoder *renc;
struct drm_connector *connector;
- struct drm_bridge *bridge;
+ struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
int ret;
/*
@@ -84,9 +84,16 @@ int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu,
bridge = devm_drm_panel_bridge_add_typed(rcdu->dev, panel,
DRM_MODE_CONNECTOR_DPI);
if (IS_ERR(bridge))
- return PTR_ERR(bridge);
+ return PTR_ERR(no_free_ptr(bridge));
+
+ /*
+ * The reference taken by devm_drm_panel_bridge_add_typed() is
+ * released automatically. Take a second one for the __free()
+ * when this function will return.
+ */
+ drm_bridge_get(bridge);
} else {
- bridge = of_drm_find_bridge(enc_node);
+ bridge = of_drm_find_and_get_bridge(enc_node);
if (!bridge)
return -EPROBE_DEFER;
}
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
index bd486377f037..eb626c3cc421 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
@@ -20,6 +20,7 @@
#include <drm/drm_vblank.h>
#include <linux/bitops.h>
+#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -293,6 +294,9 @@ static void rzg2l_du_vsp_cleanup(struct drm_device *dev, void *res)
{
struct rzg2l_du_vsp *vsp = res;
+ if (vsp->link)
+ device_link_del(vsp->link);
+
put_device(vsp->vsp);
}
@@ -317,6 +321,18 @@ int rzg2l_du_vsp_init(struct rzg2l_du_vsp *vsp, struct device_node *np,
if (ret < 0)
return ret;
+ /*
+ * Enforce suspend/resume ordering between the DU (consumer) and the
+ * VSP (supplier). The DU will be suspended before and resume after the
+ * VSP.
+ */
+ vsp->link = device_link_add(rcdu->dev, vsp->vsp, DL_FLAG_STATELESS);
+ if (!vsp->link) {
+ dev_err(rcdu->dev, "Failed to create device link to VSP %s\n",
+ dev_name(vsp->vsp));
+ return -EINVAL;
+ }
+
ret = vsp1_du_init(vsp->vsp);
if (ret < 0)
return ret;
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h
index 322eb80dcbaf..a22aaf0843ed 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.h
@@ -15,6 +15,7 @@
#include <linux/scatterlist.h>
struct device;
+struct device_link;
struct drm_framebuffer;
struct rzg2l_du_device;
struct rzg2l_du_format_info;
@@ -29,6 +30,7 @@ struct rzg2l_du_vsp_plane {
struct rzg2l_du_vsp {
unsigned int index;
struct device *vsp;
+ struct device_link *link;
struct rzg2l_du_device *dev;
};
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
index a87a301326c7..715872130780 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c
@@ -484,7 +484,6 @@ static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi,
u32 dphytim1;
u32 dphytim2;
u32 dphytim3;
- int ret;
/* All DSI global operation timings are set with recommended setting */
for (i = 0; i < ARRAY_SIZE(rzg2l_mipi_dsi_global_timings); ++i) {
@@ -524,12 +523,6 @@ static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi,
rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM2, dphytim2);
rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYTIM3, dphytim3);
- ret = reset_control_deassert(dsi->rstc);
- if (ret < 0)
- return ret;
-
- udelay(1);
-
return 0;
}
@@ -541,8 +534,6 @@ static void rzg2l_mipi_dsi_dphy_exit(struct rzg2l_mipi_dsi *dsi)
dphyctrl0 &= ~(DSIDPHYCTRL0_EN_LDO1200 | DSIDPHYCTRL0_EN_BGR);
rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0);
-
- reset_control_assert(dsi->rstc);
}
static int rzg2l_dphy_conf_clks(struct rzg2l_mipi_dsi *dsi, unsigned long mode_freq,
@@ -811,6 +802,14 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi,
FIELD_MODIFY(DSISETR_MRPSZ, &dsisetr, RZG2L_DCS_BUF_SIZE);
rzg2l_mipi_dsi_link_write(dsi, DSISETR, dsisetr);
+ if (dsi->rstc) {
+ ret = reset_control_deassert(dsi->rstc);
+ if (ret < 0)
+ goto err_phy;
+
+ fsleep(1000);
+ }
+
return 0;
err_phy:
@@ -822,6 +821,7 @@ err_phy:
static void rzg2l_mipi_dsi_stop(struct rzg2l_mipi_dsi *dsi)
{
+ reset_control_assert(dsi->rstc);
dsi->info->dphy_exit(dsi);
pm_runtime_put(dsi->dev);
}
@@ -1025,29 +1025,33 @@ static void rzg2l_mipi_dsi_atomic_pre_enable(struct drm_bridge *bridge,
const struct drm_display_mode *mode;
struct drm_connector *connector;
struct drm_crtc *crtc;
- int ret;
connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
- ret = rzg2l_mipi_dsi_startup(dsi, mode);
- if (ret < 0)
- return;
-
- rzg2l_mipi_dsi_set_display_timing(dsi, mode);
+ rzg2l_mipi_dsi_startup(dsi, mode);
}
static void rzg2l_mipi_dsi_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct rzg2l_mipi_dsi *dsi = bridge_to_rzg2l_mipi_dsi(bridge);
+ const struct drm_display_mode *mode;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
int ret;
ret = rzg2l_mipi_dsi_start_hs_clock(dsi);
if (ret < 0)
goto err_stop;
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+ mode = &drm_atomic_get_new_crtc_state(state, crtc)->adjusted_mode;
+
+ rzg2l_mipi_dsi_set_display_timing(dsi, mode);
+
ret = rzg2l_mipi_dsi_start_video(dsi);
if (ret < 0)
goto err_stop_clock;
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 1479b8c4ed40..e7f49fe845ea 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -46,6 +46,7 @@ config ROCKCHIP_VOP2
config ROCKCHIP_ANALOGIX_DP
bool "Rockchip specific extensions for Analogix DP driver"
depends on ROCKCHIP_VOP
+ select DRM_BRIDGE_CONNECTOR
select DRM_DISPLAY_HELPER
select DRM_DISPLAY_DP_HELPER
help
diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 96bd3dd239d2..eea230f0227a 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -21,13 +21,12 @@
#include <video/of_videomode.h>
#include <video/videomode.h>
-#include <drm/display/drm_dp_aux_bus.h>
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge_connector.h>
#include <drm/bridge/analogix_dp.h>
#include <drm/drm_of.h>
-#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -166,23 +165,6 @@ static int rockchip_dp_powerdown(struct analogix_dp_plat_data *plat_data)
return 0;
}
-static int rockchip_dp_get_modes(struct analogix_dp_plat_data *plat_data,
- struct drm_connector *connector)
-{
- struct drm_display_info *di = &connector->display_info;
- /* VOP couldn't output YUV video format for eDP rightly */
- u32 mask = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422);
-
- if ((di->color_formats & mask)) {
- DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n");
- di->color_formats &= ~mask;
- di->color_formats |= BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444);
- di->bpc = 8;
- }
-
- return 0;
-}
-
static bool
rockchip_dp_drm_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode,
@@ -386,6 +368,7 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
{
struct rockchip_dp_device *dp = dev_get_drvdata(dev);
struct drm_device *drm_dev = data;
+ struct drm_connector *connector;
int ret;
dp->drm_dev = drm_dev;
@@ -405,7 +388,14 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
if (ret)
goto err_cleanup_encoder;
- return 0;
+ connector = drm_bridge_connector_init(dp->drm_dev, dp->plat_data.encoder);
+ if (IS_ERR(connector)) {
+ ret = PTR_ERR(connector);
+ dev_err(dp->dev, "Failed to initialize bridge_connector\n");
+ goto err_cleanup_encoder;
+ }
+
+ return drm_connector_attach_encoder(connector, dp->plat_data.encoder);
err_cleanup_encoder:
dp->encoder.encoder.funcs->destroy(&dp->encoder.encoder);
return ret;
@@ -425,24 +415,6 @@ static const struct component_ops rockchip_dp_component_ops = {
.unbind = rockchip_dp_unbind,
};
-static int rockchip_dp_link_panel(struct drm_dp_aux *aux)
-{
- struct analogix_dp_plat_data *plat_data = analogix_dp_aux_to_plat_data(aux);
- struct rockchip_dp_device *dp = pdata_encoder_to_dp(plat_data);
- int ret;
-
- /*
- * If drm_of_find_panel_or_bridge() returns -ENODEV, there may be no valid panel
- * or bridge nodes. The driver should go on for the driver-free bridge or the DP
- * mode applications.
- */
- ret = drm_of_find_panel_or_bridge(dp->dev->of_node, 1, 0, &plat_data->panel, NULL);
- if (ret && ret != -ENODEV)
- return ret;
-
- return component_add(dp->dev, &rockchip_dp_component_ops);
-}
-
static int rockchip_dp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -461,6 +433,8 @@ static int rockchip_dp_probe(struct platform_device *pdev)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
i = 0;
while (dp_data[i].reg) {
@@ -481,7 +455,7 @@ static int rockchip_dp_probe(struct platform_device *pdev)
dp->plat_data.dev_type = dp->data->chip_type;
dp->plat_data.power_on = rockchip_dp_poweron;
dp->plat_data.power_off = rockchip_dp_powerdown;
- dp->plat_data.get_modes = rockchip_dp_get_modes;
+ dp->plat_data.ops = &rockchip_dp_component_ops;
ret = rockchip_dp_of_probe(dp);
if (ret < 0)
@@ -493,22 +467,7 @@ static int rockchip_dp_probe(struct platform_device *pdev)
if (IS_ERR(dp->adp))
return PTR_ERR(dp->adp);
- ret = devm_of_dp_aux_populate_bus(analogix_dp_get_aux(dp->adp), rockchip_dp_link_panel);
- if (ret) {
- /*
- * If devm_of_dp_aux_populate_bus() returns -ENODEV, the done_probing() will not
- * be called because there are no EP devices. Then the rockchip_dp_link_panel()
- * will be called directly in order to support the other valid DT configurations.
- *
- * NOTE: The devm_of_dp_aux_populate_bus() is allowed to return -EPROBE_DEFER.
- */
- if (ret != -ENODEV)
- return dev_err_probe(dp->dev, ret, "failed to populate aux bus\n");
-
- return rockchip_dp_link_panel(analogix_dp_get_aux(dp->adp));
- }
-
- return 0;
+ return analogix_dp_finish_probe(dp->adp);
}
static void rockchip_dp_remove(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 0dc3804051a9..9b82b27770e5 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -685,6 +685,8 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
val = div_u64(8 * (symbol + 1), bit_per_pix) - val;
val += 2;
ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+ if (ret)
+ goto err_config_video;
switch (video->color_depth) {
case 6:
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
index dac3d202971e..22c0911f1896 100644
--- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -7,22 +7,22 @@
*/
#include <linux/component.h>
+#include <linux/media-bus-format.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/videodev2.h>
+
#include <drm/bridge/dw_dp.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
-#include <linux/media-bus-format.h>
-#include <linux/videodev2.h>
-
#include "rockchip_drm_drv.h"
-#include "rockchip_drm_vop.h"
struct rockchip_dw_dp {
struct dw_dp *base;
@@ -82,7 +82,7 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *
struct drm_connector *connector;
int ret;
- dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+ dp = drmm_kzalloc(drm_dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
return -ENOMEM;
@@ -103,20 +103,15 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *
drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
dp->base = dw_dp_bind(dev, encoder, plat_data);
- if (IS_ERR(dp->base)) {
- ret = PTR_ERR(dp->base);
- return ret;
- }
+ if (IS_ERR(dp->base))
+ return PTR_ERR(dp->base);
connector = drm_bridge_connector_init(drm_dev, encoder);
- if (IS_ERR(connector)) {
- ret = PTR_ERR(connector);
- return dev_err_probe(dev, ret, "Failed to init bridge connector");
- }
-
- drm_connector_attach_encoder(connector, encoder);
+ if (IS_ERR(connector))
+ return dev_err_probe(dev, PTR_ERR(connector),
+ "Failed to init bridge connector");
- return 0;
+ return drm_connector_attach_encoder(connector, encoder);
}
static const struct component_ops dw_dp_rockchip_component_ops = {
@@ -125,16 +120,12 @@ static const struct component_ops dw_dp_rockchip_component_ops = {
static int dw_dp_probe(struct platform_device *pdev)
{
- struct device *dev = &pdev->dev;
-
- return component_add(dev, &dw_dp_rockchip_component_ops);
+ return component_add(&pdev->dev, &dw_dp_rockchip_component_ops);
}
static void dw_dp_remove(struct platform_device *pdev)
{
- struct rockchip_dw_dp *dp = platform_get_drvdata(pdev);
-
- component_del(dp->dev, &dw_dp_rockchip_component_ops);
+ component_del(&pdev->dev, &dw_dp_rockchip_component_ops);
}
static const struct dw_dp_plat_data rk3588_dp_plat_data = {
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 1a09bcc96c3e..c78db7f8ab6c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -21,6 +21,7 @@
#include <drm/bridge/dw_hdmi_qp.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
@@ -477,7 +478,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
if (!pdev->dev.of_node)
return -ENODEV;
- hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
@@ -586,16 +587,16 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
return ret;
drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
- drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+ ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return dev_err_probe(hdmi->dev, ret, "Failed to init encoder");
platform_set_drvdata(pdev, hdmi);
hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
- if (IS_ERR(hdmi->hdmi)) {
- drm_encoder_cleanup(encoder);
+ if (IS_ERR(hdmi->hdmi))
return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmi),
"Failed to bind dw-hdmi-qp");
- }
connector = drm_bridge_connector_init(drm, encoder);
if (IS_ERR(connector))
@@ -612,8 +613,6 @@ static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
cancel_delayed_work_sync(&hdmi->hpd_work);
-
- drm_encoder_cleanup(&hdmi->encoder.encoder);
}
static const struct component_ops dw_hdmi_qp_rockchip_ops = {
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c
index 97c20500f790..28e6fb09aae7 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c
@@ -14,6 +14,7 @@
#include <drm/bridge/inno_hdmi.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_managed.h>
#include <drm/drm_of.h>
#include "rockchip_drm_drv.h"
@@ -90,7 +91,7 @@ static int inno_hdmi_rockchip_bind(struct device *dev, struct device *master, vo
const struct inno_hdmi_plat_data *plat_data;
int ret;
- hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+ hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 09d14a072d27..b188539dca0b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -334,7 +334,7 @@ void rockchip_gem_free_object(struct drm_gem_object *obj)
struct rockchip_drm_private *private = drm->dev_private;
struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
- if (obj->import_attach) {
+ if (drm_gem_is_imported(obj)) {
if (private->domain) {
rockchip_gem_iommu_unmap(rk_obj);
} else {
diff --git a/drivers/gpu/drm/scheduler/Makefile b/drivers/gpu/drm/scheduler/Makefile
index 6e13e4c63e9d..74e75eff6df5 100644
--- a/drivers/gpu/drm/scheduler/Makefile
+++ b/drivers/gpu/drm/scheduler/Makefile
@@ -20,7 +20,7 @@
# OTHER DEALINGS IN THE SOFTWARE.
#
#
-gpu-sched-y := sched_main.o sched_fence.o sched_entity.o
+gpu-sched-y := sched_main.o sched_fence.o sched_entity.o sched_rq.o
obj-$(CONFIG_DRM_SCHED) += gpu-sched.o
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index fe174a4857be..4ebb513255ed 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -33,6 +33,64 @@
#include "gpu_scheduler_trace.h"
/**
+ * drm_sched_entity_stats_release - Entity stats kref release function
+ * @kref: Entity stats embedded kref pointer
+ */
+void drm_sched_entity_stats_release(struct kref *kref)
+{
+ struct drm_sched_entity_stats *stats =
+ container_of(kref, typeof(*stats), kref);
+
+ kfree(stats);
+}
+
+/**
+ * drm_sched_entity_stats_new - Allocate a new struct drm_sched_entity_stats object
+ *
+ * Return: Pointer to newly allocated struct drm_sched_entity_stats object.
+ */
+static struct drm_sched_entity_stats *drm_sched_entity_stats_new(void)
+{
+ struct drm_sched_entity_stats *stats;
+
+ stats = kzalloc_obj(*stats);
+ if (!stats)
+ return NULL;
+
+ kref_init(&stats->kref);
+ spin_lock_init(&stats->lock);
+ ewma_drm_sched_avgtime_init(&stats->avg_job_us);
+
+ return stats;
+}
+
+/**
+ * drm_sched_entity_stats_job_add_gpu_time - Account job execution time to entity
+ * @job: Scheduler job to account.
+ *
+ * Accounts the execution time of @job to its respective entity stats object.
+ *
+ * Return: Job's real duration in micro seconds.
+ */
+ktime_t drm_sched_entity_stats_job_add_gpu_time(struct drm_sched_job *job)
+{
+ struct drm_sched_entity_stats *stats = job->entity_stats;
+ struct drm_sched_fence *s_fence = job->s_fence;
+ ktime_t start, end, duration;
+
+ start = dma_fence_timestamp(&s_fence->scheduled);
+ end = dma_fence_timestamp(&s_fence->finished);
+ duration = ktime_sub(end, start);
+
+ spin_lock(&stats->lock);
+ stats->runtime = ktime_add(stats->runtime, duration);
+ ewma_drm_sched_avgtime_add(&stats->avg_job_us, ktime_to_us(duration));
+ spin_unlock(&stats->lock);
+
+ return duration;
+}
+
+/**
* drm_sched_entity_init - Init a context entity used by scheduler when
* submit to HW ring.
*
@@ -61,45 +119,25 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
unsigned int num_sched_list,
atomic_t *guilty)
{
- if (!(entity && sched_list && (num_sched_list == 0 || sched_list[0])))
+ if (!entity || !sched_list || !num_sched_list || !sched_list[0])
return -EINVAL;
memset(entity, 0, sizeof(struct drm_sched_entity));
+
+ entity->stats = drm_sched_entity_stats_new();
+ if (!entity->stats)
+ return -ENOMEM;
+
INIT_LIST_HEAD(&entity->list);
entity->rq = NULL;
entity->guilty = guilty;
- entity->num_sched_list = num_sched_list;
entity->priority = priority;
entity->last_user = current->group_leader;
- /*
- * It's perfectly valid to initialize an entity without having a valid
- * scheduler attached. It's just not valid to use the scheduler before it
- * is initialized itself.
- */
+ entity->num_sched_list = num_sched_list;
entity->sched_list = num_sched_list > 1 ? sched_list : NULL;
+ entity->rq = &sched_list[0]->rq;
RCU_INIT_POINTER(entity->last_scheduled, NULL);
RB_CLEAR_NODE(&entity->rb_tree_node);
-
- if (num_sched_list && !sched_list[0]->sched_rq) {
- /* Since every entry covered by num_sched_list
- * should be non-NULL and therefore we warn drivers
- * not to do this and to fix their DRM calling order.
- */
- pr_warn("%s: called with uninitialized scheduler\n", __func__);
- } else if (num_sched_list) {
- /* The "priority" of an entity cannot exceed the number of run-queues of a
- * scheduler. Protect against num_rqs being 0, by converting to signed. Choose
- * the lowest priority available.
- */
- if (entity->priority >= sched_list[0]->num_rqs) {
- dev_err(sched_list[0]->dev, "entity has out-of-bounds priority: %u. num_rqs: %u\n",
- entity->priority, sched_list[0]->num_rqs);
- entity->priority = max_t(s32, (s32) sched_list[0]->num_rqs - 1,
- (s32) DRM_SCHED_PRIORITY_KERNEL);
- }
- entity->rq = sched_list[0]->sched_rq[entity->priority];
- }
-
init_completion(&entity->entity_idle);
/* We start in an idle state. */
@@ -228,8 +266,16 @@ static void drm_sched_entity_kill_jobs_cb(struct dma_fence *f,
schedule_work(&job->work);
}
-/* Remove the entity from the scheduler and kill all pending jobs */
-static void drm_sched_entity_kill(struct drm_sched_entity *entity)
+/**
+ * drm_sched_entity_kill - kill an entity's pending jobs and remove it
+ * @entity: the entity to remove
+ *
+ * Removes the entity from the scheduler's run queue and kills all pending jobs.
+ *
+ * This function should be used over drm_sched_entity_flush() if it is not
+ * desired to actually wait for all pending jobs to finish.
+ */
+void drm_sched_entity_kill(struct drm_sched_entity *entity)
{
struct drm_sched_job *job;
struct dma_fence *prev;
@@ -267,6 +313,7 @@ static void drm_sched_entity_kill(struct drm_sched_entity *entity)
}
dma_fence_put(prev);
}
+EXPORT_SYMBOL(drm_sched_entity_kill);
/**
* drm_sched_entity_flush - Flush a context entity
@@ -289,7 +336,7 @@ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout)
if (!entity->rq)
return 0;
- sched = entity->rq->sched;
+ sched = container_of(entity->rq, typeof(*sched), rq);
/*
* The client will not queue more jobs during this fini - consume
* existing queued ones, or discard them on SIGKILL.
@@ -343,6 +390,7 @@ void drm_sched_entity_fini(struct drm_sched_entity *entity)
dma_fence_put(rcu_dereference_check(entity->last_scheduled, true));
RCU_INIT_POINTER(entity->last_scheduled, NULL);
+ drm_sched_entity_stats_put(entity->stats);
}
EXPORT_SYMBOL(drm_sched_entity_fini);
@@ -369,10 +417,12 @@ static void drm_sched_entity_wakeup(struct dma_fence *f,
{
struct drm_sched_entity *entity =
container_of(cb, struct drm_sched_entity, cb);
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->rq, typeof(*sched), rq);
entity->dependency = NULL;
dma_fence_put(f);
- drm_sched_wakeup(entity->rq->sched);
+ drm_sched_wakeup(sched);
}
/**
@@ -399,7 +449,8 @@ EXPORT_SYMBOL(drm_sched_entity_set_priority);
static bool drm_sched_entity_add_dependency_cb(struct drm_sched_entity *entity,
struct drm_sched_job *sched_job)
{
- struct drm_gpu_scheduler *sched = entity->rq->sched;
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->rq, typeof(*sched), rq);
struct dma_fence *fence = entity->dependency;
struct drm_sched_fence *s_fence;
@@ -492,26 +543,7 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
spsc_queue_pop(&entity->job_queue);
- /*
- * Update the entity's location in the min heap according to
- * the timestamp of the next job, if any.
- */
- if (drm_sched_policy == DRM_SCHED_POLICY_FIFO) {
- struct drm_sched_job *next;
-
- next = drm_sched_entity_queue_peek(entity);
- if (next) {
- struct drm_sched_rq *rq;
-
- spin_lock(&entity->lock);
- rq = entity->rq;
- spin_lock(&rq->lock);
- drm_sched_rq_update_fifo_locked(entity, rq,
- next->submit_ts);
- spin_unlock(&rq->lock);
- spin_unlock(&entity->lock);
- }
- }
+ drm_sched_rq_pop_entity(entity);
/* Jobs and entities might have different lifecycles. Since we're
* removing the job from the entities queue, set the jobs entity pointer
@@ -552,7 +584,7 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
spin_lock(&entity->lock);
sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list);
- rq = sched ? sched->sched_rq[entity->priority] : NULL;
+ rq = sched ? &sched->rq : NULL;
if (rq != entity->rq) {
drm_sched_rq_remove_entity(entity->rq, entity);
entity->rq = rq;
@@ -576,8 +608,9 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity)
void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
{
struct drm_sched_entity *entity = sched_job->entity;
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->rq, typeof(*sched), rq);
bool first;
- ktime_t submit_ts;
trace_drm_sched_job_queue(sched_job, entity);
@@ -588,44 +621,20 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
xa_for_each(&sched_job->dependencies, index, entry)
trace_drm_sched_job_add_dep(sched_job, entry);
}
- atomic_inc(entity->rq->sched->score);
+ atomic_inc(sched->score);
WRITE_ONCE(entity->last_user, current->group_leader);
/*
* After the sched_job is pushed into the entity queue, it may be
* completed and freed up at any time. We can no longer access it.
- * Make sure to set the submit_ts first, to avoid a race.
*/
- sched_job->submit_ts = submit_ts = ktime_get();
first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
/* first job wakes up scheduler */
if (first) {
- struct drm_gpu_scheduler *sched;
- struct drm_sched_rq *rq;
-
- /* Add the entity to the run queue */
- spin_lock(&entity->lock);
- if (entity->stopped) {
- spin_unlock(&entity->lock);
-
- DRM_ERROR("Trying to push to a killed entity\n");
- return;
- }
-
- rq = entity->rq;
- sched = rq->sched;
-
- spin_lock(&rq->lock);
- drm_sched_rq_add_entity(rq, entity);
-
- if (drm_sched_policy == DRM_SCHED_POLICY_FIFO)
- drm_sched_rq_update_fifo_locked(entity, rq, submit_ts);
-
- spin_unlock(&rq->lock);
- spin_unlock(&entity->lock);
-
- drm_sched_wakeup(sched);
+ sched = drm_sched_rq_add_entity(entity);
+ if (sched)
+ drm_sched_wakeup(sched);
}
}
EXPORT_SYMBOL(drm_sched_entity_push_job);
diff --git a/drivers/gpu/drm/scheduler/sched_fence.c b/drivers/gpu/drm/scheduler/sched_fence.c
index 096fe28aa9c9..85ee3d694dc9 100644
--- a/drivers/gpu/drm/scheduler/sched_fence.c
+++ b/drivers/gpu/drm/scheduler/sched_fence.c
@@ -227,7 +227,7 @@ void drm_sched_fence_init(struct drm_sched_fence *fence,
{
unsigned seq;
- fence->sched = entity->rq->sched;
+ fence->sched = container_of(entity->rq, typeof(*fence->sched), rq);
seq = atomic_inc_return(&entity->fence_seq);
dma_fence_init(&fence->scheduled, &drm_sched_fence_ops_scheduled,
&fence->lock, entity->fence_context, seq);
diff --git a/drivers/gpu/drm/scheduler/sched_internal.h b/drivers/gpu/drm/scheduler/sched_internal.h
index 7ea5a6736f98..13ecb771d7a2 100644
--- a/drivers/gpu/drm/scheduler/sched_internal.h
+++ b/drivers/gpu/drm/scheduler/sched_internal.h
@@ -3,22 +3,48 @@
#ifndef _DRM_GPU_SCHEDULER_INTERNAL_H_
#define _DRM_GPU_SCHEDULER_INTERNAL_H_
+#include <linux/ktime.h>
+#include <linux/kref.h>
+#include <linux/spinlock.h>
-/* Used to choose between FIFO and RR job-scheduling */
-extern int drm_sched_policy;
-
-#define DRM_SCHED_POLICY_RR 0
-#define DRM_SCHED_POLICY_FIFO 1
-
+/**
+ * struct drm_sched_entity_stats - execution stats for an entity.
+ * @kref: reference count for the object.
+ * @lock: lock guarding the @runtime updates.
+ * @runtime: time entity spent on the GPU.
+ * @prev_runtime: previous @runtime used to get the runtime delta.
+ * @vruntime: virtual runtime as accumulated by the fair algorithm.
+ * @avg_job_us: average job duration.
+ *
+ * Because jobs and entities have decoupled lifetimes, ie. we cannot access the
+ * entity once the job has been de-queued, and we do need know how much GPU time
+ * each entity has spent, we need to track this in a separate object which is
+ * reference counted by both entities and jobs.
+ */
+struct drm_sched_entity_stats {
+ struct kref kref;
+ spinlock_t lock; /* Protects the below fields. */
+ ktime_t runtime;
+ ktime_t prev_runtime;
+ ktime_t vruntime;
+
+ struct ewma_drm_sched_avgtime avg_job_us;
+};
+
+bool drm_sched_can_queue(struct drm_gpu_scheduler *sched,
+ struct drm_sched_entity *entity);
void drm_sched_wakeup(struct drm_gpu_scheduler *sched);
-void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
- struct drm_sched_entity *entity);
+void drm_sched_rq_init(struct drm_sched_rq *rq);
+
+struct drm_gpu_scheduler *
+drm_sched_rq_add_entity(struct drm_sched_entity *entity);
void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
struct drm_sched_entity *entity);
+void drm_sched_rq_pop_entity(struct drm_sched_entity *entity);
-void drm_sched_rq_update_fifo_locked(struct drm_sched_entity *entity,
- struct drm_sched_rq *rq, ktime_t ts);
+struct drm_sched_entity *
+drm_sched_select_entity(struct drm_gpu_scheduler *sched);
void drm_sched_entity_select_rq(struct drm_sched_entity *entity);
struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity);
@@ -88,4 +114,32 @@ drm_sched_entity_is_ready(struct drm_sched_entity *entity)
return true;
}
+void drm_sched_entity_stats_release(struct kref *kref);
+
+/**
+ * drm_sched_entity_stats_get - Obtain a reference count on &struct drm_sched_entity_stats object
+ * @stats: struct drm_sched_entity_stats pointer
+ *
+ * Return: struct drm_sched_entity_stats pointer
+ */
+static inline struct drm_sched_entity_stats *
+drm_sched_entity_stats_get(struct drm_sched_entity_stats *stats)
+{
+ kref_get(&stats->kref);
+
+ return stats;
+}
+
+/**
+ * drm_sched_entity_stats_put - Release a reference count on &struct drm_sched_entity_stats object
+ * @stats: struct drm_sched_entity_stats pointer
+ */
+static inline void
+drm_sched_entity_stats_put(struct drm_sched_entity_stats *stats)
+{
+ kref_put(&stats->kref, drm_sched_entity_stats_release);
+}
+
+ktime_t drm_sched_entity_stats_job_add_gpu_time(struct drm_sched_job *job);
+
#endif
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index 21dc82c75c9e..818d3d4434b5 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -84,15 +84,6 @@
#define CREATE_TRACE_POINTS
#include "gpu_scheduler_trace.h"
-int drm_sched_policy = DRM_SCHED_POLICY_FIFO;
-
-/**
- * DOC: sched_policy (int)
- * Used to override default entities scheduling policy in a run queue.
- */
-MODULE_PARM_DESC(sched_policy, "Specify the scheduling policy for entities on a run-queue, " __stringify(DRM_SCHED_POLICY_RR) " = Round Robin, " __stringify(DRM_SCHED_POLICY_FIFO) " = FIFO (default).");
-module_param_named(sched_policy, drm_sched_policy, int, 0444);
-
static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched)
{
u32 credits;
@@ -112,8 +103,8 @@ static u32 drm_sched_available_credits(struct drm_gpu_scheduler *sched)
* Return true if we can push at least one more job from @entity, false
* otherwise.
*/
-static bool drm_sched_can_queue(struct drm_gpu_scheduler *sched,
- struct drm_sched_entity *entity)
+bool drm_sched_can_queue(struct drm_gpu_scheduler *sched,
+ struct drm_sched_entity *entity)
{
struct drm_sched_job *s_job;
@@ -133,211 +124,6 @@ static bool drm_sched_can_queue(struct drm_gpu_scheduler *sched,
return drm_sched_available_credits(sched) >= s_job->credits;
}
-static __always_inline bool drm_sched_entity_compare_before(struct rb_node *a,
- const struct rb_node *b)
-{
- struct drm_sched_entity *ent_a = rb_entry((a), struct drm_sched_entity, rb_tree_node);
- struct drm_sched_entity *ent_b = rb_entry((b), struct drm_sched_entity, rb_tree_node);
-
- return ktime_before(ent_a->oldest_job_waiting, ent_b->oldest_job_waiting);
-}
-
-static void drm_sched_rq_remove_fifo_locked(struct drm_sched_entity *entity,
- struct drm_sched_rq *rq)
-{
- if (!RB_EMPTY_NODE(&entity->rb_tree_node)) {
- rb_erase_cached(&entity->rb_tree_node, &rq->rb_tree_root);
- RB_CLEAR_NODE(&entity->rb_tree_node);
- }
-}
-
-void drm_sched_rq_update_fifo_locked(struct drm_sched_entity *entity,
- struct drm_sched_rq *rq,
- ktime_t ts)
-{
- /*
- * Both locks need to be grabbed, one to protect from entity->rq change
- * for entity from within concurrent drm_sched_entity_select_rq and the
- * other to update the rb tree structure.
- */
- lockdep_assert_held(&entity->lock);
- lockdep_assert_held(&rq->lock);
-
- drm_sched_rq_remove_fifo_locked(entity, rq);
-
- entity->oldest_job_waiting = ts;
-
- rb_add_cached(&entity->rb_tree_node, &rq->rb_tree_root,
- drm_sched_entity_compare_before);
-}
-
-/**
- * drm_sched_rq_init - initialize a given run queue struct
- *
- * @sched: scheduler instance to associate with this run queue
- * @rq: scheduler run queue
- *
- * Initializes a scheduler runqueue.
- */
-static void drm_sched_rq_init(struct drm_gpu_scheduler *sched,
- struct drm_sched_rq *rq)
-{
- spin_lock_init(&rq->lock);
- INIT_LIST_HEAD(&rq->entities);
- rq->rb_tree_root = RB_ROOT_CACHED;
- rq->current_entity = NULL;
- rq->sched = sched;
-}
-
-/**
- * drm_sched_rq_add_entity - add an entity
- *
- * @rq: scheduler run queue
- * @entity: scheduler entity
- *
- * Adds a scheduler entity to the run queue.
- */
-void drm_sched_rq_add_entity(struct drm_sched_rq *rq,
- struct drm_sched_entity *entity)
-{
- lockdep_assert_held(&entity->lock);
- lockdep_assert_held(&rq->lock);
-
- if (!list_empty(&entity->list))
- return;
-
- atomic_inc(rq->sched->score);
- list_add_tail(&entity->list, &rq->entities);
-}
-
-/**
- * drm_sched_rq_remove_entity - remove an entity
- *
- * @rq: scheduler run queue
- * @entity: scheduler entity
- *
- * Removes a scheduler entity from the run queue.
- */
-void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
- struct drm_sched_entity *entity)
-{
- lockdep_assert_held(&entity->lock);
-
- if (list_empty(&entity->list))
- return;
-
- spin_lock(&rq->lock);
-
- atomic_dec(rq->sched->score);
- list_del_init(&entity->list);
-
- if (rq->current_entity == entity)
- rq->current_entity = NULL;
-
- if (drm_sched_policy == DRM_SCHED_POLICY_FIFO)
- drm_sched_rq_remove_fifo_locked(entity, rq);
-
- spin_unlock(&rq->lock);
-}
-
-/**
- * drm_sched_rq_select_entity_rr - Select an entity which could provide a job to run
- *
- * @sched: the gpu scheduler
- * @rq: scheduler run queue to check.
- *
- * Try to find the next ready entity.
- *
- * Return an entity if one is found; return an error-pointer (!NULL) if an
- * entity was ready, but the scheduler had insufficient credits to accommodate
- * its job; return NULL, if no ready entity was found.
- */
-static struct drm_sched_entity *
-drm_sched_rq_select_entity_rr(struct drm_gpu_scheduler *sched,
- struct drm_sched_rq *rq)
-{
- struct drm_sched_entity *entity;
-
- spin_lock(&rq->lock);
-
- entity = rq->current_entity;
- if (entity) {
- list_for_each_entry_continue(entity, &rq->entities, list) {
- if (drm_sched_entity_is_ready(entity))
- goto found;
- }
- }
-
- list_for_each_entry(entity, &rq->entities, list) {
- if (drm_sched_entity_is_ready(entity))
- goto found;
-
- if (entity == rq->current_entity)
- break;
- }
-
- spin_unlock(&rq->lock);
-
- return NULL;
-
-found:
- if (!drm_sched_can_queue(sched, entity)) {
- /*
- * If scheduler cannot take more jobs signal the caller to not
- * consider lower priority queues.
- */
- entity = ERR_PTR(-ENOSPC);
- } else {
- rq->current_entity = entity;
- reinit_completion(&entity->entity_idle);
- }
-
- spin_unlock(&rq->lock);
-
- return entity;
-}
-
-/**
- * drm_sched_rq_select_entity_fifo - Select an entity which provides a job to run
- *
- * @sched: the gpu scheduler
- * @rq: scheduler run queue to check.
- *
- * Find oldest waiting ready entity.
- *
- * Return an entity if one is found; return an error-pointer (!NULL) if an
- * entity was ready, but the scheduler had insufficient credits to accommodate
- * its job; return NULL, if no ready entity was found.
- */
-static struct drm_sched_entity *
-drm_sched_rq_select_entity_fifo(struct drm_gpu_scheduler *sched,
- struct drm_sched_rq *rq)
-{
- struct rb_node *rb;
-
- spin_lock(&rq->lock);
- for (rb = rb_first_cached(&rq->rb_tree_root); rb; rb = rb_next(rb)) {
- struct drm_sched_entity *entity;
-
- entity = rb_entry(rb, struct drm_sched_entity, rb_tree_node);
- if (drm_sched_entity_is_ready(entity)) {
- /* If we can't queue yet, preserve the current entity in
- * terms of fairness.
- */
- if (!drm_sched_can_queue(sched, entity)) {
- spin_unlock(&rq->lock);
- return ERR_PTR(-ENOSPC);
- }
-
- reinit_completion(&entity->entity_idle);
- break;
- }
- }
- spin_unlock(&rq->lock);
-
- return rb ? rb_entry(rb, struct drm_sched_entity, rb_tree_node) : NULL;
-}
-
/**
* drm_sched_run_job_queue - enqueue run-job work
* @sched: scheduler instance
@@ -862,10 +648,11 @@ void drm_sched_job_arm(struct drm_sched_job *job)
BUG_ON(!entity);
drm_sched_entity_select_rq(entity);
- sched = entity->rq->sched;
+ sched = container_of(entity->rq, typeof(*sched), rq);
job->sched = sched;
job->s_priority = entity->priority;
+ job->entity_stats = drm_sched_entity_stats_get(entity->stats);
drm_sched_fence_init(job->s_fence, job->entity);
}
@@ -1055,6 +842,7 @@ void drm_sched_job_cleanup(struct drm_sched_job *job)
* been called.
*/
dma_fence_put(&job->s_fence->finished);
+ drm_sched_entity_stats_put(job->entity_stats);
} else {
/* The job was aborted before it has been committed to be run;
* notably, drm_sched_job_arm() has not been called.
@@ -1084,40 +872,9 @@ void drm_sched_wakeup(struct drm_gpu_scheduler *sched)
}
/**
- * drm_sched_select_entity - Select next entity to process
- *
- * @sched: scheduler instance
- *
- * Return an entity to process or NULL if none are found.
- *
- * Note, that we break out of the for-loop when "entity" is non-null, which can
- * also be an error-pointer--this assures we don't process lower priority
- * run-queues. See comments in the respectively called functions.
- */
-static struct drm_sched_entity *
-drm_sched_select_entity(struct drm_gpu_scheduler *sched)
-{
- struct drm_sched_entity *entity;
- int i;
-
- /* Start with the highest priority.
- */
- for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
- entity = drm_sched_policy == DRM_SCHED_POLICY_FIFO ?
- drm_sched_rq_select_entity_fifo(sched, sched->sched_rq[i]) :
- drm_sched_rq_select_entity_rr(sched, sched->sched_rq[i]);
- if (entity)
- break;
- }
-
- return IS_ERR(entity) ? NULL : entity;
-}
-
-/**
* drm_sched_get_finished_job - fetch the next finished job to be destroyed
*
* @sched: scheduler instance
- * @have_more: are there more finished jobs on the list
*
* Informs the caller through @have_more whether there are more finished jobs
* besides the returned one.
@@ -1126,7 +883,7 @@ drm_sched_select_entity(struct drm_gpu_scheduler *sched)
* ready for it to be destroyed.
*/
static struct drm_sched_job *
-drm_sched_get_finished_job(struct drm_gpu_scheduler *sched, bool *have_more)
+drm_sched_get_finished_job(struct drm_gpu_scheduler *sched)
{
struct drm_sched_job *job, *next;
@@ -1141,7 +898,6 @@ drm_sched_get_finished_job(struct drm_gpu_scheduler *sched, bool *have_more)
/* cancel this job's TO timer */
cancel_delayed_work(&sched->work_tdr);
- *have_more = false;
next = list_first_entry_or_null(&sched->pending_list,
typeof(*next), list);
if (next) {
@@ -1151,8 +907,6 @@ drm_sched_get_finished_job(struct drm_gpu_scheduler *sched, bool *have_more)
next->s_fence->scheduled.timestamp =
dma_fence_timestamp(&job->s_fence->finished);
- *have_more = dma_fence_is_signaled(&next->s_fence->finished);
-
/* start TO timer for next job */
drm_sched_start_timeout(sched);
}
@@ -1211,13 +965,15 @@ static void drm_sched_free_job_work(struct work_struct *w)
struct drm_gpu_scheduler *sched =
container_of(w, struct drm_gpu_scheduler, work_free_job);
struct drm_sched_job *job;
- bool have_more;
- job = drm_sched_get_finished_job(sched, &have_more);
- if (job) {
+ while ((job = drm_sched_get_finished_job(sched))) {
+ ktime_t duration = drm_sched_entity_stats_job_add_gpu_time(job);
+
+ /* Serialized by the worker. */
+ ewma_drm_sched_avgtime_add(&sched->avg_job_us,
+ ktime_to_us(duration));
+
sched->ops->free_job(job);
- if (have_more)
- drm_sched_run_free_queue(sched);
}
drm_sched_run_job_queue(sched);
@@ -1240,7 +996,7 @@ static void drm_sched_run_job_work(struct work_struct *w)
/* Find entity with a ready job */
entity = drm_sched_select_entity(sched);
- if (!entity) {
+ if (IS_ERR_OR_NULL(entity)) {
/*
* Either no more work to do, or the next ready job needs more
* credits than the scheduler has currently available.
@@ -1316,8 +1072,6 @@ static struct workqueue_struct *drm_sched_alloc_wq(const char *name)
*/
int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_args *args)
{
- int i;
-
sched->ops = args->ops;
sched->credit_limit = args->credit_limit;
sched->name = args->name;
@@ -1327,21 +1081,6 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
sched->score = args->score ? args->score : &sched->_score;
sched->dev = args->dev;
- if (args->num_rqs > DRM_SCHED_PRIORITY_COUNT) {
- /* This is a gross violation--tell drivers what the problem is.
- */
- dev_err(sched->dev, "%s: num_rqs cannot be greater than DRM_SCHED_PRIORITY_COUNT\n",
- __func__);
- return -EINVAL;
- } else if (sched->sched_rq) {
- /* Not an error, but warn anyway so drivers can
- * fine-tune their DRM calling order, and return all
- * is good.
- */
- dev_warn(sched->dev, "%s: scheduler already initialized!\n", __func__);
- return 0;
- }
-
if (args->submit_wq) {
sched->submit_wq = args->submit_wq;
sched->own_submit_wq = false;
@@ -1353,17 +1092,7 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
sched->own_submit_wq = true;
}
- sched->sched_rq = kmalloc_objs(*sched->sched_rq, args->num_rqs,
- GFP_KERNEL | __GFP_ZERO);
- if (!sched->sched_rq)
- goto Out_check_own;
- sched->num_rqs = args->num_rqs;
- for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) {
- sched->sched_rq[i] = kzalloc_obj(*sched->sched_rq[i]);
- if (!sched->sched_rq[i])
- goto Out_unroll;
- drm_sched_rq_init(sched, sched->sched_rq[i]);
- }
+ drm_sched_rq_init(&sched->rq);
init_waitqueue_head(&sched->job_scheduled);
INIT_LIST_HEAD(&sched->pending_list);
@@ -1375,20 +1104,10 @@ int drm_sched_init(struct drm_gpu_scheduler *sched, const struct drm_sched_init_
atomic_set(&sched->_score, 0);
atomic64_set(&sched->job_id_count, 0);
sched->pause_submit = false;
+ ewma_drm_sched_avgtime_init(&sched->avg_job_us);
sched->ready = true;
return 0;
-Out_unroll:
- for (--i ; i >= DRM_SCHED_PRIORITY_KERNEL; i--)
- kfree(sched->sched_rq[i]);
-
- kfree(sched->sched_rq);
- sched->sched_rq = NULL;
-Out_check_own:
- if (sched->own_submit_wq)
- destroy_workqueue(sched->submit_wq);
- dev_err(sched->dev, "%s: Failed to setup GPU scheduler--out of memory\n", __func__);
- return -ENOMEM;
}
EXPORT_SYMBOL(drm_sched_init);
@@ -1419,13 +1138,8 @@ static void drm_sched_cancel_remaining_jobs(struct drm_gpu_scheduler *sched)
*/
void drm_sched_fini(struct drm_gpu_scheduler *sched)
{
- int i;
-
drm_sched_wqueue_stop(sched);
- for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++)
- kfree(sched->sched_rq[i]);
-
/* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */
wake_up_all(&sched->job_scheduled);
@@ -1439,8 +1153,6 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched)
if (sched->own_submit_wq)
destroy_workqueue(sched->submit_wq);
sched->ready = false;
- kfree(sched->sched_rq);
- sched->sched_rq = NULL;
if (!list_empty(&sched->pending_list))
dev_warn(sched->dev, "Tearing down scheduler while jobs are pending!\n");
@@ -1458,35 +1170,28 @@ EXPORT_SYMBOL(drm_sched_fini);
*/
void drm_sched_increase_karma(struct drm_sched_job *bad)
{
- int i;
- struct drm_sched_entity *tmp;
- struct drm_sched_entity *entity;
struct drm_gpu_scheduler *sched = bad->sched;
+ struct drm_sched_entity *entity, *tmp;
+ struct drm_sched_rq *rq = &sched->rq;
/* don't change @bad's karma if it's from KERNEL RQ,
* because sometimes GPU hang would cause kernel jobs (like VM updating jobs)
* corrupt but keep in mind that kernel jobs always considered good.
*/
- if (bad->s_priority != DRM_SCHED_PRIORITY_KERNEL) {
- atomic_inc(&bad->karma);
-
- for (i = DRM_SCHED_PRIORITY_HIGH; i < sched->num_rqs; i++) {
- struct drm_sched_rq *rq = sched->sched_rq[i];
-
- spin_lock(&rq->lock);
- list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
- if (bad->s_fence->scheduled.context ==
- entity->fence_context) {
- if (entity->guilty)
- atomic_set(entity->guilty, 1);
- break;
- }
- }
- spin_unlock(&rq->lock);
- if (&entity->list != &rq->entities)
- break;
+ if (bad->s_priority == DRM_SCHED_PRIORITY_KERNEL)
+ return;
+
+ atomic_inc(&bad->karma);
+
+ spin_lock(&rq->lock);
+ list_for_each_entry_safe(entity, tmp, &rq->entities, list) {
+ if (bad->s_fence->scheduled.context == entity->fence_context) {
+ if (entity->guilty)
+ atomic_set(entity->guilty, 1);
+ break;
}
}
+ spin_unlock(&rq->lock);
}
EXPORT_SYMBOL(drm_sched_increase_karma);
diff --git a/drivers/gpu/drm/scheduler/sched_rq.c b/drivers/gpu/drm/scheduler/sched_rq.c
new file mode 100644
index 000000000000..044546bcb5f8
--- /dev/null
+++ b/drivers/gpu/drm/scheduler/sched_rq.c
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: MIT
+/* Copyright 2015 Advanced Micro Devices, Inc. */
+/* Copyright (c) 2025 Valve Corporation */
+
+#include <linux/rbtree.h>
+
+#include <drm/drm_print.h>
+#include <drm/gpu_scheduler.h>
+
+#include "sched_internal.h"
+
+static __always_inline bool
+drm_sched_entity_compare_before(struct rb_node *a, const struct rb_node *b)
+{
+ struct drm_sched_entity *ea =
+ rb_entry((a), struct drm_sched_entity, rb_tree_node);
+ struct drm_sched_entity *eb =
+ rb_entry((b), struct drm_sched_entity, rb_tree_node);
+
+ return ktime_before(ea->oldest_job_waiting, eb->oldest_job_waiting);
+}
+
+static void drm_sched_rq_update_prio(struct drm_sched_rq *rq)
+{
+ enum drm_sched_priority prio = DRM_SCHED_PRIORITY_INVALID;
+ struct rb_node *rb;
+
+ lockdep_assert_held(&rq->lock);
+
+ rb = rb_first_cached(&rq->rb_tree_root);
+ if (rb) {
+ struct drm_sched_entity *entity =
+ rb_entry(rb, typeof(*entity), rb_tree_node);
+
+ /*
+ * The normal locking order is entity then run-queue so taking
+ * the entity lock here would be a locking inversion for the
+ * case when the current head of the run-queue is different from
+ * the one we already have locked. The unlocked read is fine
+ * though, because if the priority had just changed it is no big
+ * deal for our algorithm, but just a transient reachable only
+ * by drivers with userspace dynamic priority changes API. Equal
+ * in effect to the priority change becoming visible a few
+ * instructions later.
+ */
+ prio = READ_ONCE(entity->priority);
+ }
+
+ rq->head_prio = prio;
+}
+
+static void drm_sched_rq_remove_tree_locked(struct drm_sched_entity *entity,
+ struct drm_sched_rq *rq)
+{
+ lockdep_assert_held(&entity->lock);
+ lockdep_assert_held(&rq->lock);
+
+ if (!RB_EMPTY_NODE(&entity->rb_tree_node)) {
+ rb_erase_cached(&entity->rb_tree_node, &rq->rb_tree_root);
+ RB_CLEAR_NODE(&entity->rb_tree_node);
+ drm_sched_rq_update_prio(rq);
+ }
+}
+
+static void drm_sched_rq_update_tree_locked(struct drm_sched_entity *entity,
+ struct drm_sched_rq *rq,
+ ktime_t ts)
+{
+ /*
+ * Both locks need to be grabbed, one to protect from entity->rq change
+ * for entity from within concurrent drm_sched_entity_select_rq and the
+ * other to update the rb tree structure.
+ */
+ lockdep_assert_held(&entity->lock);
+ lockdep_assert_held(&rq->lock);
+
+ drm_sched_rq_remove_tree_locked(entity, rq);
+
+ entity->oldest_job_waiting = ts;
+
+ rb_add_cached(&entity->rb_tree_node, &rq->rb_tree_root,
+ drm_sched_entity_compare_before);
+ drm_sched_rq_update_prio(rq);
+}
+
+/**
+ * drm_sched_rq_init - initialize a given run queue struct
+ * @rq: scheduler run queue
+ *
+ * Initializes a scheduler runqueue.
+ */
+void drm_sched_rq_init(struct drm_sched_rq *rq)
+{
+ spin_lock_init(&rq->lock);
+ INIT_LIST_HEAD(&rq->entities);
+ rq->rb_tree_root = RB_ROOT_CACHED;
+ rq->head_prio = DRM_SCHED_PRIORITY_INVALID;
+}
+
+/*
+ * Core part of the CFS-like algorithm is that the virtual runtime of lower
+ * priority tasks should grow quicker than the higher priority ones, so that
+ * when we then schedule entities with the aim of keeping their accumulated
+ * virtual time balanced, we can approach fair distribution of GPU time.
+ *
+ * For converting the real GPU time into virtual we pick some multipliers with
+ * the idea to achieve the following GPU time distribution:
+ *
+ * - Kernel priority gets roughly 2x GPU time compared to high.
+ * - High gets ~4x relative to normal.
+ * - Normal gets ~8x relative to low.
+ */
+static const unsigned int vruntime_shift[] = {
+ [DRM_SCHED_PRIORITY_KERNEL] = 1,
+ [DRM_SCHED_PRIORITY_HIGH] = 2,
+ [DRM_SCHED_PRIORITY_NORMAL] = 4,
+ [DRM_SCHED_PRIORITY_LOW] = 7,
+};
+
+static ktime_t
+drm_sched_rq_get_min_vruntime(struct drm_sched_rq *rq)
+{
+ ktime_t vruntime = 0;
+ struct rb_node *rb;
+
+ lockdep_assert_held(&rq->lock);
+
+ rb = rb_first_cached(&rq->rb_tree_root);
+ if (rb) {
+ struct drm_sched_entity *entity =
+ rb_entry(rb, typeof(*entity), rb_tree_node);
+ struct drm_sched_entity_stats *stats = entity->stats;
+
+ spin_lock(&stats->lock);
+ vruntime = stats->vruntime;
+ spin_unlock(&stats->lock);
+ }
+
+ return vruntime;
+}
+
+static void
+drm_sched_entity_save_vruntime(struct drm_sched_entity *entity,
+ ktime_t min_vruntime)
+{
+ struct drm_sched_entity_stats *stats = entity->stats;
+ ktime_t vruntime;
+
+ spin_lock(&stats->lock);
+ vruntime = stats->vruntime;
+ if (min_vruntime && vruntime > min_vruntime)
+ vruntime = ktime_sub(vruntime, min_vruntime);
+ else
+ vruntime = 0;
+ stats->vruntime = vruntime;
+ spin_unlock(&stats->lock);
+}
+
+static ktime_t
+drm_sched_entity_restore_vruntime(struct drm_sched_entity *entity,
+ ktime_t min_vruntime,
+ enum drm_sched_priority rq_prio)
+{
+ struct drm_sched_entity_stats *stats = entity->stats;
+ struct drm_gpu_scheduler *sched =
+ container_of(entity->rq, typeof(*sched), rq);
+ enum drm_sched_priority prio = entity->priority;
+ unsigned long avg_us, sched_avg_us;
+ ktime_t vruntime;
+
+ BUILD_BUG_ON(DRM_SCHED_PRIORITY_NORMAL < DRM_SCHED_PRIORITY_HIGH);
+
+ spin_lock(&stats->lock);
+ vruntime = stats->vruntime;
+ avg_us = ewma_drm_sched_avgtime_read(&stats->avg_job_us);
+ /*
+ * Unlocked read of the scheduler average is fine since it is just
+ * heuristics and data type is a natural word size.
+ */
+ sched_avg_us = ewma_drm_sched_avgtime_read(&sched->avg_job_us);
+
+ /*
+ * Special handling for entities which were picked from the top of the
+ * queue and are now re-joining the top with another one already there.
+ */
+ if (!vruntime && rq_prio != DRM_SCHED_PRIORITY_INVALID) {
+ if (prio > rq_prio) {
+ /*
+ * Lower priority should not overtake higher when re-
+ * joining at the top of the queue so push it back
+ * somewhere behind the "middle" of the run-queue,
+ * proportional to the scheduler and entity average job
+ * durations.
+ */
+ vruntime = us_to_ktime((1 + avg_us + sched_avg_us) <<
+ vruntime_shift[prio]);
+ } else if (prio < rq_prio) {
+ /*
+ * Higher priority can go first.
+ */
+ vruntime = -ns_to_ktime(rq_prio - prio);
+ } else {
+ /* Favour entity with shorter jobs (interactivity). */
+ if (avg_us <= sched_avg_us)
+ vruntime = -ns_to_ktime(1);
+ else
+ vruntime = ns_to_ktime(1);
+ }
+ }
+
+ /*
+ * Restore saved relative position in the queue.
+ */
+ vruntime = ktime_add(min_vruntime, vruntime);
+
+ stats->vruntime = vruntime;
+ spin_unlock(&stats->lock);
+
+ return vruntime;
+}
+
+static ktime_t drm_sched_entity_update_vruntime(struct drm_sched_entity *entity)
+{
+ struct drm_sched_entity_stats *stats = entity->stats;
+ ktime_t runtime, prev;
+
+ spin_lock(&stats->lock);
+ prev = stats->prev_runtime;
+ runtime = stats->runtime;
+ stats->prev_runtime = runtime;
+ runtime = ktime_add_ns(stats->vruntime,
+ ktime_to_ns(ktime_sub(runtime, prev)) <<
+ vruntime_shift[entity->priority]);
+ stats->vruntime = runtime;
+ spin_unlock(&stats->lock);
+
+ return runtime;
+}
+
+/**
+ * drm_sched_rq_add_entity - add an entity
+ * @entity: scheduler entity
+ *
+ * Adds a scheduler entity to the run queue.
+ *
+ * Return: DRM scheduler selected to handle this entity or NULL if entity has
+ * been stopped and cannot be submitted to.
+ */
+struct drm_gpu_scheduler *
+drm_sched_rq_add_entity(struct drm_sched_entity *entity)
+{
+ struct drm_gpu_scheduler *sched;
+ struct drm_sched_rq *rq;
+ ktime_t ts;
+
+ /* Add the entity to the run queue */
+ spin_lock(&entity->lock);
+ if (entity->stopped) {
+ spin_unlock(&entity->lock);
+
+ DRM_ERROR("Trying to push to a killed entity\n");
+ return NULL;
+ }
+
+ rq = entity->rq;
+ sched = container_of(rq, typeof(*sched), rq);
+ spin_lock(&rq->lock);
+
+ if (list_empty(&entity->list)) {
+ atomic_inc(sched->score);
+ list_add_tail(&entity->list, &rq->entities);
+ }
+
+ ts = drm_sched_rq_get_min_vruntime(rq);
+ ts = drm_sched_entity_restore_vruntime(entity, ts, rq->head_prio);
+ drm_sched_rq_update_tree_locked(entity, rq, ts);
+
+ spin_unlock(&rq->lock);
+ spin_unlock(&entity->lock);
+
+ return sched;
+}
+
+/**
+ * drm_sched_rq_remove_entity - remove an entity
+ * @rq: scheduler run queue
+ * @entity: scheduler entity
+ *
+ * Removes a scheduler entity from the run queue.
+ */
+void drm_sched_rq_remove_entity(struct drm_sched_rq *rq,
+ struct drm_sched_entity *entity)
+{
+ struct drm_gpu_scheduler *sched = container_of(rq, typeof(*sched), rq);
+
+ lockdep_assert_held(&entity->lock);
+
+ if (list_empty(&entity->list))
+ return;
+
+ spin_lock(&rq->lock);
+
+ atomic_dec(sched->score);
+ list_del_init(&entity->list);
+
+ drm_sched_rq_remove_tree_locked(entity, rq);
+
+ spin_unlock(&rq->lock);
+}
+
+/**
+ * drm_sched_rq_pop_entity - pops an entity
+ * @entity: scheduler entity
+ *
+ * To be called every time after a job is popped from the entity.
+ */
+void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
+{
+ struct drm_sched_job *next_job;
+ struct drm_sched_rq *rq;
+
+ /*
+ * Update the entity's location in the min heap according to
+ * the timestamp of the next job, if any.
+ */
+ spin_lock(&entity->lock);
+ rq = entity->rq;
+ spin_lock(&rq->lock);
+ next_job = drm_sched_entity_queue_peek(entity);
+ if (next_job) {
+ ktime_t ts;
+
+ ts = drm_sched_entity_update_vruntime(entity);
+ drm_sched_rq_update_tree_locked(entity, rq, ts);
+ } else {
+ ktime_t min_vruntime;
+
+ drm_sched_rq_remove_tree_locked(entity, rq);
+ min_vruntime = drm_sched_rq_get_min_vruntime(rq);
+ drm_sched_entity_save_vruntime(entity, min_vruntime);
+ }
+ spin_unlock(&rq->lock);
+ spin_unlock(&entity->lock);
+}
+
+/**
+ * drm_sched_select_entity - Select an entity which provides a job to run
+ * @sched: the gpu scheduler
+ *
+ * Find oldest waiting ready entity.
+ *
+ * Return an entity if one is found; return an error-pointer (!NULL) if an
+ * entity was ready, but the scheduler had insufficient credits to accommodate
+ * its job; return NULL, if no ready entity was found.
+ */
+struct drm_sched_entity *
+drm_sched_select_entity(struct drm_gpu_scheduler *sched)
+{
+ struct drm_sched_rq *rq = &sched->rq;
+ struct rb_node *rb;
+
+ spin_lock(&rq->lock);
+ for (rb = rb_first_cached(&rq->rb_tree_root); rb; rb = rb_next(rb)) {
+ struct drm_sched_entity *entity;
+
+ entity = rb_entry(rb, struct drm_sched_entity, rb_tree_node);
+ if (drm_sched_entity_is_ready(entity)) {
+ /* If we can't queue yet, preserve the current entity in
+ * terms of fairness.
+ */
+ if (!drm_sched_can_queue(sched, entity)) {
+ spin_unlock(&rq->lock);
+ return ERR_PTR(-ENOSPC);
+ }
+
+ reinit_completion(&entity->entity_idle);
+ break;
+ }
+ }
+ spin_unlock(&rq->lock);
+
+ return rb ? rb_entry(rb, struct drm_sched_entity, rb_tree_node) : NULL;
+}
diff --git a/drivers/gpu/drm/scheduler/tests/Makefile b/drivers/gpu/drm/scheduler/tests/Makefile
index 5bf707bad373..9ec185fbbc15 100644
--- a/drivers/gpu/drm/scheduler/tests/Makefile
+++ b/drivers/gpu/drm/scheduler/tests/Makefile
@@ -2,6 +2,7 @@
drm-sched-tests-y := \
mock_scheduler.o \
- tests_basic.o
+ tests_basic.o \
+ tests_scheduler.o
obj-$(CONFIG_DRM_SCHED_KUNIT_TEST) += drm-sched-tests.o
diff --git a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c
index 8e9ae7d980eb..14403a762335 100644
--- a/drivers/gpu/drm/scheduler/tests/mock_scheduler.c
+++ b/drivers/gpu/drm/scheduler/tests/mock_scheduler.c
@@ -290,7 +290,6 @@ struct drm_mock_scheduler *drm_mock_sched_new(struct kunit *test, long timeout)
{
struct drm_sched_init_args args = {
.ops = &drm_mock_scheduler_ops,
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = U32_MAX,
.hang_limit = 1,
.timeout = timeout,
diff --git a/drivers/gpu/drm/scheduler/tests/tests_scheduler.c b/drivers/gpu/drm/scheduler/tests/tests_scheduler.c
new file mode 100644
index 000000000000..8b2e4ef9915f
--- /dev/null
+++ b/drivers/gpu/drm/scheduler/tests/tests_scheduler.c
@@ -0,0 +1,882 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Valve Corporation */
+
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/ktime.h>
+#include <linux/math64.h>
+
+#include "sched_tests.h"
+
+/*
+ * DRM scheduler tests exercise load balancing decisions ie. entity selection
+ * logic.
+ */
+
+static int drm_sched_scheduler_init(struct kunit *test)
+{
+ struct drm_mock_scheduler *sched;
+
+ sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
+ sched->base.credit_limit = 1;
+
+ test->priv = sched;
+
+ return 0;
+}
+
+static int drm_sched_scheduler_init2(struct kunit *test)
+{
+ struct drm_mock_scheduler *sched;
+
+ sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT);
+ sched->base.credit_limit = 2;
+
+ test->priv = sched;
+
+ return 0;
+}
+
+static void drm_sched_scheduler_exit(struct kunit *test)
+{
+ struct drm_mock_scheduler *sched = test->priv;
+
+ drm_mock_sched_fini(sched);
+}
+
+static void drm_sched_scheduler_queue_overhead(struct kunit *test)
+{
+ struct drm_mock_scheduler *sched = test->priv;
+ struct drm_mock_sched_entity *entity;
+ const unsigned int job_us = 1000;
+ const unsigned int jobs = 1000;
+ const unsigned int total_us = jobs * job_us;
+ struct drm_mock_sched_job *job, *first;
+ ktime_t start, end;
+ bool done;
+ int i;
+
+ /*
+ * Deep queue job at a time processing (single credit).
+ *
+ * This measures the overhead of picking and processing a job at a time
+ * by comparing the ideal total "GPU" time of all submitted jobs versus
+ * the time actually taken.
+ */
+
+ KUNIT_ASSERT_EQ(test, sched->base.credit_limit, 1);
+
+ entity = drm_mock_sched_entity_new(test,
+ DRM_SCHED_PRIORITY_NORMAL,
+ sched);
+
+ for (i = 0; i <= jobs; i++) {
+ job = drm_mock_sched_job_new(test, entity);
+ if (i == 0)
+ first = job; /* Extra first job blocks the queue */
+ else
+ drm_mock_sched_job_set_duration_us(job, job_us);
+ drm_mock_sched_job_submit(job);
+ }
+
+ done = drm_mock_sched_job_wait_scheduled(first, HZ);
+ KUNIT_ASSERT_TRUE(test, done);
+
+ start = ktime_get();
+ i = drm_mock_sched_advance(sched, 1); /* Release the queue */
+ KUNIT_ASSERT_EQ(test, i, 1);
+
+ /* Wait with a safe margin to avoid every failing. */
+ done = drm_mock_sched_job_wait_finished(job,
+ usecs_to_jiffies(total_us) * 5);
+ end = ktime_get();
+ KUNIT_ASSERT_TRUE(test, done);
+
+ pr_info("Expected %uus, actual %lldus\n",
+ total_us,
+ ktime_to_us(ktime_sub(end, start)));
+
+ drm_mock_sched_entity_free(entity);
+}
+
+static void drm_sched_scheduler_ping_pong(struct kunit *test)
+{
+ struct drm_mock_sched_job *job, *first, *prev = NULL;
+ struct drm_mock_scheduler *sched = test->priv;
+ struct drm_mock_sched_entity *entity[2];
+ const unsigned int job_us = 1000;
+ const unsigned int jobs = 1000;
+ const unsigned int total_us = jobs * job_us;
+ ktime_t start, end;
+ bool done;
+ int i;
+
+ /*
+ * Two entitites in inter-dependency chain.
+ *
+ * This measures the overhead of picking and processing a job at a time,
+ * where each job depends on the previous one from the diffferent
+ * entity, by comparing the ideal total "GPU" time of all submitted jobs
+ * versus the time actually taken.
+ */
+
+ KUNIT_ASSERT_EQ(test, sched->base.credit_limit, 1);
+
+ for (i = 0; i < ARRAY_SIZE(entity); i++)
+ entity[i] = drm_mock_sched_entity_new(test,
+ DRM_SCHED_PRIORITY_NORMAL,
+ sched);
+
+ for (i = 0; i <= jobs; i++) {
+ job = drm_mock_sched_job_new(test, entity[i & 1]);
+ if (i == 0)
+ first = job; /* Extra first job blocks the queue */
+ else
+ drm_mock_sched_job_set_duration_us(job, job_us);
+ if (prev)
+ drm_sched_job_add_dependency(&job->base,
+ dma_fence_get(&prev->base.s_fence->finished));
+ drm_mock_sched_job_submit(job);
+ prev = job;
+ }
+
+ done = drm_mock_sched_job_wait_scheduled(first, HZ);
+ KUNIT_ASSERT_TRUE(test, done);
+
+ start = ktime_get();
+ i = drm_mock_sched_advance(sched, 1); /* Release the queue */
+ KUNIT_ASSERT_EQ(test, i, 1);
+
+ /* Wait with a safe margin to avoid every failing. */
+ done = drm_mock_sched_job_wait_finished(job,
+ usecs_to_jiffies(total_us) * 5);
+ end = ktime_get();
+ KUNIT_ASSERT_TRUE(test, done);
+
+ pr_info("Expected %uus, actual %lldus\n",
+ total_us,
+ ktime_to_us(ktime_sub(end, start)));
+
+ for (i = 0; i < ARRAY_SIZE(entity); i++)
+ drm_mock_sched_entity_free(entity[i]);
+}
+
+static struct kunit_case drm_sched_scheduler_overhead_tests[] = {
+ KUNIT_CASE_SLOW(drm_sched_scheduler_queue_overhead),
+ KUNIT_CASE_SLOW(drm_sched_scheduler_ping_pong),
+ {}
+};
+
+static struct kunit_suite drm_sched_scheduler_overhead = {
+ .name = "drm_sched_scheduler_overhead_tests",
+ .init = drm_sched_scheduler_init,
+ .exit = drm_sched_scheduler_exit,
+ .test_cases = drm_sched_scheduler_overhead_tests,
+};
+
+/*
+ * struct drm_sched_client_params - describe a workload emitted from a client
+ *
+ * A simulated client will create an entity with a scheduling @priority and emit
+ * jobs in a loop where each iteration will consist of:
+ *
+ * 1. Submit @job_cnt jobs, each with a set duration of @job_us.
+ * 2. If @sync is true wait for last submitted job to finish.
+ * 3. Sleep for @wait_us micro-seconds.
+ * 4. Repeat.
+ */
+struct drm_sched_client_params {
+ enum drm_sched_priority priority;
+ unsigned int job_cnt;
+ unsigned int job_us;
+ bool sync;
+ unsigned int wait_us;
+};
+
+struct drm_sched_test_params {
+ const char *description;
+ unsigned int num_clients;
+ struct drm_sched_client_params client[2];
+};
+
+static const struct drm_sched_test_params drm_sched_cases[] = {
+ {
+ .description = "Normal priority and normal priority",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ },
+ {
+ .description = "Normal priority and low priority",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_LOW,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ },
+ {
+ .description = "High priority and normal priority",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_HIGH,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ },
+ {
+ .description = "High priority and low priority",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_HIGH,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_LOW,
+ .job_cnt = 1,
+ .job_us = 8000,
+ .wait_us = 0,
+ .sync = false,
+ },
+ },
+ {
+ .description = "50% and 50%",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 1500,
+ .wait_us = 1500,
+ .sync = true,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 2500,
+ .wait_us = 2500,
+ .sync = true,
+ },
+ },
+ {
+ .description = "50% and 50% low priority",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 1500,
+ .wait_us = 1500,
+ .sync = true,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_LOW,
+ .job_cnt = 1,
+ .job_us = 2500,
+ .wait_us = 2500,
+ .sync = true,
+ },
+ },
+ {
+ .description = "50% high priority and 50%",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_HIGH,
+ .job_cnt = 1,
+ .job_us = 1500,
+ .wait_us = 1500,
+ .sync = true,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 2500,
+ .wait_us = 2500,
+ .sync = true,
+ },
+ },
+ {
+ .description = "Low priority hog and interactive client",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_LOW,
+ .job_cnt = 3,
+ .job_us = 2500,
+ .wait_us = 500,
+ .sync = false,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 500,
+ .wait_us = 10000,
+ .sync = true,
+ },
+ },
+ {
+ .description = "Heavy rendering and interactive client",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 3,
+ .job_us = 2500,
+ .wait_us = 2500,
+ .sync = true,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 1000,
+ .wait_us = 9000,
+ .sync = true,
+ },
+ },
+ {
+ .description = "Very heavy rendering and interactive client",
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 4,
+ .job_us = 50000,
+ .wait_us = 1,
+ .sync = true,
+ },
+ .client[1] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 1,
+ .job_us = 1000,
+ .wait_us = 9000,
+ .sync = true,
+ },
+ },
+};
+
+static void
+drm_sched_desc(const struct drm_sched_test_params *params, char *desc)
+{
+ strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE);
+}
+
+KUNIT_ARRAY_PARAM(drm_sched_scheduler_two_clients,
+ drm_sched_cases,
+ drm_sched_desc);
+
+/*
+ * struct test_client_stats - track client stats
+ *
+ * For each client executing a simulated workload we track some timings for
+ * which we are interested in the minimum of all iterations (@min_us), maximum
+ * (@max_us) and the overall total for all iterations (@tot_us).
+ */
+struct test_client_stats {
+ unsigned int min_us;
+ unsigned int max_us;
+ unsigned long tot_us;
+};
+
+/*
+ * struct test_client - a simulated userspace client submitting scheduler work
+ *
+ * Each client executing a simulated workload is represented by one of these.
+ *
+ * Each of them instantiates a scheduling @entity and executes a workloads as
+ * defined in @params. Based on those @params the theoretical execution time of
+ * the client is calculated as @ideal_duration, while the actual wall time is
+ * tracked in @duration (calculated based on the @start and @end client time-
+ * stamps).
+ *
+ * Numerical @id is assigned to each for logging purposes.
+ *
+ * @worker and @work are used to provide an independent execution context from
+ * which scheduler jobs are submitted.
+ *
+ * During execution statistics on how long it took to submit and execute one
+ * iteration (whether or not synchronous) is kept in @cycle_time, while
+ * @latency_time tracks the @cycle_time minus the ideal duration of the one
+ * cycle.
+ *
+ * Once the client has completed the set number of iterations it will write the
+ * completion status into @done.
+ */
+struct test_client {
+ struct kunit *test; /* Backpointer to the kunit test. */
+
+ struct drm_mock_sched_entity *entity;
+ struct kthread_worker *worker;
+ struct kthread_work work;
+
+ struct drm_sched_client_params params;
+
+ unsigned int id;
+ ktime_t duration;
+ ktime_t ideal_duration;
+ unsigned int cycles;
+ unsigned int cycle;
+ ktime_t start;
+ ktime_t end;
+ bool done;
+
+ struct test_client_stats cycle_time;
+ struct test_client_stats latency_time;
+};
+
+static void
+update_stats(struct test_client_stats *stats, unsigned int us)
+{
+ if (us > stats->max_us)
+ stats->max_us = us;
+ if (us < stats->min_us)
+ stats->min_us = us;
+ stats->tot_us += us;
+}
+
+static unsigned int
+get_stats_avg(struct test_client_stats *stats, unsigned int cycles)
+{
+ return div_u64(stats->tot_us, cycles);
+}
+
+static void drm_sched_client_work(struct kthread_work *work)
+{
+ struct test_client *client = container_of(work, typeof(*client), work);
+ const long sync_wait = MAX_SCHEDULE_TIMEOUT;
+ unsigned int cycle, work_us, period_us;
+ struct drm_mock_sched_job *job = NULL;
+
+ work_us = client->params.job_cnt * client->params.job_us;
+ period_us = work_us + client->params.wait_us;
+ client->cycles =
+ DIV_ROUND_UP((unsigned int)ktime_to_us(client->duration),
+ period_us);
+ client->ideal_duration = us_to_ktime(client->cycles * period_us);
+
+ client->start = ktime_get();
+
+ for (cycle = 0; cycle < client->cycles; cycle++) {
+ ktime_t cycle_time;
+ unsigned int batch;
+ unsigned long us;
+
+ if (READ_ONCE(client->done))
+ break;
+
+ cycle_time = ktime_get();
+ for (batch = 0; batch < client->params.job_cnt; batch++) {
+ job = drm_mock_sched_job_new(client->test,
+ client->entity);
+ drm_mock_sched_job_set_duration_us(job,
+ client->params.job_us);
+ drm_mock_sched_job_submit(job);
+ }
+
+ if (client->params.sync)
+ drm_mock_sched_job_wait_finished(job, sync_wait);
+
+ cycle_time = ktime_sub(ktime_get(), cycle_time);
+ us = ktime_to_us(cycle_time);
+ update_stats(&client->cycle_time, us);
+ if (ktime_to_us(cycle_time) >= (long)work_us)
+ us = ktime_to_us(cycle_time) - work_us;
+ else if (WARN_ON_ONCE(client->params.sync)) /* GPU job took less than expected. */
+ us = 0;
+ update_stats(&client->latency_time, us);
+ WRITE_ONCE(client->cycle, cycle);
+
+ if (READ_ONCE(client->done))
+ break;
+
+ if (client->params.wait_us)
+ fsleep(client->params.wait_us);
+ else if (!client->params.sync)
+ cond_resched(); /* Do not hog the CPU if fully async. */
+ }
+
+ client->done = drm_mock_sched_job_wait_finished(job, sync_wait);
+ client->end = ktime_get();
+}
+
+static const char *prio_str(enum drm_sched_priority prio)
+{
+ switch (prio) {
+ case DRM_SCHED_PRIORITY_KERNEL:
+ return "kernel";
+ case DRM_SCHED_PRIORITY_LOW:
+ return "low";
+ case DRM_SCHED_PRIORITY_NORMAL:
+ return "normal";
+ case DRM_SCHED_PRIORITY_HIGH:
+ return "high";
+ default:
+ return "???";
+ }
+}
+
+static bool client_done(struct test_client *client)
+{
+ return READ_ONCE(client->done); /* READ_ONCE to document lockless read from a loop. */
+}
+
+static void drm_sched_scheduler_two_clients_test(struct kunit *test)
+{
+ const struct drm_sched_test_params *params = test->param_value;
+ struct drm_mock_scheduler *sched = test->priv;
+ struct test_client client[2] = { };
+ unsigned int prev_cycle[2] = { };
+ unsigned int i, j;
+ ktime_t start;
+
+ /*
+ * Same job stream from two clients.
+ */
+
+ for (i = 0; i < ARRAY_SIZE(client); i++)
+ client[i].entity =
+ drm_mock_sched_entity_new(test,
+ params->client[i].priority,
+ sched);
+
+ for (i = 0; i < ARRAY_SIZE(client); i++) {
+ client[i].test = test;
+ client[i].id = i;
+ client[i].duration = ms_to_ktime(1000);
+ client[i].params = params->client[i];
+ client[i].cycle_time.min_us = ~0U;
+ client[i].latency_time.min_us = ~0U;
+ client[i].worker =
+ kthread_create_worker(0, "%s-%u", __func__, i);
+ if (IS_ERR(client[i].worker)) {
+ for (j = 0; j < i; j++)
+ kthread_destroy_worker(client[j].worker);
+ KUNIT_FAIL(test, "Failed to create worker!\n");
+ }
+
+ kthread_init_work(&client[i].work, drm_sched_client_work);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(client); i++)
+ kthread_queue_work(client[i].worker, &client[i].work);
+
+ /*
+ * The clients (workers) can be a mix of async (deep submission queue),
+ * sync (one job at a time), or something in between. Therefore it is
+ * difficult to display a single metric representing their progress.
+ *
+ * Each struct drm_sched_client_params describes the actual submission
+ * pattern which happens in the following steps:
+ * 1. Submit N jobs
+ * 2. Wait for last submitted job to finish
+ * 3. Sleep for U micro-seconds
+ * 4. Goto 1. for C cycles
+ *
+ * Where number of cycles is calculated to match the target client
+ * duration from the respective struct drm_sched_test_params.
+ *
+ * To asses scheduling behaviour what we output for both clients is:
+ * - pct: Percentage progress of the jobs submitted
+ * - cps: "Cycles" per second (where one cycle is one complete
+ * iteration from the above)
+ * - qd: Number of outstanding jobs in the client/entity
+ */
+
+ pr_info(" [pct] - Job sumission progress\n"
+" [cps] - Cycles per second\n"
+" [qd] - Number of outstanding jobs in the client/entity\n");
+ pr_info("%s:\n\t pct1 cps1 qd1; pct2 cps2 qd2\n",
+ params->description);
+ start = ktime_get();
+ while (!client_done(&client[0]) || !client_done(&client[1])) {
+ const unsigned int period_ms = 100;
+ const unsigned int frequency = 1000 / period_ms;
+ unsigned int pct[2], qd[2], cycle[2], cps[2];
+
+ for (i = 0; i < ARRAY_SIZE(client); i++) {
+ qd[i] = spsc_queue_count(&client[i].entity->base.job_queue);
+ cycle[i] = READ_ONCE(client[i].cycle);
+ cps[i] = DIV_ROUND_UP(100 * frequency *
+ (cycle[i] - prev_cycle[i]),
+ 100);
+ if (client[i].cycles)
+ pct[i] = DIV_ROUND_UP(100 * (1 + cycle[i]),
+ client[i].cycles);
+ else
+ pct[i] = 0;
+ prev_cycle[i] = cycle[i];
+ }
+
+ if (client_done(&client[0]))
+ pr_info("\t+%6lldms: ; %3u %5u %4u\n",
+ ktime_to_ms(ktime_sub(ktime_get(), start)),
+ pct[1], cps[1], qd[1]);
+ else if (client_done(&client[1]))
+ pr_info("\t+%6lldms: %3u %5u %4u;\n",
+ ktime_to_ms(ktime_sub(ktime_get(), start)),
+ pct[0], cps[0], qd[0]);
+ else
+ pr_info("\t+%6lldms: %3u %5u %4u; %3u %5u %4u\n",
+ ktime_to_ms(ktime_sub(ktime_get(), start)),
+ pct[0], cps[0], qd[0],
+ pct[1], cps[1], qd[1]);
+
+ msleep(period_ms);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(client); i++) {
+ kthread_flush_work(&client[i].work);
+ kthread_destroy_worker(client[i].worker);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(client); i++)
+ KUNIT_ASSERT_TRUE(test, client[i].done);
+
+ for (i = 0; i < ARRAY_SIZE(client); i++) {
+ pr_info(" %u: prio=%s sync=%u elapsed_ms=%lldms (ideal_ms=%lldms) cycle_time(min,avg,max)=%u,%u,%u us latency_time(min,avg,max)=%u,%u,%u us",
+ i,
+ prio_str(params->client[i].priority),
+ params->client[i].sync,
+ ktime_to_ms(ktime_sub(client[i].end, client[i].start)),
+ ktime_to_ms(client[i].ideal_duration),
+ client[i].cycle_time.min_us,
+ get_stats_avg(&client[i].cycle_time, client[i].cycles),
+ client[i].cycle_time.max_us,
+ client[i].latency_time.min_us,
+ get_stats_avg(&client[i].latency_time, client[i].cycles),
+ client[i].latency_time.max_us);
+ drm_mock_sched_entity_free(client[i].entity);
+ }
+}
+
+static const struct kunit_attributes drm_sched_scheduler_two_clients_attr = {
+ .speed = KUNIT_SPEED_SLOW,
+};
+
+static struct kunit_case drm_sched_scheduler_two_clients_tests[] = {
+ KUNIT_CASE_PARAM_ATTR(drm_sched_scheduler_two_clients_test,
+ drm_sched_scheduler_two_clients_gen_params,
+ drm_sched_scheduler_two_clients_attr),
+ {}
+};
+
+static struct kunit_suite drm_sched_scheduler_two_clients1 = {
+ .name = "drm_sched_scheduler_two_clients_one_credit_tests",
+ .init = drm_sched_scheduler_init,
+ .exit = drm_sched_scheduler_exit,
+ .test_cases = drm_sched_scheduler_two_clients_tests,
+};
+
+static struct kunit_suite drm_sched_scheduler_two_clients2 = {
+ .name = "drm_sched_scheduler_two_clients_two_credits_tests",
+ .init = drm_sched_scheduler_init2,
+ .exit = drm_sched_scheduler_exit,
+ .test_cases = drm_sched_scheduler_two_clients_tests,
+};
+
+static const struct drm_sched_test_params drm_sched_many_cases[] = {
+ {
+ .description = "2 clients",
+ .num_clients = 2,
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 4,
+ .job_us = 1000,
+ .wait_us = 0,
+ .sync = true,
+ },
+ },
+ {
+ .description = "3 clients",
+ .num_clients = 3,
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 4,
+ .job_us = 1000,
+ .wait_us = 0,
+ .sync = true,
+ },
+ },
+ {
+ .description = "7 clients",
+ .num_clients = 7,
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 4,
+ .job_us = 1000,
+ .wait_us = 0,
+ .sync = true,
+ },
+ },
+ {
+ .description = "13 clients",
+ .num_clients = 13,
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 4,
+ .job_us = 1000,
+ .wait_us = 0,
+ .sync = true,
+ },
+ },
+ {
+ .description = "31 clients",
+ .num_clients = 31,
+ .client[0] = {
+ .priority = DRM_SCHED_PRIORITY_NORMAL,
+ .job_cnt = 2,
+ .job_us = 1000,
+ .wait_us = 0,
+ .sync = true,
+ },
+ },
+};
+
+KUNIT_ARRAY_PARAM(drm_sched_scheduler_many_clients,
+ drm_sched_many_cases,
+ drm_sched_desc);
+
+static void drm_sched_scheduler_many_clients_test(struct kunit *test)
+{
+ const struct drm_sched_test_params *params = test->param_value;
+ struct drm_mock_scheduler *sched = test->priv;
+ const unsigned int clients = params->num_clients;
+ unsigned int i, j, delta_total = 0, loops = 0;
+ struct test_client *client;
+ unsigned int *prev_cycle;
+ ktime_t start;
+ char *buf;
+
+ /*
+ * Many clients with deep-ish async queues.
+ */
+
+ buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ client = kunit_kcalloc(test, clients, sizeof(*client), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, client);
+ prev_cycle = kunit_kcalloc(test, clients, sizeof(*prev_cycle),
+ GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, prev_cycle);
+
+ for (i = 0; i < clients; i++)
+ client[i].entity =
+ drm_mock_sched_entity_new(test,
+ DRM_SCHED_PRIORITY_NORMAL,
+ sched);
+
+ for (i = 0; i < clients; i++) {
+ client[i].test = test;
+ client[i].id = i;
+ client[i].params = params->client[0];
+ client[i].duration = ms_to_ktime(1000 / clients);
+ client[i].cycle_time.min_us = ~0U;
+ client[i].latency_time.min_us = ~0U;
+ client[i].worker =
+ kthread_create_worker(0, "%s-%u", __func__, i);
+ if (IS_ERR(client[i].worker)) {
+ for (j = 0; j < i; j++)
+ kthread_destroy_worker(client[j].worker);
+ KUNIT_FAIL(test, "Failed to create worker!\n");
+ }
+
+ kthread_init_work(&client[i].work, drm_sched_client_work);
+ }
+
+ for (i = 0; i < clients; i++)
+ kthread_queue_work(client[i].worker, &client[i].work);
+
+ start = ktime_get();
+ pr_info("%u clients:\n\tt\t\tcycle:\t min avg max : ...\n", clients);
+ for (;;) {
+ unsigned int min = ~0;
+ unsigned int max = 0;
+ unsigned int total = 0;
+ bool done = true;
+ char pbuf[16];
+
+ memset(buf, 0, PAGE_SIZE);
+ for (i = 0; i < clients; i++) {
+ unsigned int cycle, cycles;
+
+ /* Read current progress from the threaded worker. */
+ cycle = READ_ONCE(client[i].cycle);
+ cycles = READ_ONCE(client[i].cycles);
+
+ snprintf(pbuf, sizeof(pbuf), " %3d", cycle);
+ strncat(buf, pbuf, PAGE_SIZE);
+
+ total += cycle;
+ if (cycle < min)
+ min = cycle;
+ if (cycle > max)
+ max = cycle;
+
+ if (!min || (cycle + 1) < cycles)
+ done = false;
+ }
+
+ loops++;
+ delta_total += max - min;
+
+ pr_info("\t+%6lldms\t\t %3u %3u %3u :%s\n",
+ ktime_to_ms(ktime_sub(ktime_get(), start)),
+ min, DIV_ROUND_UP(total, clients), max, buf);
+
+ if (done)
+ break;
+
+ msleep(100);
+ }
+
+ pr_info(" avg_max_min_delta(x100)=%u\n",
+ loops ? DIV_ROUND_UP(delta_total * 100, loops) : 0);
+
+ for (i = 0; i < clients; i++) {
+ kthread_flush_work(&client[i].work);
+ kthread_destroy_worker(client[i].worker);
+ }
+
+ for (i = 0; i < clients; i++)
+ drm_mock_sched_entity_free(client[i].entity);
+}
+
+static const struct kunit_attributes drm_sched_scheduler_many_clients_attr = {
+ .speed = KUNIT_SPEED_SLOW,
+};
+
+static struct kunit_case drm_sched_scheduler_many_clients_tests[] = {
+ KUNIT_CASE_PARAM_ATTR(drm_sched_scheduler_many_clients_test,
+ drm_sched_scheduler_many_clients_gen_params,
+ drm_sched_scheduler_many_clients_attr),
+ {}
+};
+
+static struct kunit_suite drm_sched_scheduler_many_clients = {
+ .name = "drm_sched_scheduler_many_clients_tests",
+ .init = drm_sched_scheduler_init2,
+ .exit = drm_sched_scheduler_exit,
+ .test_cases = drm_sched_scheduler_many_clients_tests,
+};
+
+kunit_test_suites(&drm_sched_scheduler_overhead,
+ &drm_sched_scheduler_two_clients1,
+ &drm_sched_scheduler_two_clients2,
+ &drm_sched_scheduler_many_clients);
diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c
index 81a04f767770..db467bbcdb77 100644
--- a/drivers/gpu/drm/tidss/tidss_encoder.c
+++ b/drivers/gpu/drm/tidss/tidss_encoder.c
@@ -106,6 +106,8 @@ int tidss_encoder_create(struct tidss_device *tidss,
enc = &t_enc->encoder;
enc->possible_crtcs = possible_crtcs;
+ devm_drm_bridge_add(tidss->dev, &t_enc->bridge);
+
/* Attaching first bridge to the encoder */
ret = drm_bridge_attach(enc, &t_enc->bridge, NULL,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c
index 8bb93194e5ac..b4779c09a1bf 100644
--- a/drivers/gpu/drm/tidss/tidss_kms.c
+++ b/drivers/gpu/drm/tidss/tidss_kms.c
@@ -287,8 +287,6 @@ int tidss_modeset_init(struct tidss_device *tidss)
if (ret)
return ret;
- drm_mode_config_reset(ddev);
-
dev_dbg(tidss->dev, "%s done\n", __func__);
return 0;
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index 4d3b7059cd5b..e5fd1e74de91 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -926,7 +926,7 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)
drm_err(dev,
"%s(0x%08x): Sync lost flood detected, recovering",
__func__, stat);
- queue_work(system_wq,
+ queue_work(system_percpu_wq,
&tilcdc_crtc->recover_work);
tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
LCDC_SYNC_LOST);
diff --git a/drivers/gpu/drm/v3d/Makefile b/drivers/gpu/drm/v3d/Makefile
index b7d673f1153b..601b834e377e 100644
--- a/drivers/gpu/drm/v3d/Makefile
+++ b/drivers/gpu/drm/v3d/Makefile
@@ -10,6 +10,7 @@ v3d-y := \
v3d_irq.o \
v3d_mmu.o \
v3d_perfmon.o \
+ v3d_power.o \
v3d_trace_points.o \
v3d_sched.o \
v3d_sysfs.o \
diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c
index 89f24eec62a7..634cc796ba23 100644
--- a/drivers/gpu/drm/v3d/v3d_debugfs.c
+++ b/drivers/gpu/drm/v3d/v3d_debugfs.c
@@ -97,7 +97,11 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
struct drm_debugfs_entry *entry = m->private;
struct drm_device *dev = entry->dev;
struct v3d_dev *v3d = to_v3d_dev(dev);
- int i, core;
+ int i, core, ret;
+
+ ret = v3d_pm_runtime_get(v3d);
+ if (ret)
+ return ret;
for (i = 0; i < ARRAY_SIZE(v3d_hub_reg_defs); i++) {
const struct v3d_reg_def *def = &v3d_hub_reg_defs[i];
@@ -139,6 +143,8 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
}
}
+ v3d_pm_runtime_put(v3d);
+
return 0;
}
@@ -148,7 +154,11 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
struct drm_device *dev = entry->dev;
struct v3d_dev *v3d = to_v3d_dev(dev);
u32 ident0, ident1, ident2, ident3, cores;
- int core;
+ int core, ret;
+
+ ret = v3d_pm_runtime_get(v3d);
+ if (ret)
+ return ret;
ident0 = V3D_READ(V3D_HUB_IDENT0);
ident1 = V3D_READ(V3D_HUB_IDENT1);
@@ -207,6 +217,8 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused)
}
}
+ v3d_pm_runtime_put(v3d);
+
return 0;
}
@@ -234,6 +246,11 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
uint32_t cycles;
int core = 0;
int measure_ms = 1000;
+ int ret;
+
+ ret = v3d_pm_runtime_get(v3d);
+ if (ret)
+ return ret;
if (v3d->ver >= V3D_GEN_41) {
int cycle_count_reg = V3D_PCTR_CYCLE_COUNT(v3d->ver);
@@ -253,6 +270,8 @@ static int v3d_measure_clock(struct seq_file *m, void *unused)
msleep(measure_ms);
cycles = V3D_CORE_READ(core, V3D_PCTR_0_PCTR0);
+ v3d_pm_runtime_put(v3d);
+
seq_printf(m, "cycles: %d (%d.%d Mhz)\n",
cycles,
cycles / (measure_ms * 1000),
diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c
index 4b441afcb602..fc81dd1247e3 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.c
+++ b/drivers/gpu/drm/v3d/v3d_drv.c
@@ -59,6 +59,7 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
[DRM_V3D_PARAM_V3D_CORE0_IDENT1] = V3D_CTL_IDENT1,
[DRM_V3D_PARAM_V3D_CORE0_IDENT2] = V3D_CTL_IDENT2,
};
+ int ret;
if (args->pad != 0)
return -EINVAL;
@@ -75,12 +76,19 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data,
if (args->value != 0)
return -EINVAL;
+ ret = v3d_pm_runtime_get(v3d);
+ if (ret)
+ return ret;
+
if (args->param >= DRM_V3D_PARAM_V3D_CORE0_IDENT0 &&
args->param <= DRM_V3D_PARAM_V3D_CORE0_IDENT2) {
args->value = V3D_CORE_READ(0, offset);
} else {
args->value = V3D_READ(offset);
}
+
+ v3d_pm_runtime_put(v3d);
+
return 0;
}
@@ -290,36 +298,6 @@ static const struct of_device_id v3d_of_match[] = {
};
MODULE_DEVICE_TABLE(of, v3d_of_match);
-static void
-v3d_idle_sms(struct v3d_dev *v3d)
-{
- if (v3d->ver < V3D_GEN_71)
- return;
-
- V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF);
-
- if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
- V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) {
- drm_err(&v3d->drm, "Failed to power up SMS\n");
- }
-
- v3d_reset_sms(v3d);
-}
-
-static void
-v3d_power_off_sms(struct v3d_dev *v3d)
-{
- if (v3d->ver < V3D_GEN_71)
- return;
-
- V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF);
-
- if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
- V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) {
- drm_err(&v3d->drm, "Failed to power off SMS\n");
- }
-}
-
static int
map_regs(struct v3d_dev *v3d, void __iomem **regs, const char *name)
{
@@ -363,23 +341,66 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
return ret;
}
+ if (v3d->ver < V3D_GEN_41) {
+ ret = map_regs(v3d, &v3d->gca_regs, "gca");
+ if (ret)
+ return ret;
+ }
+
+ v3d->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(v3d->reset))
+ return dev_err_probe(dev, PTR_ERR(v3d->reset),
+ "Failed to get reset control\n");
+
+ if (!v3d->reset) {
+ ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
+ if (ret) {
+ dev_err(dev, "Failed to get bridge registers\n");
+ return ret;
+ }
+ }
+
v3d->clk = devm_clk_get_optional(dev, NULL);
if (IS_ERR(v3d->clk))
return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n");
- ret = clk_prepare_enable(v3d->clk);
- if (ret) {
- dev_err(&pdev->dev, "Couldn't enable the V3D clock\n");
+ ret = v3d_irq_init(v3d);
+ if (ret)
return ret;
+
+ v3d_perfmon_init(v3d);
+
+ v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr,
+ GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
+ if (!v3d->mmu_scratch) {
+ dev_err(dev, "Failed to allocate MMU scratch page\n");
+ return -ENOMEM;
}
- v3d_idle_sms(v3d);
+ ret = v3d_gem_init(drm);
+ if (ret)
+ goto dma_free;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto gem_destroy;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto gem_destroy;
+
+ /* If PM is disabled, we need to call v3d_power_resume() manually. */
+ if (!IS_ENABLED(CONFIG_PM)) {
+ ret = v3d_power_resume(dev);
+ if (ret)
+ goto gem_destroy;
+ }
mmu_debug = V3D_READ(V3D_MMU_DEBUG_INFO);
mask = DMA_BIT_MASK(30 + V3D_GET_FIELD(mmu_debug, V3D_MMU_PA_WIDTH));
ret = dma_set_mask_and_coherent(dev, mask);
if (ret)
- goto clk_disable;
+ goto runtime_pm_put;
dma_set_max_seg_size(&pdev->dev, UINT_MAX);
@@ -399,66 +420,30 @@ static int v3d_platform_drm_probe(struct platform_device *pdev)
ident3 = V3D_READ(V3D_HUB_IDENT3);
v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV);
- v3d_perfmon_init(v3d);
-
- v3d->reset = devm_reset_control_get_exclusive(dev, NULL);
- if (IS_ERR(v3d->reset)) {
- ret = PTR_ERR(v3d->reset);
-
- if (ret == -EPROBE_DEFER)
- goto clk_disable;
-
- v3d->reset = NULL;
- ret = map_regs(v3d, &v3d->bridge_regs, "bridge");
- if (ret) {
- dev_err(dev,
- "Failed to get reset control or bridge regs\n");
- goto clk_disable;
- }
- }
-
- if (v3d->ver < V3D_GEN_41) {
- ret = map_regs(v3d, &v3d->gca_regs, "gca");
- if (ret)
- goto clk_disable;
- }
-
- v3d->mmu_scratch = dma_alloc_wc(dev, 4096, &v3d->mmu_scratch_paddr,
- GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO);
- if (!v3d->mmu_scratch) {
- dev_err(dev, "Failed to allocate MMU scratch page\n");
- ret = -ENOMEM;
- goto clk_disable;
- }
-
- ret = v3d_gem_init(drm);
- if (ret)
- goto dma_free;
-
- ret = v3d_irq_init(v3d);
- if (ret)
- goto gem_destroy;
+ pm_runtime_set_autosuspend_delay(dev, 100);
+ pm_runtime_use_autosuspend(dev);
ret = drm_dev_register(drm, 0);
if (ret)
- goto irq_disable;
+ goto runtime_pm_put;
ret = v3d_sysfs_init(dev);
if (ret)
goto drm_unregister;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
drm_unregister:
drm_dev_unregister(drm);
-irq_disable:
- v3d_irq_disable(v3d);
+runtime_pm_put:
+ pm_runtime_put_sync_suspend(dev);
gem_destroy:
v3d_gem_destroy(drm);
dma_free:
dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr);
-clk_disable:
- clk_disable_unprepare(v3d->clk);
return ret;
}
@@ -472,22 +457,27 @@ static void v3d_platform_drm_remove(struct platform_device *pdev)
drm_dev_unregister(drm);
- v3d_gem_destroy(drm);
+ pm_runtime_suspend(dev);
- dma_free_wc(v3d->drm.dev, 4096, v3d->mmu_scratch,
- v3d->mmu_scratch_paddr);
+ /* If PM is disabled, we need to call v3d_power_suspend() manually. */
+ if (!IS_ENABLED(CONFIG_PM))
+ v3d_power_suspend(dev);
- v3d_power_off_sms(v3d);
+ v3d_gem_destroy(drm);
- clk_disable_unprepare(v3d->clk);
+ dma_free_wc(dev, 4096, v3d->mmu_scratch, v3d->mmu_scratch_paddr);
}
+static DEFINE_RUNTIME_DEV_PM_OPS(v3d_pm_ops, v3d_power_suspend,
+ v3d_power_resume, NULL);
+
static struct platform_driver v3d_platform_driver = {
.probe = v3d_platform_drm_probe,
.remove = v3d_platform_drm_remove,
.driver = {
.name = "v3d",
.of_match_table = v3d_of_match,
+ .pm = pm_ptr(&v3d_pm_ops),
},
};
diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h
index 6a3cad933439..4ebe175a8c6b 100644
--- a/drivers/gpu/drm/v3d/v3d_drv.h
+++ b/drivers/gpu/drm/v3d/v3d_drv.h
@@ -3,6 +3,7 @@
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
#include <linux/spinlock_types.h>
#include <linux/workqueue.h>
@@ -324,6 +325,8 @@ struct v3d_job {
/* Callback for the freeing of the job on refcount going to 0. */
void (*free)(struct kref *ref);
+
+ bool has_pm_ref;
};
struct v3d_bin_job {
@@ -565,6 +568,7 @@ struct dma_fence *v3d_fence_create(struct v3d_dev *v3d, enum v3d_queue q);
/* v3d_gem.c */
extern bool super_pages;
+void v3d_init_hw_state(struct v3d_dev *v3d);
int v3d_gem_init(struct drm_device *dev);
void v3d_gem_destroy(struct drm_device *dev);
void v3d_reset_sms(struct v3d_dev *v3d);
@@ -596,6 +600,20 @@ int v3d_mmu_set_page_table(struct v3d_dev *v3d);
void v3d_mmu_insert_ptes(struct v3d_bo *bo);
void v3d_mmu_remove_ptes(struct v3d_bo *bo);
+/* v3d_power.c */
+int v3d_power_suspend(struct device *dev);
+int v3d_power_resume(struct device *dev);
+
+static __always_inline int v3d_pm_runtime_get(struct v3d_dev *v3d)
+{
+ return pm_runtime_resume_and_get(v3d->drm.dev);
+}
+
+static __always_inline int v3d_pm_runtime_put(struct v3d_dev *v3d)
+{
+ return pm_runtime_put_autosuspend(v3d->drm.dev);
+}
+
/* v3d_sched.c */
void v3d_timestamp_query_info_free(struct v3d_timestamp_query_info *query_info,
unsigned int count);
diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c
index 75d9eccd7966..1ee3c038d5f6 100644
--- a/drivers/gpu/drm/v3d/v3d_gem.c
+++ b/drivers/gpu/drm/v3d/v3d_gem.c
@@ -36,13 +36,6 @@ v3d_init_core(struct v3d_dev *v3d, int core)
V3D_CORE_WRITE(core, V3D_CTL_L2TFLEND, ~0);
}
-/* Sets invariant state for the HW. */
-static void
-v3d_init_hw_state(struct v3d_dev *v3d)
-{
- v3d_init_core(v3d, 0);
-}
-
static void
v3d_idle_axi(struct v3d_dev *v3d, int core)
{
@@ -259,6 +252,13 @@ v3d_invalidate_caches(struct v3d_dev *v3d)
v3d_invalidate_slices(v3d, 0);
}
+/* Sets invariant state for the HW. */
+void
+v3d_init_hw_state(struct v3d_dev *v3d)
+{
+ v3d_init_core(v3d, 0);
+}
+
static void
v3d_huge_mnt_init(struct v3d_dev *v3d)
{
@@ -328,9 +328,6 @@ v3d_gem_init(struct drm_device *dev)
goto err_dma_alloc;
}
- v3d_init_hw_state(v3d);
- v3d_mmu_set_page_table(v3d);
-
v3d_huge_mnt_init(v3d);
ret = v3d_sched_init(v3d);
diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c
index c28e74ab5442..86efaef2722c 100644
--- a/drivers/gpu/drm/v3d/v3d_irq.c
+++ b/drivers/gpu/drm/v3d/v3d_irq.c
@@ -248,17 +248,10 @@ v3d_hub_irq(int irq, void *arg)
int
v3d_irq_init(struct v3d_dev *v3d)
{
- int irq, ret, core;
+ int irq, ret;
INIT_WORK(&v3d->overflow_mem_work, v3d_overflow_mem_work);
- /* Clear any pending interrupts someone might have left around
- * for us.
- */
- for (core = 0; core < v3d->cores; core++)
- V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
- V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
-
irq = platform_get_irq_optional(v3d_to_pdev(v3d), 1);
if (irq == -EPROBE_DEFER)
return irq;
@@ -296,7 +289,6 @@ v3d_irq_init(struct v3d_dev *v3d)
goto fail;
}
- v3d_irq_enable(v3d);
return 0;
fail:
@@ -310,6 +302,11 @@ v3d_irq_enable(struct v3d_dev *v3d)
{
int core;
+ /* Clear any pending interrupts someone might have left around for us. */
+ for (core = 0; core < v3d->cores; core++)
+ V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
+ V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
+
/* Enable our set of interrupts, masking out any others. */
for (core = 0; core < v3d->cores; core++) {
V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS(v3d->ver));
diff --git a/drivers/gpu/drm/v3d/v3d_mmu.c b/drivers/gpu/drm/v3d/v3d_mmu.c
index c513a393c031..630c64e51d2f 100644
--- a/drivers/gpu/drm/v3d/v3d_mmu.c
+++ b/drivers/gpu/drm/v3d/v3d_mmu.c
@@ -39,7 +39,11 @@ static bool v3d_mmu_is_aligned(u32 page, u32 page_address, size_t alignment)
int v3d_mmu_flush_all(struct v3d_dev *v3d)
{
- int ret;
+ int ret = 0;
+
+ /* Flush the PTs only if we're already awake */
+ if (!pm_runtime_get_if_active(v3d->drm.dev))
+ return 0;
V3D_WRITE(V3D_MMUC_CONTROL, V3D_MMUC_CONTROL_FLUSH |
V3D_MMUC_CONTROL_ENABLE);
@@ -48,7 +52,7 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d)
V3D_MMUC_CONTROL_FLUSHING), 100);
if (ret) {
dev_err(v3d->drm.dev, "MMUC flush wait idle failed\n");
- return ret;
+ goto pm_put;
}
V3D_WRITE(V3D_MMU_CTL, V3D_READ(V3D_MMU_CTL) |
@@ -59,6 +63,8 @@ int v3d_mmu_flush_all(struct v3d_dev *v3d)
if (ret)
dev_err(v3d->drm.dev, "MMU TLB clear wait idle failed\n");
+pm_put:
+ v3d_pm_runtime_put(v3d);
return ret;
}
diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c
index 8e0249580bba..02451fc09dbb 100644
--- a/drivers/gpu/drm/v3d/v3d_perfmon.c
+++ b/drivers/gpu/drm/v3d/v3d_perfmon.c
@@ -232,6 +232,9 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
return;
+ if (!pm_runtime_get_if_active(v3d->drm.dev))
+ return;
+
ncounters = perfmon->ncounters;
mask = GENMASK(ncounters - 1, 0);
@@ -257,6 +260,8 @@ void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
v3d->active_perfmon = perfmon;
+
+ v3d_pm_runtime_put(v3d);
}
void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
@@ -268,10 +273,11 @@ void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
return;
mutex_lock(&perfmon->lock);
- if (perfmon != v3d->active_perfmon) {
- mutex_unlock(&perfmon->lock);
- return;
- }
+ if (perfmon != v3d->active_perfmon)
+ goto out;
+
+ if (!pm_runtime_get_if_active(v3d->drm.dev))
+ goto out_clear;
if (capture)
for (i = 0; i < perfmon->ncounters; i++)
@@ -279,7 +285,11 @@ void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
+ v3d_pm_runtime_put(v3d);
+
+out_clear:
v3d->active_perfmon = NULL;
+out:
mutex_unlock(&perfmon->lock);
}
diff --git a/drivers/gpu/drm/v3d/v3d_power.c b/drivers/gpu/drm/v3d/v3d_power.c
new file mode 100644
index 000000000000..769e90032b04
--- /dev/null
+++ b/drivers/gpu/drm/v3d/v3d_power.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Raspberry Pi */
+
+#include <linux/clk.h>
+
+#include <drm/drm_print.h>
+
+#include "v3d_drv.h"
+#include "v3d_regs.h"
+
+static int
+v3d_resume_sms(struct v3d_dev *v3d)
+{
+ if (v3d->ver < V3D_GEN_71)
+ return 0;
+
+ V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_CLEAR_POWER_OFF);
+
+ if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
+ V3D_SMS_STATE) == V3D_SMS_IDLE), 100)) {
+ drm_err(&v3d->drm, "Failed to power up SMS\n");
+ return -ETIMEDOUT;
+ }
+
+ v3d_reset_sms(v3d);
+
+ return 0;
+}
+
+static int
+v3d_suspend_sms(struct v3d_dev *v3d)
+{
+ if (v3d->ver < V3D_GEN_71)
+ return 0;
+
+ V3D_SMS_WRITE(V3D_SMS_TEE_CS, V3D_SMS_POWER_OFF);
+
+ if (wait_for((V3D_GET_FIELD(V3D_SMS_READ(V3D_SMS_TEE_CS),
+ V3D_SMS_STATE) == V3D_SMS_POWER_OFF_STATE), 100)) {
+ drm_err(&v3d->drm, "Failed to power off SMS\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int v3d_power_suspend(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct v3d_dev *v3d = to_v3d_dev(drm);
+ int ret;
+
+ v3d_irq_disable(v3d);
+
+ ret = v3d_suspend_sms(v3d);
+ if (ret) {
+ v3d_irq_enable(v3d);
+ return ret;
+ }
+
+ clk_disable_unprepare(v3d->clk);
+
+ return 0;
+}
+
+int v3d_power_resume(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct v3d_dev *v3d = to_v3d_dev(drm);
+ int ret;
+
+ ret = clk_prepare_enable(v3d->clk);
+ if (ret)
+ return ret;
+
+ ret = v3d_resume_sms(v3d);
+ if (ret) {
+ clk_disable_unprepare(v3d->clk);
+ return ret;
+ }
+
+ v3d_init_hw_state(v3d);
+ v3d_mmu_set_page_table(v3d);
+ v3d_irq_enable(v3d);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c
index 1855ef5b3b5f..c01fa90def4c 100644
--- a/drivers/gpu/drm/v3d/v3d_sched.c
+++ b/drivers/gpu/drm/v3d/v3d_sched.c
@@ -838,7 +838,6 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op
enum v3d_queue queue, const char *name)
{
struct drm_sched_init_args args = {
- .num_rqs = DRM_SCHED_PRIORITY_COUNT,
.credit_limit = 1,
.timeout = msecs_to_jiffies(500),
.dev = v3d->drm.dev,
diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c
index ee4512db294b..d0f949a49dd2 100644
--- a/drivers/gpu/drm/v3d/v3d_submit.c
+++ b/drivers/gpu/drm/v3d/v3d_submit.c
@@ -106,6 +106,9 @@ v3d_job_free(struct kref *ref)
v3d_stats_put(job->client_stats);
v3d_stats_put(job->global_stats);
+ if (job->has_pm_ref)
+ v3d_pm_runtime_put(job->v3d);
+
kfree(job);
}
@@ -187,13 +190,13 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
if (copy_from_user(&in, handle++, sizeof(in))) {
ret = -EFAULT;
drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n");
- goto fail_deps;
+ goto fail_job_init;
}
ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in.handle, 0);
// TODO: Investigate why this was filtered out for the IOCTL.
if (ret && ret != -ENOENT)
- goto fail_deps;
+ goto fail_job_init;
}
}
} else {
@@ -201,7 +204,15 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
// TODO: Investigate why this was filtered out for the IOCTL.
if (ret && ret != -ENOENT)
- goto fail_deps;
+ goto fail_job_init;
+ }
+
+ /* CPU jobs don't require hardware resources */
+ if (queue != V3D_CPU) {
+ ret = v3d_pm_runtime_get(v3d);
+ if (ret)
+ goto fail_job_init;
+ job->has_pm_ref = true;
}
kref_init(&job->refcount);
@@ -211,7 +222,7 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv,
return 0;
-fail_deps:
+fail_job_init:
drm_sched_job_cleanup(&job->base);
return ret;
}
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 9377e58f9bc2..2161761b1f22 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -22,7 +22,6 @@
#include <drm/drm_print.h>
#include "vc4_drv.h"
-#include "uapi/drm/vc4_drm.h"
static const struct drm_gem_object_funcs vc4_gem_object_funcs;
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index a14ecb769461..616caf9d9915 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -41,8 +41,6 @@
#include <soc/bcm2835/raspberrypi-firmware.h>
-#include "uapi/drm/vc4_drm.h"
-
#include "vc4_drv.h"
#include "vc4_regs.h"
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index ab3c6d5d4eb4..e231c906709c 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -33,7 +33,6 @@
#include <drm/drm_print.h>
#include <drm/drm_syncobj.h>
-#include "uapi/drm/vc4_drm.h"
#include "vc4_drv.h"
#include "vc4_regs.h"
#include "vc4_trace.h"
diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index 63e88f90eef7..8e5141bb5075 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -47,7 +47,6 @@
#include <linux/platform_device.h>
-#include <drm/drm_drv.h>
#include <drm/drm_print.h>
#include "vc4_drv.h"
@@ -242,23 +241,6 @@ vc4_irq(int irq, void *arg)
return status;
}
-static void
-vc4_irq_prepare(struct drm_device *dev)
-{
- struct vc4_dev *vc4 = to_vc4_dev(dev);
-
- if (!vc4->v3d)
- return;
-
- init_waitqueue_head(&vc4->job_wait_queue);
- INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work);
-
- /* Clear any pending interrupts someone might have left around
- * for us.
- */
- V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
-}
-
void
vc4_irq_enable(struct drm_device *dev)
{
@@ -307,12 +289,22 @@ int vc4_irq_install(struct drm_device *dev, int irq)
if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
return -ENODEV;
+ if (!vc4->v3d)
+ return -ENODEV;
+
if (irq == IRQ_NOTCONNECTED)
return -ENOTCONN;
- vc4_irq_prepare(dev);
+ init_waitqueue_head(&vc4->job_wait_queue);
+ INIT_WORK(&vc4->overflow_mem_work, vc4_overflow_mem_work);
+
+ /* Clear any pending interrupts someone might have left around
+ * for us.
+ */
+ V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
- ret = request_irq(irq, vc4_irq, 0, dev->driver->name, dev);
+ ret = devm_request_irq(dev->dev, irq, vc4_irq, 0,
+ dev_name(dev->dev), dev);
if (ret)
return ret;
@@ -329,7 +321,6 @@ void vc4_irq_uninstall(struct drm_device *dev)
return;
vc4_irq_disable(dev);
- free_irq(vc4->irq, dev);
}
/** Reinitializes interrupt registers when a GPU reset is performed. */
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index 91d499fefba2..a81bad3519cc 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -26,8 +26,6 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
-#include "uapi/drm/vc4_drm.h"
-
#include "vc4_drv.h"
#include "vc4_regs.h"
diff --git a/drivers/gpu/drm/vc4/vc4_render_cl.c b/drivers/gpu/drm/vc4/vc4_render_cl.c
index edc471e71c0e..c264d21bc3fe 100644
--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
+++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
@@ -37,7 +37,6 @@
#include <drm/drm_print.h>
-#include "uapi/drm/vc4_drm.h"
#include "vc4_drv.h"
#include "vc4_packet.h"
diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c
index 545c4c3608f5..7f2fadfde7a8 100644
--- a/drivers/gpu/drm/vc4/vc4_validate.c
+++ b/drivers/gpu/drm/vc4/vc4_validate.c
@@ -45,7 +45,6 @@
#include <drm/drm_print.h>
-#include "uapi/drm/vc4_drm.h"
#include "vc4_drv.h"
#include "vc4_packet.h"
diff --git a/drivers/gpu/drm/xe/xe_dep_scheduler.c b/drivers/gpu/drm/xe/xe_dep_scheduler.c
index 51d99fee9aa5..004aac8b89e6 100644
--- a/drivers/gpu/drm/xe/xe_dep_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_dep_scheduler.c
@@ -78,7 +78,6 @@ xe_dep_scheduler_create(struct xe_device *xe,
const struct drm_sched_init_args args = {
.ops = &sched_ops,
.submit_wq = submit_wq,
- .num_rqs = 1,
.credit_limit = job_limit,
.timeout = MAX_SCHEDULE_TIMEOUT,
.name = name,
diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c
index 755a2bff5d7b..1f8d358e60fd 100644
--- a/drivers/gpu/drm/xe/xe_execlist.c
+++ b/drivers/gpu/drm/xe/xe_execlist.c
@@ -337,7 +337,6 @@ static int execlist_exec_queue_init(struct xe_exec_queue *q)
struct drm_gpu_scheduler *sched;
const struct drm_sched_init_args args = {
.ops = &drm_sched_ops,
- .num_rqs = 1,
.credit_limit = xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES,
.hang_limit = XE_SCHED_HANG_LIMIT,
.timeout = XE_SCHED_JOB_TIMEOUT,
diff --git a/drivers/gpu/drm/xe/xe_gpu_scheduler.c b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
index 9c8004d5dd91..67d8ce368486 100644
--- a/drivers/gpu/drm/xe/xe_gpu_scheduler.c
+++ b/drivers/gpu/drm/xe/xe_gpu_scheduler.c
@@ -66,7 +66,6 @@ int xe_sched_init(struct xe_gpu_scheduler *sched,
const struct drm_sched_init_args args = {
.ops = ops,
.submit_wq = submit_wq,
- .num_rqs = 1,
.credit_limit = hw_submission,
.hang_limit = hang_limit,
.timeout = timeout,
diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c
index 5933b2b6392b..b54587b77b2f 100644
--- a/drivers/gpu/drm/xe/xe_svm.c
+++ b/drivers/gpu/drm/xe/xe_svm.c
@@ -1055,7 +1055,6 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap,
struct xe_pagemap *xpagemap = container_of(dpagemap, typeof(*xpagemap), dpagemap);
struct drm_pagemap_migrate_details mdetails = {
.timeslice_ms = timeslice_ms,
- .source_peer_migrates = 1,
};
struct xe_vram_region *vr = xe_pagemap_to_vr(xpagemap);
struct dma_fence *pre_migrate_fence = NULL;
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index cf17646c1310..854af692229b 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -27,16 +27,13 @@ static inline bool is_rockchip(enum analogix_dp_devtype type)
struct analogix_dp_plat_data {
enum analogix_dp_devtype dev_type;
struct drm_panel *panel;
+ struct drm_bridge *next_bridge;
struct drm_encoder *encoder;
struct drm_connector *connector;
- bool skip_connector;
+ const struct component_ops *ops;
int (*power_on)(struct analogix_dp_plat_data *);
int (*power_off)(struct analogix_dp_plat_data *);
- int (*attach)(struct analogix_dp_plat_data *, struct drm_bridge *,
- struct drm_connector *);
- int (*get_modes)(struct analogix_dp_plat_data *,
- struct drm_connector *);
};
int analogix_dp_resume(struct analogix_dp_device *dp);
@@ -52,5 +49,6 @@ int analogix_dp_stop_crc(struct drm_connector *connector);
struct analogix_dp_plat_data *analogix_dp_aux_to_plat_data(struct drm_dp_aux *aux);
struct drm_dp_aux *analogix_dp_get_aux(struct analogix_dp_device *dp);
+int analogix_dp_finish_probe(struct analogix_dp_device *dp);
#endif /* _ANALOGIX_DP_H_ */
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 336f062e1f9d..8500dd4f99d8 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -126,6 +126,12 @@ struct dw_hdmi_phy_ops {
struct dw_hdmi_plat_data {
struct regmap *regm;
+ /*
+ * The HDMI output port number must be 1 if the port is described
+ * in the device tree. 0 if the device tree does not describe the
+ * next component (legacy mode, i.e. without
+ * DRM_BRIDGE_ATTACH_NO_CONNECTOR flag when attaching bridge).
+ */
unsigned int output_port;
unsigned long input_bus_encoding;
diff --git a/include/drm/bridge/imx.h b/include/drm/bridge/imx.h
deleted file mode 100644
index b93f719fe0e7..000000000000
--- a/include/drm/bridge/imx.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2012 Sascha Hauer, Pengutronix
- */
-
-#ifndef DRM_IMX_BRIDGE_H
-#define DRM_IMX_BRIDGE_H
-
-struct device;
-struct device_node;
-struct drm_bridge;
-
-struct drm_bridge *devm_imx_drm_legacy_bridge(struct device *dev,
- struct device_node *np,
- int type);
-
-#endif
diff --git a/include/drm/bridge/of-display-mode-bridge.h b/include/drm/bridge/of-display-mode-bridge.h
new file mode 100644
index 000000000000..89fcfedf68d8
--- /dev/null
+++ b/include/drm/bridge/of-display-mode-bridge.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ */
+
+#ifndef DRM_OF_DISPLAY_MODE_BRIDGE_H
+#define DRM_OF_DISPLAY_MODE_BRIDGE_H
+
+struct device;
+struct device_node;
+struct drm_bridge;
+
+struct drm_bridge *devm_drm_of_display_mode_bridge(struct device *dev,
+ struct device_node *np,
+ int type);
+
+#endif
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index a8d67bd9ee50..d6cd0f5af045 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1457,26 +1457,37 @@ drm_bridge_chain_get_last_bridge(struct drm_encoder *encoder)
struct drm_bridge, chain_node));
}
-/**
- * drm_bridge_get_next_bridge_and_put - Get the next bridge in the chain
- * and put the previous
- * @bridge: bridge object
- *
- * Same as drm_bridge_get_next_bridge() but additionally puts the @bridge.
- *
- * RETURNS:
- * the next bridge in the chain after @bridge, or NULL if @bridge is the last.
- */
-static inline struct drm_bridge *
-drm_bridge_get_next_bridge_and_put(struct drm_bridge *bridge)
+/* Internal to drm_for_each_bridge_in_chain*() */
+static inline struct drm_bridge *__drm_for_each_bridge_in_chain_next(struct drm_bridge *bridge)
{
struct drm_bridge *next = drm_bridge_get_next_bridge(bridge);
+ if (!next)
+ mutex_unlock(&bridge->encoder->bridge_chain_mutex);
+
drm_bridge_put(bridge);
return next;
}
+/* Internal to drm_for_each_bridge_in_chain*() */
+DEFINE_FREE(__drm_for_each_bridge_in_chain_cleanup, struct drm_bridge *,
+ if (_T) { mutex_unlock(&_T->encoder->bridge_chain_mutex); drm_bridge_put(_T); })
+
+/* Internal to drm_for_each_bridge_in_chain_scoped() */
+static inline struct drm_bridge *
+__drm_for_each_bridge_in_chain_scoped_start(struct drm_encoder *encoder)
+{
+ mutex_lock(&encoder->bridge_chain_mutex);
+
+ struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder);
+
+ if (!bridge)
+ mutex_unlock(&encoder->bridge_chain_mutex);
+
+ return bridge;
+}
+
/**
* drm_for_each_bridge_in_chain_scoped - iterate over all bridges attached
* to an encoder
@@ -1486,14 +1497,24 @@ drm_bridge_get_next_bridge_and_put(struct drm_bridge *bridge)
*
* Iterate over all bridges present in the bridge chain attached to @encoder.
*
- * Automatically gets/puts the bridge reference while iterating, and puts
- * the reference even if returning or breaking in the middle of the loop.
+ * Automatically gets/puts the bridge reference while iterating and locks
+ * the encoder chain mutex to prevent chain modifications while iterating.
*/
-#define drm_for_each_bridge_in_chain_scoped(encoder, bridge) \
- for (struct drm_bridge *bridge __free(drm_bridge_put) = \
- drm_bridge_chain_get_first_bridge(encoder); \
- bridge; \
- bridge = drm_bridge_get_next_bridge_and_put(bridge))
+#define drm_for_each_bridge_in_chain_scoped(encoder, bridge) \
+ for (struct drm_bridge *bridge __free(__drm_for_each_bridge_in_chain_cleanup) = \
+ __drm_for_each_bridge_in_chain_scoped_start((encoder)); \
+ bridge; \
+ bridge = __drm_for_each_bridge_in_chain_next(bridge)) \
+
+/* Internal to drm_for_each_bridge_in_chain_from() */
+static inline struct drm_bridge *
+__drm_for_each_bridge_in_chain_from_start(struct drm_bridge *bridge)
+{
+ drm_bridge_get(bridge);
+ mutex_lock(&bridge->encoder->bridge_chain_mutex);
+
+ return bridge;
+}
/**
* drm_for_each_bridge_in_chain_from - iterate over all bridges starting
@@ -1505,14 +1526,14 @@ drm_bridge_get_next_bridge_and_put(struct drm_bridge *bridge)
* Iterate over all bridges in the encoder chain starting from
* @first_bridge, included.
*
- * Automatically gets/puts the bridge reference while iterating, and puts
- * the reference even if returning or breaking in the middle of the loop.
+ * Automatically gets/puts the bridge reference while iterating and locks
+ * the encoder chain mutex to prevent chain modifications while iterating.
*/
-#define drm_for_each_bridge_in_chain_from(first_bridge, bridge) \
- for (struct drm_bridge *bridge __free(drm_bridge_put) = \
- drm_bridge_get(first_bridge); \
- bridge; \
- bridge = drm_bridge_get_next_bridge_and_put(bridge))
+#define drm_for_each_bridge_in_chain_from(first_bridge, bridge) \
+ for (struct drm_bridge *bridge __free(__drm_for_each_bridge_in_chain_cleanup) = \
+ __drm_for_each_bridge_in_chain_from_start(first_bridge); \
+ bridge; \
+ bridge = __drm_for_each_bridge_in_chain_next(bridge)) \
enum drm_mode_status
drm_bridge_chain_mode_valid(struct drm_bridge *bridge,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f83f28cae207..3e422a4f2e72 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -2556,7 +2556,7 @@ int drm_connector_attach_vrr_capable_property(
void drm_connector_attach_panel_type_property(struct drm_connector *connector);
int drm_connector_attach_broadcast_rgb_property(struct drm_connector *connector);
int drm_connector_attach_colorspace_property(struct drm_connector *connector);
-int drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector);
+void drm_connector_attach_hdr_output_metadata_property(struct drm_connector *connector);
bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 04f7a7f1f108..272506331634 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -463,6 +463,9 @@ struct drm_display_mode *
drm_display_mode_from_cea_vic(struct drm_device *dev,
u8 video_code);
+struct drm_display_mode *drm_ovt_mode(struct drm_device *dev, int rid,
+ int vrefresh);
+
/* Interface based on struct drm_edid */
const struct drm_edid *drm_edid_alloc(const void *edid, size_t size);
const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid);
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index 977a9381c8ba..eded7c34481a 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -25,6 +25,7 @@
#include <linux/list.h>
#include <linux/ctype.h>
+#include <linux/mutex.h>
#include <drm/drm_crtc.h>
#include <drm/drm_mode.h>
#include <drm/drm_mode_object.h>
@@ -189,6 +190,9 @@ struct drm_encoder {
*/
struct list_head bridge_chain;
+ /** @bridge_chain_mutex: protect bridge_chain from changes while iterating */
+ struct mutex bridge_chain_mutex;
+
const struct drm_encoder_funcs *funcs;
const struct drm_encoder_helper_funcs *helper_private;
diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h
index 2578ac92a8d4..cd94bb2ee6ee 100644
--- a/include/drm/drm_gpusvm.h
+++ b/include/drm/drm_gpusvm.h
@@ -6,6 +6,7 @@
#ifndef __DRM_GPUSVM_H__
#define __DRM_GPUSVM_H__
+#include <linux/dma-mapping.h>
#include <linux/kref.h>
#include <linux/interval_tree.h>
#include <linux/mmu_notifier.h>
@@ -136,6 +137,8 @@ struct drm_gpusvm_pages_flags {
* @dma_addr: Device address array
* @dpagemap: The struct drm_pagemap of the device pages we're dma-mapping.
* Note this is assuming only one drm_pagemap per range is allowed.
+ * @state: DMA IOVA state for mapping.
+ * @state_offset: DMA IOVA offset for mapping.
* @notifier_seq: Notifier sequence number of the range's pages
* @flags: Flags for range
* @flags.migrate_devmem: Flag indicating whether the range can be migrated to device memory
@@ -147,6 +150,8 @@ struct drm_gpusvm_pages_flags {
struct drm_gpusvm_pages {
struct drm_pagemap_addr *dma_addr;
struct drm_pagemap *dpagemap;
+ struct dma_iova_state state;
+ unsigned long state_offset;
unsigned long notifier_seq;
struct drm_gpusvm_pages_flags flags;
};
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index 2ab651a36115..b429acde4f71 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -393,6 +393,7 @@ void mipi_dsi_dcs_set_page_address_multi(struct mipi_dsi_multi_context *ctx,
void mipi_dsi_dcs_set_tear_scanline_multi(struct mipi_dsi_multi_context *ctx,
u16 scanline);
void mipi_dsi_dcs_set_tear_off_multi(struct mipi_dsi_multi_context *ctx);
+void mipi_dsi_shutdown_peripheral_multi(struct mipi_dsi_multi_context *ctx);
/**
* mipi_dsi_generic_write_seq_multi - transmit data using a generic write packet
diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h
index f2f2bf82eff9..7bcc0ccfe0f4 100644
--- a/include/drm/drm_of.h
+++ b/include/drm/drm_of.h
@@ -62,6 +62,10 @@ int drm_of_get_data_lanes_count_ep(const struct device_node *port,
int port_reg, int reg,
const unsigned int min,
const unsigned int max);
+int drm_of_get_data_lanes_count_remote(const struct device_node *port,
+ int port_reg, int reg,
+ const unsigned int min,
+ const unsigned int max);
#else
static inline uint32_t drm_of_crtc_port_mask(struct drm_device *dev,
struct device_node *port)
@@ -140,6 +144,15 @@ drm_of_get_data_lanes_count_ep(const struct device_node *port,
{
return -EINVAL;
}
+
+static inline int
+drm_of_get_data_lanes_count_remote(const struct device_node *port,
+ int port_reg, int reg,
+ const unsigned int min,
+ const unsigned int max)
+{
+ return -EINVAL;
+}
#endif
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_MIPI_DSI)
diff --git a/include/drm/drm_pagemap.h b/include/drm/drm_pagemap.h
index 75e6ca58922d..95eb4b66b057 100644
--- a/include/drm/drm_pagemap.h
+++ b/include/drm/drm_pagemap.h
@@ -329,17 +329,12 @@ struct drm_pagemap_devmem {
* struct drm_pagemap_migrate_details - Details to govern migration.
* @timeslice_ms: The time requested for the migrated pagemap pages to
* be present in @mm before being allowed to be migrated back.
- * @can_migrate_same_pagemap: Whether the copy function as indicated by
- * the @source_peer_migrates flag, can migrate device pages within a
- * single drm_pagemap.
- * @source_peer_migrates: Whether on p2p migration, The source drm_pagemap
- * should use the copy_to_ram() callback rather than the destination
- * drm_pagemap should use the copy_to_devmem() callback.
+ * @can_migrate_same_pagemap: Whether the copy function can migrate
+ * device pages within a single drm_pagemap.
*/
struct drm_pagemap_migrate_details {
unsigned long timeslice_ms;
u32 can_migrate_same_pagemap : 1;
- u32 source_peer_migrates : 1;
};
#if IS_ENABLED(CONFIG_ZONE_DEVICE)
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 2407bfa60236..1fb9148dd095 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -329,6 +329,7 @@ void drm_panel_put(struct drm_panel *panel);
void drm_panel_add(struct drm_panel *panel);
void drm_panel_remove(struct drm_panel *panel);
+int devm_drm_panel_add(struct device *dev, struct drm_panel *panel);
void drm_panel_prepare(struct drm_panel *panel);
void drm_panel_unprepare(struct drm_panel *panel);
diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h
index 78e07c2507c7..d61c19e78182 100644
--- a/include/drm/gpu_scheduler.h
+++ b/include/drm/gpu_scheduler.h
@@ -25,11 +25,14 @@
#define _DRM_GPU_SCHEDULER_H_
#include <drm/spsc_queue.h>
+#include <linux/average.h>
#include <linux/dma-fence.h>
#include <linux/completion.h>
#include <linux/xarray.h>
#include <linux/workqueue.h>
+DECLARE_EWMA(drm_sched_avgtime, 6, 4);
+
#define MAX_WAIT_SCHED_ENTITY_Q_EMPTY msecs_to_jiffies(1000)
/**
@@ -63,6 +66,7 @@ struct drm_file;
* to an array, and as such should start at 0.
*/
enum drm_sched_priority {
+ DRM_SCHED_PRIORITY_INVALID = -1, /* Internal marker - do not use. */
DRM_SCHED_PRIORITY_KERNEL,
DRM_SCHED_PRIORITY_HIGH,
DRM_SCHED_PRIORITY_NORMAL,
@@ -71,6 +75,8 @@ enum drm_sched_priority {
DRM_SCHED_PRIORITY_COUNT
};
+struct drm_sched_entity_stats;
+
/**
* struct drm_sched_entity - A wrapper around a job queue (typically
* attached to the DRM file_priv).
@@ -110,6 +116,11 @@ struct drm_sched_entity {
struct drm_sched_rq *rq;
/**
+ * @stats: Stats object reference held by the entity and jobs.
+ */
+ struct drm_sched_entity_stats *stats;
+
+ /**
* @sched_list:
*
* A list of schedulers (struct drm_gpu_scheduler). Jobs from this entity can
@@ -238,24 +249,21 @@ struct drm_sched_entity {
/**
* struct drm_sched_rq - queue of entities to be scheduled.
*
- * @sched: the scheduler to which this rq belongs to.
- * @lock: protects @entities, @rb_tree_root and @current_entity.
- * @current_entity: the entity which is to be scheduled.
+ * @lock: protects @entities, @rb_tree_root and @head_prio.
* @entities: list of the entities to be scheduled.
* @rb_tree_root: root of time based priority queue of entities for FIFO scheduling
+ * @head_prio: priority of the top tree element.
*
* Run queue is a set of entities scheduling command submissions for
* one specific ring. It implements the scheduling policy that selects
* the next entity to emit commands from.
*/
struct drm_sched_rq {
- struct drm_gpu_scheduler *sched;
-
spinlock_t lock;
/* Following members are protected by the @lock: */
- struct drm_sched_entity *current_entity;
struct list_head entities;
struct rb_root_cached rb_tree_root;
+ enum drm_sched_priority head_prio;
};
/**
@@ -339,13 +347,6 @@ struct drm_sched_fence *to_drm_sched_fence(struct dma_fence *f);
*/
struct drm_sched_job {
/**
- * @submit_ts:
- *
- * When the job was pushed into the entity queue.
- */
- ktime_t submit_ts;
-
- /**
* @sched:
*
* The scheduler this job is or will be scheduled on. Gets set by
@@ -357,6 +358,11 @@ struct drm_sched_job {
struct drm_sched_fence *s_fence;
struct drm_sched_entity *entity;
+ /**
+ * @entity_stats: Stats object reference held by the job and entity.
+ */
+ struct drm_sched_entity_stats *entity_stats;
+
enum drm_sched_priority s_priority;
u32 credits;
/** @last_dependency: tracks @dependencies as they signal */
@@ -543,15 +549,14 @@ struct drm_sched_backend_ops {
* @credit_count: the current credit count of this scheduler
* @timeout: the time after which a job is removed from the scheduler.
* @name: name of the ring for which this scheduler is being used.
- * @num_rqs: Number of run-queues. This is at most DRM_SCHED_PRIORITY_COUNT,
- * as there's usually one run-queue per priority, but could be less.
- * @sched_rq: An allocated array of run-queues of size @num_rqs;
+ * @rq: Scheduler run queue.
* @job_scheduled: once drm_sched_entity_flush() is called the scheduler
* waits on this wait queue until all the scheduled jobs are
* finished.
* @job_id_count: used to assign unique id to the each job.
* @submit_wq: workqueue used to queue @work_run_job and @work_free_job
* @timeout_wq: workqueue used to queue @work_tdr
+ * @avg_job_us: Average job duration.
* @work_run_job: work which calls run_job op of each scheduler.
* @work_free_job: work which calls free_job op of each scheduler.
* @work_tdr: schedules a delayed call to @drm_sched_job_timedout after the
@@ -576,12 +581,12 @@ struct drm_gpu_scheduler {
atomic_t credit_count;
long timeout;
const char *name;
- u32 num_rqs;
- struct drm_sched_rq **sched_rq;
+ struct drm_sched_rq rq;
wait_queue_head_t job_scheduled;
atomic64_t job_id_count;
struct workqueue_struct *submit_wq;
struct workqueue_struct *timeout_wq;
+ struct ewma_drm_sched_avgtime avg_job_us;
struct work_struct work_run_job;
struct work_struct work_free_job;
struct delayed_work work_tdr;
@@ -603,8 +608,6 @@ struct drm_gpu_scheduler {
* @ops: backend operations provided by the driver
* @submit_wq: workqueue to use for submission. If NULL, an ordered wq is
* allocated and used.
- * @num_rqs: Number of run-queues. This may be at most DRM_SCHED_PRIORITY_COUNT,
- * as there's usually one run-queue per priority, but may be less.
* @credit_limit: the number of credits this scheduler can hold from all jobs
* @hang_limit: number of times to allow a job to hang before dropping it.
* This mechanism is DEPRECATED. Set it to 0.
@@ -618,7 +621,6 @@ struct drm_sched_init_args {
const struct drm_sched_backend_ops *ops;
struct workqueue_struct *submit_wq;
struct workqueue_struct *timeout_wq;
- u32 num_rqs;
u32 credit_limit;
unsigned int hang_limit;
long timeout;
@@ -691,6 +693,7 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
unsigned int num_sched_list,
atomic_t *guilty);
long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout);
+void drm_sched_entity_kill(struct drm_sched_entity *entity);
void drm_sched_entity_fini(struct drm_sched_entity *entity);
void drm_sched_entity_destroy(struct drm_sched_entity *entity);
void drm_sched_entity_set_priority(struct drm_sched_entity *entity,
diff --git a/include/trace/events/amdxdna.h b/include/trace/events/amdxdna.h
index c6cb2da7b706..71da24267e52 100644
--- a/include/trace/events/amdxdna.h
+++ b/include/trace/events/amdxdna.h
@@ -30,26 +30,30 @@ TRACE_EVENT(amdxdna_debug_point,
);
TRACE_EVENT(xdna_job,
- TP_PROTO(struct drm_sched_job *sched_job, const char *name, const char *str, u64 seq),
+ TP_PROTO(struct drm_sched_job *sched_job, const char *name,
+ const char *str, u64 seq, u32 op),
- TP_ARGS(sched_job, name, str, seq),
+ TP_ARGS(sched_job, name, str, seq, op),
TP_STRUCT__entry(__string(name, name)
__string(str, str)
__field(u64, fence_context)
__field(u64, fence_seqno)
- __field(u64, seq)),
+ __field(u64, seq)
+ __field(u32, op)),
TP_fast_assign(__assign_str(name);
__assign_str(str);
__entry->fence_context = sched_job->s_fence->finished.context;
__entry->fence_seqno = sched_job->s_fence->finished.seqno;
- __entry->seq = seq;),
+ __entry->seq = seq;
+ __entry->op = op;),
- TP_printk("fence=(context:%llu, seqno:%lld), %s seq#:%lld %s",
+ TP_printk("fence=(context:%llu, seqno:%llu), %s seq#:%llu %s, op=%u",
__entry->fence_context, __entry->fence_seqno,
__get_str(name), __entry->seq,
- __get_str(str))
+ __get_str(str),
+ __entry->op)
);
DECLARE_EVENT_CLASS(xdna_mbox_msg,
@@ -81,18 +85,28 @@ DEFINE_EVENT(xdna_mbox_msg, mbox_set_head,
TP_ARGS(name, chann_id, opcode, id)
);
-TRACE_EVENT(mbox_irq_handle,
- TP_PROTO(char *name, int irq),
+DECLARE_EVENT_CLASS(xdna_mbox_name_id,
+ TP_PROTO(char *name, int irq),
- TP_ARGS(name, irq),
+ TP_ARGS(name, irq),
- TP_STRUCT__entry(__string(name, name)
- __field(int, irq)),
+ TP_STRUCT__entry(__string(name, name)
+ __field(int, irq)),
- TP_fast_assign(__assign_str(name);
- __entry->irq = irq;),
+ TP_fast_assign(__assign_str(name);
+ __entry->irq = irq;),
+
+ TP_printk("%s.%d", __get_str(name), __entry->irq)
+);
+
+DEFINE_EVENT(xdna_mbox_name_id, mbox_irq_handle,
+ TP_PROTO(char *name, int irq),
+ TP_ARGS(name, irq)
+);
- TP_printk("%s.%d", __get_str(name), __entry->irq)
+DEFINE_EVENT(xdna_mbox_name_id, mbox_rx_worker,
+ TP_PROTO(char *name, int irq),
+ TP_ARGS(name, irq)
);
#endif /* !defined(_TRACE_AMDXDNA_H) || defined(TRACE_HEADER_MULTI_READ) */
diff --git a/include/trace/events/dma_fence.h b/include/trace/events/dma_fence.h
index 3abba45c0601..5b10a9e06fb4 100644
--- a/include/trace/events/dma_fence.h
+++ b/include/trace/events/dma_fence.h
@@ -9,12 +9,40 @@
struct dma_fence;
+DECLARE_EVENT_CLASS(dma_fence,
+
+ TP_PROTO(struct dma_fence *fence),
+
+ TP_ARGS(fence),
+
+ TP_STRUCT__entry(
+ __string(driver, dma_fence_driver_name(fence))
+ __string(timeline, dma_fence_timeline_name(fence))
+ __field(unsigned int, context)
+ __field(unsigned int, seqno)
+ ),
+
+ TP_fast_assign(
+ __assign_str(driver);
+ __assign_str(timeline);
+ __entry->context = fence->context;
+ __entry->seqno = fence->seqno;
+ ),
+
+ TP_printk("driver=%s timeline=%s context=%u seqno=%u",
+ __get_str(driver), __get_str(timeline), __entry->context,
+ __entry->seqno)
+);
+
/*
* Safe only for call sites which are guaranteed to not race with fence
- * signaling,holding the fence->lock and having checked for not signaled, or the
- * signaling path itself.
+ * signaling, holding the fence->lock and having checked for not signaled, or
+ * the signaling path itself.
+ *
+ * TODO: Remove the need for this event class when drivers switch to independent
+ * fences.
*/
-DECLARE_EVENT_CLASS(dma_fence,
+DECLARE_EVENT_CLASS(dma_fence_ops,
TP_PROTO(struct dma_fence *fence),
@@ -46,7 +74,7 @@ DEFINE_EVENT(dma_fence, dma_fence_emit,
TP_ARGS(fence)
);
-DEFINE_EVENT(dma_fence, dma_fence_init,
+DEFINE_EVENT(dma_fence_ops, dma_fence_init,
TP_PROTO(struct dma_fence *fence),
@@ -60,14 +88,14 @@ DEFINE_EVENT(dma_fence, dma_fence_destroy,
TP_ARGS(fence)
);
-DEFINE_EVENT(dma_fence, dma_fence_enable_signal,
+DEFINE_EVENT(dma_fence_ops, dma_fence_enable_signal,
TP_PROTO(struct dma_fence *fence),
TP_ARGS(fence)
);
-DEFINE_EVENT(dma_fence, dma_fence_signaled,
+DEFINE_EVENT(dma_fence_ops, dma_fence_signaled,
TP_PROTO(struct dma_fence *fence),
diff --git a/include/uapi/drm/amdxdna_accel.h b/include/uapi/drm/amdxdna_accel.h
index 61d3686fa3b1..0b11e8e3ea5d 100644
--- a/include/uapi/drm/amdxdna_accel.h
+++ b/include/uapi/drm/amdxdna_accel.h
@@ -29,7 +29,8 @@ extern "C" {
enum amdxdna_device_type {
AMDXDNA_DEV_TYPE_UNKNOWN = -1,
- AMDXDNA_DEV_TYPE_KMQ,
+ AMDXDNA_DEV_TYPE_KMQ = 0,
+ AMDXDNA_DEV_TYPE_PF = 2,
};
enum amdxdna_drm_ioctl_id {
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 27cc159c1d27..495462e44a17 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -1323,6 +1323,13 @@ extern "C" {
*/
#define DRM_IOCTL_MODE_GETFB2 DRM_IOWR(0xCE, struct drm_mode_fb_cmd2)
+/**
+ * DRM_IOCTL_SYNCOBJ_EVENTFD - Register an eventfd to be signalled by a syncobj.
+ *
+ * This can be used to integrate a syncobj in an event loop.
+ *
+ * The IOCTL argument is a struct drm_syncobj_eventfd.
+ */
#define DRM_IOCTL_SYNCOBJ_EVENTFD DRM_IOWR(0xCF, struct drm_syncobj_eventfd)
/**