diff options
| author | Thierry Reding <treding@nvidia.com> | 2026-04-29 12:30:00 +0200 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2026-04-29 12:30:00 +0200 |
| commit | e48ef8c3e17e4bacdb8a6206fa6d3b33eda74644 (patch) | |
| tree | f1f08c2518f527d68a7e67f49fe041fb21737d6e | |
| parent | f3dc3b95346e4648645c2183fcf5a9abd9dfa244 (diff) | |
| parent | c82a16b0caac5799333bd3dc63c12ab4c6ee13b4 (diff) | |
| download | linux-next-e48ef8c3e17e4bacdb8a6206fa6d3b33eda74644.tar.gz linux-next-e48ef8c3e17e4bacdb8a6206fa6d3b33eda74644.zip | |
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/misc/kernel.git
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 = ðosu_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, ¶m); } - 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) /** |
