diff options
106 files changed, 3702 insertions, 1617 deletions
diff --git a/Documentation/accel/amdxdna/amdnpu.rst b/Documentation/accel/amdxdna/amdnpu.rst index 064973bf4893..d72af1b51ccf 100644 --- a/Documentation/accel/amdxdna/amdnpu.rst +++ b/Documentation/accel/amdxdna/amdnpu.rst @@ -289,6 +289,7 @@ Example of the output showing the implemented key value pairs: drm-driver: amdxdna_accel_driver drm-client-id: 3219 drm-pdev: 0000:c5:00.1 + drm-engine-npu: 14003870 ns amdxdna_accel_driver-heap-alloc: 60 KiB amdxdna_accel_driver-internal-alloc: 67588 KiB amdxdna_accel_driver-external-alloc: 0 diff --git a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml index 7636c24906ba..9e0e7e09774e 100644 --- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml +++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml @@ -29,6 +29,7 @@ properties: - adi,adv7123 - algoltek,ag6311 - asl-tek,cs5263 + - chrontel,ch7218a - dumb-vga-dac - mstar,tsumu88adt3-lf-1 - parade,ps185hdm diff --git a/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml b/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml new file mode 100644 index 000000000000..7754a8cb1e5c --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/novatek,nt51021.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Novatek NT51021 based DSI display Panels + +maintainers: + - Nickolay Goppen <setotau@mainlining.org> + +description: + The NT51021 IC from Novatek is a generic DSI Panel IC used to drive dsi + panels. + Right now, support is added only for a BOE WUXGA LCD display panels with a + resolution of 1200x1920. They are video mode DSI panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - boe,tv101wum-nm0 + - boe,tv101wum-nx0 + - boe,tv080wum-nx2 + - const: novatek,nt51021 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: phandle of gpio for reset line - This should be 8mA, gpio + can be configured using mux, pinctrl, pinctrl-names (active low) + + vdd-supply: + description: phandle of the regulator that provides the supply voltage + for digital circuits + + avdd-supply: + description: phandle of the regulator that provides the supply voltage + for analog circuits + + havdd-supply: + description: phandle of the regulator that provides the supply voltage + for analog circuits + +required: + - compatible + - reg + - vdd-supply + - avdd-supply + - havdd-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "boe,tv101wum-nx0", "novatek,nt51021"; + reg = <0>; + vdd-supply = <&lcd_pwr>; + avdd-supply = <&lcd_pwr>; + havdd-supply = <&lcd_pwr>; + + backlight = <&i2c_backlight>; + reset-gpios = <&tlmm 62 GPIO_ACTIVE_LOW>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml b/Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml new file mode 100644 index 000000000000..3c94db88f797 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/raydium,rm69220.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Raydium RM69220 based MIPI-DSI panels + +maintainers: + - Alexander Koskovich <akoskovich@pm.me> + +description: + The Raydium RM69220 is a generic DSI Panel IC used to control + AMOLED panels. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - boe,bf068mwm-td0 + - const: raydium,rm69220 + + reg: + maxItems: 1 + + dvdd-supply: + description: Digital voltage rail + + vci-supply: + description: Analog voltage rail + + vddio-supply: + description: I/O voltage rail + +required: + - compatible + - reg + - dvdd-supply + - vci-supply + - vddio-supply + - reset-gpios + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "boe,bf068mwm-td0", "raydium,rm69220"; + reg = <0>; + + reset-gpios = <&tlmm 12 GPIO_ACTIVE_LOW>; + dvdd-supply = <&vreg_oled_dvdd_1p8>; + vci-supply = <&vreg_l19b>; + vddio-supply = <&vreg_l9b>; + + port { + panel_in_0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml b/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml index c99f4146f1bb..682f30dc9ee4 100644 --- a/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml +++ b/Documentation/devicetree/bindings/display/panel/visionox,vtdr6130.yaml @@ -14,7 +14,11 @@ allOf: properties: compatible: - const: visionox,vtdr6130 + oneOf: + - const: visionox,vtdr6130 + - items: + - const: retroidpocket,rp6-panel + - const: visionox,vtdr6130 reg: maxItems: 1 @@ -25,13 +29,13 @@ properties: vdd-supply: true port: true reset-gpios: true + rotation: true required: - compatible - reg - vddio-supply - vci-supply - - vdd-supply - reset-gpios - port diff --git a/Documentation/devicetree/bindings/display/ste,mcde.yaml b/Documentation/devicetree/bindings/display/ste,mcde.yaml index 7a12d0b817e6..05e7e4ebc882 100644 --- a/Documentation/devicetree/bindings/display/ste,mcde.yaml +++ b/Documentation/devicetree/bindings/display/ste,mcde.yaml @@ -35,6 +35,9 @@ properties: resets: maxItems: 1 + power-domains: + maxItems: 1 + epod-supply: description: a phandle to the EPOD regulator @@ -109,6 +112,7 @@ examples: - | #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/arm/ux500_pm_domains.h> #include <dt-bindings/mfd/dbx500-prcmu.h> #include <dt-bindings/gpio/gpio.h> @@ -116,6 +120,7 @@ examples: compatible = "ste,mcde"; reg = <0xa0350000 0x1000>; interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&pm_domains DOMAIN_B2R2_MCDE>; epod-supply = <&db8500_b2r2_mcde_reg>; vana-supply = <&ab8500_ldo_ana_reg>; clocks = <&prcmu_clk PRCMU_MCDECLK>, diff --git a/MAINTAINERS b/MAINTAINERS index 4c8ed64144fd..84b684425bbc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8368,6 +8368,13 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/novatek,nt37801.yaml F: drivers/gpu/drm/panel/panel-novatek-nt37801.c +DRM DRIVER FOR NOVATEK NT51021 PANELS +M: Ryan Brue <ryanbrue.dev@gmail.com> +M: Nickolay Goppen <setotau@mainlining.org> +S: Maintained +F: Documentation/devicetree/bindings/display/panel/novatek,nt51021.yaml +F: drivers/gpu/drm/panel/panel-novatek-nt51021.c + DRM DRIVER FOR NVIDIA GEFORCE/QUADRO GPUS M: Lyude Paul <lyude@redhat.com> M: Danilo Krummrich <dakr@kernel.org> @@ -8457,6 +8464,12 @@ S: Maintained F: Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml F: drivers/gpu/drm/panel/panel-raydium-rm67191.c +DRM DRIVER FOR RAYDIUM RM69220 PANELS +M: Alexander Koskovich <akoskovich@pm.me> +S: Maintained +F: Documentation/devicetree/bindings/display/panel/raydium,rm69220.yaml +F: drivers/gpu/drm/panel/panel-raydium-rm69220.c + DRM DRIVER FOR SAMSUNG DB7430 PANELS M: Linus Walleij <linusw@kernel.org> S: Maintained diff --git a/drivers/accel/amdxdna/aie.c b/drivers/accel/amdxdna/aie.c index dd6f36f222c7..bf8eb66baab8 100644 --- a/drivers/accel/amdxdna/aie.c +++ b/drivers/accel/amdxdna/aie.c @@ -118,6 +118,45 @@ void amdxdna_vbnv_init(struct amdxdna_dev *xdna) amdxdna_update_vbnv(xdna, info->rev_vbnv_tbl, rev); } +void amdxdna_io_stats_job_start(struct amdxdna_client *client) +{ + int depth; + + guard(spinlock)(&client->io_stats.lock); + + depth = client->io_stats.job_depth++; + if (!depth) + client->io_stats.start_time = ktime_get_ns(); +} + +void amdxdna_io_stats_job_done(struct amdxdna_client *client) +{ + u64 busy_ns; + int depth; + + guard(spinlock)(&client->io_stats.lock); + + depth = --client->io_stats.job_depth; + if (!depth) { + busy_ns = ktime_get_ns() - client->io_stats.start_time; + client->io_stats.start_time = 0; + client->io_stats.busy_time += busy_ns; + } +} + +u64 amdxdna_io_stats_busy_time_ns(struct amdxdna_client *client) +{ + u64 busy_ns; + + guard(spinlock)(&client->io_stats.lock); + + busy_ns = client->io_stats.busy_time; + if (client->io_stats.job_depth) + busy_ns += ktime_get_ns() - client->io_stats.start_time; + + return busy_ns; +} + int amdxdna_get_metadata(struct aie_device *aie, struct amdxdna_client *client, struct amdxdna_drm_get_info *args) diff --git a/drivers/accel/amdxdna/aie.h b/drivers/accel/amdxdna/aie.h index 0483d582b7f8..1fc5ddfe696a 100644 --- a/drivers/accel/amdxdna/aie.h +++ b/drivers/accel/amdxdna/aie.h @@ -117,4 +117,8 @@ 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); +void amdxdna_io_stats_job_start(struct amdxdna_client *client); +void amdxdna_io_stats_job_done(struct amdxdna_client *client); +u64 amdxdna_io_stats_busy_time_ns(struct amdxdna_client *client); + #endif /* _AIE_H_ */ diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c index d927c8c9d557..9c6b7353cbe9 100644 --- a/drivers/accel/amdxdna/aie2_ctx.c +++ b/drivers/accel/amdxdna/aie2_ctx.c @@ -290,6 +290,7 @@ aie2_sched_resp_handler(void *handle, void __iomem *data, size_t size) int ret = 0; u32 status; + amdxdna_io_stats_job_done(job->hwctx->client); cmd_abo = job->cmd_bo; if (unlikely(job->job_timeout)) { @@ -322,6 +323,8 @@ aie2_sched_drvcmd_resp_handler(void *handle, void __iomem *data, size_t size) struct amdxdna_sched_job *job = handle; int ret = 0; + amdxdna_io_stats_job_done(job->hwctx->client); + if (unlikely(!data || size != sizeof(u32))) { job->drv_cmd->result = U32_MAX; ret = -EINVAL; @@ -344,6 +347,7 @@ aie2_sched_cmdlist_resp_handler(void *handle, void __iomem *data, size_t size) u32 cmd_status; int ret = 0; + amdxdna_io_stats_job_done(job->hwctx->client); cmd_abo = job->cmd_bo; if (unlikely(job->job_timeout)) { @@ -405,6 +409,7 @@ aie2_sched_job_run(struct drm_sched_job *sched_job) kref_get(&job->refcnt); fence = dma_fence_get(job->fence); + amdxdna_io_stats_job_start(job->hwctx->client); if (job->drv_cmd) { switch (job->drv_cmd->opcode) { case SYNC_DEBUG_BO: @@ -432,6 +437,7 @@ aie2_sched_job_run(struct drm_sched_job *sched_job) out: if (ret) { + amdxdna_io_stats_job_done(job->hwctx->client); dma_fence_put(job->fence); aie2_job_put(job); mmput(job->mm); diff --git a/drivers/accel/amdxdna/aie2_error.c b/drivers/accel/amdxdna/aie2_error.c index babdac0157ab..8516574e33eb 100644 --- a/drivers/accel/amdxdna/aie2_error.c +++ b/drivers/accel/amdxdna/aie2_error.c @@ -343,15 +343,14 @@ void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev) kfree(events); } -int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev) +int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev, u32 num_events) { struct amdxdna_dev *xdna = ndev->aie.xdna; - u32 total_col = ndev->total_col; - u32 total_size = ASYNC_BUF_SIZE * total_col; + u32 total_size = ASYNC_BUF_SIZE * num_events; struct async_events *events; int i, ret; - events = kzalloc_flex(*events, event, total_col); + events = kzalloc_flex(*events, event, num_events); if (!events) return -ENOMEM; @@ -361,7 +360,7 @@ int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev) goto free_events; } events->size = total_size; - events->event_cnt = total_col; + events->event_cnt = num_events; events->wq = alloc_ordered_workqueue("async_wq", 0); if (!events->wq) { diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index f658760c3d48..56b919b350ac 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -261,8 +261,9 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct goto del_ctx_req; } + /* +1 for the config_cu message that can be in-flight concurrently */ ret = xdna_mailbox_start_channel(hwctx->priv->mbox_chann, &x2i, &i2x, - intr_reg, ret); + intr_reg, ret, HWCTX_MAX_CMDS + 1); if (ret) { XDNA_ERR(xdna, "Not able to create channel"); ret = -EINVAL; @@ -486,7 +487,7 @@ int aie2_register_asyn_event_msg(struct amdxdna_dev_hdl *ndev, dma_addr_t addr, req.buf_size = size; 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); + return xdna_mailbox_send_msg(ndev->aie.mgmt_chann, &msg); } int aie2_config_cu(struct amdxdna_hwctx *hwctx, @@ -545,7 +546,8 @@ int aie2_config_cu(struct amdxdna_hwctx *hwctx, msg.handle = hwctx; msg.opcode = MSG_OP_CONFIG_CU; msg.notify_cb = notify_cb; - return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); + + return xdna_mailbox_send_msg(chann, &msg); } static int aie2_init_exec_cu_req(struct amdxdna_gem_obj *cmd_bo, void *req, @@ -950,7 +952,7 @@ int aie2_execbuf(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, print_hex_dump_debug("cmd: ", DUMP_PREFIX_OFFSET, 16, 4, &req, 0x40, false); - ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); + ret = xdna_mailbox_send_msg(chann, &msg); if (ret) { XDNA_ERR(xdna, "Send message failed"); return ret; @@ -1039,7 +1041,7 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx, msg.send_size = sizeof(req); print_hex_dump_debug("cmdlist msg: ", DUMP_PREFIX_OFFSET, 16, 4, &req, msg.send_size, false); - ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); + ret = xdna_mailbox_send_msg(chann, &msg); if (ret) { XDNA_ERR(xdna, "Send message failed"); return ret; @@ -1086,7 +1088,7 @@ int aie2_cmdlist_single_execbuf(struct amdxdna_hwctx *hwctx, msg.send_size = sizeof(req); print_hex_dump_debug("cmdlist msg: ", DUMP_PREFIX_OFFSET, 16, 4, &req, msg.send_size, false); - ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); + ret = xdna_mailbox_send_msg(chann, &msg); if (ret) { XDNA_ERR(hwctx->client->xdna, "Send message failed"); return ret; @@ -1122,7 +1124,7 @@ int aie2_sync_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, msg.send_size = sizeof(req); msg.opcode = MSG_OP_SYNC_BO; - ret = xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); + ret = xdna_mailbox_send_msg(chann, &msg); if (ret) { XDNA_ERR(xdna, "Send message failed"); return ret; @@ -1157,7 +1159,7 @@ int aie2_config_debug_bo(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job * msg.send_size = sizeof(req); msg.opcode = MSG_OP_CONFIG_DEBUG_BO; - return xdna_mailbox_send_msg(chann, &msg, TX_TIMEOUT); + return xdna_mailbox_send_msg(chann, &msg); } int aie2_query_app_health(struct amdxdna_dev_hdl *ndev, u32 context_id, diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c index 7a4314ca843b..d2fa46e4d3a4 100644 --- a/drivers/accel/amdxdna/aie2_pci.c +++ b/drivers/accel/amdxdna/aie2_pci.c @@ -24,6 +24,7 @@ #include "aie2_pci.h" #include "aie2_solver.h" #include "amdxdna_ctx.h" +#include "amdxdna_error.h" #include "amdxdna_gem.h" #include "amdxdna_mailbox.h" #include "amdxdna_pci_drv.h" @@ -385,11 +386,21 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) } xdna_mailbox_intr_reg = ndev->aie.mgmt_i2x.mb_head_ptr_reg + 4; + /* + * At most AMDXDNA_MAX_ASYNC_EVENT_BUFS async event messages plus one + * management command can be unresponded. The async slots stay busy + * until firmware reports an error. Every management command holds + * dev_lock across aie_send_mgmt_msg_wait(), and the response clears + * the slot before that wait returns, so a second ioctl blocks on + * dev_lock rather than observing -ENOBUFS. Async re-registration + * takes the same lock after the RX path has freed its slot. + */ ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann, &ndev->aie.mgmt_x2i, &ndev->aie.mgmt_i2x, xdna_mailbox_intr_reg, - mgmt_mb_irq); + mgmt_mb_irq, + AMDXDNA_MAX_ASYNC_EVENT_BUFS + 1); if (ret) { XDNA_ERR(xdna, "failed to start management mailbox channel"); ret = -EINVAL; @@ -414,7 +425,7 @@ static int aie2_hw_start(struct amdxdna_dev *xdna) goto stop_fw; } - ret = aie2_error_async_events_alloc(ndev); + ret = aie2_error_async_events_alloc(ndev, AMDXDNA_MAX_ASYNC_EVENT_BUFS); if (ret) { XDNA_ERR(xdna, "Allocate async events failed, ret %d", ret); goto stop_fw; diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h index 2c7019bd26b5..a5fd71329b93 100644 --- a/drivers/accel/amdxdna/aie2_pci.h +++ b/drivers/accel/amdxdna/aie2_pci.h @@ -244,7 +244,7 @@ int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level); /* aie2_error.c */ -int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev); +int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev, u32 num_events); void aie2_error_async_events_free(struct amdxdna_dev_hdl *ndev); int aie2_error_async_msg_thread(void *data); int aie2_get_array_async_error(struct amdxdna_dev_hdl *ndev, diff --git a/drivers/accel/amdxdna/aie4_pci.c b/drivers/accel/amdxdna/aie4_pci.c index a58a83af42a4..e99ad35870ef 100644 --- a/drivers/accel/amdxdna/aie4_pci.c +++ b/drivers/accel/amdxdna/aie4_pci.c @@ -12,6 +12,7 @@ #include "aie.h" #include "aie4_msg_priv.h" #include "aie4_pci.h" +#include "amdxdna_error.h" #include "amdxdna_mailbox.h" #include "amdxdna_mailbox_helper.h" #include "amdxdna_pci_drv.h" @@ -181,11 +182,16 @@ static int aie4_mailbox_start(struct amdxdna_dev *xdna, goto free_channel; } + /* + * At any given time, at most AMDXDNA_MAX_ASYNC_EVENT_BUFS async event + * messages plus 1 other management command can be unresponded. + */ ret = xdna_mailbox_start_channel(ndev->aie.mgmt_chann, &ndev->aie.mgmt_x2i, &ndev->aie.mgmt_i2x, NO_IOHUB, - mgmt_mb_irq); + mgmt_mb_irq, + AMDXDNA_MAX_ASYNC_EVENT_BUFS + 1); if (ret) { XDNA_ERR(xdna, "failed to start management mailbox channel"); ret = -EINVAL; diff --git a/drivers/accel/amdxdna/amdxdna_error.h b/drivers/accel/amdxdna/amdxdna_error.h index c51de86ec12b..326d7153b17d 100644 --- a/drivers/accel/amdxdna/amdxdna_error.h +++ b/drivers/accel/amdxdna/amdxdna_error.h @@ -56,4 +56,14 @@ enum amdxdna_error_module { (FIELD_PREP(AMDXDNA_EXTRA_ERR_COL_MASK, col) | \ FIELD_PREP(AMDXDNA_EXTRA_ERR_ROW_MASK, row)) +/* + * Maximum number of async event buffers. This value is used in two places: + * (1) to size the management mailbox channel, which must happen before + * total_col is known from firmware; (2) to allocate the async event pool, + * which runs after total_col is known. Both counts must match. The channel + * is sized AMDXDNA_MAX_ASYNC_EVENT_BUFS + 1 (one extra for other management + * commands), so the pool cannot exceed AMDXDNA_MAX_ASYNC_EVENT_BUFS slots. + */ +#define AMDXDNA_MAX_ASYNC_EVENT_BUFS 4 + #endif /* _AMDXDNA_ERROR_H_ */ diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index f4832337ec31..f14484bc31ec 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -1140,6 +1140,11 @@ amdxdna_drm_create_dev_heap_bo(struct drm_device *dev, struct amdxdna_gem_obj *abo; int ret; + if (!xdna->dev_info->dev_mem_size) { + XDNA_DBG(xdna, "Device heap is not supported"); + return ERR_PTR(-EOPNOTSUPP); + } + WARN_ON(!is_power_of_2(xdna->dev_info->dev_mem_size)); XDNA_DBG(xdna, "Requested dev heap size 0x%llx", args->size); if (!args->size || !IS_ALIGNED(args->size, xdna->dev_info->dev_mem_size)) { diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.c b/drivers/accel/amdxdna/amdxdna_mailbox.c index 05c3786de135..c4701b2703d7 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox.c +++ b/drivers/accel/amdxdna/amdxdna_mailbox.c @@ -9,7 +9,6 @@ #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/slab.h> -#include <linux/xarray.h> #define CREATE_TRACE_POINTS #include <trace/events/amdxdna.h> @@ -35,9 +34,12 @@ (_chann)->msix_irq, ##args); \ }) +#define MSG_BUF_SZ(chann) \ + (mailbox_get_ringbuf_size(chann, CHAN_RES_X2I) + \ + sizeof(struct mailbox_msg)) + #define MAGIC_VAL 0x1D000000U #define MAGIC_VAL_MASK 0xFF000000 -#define MAX_MSG_ID_ENTRIES 256 #define MSG_RX_TIMER 200 /* milliseconds */ #define MAILBOX_NAME "xdna_mailbox" @@ -57,8 +59,6 @@ struct mailbox_channel { struct xdna_mailbox_chann_res res[CHAN_RES_NUM]; int msix_irq; u32 iohub_int_addr; - struct xarray chan_xa; - u32 next_msgid; u32 x2i_tail; /* Received msg related fields */ @@ -66,6 +66,12 @@ struct mailbox_channel { struct work_struct rx_work; u32 i2x_head; bool bad_state; + + /* Pre-allocated message buffer pool */ + void *msg_buf; + u32 msg_buf_num; + u32 next_slot; /* next slot index to try for allocation */ + struct mutex lock; }; #define MSG_BODY_SZ GENMASK(10, 0) @@ -90,12 +96,36 @@ struct mailbox_pkg { #define TOMBSTONE 0xDEADFACE struct mailbox_msg { + bool busy; void *handle; int (*notify_cb)(void *handle, void __iomem *data, size_t size); size_t pkg_size; /* package size in bytes */ struct mailbox_pkg pkg; }; +/* + * Clear busy before invoking the callback so that a new submission can + * reuse the slot immediately - by the time notify_cb returns, the caller + * may already have submitted a new job that needs this slot. + * + * notify_cb and handle are cached in locals first. smp_store_release() + * ensures those loads complete before busy is cleared - without it the + * compiler or CPU could sink the loads past the store, letting a + * concurrent sender overwrite notify_cb/handle before we read them. + * Paired with smp_load_acquire() in mailbox_acquire_msgid(). + */ +static int mailbox_msg_done(struct mailbox_msg *mb_msg, void __iomem *data, size_t size) +{ + int (*notify_cb)(void *, void __iomem *, size_t) = mb_msg->notify_cb; + void *handle = mb_msg->handle; + + /* Pairs with smp_load_acquire() in mailbox_acquire_msgid(). */ + smp_store_release(&mb_msg->busy, false); + if (notify_cb) + return notify_cb(handle, data, size); + return 0; +} + static void mailbox_reg_write(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 data) { struct xdna_mailbox_res *mb_res = &mb_chann->mb->res; @@ -161,38 +191,57 @@ static inline int mailbox_validate_msgid(int msg_id) return (msg_id & MAGIC_VAL_MASK) == MAGIC_VAL; } -static int mailbox_acquire_msgid(struct mailbox_channel *mb_chann, struct mailbox_msg *mb_msg) +static struct mailbox_msg *mailbox_msg_ptr(struct mailbox_channel *mb_chann, u32 slot) { - u32 msg_id; - int ret; + return mb_chann->msg_buf + slot * MSG_BUF_SZ(mb_chann); +} - ret = xa_alloc_cyclic_irq(&mb_chann->chan_xa, &msg_id, mb_msg, - XA_LIMIT(0, MAX_MSG_ID_ENTRIES - 1), - &mb_chann->next_msgid, GFP_NOWAIT); - if (ret < 0) - return ret; +static int mailbox_acquire_msgid(struct mailbox_channel *mb_chann) +{ + struct mailbox_msg *mb_msg; + u32 slot; + u32 i; /* - * Add MAGIC_VAL to the higher bits. + * msg_id = slot | MAGIC_VAL. Scan slots starting from next_slot. + * smp_load_acquire() pairs with smp_store_release() in mailbox_msg_done() + * to ensure that once we observe busy == false, notify_cb and handle are + * also visible as their post-completion values before we overwrite them. */ - msg_id |= MAGIC_VAL; - return msg_id; -} + for (i = 0; i < mb_chann->msg_buf_num; i++) { + slot = (mb_chann->next_slot + i) % mb_chann->msg_buf_num; + mb_msg = mailbox_msg_ptr(mb_chann, slot); + /* Pairs with smp_store_release() in mailbox_msg_done(). */ + if (!smp_load_acquire(&mb_msg->busy)) { + mb_chann->next_slot = (slot + 1) % mb_chann->msg_buf_num; + return slot | MAGIC_VAL; + } + } -static void mailbox_release_msgid(struct mailbox_channel *mb_chann, int msg_id) -{ - msg_id &= ~MAGIC_VAL_MASK; - xa_erase_irq(&mb_chann->chan_xa, msg_id); + return -ENOBUFS; } -static void mailbox_release_msg(struct mailbox_channel *mb_chann, - struct mailbox_msg *mb_msg) +static struct mailbox_msg *mailbox_get_msg_buf(struct mailbox_channel *mb_chann, + size_t msg_size) { - MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x", - mb_msg->pkg.header.id, mb_msg->pkg.header.opcode); - if (mb_msg->notify_cb) - mb_msg->notify_cb(mb_msg->handle, NULL, 0); - kfree(mb_msg); + struct mailbox_msg *mb_msg; + int msg_id; + u32 slot; + + /* msg_id = slot | MAGIC_VAL */ + msg_id = mailbox_acquire_msgid(mb_chann); + if (msg_id < 0) { + MB_ERR(mb_chann, "No free message slot"); + return NULL; + } + + slot = msg_id & ~MAGIC_VAL_MASK; + mb_msg = mailbox_msg_ptr(mb_chann, slot); + memset(mb_msg, 0, msg_size + sizeof(*mb_msg)); + mb_msg->busy = true; + mb_msg->pkg.header.id = msg_id; + + return mb_msg; } static int @@ -224,7 +273,7 @@ check_again: if (tail < head && tmp_tail >= head) { ret = read_poll_timeout(mailbox_get_headptr, head, tmp_tail < head || tail >= head, - 1, 100, false, mb_chann, CHAN_RES_X2I); + 1, 2000000, false, mb_chann, CHAN_RES_X2I); if (ret) return ret; @@ -248,8 +297,9 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade void __iomem *data) { struct mailbox_msg *mb_msg; - int msg_id; int ret = 0; + int msg_id; + u32 slot; msg_id = header->id; if (!mailbox_validate_msgid(msg_id)) { @@ -257,22 +307,32 @@ mailbox_get_resp(struct mailbox_channel *mb_chann, struct xdna_msg_header *heade return -EINVAL; } - msg_id &= ~MAGIC_VAL_MASK; - mb_msg = xa_erase_irq(&mb_chann->chan_xa, msg_id); - if (!mb_msg) { - MB_ERR(mb_chann, "Cannot find msg 0x%x", msg_id); + slot = msg_id & ~MAGIC_VAL_MASK; + if (unlikely(slot >= mb_chann->msg_buf_num)) { + MB_ERR(mb_chann, "Invalid msg_id 0x%x", msg_id); return -EINVAL; } + mb_msg = mailbox_msg_ptr(mb_chann, slot); MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x", header->opcode, header->total_size, header->id); - if (mb_msg->notify_cb) { - ret = mb_msg->notify_cb(mb_msg->handle, data, header->total_size); - if (unlikely(ret)) - MB_ERR(mb_chann, "Message callback ret %d", ret); + + /* + * msg_id is slot | MAGIC_VAL and is reused as soon as the slot is + * freed. The management firmware protocol guarantees exactly one + * response for each request, so a response cannot refer to a previous + * user of a reused slot. Keep the busy check as a defensive guard + * against an unexpected response for an idle slot. + */ + if (unlikely(!mb_msg->busy)) { + MB_WARN_ONCE(mb_chann, "Unexpected response for idle slot 0x%x", msg_id); + return 0; } - kfree(mb_msg); + ret = mailbox_msg_done(mb_msg, data, header->total_size); + if (unlikely(ret)) + MB_ERR(mb_chann, "Message callback ret %d", ret); + return ret; } @@ -399,7 +459,7 @@ again: } int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann, - const struct xdna_mailbox_msg *msg, u64 tx_timeout) + const struct xdna_mailbox_msg *msg) { struct xdna_msg_header *header; struct mailbox_msg *mb_msg; @@ -428,9 +488,12 @@ int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann, return -EPIPE; } - mb_msg = kzalloc(sizeof(*mb_msg) + pkg_size, GFP_KERNEL); - if (!mb_msg) - return -ENOMEM; + mutex_lock(&mb_chann->lock); + mb_msg = mailbox_get_msg_buf(mb_chann, pkg_size); + if (!mb_msg) { + mutex_unlock(&mb_chann->lock); + return -ENOBUFS; + } mb_msg->handle = msg->handle; mb_msg->notify_cb = msg->notify_cb; @@ -447,28 +510,18 @@ int xdna_mailbox_send_msg(struct mailbox_channel *mb_chann, header->opcode = msg->opcode; memcpy(mb_msg->pkg.payload, msg->send_data, msg->send_size); - ret = mailbox_acquire_msgid(mb_chann, mb_msg); - if (unlikely(ret < 0)) { - MB_ERR(mb_chann, "mailbox_acquire_msgid failed"); - goto msg_id_failed; - } - header->id = ret; - MB_DBG(mb_chann, "opcode 0x%x size %d id 0x%x", header->opcode, header->total_size, header->id); ret = mailbox_send_msg(mb_chann, mb_msg); if (ret) { MB_DBG(mb_chann, "Error in mailbox send msg, ret %d", ret); - goto release_id; + mb_msg->busy = false; + mutex_unlock(&mb_chann->lock); + return ret; } + mutex_unlock(&mb_chann->lock); - return 0; - -release_id: - mailbox_release_msgid(mb_chann, header->id); -msg_id_failed: - kfree(mb_msg); return ret; } @@ -487,6 +540,7 @@ struct mailbox_channel *xdna_mailbox_alloc_channel(struct mailbox *mb) goto free_chann; } mb_chann->mb = mb; + mutex_init(&mb_chann->lock); return mb_chann; @@ -500,7 +554,9 @@ void xdna_mailbox_free_channel(struct mailbox_channel *mb_chann) if (!mb_chann) return; + kfree(mb_chann->msg_buf); destroy_workqueue(mb_chann->work_q); + mutex_destroy(&mb_chann->lock); kfree(mb_chann); } @@ -509,21 +565,32 @@ xdna_mailbox_start_channel(struct mailbox_channel *mb_chann, const struct xdna_mailbox_chann_res *x2i, const struct xdna_mailbox_chann_res *i2x, u32 iohub_int_addr, - int mb_irq) + int mb_irq, u32 n_msg) { int ret; - if (!is_power_of_2(x2i->rb_size) || !is_power_of_2(i2x->rb_size)) { - pr_err("Ring buf size must be power of 2\n"); - return -EINVAL; - } - + /* + * The ring buffer geometry, rb_start_addr and rb_size for both the + * x2i and the i2x channel, comes from AMD signed firmware, through + * the management or mailbox information block, or through the + * CREATE_CONTEXT response. The driver trusts those values by design + * and does not bound them against the mapped region. + */ mb_chann->msix_irq = mb_irq; mb_chann->iohub_int_addr = iohub_int_addr; memcpy(&mb_chann->res[CHAN_RES_X2I], x2i, sizeof(*x2i)); memcpy(&mb_chann->res[CHAN_RES_I2X], i2x, sizeof(*i2x)); - xa_init_flags(&mb_chann->chan_xa, XA_FLAGS_ALLOC | XA_FLAGS_LOCK_IRQ); + /* + * msg_buf is a flat array of n_msg slots, separate from the hardware + * ring buffer. Each slot is rb_size bytes - the combined size of the + * mailbox_msg metadata and the package payload must not exceed rb_size. + */ + mb_chann->msg_buf = kcalloc(n_msg, MSG_BUF_SZ(mb_chann), GFP_KERNEL); + if (!mb_chann->msg_buf) + return -ENOMEM; + + mb_chann->msg_buf_num = n_msg; mb_chann->x2i_tail = mailbox_get_tailptr(mb_chann, CHAN_RES_X2I); mb_chann->i2x_head = mailbox_get_headptr(mb_chann, CHAN_RES_I2X); @@ -531,6 +598,8 @@ xdna_mailbox_start_channel(struct mailbox_channel *mb_chann, ret = request_irq(mb_irq, mailbox_irq_handler, 0, MAILBOX_NAME, mb_chann); if (ret) { MB_ERR(mb_chann, "Failed to request irq %d ret %d", mb_irq, ret); + kfree(mb_chann->msg_buf); + mb_chann->msg_buf = NULL; return ret; } @@ -544,7 +613,7 @@ xdna_mailbox_start_channel(struct mailbox_channel *mb_chann, void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann) { struct mailbox_msg *mb_msg; - unsigned long msg_id; + u32 i; if (!mb_chann) return; @@ -555,12 +624,22 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mb_chann) /* Cancel RX work and wait for it to finish */ drain_workqueue(mb_chann->work_q); - /* We can clean up and release resources */ - 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); + /* Release any in-flight messages in the pool + * + * Drain in submission order: next_slot points to the slot after + * the most recently assigned one, so start from next_slot and + * wrap around so callbacks fire oldest-first. + */ + for (i = 0; i < mb_chann->msg_buf_num; i++) { + u32 slot = (mb_chann->next_slot + i) % mb_chann->msg_buf_num; + + mb_msg = mailbox_msg_ptr(mb_chann, slot); + if (!mb_msg->busy) + continue; + MB_DBG(mb_chann, "msg_id 0x%x msg opcode 0x%x", + mb_msg->pkg.header.id, mb_msg->pkg.header.opcode); + mailbox_msg_done(mb_msg, NULL, 0); + } MB_DBG(mb_chann, "Mailbox channel stopped, irq: %d", mb_chann->msix_irq); } diff --git a/drivers/accel/amdxdna/amdxdna_mailbox.h b/drivers/accel/amdxdna/amdxdna_mailbox.h index 2908404303ae..7f4a40b4230b 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox.h +++ b/drivers/accel/amdxdna/amdxdna_mailbox.h @@ -88,6 +88,10 @@ struct mailbox_channel *xdna_mailbox_alloc_channel(struct mailbox *mb); * @i2x: firmware to host mailbox resources * @xdna_mailbox_intr_reg: register addr of MSI-X interrupt * @mb_irq: Linux IRQ number associated with mailbox MSI-X interrupt vector index + * @n_msg: number of message slots to pre-allocate; must be non-zero for + * the PCI transport (the platform transport ignores this argument). + * Each slot is rb_size bytes. The caller must ensure at most n_msg + * messages are in flight at any time. * * Return: If success, return a handle of mailbox channel. Otherwise, return NULL. */ @@ -96,7 +100,7 @@ xdna_mailbox_start_channel(struct mailbox_channel *mb_chann, const struct xdna_mailbox_chann_res *x2i, const struct xdna_mailbox_chann_res *i2x, u32 xdna_mailbox_intr_reg, - int mb_irq); + int mb_irq, u32 n_msg); /* * xdna_mailbox_free_channel() -- free mailbox channel @@ -117,11 +121,10 @@ void xdna_mailbox_stop_channel(struct mailbox_channel *mailbox_chann); * * @mailbox_chann: Mailbox channel handle * @msg: message struct for message information - * @tx_timeout: the timeout value for sending the message in ms. * * Return: If success return 0, otherwise, return error code */ int xdna_mailbox_send_msg(struct mailbox_channel *mailbox_chann, - const struct xdna_mailbox_msg *msg, u64 tx_timeout); + const struct xdna_mailbox_msg *msg); #endif /* _AIE_MAILBOX_ */ diff --git a/drivers/accel/amdxdna/amdxdna_mailbox_helper.c b/drivers/accel/amdxdna/amdxdna_mailbox_helper.c index 6d0c24513476..ee33a34852f0 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox_helper.c +++ b/drivers/accel/amdxdna/amdxdna_mailbox_helper.c @@ -44,7 +44,7 @@ int xdna_send_msg_wait(struct amdxdna_dev *xdna, struct mailbox_channel *chann, struct xdna_notify *hdl = msg->handle; int ret; - ret = xdna_mailbox_send_msg(chann, msg, TX_TIMEOUT); + ret = xdna_mailbox_send_msg(chann, msg); if (ret) { XDNA_ERR(xdna, "Send message failed, ret %d", ret); return ret; diff --git a/drivers/accel/amdxdna/amdxdna_mailbox_helper.h b/drivers/accel/amdxdna/amdxdna_mailbox_helper.h index 556c712cad0a..11924d38f2fe 100644 --- a/drivers/accel/amdxdna/amdxdna_mailbox_helper.h +++ b/drivers/accel/amdxdna/amdxdna_mailbox_helper.h @@ -6,7 +6,6 @@ #ifndef _AMDXDNA_MAILBOX_HELPER_H #define _AMDXDNA_MAILBOX_HELPER_H -#define TX_TIMEOUT 2000 /* milliseconds */ #define RX_TIMEOUT 5000 /* milliseconds */ struct amdxdna_dev; diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c index bb339e641416..67a02d6b7f97 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.c +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c @@ -14,6 +14,7 @@ #include <linux/iommu.h> #include <linux/pci.h> +#include "aie.h" #include "amdxdna_cbuf.h" #include "amdxdna_ctx.h" #include "amdxdna_debugfs.h" @@ -151,6 +152,8 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp) filp->driver_priv = client; client->filp = filp; + spin_lock_init(&client->io_stats.lock); + XDNA_DBG(xdna, "pid %d opened", client->pid); return 0; @@ -287,6 +290,9 @@ static void amdxdna_show_fdinfo(struct drm_printer *p, struct drm_file *filp) size_t heap_usage, external_usage, internal_usage; char *drv_name = filp->minor->dev->driver->name; + drm_printf(p, "drm-engine-npu:\t%llu ns\n", + amdxdna_io_stats_busy_time_ns(client)); + mutex_lock(&client->mm_lock); heap_usage = client->heap_usage; diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h index a997d27a504d..510356a31532 100644 --- a/drivers/accel/amdxdna/amdxdna_pci_drv.h +++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h @@ -143,6 +143,13 @@ struct amdxdna_device_id { const struct amdxdna_dev_info *dev_info; }; +struct amdxdna_io_stats { + spinlock_t lock; /* protect io stats */ + int job_depth; + u64 start_time; + u64 busy_time; +}; + /* * struct amdxdna_client - amdxdna client * A per fd data structure for managing context and other user process stuffs. @@ -169,6 +176,8 @@ struct amdxdna_client { size_t heap_usage; size_t total_bo_usage; size_t total_int_bo_usage; + + struct amdxdna_io_stats io_stats; }; #define amdxdna_for_each_hwctx(client, hwctx_id, entry) \ diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 647684219e9d..247ed6b2ff62 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -67,6 +67,48 @@ bool ivpu_force_snoop; module_param_named(force_snoop, ivpu_force_snoop, bool, 0444); MODULE_PARM_DESC(force_snoop, "Force snooping for NPU host memory access"); +static struct pci_device_id ivpu_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_MTL) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_ARL) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PTL) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_WCL) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_NVL) }, + {} +}; +MODULE_DEVICE_TABLE(pci, ivpu_pci_ids); + +static int ivpu_ip_gen_init(struct ivpu_device *vdev) +{ + vdev->platform = IVPU_PLATFORM_INVALID; + + switch (ivpu_device_id(vdev)) { + case PCI_DEVICE_ID_MTL: + case PCI_DEVICE_ID_ARL: + vdev->hw_ip_gen = IVPU_HW_IP_37XX; + vdev->btrs_gen = IVPU_HW_BTRS_MTL; + break; + case PCI_DEVICE_ID_LNL: + vdev->hw_ip_gen = IVPU_HW_IP_40XX; + vdev->btrs_gen = IVPU_HW_BTRS_LNL; + break; + case PCI_DEVICE_ID_PTL: + case PCI_DEVICE_ID_WCL: + vdev->hw_ip_gen = IVPU_HW_IP_50XX; + vdev->btrs_gen = IVPU_HW_BTRS_LNL; + break; + case PCI_DEVICE_ID_NVL: + vdev->hw_ip_gen = IVPU_HW_IP_60XX; + vdev->btrs_gen = IVPU_HW_BTRS_LNL; + break; + default: + ivpu_err(vdev, "Unknown PCI device [%04x:%04x]\n", ivpu_vendor_id(vdev), + ivpu_device_id(vdev)); + return -EINVAL; + } + return 0; +} + static struct ivpu_user_limits *ivpu_user_limits_alloc(struct ivpu_device *vdev, uid_t uid) { struct ivpu_user_limits *limits; @@ -248,10 +290,7 @@ static int ivpu_get_param_ioctl(struct drm_device *dev, void *data, struct drm_f break; case DRM_IVPU_PARAM_FW_API_VERSION: if (args->index < VPU_FW_API_VER_NUM) { - struct vpu_firmware_header *fw_hdr; - - fw_hdr = (struct vpu_firmware_header *)vdev->fw->file->data; - args->value = fw_hdr->api_version[args->index]; + args->value = vdev->fw->api_version[args->index]; } else { ret = -EINVAL; } @@ -481,6 +520,14 @@ int ivpu_boot(struct ivpu_device *vdev) ivpu_fw_boot_params_setup(vdev, ivpu_bo_vaddr(vdev->fw->mem_bp)); vdev->fw->last_boot_mode = vdev->fw->next_boot_mode; + if (!ivpu_fw_is_warm_boot(vdev)) { + ret = ivpu_fw_load(vdev); + if (ret) { + ivpu_err(vdev, "Failed to load the firmware: %d\n", ret); + return ret; + } + } + ret = ivpu_hw_boot_fw(vdev); if (ret) { ivpu_err(vdev, "Failed to start the firmware: %d\n", ret); @@ -497,6 +544,9 @@ int ivpu_boot(struct ivpu_device *vdev) ivpu_hw_irq_enable(vdev); ivpu_ipc_enable(vdev); + if (ivpu_test_mode & IVPU_TEST_MODE_FW_TEST) + return 0; + if (!ivpu_fw_is_warm_boot(vdev)) { ret = ivpu_pm_dct_init(vdev); if (ret) @@ -650,6 +700,7 @@ static int ivpu_pci_init(struct ivpu_device *vdev) struct pci_dev *pdev = to_pci_dev(vdev->drm.dev); struct resource *bar0 = &pdev->resource[0]; struct resource *bar4 = &pdev->resource[4]; + int dma_bits; int ret; ivpu_dbg(vdev, MISC, "Mapping BAR0 (RegV) %pR\n", bar0); @@ -666,7 +717,12 @@ static int ivpu_pci_init(struct ivpu_device *vdev) return PTR_ERR(vdev->regb); } - ret = dma_set_mask_and_coherent(vdev->drm.dev, DMA_BIT_MASK(vdev->hw->dma_bits)); + if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX) + dma_bits = 42; + else + dma_bits = 38; + + ret = dma_set_mask_and_coherent(vdev->drm.dev, DMA_BIT_MASK(dma_bits)); if (ret) { ivpu_err(vdev, "Failed to set DMA mask: %d\n", ret); return ret; @@ -714,12 +770,10 @@ static int ivpu_dev_init(struct ivpu_device *vdev) if (!vdev->pm) return -ENOMEM; - if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_40XX) - vdev->hw->dma_bits = 48; - else - vdev->hw->dma_bits = 38; + ret = ivpu_ip_gen_init(vdev); + if (ret) + return ret; - vdev->platform = IVPU_PLATFORM_INVALID; vdev->context_xa_limit.min = IVPU_USER_CONTEXT_MIN_SSID; vdev->context_xa_limit.max = IVPU_USER_CONTEXT_MAX_SSID; atomic64_set(&vdev->unique_id_counter, 0); @@ -852,17 +906,6 @@ static void ivpu_dev_fini(struct ivpu_device *vdev) xa_destroy(&vdev->context_xa); } -static struct pci_device_id ivpu_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_MTL) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_ARL) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_LNL) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PTL_P) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_WCL) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_NVL) }, - {} -}; -MODULE_DEVICE_TABLE(pci, ivpu_pci_ids); - static int ivpu_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct ivpu_device *vdev; diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 8762b8969d96..a713f3645bfd 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -27,21 +27,13 @@ #define PCI_DEVICE_ID_MTL 0x7d1d #define PCI_DEVICE_ID_ARL 0xad1d #define PCI_DEVICE_ID_LNL 0x643e -#define PCI_DEVICE_ID_PTL_P 0xb03e +#define PCI_DEVICE_ID_PTL 0xb03e #define PCI_DEVICE_ID_WCL 0xfd3e #define PCI_DEVICE_ID_NVL 0xd71d -#define IVPU_HW_IP_37XX 37 -#define IVPU_HW_IP_40XX 40 -#define IVPU_HW_IP_50XX 50 -#define IVPU_HW_IP_60XX 60 - #define IVPU_HW_IP_REV_LNL_B0 4 #define IVPU_HW_IP_REV_NVL_A0 0 -#define IVPU_HW_BTRS_MTL 1 -#define IVPU_HW_BTRS_LNL 2 - #define IVPU_GLOBAL_CONTEXT_MMU_SSID 0 /* SSID 1 is used by the VPU to represent reserved context */ #define IVPU_RESERVED_CONTEXT_MMU_SSID 1 @@ -107,10 +99,21 @@ ivpu_dbg(vdev, MISC, "Using WA: " #wa_name "\n"); \ } while (0) +enum vpu_hw_ip { + IVPU_HW_IP_37XX = 37, + IVPU_HW_IP_40XX = 40, + IVPU_HW_IP_50XX = 50, + IVPU_HW_IP_60XX = 60, +}; + +enum vpu_hw_btrs { + IVPU_HW_BTRS_MTL = 1, + IVPU_HW_BTRS_LNL = 2, +}; + struct ivpu_wa_table { bool punit_disabled; bool clear_runtime_mem; - bool interrupt_clear_with_0; bool disable_clock_relinquish; bool wp0_during_power_up; bool disable_d0i2; @@ -136,6 +139,8 @@ struct ivpu_device { struct drm_device drm; void __iomem *regb; void __iomem *regv; + enum vpu_hw_ip hw_ip_gen; + enum vpu_hw_btrs btrs_gen; u32 platform; u32 irq; @@ -225,6 +230,7 @@ extern bool ivpu_force_snoop; #define IVPU_TEST_MODE_FW_TEST BIT(0) #define IVPU_TEST_MODE_NULL_HW BIT(1) #define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2) +#define IVPU_TEST_MODE_FREQ_CHANGE_ENABLE BIT(3) #define IVPU_TEST_MODE_MIP_DISABLE BIT(6) #define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8) #define IVPU_TEST_MODE_TURBO_ENABLE BIT(9) @@ -247,47 +253,24 @@ static inline u8 ivpu_revision(struct ivpu_device *vdev) return to_pci_dev(vdev->drm.dev)->revision; } +static inline u16 ivpu_vendor_id(struct ivpu_device *vdev) +{ + return to_pci_dev(vdev->drm.dev)->vendor; +} + static inline u16 ivpu_device_id(struct ivpu_device *vdev) { return to_pci_dev(vdev->drm.dev)->device; } -static inline int ivpu_hw_ip_gen(struct ivpu_device *vdev) +static inline enum vpu_hw_ip ivpu_hw_ip_gen(struct ivpu_device *vdev) { - switch (ivpu_device_id(vdev)) { - case PCI_DEVICE_ID_MTL: - case PCI_DEVICE_ID_ARL: - return IVPU_HW_IP_37XX; - case PCI_DEVICE_ID_LNL: - return IVPU_HW_IP_40XX; - case PCI_DEVICE_ID_PTL_P: - case PCI_DEVICE_ID_WCL: - return IVPU_HW_IP_50XX; - case PCI_DEVICE_ID_NVL: - return IVPU_HW_IP_60XX; - default: - dump_stack(); - ivpu_err(vdev, "Unknown NPU IP generation\n"); - return 0; - } + return vdev->hw_ip_gen; } -static inline int ivpu_hw_btrs_gen(struct ivpu_device *vdev) +static inline enum vpu_hw_btrs ivpu_hw_btrs_gen(struct ivpu_device *vdev) { - switch (ivpu_device_id(vdev)) { - case PCI_DEVICE_ID_MTL: - case PCI_DEVICE_ID_ARL: - return IVPU_HW_BTRS_MTL; - case PCI_DEVICE_ID_LNL: - case PCI_DEVICE_ID_PTL_P: - case PCI_DEVICE_ID_WCL: - case PCI_DEVICE_ID_NVL: - return IVPU_HW_BTRS_LNL; - default: - dump_stack(); - ivpu_err(vdev, "Unknown buttress generation\n"); - return 0; - } + return vdev->btrs_gen; } static inline struct ivpu_device *to_ivpu_device(struct drm_device *dev) diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c index f9ddaa2420c4..96f5be23a867 100644 --- a/drivers/accel/ivpu/ivpu_fw.c +++ b/drivers/accel/ivpu/ivpu_fw.c @@ -28,15 +28,15 @@ #define ADDR_TO_L2_CACHE_CFG(addr) ((addr) >> 31) /* Check if FW API is compatible with the driver */ -#define IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, name, min_major) \ - ivpu_fw_check_api(vdev, fw_hdr, #name, \ +#define IVPU_FW_CHECK_API_COMPAT(vdev, name, min_major) \ + ivpu_fw_check_api(vdev, #name, \ VPU_##name##_API_VER_INDEX, \ VPU_##name##_API_VER_MAJOR, \ VPU_##name##_API_VER_MINOR, min_major) /* Check if API version is lower that the given version */ -#define IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, name, major, minor) \ - ivpu_fw_check_api_ver_lt(vdev, fw_hdr, #name, VPU_##name##_API_VER_INDEX, major, minor) +#define IVPU_FW_CHECK_API_VER_LT(vdev, name, major, minor) \ + ivpu_fw_check_api_ver_lt(vdev, #name, VPU_##name##_API_VER_INDEX, major, minor) #define IVPU_FOCUS_PRESENT_TIMER_MS 1000 @@ -93,12 +93,11 @@ static int ivpu_fw_request(struct ivpu_device *vdev) } static int -ivpu_fw_check_api(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr, - const char *str, int index, u16 expected_major, u16 expected_minor, - u16 min_major) +ivpu_fw_check_api(struct ivpu_device *vdev, const char *str, int index, u16 expected_major, + u16 expected_minor, u16 min_major) { - u16 major = (u16)(fw_hdr->api_version[index] >> 16); - u16 minor = (u16)(fw_hdr->api_version[index]); + u16 major = (u16)(vdev->fw->api_version[index] >> 16); + u16 minor = (u16)(vdev->fw->api_version[index]); if (major < min_major) { ivpu_err(vdev, "Incompatible FW %s API version: %d.%d, required %d.0 or later\n", @@ -116,11 +115,10 @@ ivpu_fw_check_api(struct ivpu_device *vdev, const struct vpu_firmware_header *fw } static bool -ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr, - const char *str, int index, u16 major, u16 minor) +ivpu_fw_check_api_ver_lt(struct ivpu_device *vdev, const char *str, int index, u16 major, u16 minor) { - u16 fw_major = (u16)(fw_hdr->api_version[index] >> 16); - u16 fw_minor = (u16)(fw_hdr->api_version[index]); + u16 fw_major = (u16)(vdev->fw->api_version[index] >> 16); + u16 fw_minor = (u16)(vdev->fw->api_version[index]); if (fw_major < major || (fw_major == major && fw_minor < minor)) return true; @@ -142,7 +140,7 @@ bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range) } static u32 -ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_header *fw_hdr) +ivpu_fw_sched_mode_select(struct ivpu_device *vdev) { if (ivpu_hw_ip_gen(vdev) >= IVPU_HW_IP_60XX && ivpu_sched_mode == VPU_SCHEDULING_MODE_OS) { @@ -153,7 +151,7 @@ ivpu_fw_sched_mode_select(struct ivpu_device *vdev, const struct vpu_firmware_he if (ivpu_sched_mode != IVPU_SCHED_MODE_AUTO) return ivpu_sched_mode; - if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, JSM, 3, 24)) + if (IVPU_FW_CHECK_API_VER_LT(vdev, JSM, 3, 24)) return VPU_SCHEDULING_MODE_OS; return VPU_SCHEDULING_MODE_HW; @@ -302,9 +300,11 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) ivpu_info(vdev, "Firmware: %s, version: %s\n", fw->name, fw->version); - if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, BOOT, 3)) + memcpy(fw->api_version, fw_hdr->api_version, sizeof(fw->api_version)); + + if (IVPU_FW_CHECK_API_COMPAT(vdev, BOOT, 3)) return -EINVAL; - if (IVPU_FW_CHECK_API_COMPAT(vdev, fw_hdr, JSM, 3)) + if (IVPU_FW_CHECK_API_COMPAT(vdev, JSM, 3)) return -EINVAL; fw->boot_params_addr = boot_params_addr; @@ -324,7 +324,7 @@ static int ivpu_fw_parse(struct ivpu_device *vdev) fw->dvfs_mode = 0; - fw->sched_mode = ivpu_fw_sched_mode_select(vdev, fw_hdr); + fw->sched_mode = ivpu_fw_sched_mode_select(vdev); ivpu_info(vdev, "Scheduler mode: %s\n", fw->sched_mode ? "HW" : "OS"); ivpu_preemption_config_parse(vdev, fw_hdr); @@ -473,8 +473,6 @@ int ivpu_fw_init(struct ivpu_device *vdev) if (ret) goto err_fw_release; - ivpu_fw_load(vdev); - return 0; err_fw_release: @@ -488,7 +486,7 @@ void ivpu_fw_fini(struct ivpu_device *vdev) ivpu_fw_release(vdev); } -void ivpu_fw_load(struct ivpu_device *vdev) +int ivpu_fw_load(struct ivpu_device *vdev) { struct ivpu_fw_info *fw = vdev->fw; u64 image_end_offset = fw->image_load_offset + fw->image_size; @@ -505,6 +503,7 @@ void ivpu_fw_load(struct ivpu_device *vdev) } wmb(); /* Flush WC buffers after writing fw->mem */ + return 0; } static void ivpu_fw_boot_params_print(struct ivpu_device *vdev, struct vpu_boot_params *boot_params) diff --git a/drivers/accel/ivpu/ivpu_fw.h b/drivers/accel/ivpu/ivpu_fw.h index d3c410912c9c..45a705431f22 100644 --- a/drivers/accel/ivpu/ivpu_fw.h +++ b/drivers/accel/ivpu/ivpu_fw.h @@ -20,6 +20,7 @@ struct ivpu_fw_info { const struct firmware *file; const char *name; char version[FW_VERSION_STR_SIZE]; + u32 api_version[VPU_FW_API_VER_NUM]; struct ivpu_bo *mem_bp; struct ivpu_bo *mem_fw_ver; struct ivpu_bo *mem; @@ -54,7 +55,7 @@ struct ivpu_fw_info { bool ivpu_is_within_range(u64 addr, size_t size, struct ivpu_addr_range *range); int ivpu_fw_init(struct ivpu_device *vdev); void ivpu_fw_fini(struct ivpu_device *vdev); -void ivpu_fw_load(struct ivpu_device *vdev); +int ivpu_fw_load(struct ivpu_device *vdev); void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params *boot_params); static inline bool ivpu_fw_is_warm_boot(struct ivpu_device *vdev) diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c index 47ed9beeb3fb..86679d64fc14 100644 --- a/drivers/accel/ivpu/ivpu_hw.c +++ b/drivers/accel/ivpu/ivpu_hw.c @@ -67,9 +67,6 @@ static void wa_init(struct ivpu_device *vdev) vdev->wa.punit_disabled = false; vdev->wa.clear_runtime_mem = false; - if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) - vdev->wa.interrupt_clear_with_0 = ivpu_hw_btrs_irqs_clear_with_0_mtl(vdev); - if ((ivpu_device_id(vdev) == PCI_DEVICE_ID_LNL && ivpu_revision(vdev) < IVPU_HW_IP_REV_LNL_B0) || (ivpu_device_id(vdev) == PCI_DEVICE_ID_NVL && @@ -90,7 +87,6 @@ static void wa_init(struct ivpu_device *vdev) IVPU_PRINT_WA(punit_disabled); IVPU_PRINT_WA(clear_runtime_mem); - IVPU_PRINT_WA(interrupt_clear_with_0); IVPU_PRINT_WA(disable_clock_relinquish); IVPU_PRINT_WA(wp0_during_power_up); IVPU_PRINT_WA(disable_d0i2); @@ -98,7 +94,7 @@ static void wa_init(struct ivpu_device *vdev) static void timeouts_init(struct ivpu_device *vdev) { - if (ivpu_test_mode & IVPU_TEST_MODE_DISABLE_TIMEOUTS) { + if (ivpu_test_mode & (IVPU_TEST_MODE_DISABLE_TIMEOUTS | IVPU_TEST_MODE_FW_TEST)) { /* in milliseconds, negative value disables the timeout */ vdev->timeout.boot = -1; vdev->timeout.jsm = -1; @@ -378,11 +374,6 @@ void ivpu_hw_profiling_freq_drive(struct ivpu_device *vdev, bool enable) void ivpu_irq_handlers_init(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_37xx; - else - vdev->hw->irq.ip_irq_handler = ivpu_hw_ip_irq_handler_40xx; - if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) vdev->hw->irq.btrs_irq_handler = ivpu_hw_btrs_irq_handler_mtl; else diff --git a/drivers/accel/ivpu/ivpu_hw.h b/drivers/accel/ivpu/ivpu_hw.h index 487a918e2fa9..8c1b61df6cd4 100644 --- a/drivers/accel/ivpu/ivpu_hw.h +++ b/drivers/accel/ivpu/ivpu_hw.h @@ -18,7 +18,6 @@ struct ivpu_addr_range { struct ivpu_hw_info { struct { bool (*btrs_irq_handler)(struct ivpu_device *vdev, int irq); - bool (*ip_irq_handler)(struct ivpu_device *vdev, int irq); } irq; struct { struct ivpu_addr_range runtime; @@ -49,9 +48,9 @@ struct ivpu_hw_info { u32 tile_fuse; u32 sku; u16 config; - int dma_bits; ktime_t d0i3_entry_host_ts; u64 d0i3_entry_vpu_ts; + u32 btrs_irq_mask; atomic_t firewall_irq_counter; }; @@ -74,11 +73,6 @@ static inline u32 ivpu_hw_btrs_irq_handler(struct ivpu_device *vdev, int irq) return vdev->hw->irq.btrs_irq_handler(vdev, irq); } -static inline u32 ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq) -{ - return vdev->hw->irq.ip_irq_handler(vdev, irq); -} - static inline u64 ivpu_hw_range_size(const struct ivpu_addr_range *range) { return range->end - range->start; diff --git a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h b/drivers/accel/ivpu/ivpu_hw_40xx_reg.h deleted file mode 100644 index fc0ee8d637f9..000000000000 --- a/drivers/accel/ivpu/ivpu_hw_40xx_reg.h +++ /dev/null @@ -1,195 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2020-2023 Intel Corporation - */ - -#ifndef __IVPU_HW_40XX_REG_H__ -#define __IVPU_HW_40XX_REG_H__ - -#include <linux/bits.h> - -#define VPU_40XX_HOST_SS_CPR_CLK_EN 0x00000080u -#define VPU_40XX_HOST_SS_CPR_CLK_EN_TOP_NOC_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_CPR_CLK_EN_DSS_MAS_MASK BIT_MASK(10) -#define VPU_40XX_HOST_SS_CPR_CLK_EN_CSS_MAS_MASK BIT_MASK(11) - -#define VPU_40XX_HOST_SS_CPR_CLK_SET 0x00000084u -#define VPU_40XX_HOST_SS_CPR_CLK_SET_TOP_NOC_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_CPR_CLK_SET_DSS_MAS_MASK BIT_MASK(10) -#define VPU_40XX_HOST_SS_CPR_CLK_SET_MSS_MAS_MASK BIT_MASK(11) - -#define VPU_40XX_HOST_SS_CPR_RST_EN 0x00000090u -#define VPU_40XX_HOST_SS_CPR_RST_EN_TOP_NOC_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_CPR_RST_EN_DSS_MAS_MASK BIT_MASK(10) -#define VPU_40XX_HOST_SS_CPR_RST_EN_CSS_MAS_MASK BIT_MASK(11) - -#define VPU_40XX_HOST_SS_CPR_RST_SET 0x00000094u -#define VPU_40XX_HOST_SS_CPR_RST_SET_TOP_NOC_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_CPR_RST_SET_DSS_MAS_MASK BIT_MASK(10) -#define VPU_40XX_HOST_SS_CPR_RST_SET_MSS_MAS_MASK BIT_MASK(11) - -#define VPU_40XX_HOST_SS_CPR_RST_CLR 0x00000098u -#define VPU_40XX_HOST_SS_CPR_RST_CLR_TOP_NOC_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_CPR_RST_CLR_DSS_MAS_MASK BIT_MASK(10) -#define VPU_40XX_HOST_SS_CPR_RST_CLR_MSS_MAS_MASK BIT_MASK(11) - -#define VPU_40XX_HOST_SS_HW_VERSION 0x00000108u -#define VPU_40XX_HOST_SS_HW_VERSION_SOC_REVISION_MASK GENMASK(7, 0) -#define VPU_40XX_HOST_SS_HW_VERSION_SOC_NUMBER_MASK GENMASK(15, 8) -#define VPU_40XX_HOST_SS_HW_VERSION_VPU_GENERATION_MASK GENMASK(23, 16) - -#define VPU_40XX_HOST_SS_SW_VERSION 0x0000010cu - -#define VPU_40XX_HOST_SS_GEN_CTRL 0x00000118u -#define VPU_40XX_HOST_SS_GEN_CTRL_PS_MASK GENMASK(31, 29) - -#define VPU_40XX_HOST_SS_NOC_QREQN 0x00000154u -#define VPU_40XX_HOST_SS_NOC_QREQN_TOP_SOCMMIO_MASK BIT_MASK(0) - -#define VPU_40XX_HOST_SS_NOC_QACCEPTN 0x00000158u -#define VPU_40XX_HOST_SS_NOC_QACCEPTN_TOP_SOCMMIO_MASK BIT_MASK(0) - -#define VPU_40XX_HOST_SS_NOC_QDENY 0x0000015cu -#define VPU_40XX_HOST_SS_NOC_QDENY_TOP_SOCMMIO_MASK BIT_MASK(0) - -#define VPU_40XX_TOP_NOC_QREQN 0x00000160u -#define VPU_40XX_TOP_NOC_QREQN_CPU_CTRL_MASK BIT_MASK(0) -#define VPU_40XX_TOP_NOC_QREQN_HOSTIF_L2CACHE_MASK BIT_MASK(2) - -#define VPU_40XX_TOP_NOC_QACCEPTN 0x00000164u -#define VPU_40XX_TOP_NOC_QACCEPTN_CPU_CTRL_MASK BIT_MASK(0) -#define VPU_40XX_TOP_NOC_QACCEPTN_HOSTIF_L2CACHE_MASK BIT_MASK(2) - -#define VPU_40XX_TOP_NOC_QDENY 0x00000168u -#define VPU_40XX_TOP_NOC_QDENY_CPU_CTRL_MASK BIT_MASK(0) -#define VPU_40XX_TOP_NOC_QDENY_HOSTIF_L2CACHE_MASK BIT_MASK(2) - -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN 0x00000170u -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_CSS_ROM_CMX_MASK BIT_MASK(0) -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_CSS_DBG_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_CSS_CTRL_MASK BIT_MASK(2) -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_DEC400_MASK BIT_MASK(3) -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_MSS_NCE_MASK BIT_MASK(4) -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_MSS_MBI_MASK BIT_MASK(5) -#define VPU_40XX_HOST_SS_FW_SOC_IRQ_EN_MSS_MBI_CMX_MASK BIT_MASK(6) - -#define VPU_40XX_HOST_SS_ICB_STATUS_0 0x00010210u -#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_0_INT_MASK BIT_MASK(0) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_1_INT_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_2_INT_MASK BIT_MASK(2) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_TIMER_3_INT_MASK BIT_MASK(3) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_HOST_IPC_FIFO_INT_MASK BIT_MASK(4) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_0_INT_MASK BIT_MASK(5) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_1_INT_MASK BIT_MASK(6) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_MMU_IRQ_2_INT_MASK BIT_MASK(7) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_NOC_FIREWALL_INT_MASK BIT_MASK(8) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_CPU_INT_REDIRECT_0_INT_MASK BIT_MASK(30) -#define VPU_40XX_HOST_SS_ICB_STATUS_0_CPU_INT_REDIRECT_1_INT_MASK BIT_MASK(31) - -#define VPU_40XX_HOST_SS_ICB_STATUS_1 0x00010214u -#define VPU_40XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_2_INT_MASK BIT_MASK(0) -#define VPU_40XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_3_INT_MASK BIT_MASK(1) -#define VPU_40XX_HOST_SS_ICB_STATUS_1_CPU_INT_REDIRECT_4_INT_MASK BIT_MASK(2) - -#define VPU_40XX_HOST_SS_ICB_CLEAR_0 0x00010220u -#define VPU_40XX_HOST_SS_ICB_CLEAR_1 0x00010224u -#define VPU_40XX_HOST_SS_ICB_ENABLE_0 0x00010240u -#define VPU_40XX_HOST_SS_ICB_ENABLE_1 0x00010244u - -#define VPU_40XX_HOST_SS_TIM_IPC_FIFO_ATM 0x000200f4u - -#define VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT 0x000200fcu -#define VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT_FILL_LEVEL_MASK GENMASK(23, 16) - -#define VPU_40XX_HOST_SS_AON_PWR_ISO_EN0 0x00030020u -#define VPU_40XX_HOST_SS_AON_PWR_ISO_EN0_CSS_CPU_MASK BIT_MASK(3) - -#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0 0x00030024u -#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0_CSS_CPU_MASK BIT_MASK(3) - -#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0 0x00030028u -#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0_CSS_CPU_MASK BIT_MASK(3) - -#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_STATUS0 0x0003002cu -#define VPU_40XX_HOST_SS_AON_PWR_ISLAND_STATUS0_CSS_CPU_MASK BIT_MASK(3) - -#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY 0x00030068u -#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST_DLY_MASK GENMASK(7, 0) -#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST1_DLY_MASK GENMASK(15, 8) -#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST2_DLY_MASK GENMASK(23, 16) - -#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY 0x0003006cu -#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY_STATUS_DLY_MASK GENMASK(7, 0) - -#define VPU_40XX_HOST_SS_AON_IDLE_GEN 0x00030200u -#define VPU_40XX_HOST_SS_AON_IDLE_GEN_EN_MASK BIT_MASK(0) -#define VPU_40XX_HOST_SS_AON_IDLE_GEN_HW_PG_EN_MASK BIT_MASK(1) - -#define VPU_40XX_HOST_SS_AON_DPU_ACTIVE 0x00030204u -#define VPU_40XX_HOST_SS_AON_DPU_ACTIVE_DPU_ACTIVE_MASK BIT_MASK(0) - -#define VPU_50XX_HOST_SS_AON_FABRIC_REQ_OVERRIDE 0x00030210u -#define VPU_50XX_HOST_SS_AON_FABRIC_REQ_OVERRIDE_REQ_OVERRIDE_MASK BIT_MASK(0) - -#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO 0x00040040u -#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_DONE_MASK BIT_MASK(0) -#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IOSF_RS_ID_MASK GENMASK(2, 1) -#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IMAGE_LOCATION_MASK GENMASK(31, 3) - -#define VPU_40XX_HOST_SS_WORKPOINT_CONFIG_MIRROR 0x00082020u -#define VPU_40XX_HOST_SS_WORKPOINT_CONFIG_MIRROR_FINAL_PLL_FREQ_MASK GENMASK(15, 0) -#define VPU_40XX_HOST_SS_WORKPOINT_CONFIG_MIRROR_CONFIG_ID_MASK GENMASK(31, 16) - -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES 0x00360000u -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_CACHE_OVERRIDE_EN_MASK BIT_MASK(0) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AWCACHE_OVERRIDE_MASK BIT_MASK(1) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_ARCACHE_OVERRIDE_MASK BIT_MASK(2) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_SNOOP_OVERRIDE_EN_MASK BIT_MASK(3) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AW_SNOOP_OVERRIDE_MASK BIT_MASK(4) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AR_SNOOP_OVERRIDE_MASK BIT_MASK(5) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AW_CONTEXT_FLAG_MASK GENMASK(10, 6) -#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AR_CONTEXT_FLAG_MASK GENMASK(15, 11) - -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV 0x00360004u -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU0_AWMMUSSIDV_MASK BIT_MASK(0) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU0_ARMMUSSIDV_MASK BIT_MASK(1) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU1_AWMMUSSIDV_MASK BIT_MASK(2) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU1_ARMMUSSIDV_MASK BIT_MASK(3) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU2_AWMMUSSIDV_MASK BIT_MASK(4) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU2_ARMMUSSIDV_MASK BIT_MASK(5) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU3_AWMMUSSIDV_MASK BIT_MASK(6) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU3_ARMMUSSIDV_MASK BIT_MASK(7) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU4_AWMMUSSIDV_MASK BIT_MASK(8) -#define VPU_40XX_HOST_IF_TBU_MMUSSIDV_TBU4_ARMMUSSIDV_MASK BIT_MASK(9) - -#define VPU_40XX_CPU_SS_DSU_LEON_RT_BASE 0x04000000u -#define VPU_40XX_CPU_SS_DSU_LEON_RT_DSU_CTRL 0x04000000u -#define VPU_40XX_CPU_SS_DSU_LEON_RT_PC_REG 0x04400010u -#define VPU_40XX_CPU_SS_DSU_LEON_RT_NPC_REG 0x04400014u -#define VPU_40XX_CPU_SS_DSU_LEON_RT_DSU_TRAP_REG 0x04400020u - -#define VPU_40XX_CPU_SS_TIM_WATCHDOG 0x0102009cu -#define VPU_40XX_CPU_SS_TIM_WDOG_EN 0x010200a4u -#define VPU_40XX_CPU_SS_TIM_SAFE 0x010200a8u - -#define VPU_40XX_CPU_SS_TIM_GEN_CONFIG 0x01021008u -#define VPU_40XX_CPU_SS_TIM_GEN_CONFIG_WDOG_TO_INT_CLR_MASK BIT_MASK(9) - -#define VPU_40XX_CPU_SS_CPR_NOC_QREQN 0x01010030u -#define VPU_40XX_CPU_SS_CPR_NOC_QREQN_TOP_MMIO_MASK BIT_MASK(0) - -#define VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN 0x01010034u -#define VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN_TOP_MMIO_MASK BIT_MASK(0) - -#define VPU_40XX_CPU_SS_CPR_NOC_QDENY 0x01010038u -#define VPU_40XX_CPU_SS_CPR_NOC_QDENY_TOP_MMIO_MASK BIT_MASK(0) - -#define VPU_40XX_CPU_SS_TIM_IPC_FIFO 0x010200f0u -#define VPU_40XX_CPU_SS_TIM_PERF_EXT_FREE_CNT 0x01029008u - -#define VPU_40XX_CPU_SS_DOORBELL_0 0x01300000u -#define VPU_40XX_CPU_SS_DOORBELL_0_SET_MASK BIT_MASK(0) - -#define VPU_40XX_CPU_SS_DOORBELL_1 0x01301000u - -#endif /* __IVPU_HW_40XX_REG_H__ */ diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.c b/drivers/accel/ivpu/ivpu_hw_btrs.c index a17c829adb89..840983a618ca 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.c +++ b/drivers/accel/ivpu/ivpu_hw_btrs.c @@ -8,8 +8,7 @@ #include "ivpu_drv.h" #include "ivpu_hw.h" #include "ivpu_hw_btrs.h" -#include "ivpu_hw_btrs_lnl_reg.h" -#include "ivpu_hw_btrs_mtl_reg.h" +#include "ivpu_hw_btrs_reg.h" #include "ivpu_hw_reg_io.h" #include "ivpu_jsm_msg.h" #include "ivpu_pm.h" @@ -24,9 +23,6 @@ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, IMR1_ERR)) | \ (REG_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, SURV_ERR))) -#define BTRS_MTL_ALL_IRQ_MASK (BTRS_MTL_IRQ_MASK | (REG_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, \ - FREQ_CHANGE))) - #define BTRS_IRQ_DISABLE_MASK ((u32)-1) #define BTRS_LNL_ALL_IRQ_MASK ((u32)-1) @@ -49,9 +45,6 @@ #define BTRS_MTL_TILE_FUSE_ENABLE_BOTH 0x0 #define BTRS_MTL_TILE_SKU_BOTH 0x3630 -#define BTRS_LNL_TILE_MAX_NUM 6 -#define BTRS_LNL_TILE_MAX_MASK 0x3f - #define WEIGHTS_DEFAULT 0xf711f711u #define WEIGHTS_ATS_DEFAULT 0x0000f711u @@ -59,18 +52,6 @@ #define DCT_ENABLE 0x1 #define DCT_DISABLE 0x0 -int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev) -{ - REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, BTRS_MTL_ALL_IRQ_MASK); - if (REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) == BTRS_MTL_ALL_IRQ_MASK) { - /* Writing 1s does not clear the interrupt status register */ - REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, 0x0); - return true; - } - - return false; -} - static void freq_ratios_init_mtl(struct ivpu_device *vdev) { struct ivpu_hw_info *hw = vdev->hw; @@ -115,13 +96,9 @@ void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev) static bool tile_disable_check(u32 config) { - /* Allowed values: 0 or one bit from range 0-5 (6 tiles) */ if (config == 0) return true; - if (config > BIT(BTRS_LNL_TILE_MAX_NUM - 1)) - return false; - if ((config & (config - 1)) == 0) return true; @@ -156,6 +133,11 @@ static int info_init_mtl(struct ivpu_device *vdev) hw->tile_fuse = BTRS_MTL_TILE_FUSE_ENABLE_BOTH; hw->sku = BTRS_MTL_TILE_SKU_BOTH; hw->config = WP_CONFIG(MTL_CONFIG_2_TILE, MTL_PLL_RATIO_4_3); + hw->btrs_irq_mask = BTRS_MTL_IRQ_MASK; + + if (ivpu_test_mode & IVPU_TEST_MODE_FREQ_CHANGE_ENABLE) + hw->btrs_irq_mask = REG_SET_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, FREQ_CHANGE, + hw->btrs_irq_mask); return 0; } @@ -172,6 +154,11 @@ static int info_init_lnl(struct ivpu_device *vdev) hw->tile_fuse = tile_fuse_config; hw->pll.profiling_freq = PLL_PROFILING_FREQ_DEFAULT; + hw->btrs_irq_mask = BTRS_LNL_IRQ_MASK; + + if (ivpu_test_mode & IVPU_TEST_MODE_FREQ_CHANGE_ENABLE) + hw->btrs_irq_mask = REG_SET_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, FREQ_CHANGE, + hw->btrs_irq_mask); return 0; } @@ -698,7 +685,7 @@ int ivpu_hw_btrs_cfg_freq_init(struct ivpu_device *vdev) /* Handler for IRQs from Buttress core (irqB) */ bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq) { - u32 status = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_MTL_IRQ_MASK; + u32 status = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & vdev->hw->btrs_irq_mask; bool schedule_recovery = false; if (!status) @@ -729,14 +716,7 @@ bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq) } /* This must be done after interrupts are cleared at the source. */ - if (IVPU_WA(interrupt_clear_with_0)) - /* - * Writing 1 triggers an interrupt, so we can't perform read update write. - * Clear local interrupt status by writing 0 to all bits. - */ - REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, 0x0); - else - REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, status); + REGB_WR32(VPU_HW_BTRS_MTL_INTERRUPT_STAT, status); if (schedule_recovery) ivpu_pm_trigger_recovery(vdev, "Buttress IRQ"); @@ -747,7 +727,7 @@ bool ivpu_hw_btrs_irq_handler_mtl(struct ivpu_device *vdev, int irq) /* Handler for IRQs from Buttress core (irqB) */ bool ivpu_hw_btrs_irq_handler_lnl(struct ivpu_device *vdev, int irq) { - u32 status = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK; + u32 status = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & vdev->hw->btrs_irq_mask; bool schedule_recovery = false; if (!status) @@ -889,10 +869,10 @@ void ivpu_hw_btrs_global_int_enable(struct ivpu_device *vdev) void ivpu_hw_btrs_irq_enable(struct ivpu_device *vdev) { if (ivpu_hw_btrs_gen(vdev) == IVPU_HW_BTRS_MTL) { - REGB_WR32(VPU_HW_BTRS_MTL_LOCAL_INT_MASK, (u32)(~BTRS_MTL_IRQ_MASK)); + REGB_WR32(VPU_HW_BTRS_MTL_LOCAL_INT_MASK, (u32)(~vdev->hw->btrs_irq_mask)); REGB_WR32(VPU_HW_BTRS_MTL_GLOBAL_INT_MASK, 0x0); } else { - REGB_WR32(VPU_HW_BTRS_LNL_LOCAL_INT_MASK, (u32)(~BTRS_LNL_IRQ_MASK)); + REGB_WR32(VPU_HW_BTRS_LNL_LOCAL_INT_MASK, (u32)(~vdev->hw->btrs_irq_mask)); REGB_WR32(VPU_HW_BTRS_LNL_GLOBAL_INT_MASK, 0x0); } } @@ -910,7 +890,7 @@ void ivpu_hw_btrs_irq_disable(struct ivpu_device *vdev) static void diagnose_failure_mtl(struct ivpu_device *vdev) { - u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_MTL_IRQ_MASK; + u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & vdev->hw->btrs_irq_mask; if (REG_TEST_FLD(VPU_HW_BTRS_MTL_INTERRUPT_STAT, ATS_ERR, reg)) ivpu_err(vdev, "ATS_ERR irq 0x%016llx", REGB_RD64(VPU_HW_BTRS_MTL_ATS_ERR_LOG_0)); @@ -927,7 +907,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev) static void diagnose_failure_lnl(struct ivpu_device *vdev) { - u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK; + u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & vdev->hw->btrs_irq_mask; if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) { ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n", diff --git a/drivers/accel/ivpu/ivpu_hw_btrs.h b/drivers/accel/ivpu/ivpu_hw_btrs.h index d6343d734570..30c89ab8e4bd 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs.h +++ b/drivers/accel/ivpu/ivpu_hw_btrs.h @@ -7,8 +7,6 @@ #define __IVPU_HW_BTRS_H__ #include "ivpu_drv.h" -#include "ivpu_hw_37xx_reg.h" -#include "ivpu_hw_40xx_reg.h" #include "ivpu_hw_reg_io.h" #define PLL_PROFILING_FREQ_DEFAULT 38400000 @@ -19,7 +17,6 @@ int ivpu_hw_btrs_info_init(struct ivpu_device *vdev); void ivpu_hw_btrs_freq_ratios_init(struct ivpu_device *vdev); -int ivpu_hw_btrs_irqs_clear_with_0_mtl(struct ivpu_device *vdev); int ivpu_hw_btrs_wp_drive(struct ivpu_device *vdev, bool enable); int ivpu_hw_btrs_wait_for_clock_res_own_ack(struct ivpu_device *vdev); int ivpu_hw_btrs_d0i3_enable(struct ivpu_device *vdev); diff --git a/drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h b/drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h deleted file mode 100644 index e93d539e066f..000000000000 --- a/drivers/accel/ivpu/ivpu_hw_btrs_mtl_reg.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) 2020-2023 Intel Corporation - */ - -#ifndef __IVPU_HW_BTRS_MTL_REG_H__ -#define __IVPU_HW_BTRS_MTL_REG_H__ - -#include <linux/bits.h> - -#define VPU_HW_BTRS_MTL_INTERRUPT_TYPE 0x00000000u - -#define VPU_HW_BTRS_MTL_INTERRUPT_STAT 0x00000004u -#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_FREQ_CHANGE_MASK BIT_MASK(0) -#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_ATS_ERR_MASK BIT_MASK(1) -#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_UFI_ERR_MASK BIT_MASK(2) - -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0 0x00000008u -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0_MIN_RATIO_MASK GENMASK(15, 0) -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0_MAX_RATIO_MASK GENMASK(31, 16) - -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1 0x0000000cu -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1_TARGET_RATIO_MASK GENMASK(15, 0) -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1_EPP_MASK GENMASK(31, 16) - -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2 0x00000010u -#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2_CONFIG_MASK GENMASK(15, 0) - -#define VPU_HW_BTRS_MTL_WP_REQ_CMD 0x00000014u -#define VPU_HW_BTRS_MTL_WP_REQ_CMD_SEND_MASK BIT_MASK(0) - -#define VPU_HW_BTRS_MTL_WP_DOWNLOAD 0x00000018u -#define VPU_HW_BTRS_MTL_WP_DOWNLOAD_TARGET_RATIO_MASK GENMASK(15, 0) - -#define VPU_HW_BTRS_MTL_CURRENT_PLL 0x0000001cu -#define VPU_HW_BTRS_MTL_CURRENT_PLL_RATIO_MASK GENMASK(15, 0) - -#define VPU_HW_BTRS_MTL_PLL_ENABLE 0x00000020u - -#define VPU_HW_BTRS_MTL_FMIN_FUSE 0x00000024u -#define VPU_HW_BTRS_MTL_FMIN_FUSE_MIN_RATIO_MASK GENMASK(7, 0) -#define VPU_HW_BTRS_MTL_FMIN_FUSE_PN_RATIO_MASK GENMASK(15, 8) - -#define VPU_HW_BTRS_MTL_FMAX_FUSE 0x00000028u -#define VPU_HW_BTRS_MTL_FMAX_FUSE_MAX_RATIO_MASK GENMASK(7, 0) - -#define VPU_HW_BTRS_MTL_TILE_FUSE 0x0000002cu -#define VPU_HW_BTRS_MTL_TILE_FUSE_VALID_MASK BIT_MASK(0) -#define VPU_HW_BTRS_MTL_TILE_FUSE_SKU_MASK GENMASK(3, 2) - -#define VPU_HW_BTRS_MTL_LOCAL_INT_MASK 0x00000030u -#define VPU_HW_BTRS_MTL_GLOBAL_INT_MASK 0x00000034u - -#define VPU_HW_BTRS_MTL_PLL_STATUS 0x00000040u -#define VPU_HW_BTRS_MTL_PLL_STATUS_LOCK_MASK BIT_MASK(1) - -#define VPU_HW_BTRS_MTL_VPU_STATUS 0x00000044u -#define VPU_HW_BTRS_MTL_VPU_STATUS_READY_MASK BIT_MASK(0) -#define VPU_HW_BTRS_MTL_VPU_STATUS_IDLE_MASK BIT_MASK(1) - -#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL 0x00000060u -#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL_INPROGRESS_MASK BIT_MASK(0) -#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL_I3_MASK BIT_MASK(2) - -#define VPU_HW_BTRS_MTL_VPU_IP_RESET 0x00000050u -#define VPU_HW_BTRS_MTL_VPU_IP_RESET_TRIGGER_MASK BIT_MASK(0) - -#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_OFFSET 0x00000080u -#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_SIZE 0x00000084u -#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_ENABLE 0x00000088u - -#define VPU_HW_BTRS_MTL_ATS_ERR_LOG_0 0x000000a0u -#define VPU_HW_BTRS_MTL_ATS_ERR_LOG_1 0x000000a4u -#define VPU_HW_BTRS_MTL_ATS_ERR_CLEAR 0x000000a8u - -#define VPU_HW_BTRS_MTL_UFI_ERR_LOG 0x000000b0u -#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_CQ_ID_MASK GENMASK(11, 0) -#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_AXI_ID_MASK GENMASK(19, 12) -#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_OPCODE_MASK GENMASK(24, 20) - -#define VPU_HW_BTRS_MTL_UFI_ERR_CLEAR 0x000000b4u - -#endif /* __IVPU_HW_BTRS_MTL_REG_H__ */ diff --git a/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h b/drivers/accel/ivpu/ivpu_hw_btrs_reg.h index a81a9ba540fa..b9628bd9cb56 100644 --- a/drivers/accel/ivpu/ivpu_hw_btrs_lnl_reg.h +++ b/drivers/accel/ivpu/ivpu_hw_btrs_reg.h @@ -1,13 +1,85 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2020-2024 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ -#ifndef __IVPU_HW_BTRS_LNL_REG_H__ -#define __IVPU_HW_BTRS_LNL_REG_H__ +#ifndef __IVPU_HW_BTRS_REG_H__ +#define __IVPU_HW_BTRS_REG_H__ #include <linux/bits.h> +#define VPU_HW_BTRS_MTL_INTERRUPT_TYPE 0x00000000u + +#define VPU_HW_BTRS_MTL_INTERRUPT_STAT 0x00000004u +#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_FREQ_CHANGE_MASK BIT_MASK(0) +#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_ATS_ERR_MASK BIT_MASK(1) +#define VPU_HW_BTRS_MTL_INTERRUPT_STAT_UFI_ERR_MASK BIT_MASK(2) + +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0 0x00000008u +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0_MIN_RATIO_MASK GENMASK(15, 0) +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD0_MAX_RATIO_MASK GENMASK(31, 16) + +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1 0x0000000cu +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1_TARGET_RATIO_MASK GENMASK(15, 0) +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD1_EPP_MASK GENMASK(31, 16) + +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2 0x00000010u +#define VPU_HW_BTRS_MTL_WP_REQ_PAYLOAD2_CONFIG_MASK GENMASK(15, 0) + +#define VPU_HW_BTRS_MTL_WP_REQ_CMD 0x00000014u +#define VPU_HW_BTRS_MTL_WP_REQ_CMD_SEND_MASK BIT_MASK(0) + +#define VPU_HW_BTRS_MTL_WP_DOWNLOAD 0x00000018u +#define VPU_HW_BTRS_MTL_WP_DOWNLOAD_TARGET_RATIO_MASK GENMASK(15, 0) + +#define VPU_HW_BTRS_MTL_CURRENT_PLL 0x0000001cu +#define VPU_HW_BTRS_MTL_CURRENT_PLL_RATIO_MASK GENMASK(15, 0) + +#define VPU_HW_BTRS_MTL_PLL_ENABLE 0x00000020u + +#define VPU_HW_BTRS_MTL_FMIN_FUSE 0x00000024u +#define VPU_HW_BTRS_MTL_FMIN_FUSE_MIN_RATIO_MASK GENMASK(7, 0) +#define VPU_HW_BTRS_MTL_FMIN_FUSE_PN_RATIO_MASK GENMASK(15, 8) + +#define VPU_HW_BTRS_MTL_FMAX_FUSE 0x00000028u +#define VPU_HW_BTRS_MTL_FMAX_FUSE_MAX_RATIO_MASK GENMASK(7, 0) + +#define VPU_HW_BTRS_MTL_TILE_FUSE 0x0000002cu +#define VPU_HW_BTRS_MTL_TILE_FUSE_VALID_MASK BIT_MASK(0) +#define VPU_HW_BTRS_MTL_TILE_FUSE_SKU_MASK GENMASK(3, 2) + +#define VPU_HW_BTRS_MTL_LOCAL_INT_MASK 0x00000030u +#define VPU_HW_BTRS_MTL_GLOBAL_INT_MASK 0x00000034u + +#define VPU_HW_BTRS_MTL_PLL_STATUS 0x00000040u +#define VPU_HW_BTRS_MTL_PLL_STATUS_LOCK_MASK BIT_MASK(1) + +#define VPU_HW_BTRS_MTL_VPU_STATUS 0x00000044u +#define VPU_HW_BTRS_MTL_VPU_STATUS_READY_MASK BIT_MASK(0) +#define VPU_HW_BTRS_MTL_VPU_STATUS_IDLE_MASK BIT_MASK(1) + +#define VPU_HW_BTRS_MTL_VPU_IP_RESET 0x00000050u +#define VPU_HW_BTRS_MTL_VPU_IP_RESET_TRIGGER_MASK BIT_MASK(0) + +#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL 0x00000060u +#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL_INPROGRESS_MASK BIT_MASK(0) +#define VPU_HW_BTRS_MTL_VPU_D0I3_CONTROL_I3_MASK BIT_MASK(2) + +#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_OFFSET 0x00000080u +#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_SIZE 0x00000084u +#define VPU_HW_BTRS_MTL_VPU_TELEMETRY_ENABLE 0x00000088u + +#define VPU_HW_BTRS_MTL_ATS_ERR_LOG_0 0x000000a0u +#define VPU_HW_BTRS_MTL_ATS_ERR_LOG_1 0x000000a4u +#define VPU_HW_BTRS_MTL_ATS_ERR_CLEAR 0x000000a8u + +#define VPU_HW_BTRS_MTL_UFI_ERR_LOG 0x000000b0u +#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_CQ_ID_MASK GENMASK(11, 0) +#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_AXI_ID_MASK GENMASK(19, 12) +#define VPU_HW_BTRS_MTL_UFI_ERR_LOG_OPCODE_MASK GENMASK(24, 20) + +#define VPU_HW_BTRS_MTL_UFI_ERR_CLEAR 0x000000b4u + #define VPU_HW_BTRS_LNL_INTERRUPT_STAT 0x00000000u #define VPU_HW_BTRS_LNL_INTERRUPT_STAT_FREQ_CHANGE_MASK BIT_MASK(0) #define VPU_HW_BTRS_LNL_INTERRUPT_STAT_ATS_ERR_MASK BIT_MASK(1) @@ -79,12 +151,12 @@ #define VPU_HW_BTRS_LNL_TILE_FUSE 0x00000150u #define VPU_HW_BTRS_LNL_TILE_FUSE_VALID_MASK BIT_MASK(0) -#define VPU_HW_BTRS_LNL_TILE_FUSE_CONFIG_MASK GENMASK(6, 1) +#define VPU_HW_BTRS_LNL_TILE_FUSE_CONFIG_MASK GENMASK(7, 1) #define VPU_HW_BTRS_LNL_VPU_STATUS 0x00000154u #define VPU_HW_BTRS_LNL_VPU_STATUS_READY_MASK BIT_MASK(0) #define VPU_HW_BTRS_LNL_VPU_STATUS_IDLE_MASK BIT_MASK(1) -#define VPU_HW_BTRS_LNL_VPU_STATUS_DUP_IDLE_MASK BIT_MASK(2) +#define VPU_HW_BTRS_LNL_VPU_STATUS_DPU_IDLE_MASK BIT_MASK(2) #define VPU_HW_BTRS_LNL_VPU_STATUS_CLOCK_RESOURCE_OWN_ACK_MASK BIT_MASK(6) #define VPU_HW_BTRS_LNL_VPU_STATUS_POWER_RESOURCE_OWN_ACK_MASK BIT_MASK(7) #define VPU_HW_BTRS_LNL_VPU_STATUS_PERF_CLK_MASK BIT_MASK(11) @@ -109,4 +181,4 @@ #define VPU_HW_BTRS_LNL_FMAX_FUSE 0x00000178u #define VPU_HW_BTRS_LNL_FMAX_FUSE_MAX_RATIO_MASK GENMASK(7, 0) -#endif /* __IVPU_HW_BTRS_LNL_REG_H__ */ +#endif /* __IVPU_HW_BTRS_REG_H__ */ diff --git a/drivers/accel/ivpu/ivpu_hw_ip.c b/drivers/accel/ivpu/ivpu_hw_ip.c index 46661fbf9d05..47f4f9e17d8a 100644 --- a/drivers/accel/ivpu/ivpu_hw_ip.c +++ b/drivers/accel/ivpu/ivpu_hw_ip.c @@ -7,10 +7,9 @@ #include "ivpu_fw.h" #include "ivpu_gem.h" #include "ivpu_hw.h" -#include "ivpu_hw_37xx_reg.h" -#include "ivpu_hw_40xx_reg.h" #include "ivpu_hw_btrs.h" #include "ivpu_hw_ip.h" +#include "ivpu_hw_ip_reg.h" #include "ivpu_hw_reg_io.h" #include "ivpu_mmu.h" #include "ivpu_pm.h" @@ -18,49 +17,27 @@ #define TIM_SAFE_ENABLE 0xf1d0dead #define TIM_WATCHDOG_RESET_VALUE 0xffffffff -#define ICB_0_IRQ_MASK_37XX ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT))) - -#define ICB_1_IRQ_MASK_37XX ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_2_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_3_INT)) | \ - (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_4_INT))) - -#define ICB_0_1_IRQ_MASK_37XX ((((u64)ICB_1_IRQ_MASK_37XX) << 32) | ICB_0_IRQ_MASK_37XX) - -#define ICB_0_IRQ_MASK_40XX ((REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT))) - -#define ICB_1_IRQ_MASK_40XX ((REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_2_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_3_INT)) | \ - (REG_FLD(VPU_40XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_4_INT))) - -#define ICB_0_1_IRQ_MASK_40XX ((((u64)ICB_1_IRQ_MASK_40XX) << 32) | ICB_0_IRQ_MASK_40XX) - -#define ITF_FIREWALL_VIOLATION_MASK_37XX ((REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_ROM_CMX)) | \ - (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_DBG)) | \ - (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_CTRL)) | \ - (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, DEC400)) | \ - (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_NCE)) | \ - (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI)) | \ - (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI_CMX))) - -#define ITF_FIREWALL_VIOLATION_MASK_40XX ((REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, CSS_ROM_CMX)) | \ - (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, CSS_DBG)) | \ - (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, CSS_CTRL)) | \ - (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, DEC400)) | \ - (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, MSS_NCE)) | \ - (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI)) | \ - (REG_FLD(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI_CMX))) +#define ICB_0_IRQ_MASK ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT))) + +#define ICB_1_IRQ_MASK ((REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_2_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_3_INT)) | \ + (REG_FLD(VPU_37XX_HOST_SS_ICB_STATUS_1, CPU_INT_REDIRECT_4_INT))) + +#define ICB_0_1_IRQ_MASK ((((u64)ICB_1_IRQ_MASK) << 32) | ICB_0_IRQ_MASK) + +#define ITF_FIREWALL_VIOLATION_MASK ((REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_ROM_CMX)) | \ + (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_DBG)) | \ + (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, CSS_CTRL)) | \ + (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, DEC400)) | \ + (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_NCE)) | \ + (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI)) | \ + (REG_FLD(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, MSS_MBI_CMX))) static int wait_for_ip_bar(struct ivpu_device *vdev) { @@ -78,7 +55,7 @@ static void host_ss_rst_clr(struct ivpu_device *vdev) REGV_WR32(VPU_37XX_HOST_SS_CPR_RST_CLR, val); } -static int host_ss_noc_qreqn_check_37xx(struct ivpu_device *vdev, u32 exp_val) +static int host_ss_noc_qreqn_check(struct ivpu_device *vdev, u32 exp_val) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QREQN); @@ -88,25 +65,7 @@ static int host_ss_noc_qreqn_check_37xx(struct ivpu_device *vdev, u32 exp_val) return 0; } -static int host_ss_noc_qreqn_check_40xx(struct ivpu_device *vdev, u32 exp_val) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QREQN); - - if (!REG_TEST_FLD_NUM(VPU_40XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, exp_val, val)) - return -EIO; - - return 0; -} - -static int host_ss_noc_qreqn_check(struct ivpu_device *vdev, u32 exp_val) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - return host_ss_noc_qreqn_check_37xx(vdev, exp_val); - else - return host_ss_noc_qreqn_check_40xx(vdev, exp_val); -} - -static int host_ss_noc_qacceptn_check_37xx(struct ivpu_device *vdev, u32 exp_val) +static int host_ss_noc_qacceptn_check(struct ivpu_device *vdev, u32 exp_val) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QACCEPTN); @@ -116,25 +75,7 @@ static int host_ss_noc_qacceptn_check_37xx(struct ivpu_device *vdev, u32 exp_val return 0; } -static int host_ss_noc_qacceptn_check_40xx(struct ivpu_device *vdev, u32 exp_val) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QACCEPTN); - - if (!REG_TEST_FLD_NUM(VPU_40XX_HOST_SS_NOC_QACCEPTN, TOP_SOCMMIO, exp_val, val)) - return -EIO; - - return 0; -} - -static int host_ss_noc_qacceptn_check(struct ivpu_device *vdev, u32 exp_val) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - return host_ss_noc_qacceptn_check_37xx(vdev, exp_val); - else - return host_ss_noc_qacceptn_check_40xx(vdev, exp_val); -} - -static int host_ss_noc_qdeny_check_37xx(struct ivpu_device *vdev, u32 exp_val) +static int host_ss_noc_qdeny_check(struct ivpu_device *vdev, u32 exp_val) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QDENY); @@ -144,24 +85,6 @@ static int host_ss_noc_qdeny_check_37xx(struct ivpu_device *vdev, u32 exp_val) return 0; } -static int host_ss_noc_qdeny_check_40xx(struct ivpu_device *vdev, u32 exp_val) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QDENY); - - if (!REG_TEST_FLD_NUM(VPU_40XX_HOST_SS_NOC_QDENY, TOP_SOCMMIO, exp_val, val)) - return -EIO; - - return 0; -} - -static int host_ss_noc_qdeny_check(struct ivpu_device *vdev, u32 exp_val) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - return host_ss_noc_qdeny_check_37xx(vdev, exp_val); - else - return host_ss_noc_qdeny_check_40xx(vdev, exp_val); -} - static int top_noc_qrenqn_check_37xx(struct ivpu_device *vdev, u32 exp_val) { u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QREQN); @@ -175,9 +98,9 @@ static int top_noc_qrenqn_check_37xx(struct ivpu_device *vdev, u32 exp_val) static int top_noc_qrenqn_check_40xx(struct ivpu_device *vdev, u32 exp_val) { - u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QREQN); + u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QREQN); - if (!REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QREQN, CPU_CTRL, exp_val, val) || + if (!REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QREQN, CPU_CTRL, exp_val, val) || !REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, exp_val, val)) return -EIO; @@ -224,7 +147,7 @@ int ivpu_hw_ip_host_ss_configure(struct ivpu_device *vdev) return ret; } -static void idle_gen_drive_37xx(struct ivpu_device *vdev, bool enable) +static void idle_gen_drive(struct ivpu_device *vdev, bool enable) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN); @@ -236,75 +159,59 @@ static void idle_gen_drive_37xx(struct ivpu_device *vdev, bool enable) REGV_WR32(VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN, val); } -static void idle_gen_drive_40xx(struct ivpu_device *vdev, bool enable) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_IDLE_GEN); - - if (enable) - val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_IDLE_GEN, EN, val); - else - val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_IDLE_GEN, EN, val); - - REGV_WR32(VPU_40XX_HOST_SS_AON_IDLE_GEN, val); -} - void ivpu_hw_ip_idle_gen_enable(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - idle_gen_drive_37xx(vdev, true); - else - idle_gen_drive_40xx(vdev, true); + idle_gen_drive(vdev, true); } void ivpu_hw_ip_idle_gen_disable(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - idle_gen_drive_37xx(vdev, false); - else - idle_gen_drive_40xx(vdev, false); + idle_gen_drive(vdev, false); } static void -pwr_island_delay_set_50xx(struct ivpu_device *vdev, u32 post, u32 post1, u32 post2, u32 status) +pwr_island_delay_write(struct ivpu_device *vdev, u32 post, u32 post1, u32 post2, u32 status) { u32 val; - val = REGV_RD32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY); - val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST_DLY, post, val); + val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST_DLY, post, 0); val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST1_DLY, post1, val); val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, POST2_DLY, post2, val); REGV_WR32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY, val); - val = REGV_RD32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY); - val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY, STATUS_DLY, status, val); + val = REG_SET_FLD_NUM(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY, STATUS_DLY, status, 0); REGV_WR32(VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY, val); } -static void pwr_island_trickle_drive_37xx(struct ivpu_device *vdev, bool enable) +static void pwr_island_delay_set_50xx(struct ivpu_device *vdev) { - u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0); - - if (enable) - val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, MSS_CPU, val); + if (vdev->hw->pll.profiling_freq == PLL_PROFILING_FREQ_HIGH) + pwr_island_delay_write(vdev, 18, 0, 0, 46); else - val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, MSS_CPU, val); + pwr_island_delay_write(vdev, 0, 0, 0, 3); +} - REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, val); +static void pwr_island_delay_set_60xx(struct ivpu_device *vdev) +{ + if (vdev->hw->pll.profiling_freq == PLL_PROFILING_FREQ_HIGH) + pwr_island_delay_write(vdev, 198, 0, 198, 0); + else + pwr_island_delay_write(vdev, 17, 0, 17, 0); } -static void pwr_island_trickle_drive_40xx(struct ivpu_device *vdev, bool enable) +static void pwr_island_trickle_drive(struct ivpu_device *vdev, bool enable) { - u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0); + u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0); if (enable) - val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, CSS_CPU, val); + val = REG_SET_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, MSS_CPU, val); else - val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, CSS_CPU, val); + val = REG_CLR_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, MSS_CPU, val); - REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, val); + REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_TRICKLE_EN0, val); } -static void pwr_island_drive_37xx(struct ivpu_device *vdev, bool enable) +static void pwr_island_drive(struct ivpu_device *vdev, bool enable) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISLAND_EN0); @@ -316,29 +223,11 @@ static void pwr_island_drive_37xx(struct ivpu_device *vdev, bool enable) 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_40XX_HOST_SS_AON_PWR_ISLAND_EN0); - - if (enable) - val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val); - else - val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, CSS_CPU, val); - - REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISLAND_EN0, val); -} - static void pwr_island_enable(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { - pwr_island_trickle_drive_37xx(vdev, true); - ndelay(500); - pwr_island_drive_37xx(vdev, true); - } else { - pwr_island_trickle_drive_40xx(vdev, true); - ndelay(500); - pwr_island_drive_40xx(vdev, true); - } + pwr_island_trickle_drive(vdev, true); + ndelay(500); + pwr_island_drive(vdev, true); } static int wait_for_pwr_island_status(struct ivpu_device *vdev, u32 exp_val) @@ -346,15 +235,11 @@ static int wait_for_pwr_island_status(struct ivpu_device *vdev, u32 exp_val) if (IVPU_WA(punit_disabled)) return 0; - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - return REGV_POLL_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0, MSS_CPU, exp_val, - vdev->timeout.pwr_island_status); - else - return REGV_POLL_FLD(VPU_40XX_HOST_SS_AON_PWR_ISLAND_STATUS0, CSS_CPU, exp_val, - vdev->timeout.pwr_island_status); + return REGV_POLL_FLD(VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0, MSS_CPU, exp_val, + vdev->timeout.pwr_island_status); } -static void pwr_island_isolation_drive_37xx(struct ivpu_device *vdev, bool enable) +static void pwr_island_isolation_drive(struct ivpu_device *vdev, bool enable) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_AON_PWR_ISO_EN0); @@ -366,26 +251,6 @@ static void pwr_island_isolation_drive_37xx(struct ivpu_device *vdev, bool enabl REGV_WR32(VPU_37XX_HOST_SS_AON_PWR_ISO_EN0, val); } -static void pwr_island_isolation_drive_40xx(struct ivpu_device *vdev, bool enable) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0); - - if (enable) - val = REG_SET_FLD(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0, CSS_CPU, val); - else - val = REG_CLR_FLD(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0, CSS_CPU, val); - - REGV_WR32(VPU_40XX_HOST_SS_AON_PWR_ISO_EN0, val); -} - -static void pwr_island_isolation_drive(struct ivpu_device *vdev, bool enable) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - pwr_island_isolation_drive_37xx(vdev, enable); - else - pwr_island_isolation_drive_40xx(vdev, enable); -} - static void pwr_island_isolation_disable(struct ivpu_device *vdev) { pwr_island_isolation_drive(vdev, false); @@ -485,7 +350,7 @@ static void host_ss_rst_enable(struct ivpu_device *vdev) host_ss_rst_drive(vdev, true); } -static void host_ss_noc_qreqn_top_socmmio_drive_37xx(struct ivpu_device *vdev, bool enable) +static void host_ss_noc_qreqn_top_socmmio_drive(struct ivpu_device *vdev, bool enable) { u32 val = REGV_RD32(VPU_37XX_HOST_SS_NOC_QREQN); @@ -496,25 +361,6 @@ static void host_ss_noc_qreqn_top_socmmio_drive_37xx(struct ivpu_device *vdev, b REGV_WR32(VPU_37XX_HOST_SS_NOC_QREQN, val); } -static void host_ss_noc_qreqn_top_socmmio_drive_40xx(struct ivpu_device *vdev, bool enable) -{ - u32 val = REGV_RD32(VPU_40XX_HOST_SS_NOC_QREQN); - - if (enable) - val = REG_SET_FLD(VPU_40XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, val); - else - val = REG_CLR_FLD(VPU_40XX_HOST_SS_NOC_QREQN, TOP_SOCMMIO, val); - REGV_WR32(VPU_40XX_HOST_SS_NOC_QREQN, val); -} - -static void host_ss_noc_qreqn_top_socmmio_drive(struct ivpu_device *vdev, bool enable) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - host_ss_noc_qreqn_top_socmmio_drive_37xx(vdev, enable); - else - host_ss_noc_qreqn_top_socmmio_drive_40xx(vdev, enable); -} - static int host_ss_axi_drive(struct ivpu_device *vdev, bool enable) { int ret; @@ -536,17 +382,17 @@ static int host_ss_axi_drive(struct ivpu_device *vdev, bool enable) static void top_noc_qreqn_drive_40xx(struct ivpu_device *vdev, bool enable) { - u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QREQN); + u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QREQN); if (enable) { - val = REG_SET_FLD(VPU_40XX_TOP_NOC_QREQN, CPU_CTRL, val); + val = REG_SET_FLD(VPU_37XX_TOP_NOC_QREQN, CPU_CTRL, val); val = REG_SET_FLD(VPU_40XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, val); } else { - val = REG_CLR_FLD(VPU_40XX_TOP_NOC_QREQN, CPU_CTRL, val); + val = REG_CLR_FLD(VPU_37XX_TOP_NOC_QREQN, CPU_CTRL, val); val = REG_CLR_FLD(VPU_40XX_TOP_NOC_QREQN, HOSTIF_L2CACHE, val); } - REGV_WR32(VPU_40XX_TOP_NOC_QREQN, val); + REGV_WR32(VPU_37XX_TOP_NOC_QREQN, val); } static void top_noc_qreqn_drive_37xx(struct ivpu_device *vdev, bool enable) @@ -590,9 +436,9 @@ static int top_noc_qacceptn_check_37xx(struct ivpu_device *vdev, u32 exp_val) static int top_noc_qacceptn_check_40xx(struct ivpu_device *vdev, u32 exp_val) { - u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QACCEPTN); + u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QACCEPTN); - if (!REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QACCEPTN, CPU_CTRL, exp_val, val) || + if (!REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QACCEPTN, CPU_CTRL, exp_val, val) || !REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QACCEPTN, HOSTIF_L2CACHE, exp_val, val)) return -EIO; @@ -620,9 +466,9 @@ static int top_noc_qdeny_check_37xx(struct ivpu_device *vdev, u32 exp_val) static int top_noc_qdeny_check_40xx(struct ivpu_device *vdev, u32 exp_val) { - u32 val = REGV_RD32(VPU_40XX_TOP_NOC_QDENY); + u32 val = REGV_RD32(VPU_37XX_TOP_NOC_QDENY); - if (!REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QDENY, CPU_CTRL, exp_val, val) || + if (!REG_TEST_FLD_NUM(VPU_37XX_TOP_NOC_QDENY, CPU_CTRL, exp_val, val) || !REG_TEST_FLD_NUM(VPU_40XX_TOP_NOC_QDENY, HOSTIF_L2CACHE, exp_val, val)) return -EIO; @@ -675,35 +521,19 @@ static void dpu_active_drive_37xx(struct ivpu_device *vdev, bool enable) static void pwr_island_delay_set(struct ivpu_device *vdev) { - bool high = vdev->hw->pll.profiling_freq == PLL_PROFILING_FREQ_HIGH; - u32 post, post1, post2, status; + switch (ivpu_hw_ip_gen(vdev)) { + case IVPU_HW_IP_37XX: + case IVPU_HW_IP_40XX: + return; - if (ivpu_hw_ip_gen(vdev) < IVPU_HW_IP_50XX) + case IVPU_HW_IP_50XX: + pwr_island_delay_set_50xx(vdev); return; - switch (ivpu_device_id(vdev)) { - case PCI_DEVICE_ID_WCL: - case PCI_DEVICE_ID_PTL_P: - post = high ? 18 : 0; - post1 = 0; - post2 = 0; - status = high ? 46 : 3; - break; - - case PCI_DEVICE_ID_NVL: - post = high ? 198 : 17; - post1 = 0; - post2 = high ? 198 : 17; - status = 0; - break; - - default: - dump_stack(); - ivpu_err(vdev, "Unknown device ID\n"); + case IVPU_HW_IP_60XX: + pwr_island_delay_set_60xx(vdev); return; } - - pwr_island_delay_set_50xx(vdev, post, post1, post2, status); } int ivpu_hw_ip_pwr_domain_enable(struct ivpu_device *vdev) @@ -760,7 +590,7 @@ static void ivpu_hw_ip_snoop_disable_37xx(struct ivpu_device *vdev) static void ivpu_hw_ip_snoop_disable_40xx(struct ivpu_device *vdev) { - u32 val = REGV_RD32(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES); + u32 val = REGV_RD32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES); val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, SNOOP_OVERRIDE_EN, val); val = REG_SET_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AW_SNOOP_OVERRIDE, val); @@ -770,7 +600,7 @@ static void ivpu_hw_ip_snoop_disable_40xx(struct ivpu_device *vdev) else val = REG_CLR_FLD(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, AR_SNOOP_OVERRIDE, val); - REGV_WR32(VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES, val); + REGV_WR32(VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES, val); } void ivpu_hw_ip_snoop_disable(struct ivpu_device *vdev) @@ -795,24 +625,29 @@ static void ivpu_hw_ip_tbu_mmu_enable_37xx(struct ivpu_device *vdev) static void ivpu_hw_ip_tbu_mmu_enable_40xx(struct ivpu_device *vdev) { - u32 val = REGV_RD32(VPU_40XX_HOST_IF_TBU_MMUSSIDV); + u32 val = REGV_RD32(VPU_37XX_HOST_IF_TBU_MMUSSIDV); - val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val); - val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val); - val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU1_AWMMUSSIDV, val); - val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU1_ARMMUSSIDV, val); - val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val); - val = REG_SET_FLD(VPU_40XX_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val); + val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU0_AWMMUSSIDV, val); + val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU0_ARMMUSSIDV, val); + val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU1_AWMMUSSIDV, val); + val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU1_ARMMUSSIDV, val); + val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU2_AWMMUSSIDV, val); + val = REG_SET_FLD(VPU_37XX_HOST_IF_TBU_MMUSSIDV, TBU2_ARMMUSSIDV, val); - REGV_WR32(VPU_40XX_HOST_IF_TBU_MMUSSIDV, val); + REGV_WR32(VPU_37XX_HOST_IF_TBU_MMUSSIDV, val); } void ivpu_hw_ip_tbu_mmu_enable(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) + switch (ivpu_hw_ip_gen(vdev)) { + case IVPU_HW_IP_37XX: return ivpu_hw_ip_tbu_mmu_enable_37xx(vdev); - else + + case IVPU_HW_IP_40XX: + case IVPU_HW_IP_50XX: + case IVPU_HW_IP_60XX: return ivpu_hw_ip_tbu_mmu_enable_40xx(vdev); + } } static inline u64 get_entry_point_addr(struct ivpu_device *vdev) @@ -936,29 +771,22 @@ static int soc_cpu_boot_60xx(struct ivpu_device *vdev) int ivpu_hw_ip_soc_cpu_boot(struct ivpu_device *vdev) { - int ret; + ivpu_dbg(vdev, PM, "Booting firmware, mode: %s\n", + ivpu_fw_is_warm_boot(vdev) ? "warm boot" : "cold boot"); switch (ivpu_hw_ip_gen(vdev)) { case IVPU_HW_IP_37XX: - ret = soc_cpu_boot_37xx(vdev); - break; + return soc_cpu_boot_37xx(vdev); case IVPU_HW_IP_40XX: case IVPU_HW_IP_50XX: - ret = soc_cpu_boot_40xx(vdev); - break; + return soc_cpu_boot_40xx(vdev); - default: - ret = soc_cpu_boot_60xx(vdev); + case IVPU_HW_IP_60XX: + return soc_cpu_boot_60xx(vdev); } - if (ret) - return ret; - - ivpu_dbg(vdev, PM, "Booting firmware, mode: %s\n", - ivpu_fw_is_warm_boot(vdev) ? "warm boot" : "cold boot"); - - return 0; + return -EINVAL; } static void wdt_disable_37xx(struct ivpu_device *vdev) @@ -1002,55 +830,30 @@ void ivpu_hw_ip_wdt_disable(struct ivpu_device *vdev) return wdt_disable_40xx(vdev); } -static u32 ipc_rx_count_get_37xx(struct ivpu_device *vdev) +u32 ivpu_hw_ip_ipc_rx_count_get(struct ivpu_device *vdev) { u32 count = readl(vdev->regv + VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT); return REG_GET_FLD(VPU_37XX_HOST_SS_TIM_IPC_FIFO_STAT, FILL_LEVEL, count); } -static u32 ipc_rx_count_get_40xx(struct ivpu_device *vdev) -{ - u32 count = readl(vdev->regv + VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT); - - return REG_GET_FLD(VPU_40XX_HOST_SS_TIM_IPC_FIFO_STAT, FILL_LEVEL, count); -} - -u32 ivpu_hw_ip_ipc_rx_count_get(struct ivpu_device *vdev) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - return ipc_rx_count_get_37xx(vdev); - else - return ipc_rx_count_get_40xx(vdev); -} - void ivpu_hw_ip_irq_enable(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { - REGV_WR32(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, ITF_FIREWALL_VIOLATION_MASK_37XX); - REGV_WR64(VPU_37XX_HOST_SS_ICB_ENABLE_0, ICB_0_1_IRQ_MASK_37XX); - } else { - REGV_WR32(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, ITF_FIREWALL_VIOLATION_MASK_40XX); - REGV_WR64(VPU_40XX_HOST_SS_ICB_ENABLE_0, ICB_0_1_IRQ_MASK_40XX); - } + REGV_WR32(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, ITF_FIREWALL_VIOLATION_MASK); + REGV_WR64(VPU_37XX_HOST_SS_ICB_ENABLE_0, ICB_0_1_IRQ_MASK); } void ivpu_hw_ip_irq_disable(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) { - REGV_WR64(VPU_37XX_HOST_SS_ICB_ENABLE_0, 0x0ull); - REGV_WR32(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, 0x0); - } else { - REGV_WR64(VPU_40XX_HOST_SS_ICB_ENABLE_0, 0x0ull); - REGV_WR32(VPU_40XX_HOST_SS_FW_SOC_IRQ_EN, 0x0ul); - } + REGV_WR64(VPU_37XX_HOST_SS_ICB_ENABLE_0, 0x0ull); + REGV_WR32(VPU_37XX_HOST_SS_FW_SOC_IRQ_EN, 0x0); } -static void diagnose_failure_37xx(struct ivpu_device *vdev) +void ivpu_hw_ip_diagnose_failure(struct ivpu_device *vdev) { - u32 reg = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_37XX; + u32 reg = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK; - if (ipc_rx_count_get_37xx(vdev)) + if (ivpu_hw_ip_ipc_rx_count_get(vdev)) ivpu_err(vdev, "IPC FIFO queue not empty, missed IPC IRQ"); if (REG_TEST_FLD(VPU_37XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, reg)) @@ -1063,37 +866,9 @@ static void diagnose_failure_37xx(struct ivpu_device *vdev) ivpu_err(vdev, "NOC Firewall irq detected\n"); } -static void diagnose_failure_40xx(struct ivpu_device *vdev) -{ - u32 reg = REGV_RD32(VPU_40XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_40XX; - - if (ipc_rx_count_get_40xx(vdev)) - ivpu_err(vdev, "IPC FIFO queue not empty, missed IPC IRQ"); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, reg)) - ivpu_err(vdev, "WDT MSS timeout detected\n"); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT, reg)) - ivpu_err(vdev, "WDT NCE timeout detected\n"); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, reg)) - ivpu_err(vdev, "NOC Firewall irq detected\n"); -} - -void ivpu_hw_ip_diagnose_failure(struct ivpu_device *vdev) -{ - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - diagnose_failure_37xx(vdev); - else - diagnose_failure_40xx(vdev); -} - void ivpu_hw_ip_irq_clear(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - REGV_WR64(VPU_37XX_HOST_SS_ICB_CLEAR_0, ICB_0_1_IRQ_MASK_37XX); - else - REGV_WR64(VPU_40XX_HOST_SS_ICB_CLEAR_0, ICB_0_1_IRQ_MASK_40XX); + REGV_WR64(VPU_37XX_HOST_SS_ICB_CLEAR_0, ICB_0_1_IRQ_MASK); } static void irq_wdt_nce_handler(struct ivpu_device *vdev) @@ -1116,9 +891,9 @@ static void irq_noc_firewall_handler(struct ivpu_device *vdev) } /* Handler for IRQs from NPU core */ -bool ivpu_hw_ip_irq_handler_37xx(struct ivpu_device *vdev, int irq) +bool ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq) { - u32 status = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_37XX; + u32 status = REGV_RD32(VPU_37XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK; if (!status) return false; @@ -1149,40 +924,6 @@ bool ivpu_hw_ip_irq_handler_37xx(struct ivpu_device *vdev, int irq) return true; } -/* Handler for IRQs from NPU core */ -bool ivpu_hw_ip_irq_handler_40xx(struct ivpu_device *vdev, int irq) -{ - u32 status = REGV_RD32(VPU_40XX_HOST_SS_ICB_STATUS_0) & ICB_0_IRQ_MASK_40XX; - - if (!status) - return false; - - REGV_WR32(VPU_40XX_HOST_SS_ICB_CLEAR_0, status); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_0_INT, status)) - ivpu_mmu_irq_evtq_handler(vdev); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, HOST_IPC_FIFO_INT, status)) - ivpu_ipc_irq_handler(vdev); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_1_INT, status)) - ivpu_dbg(vdev, IRQ, "MMU sync complete\n"); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, MMU_IRQ_2_INT, status)) - ivpu_mmu_irq_gerr_handler(vdev); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_0_INT, status)) - irq_wdt_mss_handler(vdev); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, CPU_INT_REDIRECT_1_INT, status)) - irq_wdt_nce_handler(vdev); - - if (REG_TEST_FLD(VPU_40XX_HOST_SS_ICB_STATUS_0, NOC_FIREWALL_INT, status)) - irq_noc_firewall_handler(vdev); - - return true; -} - static void db_set_37xx(struct ivpu_device *vdev, u32 db_id) { u32 reg_stride = VPU_37XX_CPU_SS_DOORBELL_1 - VPU_37XX_CPU_SS_DOORBELL_0; @@ -1209,10 +950,7 @@ void ivpu_hw_ip_db_set(struct ivpu_device *vdev, u32 db_id) u32 ivpu_hw_ip_ipc_rx_addr_get(struct ivpu_device *vdev) { - if (ivpu_hw_ip_gen(vdev) == IVPU_HW_IP_37XX) - return REGV_RD32(VPU_37XX_HOST_SS_TIM_IPC_FIFO_ATM); - else - return REGV_RD32(VPU_40XX_HOST_SS_TIM_IPC_FIFO_ATM); + return REGV_RD32(VPU_37XX_HOST_SS_TIM_IPC_FIFO_ATM); } void ivpu_hw_ip_ipc_tx_set(struct ivpu_device *vdev, u32 vpu_addr) diff --git a/drivers/accel/ivpu/ivpu_hw_ip.h b/drivers/accel/ivpu/ivpu_hw_ip.h index dbbcdd10a5f8..59ef904c08da 100644 --- a/drivers/accel/ivpu/ivpu_hw_ip.h +++ b/drivers/accel/ivpu/ivpu_hw_ip.h @@ -22,8 +22,7 @@ void ivpu_hw_ip_wdt_disable(struct ivpu_device *vdev); void ivpu_hw_ip_diagnose_failure(struct ivpu_device *vdev); u32 ivpu_hw_ip_ipc_rx_count_get(struct ivpu_device *vdev); void ivpu_hw_ip_irq_clear(struct ivpu_device *vdev); -bool ivpu_hw_ip_irq_handler_37xx(struct ivpu_device *vdev, int irq); -bool ivpu_hw_ip_irq_handler_40xx(struct ivpu_device *vdev, int irq); +bool ivpu_hw_ip_irq_handler(struct ivpu_device *vdev, int irq); void ivpu_hw_ip_db_set(struct ivpu_device *vdev, u32 db_id); u32 ivpu_hw_ip_ipc_rx_addr_get(struct ivpu_device *vdev); void ivpu_hw_ip_ipc_tx_set(struct ivpu_device *vdev, u32 vpu_addr); diff --git a/drivers/accel/ivpu/ivpu_hw_37xx_reg.h b/drivers/accel/ivpu/ivpu_hw_ip_reg.h index cf5e2f01049c..126df1012f23 100644 --- a/drivers/accel/ivpu/ivpu_hw_37xx_reg.h +++ b/drivers/accel/ivpu/ivpu_hw_ip_reg.h @@ -1,18 +1,28 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (C) 2020-2023 Intel Corporation + * Copyright (C) 2020-2026 Intel Corporation */ -#ifndef __IVPU_HW_37XX_REG_H__ -#define __IVPU_HW_37XX_REG_H__ +#ifndef __IVPU_HW_IP_REG_H__ +#define __IVPU_HW_IP_REG_H__ #include <linux/bits.h> +#define VPU_40XX_HOST_SS_CPR_CLK_EN 0x00000080u +#define VPU_40XX_HOST_SS_CPR_CLK_EN_TOP_NOC_MASK BIT_MASK(1) +#define VPU_40XX_HOST_SS_CPR_CLK_EN_DSS_MAS_MASK BIT_MASK(10) +#define VPU_40XX_HOST_SS_CPR_CLK_EN_CSS_MAS_MASK BIT_MASK(11) + #define VPU_37XX_HOST_SS_CPR_CLK_SET 0x00000084u #define VPU_37XX_HOST_SS_CPR_CLK_SET_TOP_NOC_MASK BIT_MASK(1) #define VPU_37XX_HOST_SS_CPR_CLK_SET_DSS_MAS_MASK BIT_MASK(10) #define VPU_37XX_HOST_SS_CPR_CLK_SET_MSS_MAS_MASK BIT_MASK(11) +#define VPU_40XX_HOST_SS_CPR_RST_EN 0x00000090u +#define VPU_40XX_HOST_SS_CPR_RST_EN_TOP_NOC_MASK BIT_MASK(1) +#define VPU_40XX_HOST_SS_CPR_RST_EN_DSS_MAS_MASK BIT_MASK(10) +#define VPU_40XX_HOST_SS_CPR_RST_EN_CSS_MAS_MASK BIT_MASK(11) + #define VPU_37XX_HOST_SS_CPR_RST_SET 0x00000094u #define VPU_37XX_HOST_SS_CPR_RST_SET_TOP_NOC_MASK BIT_MASK(1) #define VPU_37XX_HOST_SS_CPR_RST_SET_DSS_MAS_MASK BIT_MASK(10) @@ -29,6 +39,8 @@ #define VPU_37XX_HOST_SS_HW_VERSION_SOC_NUMBER_MASK GENMASK(15, 8) #define VPU_37XX_HOST_SS_HW_VERSION_VPU_GENERATION_MASK GENMASK(23, 16) +#define VPU_40XX_HOST_SS_SW_VERSION 0x0000010cu + #define VPU_37XX_HOST_SS_GEN_CTRL 0x00000118u #define VPU_37XX_HOST_SS_GEN_CTRL_PS_MASK GENMASK(31, 29) @@ -44,14 +56,17 @@ #define VPU_37XX_TOP_NOC_QREQN 0x00000160u #define VPU_37XX_TOP_NOC_QREQN_CPU_CTRL_MASK BIT_MASK(0) #define VPU_37XX_TOP_NOC_QREQN_HOSTIF_L2CACHE_MASK BIT_MASK(1) +#define VPU_40XX_TOP_NOC_QREQN_HOSTIF_L2CACHE_MASK BIT_MASK(2) #define VPU_37XX_TOP_NOC_QACCEPTN 0x00000164u #define VPU_37XX_TOP_NOC_QACCEPTN_CPU_CTRL_MASK BIT_MASK(0) #define VPU_37XX_TOP_NOC_QACCEPTN_HOSTIF_L2CACHE_MASK BIT_MASK(1) +#define VPU_40XX_TOP_NOC_QACCEPTN_HOSTIF_L2CACHE_MASK BIT_MASK(2) #define VPU_37XX_TOP_NOC_QDENY 0x00000168u #define VPU_37XX_TOP_NOC_QDENY_CPU_CTRL_MASK BIT_MASK(0) #define VPU_37XX_TOP_NOC_QDENY_HOSTIF_L2CACHE_MASK BIT_MASK(1) +#define VPU_40XX_TOP_NOC_QDENY_HOSTIF_L2CACHE_MASK BIT_MASK(2) #define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN 0x00000170u #define VPU_37XX_HOST_SS_FW_SOC_IRQ_EN_CSS_ROM_CMX_MASK BIT_MASK(0) @@ -83,6 +98,7 @@ #define VPU_37XX_HOST_SS_ICB_CLEAR_0 0x00010220u #define VPU_37XX_HOST_SS_ICB_CLEAR_1 0x00010224u #define VPU_37XX_HOST_SS_ICB_ENABLE_0 0x00010240u +#define VPU_40XX_HOST_SS_ICB_ENABLE_1 0x00010244u #define VPU_37XX_HOST_SS_TIM_IPC_FIFO_ATM 0x000200f4u @@ -104,12 +120,29 @@ #define VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0 0x0003002cu #define VPU_37XX_HOST_SS_AON_PWR_ISLAND_STATUS0_MSS_CPU_MASK BIT_MASK(3) +#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY 0x00030068u +#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST_DLY_MASK GENMASK(7, 0) +#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST1_DLY_MASK GENMASK(15, 8) +#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_EN_POST_DLY_POST2_DLY_MASK GENMASK(23, 16) + +#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY 0x0003006cu +#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY_STATUS_DLY_MASK GENMASK(7, 0) + #define VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN 0x00030200u #define VPU_37XX_HOST_SS_AON_VPU_IDLE_GEN_EN_MASK BIT_MASK(0) +#define VPU_40XX_HOST_SS_AON_VPU_IDLE_GEN_HW_PG_EN_MASK BIT_MASK(1) #define VPU_37XX_HOST_SS_AON_DPU_ACTIVE 0x00030204u #define VPU_37XX_HOST_SS_AON_DPU_ACTIVE_DPU_ACTIVE_MASK BIT_MASK(0) +#define VPU_50XX_HOST_SS_AON_FABRIC_REQ_OVERRIDE 0x00030210u +#define VPU_50XX_HOST_SS_AON_FABRIC_REQ_OVERRIDE_REQ_OVERRIDE_MASK BIT_MASK(0) + +#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO 0x00040040u +#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_DONE_MASK BIT_MASK(0) +#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IOSF_RS_ID_MASK GENMASK(2, 1) +#define VPU_40XX_HOST_SS_VERIFICATION_ADDRESS_LO_IMAGE_LOCATION_MASK GENMASK(31, 3) + #define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO 0x00041040u #define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO_DONE_MASK BIT_MASK(0) #define VPU_37XX_HOST_SS_LOADING_ADDRESS_LO_IOSF_RS_ID_MASK GENMASK(2, 1) @@ -126,6 +159,9 @@ #define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_NOSNOOP_OVERRIDE_EN_MASK BIT_MASK(3) #define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_AW_NOSNOOP_OVERRIDE_MASK BIT_MASK(4) #define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_AR_NOSNOOP_OVERRIDE_MASK BIT_MASK(5) +#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_SNOOP_OVERRIDE_EN_MASK BIT_MASK(3) +#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AW_SNOOP_OVERRIDE_MASK BIT_MASK(4) +#define VPU_40XX_HOST_IF_TCU_PTW_OVERRIDES_AR_SNOOP_OVERRIDE_MASK BIT_MASK(5) #define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AW_CONTEXT_FLAG_MASK GENMASK(10, 6) #define VPU_37XX_HOST_IF_TCU_PTW_OVERRIDES_PTW_AR_CONTEXT_FLAG_MASK GENMASK(15, 11) @@ -141,6 +177,17 @@ #define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU4_AWMMUSSIDV_MASK BIT_MASK(8) #define VPU_37XX_HOST_IF_TBU_MMUSSIDV_TBU4_ARMMUSSIDV_MASK BIT_MASK(9) +#define VPU_40XX_CPU_SS_CPR_NOC_QREQN 0x01010030u +#define VPU_40XX_CPU_SS_CPR_NOC_QREQN_TOP_MMIO_MASK BIT_MASK(0) + +#define VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN 0x01010034u +#define VPU_40XX_CPU_SS_CPR_NOC_QACCEPTN_TOP_MMIO_MASK BIT_MASK(0) + +#define VPU_40XX_CPU_SS_CPR_NOC_QDENY 0x01010038u +#define VPU_40XX_CPU_SS_CPR_NOC_QDENY_TOP_MMIO_MASK BIT_MASK(0) + +#define VPU_40XX_CPU_SS_TIM_PERF_EXT_FREE_CNT 0x01029008u + #define VPU_37XX_CPU_SS_DSU_LEON_RT_BASE 0x04000000u #define VPU_37XX_CPU_SS_DSU_LEON_RT_DSU_CTRL 0x04000000u #define VPU_37XX_CPU_SS_DSU_LEON_RT_PC_REG 0x04400010u @@ -161,18 +208,32 @@ #define VPU_37XX_CPU_SS_MSSCPU_CPR_LEON_RT_VEC_IRQI_RSTVEC_MASK GENMASK(31, 4) #define VPU_37XX_CPU_SS_TIM_WATCHDOG 0x0602009cu +#define VPU_40XX_CPU_SS_TIM_WATCHDOG 0x0102009cu + #define VPU_37XX_CPU_SS_TIM_WDOG_EN 0x060200a4u +#define VPU_40XX_CPU_SS_TIM_WDOG_EN 0x010200a4u + #define VPU_37XX_CPU_SS_TIM_SAFE 0x060200a8u +#define VPU_40XX_CPU_SS_TIM_SAFE 0x010200a8u + #define VPU_37XX_CPU_SS_TIM_IPC_FIFO 0x060200f0u +#define VPU_40XX_CPU_SS_TIM_IPC_FIFO 0x010200f0u #define VPU_37XX_CPU_SS_TIM_GEN_CONFIG 0x06021008u #define VPU_37XX_CPU_SS_TIM_GEN_CONFIG_WDOG_TO_INT_CLR_MASK BIT_MASK(9) +#define VPU_40XX_CPU_SS_TIM_GEN_CONFIG 0x01021008u +#define VPU_40XX_CPU_SS_TIM_GEN_CONFIG_WDOG_TO_INT_CLR_MASK BIT_MASK(9) + #define VPU_37XX_CPU_SS_TIM_PERF_FREE_CNT 0x06029000u #define VPU_37XX_CPU_SS_DOORBELL_0 0x06300000u #define VPU_37XX_CPU_SS_DOORBELL_0_SET_MASK BIT_MASK(0) +#define VPU_40XX_CPU_SS_DOORBELL_0 0x01300000u +#define VPU_40XX_CPU_SS_DOORBELL_0_SET_MASK BIT_MASK(0) + #define VPU_37XX_CPU_SS_DOORBELL_1 0x06301000u +#define VPU_40XX_CPU_SS_DOORBELL_1 0x01301000u -#endif /* __IVPU_HW_37XX_REG_H__ */ +#endif /* __IVPU_HW_IP_REG_H__ */ diff --git a/drivers/accel/ivpu/ivpu_pm.c b/drivers/accel/ivpu/ivpu_pm.c index de0becbfdffb..aa813262253e 100644 --- a/drivers/accel/ivpu/ivpu_pm.c +++ b/drivers/accel/ivpu/ivpu_pm.c @@ -46,7 +46,6 @@ static void ivpu_pm_prepare_cold_boot(struct ivpu_device *vdev) ivpu_cmdq_reset_all_contexts(vdev); ivpu_ipc_reset(vdev); ivpu_fw_log_reset(vdev); - ivpu_fw_load(vdev); fw->last_heartbeat = 0; ivpu_dbg(vdev, FW_BOOT, "Cold boot entry point 0x%llx", vdev->fw->cold_boot_entry_point); diff --git a/drivers/accel/qaic/mhi_controller.c b/drivers/accel/qaic/mhi_controller.c index 40e6d262ef21..c720b70b1e0f 100644 --- a/drivers/accel/qaic/mhi_controller.c +++ b/drivers/accel/qaic/mhi_controller.c @@ -23,6 +23,7 @@ MODULE_PARM_DESC(mhi_timeout_ms, "MHI controller timeout value"); static const char *fw_image_paths[FAMILY_MAX] = { [FAMILY_AIC100] = "qcom/aic100/sbl.bin", [FAMILY_AIC200] = "qcom/aic200/sbl.bin", + [FAMILY_AIC200_VF] = NULL, }; static const struct mhi_channel_config aic100_channels[] = { @@ -691,6 +692,159 @@ static const struct mhi_channel_config aic200_channels[] = { }, }; +static const struct mhi_channel_config aic200vf_channels[] = { + { + .name = "QAIC_LOOPBACK", + .num = 0, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_LOOPBACK", + .num = 1, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_SSR", + .num = 6, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_SSR", + .num = 7, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_CONTROL", + .num = 10, + .num_elements = 128, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_CONTROL", + .num = 11, + .num_elements = 128, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_TELEMETRY", + .num = 16, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "QAIC_TELEMETRY", + .num = 17, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "IPCR", + .num = 24, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, + { + .name = "IPCR", + .num = 25, + .num_elements = 32, + .local_elements = 0, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = MHI_CH_EE_AMSS, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .wake_capable = false, + }, +}; + static struct mhi_event_config aic100_events[] = { { .num_elements = 32, @@ -721,6 +875,21 @@ static struct mhi_event_config aic200_events[] = { }, }; +static struct mhi_event_config aic200vf_events[] = { + { + .num_elements = 32, + .irq_moderation_ms = 0, + .irq = 0, + .channel = U32_MAX, + .priority = 1, + .mode = MHI_DB_BRST_DISABLE, + .data_type = MHI_ER_CTRL, + .hardware_event = false, + .client_managed = false, + .offload_channel = false, + }, +}; + static struct mhi_controller_config mhi_cntrl_configs[] = { [FAMILY_AIC100] = { .max_channels = 128, @@ -744,6 +913,17 @@ static struct mhi_controller_config mhi_cntrl_configs[] = { .use_bounce_buf = false, .m2_no_db = false, }, + [FAMILY_AIC200_VF] = { + .max_channels = 128, + .timeout_ms = 0, /* controlled by mhi_timeout */ + .buf_len = 0, + .num_channels = ARRAY_SIZE(aic200vf_channels), + .ch_cfg = aic200vf_channels, + .num_events = ARRAY_SIZE(aic200vf_events), + .event_cfg = aic200vf_events, + .use_bounce_buf = false, + .m2_no_db = false, + }, }; static int mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *addr, u32 *out) @@ -864,11 +1044,19 @@ struct mhi_controller *qaic_mhi_register_controller(struct pci_dev *pci_dev, voi mhi_cntrl->fw_image = fw_image_paths[family]; - if (family == FAMILY_AIC200) { + switch (family) { + case FAMILY_AIC200: mhi_cntrl->name = "AIC200"; mhi_cntrl->seg_len = SZ_512K; - } else { + break; + case FAMILY_AIC200_VF: + mhi_cntrl->name = "AIC200_VF"; + break; + case FAMILY_AIC100: mhi_cntrl->name = "AIC100"; + break; + default: + mhi_cntrl->name = NULL; } /* use latest configured timeout */ diff --git a/drivers/accel/qaic/qaic.h b/drivers/accel/qaic/qaic.h index 02ca99000e2a..889fa51da7da 100644 --- a/drivers/accel/qaic/qaic.h +++ b/drivers/accel/qaic/qaic.h @@ -35,6 +35,7 @@ enum aic_families { FAMILY_AIC100, FAMILY_AIC200, + FAMILY_AIC200_VF, FAMILY_MAX, }; diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index 4d55531bf1c9..ee35e13091b3 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -1840,15 +1840,16 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file goto free_ent; } bo = to_qaic_bo(obj); + ret = mutex_lock_interruptible(&bo->lock); + if (ret) + goto put_obj; if (!bo->sliced) { - drm_gem_object_put(obj); ret = -EINVAL; - goto free_ent; + goto unlock_bo; } if (bo->dbc->id != args->hdr.dbc_id) { - drm_gem_object_put(obj); ret = -EINVAL; - goto free_ent; + goto unlock_bo; } /* * perf stats ioctl is called before wait ioctl is complete then @@ -1864,7 +1865,12 @@ int qaic_perf_stats_bo_ioctl(struct drm_device *dev, void *data, struct drm_file bo->perf_stats.req_received_ts), 1000); ent[i].queue_level_before = bo->perf_stats.queue_level_before; ent[i].num_queue_element = bo->total_slice_nents; +unlock_bo: + mutex_unlock(&bo->lock); +put_obj: drm_gem_object_put(obj); + if (ret) + goto free_ent; } if (copy_to_user(u64_to_user_ptr(args->data), ent, args->hdr.count * sizeof(*ent))) diff --git a/drivers/accel/qaic/qaic_drv.c b/drivers/accel/qaic/qaic_drv.c index c1bb6db78d12..9e1e184efe0d 100644 --- a/drivers/accel/qaic/qaic_drv.c +++ b/drivers/accel/qaic/qaic_drv.c @@ -40,6 +40,7 @@ MODULE_IMPORT_NS("DMA_BUF"); #define PCI_DEVICE_ID_QCOM_AIC080 0xa080 #define PCI_DEVICE_ID_QCOM_AIC100 0xa100 #define PCI_DEVICE_ID_QCOM_AIC200 0xa110 +#define PCI_DEVICE_ID_QCOM_AIC200VF 0xa111 #define QAIC_NAME "qaic" #define QAIC_DESC "Qualcomm Cloud AI Accelerators" #define CNTL_MAJOR 5 @@ -78,6 +79,13 @@ static const struct qaic_device_config aic200_config = { .dbc_bar_idx = 2, }; +static const struct qaic_device_config aic200vf_config = { + .family = FAMILY_AIC200_VF, + .bar_mask = BIT(0) | BIT(1) | BIT(2) | BIT(4), + .mhi_bar_idx = 1, + .dbc_bar_idx = 2, +}; + bool datapath_polling; module_param(datapath_polling, bool, 0400); MODULE_PARM_DESC(datapath_polling, "Operate the datapath in polling mode"); @@ -610,6 +618,8 @@ static void qaic_pci_remove(struct pci_dev *pdev) { struct qaic_device *qdev = pci_get_drvdata(pdev); + pci_disable_sriov(pdev); + if (!qdev) return; @@ -666,6 +676,7 @@ static const struct pci_device_id qaic_ids[] = { { PCI_DEVICE_DATA(QCOM, AIC080, (kernel_ulong_t)&aic080_config), }, { PCI_DEVICE_DATA(QCOM, AIC100, (kernel_ulong_t)&aic100_config), }, { PCI_DEVICE_DATA(QCOM, AIC200, (kernel_ulong_t)&aic200_config), }, + { PCI_DEVICE_DATA(QCOM, AIC200VF, (kernel_ulong_t)&aic200vf_config), }, { } }; MODULE_DEVICE_TABLE(pci, qaic_ids); @@ -762,6 +773,41 @@ static const struct dev_pm_ops qaic_pm_ops = { SYSTEM_SLEEP_PM_OPS(qaic_pm_suspend, qaic_pm_resume) }; +static int qaic_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) +{ + struct qaic_device *qdev = pci_get_drvdata(pdev); + int rcu_id; + int ret; + + if (num_vfs == 0) { + pci_disable_sriov(pdev); + return 0; + } + + rcu_id = srcu_read_lock(&qdev->dev_lock); + /* Qaic device must be online to process VF bringup */ + if (qdev->dev_state == QAIC_OFFLINE) { + ret = -ENODEV; + goto unlock_dev_srcu; + } + if (qdev->dev_state == QAIC_BOOT) { + ret = -EBUSY; + goto unlock_dev_srcu; + } + + ret = pci_enable_sriov(pdev, num_vfs); + if (ret) { + dev_err(&pdev->dev, "Failed to enable SR-IOV: %d (requested %d VFs, max %d)\n", + ret, num_vfs, pci_sriov_get_totalvfs(pdev)); + goto unlock_dev_srcu; + } + + dev_dbg(&pdev->dev, "Successfully enabled %d VFs\n", num_vfs); +unlock_dev_srcu: + srcu_read_unlock(&qdev->dev_lock, rcu_id); + return ret ?: num_vfs; +} + static struct pci_driver qaic_pci_driver = { .name = QAIC_NAME, .id_table = qaic_ids, @@ -769,6 +815,7 @@ static struct pci_driver qaic_pci_driver = { .remove = qaic_pci_remove, .shutdown = qaic_pci_shutdown, .err_handler = &qaic_pci_err_handler, + .sriov_configure = qaic_pci_sriov_configure, .driver = { .pm = pm_sleep_ptr(&qaic_pm_ops), }, diff --git a/drivers/accel/qaic/sahara.c b/drivers/accel/qaic/sahara.c index 8476be9b9148..e951181e424f 100644 --- a/drivers/accel/qaic/sahara.c +++ b/drivers/accel/qaic/sahara.c @@ -881,6 +881,7 @@ static void sahara_mhi_remove(struct mhi_device *mhi_dev) cancel_work_sync(&context->fw_work); cancel_work_sync(&context->dump_work); + cancel_work_sync(&context->read_data_work); vfree(context->mem_dump); sahara_release_image(context); mhi_unprepare_from_transfer(mhi_dev); diff --git a/drivers/accel/rocket/rocket_job.c b/drivers/accel/rocket/rocket_job.c index f40435505818..ce994748dd5d 100644 --- a/drivers/accel/rocket/rocket_job.c +++ b/drivers/accel/rocket/rocket_job.c @@ -7,6 +7,7 @@ #include <drm/drm_file.h> #include <drm/drm_gem.h> #include <drm/rocket_accel.h> +#include <linux/align.h> #include <linux/interrupt.h> #include <linux/overflow.h> #include <linux/iommu.h> @@ -21,6 +22,15 @@ #define JOB_TIMEOUT_MS 500 +/* + * The PC unit fetches two 64-bit register commands per pc_data_amount unit, + * and the field holds (regcmd_count + 1) / 2 - 1. + */ +#define ROCKET_MAX_REGCMDS ((PC_REGISTER_AMOUNTS_PC_DATA_AMOUNT__MASK + 1) * 2U) + +/* Bits 3:0 of PC_BASE_ADDRESS hold the mode selection bit and reserved bits */ +#define ROCKET_REGCMD_ALIGN 16 + static struct rocket_job * to_rocket_job(struct drm_sched_job *sched_job) { @@ -78,7 +88,7 @@ rocket_copy_tasks(struct drm_device *dev, return -ENOMEM; } - for (int i = 0; i < rjob->task_count; i++) { + for (unsigned int i = 0; i < rjob->task_count; i++) { struct drm_rocket_task task = {0}; if (copy_from_user(&task, @@ -95,6 +105,20 @@ rocket_copy_tasks(struct drm_device *dev, goto fail; } + if (task.regcmd_count > ROCKET_MAX_REGCMDS) { + drm_dbg(dev, "regcmd_count field in drm_rocket_task should be <= %u.\n", + ROCKET_MAX_REGCMDS); + ret = -EINVAL; + goto fail; + } + + if (!IS_ALIGNED(task.regcmd, ROCKET_REGCMD_ALIGN)) { + drm_dbg(dev, "regcmd field in drm_rocket_task should be aligned to %u bytes.\n", + ROCKET_REGCMD_ALIGN); + ret = -EINVAL; + goto fail; + } + rjob->tasks[i].regcmd = task.regcmd; rjob->tasks[i].regcmd_count = task.regcmd_count; } @@ -494,8 +518,6 @@ int rocket_job_init(struct rocket_core *core) return 0; err_sched: - drm_sched_fini(&core->sched); - destroy_workqueue(core->reset.wq); return ret; } diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c index 4d9d313b460c..c5353b36f0e4 100644 --- a/drivers/dma-buf/st-dma-fence-unwrap.c +++ b/drivers/dma-buf/st-dma-fence-unwrap.c @@ -370,6 +370,15 @@ error_put_f1: dma_fence_put(f1); } +static void test_dedup_empty(struct kunit *test) +{ + struct dma_fence *fence[1] = { NULL }; + int ret; + + ret = dma_fence_dedup_array(fence, 0); + KUNIT_EXPECT_EQ(test, ret, 0); +} + static void test_unwrap_merge_order(struct kunit *test) { struct dma_fence *fence, *f1, *f2, *a1, *a2, *c1, *c2; @@ -595,6 +604,7 @@ static struct kunit_case dma_fence_unwrap_cases[] = { KUNIT_CASE(test_unwrap_merge_order), KUNIT_CASE(test_unwrap_merge_complex), KUNIT_CASE(test_unwrap_merge_complex_seqno), + KUNIT_CASE(test_dedup_empty), {} }; diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index 2f2aaadafe35..503f7d5934e4 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -2148,7 +2148,6 @@ EXPORT_SYMBOL(gpu_buddy_block_print); * gpu_buddy_print - print allocator state * * @mm: GPU buddy manager - * @p: GPU printer to use */ void gpu_buddy_print(struct gpu_buddy *mm) { diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c index 4872253a2449..f394971c0edb 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c @@ -128,7 +128,7 @@ static void komeda_wb_connector_destroy(struct drm_connector *connector) } static const struct drm_connector_funcs komeda_wb_connector_funcs = { - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .detect = komeda_wb_connector_detect, .fill_modes = komeda_wb_connector_fill_modes, .destroy = komeda_wb_connector_destroy, diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c index f8be65119e3b..edd924ba722d 100644 --- a/drivers/gpu/drm/arm/malidp_mw.c +++ b/drivers/gpu/drm/arm/malidp_mw.c @@ -64,18 +64,18 @@ static const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs .mode_valid = malidp_mw_connector_mode_valid, }; -static void malidp_mw_connector_reset(struct drm_connector *connector) +static struct drm_connector_state * +malidp_mw_connector_create_state(struct drm_connector *connector) { - struct malidp_mw_connector_state *mw_state = kzalloc_obj(*mw_state); + struct malidp_mw_connector_state *mw_state; - if (connector->state) - __drm_atomic_helper_connector_destroy_state(connector->state); + mw_state = kzalloc_obj(*mw_state); + if (!mw_state) + return ERR_PTR(-ENOMEM); - kfree(connector->state); - connector->state = NULL; + __drm_atomic_helper_connector_state_init(&mw_state->base, connector); - if (mw_state) - __drm_atomic_helper_connector_reset(connector, &mw_state->base); + return &mw_state->base; } static enum drm_connector_status @@ -111,7 +111,7 @@ malidp_mw_connector_duplicate_state(struct drm_connector *connector) } static const struct drm_connector_funcs malidp_mw_connector_funcs = { - .reset = malidp_mw_connector_reset, + .atomic_create_state = malidp_mw_connector_create_state, .detect = malidp_mw_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = malidp_mw_connector_destroy, diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 038b852405b5..c3ef44074477 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/device.h> #include <linux/gpio/consumer.h> +#include <linux/hdmi.h> #include <linux/module.h> #include <linux/of.h> #include <linux/slab.h> @@ -775,11 +776,6 @@ static int adv7511_connector_init(struct adv7511 *adv) * DRM Bridge Operations */ -static const struct adv7511 *bridge_to_adv7511_const(const struct drm_bridge *bridge) -{ - return container_of(bridge, struct adv7511, bridge); -} - static void adv7511_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_atomic_commit *state) { @@ -817,19 +813,6 @@ static void adv7511_bridge_atomic_disable(struct drm_bridge *bridge, adv7511_power_off(adv); } -static enum drm_mode_status -adv7511_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, - const struct drm_display_mode *mode, - unsigned long long tmds_rate) -{ - const struct adv7511 *adv = bridge_to_adv7511_const(bridge); - - if (tmds_rate > 1000ULL * adv->info->max_mode_clock_khz) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - static enum drm_mode_status adv7511_bridge_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode) @@ -1006,7 +989,6 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_create_state = drm_atomic_helper_bridge_create_state, - .hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid, .hdmi_clear_audio_infoframe = adv7511_bridge_hdmi_clear_audio_infoframe, .hdmi_write_audio_infoframe = adv7511_bridge_hdmi_write_audio_infoframe, .hdmi_clear_avi_infoframe = adv7511_bridge_hdmi_clear_avi_infoframe, @@ -1359,6 +1341,8 @@ static int adv7511_probe(struct i2c_client *i2c) adv7511->bridge.vendor = "Analog"; adv7511->bridge.product = adv7511->info->name; + adv7511->bridge.supported_hdmi_ver = HDMI_VERSION_1_2; + adv7511->bridge.max_tmds_char_rate = 1000ULL * adv7511->info->max_mode_clock_khz; #ifdef CONFIG_DRM_I2C_ADV7511_AUDIO adv7511->bridge.ops |= DRM_BRIDGE_OP_HDMI_AUDIO; diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c index dd35f5a875d3..ea3447d4f285 100644 --- a/drivers/gpu/drm/bridge/inno-hdmi.c +++ b/drivers/gpu/drm/bridge/inno-hdmi.c @@ -1119,6 +1119,7 @@ struct inno_hdmi *inno_hdmi_bind(struct device *dev, hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; hdmi->bridge.vendor = "Inno"; hdmi->bridge.product = "Inno HDMI"; + hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_1_2; hdmi->bridge.ddc = inno_hdmi_i2c_adapter(hdmi); if (IS_ERR(hdmi->bridge.ddc)) diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index 21ac8e58b9d2..e8e0197171e1 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -15,7 +15,6 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> -#include <drm/display/drm_hdmi_helper.h> #include <drm/display/drm_hdmi_state_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -669,20 +668,6 @@ static void it6263_bridge_atomic_enable(struct drm_bridge *bridge, regmap_write(regmap, HDMI_REG_PKT_GENERAL_CTRL, ENABLE_PKT | REPEAT_PKT); } -static enum drm_mode_status -it6263_bridge_mode_valid(struct drm_bridge *bridge, - const struct drm_display_info *info, - const struct drm_display_mode *mode) -{ - unsigned long long rate; - - rate = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444); - if (rate == 0) - return MODE_NOCLOCK; - - return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, rate); -} - static int it6263_bridge_attach(struct drm_bridge *bridge, struct drm_encoder *encoder, enum drm_bridge_attach_flags flags) @@ -753,20 +738,6 @@ it6263_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, return input_fmts; } -static enum drm_mode_status -it6263_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, - const struct drm_display_mode *mode, - unsigned long long tmds_rate) -{ - if (mode->clock > MAX_PIXEL_CLOCK_KHZ) - return MODE_CLOCK_HIGH; - - if (tmds_rate > MAX_HDMI_TMDS_CHAR_RATE_HZ) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - static int it6263_hdmi_clear_avi_infoframe(struct drm_bridge *bridge) { struct it6263 *it = bridge_to_it6263(bridge); @@ -831,13 +802,11 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_create_state = drm_atomic_helper_bridge_create_state, .attach = it6263_bridge_attach, - .mode_valid = it6263_bridge_mode_valid, .atomic_disable = it6263_bridge_atomic_disable, .atomic_enable = it6263_bridge_atomic_enable, .detect = it6263_bridge_detect, .edid_read = it6263_bridge_edid_read, .atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts, - .hdmi_tmds_char_rate_valid = it6263_hdmi_tmds_char_rate_valid, .hdmi_clear_avi_infoframe = it6263_hdmi_clear_avi_infoframe, .hdmi_write_avi_infoframe = it6263_hdmi_write_avi_infoframe, .hdmi_clear_hdmi_infoframe = it6263_hdmi_clear_hdmi_infoframe, @@ -899,6 +868,8 @@ static int it6263_probe(struct i2c_client *client) it->bridge.type = DRM_MODE_CONNECTOR_HDMIA; it->bridge.vendor = "ITE"; it->bridge.product = "IT6263"; + it->bridge.supported_hdmi_ver = HDMI_VERSION_1_3; + it->bridge.max_tmds_char_rate = 1000ULL * MAX_PIXEL_CLOCK_KHZ; return devm_drm_bridge_add(dev, &it->bridge); } diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 51fc68ffdf8f..c8d0a26e8e90 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -10,6 +10,7 @@ #include <linux/media-bus-format.h> #include <linux/module.h> #include <linux/device.h> +#include <linux/hdmi.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/bitfield.h> @@ -867,15 +868,6 @@ it66121_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, const struct drm_display_mode *mode, unsigned long long tmds_rate) { - const struct it66121_ctx *ctx = - container_of(bridge, const struct it66121_ctx, bridge); - unsigned long long max_rate; - - max_rate = (ctx->bus_width == 12) ? 74250000ULL : 148500000ULL; - - if (tmds_rate > max_rate) - return MODE_CLOCK_HIGH; - if (tmds_rate < HDMI_TMDS_CHAR_RATE_MIN_HZ) return MODE_CLOCK_LOW; @@ -1670,6 +1662,9 @@ static int it66121_probe(struct i2c_client *client) DRM_BRIDGE_OP_HDMI; ctx->bridge.vendor = "ITE"; ctx->bridge.product = "IT66121"; + ctx->bridge.supported_hdmi_ver = HDMI_VERSION_1_2; + ctx->bridge.max_tmds_char_rate = ctx->bus_width == 12 ? 74250000ULL : 148500000ULL; + if (client->irq > 0) { ctx->bridge.ops |= DRM_BRIDGE_OP_HPD; diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c index 926f9e1e85bf..18696b75bec1 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8713sx.c +++ b/drivers/gpu/drm/bridge/lontium-lt8713sx.c @@ -554,7 +554,6 @@ static int lt8713sx_probe(struct i2c_client *client) lt8713sx_reset(lt8713sx); - lt8713sx->bridge.funcs = <8713sx_bridge_funcs; lt8713sx->bridge.of_node = dev->of_node; lt8713sx->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; drm_bridge_add(<8713sx->bridge); diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index e347d04b7b2d..a4d05dcba9e0 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -416,7 +416,7 @@ static const struct drm_connector_funcs lt8912_connector_funcs = { .detect = lt8912_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index cdc2b188e2d8..2d1a28261037 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -5,6 +5,7 @@ */ #include <linux/gpio/consumer.h> +#include <linux/hdmi.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/media-bus-format.h> @@ -952,18 +953,6 @@ static int lt9611_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge, return 0; } -static enum drm_mode_status -lt9611_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge, - const struct drm_display_mode *mode, - unsigned long long tmds_rate) -{ - /* 297 MHz for 4k@30 mode */ - if (tmds_rate > 297000000) - return MODE_CLOCK_HIGH; - - return MODE_OK; -} - static int lt9611_hdmi_audio_startup(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -1029,7 +1018,6 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = { .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = lt9611_atomic_get_input_bus_fmts, - .hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid, .hdmi_write_audio_infoframe = lt9611_hdmi_write_audio_infoframe, .hdmi_clear_audio_infoframe = lt9611_hdmi_clear_audio_infoframe, .hdmi_write_avi_infoframe = lt9611_hdmi_write_avi_infoframe, @@ -1175,6 +1163,8 @@ static int lt9611_probe(struct i2c_client *client) lt9611->bridge.hdmi_audio_dev = dev; lt9611->bridge.hdmi_audio_max_i2s_playback_channels = 8; lt9611->bridge.hdmi_audio_dai_port = 2; + lt9611->bridge.supported_hdmi_ver = HDMI_VERSION_1_4; + lt9611->bridge.max_tmds_char_rate = 297000000; /* 297 MHz for 4k@30 mode */ drm_bridge_add(<9611->bridge); diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 4d244e6d4ed9..9153bf629fd1 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -149,7 +149,7 @@ static const struct drm_connector_funcs ge_b850v3_lvds_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .detect = ge_b850v3_lvds_detect, .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/bridge/simple-bridge.c b/drivers/gpu/drm/bridge/simple-bridge.c index 3fba7002322e..fab793fb12b5 100644 --- a/drivers/gpu/drm/bridge/simple-bridge.c +++ b/drivers/gpu/drm/bridge/simple-bridge.c @@ -276,6 +276,11 @@ static const struct of_device_id simple_bridge_match[] = { .connector_type = DRM_MODE_CONNECTOR_HDMIA, }, }, { + .compatible = "chrontel,ch7218a", + .data = &(const struct simple_bridge_info) { + .connector_type = DRM_MODE_CONNECTOR_HDMIA, + }, + }, { .compatible = "mstar,tsumu88adt3-lf-1", .data = &(const struct simple_bridge_info) { .connector_type = DRM_MODE_CONNECTOR_HDMIA, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index c9568aea98dc..b268c9376087 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd. * Copyright (c) 2024 Collabora Ltd. + * Copyright (c) 2025 Amazon.com, Inc. or its affiliates. * * Author: Algea Cao <algea.cao@rock-chips.com> * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com> @@ -15,7 +16,6 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> -#include <linux/workqueue.h> #include <drm/bridge/dw_hdmi_qp.h> #include <drm/display/drm_hdmi_helper.h> @@ -28,6 +28,7 @@ #include <drm/drm_edid.h> #include <drm/drm_modes.h> #include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> #include <media/cec.h> @@ -38,8 +39,6 @@ #define DDC_CI_ADDR 0x37 #define DDC_SEGMENT_ADDR 0x30 -#define SCRAMB_POLL_DELAY_MS 3000 - struct dw_hdmi_qp_i2c { struct i2c_adapter adap; @@ -83,6 +82,9 @@ struct dw_hdmi_qp { struct regmap *regm; int main_irq; + /* Guards curr_conn between atomic hooks and HPD notify readers */ + struct mutex mutex; + struct drm_connector *curr_conn; unsigned long tmds_char_rate; bool no_hpd; }; @@ -350,22 +352,14 @@ static int dw_hdmi_qp_i2c_read(struct dw_hdmi_qp *hdmi, stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); if (!stat) { - if (hdmi->no_hpd) - dev_dbg_ratelimited(hdmi->dev, - "i2c read timed out\n"); - else - dev_err(hdmi->dev, "i2c read timed out\n"); + dev_dbg_ratelimited(hdmi->dev, "i2c read timed out\n"); dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); return -EAGAIN; } /* Check for error condition on the bus */ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { - if (hdmi->no_hpd) - dev_dbg_ratelimited(hdmi->dev, - "i2c read error\n"); - else - dev_err(hdmi->dev, "i2c read error\n"); + dev_dbg_ratelimited(hdmi->dev, "i2c read error\n"); dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); return -EIO; } @@ -404,14 +398,14 @@ static int dw_hdmi_qp_i2c_write(struct dw_hdmi_qp *hdmi, stat = wait_for_completion_timeout(&i2c->cmp, HZ / 10); if (!stat) { - dev_err(hdmi->dev, "i2c write time out!\n"); + dev_dbg_ratelimited(hdmi->dev, "i2c write time out!\n"); dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); return -EAGAIN; } /* Check for error condition on the bus */ if (i2c->stat & I2CM_NACK_RCVD_IRQ) { - dev_err(hdmi->dev, "i2c write nack!\n"); + dev_dbg_ratelimited(hdmi->dev, "i2c write nack!\n"); dw_hdmi_qp_write(hdmi, 0x01, I2CM_CONTROL0); return -EIO; } @@ -547,6 +541,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_connector_state *conn_state; struct drm_connector *connector; unsigned int op_mode; + int ret; connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder); if (WARN_ON(!connector)) @@ -557,16 +552,26 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, return; if (connector->display_info.is_hdmi) { - dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u\n", __func__, - drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), - conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc); op_mode = 0; hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; + + ret = drm_connector_hdmi_enable_scrambling(connector, conn_state); + if (ret) + dev_warn(hdmi->dev, "Failed to enable scrambling: %d\n", ret); + + dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u scramb=%d\n", __func__, + drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format), + conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc, + connector->hdmi.scrambler_enabled); } else { - dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); op_mode = OPMODE_DVI; + dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__); } + drm_connector_get(connector); + scoped_guard(mutex, &hdmi->mutex) + hdmi->curr_conn = connector; + hdmi->phy.ops->init(hdmi, hdmi->phy.data); dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0); @@ -579,9 +584,20 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, struct drm_atomic_commit *state) { struct dw_hdmi_qp *hdmi = bridge->driver_private; + struct drm_connector *connector; + + scoped_guard(mutex, &hdmi->mutex) { + if (hdmi->curr_conn) { + drm_connector_put(hdmi->curr_conn); + hdmi->curr_conn = NULL; + } + } hdmi->tmds_char_rate = 0; + connector = drm_atomic_get_old_connector_for_encoder(state, bridge->encoder); + drm_connector_hdmi_disable_scrambling(connector); + hdmi->phy.ops->disable(hdmi, hdmi->phy.data); } @@ -616,24 +632,24 @@ dw_hdmi_qp_bridge_edid_read(struct drm_bridge *bridge, return drm_edid; } -static enum drm_mode_status -dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge, - const struct drm_display_mode *mode, - unsigned long long rate) +static int dw_hdmi_qp_bridge_scrambler_enable(struct drm_bridge *bridge) { struct dw_hdmi_qp *hdmi = bridge->driver_private; - /* - * TODO: when hdmi->no_hpd is 1 we must not support modes that - * require scrambling, including every mode with a clock above - * HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ. - */ - if (rate > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ) { - dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate); - return MODE_CLOCK_HIGH; - } + dw_hdmi_qp_write(hdmi, 1, SCRAMB_CONFIG0); + dev_dbg(hdmi->dev, "scrambler enabled\n"); - return MODE_OK; + return 0; +} + +static int dw_hdmi_qp_bridge_scrambler_disable(struct drm_bridge *bridge) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + dw_hdmi_qp_write(hdmi, 0, SCRAMB_CONFIG0); + dev_dbg(hdmi->dev, "scrambler disabled\n"); + + return 0; } static int dw_hdmi_qp_bridge_clear_avi_infoframe(struct drm_bridge *bridge) @@ -982,6 +998,22 @@ static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts, #define dw_hdmi_qp_cec_transmit NULL #endif /* CONFIG_DRM_DW_HDMI_QP_CEC */ +static void dw_hdmi_qp_bridge_hpd_enable(struct drm_bridge *bridge) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + if (hdmi->phy.ops->enable_hpd) + hdmi->phy.ops->enable_hpd(hdmi, hdmi->phy.data); +} + +static void dw_hdmi_qp_bridge_hpd_disable(struct drm_bridge *bridge) +{ + struct dw_hdmi_qp *hdmi = bridge->driver_private; + + if (hdmi->phy.ops->disable_hpd) + hdmi->phy.ops->disable_hpd(hdmi, hdmi->phy.data); +} + static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, @@ -990,8 +1022,11 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, .detect = dw_hdmi_qp_bridge_detect, + .hpd_enable = dw_hdmi_qp_bridge_hpd_enable, + .hpd_disable = dw_hdmi_qp_bridge_hpd_disable, .edid_read = dw_hdmi_qp_bridge_edid_read, - .hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid, + .hdmi_scrambler_enable = dw_hdmi_qp_bridge_scrambler_enable, + .hdmi_scrambler_disable = dw_hdmi_qp_bridge_scrambler_disable, .hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe, .hdmi_write_avi_infoframe = dw_hdmi_qp_bridge_write_avi_infoframe, .hdmi_clear_hdmi_infoframe = dw_hdmi_qp_bridge_clear_hdmi_infoframe, @@ -1054,9 +1089,6 @@ static void dw_hdmi_qp_init_hw(struct dw_hdmi_qp *hdmi) /* Clear DONE and ERROR interrupts */ dw_hdmi_qp_write(hdmi, I2CM_OP_DONE_CLEAR | I2CM_NACK_RCVD_CLEAR, MAINUNIT_1_INT_CLEAR); - - if (hdmi->phy.ops->setup_hpd) - hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); } struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, @@ -1079,6 +1111,10 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, if (IS_ERR(hdmi)) return ERR_CAST(hdmi); + ret = devm_mutex_init(dev, &hdmi->mutex); + if (ret) + return ERR_PTR(ret); + hdmi->dev = dev; regs = devm_platform_ioremap_resource(pdev, 0); @@ -1119,13 +1155,18 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, DRM_BRIDGE_OP_HDMI_AUDIO | DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME | DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME; - if (!hdmi->no_hpd) - hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD; hdmi->bridge.of_node = pdev->dev.of_node; hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; hdmi->bridge.vendor = "Synopsys"; hdmi->bridge.product = "DW HDMI QP TX"; + if (hdmi->no_hpd) { + hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_1_4; + } else { + hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_2_0; + hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD; + } + if (plat_data->supported_formats) hdmi->bridge.supported_formats = plat_data->supported_formats; @@ -1169,6 +1210,46 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(dw_hdmi_qp_bind); +/** + * dw_hdmi_qp_hpd_notify() - Notify a hot-plug detection event + * @hdmi: pointer to the DW HDMI QP controller + * + * Platform drivers should call this from their threaded IRQ handler or work + * function to notify the bridge of a connection status change. + * The bridge's .read_hpd() phy_ops callback is used to read the current + * connection status. + */ +void dw_hdmi_qp_hpd_notify(struct dw_hdmi_qp *hdmi) +{ + enum drm_connector_status status; + struct drm_connector *conn; + + scoped_guard(mutex, &hdmi->mutex) { + conn = hdmi->curr_conn; + if (conn) + drm_connector_get(conn); + } + + status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); + dev_dbg(hdmi->dev, "%s status=%d\n", __func__, status); + + /* + * When the display pipeline has been already active, switch to + * drm_connector_helper_hpd_irq_event() to ensure .detect_ctx() + * gets invoked, i.e. via drm_helper_probe_detect(), because + * drm_bridge_hpd_notify() defers to a delayed hotplug path in + * this case. + */ + if (conn && status == connector_status_connected) + drm_connector_helper_hpd_irq_event(conn); + else + drm_bridge_hpd_notify(&hdmi->bridge, status); + + if (conn) + drm_connector_put(conn); +} +EXPORT_SYMBOL_GPL(dw_hdmi_qp_hpd_notify); + void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi) { disable_irq(hdmi->main_irq); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 523d1c7ea072..da399cb951a5 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -66,7 +66,7 @@ static const struct drm_connector_helper_funcs hv_drm_connector_helper_funcs = { static const struct drm_connector_funcs hv_drm_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/imx/ipuv3/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c index e861b8b9d8fa..4ece79374372 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-tve.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c @@ -314,7 +314,7 @@ static void imx_tve_connector_destroy(struct drm_connector *connector) static const struct drm_connector_funcs imx_tve_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = imx_tve_connector_destroy, - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index e907c1d07887..d55d073f4804 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -306,7 +306,7 @@ nouveau_abi16_ioctl_getparam(ABI16_IOCTL_ARGS) getparam->value = 1; break; case NOUVEAU_GETPARAM_GRAPH_UNITS: - getparam->value = nvkm_gr_units(gr); + getparam->value = gr ? nvkm_gr_units(gr) : 0; break; case NOUVEAU_GETPARAM_EXEC_PUSH_MAX: { int ib_max = getparam_dma_ib_max(device); diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 53a1fc2913fe..7a520b4d60fe 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -695,6 +695,8 @@ config DRM_PANEL_NOVATEK_NT36536 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER select DRM_KMS_HELPER help Say Y here if you want to enable support for Novatek NT36536-based @@ -753,6 +755,15 @@ config DRM_PANEL_NOVATEK_NT39016 Say Y here if you want to enable support for the panels built around the Novatek NT39016 display controller. +config DRM_PANEL_NOVATEK_NT51021 + tristate "Novatek NT51021 DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for the panels built + around the Novatek NT51021 Display controller. + config DRM_PANEL_OLIMEX_LCD_OLINUXINO tristate "Olimex LCD-OLinuXino panel" depends on OF @@ -850,6 +861,21 @@ config DRM_PANEL_RAYDIUM_RM692E5 Say Y here if you want to enable support for Raydium RM692E5-based display panels, such as the one found in the Fairphone 5 smartphone. +config DRM_PANEL_RAYDIUM_RM69220 + tristate "Raydium RM69220-based DSI panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER + help + Say Y here if you want to enable support for Raydium RM69220-based + display panels, such as the BOE BF068MWM-TD0 which can be found in + the Nothing Phone (3a). + + To compile this driver as a module, choose M here: the module + will be called panel-raydium-rm69220. + config DRM_PANEL_RAYDIUM_RM69380 tristate "Raydium RM69380-based DSI panel" depends on GPIOLIB diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index f2c9c80a218f..da5175909d54 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672E) += panel-novatek-nt36672e.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37700F) += panel-novatek-nt37700f.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT37801) += panel-novatek-nt37801.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o +obj-$(CONFIG_DRM_PANEL_NOVATEK_NT51021) += panel-novatek-nt51021.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) += panel-orisetech-ota5601a.o @@ -83,6 +84,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67191) += panel-raydium-rm67191.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM67200) += panel-raydium-rm67200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o +obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69220) += panel-raydium-rm69220.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o obj-$(CONFIG_DRM_PANEL_RENESAS_R63419) += panel-renesas-r63419.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 5d7d696b7725..f83b93a1a414 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1958,6 +1958,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x0290, &delay_200_500_e50, "B140XTN07.5"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x04a4, &delay_200_500_e50, "B122UAN01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x0ba4, &delay_200_500_e50, "B140QAX01.H"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x0db3, &delay_200_500_e50, "Unknown"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x105c, &delay_200_500_e50, "B116XTN01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x125c, &delay_200_500_e50, "Unknown"), @@ -1986,6 +1987,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x643d, &delay_200_500_e50, "B140HAN06.4"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x65a9, &delay_200_500_e50, "B140HAN04.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x67a8, &delay_200_500_e200, "B140XTK02.4"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x723c, &delay_200_500_e50, "B140XTN07.2"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x73aa, &delay_200_500_e50, "B116XTN02.3"), @@ -2003,6 +2005,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0xd497, &delay_200_500_e50, "B120XAN01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0xf390, &delay_200_500_e50, "B140XTN07.7"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x03f4, &delay_200_500_e50, "NV140FHD-N43"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0607, &delay_200_500_e200, "Unknown"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0608, &delay_200_500_e50, "NT116WHM-N11"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0609, &delay_200_500_e50_po2e200, "NT116WHM-N21 V4.1"), @@ -2198,6 +2201,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('S', 'H', 'P', 0x1523, &delay_80_500_e50, "LQ140M1JW46"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x153a, &delay_200_500_e50, "LQ140T1JH01"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x154c, &delay_200_500_p2e100, "LQ116M1JW10"), + EDP_PANEL_ENTRY('S', 'H', 'P', 0x1572, &delay_200_500_e200, "LQ138P1JX61"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x158f, &delay_200_500_p2e100, "LQ134Z1"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x1593, &delay_200_500_p2e100, "LQ134N1"), EDP_PANEL_ENTRY('S', 'H', 'P', 0x15a7, &delay_200_500_e200, "LQ120P1JX51"), diff --git a/drivers/gpu/drm/panel/panel-novatek-nt51021.c b/drivers/gpu/drm/panel/panel-novatek-nt51021.c new file mode 100644 index 000000000000..94e7d6aaf0cb --- /dev/null +++ b/drivers/gpu/drm/panel/panel-novatek-nt51021.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2026, Ryan Brue <ryanbrue.dev@gmail.com> +// Copyright (c) 2025, Nickolay Goppen <setotau@mainlining.org> +// Copyright (c) 2024, Alexey Minnekhanov <alexeymin@minlexx.ru> + +/* + * This panel IC doesn't use the set_display_on command although + * it uses set_display_off command. This is not an error. + */ +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.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 novatek_nt51021_desc; + +struct novatek_nt51021_variant { + int (*init)(struct novatek_nt51021_desc *ctx); + int (*off)(struct novatek_nt51021_desc *ctx); + + const struct drm_display_mode *display_mode; + unsigned long mode_flags; +}; + +struct novatek_nt51021_desc { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; + + const struct novatek_nt51021_variant *variant; +}; + +static const struct regulator_bulk_data nt51021_novatek_supplies[] = { + { .supply = "vdd" }, + { .supply = "avdd" }, + { .supply = "havdd" }, +}; + +static inline struct novatek_nt51021_desc *to_novatek_panel(struct drm_panel *panel) +{ + return container_of_const(panel, struct novatek_nt51021_desc, panel); +} + +static void nt51021_novatek_reset(struct novatek_nt51021_desc *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + msleep(20); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); +} + +static void nt51021_cabc_on(struct mipi_dsi_multi_context *dsi_ctx) +{ + if (dsi_ctx->accum_err) + return; + + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x90, 0x40); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); +} + +static void nt51021_cabc_off(struct mipi_dsi_multi_context *dsi_ctx) +{ + if (dsi_ctx->accum_err) + return; + + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x90, 0xc0); + mipi_dsi_generic_write_seq_multi(dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(dsi_ctx, 1000, 2000); +} + +static int boe_tv080wum_nx2_init(struct novatek_nt51021_desc *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x8e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x8b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xf0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0x1b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x98, 0xc1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa1, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa2, 0xfe); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa3, 0xfa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa4, 0xf7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa5, 0xf3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa6, 0xf1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa7, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa8, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0xe9); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaa, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaf, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x35); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb1, 0x89); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb2, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb4, 0x0d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb5, 0x1a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb6, 0x16); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x96, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x99, 0x06); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xcc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x33); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x05); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x0c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x66); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x3b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x53); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x69); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x15); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x1c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x32); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x44); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x60); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x74); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x7e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0x14); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0x25); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x4d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x63); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x71); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x6a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x6b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x75); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x83); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x99); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x9a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xd0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xdb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xe1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xe7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xf4); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xfb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9f, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x94, 0xba); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + + nt51021_cabc_on(&dsi_ctx); + + return dsi_ctx.accum_err; +} + +static int boe_tv101wum_nx0_init(struct novatek_nt51021_desc *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x8e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0x12); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0x1b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x98, 0xc1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9c, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xbb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x22); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa1, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa2, 0xfe); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa3, 0xfa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa4, 0xf7); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa5, 0xf3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa6, 0xf1); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa7, 0xed); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa8, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0xe9); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaa, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xaf, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb0, 0x12); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb1, 0x23); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb2, 0x34); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb3, 0x77); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb4, 0x0d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb5, 0x1a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xb6, 0x16); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x96, 0xe6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x99, 0x06); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xcc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x33); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xca, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcc, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xce, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcf, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xd9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xda, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xde, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xdf, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe0, 0x0e); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe1, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe2, 0x1f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe3, 0x30); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe4, 0x3f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe5, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe6, 0x55); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe7, 0x5d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe8, 0x68); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xe9, 0xe3); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xea, 0xea); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xeb, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xec, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xed, 0x0a); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xee, 0x07); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xef, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf0, 0x09); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf1, 0x1d); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf2, 0x31); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf3, 0x52); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf4, 0x5b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf5, 0xb6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf6, 0xbd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf7, 0xc5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf8, 0xcd); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xf9, 0xd6); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfa, 0xe0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfb, 0xeb); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfc, 0xf8); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfd, 0xff); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xfe, 0xfc); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xff, 0x0f); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9f, 0xff); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 5000, 6000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + + nt51021_cabc_on(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + + return dsi_ctx.accum_err; +} + +static int boe_tv101wum_nm0_init(struct novatek_nt51021_desc *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x01, 0x00); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_usleep_range(&dsi_ctx, 1000, 2000); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8c, 0x80); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xcd, 0x6c); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc0, 0x8b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xc8, 0xf0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x97, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8b, 0x10); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x20); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x83, 0xaa); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x84, 0x11); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xa9, 0x4b); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x85, 0x04); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x86, 0x08); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9c, 0x10); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_msleep(&dsi_ctx, 5); + + return dsi_ctx.accum_err; +} + +static int nt51021_novatek_with_cabc_off(struct novatek_nt51021_desc *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + nt51021_cabc_off(&dsi_ctx); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 130); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 4000, 5000); + + return dsi_ctx.accum_err; +} + +static int nt51021_novatek_off(struct novatek_nt51021_desc *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0xa5); + mipi_dsi_msleep(&dsi_ctx, 20); + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 130); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 70); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x8f, 0x00); + mipi_dsi_usleep_range(&dsi_ctx, 4000, 5000); + + return dsi_ctx.accum_err; +} + +static int nt51021_novatek_prepare(struct drm_panel *panel) +{ + struct novatek_nt51021_desc *ctx = to_novatek_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + nt51021_novatek_reset(ctx); + + ret = ctx->variant->init(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies); + return ret; + } + + return 0; +} + +static int nt51021_novatek_unprepare(struct drm_panel *panel) +{ + struct novatek_nt51021_desc *ctx = to_novatek_panel(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + ret = ctx->variant->off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(nt51021_novatek_supplies), ctx->supplies); + + return 0; +} + +static int nt51021_novatek_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct novatek_nt51021_desc *ctx = to_novatek_panel(panel); + + return drm_connector_helper_get_modes_fixed(connector, + ctx->variant->display_mode); +} + +static const struct drm_panel_funcs nt51021_novatek_panel_funcs = { + .prepare = nt51021_novatek_prepare, + .unprepare = nt51021_novatek_unprepare, + .get_modes = nt51021_novatek_get_modes, +}; + +static int nt51021_novatek_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct novatek_nt51021_desc *ctx; + int ret; + + ctx = devm_drm_panel_alloc(dev, struct novatek_nt51021_desc, panel, + &nt51021_novatek_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->variant = of_device_get_match_data(dev); + if (!ctx->variant) + return dev_err_probe(dev, -ENODEV, "No match data\n"); + + ret = devm_regulator_bulk_get_const(dev, + ARRAY_SIZE(nt51021_novatek_supplies), + nt51021_novatek_supplies, + &ctx->supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = ctx->variant->mode_flags; + + ctx->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return ret; + + return devm_mipi_dsi_attach(dev, dsi); +} + +static const struct drm_display_mode boe_tv080wum_nx2_mode = { + .clock = (1200 + 84 + 4 + 60) * (1920 + 36 + 1 + 25) * 60 / 1000, + .hdisplay = 1200, + .hsync_start = 1200 + 84, + .hsync_end = 1200 + 84 + 4, + .htotal = 1200 + 84 + 4 + 60, + .vdisplay = 1920, + .vsync_start = 1920 + 36, + .vsync_end = 1920 + 36 + 1, + .vtotal = 1920 + 36 + 1 + 25, + .width_mm = 107, + .height_mm = 172, + .type = DRM_MODE_TYPE_DRIVER, +}; + +static const struct drm_display_mode boe_tv101wum_nm0_mode = { + .clock = 163333, + .hdisplay = 1200, + .hsync_start = 1200 + 110, + .hsync_end = 1200 + 110 + 1, + .htotal = 1200 + 110 + 1 + 32, + .vdisplay = 1920, + .vsync_start = 1920 + 11, + .vsync_end = 1920 + 11 + 1, + .vtotal = 1920 + 11 + 1 + 14, + .width_mm = 135, + .height_mm = 216, + .type = DRM_MODE_TYPE_DRIVER, +}; + +static const struct drm_display_mode boe_tv101wum_nx0_mode = { + .clock = (1200 + 172 + 4 + 32) * (1920 + 25 + 1 + 14) * 60 / 1000, + .hdisplay = 1200, + .hsync_start = 1200 + 172, + .hsync_end = 1200 + 172 + 4, + .htotal = 1200 + 172 + 4 + 32, + .vdisplay = 1920, + .vsync_start = 1920 + 25, + .vsync_end = 1920 + 25 + 1, + .vtotal = 1920 + 25 + 1 + 14, + .width_mm = 135, + .height_mm = 216, + .type = DRM_MODE_TYPE_DRIVER, +}; + +#define NT51021_BURST_MODE_FLAGS (MIPI_DSI_MODE_VIDEO | \ + MIPI_DSI_MODE_VIDEO_BURST | \ + MIPI_DSI_MODE_NO_EOT_PACKET | \ + MIPI_DSI_MODE_LPM) + +static const struct novatek_nt51021_variant boe_tv080wum_nx2_data = { + .init = boe_tv080wum_nx2_init, + .off = nt51021_novatek_with_cabc_off, + .display_mode = &boe_tv080wum_nx2_mode, + .mode_flags = NT51021_BURST_MODE_FLAGS, +}; + +static const struct novatek_nt51021_variant boe_tv101wum_nm0_data = { + .init = boe_tv101wum_nm0_init, + .off = nt51021_novatek_off, + .display_mode = &boe_tv101wum_nm0_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM, +}; + +static const struct novatek_nt51021_variant boe_tv101wum_nx0_data = { + .init = boe_tv101wum_nx0_init, + .off = nt51021_novatek_with_cabc_off, + .display_mode = &boe_tv101wum_nx0_mode, + .mode_flags = NT51021_BURST_MODE_FLAGS, +}; + +static const struct of_device_id nt51021_novatek_of_match[] = { + { .compatible = "boe,tv080wum-nx2", .data = &boe_tv080wum_nx2_data }, + { .compatible = "boe,tv101wum-nm0", .data = &boe_tv101wum_nm0_data }, + { .compatible = "boe,tv101wum-nx0", .data = &boe_tv101wum_nx0_data }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, nt51021_novatek_of_match); + +static struct mipi_dsi_driver nt51021_boe_driver = { + .probe = nt51021_novatek_probe, + .driver = { + .name = "panel-novatek-nt51021", + .of_match_table = nt51021_novatek_of_match, + }, +}; +module_mipi_dsi_driver(nt51021_boe_driver); + +MODULE_AUTHOR("Nickolay Goppen <setotau@mainlining.org>"); +MODULE_AUTHOR("Alexey Minnekhanov <alexeymin@minlexx.ru>"); +MODULE_AUTHOR("Ryan Brue <ryanbrue.dev@gmail.com>"); +MODULE_DESCRIPTION("DRM driver for Novatek NT51021-based DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-raydium-rm69220.c b/drivers/gpu/drm/panel/panel-raydium-rm69220.c new file mode 100644 index 000000000000..3030f8b89760 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-raydium-rm69220.c @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree. + * Copyright (c) 2026 Alexander Koskovich <akoskovich@pm.me> + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/device-id/of.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/display/drm_dsc.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +struct raydium_rm69220 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct drm_dsc_config dsc; + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; +}; + +static const struct regulator_bulk_data raydium_rm69220_supplies[] = { + { .supply = "vddio" }, + { .supply = "dvdd" }, + { .supply = "vci" }, +}; + +static inline +struct raydium_rm69220 *to_raydium_rm69220(struct drm_panel *panel) +{ + return container_of(panel, struct raydium_rm69220, panel); +} + +static void raydium_rm69220_reset(struct raydium_rm69220 *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(1000, 2000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + msleep(32); +} + +static int raydium_rm69220_on(struct raydium_rm69220 *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x76); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x78); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x79); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9a, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x9b, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1a, 0xe0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x1b, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa9, 0x68); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xab, 0x22); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc0, 0xa6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbf, 0x87); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x82); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x00, 0x90); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xa2, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xd2); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x97, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x36, 0x11); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x39, 0xab); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3a, 0x30); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3b, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3d, 0x09); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x58); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x40, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x41, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x42, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x43, 0x0d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x44, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x45, 0x1c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x46, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x47, 0x1c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x48, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x49, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4a, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4e, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4f, 0x39); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x50, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x53, 0x0c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x54, 0x08); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x55, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x56, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0xd3); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x18); + 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, 0xf0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5d, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5e, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x5f, 0x20); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x60, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x61, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x62, 0x0f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x0f); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x64, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x0e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x1c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x67, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x68, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x69, 0x46); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6a, 0x54); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6b, 0x62); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6c, 0x69); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6d, 0x70); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6e, 0x77); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x79); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x70, 0x7b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x71, 0x7d); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x72, 0x7e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x73, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x74, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0x22); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x76, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x77, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x78, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x79, 0x2a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7a, 0xbe); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7b, 0x3a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7c, 0xfc); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7d, 0x3a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7e, 0xfa); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x3a); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x80, 0xf8); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x81, 0x3b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x82, 0x38); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x83, 0x3b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x84, 0x78); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x85, 0x3b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x86, 0xb6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x4b); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x88, 0xf6); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x89, 0x4c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8a, 0x34); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8b, 0x4c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8c, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8d, 0x5c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8e, 0x74); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x8c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x90, 0xf4); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x91, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x92, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x93, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x94, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x95, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x96, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xa0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x66, 0x06); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x49); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x87, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x3f, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xa0); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x04, 0x07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4b, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x4d, 0x40); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0xa1); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x58, 0x67); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x75, 0xa7); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x2f, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfa, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc1, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xc2, 0x03); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x35, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x51, 0x0d, 0xbb); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfe, 0x00); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 80); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); + + return dsi_ctx.accum_err; +} + +static int raydium_rm69220_off(struct raydium_rm69220 *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 100); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 100); + + return dsi_ctx.accum_err; +} + +static int raydium_rm69220_prepare(struct drm_panel *panel) +{ + struct raydium_rm69220 *ctx = to_raydium_rm69220(panel); + struct device *dev = &ctx->dsi->dev; + struct drm_dsc_picture_parameter_set pps; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(raydium_rm69220_supplies), ctx->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + raydium_rm69220_reset(ctx); + + ret = raydium_rm69220_on(ctx); + if (ret < 0) { + dev_err(dev, "Failed to initialize panel: %d\n", ret); + goto err; + } + + drm_dsc_pps_payload_pack(&pps, &ctx->dsc); + + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps); + if (ret < 0) { + dev_err(dev, "failed to transmit PPS: %d\n", ret); + goto err; + } + + ret = mipi_dsi_compression_mode(ctx->dsi, true); + if (ret < 0) { + dev_err(dev, "failed to enable compression mode: %d\n", ret); + goto err; + } + + msleep(28); + + return 0; + +err: + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(raydium_rm69220_supplies), ctx->supplies); + return ret; +} + +static int raydium_rm69220_unprepare(struct drm_panel *panel) +{ + struct raydium_rm69220 *ctx = to_raydium_rm69220(panel); + struct device *dev = &ctx->dsi->dev; + int ret; + + ret = raydium_rm69220_off(ctx); + if (ret < 0) + dev_err(dev, "Failed to un-initialize panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(raydium_rm69220_supplies), ctx->supplies); + + return 0; +} + +static const struct drm_display_mode raydium_rm69220_mode = { + .clock = (1080 + 36 + 4 + 36) * (2392 + 72 + 4 + 52) * 120 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 36, + .hsync_end = 1080 + 36 + 4, + .htotal = 1080 + 36 + 4 + 36, + .vdisplay = 2392, + .vsync_start = 2392 + 72, + .vsync_end = 2392 + 72 + 4, + .vtotal = 2392 + 72 + 4 + 52, + .width_mm = 71, + .height_mm = 157, + .type = DRM_MODE_TYPE_DRIVER, +}; + +static int raydium_rm69220_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + return drm_connector_helper_get_modes_fixed(connector, &raydium_rm69220_mode); +} + +static const struct drm_panel_funcs raydium_rm69220_panel_funcs = { + .prepare = raydium_rm69220_prepare, + .unprepare = raydium_rm69220_unprepare, + .get_modes = raydium_rm69220_get_modes, +}; + +static int raydium_rm69220_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return 0; +} + +static const struct backlight_ops raydium_rm69220_bl_ops = { + .update_status = raydium_rm69220_bl_update_status, +}; + +static struct backlight_device * +raydium_rm69220_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 2946, + .max_brightness = 3442, /* 4095 is HBM max */ + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &raydium_rm69220_bl_ops, &props); +} + +static int raydium_rm69220_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct raydium_rm69220 *ctx; + int ret; + + ctx = devm_drm_panel_alloc(dev, struct raydium_rm69220, panel, + &raydium_rm69220_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ret = devm_regulator_bulk_get_const(dev, + ARRAY_SIZE(raydium_rm69220_supplies), + raydium_rm69220_supplies, + &ctx->supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset-gpios\n"); + + ctx->dsi = dsi; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB101010; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM | + MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT; + + ctx->panel.prepare_prev_first = true; + + ctx->panel.backlight = raydium_rm69220_create_backlight(dsi); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "Failed to create backlight\n"); + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret < 0) + return ret; + + /* This panel only supports DSC; unconditionally enable it */ + dsi->dsc = &ctx->dsc; + + ctx->dsc.dsc_version_major = 1; + ctx->dsc.dsc_version_minor = 1; + ctx->dsc.slice_height = 13; + ctx->dsc.slice_width = 540; + + ctx->dsc.slice_count = 1080 / ctx->dsc.slice_width; + ctx->dsc.bits_per_component = 10; + ctx->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */ + ctx->dsc.block_pred_enable = true; + + return devm_mipi_dsi_attach(dev, dsi); +} + +static const struct of_device_id raydium_rm69220_of_match[] = { + { .compatible = "boe,bf068mwm-td0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, raydium_rm69220_of_match); + +static struct mipi_dsi_driver raydium_rm69220_driver = { + .probe = raydium_rm69220_probe, + .driver = { + .name = "panel-raydium-rm69220", + .of_match_table = raydium_rm69220_of_match, + }, +}; +module_mipi_dsi_driver(raydium_rm69220_driver); + +MODULE_AUTHOR("Alexander Koskovich <akoskovich@pm.me>"); +MODULE_DESCRIPTION("DRM driver for RM69220-equipped DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c index c3fbc459c7e0..9adf97aba5d3 100644 --- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c +++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c @@ -75,15 +75,25 @@ static int rb070d30_panel_unprepare(struct drm_panel *panel) static int rb070d30_panel_enable(struct drm_panel *panel) { struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - return mipi_dsi_dcs_exit_sleep_mode(ctx->dsi); + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + return dsi_ctx.accum_err; } static int rb070d30_panel_disable(struct drm_panel *panel) { struct rb070d30_panel *ctx = panel_to_rb070d30_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; - return mipi_dsi_dcs_enter_sleep_mode(ctx->dsi); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + + /* + * Returning an error would leave panel->enabled set, causing the + * next enable to be skipped even after unprepare powers off the panel. + * The helper above already logs any command failure. + */ + return 0; } /* Default timings */ diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index d7dbd7442919..a944c34642dc 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4379,6 +4379,9 @@ static const struct panel_desc raystar_rff500f_awh_dnn = { .width = 108, .height = 65, }, + .delay = { + .enable = 250, + }, .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, .connector_type = DRM_MODE_CONNECTOR_LVDS, }; diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 6733e0e1d061..d0db128cda81 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -11,6 +11,7 @@ #include <drm/display/drm_dsc.h> #include <drm/display/drm_dsc_helper.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_of.h> #include <drm/drm_modes.h> #include <drm/drm_panel.h> @@ -18,10 +19,24 @@ struct visionox_vtdr6130 { struct drm_panel panel; - struct drm_dsc_config dsc; struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; + enum drm_panel_orientation orientation; + struct visionox_vtdr6130_desc *desc; +}; + +struct visionox_vtdr6130_desc { + unsigned int bpc; + unsigned int lanes; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + + const struct drm_display_mode *modes; + unsigned int num_modes; + int (*init_sequence)(struct visionox_vtdr6130 *ctx); + + struct drm_dsc_config dsc; }; static const struct regulator_bulk_data visionox_vtdr6130_supplies[] = { @@ -45,6 +60,82 @@ static void visionox_vtdr6130_reset(struct visionox_vtdr6130 *ctx) usleep_range(10000, 11000); } +static int retroidpocket_rp6_on(struct visionox_vtdr6130 *ctx) +{ + struct mipi_dsi_device *dsi = ctx->dsi; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + struct drm_dsc_picture_parameter_set pps; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x01); + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, + MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20); + mipi_dsi_dcs_set_display_brightness_multi(&dsi_ctx, 0xff07); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x6f, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_SET_ADDRESS_MODE, 0x02); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x00); + 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, 0x70, 0x11, 0x00, 0x00, 0x89, + 0x30, 0x80, 0x07, 0x80, 0x04, 0x38, 0x00, + 0x14, 0x02, 0x1c, 0x02, 0x1c, 0x02, 0x00, + 0x02, 0x25, 0x00, 0x20, 0x01, 0xd5, 0x00, + 0x07, 0x00, 0x0d, 0x05, 0x7a, 0x05, 0x16, + 0x18, 0x00, 0x10, 0xf0, 0x03, 0x0c, 0x20, + 0x00, 0x06, 0x0b, 0x0b, 0x33, 0x0e, 0x1c, + 0x2a, 0x38, 0x46, 0x54, 0x62, 0x69, 0x70, + 0x77, 0x79, 0x7b, 0x7d, 0x7e, 0x01, 0x02, + 0x01, 0x00, 0x09, 0x40, 0x09, 0xbe, 0x19, + 0xfc, 0x19, 0xfa, 0x19, 0xf8, 0x1a, 0x38, + 0x1a, 0x78, 0x1a, 0xb6, 0x2a, 0xb6, 0x2a, + 0xf4, 0x2a, 0xf4, 0x4b, 0x34, 0x63, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00); + + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x10); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb1, 0x02, 0x15, 0x00, 0x14, + 0x00, 0x20, 0x00, 0x02, 0x16, 0x00, 0x14, + 0x07, 0xb4, 0x00, 0x02, 0x16, 0x00, 0x14, + 0x00, 0x10, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x14); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb2, 0x03, 0x33); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb4, 0x0a, 0x72, 0x00, 0x00, + 0x0a, 0x72, 0x00, 0x00, 0x0a, 0x72, 0x00, + 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb5, 0x00, 0x1e, 0x1e, 0x1e, + 0x1e, 0x1e, 0x1e, 0x06, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb9, 0x00, 0x00, 0x08, 0x1e, + 0x1e, 0x1e); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbc, 0x10, 0x00, 0x00, 0x08, + 0x11, 0x1e, 0x7c, 0x1e, 0x7c, 0x1e, 0x7c); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x10, 0x10, 0x00, 0x0c, + 0x22, 0x1e, 0x62, 0x1e, 0x62, 0x1e, 0x62); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x80); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x13); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfd, 0x01); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x81); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf9, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x83); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x65, 0x04); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf8, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xaa, 0x00); + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xff, 0x5a, 0x00); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 150); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 50); + + drm_dsc_pps_payload_pack(&pps, dsi->dsc); + mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); + mipi_dsi_compression_mode_multi(&dsi_ctx, true); + + return dsi_ctx.accum_err; +} + static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx) { struct mipi_dsi_device *dsi = ctx->dsi; @@ -160,7 +251,7 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) visionox_vtdr6130_reset(ctx); - ret = visionox_vtdr6130_on(ctx); + ret = ctx->desc->init_sequence(ctx); if (ret < 0) { gpiod_set_value_cansleep(ctx->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(visionox_vtdr6130_supplies), @@ -185,26 +276,87 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel) return 0; } -static const struct drm_display_mode visionox_vtdr6130_mode = { - .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000, - .hdisplay = 1080, - .hsync_start = 1080 + 20, - .hsync_end = 1080 + 20 + 2, - .htotal = 1080 + 20 + 2 + 20, - .vdisplay = 2400, - .vsync_start = 2400 + 20, - .vsync_end = 2400 + 20 + 2, - .vtotal = 2400 + 20 + 2 + 18, - .width_mm = 71, - .height_mm = 157, +static const struct drm_display_mode retroidpocket_rp6_modes[] = { + { + .clock = (1080 + 22 + 2 + 16) * (1920 + 16 + 2 + 20) * 120 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 22, + .hsync_end = 1080 + 22 + 2, + .htotal = 1080 + 22 + 2 + 16, + .vdisplay = 1920, + .vsync_start = 1920 + 16, + .vsync_end = 1920 + 16 + 2, + .vtotal = 1920 + 16 + 2 + 20, + .width_mm = 69, + .height_mm = 122, + }, +}; + +static struct visionox_vtdr6130_desc retroidpocket_rp6_panel_desc = { + .modes = retroidpocket_rp6_modes, + .num_modes = ARRAY_SIZE(retroidpocket_rp6_modes), + .bpc = 8, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .init_sequence = retroidpocket_rp6_on, + .dsc = { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x1, + .slice_height = 12, + .slice_width = 540, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + }, +}; + +static const struct drm_display_mode visionox_vtdr6130_modes[] = { + { + .clock = (1080 + 20 + 2 + 20) * (2400 + 20 + 2 + 18) * 144 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 20, + .hsync_end = 1080 + 20 + 2, + .htotal = 1080 + 20 + 2 + 20, + .vdisplay = 2400, + .vsync_start = 2400 + 20, + .vsync_end = 2400 + 20 + 2, + .vtotal = 2400 + 20 + 2 + 18, + .width_mm = 71, + .height_mm = 157, + }, +}; + +static struct visionox_vtdr6130_desc visionox_vtdr6130_panel_desc = { + .modes = visionox_vtdr6130_modes, + .num_modes = ARRAY_SIZE(visionox_vtdr6130_modes), + .bpc = 8, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .init_sequence = visionox_vtdr6130_on, + .dsc = { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x2, + .slice_height = 40, + .slice_width = 540, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + }, }; static int visionox_vtdr6130_get_modes(struct drm_panel *panel, struct drm_connector *connector) { + struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); struct drm_display_mode *mode; - mode = drm_mode_duplicate(connector->dev, &visionox_vtdr6130_mode); + mode = drm_mode_duplicate(connector->dev, ctx->desc->modes); if (!mode) return -ENOMEM; @@ -218,10 +370,18 @@ static int visionox_vtdr6130_get_modes(struct drm_panel *panel, return 1; } +static enum drm_panel_orientation visionox_vtdr6130_get_orientation(struct drm_panel *panel) +{ + struct visionox_vtdr6130 *ctx = to_visionox_vtdr6130(panel); + + return ctx->orientation; +} + static const struct drm_panel_funcs visionox_vtdr6130_panel_funcs = { .prepare = visionox_vtdr6130_prepare, .unprepare = visionox_vtdr6130_unprepare, .get_modes = visionox_vtdr6130_get_modes, + .get_orientation = visionox_vtdr6130_get_orientation, }; static int visionox_vtdr6130_bl_update_status(struct backlight_device *bl) @@ -274,25 +434,25 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "Failed to get reset-gpios\n"); + ctx->desc = (struct visionox_vtdr6130_desc *)of_device_get_match_data(dev); + if (!ctx->desc) + return -ENODEV; + ctx->dsi = dsi; mipi_dsi_set_drvdata(dsi, ctx); - ctx->dsc.dsc_version_major = 0x1; - ctx->dsc.dsc_version_minor = 0x2; - ctx->dsc.slice_height = 40; - ctx->dsc.slice_width = 540; - ctx->dsc.slice_count = 2; - ctx->dsc.bits_per_component = 8; - ctx->dsc.bits_per_pixel = 8 << 4; - ctx->dsc.block_pred_enable = true; - - dsi->dsc = &ctx->dsc; - dsi->lanes = 4; - dsi->format = MIPI_DSI_FMT_RGB888; - dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | - MIPI_DSI_CLOCK_NON_CONTINUOUS; + dsi->dsc = &ctx->desc->dsc; + dsi->lanes = ctx->desc->lanes; + dsi->format = ctx->desc->format; + dsi->mode_flags = ctx->desc->mode_flags; ctx->panel.prepare_prev_first = true; + ret = drm_of_get_panel_orientation(dev->of_node, &ctx->orientation); + if (ret < 0) { + dev_err(dev, "Failed to get orientation %d\n", ret); + return ret; + } + ctx->panel.backlight = visionox_vtdr6130_create_backlight(dsi); if (IS_ERR(ctx->panel.backlight)) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), @@ -306,7 +466,8 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) } static const struct of_device_id visionox_vtdr6130_of_match[] = { - { .compatible = "visionox,vtdr6130" }, + { .compatible = "retroidpocket,rp6-panel", .data = &retroidpocket_rp6_panel_desc }, + { .compatible = "visionox,vtdr6130", .data = &visionox_vtdr6130_panel_desc }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, visionox_vtdr6130_of_match); diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index 485349faf251..fb0a1b45007b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -2,12 +2,15 @@ /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */ /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ +#include <linux/debugfs.h> #include <linux/clk.h> #include <linux/reset.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <drm/drm_drv.h> +#include <drm/drm_print.h> #include "panfrost_device.h" #include "panfrost_devfreq.h" @@ -34,10 +37,46 @@ static void panfrost_reset_fini(struct panfrost_device *pfdev) reset_control_assert(pfdev->rstc); } -static int panfrost_clk_init(struct panfrost_device *pfdev) +static int panfrost_clks_enable(struct panfrost_device *pfdev, bool on_resume) { int err; + + err = clk_prepare_enable(pfdev->clock); + if (err) + return err; + + err = clk_prepare_enable(pfdev->bus_clock); + if (err) + goto disable_clock; + + if (on_resume) { + 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); + + return err; +} + +static void panfrost_clks_disable(struct panfrost_device *pfdev, bool on_suspend) +{ + if (on_suspend) + clk_disable_unprepare(pfdev->bus_ace_clock); + clk_disable_unprepare(pfdev->bus_clock); + clk_disable_unprepare(pfdev->clock); +} + +static int panfrost_clk_init(struct panfrost_device *pfdev) +{ unsigned long rate; + int err; pfdev->clock = devm_clk_get(pfdev->base.dev, NULL); if (IS_ERR(pfdev->clock)) { @@ -48,53 +87,59 @@ static int panfrost_clk_init(struct panfrost_device *pfdev) rate = clk_get_rate(pfdev->clock); dev_info(pfdev->base.dev, "clock rate = %lu\n", rate); - err = clk_prepare_enable(pfdev->clock); + err = clk_prepare(pfdev->clock); if (err) return err; pfdev->bus_clock = devm_clk_get_optional(pfdev->base.dev, "bus"); if (IS_ERR(pfdev->bus_clock)) { - dev_err(pfdev->base.dev, "get bus_clock failed %ld\n", - PTR_ERR(pfdev->bus_clock)); err = PTR_ERR(pfdev->bus_clock); - goto disable_clock; + dev_err(pfdev->base.dev, "get bus_clock failed %d\n", err); + goto unprepare_clock; } if (pfdev->bus_clock) { rate = clk_get_rate(pfdev->bus_clock); dev_info(pfdev->base.dev, "bus_clock rate = %lu\n", rate); - err = clk_prepare_enable(pfdev->bus_clock); + err = clk_prepare(pfdev->bus_clock); if (err) - goto disable_clock; + goto unprepare_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; + goto unprepare_bus_clock; } - err = clk_prepare_enable(pfdev->bus_ace_clock); + err = clk_prepare(pfdev->bus_ace_clock); if (err) - goto disable_bus_clock; + goto unprepare_bus_clock; + + err = panfrost_clks_enable(pfdev, true); + if (err) + goto unprepare_bus_ace_clock; return 0; -disable_bus_clock: - clk_disable_unprepare(pfdev->bus_clock); -disable_clock: - clk_disable_unprepare(pfdev->clock); +unprepare_bus_ace_clock: + clk_unprepare(pfdev->bus_ace_clock); +unprepare_bus_clock: + clk_unprepare(pfdev->bus_clock); +unprepare_clock: + clk_unprepare(pfdev->clock); return err; } 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); + panfrost_clks_disable(pfdev, true); + clk_unprepare(pfdev->bus_ace_clock); + clk_unprepare(pfdev->bus_clock); + clk_unprepare(pfdev->clock); } static int panfrost_regulator_init(struct panfrost_device *pfdev) @@ -214,14 +259,14 @@ err: int panfrost_device_init(struct panfrost_device *pfdev) { + bool device_initialised = false; int err; - mutex_init(&pfdev->sched_lock); - INIT_LIST_HEAD(&pfdev->as_lru_list); - - spin_lock_init(&pfdev->as_lock); + pfdev->comp = of_device_get_match_data(pfdev->base.dev); + if (!pfdev->comp) + return -ENODEV; - spin_lock_init(&pfdev->cycle_counter.lock); + pfdev->coherent = device_get_dma_attr(pfdev->base.dev) == DEV_DMA_COHERENT; #ifdef CONFIG_DEBUG_FS mutex_init(&pfdev->debugfs.gems_lock); @@ -280,9 +325,41 @@ int panfrost_device_init(struct panfrost_device *pfdev) if (err) goto out_job; - panfrost_gem_init(pfdev); + err = panfrost_gem_init(pfdev); + if (err) + goto out_perfcnt; + + device_initialised = true; + + /* The reason we must manually set the PM status and usage counter is + * we have just powered the device up but did not go through the PM + * runtime resume callback, so we need to update these ourselves. + */ + pm_runtime_get_noresume(pfdev->base.dev); + pm_runtime_set_active(pfdev->base.dev); + pm_runtime_mark_last_busy(pfdev->base.dev); + pm_runtime_enable(pfdev->base.dev); + pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */ + pm_runtime_use_autosuspend(pfdev->base.dev); + + /* + * Register the DRM device with the core and the connectors with + * sysfs + */ + err = drm_dev_register(&pfdev->base, 0); + if (err < 0) + goto err_disable_rpm; + + pm_runtime_put_autosuspend(pfdev->base.dev); return 0; + +err_disable_rpm: + pm_runtime_dont_use_autosuspend(pfdev->base.dev); + pm_runtime_disable(pfdev->base.dev); + panfrost_gem_fini(pfdev); +out_perfcnt: + panfrost_perfcnt_fini(pfdev); out_job: panfrost_jm_fini(pfdev); out_mmu: @@ -299,11 +376,23 @@ out_reset: panfrost_reset_fini(pfdev); out_pm_domain: panfrost_pm_domain_fini(pfdev); + + if (device_initialised) { + pm_runtime_set_suspended(pfdev->base.dev); + pm_runtime_put_noidle(pfdev->base.dev); + } + return err; } void panfrost_device_fini(struct panfrost_device *pfdev) { + drm_WARN_ON(&pfdev->base, pm_runtime_get_sync(pfdev->base.dev) < 0); + + pm_runtime_dont_use_autosuspend(pfdev->base.dev); + pm_runtime_disable(pfdev->base.dev); + + panfrost_gem_fini(pfdev); panfrost_perfcnt_fini(pfdev); panfrost_jm_fini(pfdev); panfrost_mmu_fini(pfdev); @@ -313,6 +402,9 @@ void panfrost_device_fini(struct panfrost_device *pfdev) panfrost_clk_fini(pfdev); panfrost_reset_fini(pfdev); panfrost_pm_domain_fini(pfdev); + + pm_runtime_set_suspended(pfdev->base.dev); + pm_runtime_put_noidle(pfdev->base.dev); } #define PANFROST_EXCEPTION(id) \ @@ -417,6 +509,8 @@ bool panfrost_exception_needs_reset(const struct panfrost_device *pfdev, void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int) { + guard(rwsem_write)(&pfdev->reset.lock); + panfrost_gpu_soft_reset(pfdev); panfrost_gpu_power_on(pfdev); @@ -425,6 +519,8 @@ void panfrost_device_reset(struct panfrost_device *pfdev, bool enable_job_int) panfrost_jm_reset_interrupts(pfdev); if (enable_job_int) panfrost_jm_enable_interrupts(pfdev); + + panfrost_perfcnt_reset(pfdev); } static int panfrost_device_runtime_resume(struct device *dev) @@ -437,34 +533,17 @@ static int panfrost_device_runtime_resume(struct device *dev) if (ret) return ret; - ret = clk_enable(pfdev->clock); - if (ret) - goto err_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; + ret = panfrost_clks_enable(pfdev, true); + if (ret) { + reset_control_assert(pfdev->rstc); + return ret; + } } panfrost_device_reset(pfdev, true); panfrost_devfreq_resume(pfdev); 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); -err_clk: - if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) - reset_control_assert(pfdev->rstc); - return ret; } static int panfrost_device_runtime_suspend(struct device *dev) @@ -481,9 +560,7 @@ static int panfrost_device_runtime_suspend(struct device *dev) panfrost_gpu_power_off(pfdev); if (pfdev->comp->pm_features & BIT(GPU_PM_RT)) { - clk_disable(pfdev->bus_ace_clock); - clk_disable(pfdev->bus_clock); - clk_disable(pfdev->clock); + panfrost_clks_disable(pfdev, true); reset_control_assert(pfdev->rstc); } @@ -507,15 +584,9 @@ static int panfrost_device_resume(struct device *dev) } if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) { - ret = clk_enable(pfdev->clock); + ret = panfrost_clks_enable(pfdev, false); if (ret) goto err_clk; - - if (pfdev->bus_clock) { - ret = clk_enable(pfdev->bus_clock); - if (ret) - goto err_bus_clk; - } } ret = pm_runtime_force_resume(dev); @@ -525,11 +596,8 @@ static int panfrost_device_resume(struct device *dev) return 0; err_resume: - if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS) && pfdev->bus_clock) - clk_disable(pfdev->bus_clock); -err_bus_clk: if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) - clk_disable(pfdev->clock); + panfrost_clks_disable(pfdev, false); err_clk: if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF)) dev_pm_opp_set_opp(dev, NULL); @@ -545,12 +613,8 @@ static int panfrost_device_suspend(struct device *dev) if (ret) return ret; - if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) { - if (pfdev->bus_clock) - clk_disable(pfdev->bus_clock); - - clk_disable(pfdev->clock); - } + if (pfdev->comp->pm_features & BIT(GPU_PM_CLK_DIS)) + panfrost_clks_disable(pfdev, false); if (pfdev->comp->pm_features & BIT(GPU_PM_VREG_OFF)) dev_pm_opp_set_opp(dev, NULL); @@ -562,3 +626,56 @@ EXPORT_GPL_DEV_PM_OPS(panfrost_pm_ops) = { RUNTIME_PM_OPS(panfrost_device_runtime_suspend, panfrost_device_runtime_resume, NULL) SYSTEM_SLEEP_PM_OPS(panfrost_device_suspend, panfrost_device_resume) }; + +#ifdef CONFIG_DEBUG_FS +static int reset_get(void *data, u64 *val) +{ + struct panfrost_device *pfdev = + container_of(data, struct panfrost_device, base); + + *val = atomic_read(&pfdev->reset.pending); + return 0; +} + +static int reset_set(void *data, u64 val) +{ + struct panfrost_device *pfdev = + container_of(data, struct panfrost_device, base); + int ret; + + if (val != 1) + return -EINVAL; + + ret = pm_runtime_get_if_active(pfdev->base.dev); + if (!ret) + return 0; + + panfrost_device_schedule_reset(pfdev); + flush_work(&pfdev->reset.work); + + /* ret < 0 means runtime PM for the device is disabled, so we + * only need to return the PM reference in the opposite case + */ + if (ret > 0) + pm_runtime_put(pfdev->base.dev); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(panfrost_reset_debugfs_fops, + reset_get, reset_set, + "0x%08llx\n"); + +static void panfrost_reset_debugfs_init(struct drm_minor *minor) +{ + debugfs_create_file("reset", 0600, minor->debugfs_root, + minor->dev, &panfrost_reset_debugfs_fops); +} + +void panfrost_device_debugfs_init(struct drm_minor *minor) +{ + panfrost_gem_debugfs_init(minor); + panfrost_sched_debugfs_init(minor); + panfrost_reset_debugfs_init(minor); +} +#endif // CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 1fe1554f2652..63452febb1cf 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -165,6 +165,7 @@ struct panfrost_device { struct { struct workqueue_struct *wq; struct work_struct work; + struct rw_semaphore lock; atomic_t pending; } reset; @@ -341,4 +342,8 @@ panfrost_device_schedule_reset(struct panfrost_device *pfdev) queue_work(pfdev->reset.wq, &pfdev->reset.work); } +#ifdef CONFIG_DEBUG_FS +void panfrost_device_debugfs_init(struct drm_minor *minor); +#endif // CONFIG_DEBUG_FS + #endif diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 70c05210ea84..7c348a0bf1ee 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -21,7 +21,6 @@ #include <drm/drm_timeout.h> #include "panfrost_device.h" -#include "panfrost_drv.h" #include "panfrost_gem.h" #include "panfrost_mmu.h" #include "panfrost_job.h" @@ -42,9 +41,12 @@ static int panfrost_ioctl_query_timestamp(struct panfrost_device *pfdev, if (ret) return ret; - panfrost_cycle_counter_get(pfdev); - *arg = panfrost_timestamp_read(pfdev); - panfrost_cycle_counter_put(pfdev); + /* We should not read timestamp register while the GPU is being reset */ + scoped_guard(rwsem_read, &pfdev->reset.lock) { + panfrost_cycle_counter_get(pfdev); + *arg = panfrost_timestamp_read(pfdev); + panfrost_cycle_counter_put(pfdev); + } pm_runtime_put(pfdev->base.dev); return 0; @@ -793,133 +795,6 @@ static const struct file_operations panfrost_drm_driver_fops = { .show_fdinfo = drm_show_fdinfo, }; -#ifdef CONFIG_DEBUG_FS -static int panthor_gems_show(struct seq_file *m, void *data) -{ - struct drm_info_node *node = m->private; - struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev); - - panfrost_gem_debugfs_print_bos(pfdev, m); - - return 0; -} - -static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle, - struct seq_file *m) -{ - struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev; - const char *prio = "UNKNOWN"; - - static const char * const prios[] = { - [DRM_SCHED_PRIORITY_HIGH] = "HIGH", - [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL", - [DRM_SCHED_PRIORITY_LOW] = "LOW", - }; - - if (jm_ctx->slot_entity[0].priority != - jm_ctx->slot_entity[1].priority) - drm_warn(ddev, "Slot priorities should be the same in a single context"); - - if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios)) - prio = prios[jm_ctx->slot_entity[0].priority]; - - seq_printf(m, " JM context %u: priority %s\n", handle, prio); -} - -static int show_file_jm_ctxs(struct panfrost_file_priv *pfile, - struct seq_file *m) -{ - struct panfrost_jm_ctx *jm_ctx; - unsigned long i; - - xa_lock(&pfile->jm_ctxs); - xa_for_each(&pfile->jm_ctxs, i, jm_ctx) { - jm_ctx = panfrost_jm_ctx_get(jm_ctx); - xa_unlock(&pfile->jm_ctxs); - show_panfrost_jm_ctx(jm_ctx, i, m); - panfrost_jm_ctx_put(jm_ctx); - xa_lock(&pfile->jm_ctxs); - } - xa_unlock(&pfile->jm_ctxs); - - 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 int show_each_file(struct seq_file *m, void *arg) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *ddev = node->minor->dev; - int (*show)(struct panfrost_file_priv *, struct seq_file *) = - node->info_ent->data; - struct drm_file *file; - int ret; - - ret = mutex_lock_interruptible(&ddev->filelist_mutex); - if (ret) - return ret; - - list_for_each_entry(file, &ddev->filelist, lhead) { - struct task_struct *task; - struct panfrost_file_priv *pfile = file->driver_priv; - struct pid *pid; - - /* - * Although we have a valid reference on file->pid, that does - * not guarantee that the task_struct who called get_pid() is - * still alive (e.g. get_pid(current) => fork() => exit()). - * Therefore, we need to protect this ->comm access using RCU. - */ - rcu_read_lock(); - pid = rcu_dereference(file->pid); - task = pid_task(pid, PIDTYPE_TGID); - seq_printf(m, "client_id %8llu pid %8d command %s:\n", - file->client_id, pid_nr(pid), - task ? task->comm : "<unknown>"); - rcu_read_unlock(); - - ret = show(pfile, m); - if (ret < 0) - break; - - seq_puts(m, "\n"); - } - - mutex_unlock(&ddev->filelist_mutex); - return ret; -} - -static struct drm_info_list panfrost_sched_debugfs_list[] = { - { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs }, -}; - -static void panfrost_sched_debugfs_init(struct drm_minor *minor) -{ - drm_debugfs_create_files(panfrost_sched_debugfs_list, - ARRAY_SIZE(panfrost_sched_debugfs_list), - minor->debugfs_root, minor); -} - -static void panfrost_debugfs_init(struct drm_minor *minor) -{ - panthor_gems_debugfs_init(minor); - panfrost_sched_debugfs_init(minor); -} -#endif - /* * Panfrost driver version: * - 1.0 - initial interface @@ -933,6 +808,7 @@ static void panfrost_debugfs_init(struct drm_minor *minor) * - 1.6 - adds PANFROST_BO_MAP_WB, PANFROST_IOCTL_SYNC_BO, * PANFROST_IOCTL_QUERY_BO_INFO and * DRM_PANFROST_PARAM_SELECTED_COHERENCY + * - 1.7 - adds PERFCNT_DUMP state field */ static const struct drm_driver panfrost_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, @@ -945,26 +821,19 @@ static const struct drm_driver panfrost_drm_driver = { .name = "panfrost", .desc = "panfrost DRM", .major = 1, - .minor = 6, + .minor = 7, .gem_create_object = panfrost_gem_create_object, .gem_prime_import = panfrost_gem_prime_import, .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, #ifdef CONFIG_DEBUG_FS - .debugfs_init = panfrost_debugfs_init, + .debugfs_init = panfrost_device_debugfs_init, #endif }; -#ifdef CONFIG_TRANSPARENT_HUGEPAGE -bool panfrost_transparent_hugepage = true; -module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400); -MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)"); -#endif - static int panfrost_probe(struct platform_device *pdev) { struct panfrost_device *pfdev; - int err; pfdev = devm_drm_dev_alloc(&pdev->dev, &panfrost_drm_driver, struct panfrost_device, base); @@ -973,50 +842,7 @@ static int panfrost_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pfdev); - pfdev->comp = of_device_get_match_data(&pdev->dev); - if (!pfdev->comp) - return -ENODEV; - - pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT; - - mutex_init(&pfdev->shrinker_lock); - INIT_LIST_HEAD(&pfdev->shrinker_list); - - err = panfrost_device_init(pfdev); - if (err) { - if (err != -EPROBE_DEFER) - dev_err(&pdev->dev, "Fatal error during GPU init\n"); - goto err_out0; - } - - pm_runtime_set_active(pfdev->base.dev); - pm_runtime_mark_last_busy(pfdev->base.dev); - pm_runtime_enable(pfdev->base.dev); - pm_runtime_set_autosuspend_delay(pfdev->base.dev, 50); /* ~3 frames */ - pm_runtime_use_autosuspend(pfdev->base.dev); - - /* - * Register the DRM device with the core and the connectors with - * sysfs - */ - err = drm_dev_register(&pfdev->base, 0); - if (err < 0) - goto err_out1; - - err = panfrost_gem_shrinker_init(&pfdev->base); - if (err) - goto err_out2; - - return 0; - -err_out2: - drm_dev_unregister(&pfdev->base); -err_out1: - pm_runtime_disable(pfdev->base.dev); - panfrost_device_fini(pfdev); - pm_runtime_set_suspended(pfdev->base.dev); -err_out0: - return err; + return panfrost_device_init(pfdev); } static void panfrost_remove(struct platform_device *pdev) @@ -1024,12 +850,8 @@ static void panfrost_remove(struct platform_device *pdev) struct panfrost_device *pfdev = platform_get_drvdata(pdev); drm_dev_unregister(&pfdev->base); - panfrost_gem_shrinker_cleanup(&pfdev->base); - pm_runtime_get_sync(pfdev->base.dev); - pm_runtime_disable(pfdev->base.dev); panfrost_device_fini(pfdev); - pm_runtime_set_suspended(pfdev->base.dev); } static ssize_t profiling_show(struct device *dev, diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.h b/drivers/gpu/drm/panfrost/panfrost_drv.h deleted file mode 100644 index a2277ec61aab..000000000000 --- a/drivers/gpu/drm/panfrost/panfrost_drv.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 or MIT */ -/* Copyright 2025 Amazon.com, Inc. or its affiliates */ - -#ifndef __PANFROST_DRV_H__ -#define __PANFROST_DRV_H__ - -extern bool panfrost_transparent_hugepage; - -#endif diff --git a/drivers/gpu/drm/panfrost/panfrost_features.h b/drivers/gpu/drm/panfrost/panfrost_features.h index 52f9d69f6db9..34c65847192f 100644 --- a/drivers/gpu/drm/panfrost/panfrost_features.h +++ b/drivers/gpu/drm/panfrost/panfrost_features.h @@ -121,6 +121,8 @@ enum panfrost_hw_feature { BIT_ULL(HW_FEATURE_IDVS_GROUP_SIZE) | \ BIT_ULL(HW_FEATURE_CLEAN_ONLY_SAFE)) +#define hw_features_g68 hw_features_g57 + static inline bool panfrost_has_hw_feature(struct panfrost_device *pfdev, enum panfrost_hw_feature feat) { diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c index 3a7fce428898..518f24549b9d 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem.c @@ -9,26 +9,38 @@ #include <linux/dma-mapping.h> #include <drm/panfrost_drm.h> +#include <drm/drm_debugfs.h> #include <drm/drm_print.h> #include "panfrost_device.h" -#include "panfrost_drv.h" #include "panfrost_gem.h" #include "panfrost_mmu.h" -void panfrost_gem_init(struct panfrost_device *pfdev) +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +static bool panfrost_transparent_hugepage = true; +module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400); +MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)"); +#else +#define panfrost_transparent_hugepage false +#endif + +int panfrost_gem_init(struct panfrost_device *pfdev) { - int err; + if (panfrost_transparent_hugepage) { + int err = drm_gem_huge_mnt_create(&pfdev->base, "within_size"); + + if (err) + drm_warn(&pfdev->base, + "Can't use Transparent Hugepage (%d)\n", err); + else + drm_info(&pfdev->base, "Using Transparent Hugepage\n"); + } - if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && - !panfrost_transparent_hugepage) - return; + return panfrost_gem_shrinker_init(pfdev); +} - err = drm_gem_huge_mnt_create(&pfdev->base, "within_size"); - if (drm_gem_get_huge_mnt(&pfdev->base)) - drm_info(&pfdev->base, "Using Transparent Hugepage\n"); - else if (err) - drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n", - err); +void panfrost_gem_fini(struct panfrost_device *pfdev) +{ + panfrost_gem_shrinker_fini(pfdev); } #ifdef CONFIG_DEBUG_FS @@ -724,8 +736,8 @@ static void panfrost_gem_debugfs_bo_print(struct panfrost_gem_object *bo, totals->reclaimable += resident_size; } -void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, - struct seq_file *m) +static void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, + struct seq_file *m) { struct gem_size_totals totals = {0}; struct panfrost_gem_object *bo; @@ -745,4 +757,28 @@ void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n", totals.size, totals.resident, totals.reclaimable); } + +static int panfrost_gems_show(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct panfrost_device *pfdev = to_panfrost_device(node->minor->dev); + + panfrost_gem_debugfs_print_bos(pfdev, m); + + return 0; +} + +static struct drm_info_list panfrost_debugfs_list[] = { + {"gems", + panfrost_gems_show, 0, NULL}, +}; + +int panfrost_gem_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(panfrost_debugfs_list, + ARRAY_SIZE(panfrost_debugfs_list), + minor->debugfs_root, minor); + + return 0; +} #endif diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h index 79d4377019e9..3008afcf4bff 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem.h +++ b/drivers/gpu/drm/panfrost/panfrost_gem.h @@ -129,7 +129,8 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node) return container_of(node, struct panfrost_gem_mapping, mmnode); } -void panfrost_gem_init(struct panfrost_device *pfdev); +int panfrost_gem_init(struct panfrost_device *pfdev); +void panfrost_gem_fini(struct panfrost_device *pfdev); struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size); @@ -154,8 +155,8 @@ panfrost_gem_mapping_get(struct panfrost_gem_object *bo, void panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping); void panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo); -int panfrost_gem_shrinker_init(struct drm_device *dev); -void panfrost_gem_shrinker_cleanup(struct drm_device *dev); +int panfrost_gem_shrinker_init(struct panfrost_device *pfdev); +void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev); void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label); int panfrost_gem_sync(struct drm_gem_object *obj, u32 type, @@ -163,8 +164,7 @@ int panfrost_gem_sync(struct drm_gem_object *obj, u32 type, void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label); #ifdef CONFIG_DEBUG_FS -void panfrost_gem_debugfs_print_bos(struct panfrost_device *pfdev, - struct seq_file *m); +int panfrost_gem_debugfs_init(struct drm_minor *minor); #endif #endif /* __PANFROST_GEM_H__ */ diff --git a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c index 2fe967a90bcb..72cd7a165e6d 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c +++ b/drivers/gpu/drm/panfrost/panfrost_gem_shrinker.c @@ -95,9 +95,10 @@ panfrost_gem_shrinker_scan(struct shrinker *shrinker, struct shrink_control *sc) * * This function registers and sets up the panfrost shrinker. */ -int panfrost_gem_shrinker_init(struct drm_device *dev) +int panfrost_gem_shrinker_init(struct panfrost_device *pfdev) { - struct panfrost_device *pfdev = to_panfrost_device(dev); + mutex_init(&pfdev->shrinker_lock); + INIT_LIST_HEAD(&pfdev->shrinker_list); pfdev->shrinker = shrinker_alloc(0, "drm-panfrost"); if (!pfdev->shrinker) @@ -118,10 +119,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev) * * This function unregisters the panfrost shrinker. */ -void panfrost_gem_shrinker_cleanup(struct drm_device *dev) +void panfrost_gem_shrinker_fini(struct panfrost_device *pfdev) { - struct panfrost_device *pfdev = to_panfrost_device(dev); - if (pfdev->shrinker) shrinker_free(pfdev->shrinker); } diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index 7d555e63e21a..6081b27829e1 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -242,6 +242,9 @@ static const struct panfrost_model gpu_models[] = { /* MediaTek MT8188 Mali-G57 MC3 */ GPU_MODEL(g57, 0x9093, GPU_REV(g57, 0, 0)), + + GPU_MODEL(g68, 0x9004, + GPU_REV(g68, 1, 0), GPU_REV(g68, 1, 1)), {0}, }; @@ -516,6 +519,8 @@ int panfrost_gpu_init(struct panfrost_device *pfdev) { int err; + spin_lock_init(&pfdev->cycle_counter.lock); + err = panfrost_gpu_soft_reset(pfdev); if (err) return err; diff --git a/drivers/gpu/drm/panfrost/panfrost_issues.h b/drivers/gpu/drm/panfrost/panfrost_issues.h index eb60cb83667a..dd39946bb572 100644 --- a/drivers/gpu/drm/panfrost/panfrost_issues.h +++ b/drivers/gpu/drm/panfrost/panfrost_issues.h @@ -265,6 +265,14 @@ enum panfrost_hw_issue { #define hw_issues_g57_r0p0 (\ BIT_ULL(HW_ISSUE_TTRX_3485)) +#define hw_issues_g68 (\ + BIT_ULL(HW_ISSUE_TTRX_2968_TTRX_3162)) + +#define hw_issues_g68_r1p0 (\ + BIT_ULL(HW_ISSUE_TTRX_3485)) + +#define hw_issues_g68_r1p1 0 + static inline bool panfrost_has_hw_issue(const struct panfrost_device *pfdev, enum panfrost_hw_issue issue) { diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index d59b4863b8ad..4ddf4b722ddf 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -8,6 +8,8 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/dma-resv.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_print.h> #include <drm/gpu_scheduler.h> #include <drm/panfrost_drm.h> @@ -874,6 +876,8 @@ int panfrost_jm_init(struct panfrost_device *pfdev) INIT_WORK(&pfdev->reset.work, panfrost_reset_work); spin_lock_init(&js->job_lock); + mutex_init(&pfdev->sched_lock); + init_rwsem(&pfdev->reset.lock); js->irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "job"); if (js->irq < 0) @@ -929,7 +933,7 @@ void panfrost_jm_fini(struct panfrost_device *pfdev) drm_sched_fini(&js->queue[j].sched); } - cancel_work_sync(&pfdev->reset.work); + disable_work_sync(&pfdev->reset.work); destroy_workqueue(pfdev->reset.wq); } @@ -1137,3 +1141,100 @@ int panfrost_jm_ctx_destroy(struct drm_file *file, u32 handle) panfrost_jm_ctx_put(jm_ctx); return 0; } + +#ifdef CONFIG_DEBUG_FS +static void show_panfrost_jm_ctx(struct panfrost_jm_ctx *jm_ctx, u32 handle, + struct seq_file *m) +{ + struct drm_device *ddev = ((struct drm_info_node *)m->private)->minor->dev; + const char *prio = "UNKNOWN"; + + static const char * const prios[] = { + [DRM_SCHED_PRIORITY_HIGH] = "HIGH", + [DRM_SCHED_PRIORITY_NORMAL] = "NORMAL", + [DRM_SCHED_PRIORITY_LOW] = "LOW", + }; + + if (jm_ctx->slot_entity[0].priority != + jm_ctx->slot_entity[1].priority) + drm_warn(ddev, "Slot priorities should be the same in a single context"); + + if (jm_ctx->slot_entity[0].priority < ARRAY_SIZE(prios)) + prio = prios[jm_ctx->slot_entity[0].priority]; + + seq_printf(m, " JM context %u: priority %s\n", handle, prio); +} + +static int show_file_jm_ctxs(struct panfrost_file_priv *pfile, + struct seq_file *m) +{ + struct panfrost_jm_ctx *jm_ctx; + unsigned long i; + + xa_lock(&pfile->jm_ctxs); + xa_for_each(&pfile->jm_ctxs, i, jm_ctx) { + jm_ctx = panfrost_jm_ctx_get(jm_ctx); + xa_unlock(&pfile->jm_ctxs); + show_panfrost_jm_ctx(jm_ctx, i, m); + panfrost_jm_ctx_put(jm_ctx); + xa_lock(&pfile->jm_ctxs); + } + xa_unlock(&pfile->jm_ctxs); + + return 0; +} + +static int show_each_file(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + int (*show)(struct panfrost_file_priv *, struct seq_file *) = + node->info_ent->data; + struct drm_file *file; + int ret; + + ret = mutex_lock_interruptible(&ddev->filelist_mutex); + if (ret) + return ret; + + list_for_each_entry(file, &ddev->filelist, lhead) { + struct task_struct *task; + struct panfrost_file_priv *pfile = file->driver_priv; + struct pid *pid; + + /* + * Although we have a valid reference on file->pid, that does + * not guarantee that the task_struct who called get_pid() is + * still alive (e.g. get_pid(current) => fork() => exit()). + * Therefore, we need to protect this ->comm access using RCU. + */ + rcu_read_lock(); + pid = rcu_dereference(file->pid); + task = pid_task(pid, PIDTYPE_TGID); + seq_printf(m, "client_id %8llu pid %8d command %s:\n", + file->client_id, pid_nr(pid), + task ? task->comm : "<unknown>"); + rcu_read_unlock(); + + ret = show(pfile, m); + if (ret < 0) + break; + + seq_puts(m, "\n"); + } + + mutex_unlock(&ddev->filelist_mutex); + return ret; +} + +static struct drm_info_list panfrost_sched_debugfs_list[] = { + { "sched_ctxs", show_each_file, 0, show_file_jm_ctxs }, +}; + +void panfrost_sched_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(panfrost_sched_debugfs_list, + ARRAY_SIZE(panfrost_sched_debugfs_list), + minor->debugfs_root, minor); +} +#endif diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h index c3f57e41a571..3e3b9717838f 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.h +++ b/drivers/gpu/drm/panfrost/panfrost_job.h @@ -74,4 +74,8 @@ int panfrost_job_get_slot(struct panfrost_job *job); int panfrost_job_push(struct panfrost_job *job); void panfrost_job_put(struct panfrost_job *job); +#ifdef CONFIG_DEBUG_FS +void panfrost_sched_debugfs_init(struct drm_minor *minor); +#endif + #endif diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index 4a3162c3b659..faa62643caf1 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -336,6 +336,12 @@ void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu WARN_ON(atomic_read(&mmu->as_count) < 0); } +static void panfrost_mmu_enable_interrupts(struct panfrost_device *pfdev) +{ + mmu_write(pfdev, MMU_INT_CLEAR, ~0); + mmu_write(pfdev, MMU_INT_MASK, ~0); +} + void panfrost_mmu_reset(struct panfrost_device *pfdev) { struct panfrost_mmu *mmu, *mmu_tmp; @@ -355,8 +361,7 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev) spin_unlock(&pfdev->as_lock); - mmu_write(pfdev, MMU_INT_CLEAR, ~0); - mmu_write(pfdev, MMU_INT_MASK, ~0); + panfrost_mmu_enable_interrupts(pfdev); } static size_t get_pgsize(u64 addr, size_t size, size_t *count) @@ -955,6 +960,9 @@ int panfrost_mmu_init(struct panfrost_device *pfdev) { int err; + INIT_LIST_HEAD(&pfdev->as_lru_list); + spin_lock_init(&pfdev->as_lock); + pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->base.dev), "mmu"); if (pfdev->mmu_irq < 0) return pfdev->mmu_irq; @@ -970,6 +978,8 @@ int panfrost_mmu_init(struct panfrost_device *pfdev) return err; } + panfrost_mmu_enable_interrupts(pfdev); + return 0; } diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c index 7020c0192e18..a9def452d5ef 100644 --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.c +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright 2019 Collabora Ltd */ +#include "asm-generic/errno-base.h" #include <linux/completion.h> #include <linux/iopoll.h> #include <linux/iosys-map.h> @@ -11,6 +12,7 @@ #include <drm/drm_file.h> #include <drm/drm_gem_shmem_helper.h> #include <drm/panfrost_drm.h> +#include <drm/drm_print.h> #include "panfrost_device.h" #include "panfrost_features.h" @@ -28,13 +30,33 @@ struct panfrost_perfcnt { struct panfrost_gem_mapping *mapping; + unsigned int counterset; size_t bosize; void *buf; struct panfrost_file_priv *user; struct mutex lock; struct completion dump_comp; + u32 state; + bool owns_as_ref; }; +static void panfrost_perfcnt_hw_disable(struct panfrost_device *pfdev) +{ + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; + + gpu_write(pfdev, GPU_PERFCNT_CFG, + GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF)); + gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0); + gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0); + gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0); + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0); + + if (perfcnt->owns_as_ref) { + panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu); + perfcnt->owns_as_ref = false; + } +} + void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev) { complete(&pfdev->perfcnt->dump_comp); @@ -42,30 +64,136 @@ void panfrost_perfcnt_clean_cache_done(struct panfrost_device *pfdev) void panfrost_perfcnt_sample_done(struct panfrost_device *pfdev) { - gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES); + if (pfdev->features.selected_coherency != COHERENCY_ACE) + gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_CACHES); + else + complete(&pfdev->perfcnt->dump_comp); +} + +static int panfrost_perfcnt_hw_enable(struct panfrost_device *pfdev) +{ + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; + u32 cfg, as; + int ret; + + lockdep_assert_held(&pfdev->reset.lock); + drm_WARN_ON(&pfdev->base, perfcnt->owns_as_ref); + + ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu); + if (ret < 0) + return ret; + + perfcnt->owns_as_ref = true; + + as = ret; + cfg = GPU_PERFCNT_CFG_AS(as) | + GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL); + + /* + * Bifrost GPUs have 2 set of counters, but we're only interested by + * the first one for now. + */ + if (panfrost_model_is_bifrost(pfdev)) + cfg |= GPU_PERFCNT_CFG_SETSEL(perfcnt->counterset); + + gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff); + gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff); + gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff); + + /* + * Due to PRLAM-8186 we need to disable the Tiler before we enable HW + * counters. + */ + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186)) + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0); + else + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff); + + gpu_write(pfdev, GPU_PERFCNT_CFG, cfg); + + if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186)) + gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff); + + return 0; } -static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev) +static int panfrost_perfcnt_dump_locked(struct panfrost_device *pfdev, u32 *state) { - u64 gpuva; + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; + u64 gpuva = perfcnt->mapping->mmnode.start << PAGE_SHIFT; int ret; - reinit_completion(&pfdev->perfcnt->dump_comp); - gpuva = pfdev->perfcnt->mapping->mmnode.start << PAGE_SHIFT; - gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva)); - gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva)); - gpu_write(pfdev, GPU_INT_CLEAR, - GPU_IRQ_CLEAN_CACHES_COMPLETED | - GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); - gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE); + scoped_guard(rwsem_read, &pfdev->reset.lock) { + *state = perfcnt->state; + if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD) + return -EIO; + + perfcnt->state = 0; + + reinit_completion(&pfdev->perfcnt->dump_comp); + + gpu_write(pfdev, GPU_PERFCNT_BASE_LO, lower_32_bits(gpuva)); + gpu_write(pfdev, GPU_PERFCNT_BASE_HI, upper_32_bits(gpuva)); + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_CLEAN_CACHES_COMPLETED | + GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); + gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_SAMPLE); + } + + /* + * Here we release the reset semaphore because perfcnt should not get in the way + * of a HW reset. Besides, a legitimate reset might be issued during the wait. + */ ret = wait_for_completion_interruptible_timeout(&pfdev->perfcnt->dump_comp, msecs_to_jiffies(1000)); + if (!ret) ret = -ETIMEDOUT; - else if (ret > 0) - ret = 0; - return ret; + /* We need to restore perfcnt state so the next successful dump can relay it to UM */ + if (ret < 0) { + perfcnt->state |= *state; + *state = 0; + return ret; + } + + scoped_guard(rwsem_read, &pfdev->reset.lock) { + *state |= perfcnt->state; + + /* UM must re-enable their session before requesting new dumps. */ + if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD) + return -EIO; + + /* If we faced a reset during our SAMPLE, the user needs to try again. */ + if (perfcnt->state & PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET) + return -EAGAIN; + } + + return 0; +} + +static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev, + struct drm_file *file_priv) +{ + struct panfrost_file_priv *user = file_priv->driver_priv; + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; + struct iosys_map map = IOSYS_MAP_INIT_VADDR(perfcnt->buf); + + if (user != perfcnt->user) + return -EINVAL; + + scoped_guard(rwsem_read, &pfdev->reset.lock) { + panfrost_perfcnt_hw_disable(pfdev); + perfcnt->user = NULL; + } + + drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map); + perfcnt->buf = NULL; + panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv); + panfrost_gem_mapping_put(perfcnt->mapping); + perfcnt->mapping = NULL; + pm_runtime_put_autosuspend(pfdev->base.dev); + + return 0; } static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, @@ -74,14 +202,29 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, { struct panfrost_file_priv *user = file_priv->driver_priv; struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; - struct iosys_map map; struct drm_gem_shmem_object *bo; - u32 cfg, as; + struct iosys_map map; int ret; - if (user == perfcnt->user) + if (perfcnt->user == user) { + scoped_guard(rwsem_read, &pfdev->reset.lock) { + if (perfcnt->state & PANFROST_PERFCNT_SESSION_DEAD) { + /* A successful reset might've come through in the time gap + * between the last failed one and this re-enable attempt. + */ + ret = (perfcnt->owns_as_ref) ? 0 : + panfrost_perfcnt_hw_enable(pfdev); + if (!ret) + perfcnt->state = 0; + + return ret; + } + } + return 0; - else if (perfcnt->user) + } + + if (perfcnt->user) return -EBUSY; ret = pm_runtime_get_sync(pfdev->base.dev); @@ -109,65 +252,30 @@ static int panfrost_perfcnt_enable_locked(struct panfrost_device *pfdev, ret = drm_gem_vmap(&bo->base, &map); if (ret) goto err_put_mapping; + perfcnt->buf = map.vaddr; + perfcnt->counterset = counterset; panfrost_gem_internal_set_label(&bo->base, "Perfcnt sample buffer"); - /* - * Invalidate the cache and clear the counters to start from a fresh - * state. - */ - reinit_completion(&pfdev->perfcnt->dump_comp); - gpu_write(pfdev, GPU_INT_CLEAR, - GPU_IRQ_CLEAN_CACHES_COMPLETED | - GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); - gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR); - gpu_write(pfdev, GPU_CMD, GPU_CMD_CLEAN_INV_CACHES); - ret = wait_for_completion_timeout(&pfdev->perfcnt->dump_comp, - msecs_to_jiffies(1000)); - if (!ret) { - ret = -ETIMEDOUT; - goto err_vunmap; - } - - ret = panfrost_mmu_as_get(pfdev, perfcnt->mapping->mmu); - if (ret < 0) - goto err_vunmap; - - as = ret; - cfg = GPU_PERFCNT_CFG_AS(as) | - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_MANUAL); - - /* - * Bifrost GPUs have 2 set of counters, but we're only interested by - * the first one for now. - */ - if (panfrost_model_is_bifrost(pfdev)) - cfg |= GPU_PERFCNT_CFG_SETSEL(counterset); - - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0xffffffff); - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0xffffffff); - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0xffffffff); - - /* - * Due to PRLAM-8186 we need to disable the Tiler before we enable HW - * counters. - */ - if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186)) - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0); - else - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff); + scoped_guard(rwsem_read, &pfdev->reset.lock) { + /* + * Clear the counters to start from a fresh state. + */ + gpu_write(pfdev, GPU_INT_CLEAR, GPU_IRQ_PERFCNT_SAMPLE_COMPLETED); + gpu_write(pfdev, GPU_CMD, GPU_CMD_PERFCNT_CLEAR); - gpu_write(pfdev, GPU_PERFCNT_CFG, cfg); + ret = panfrost_perfcnt_hw_enable(pfdev); + if (ret) + goto err_vunmap; - if (panfrost_has_hw_issue(pfdev, HW_ISSUE_8186)) - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0xffffffff); + perfcnt->user = user; + perfcnt->state = 0; + } /* The BO ref is retained by the mapping. */ drm_gem_object_put(&bo->base); - perfcnt->user = user; - return 0; err_vunmap: @@ -183,35 +291,6 @@ err_put_pm: return ret; } -static int panfrost_perfcnt_disable_locked(struct panfrost_device *pfdev, - struct drm_file *file_priv) -{ - struct panfrost_file_priv *user = file_priv->driver_priv; - struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; - struct iosys_map map = IOSYS_MAP_INIT_VADDR(perfcnt->buf); - - if (user != perfcnt->user) - return -EINVAL; - - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0x0); - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0x0); - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0x0); - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0); - gpu_write(pfdev, GPU_PERFCNT_CFG, - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF)); - - perfcnt->user = NULL; - drm_gem_vunmap(&perfcnt->mapping->obj->base.base, &map); - perfcnt->buf = NULL; - panfrost_gem_close(&perfcnt->mapping->obj->base.base, file_priv); - panfrost_mmu_as_put(pfdev, perfcnt->mapping->mmu); - panfrost_gem_mapping_put(perfcnt->mapping); - perfcnt->mapping = NULL; - pm_runtime_put_autosuspend(pfdev->base.dev); - - return 0; -} - int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -252,13 +331,16 @@ int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data, if (ret) return ret; + if (req->pad) + return -EINVAL; + mutex_lock(&perfcnt->lock); if (perfcnt->user != file_priv->driver_priv) { ret = -EINVAL; goto out; } - ret = panfrost_perfcnt_dump_locked(pfdev); + ret = panfrost_perfcnt_dump_locked(pfdev, &req->state); if (ret) goto out; @@ -325,18 +407,12 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev) return -ENOMEM; perfcnt->bosize = size; + pfdev->perfcnt = perfcnt; + mutex_init(&perfcnt->lock); + init_completion(&perfcnt->dump_comp); /* Start with everything disabled. */ - gpu_write(pfdev, GPU_PERFCNT_CFG, - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF)); - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0); - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0); - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0); - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0); - - init_completion(&perfcnt->dump_comp); - mutex_init(&perfcnt->lock); - pfdev->perfcnt = perfcnt; + panfrost_perfcnt_hw_disable(pfdev); return 0; } @@ -344,10 +420,27 @@ int panfrost_perfcnt_init(struct panfrost_device *pfdev) void panfrost_perfcnt_fini(struct panfrost_device *pfdev) { /* Disable everything before leaving. */ - gpu_write(pfdev, GPU_PERFCNT_CFG, - GPU_PERFCNT_CFG_MODE(GPU_PERFCNT_CFG_MODE_OFF)); - gpu_write(pfdev, GPU_PRFCNT_JM_EN, 0); - gpu_write(pfdev, GPU_PRFCNT_SHADER_EN, 0); - gpu_write(pfdev, GPU_PRFCNT_MMU_L2_EN, 0); - gpu_write(pfdev, GPU_PRFCNT_TILER_EN, 0); + panfrost_perfcnt_hw_disable(pfdev); +} + +void panfrost_perfcnt_reset(struct panfrost_device *pfdev) +{ + struct panfrost_perfcnt *perfcnt = pfdev->perfcnt; + + if (drm_WARN_ON(&pfdev->base, !perfcnt)) + return; + + lockdep_assert_held(&pfdev->reset.lock); + + if (!perfcnt->user) + return; + + /* All active AS are released during the MMU post_reset. */ + perfcnt->owns_as_ref = false; + perfcnt->state |= PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET; + if (panfrost_perfcnt_hw_enable(pfdev)) + perfcnt->state |= PANFROST_PERFCNT_SESSION_DEAD; + + /* Unblock pending sample requests. */ + complete(&perfcnt->dump_comp); } diff --git a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h index 8bbcf5f5fb33..8b9bc704b634 100644 --- a/drivers/gpu/drm/panfrost/panfrost_perfcnt.h +++ b/drivers/gpu/drm/panfrost/panfrost_perfcnt.h @@ -14,5 +14,6 @@ int panfrost_ioctl_perfcnt_enable(struct drm_device *dev, void *data, struct drm_file *file_priv); int panfrost_ioctl_perfcnt_dump(struct drm_device *dev, void *data, struct drm_file *file_priv); +void panfrost_perfcnt_reset(struct panfrost_device *pfdev); #endif diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 1ca8aedd83f4..dc8d0191aef4 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -1081,7 +1081,7 @@ static int panthor_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data, struct drm_panthor_bo_mmap_offset *args = data; struct panthor_gem_object *bo; struct drm_gem_object *obj; - int ret; + int ret = 0; if (args->pad) return -EINVAL; @@ -1096,10 +1096,6 @@ static int panthor_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data, goto out; } - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto out; - args->offset = drm_vma_node_offset_addr(&obj->vma_node); out: diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index c07a44057e42..22b57ab6f929 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -744,6 +744,9 @@ static int panthor_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *v struct panthor_gem_object *bo = to_panthor_bo(obj); int ret; + if (bo->flags & DRM_PANTHOR_BO_NO_MMAP) + return -EINVAL; + 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 diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 0493858e55d7..a343c5180e01 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -3770,12 +3770,18 @@ int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle) struct panthor_group_pool *gpool = pfile->groups; struct panthor_device *ptdev = pfile->ptdev; struct panthor_scheduler *sched = ptdev->scheduler; - struct panthor_group *group; + struct panthor_group *group = NULL; - if (!xa_get_mark(&gpool->xa, group_handle, GROUP_REGISTERED)) - return -EINVAL; + /* + * Check the mark and erase the entry atomically, so a concurrent + * destroy + create can't make us erase a group that's still being + * initialized and happens to reuse the same handle. + */ + xa_lock(&gpool->xa); + if (xa_get_mark(&gpool->xa, group_handle, GROUP_REGISTERED)) + group = __xa_erase(&gpool->xa, group_handle); + xa_unlock(&gpool->xa); - group = xa_erase(&gpool->xa, group_handle); if (!group) return -EINVAL; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c index 9c4cf68d79d5..f2a1ea8dfefa 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -24,7 +24,6 @@ #include <drm/drm_bridge_connector.h> #include <drm/drm_managed.h> #include <drm/drm_of.h> -#include <drm/drm_probe_helper.h> #include "rockchip_drm_drv.h" @@ -34,6 +33,7 @@ #define RK3576_IOC_HDMI_HPD_STATUS 0xa440 #define RK3576_HDMI_LEVEL_INT BIT(3) +#define RK3576_HDMI_OHPD_INT BIT(4) #define RK3576_VO0_GRF_SOC_CON1 0x0004 #define RK3576_HDMI_FRL_MOD BIT(0) @@ -64,7 +64,9 @@ #define RK3588_HPD_HDMI1_IO_EN_MASK BIT(13) #define RK3588_GRF_SOC_STATUS1 0x0384 #define RK3588_HDMI0_LEVEL_INT BIT(16) +#define RK3588_HDMI0_OHPD_INT BIT(17) #define RK3588_HDMI1_LEVEL_INT BIT(24) +#define RK3588_HDMI1_OHPD_INT BIT(25) #define RK3588_GRF_VO1_CON3 0x000c #define RK3588_GRF_VO1_CON6 0x0018 #define RK3588_COLOR_DEPTH_MASK GENMASK(7, 4) @@ -96,6 +98,7 @@ struct rockchip_hdmi_qp { struct phy *phy; struct gpio_desc *frl_enable_gpio; struct delayed_work hpd_work; + int hpd_irq; int port_id; const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops; unsigned long long tmds_char_rate; @@ -274,9 +277,8 @@ dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) return val ? connector_status_connected : connector_status_disconnected; } -static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +static void dw_hdmi_qp_rk3588_unmask_hpd(struct rockchip_hdmi_qp *hdmi) { - struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; u32 val; if (hdmi->port_id) @@ -289,11 +291,48 @@ static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); } +static void dw_hdmi_qp_rk3588_mask_hpd(struct rockchip_hdmi_qp *hdmi) +{ + u32 val; + + if (hdmi->port_id) + val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1); + else + val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1); + + regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); +} + +static void dw_hdmi_qp_rk3588_enable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + + dw_hdmi_qp_rk3588_unmask_hpd(hdmi); + enable_irq(hdmi->hpd_irq); +} + +static void dw_hdmi_qp_rk3588_disable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + + disable_irq(hdmi->hpd_irq); + dw_hdmi_qp_rk3588_mask_hpd(hdmi); + + /* + * Cannot use the _sync() variant: the work takes mode_config.mutex, + * which the connector probe path already holds when acquiring the + * hpd_state_mutex this caller runs under. Requeuing is already ruled + * out by disable_irq(). + */ + cancel_delayed_work(&hdmi->hpd_work); +} + static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = { .init = dw_hdmi_qp_rk3588_phy_init, .disable = dw_hdmi_qp_rk3588_phy_disable, .read_hpd = dw_hdmi_qp_rk3588_read_hpd, - .setup_hpd = dw_hdmi_qp_rk3588_setup_hpd, + .enable_hpd = dw_hdmi_qp_rk3588_enable_hpd, + .disable_hpd = dw_hdmi_qp_rk3588_disable_hpd, }; static enum drm_connector_status @@ -308,23 +347,49 @@ dw_hdmi_qp_rk3576_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) connector_status_connected : connector_status_disconnected; } -static void dw_hdmi_qp_rk3576_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +static void dw_hdmi_qp_rk3576_unmask_hpd(struct rockchip_hdmi_qp *hdmi) +{ + regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, + FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1) | + FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0)); +} + +static void dw_hdmi_qp_rk3576_mask_hpd(struct rockchip_hdmi_qp *hdmi) +{ + regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, + FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 1)); +} + +static void dw_hdmi_qp_rk3576_enable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) { struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; - u32 val; - val = (FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1) | - FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0)); + dw_hdmi_qp_rk3576_unmask_hpd(hdmi); + enable_irq(hdmi->hpd_irq); +} - regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val); - regmap_write(hdmi->regmap, 0xa404, 0xffff0102); +static void dw_hdmi_qp_rk3576_disable_hpd(struct dw_hdmi_qp *dw_hdmi, void *data) +{ + struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data; + + disable_irq(hdmi->hpd_irq); + dw_hdmi_qp_rk3576_mask_hpd(hdmi); + + /* + * Cannot use the _sync() variant: the work takes mode_config.mutex, + * which the connector probe path already holds when acquiring the + * hpd_state_mutex this caller runs under. Requeuing is already ruled + * out by disable_irq(). + */ + cancel_delayed_work(&hdmi->hpd_work); } static const struct dw_hdmi_qp_phy_ops rk3576_hdmi_phy_ops = { .init = dw_hdmi_qp_rk3588_phy_init, .disable = dw_hdmi_qp_rk3588_phy_disable, .read_hpd = dw_hdmi_qp_rk3576_read_hpd, - .setup_hpd = dw_hdmi_qp_rk3576_setup_hpd, + .enable_hpd = dw_hdmi_qp_rk3576_enable_hpd, + .disable_hpd = dw_hdmi_qp_rk3576_disable_hpd, }; static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work) @@ -332,26 +397,19 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work) struct rockchip_hdmi_qp *hdmi = container_of(work, struct rockchip_hdmi_qp, hpd_work.work); - struct drm_device *drm = hdmi->encoder.encoder.dev; - bool changed; - if (drm) { - changed = drm_helper_hpd_irq_event(drm); - if (changed) - dev_dbg(hdmi->dev, "connector status changed\n"); - } + dw_hdmi_qp_hpd_notify(hdmi->hdmi); } static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id) { struct rockchip_hdmi_qp *hdmi = dev_id; - u32 intr_stat, val; + u32 intr_stat; regmap_read(hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, &intr_stat); - if (intr_stat) { - val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 1); - regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val); + if (intr_stat & RK3576_HDMI_OHPD_INT) { + dw_hdmi_qp_rk3576_mask_hpd(hdmi); return IRQ_WAKE_THREAD; } @@ -361,15 +419,11 @@ static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id) static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id) { struct rockchip_hdmi_qp *hdmi = dev_id; - u32 val; - val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1); - regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val); mod_delayed_work(system_percpu_wq, &hdmi->hpd_work, msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); - val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0); - regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val); + dw_hdmi_qp_rk3576_unmask_hpd(hdmi); return IRQ_HANDLED; } @@ -377,16 +431,14 @@ static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id) static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id) { struct rockchip_hdmi_qp *hdmi = dev_id; - u32 intr_stat, val; + u32 intr_stat; regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat); + intr_stat &= hdmi->port_id ? RK3588_HDMI1_OHPD_INT : RK3588_HDMI0_OHPD_INT; + if (intr_stat) { - if (hdmi->port_id) - val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1); - else - val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1); - regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + dw_hdmi_qp_rk3588_mask_hpd(hdmi); return IRQ_WAKE_THREAD; } @@ -396,22 +448,11 @@ static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id) static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id) { struct rockchip_hdmi_qp *hdmi = dev_id; - u32 val; - - if (hdmi->port_id) - val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_CLR, 1); - else - val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_CLR, 1); - regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); mod_delayed_work(system_percpu_wq, &hdmi->hpd_work, msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS)); - if (hdmi->port_id) - val |= FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 0); - else - val |= FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 0); - regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + dw_hdmi_qp_rk3588_unmask_hpd(hdmi); return IRQ_HANDLED; } @@ -424,11 +465,11 @@ static void dw_hdmi_qp_rk3576_io_init(struct rockchip_hdmi_qp *hdmi) FIELD_PREP_WM16(RK3576_SDAIN_MASK, 1) | FIELD_PREP_WM16(RK3576_HDMI_GRANT_SEL, 1) | FIELD_PREP_WM16(RK3576_I2S_SEL_MASK, 1); - regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON14, val); - val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0); - regmap_write(hdmi->regmap, RK3576_IOC_MISC_CON0, val); + regmap_write(hdmi->regmap, 0xa404, 0xffff0102); + + dw_hdmi_qp_rk3576_mask_hpd(hdmi); } static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi) @@ -453,11 +494,7 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi) val = FIELD_PREP_WM16(RK3588_HDMI0_GRANT_SEL, 1); regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val); - if (hdmi->port_id) - val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1); - else - val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1); - regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val); + dw_hdmi_qp_rk3588_mask_hpd(hdmi); } static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi, @@ -561,9 +598,9 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, struct resource *res; struct clk_bulk_data *clks; struct clk *ref_clk; - int ret, irq, i; + int ret, i; - if (!pdev->dev.of_node) + if (!dev->of_node) return -ENODEV; hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL); @@ -583,7 +620,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, return dev_err_probe(dev, -ENODEV, "Missing platform ctrl ops\n"); hdmi->ctrl_ops = cfg->ctrl_ops; - hdmi->dev = &pdev->dev; + hdmi->dev = dev; hdmi->port_id = -ENODEV; /* Identify port ID by matching base IO address */ @@ -594,7 +631,7 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, } } if (hdmi->port_id < 0) - return dev_err_probe(hdmi->dev, hdmi->port_id, + return dev_err_probe(dev, hdmi->port_id, "Failed to match HDMI port ID\n"); plat_data.phy_ops = cfg->phy_ops; @@ -622,37 +659,36 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); if (IS_ERR(hdmi->regmap)) - return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->regmap), + return dev_err_probe(dev, PTR_ERR(hdmi->regmap), "Unable to get rockchip,grf\n"); hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,vo-grf"); if (IS_ERR(hdmi->vo_regmap)) - return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->vo_regmap), + return dev_err_probe(dev, PTR_ERR(hdmi->vo_regmap), "Unable to get rockchip,vo-grf\n"); - ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks); + ret = devm_clk_bulk_get_all_enabled(dev, &clks); if (ret < 0) - return dev_err_probe(hdmi->dev, ret, "Failed to get clocks\n"); + return dev_err_probe(dev, ret, "Failed to get clocks\n"); - ref_clk = clk_get(hdmi->dev, "ref"); + ref_clk = clk_get(dev, "ref"); if (IS_ERR(ref_clk)) - return dev_err_probe(hdmi->dev, PTR_ERR(ref_clk), + return dev_err_probe(dev, PTR_ERR(ref_clk), "Failed to get ref clock\n"); plat_data.ref_clk_rate = clk_get_rate(ref_clk); clk_put(ref_clk); - hdmi->frl_enable_gpio = devm_gpiod_get_optional(hdmi->dev, "frl-enable", + hdmi->frl_enable_gpio = devm_gpiod_get_optional(dev, "frl-enable", GPIOD_OUT_LOW); if (IS_ERR(hdmi->frl_enable_gpio)) - return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->frl_enable_gpio), + return dev_err_probe(dev, PTR_ERR(hdmi->frl_enable_gpio), "Failed to request FRL enable GPIO\n"); hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0); if (IS_ERR(hdmi->phy)) - return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->phy), - "Failed to get phy\n"); + return dev_err_probe(dev, PTR_ERR(hdmi->phy), "Failed to get phy\n"); cfg->ctrl_ops->io_init(hdmi); @@ -666,14 +702,14 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, if (plat_data.cec_irq < 0) return plat_data.cec_irq; - irq = platform_get_irq_byname(pdev, "hpd"); - if (irq < 0) - return irq; + hdmi->hpd_irq = platform_get_irq_byname(pdev, "hpd"); + if (hdmi->hpd_irq < 0) + return hdmi->hpd_irq; - ret = devm_request_threaded_irq(hdmi->dev, irq, + ret = devm_request_threaded_irq(dev, hdmi->hpd_irq, cfg->ctrl_ops->hardirq_callback, cfg->ctrl_ops->irq_callback, - IRQF_SHARED, "dw-hdmi-qp-hpd", + IRQF_NO_AUTOEN, "dw-hdmi-qp-hpd", hdmi); if (ret) return ret; @@ -681,20 +717,20 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs); 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); + return dev_err_probe(dev, ret, "Failed to init encoder\n"); hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data); if (IS_ERR(hdmi->hdmi)) - return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->hdmi), - "Failed to bind dw-hdmi-qp"); + return dev_err_probe(dev, PTR_ERR(hdmi->hdmi), + "Failed to bind dw-hdmi-qp\n"); connector = drm_bridge_connector_init(drm, encoder); if (IS_ERR(connector)) - return dev_err_probe(hdmi->dev, PTR_ERR(connector), + return dev_err_probe(dev, PTR_ERR(connector), "Failed to init bridge connector\n"); + platform_set_drvdata(pdev, hdmi); + return 0; } @@ -704,6 +740,8 @@ static void dw_hdmi_qp_rockchip_unbind(struct device *dev, { struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev); + dev_set_drvdata(dev, NULL); + cancel_delayed_work_sync(&hdmi->hpd_work); } @@ -726,6 +764,11 @@ static int __maybe_unused dw_hdmi_qp_rockchip_suspend(struct device *dev) { struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev); + if (!hdmi) + return 0; + + cancel_delayed_work_sync(&hdmi->hpd_work); + dw_hdmi_qp_suspend(dev, hdmi->hdmi); return 0; @@ -735,12 +778,14 @@ static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev) { struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev); + if (!hdmi) + return 0; + hdmi->ctrl_ops->io_init(hdmi); dw_hdmi_qp_resume(dev, hdmi->hdmi); - if (hdmi->encoder.encoder.dev) - drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev); + dw_hdmi_qp_hpd_notify(hdmi->hdmi); return 0; } diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index f28d6a883e09..273f61758b10 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -14,6 +14,7 @@ #include <drm/drm_probe_helper.h> #include <linux/clk.h> +#include <linux/hdmi.h> #include <linux/mfd/syscon.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -703,7 +704,6 @@ rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) DRM_MODE_ENCODER_TMDS, NULL); hdmi->bridge.driver_private = hdmi; - hdmi->bridge.funcs = &rk3066_hdmi_bridge_funcs; hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HDMI | @@ -712,6 +712,7 @@ rk3066_hdmi_register(struct drm_device *drm, struct rk3066_hdmi *hdmi) hdmi->bridge.type = DRM_MODE_CONNECTOR_HDMIA; hdmi->bridge.vendor = "Rockchip"; hdmi->bridge.product = "RK3066 HDMI"; + hdmi->bridge.supported_hdmi_ver = HDMI_VERSION_1_4; hdmi->bridge.ddc = rk3066_hdmi_i2c_adapter(hdmi); if (IS_ERR(hdmi->bridge.ddc)) @@ -747,9 +748,10 @@ static int rk3066_hdmi_bind(struct device *dev, struct device *master, int irq; int ret; - hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL); - if (!hdmi) - return -ENOMEM; + hdmi = devm_drm_bridge_alloc(dev, struct rk3066_hdmi, bridge, + &rk3066_hdmi_bridge_funcs); + if (IS_ERR(hdmi)) + return PTR_ERR(hdmi); hdmi->dev = dev; hdmi->drm_dev = drm; diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index b9580ce1ddec..99252674e266 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -1202,6 +1202,11 @@ static void drm_sched_cancel_remaining_jobs(struct drm_gpu_scheduler *sched) * is implemented, all jobs will be canceled through it and afterwards cleaned * up through &struct drm_sched_backend_ops.free_job. If cancel_job is not * implemented, memory could leak. + * + * The user must wait one RCU grace period between signaling the last hardware- + * fence and calling this function because the timeline name returned by + * scheduler fences must stay valid until all readers have exited their RCU read + * side critical section. */ void drm_sched_fini(struct drm_gpu_scheduler *sched) { diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 8cbf18754487..ee43cf0da879 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -1985,7 +1985,7 @@ static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = }; static const struct drm_connector_funcs ssd130x_connector_funcs = { - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index d98b6f12a7bc..6f4fe0c8efae 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -192,10 +192,6 @@ sun4i_hdmi_connector_clock_valid(const struct drm_connector *connector, if (mode->flags & DRM_MODE_FLAG_DBLCLK) return MODE_BAD; - /* HDMI 1.0 max TMDS character rate */ - if (clock > HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ) - return MODE_CLOCK_HIGH; - rounded_rate = clk_round_rate(hdmi->tmds_clk, clock); if (rounded_rate > 0 && max_t(unsigned long, rounded_rate, clock) - @@ -253,6 +249,15 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev) } static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = { + /* + * NOTE: Those are likely to be wrong, but I couldn't find + * the actual ones in the BSP. + */ + .vendor = "AW", + .product = "HDMI", + .supported_hdmi_ver = HDMI_VERSION_1_2, + .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), + .max_bpc = 8, .tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid, .avi = { .clear_infoframe = sun4i_hdmi_clear_avi_infoframe, @@ -654,19 +659,11 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, drm_connector_helper_add(&hdmi->connector, &sun4i_hdmi_connector_helper_funcs); - ret = drmm_connector_hdmi_ini2(drm, &hdmi->connector, - /* - * NOTE: Those are likely to be - * wrong, but I couldn't find the - * actual ones in the BSP. - */ - "AW", "HDMI", + ret = drmm_connector_hdmi_init(drm, &hdmi->connector, &sun4i_hdmi_connector_funcs, &sun4i_hdmi_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA, - hdmi->ddc_i2c, - BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), - 8); + hdmi->ddc_i2c); if (ret) { dev_err(dev, "Couldn't initialise the HDMI connector\n"); diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index c043428e02fd..6dab27b41c08 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -194,7 +194,7 @@ int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector); .get_modes = drm_sysfb_connector_helper_get_modes #define DRM_SYSFB_CONNECTOR_FUNCS \ - .reset = drm_atomic_helper_connector_reset, \ + .atomic_create_state = drm_atomic_helper_connector_create_state, \ .fill_modes = drm_helper_probe_single_connector_modes, \ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, \ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c index 4297d9c1125e..ebfe37557bfe 100644 --- a/drivers/gpu/drm/tiny/appletbdrm.c +++ b/drivers/gpu/drm/tiny/appletbdrm.c @@ -30,6 +30,7 @@ #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_modeset_helper.h> #include <drm/drm_plane.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -589,7 +590,7 @@ static const struct drm_mode_config_funcs appletbdrm_mode_config_funcs = { }; static const struct drm_connector_funcs appletbdrm_connector_funcs = { - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .destroy = drm_connector_cleanup, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -724,7 +725,7 @@ static int appletbdrm_setup_mode_config(struct appletbdrm_device *adev) ret = drm_connector_attach_encoder(connector, encoder); if (ret) { - drm_err(drm, "Failed to initialize simple display pipe\n"); + drm_err(drm, "Failed to attach connector to encoder\n"); return ret; } @@ -821,6 +822,48 @@ static void appletbdrm_shutdown(struct usb_interface *intf) drm_atomic_helper_shutdown(&adev->drm); } +static int appletbdrm_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct appletbdrm_device *adev = usb_get_intfdata(intf); + + return drm_mode_config_helper_suspend(&adev->drm); +} + +static int appletbdrm_resume(struct usb_interface *intf) +{ + struct appletbdrm_device *adev = usb_get_intfdata(intf); + + return drm_mode_config_helper_resume(&adev->drm); +} + +static int appletbdrm_reset_resume(struct usb_interface *intf) +{ + struct appletbdrm_device *adev = usb_get_intfdata(intf); + struct drm_device *drm = &adev->drm; + int ret, resume_ret; + + /* + * A reset puts the device back into its initial state, where it + * expects the same handshake as during probe. Resume the DRM side + * even if the handshake fails, so that the state saved on suspend + * is released. + */ + ret = appletbdrm_get_information(adev); + if (ret) { + drm_err(drm, "Failed to get display information\n"); + goto resume; + } + + ret = appletbdrm_signal_readiness(adev); + if (ret) + drm_err(drm, "Failed to signal readiness\n"); + +resume: + resume_ret = drm_mode_config_helper_resume(drm); + + return ret ? ret : resume_ret; +} + static const struct usb_device_id appletbdrm_usb_id_table[] = { { USB_DEVICE_INTERFACE_CLASS(0x05ac, 0x8302, USB_CLASS_AUDIO_VIDEO) }, {} @@ -832,6 +875,9 @@ static struct usb_driver appletbdrm_usb_driver = { .probe = appletbdrm_probe, .disconnect = appletbdrm_disconnect, .shutdown = appletbdrm_shutdown, + .suspend = appletbdrm_suspend, + .resume = appletbdrm_resume, + .reset_resume = appletbdrm_reset_resume, .id_table = appletbdrm_usb_id_table, }; module_usb_driver(appletbdrm_usb_driver); diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index bd0b23ac2cc4..f9458a4d5a09 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -356,6 +356,10 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, if (ret) return ret; + /* Imported sg tables only come with DMA addresses, no pages to map. */ + if (!ttm->pages) + return -EINVAL; + if (num_pages == 1 && ttm->caching == ttm_cached && !(man->use_tt && (ttm->page_flags & TTM_TT_FLAG_DECRYPTED))) { /* @@ -419,7 +423,7 @@ EXPORT_SYMBOL(ttm_bo_kmap_try_from_panic); * * Returns * -ENOMEM: Out of memory. - * -EINVAL: Invalid range. + * -EINVAL: Invalid range or the BO has no pages to map. */ int ttm_bo_kmap(struct ttm_buffer_object *bo, unsigned long start_page, unsigned long num_pages, @@ -493,7 +497,7 @@ EXPORT_SYMBOL(ttm_bo_kunmap); * * Returns * -ENOMEM: Out of memory. - * -EINVAL: Invalid range. + * -EINVAL: Invalid range or the BO has no pages to map. */ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map) { @@ -537,6 +541,9 @@ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map) if (ret) return ret; + if (!ttm->pages) + return -EINVAL; + /* * We need to use vmap to get the desired page protection * or to make the buffer object look contiguous. diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c index b53c3635ce67..54f952e4b1ac 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c @@ -17,7 +17,7 @@ static const struct drm_connector_helper_funcs vc4_dummy_connector_helper_funcs static const struct drm_connector_funcs vc4_dummy_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, }; struct vc4_dummy_output *vc4_dummy_output(struct kunit *test, diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 49ea2ed0996b..320c5a0c71ed 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -303,7 +303,8 @@ static void vc4_bo_purge(struct drm_gem_object *obj) drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); - dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.dma_addr); + dma_free_wc(drm_dev_dma_dev(dev), obj->size, bo->base.vaddr, + bo->base.dma_addr); bo->base.vaddr = NULL; bo->madv = __VC4_MADV_PURGED; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index eb859c65a453..83a9443db98f 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -24,10 +24,8 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/device.h> -#include <linux/dma-mapping.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> @@ -273,15 +271,6 @@ static void vc4_component_unbind_all(void *ptr) component_unbind_all(vc4->dev, &vc4->base); } -static const struct of_device_id vc4_dma_range_matches[] = { - { .compatible = "brcm,bcm2711-hvs" }, - { .compatible = "brcm,bcm2712-hvs" }, - { .compatible = "brcm,bcm2835-hvs" }, - { .compatible = "brcm,bcm2835-v3d" }, - { .compatible = "brcm,cygnus-v3d" }, - {} -}; - static int vc4_drm_bind(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -294,8 +283,6 @@ static int vc4_drm_bind(struct device *dev) enum vc4_gen gen; int ret = 0; - dev->coherent_dma_mask = DMA_BIT_MASK(32); - gen = (enum vc4_gen)of_device_get_match_data(dev); if (gen > VC4_GEN_4) @@ -303,21 +290,6 @@ static int vc4_drm_bind(struct device *dev) else driver = &vc4_drm_driver; - if (gen >= VC4_GEN_6_C) - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); - else - dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); - - node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches, - NULL); - if (node) { - ret = of_dma_configure(dev, node, true); - of_node_put(node); - - if (ret) - return ret; - } - vc4 = devm_drm_dev_alloc(dev, driver, struct vc4_dev, base); if (IS_ERR(vc4)) return PTR_ERR(vc4); diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index e715147d091c..b1fb096858cc 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -22,6 +22,7 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/component.h> +#include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <drm/drm_atomic_helper.h> @@ -1662,6 +1663,15 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs); } + if (vc4->gen >= VC4_GEN_6_C) { + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + } + + if (drm_dev_dma_dev(drm) == drm->dev) + drm_dev_set_dma_dev(drm, dev); + if (vc4->gen >= VC4_GEN_5) { struct rpi_firmware *firmware; struct device_node *node; diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index c469f8b1068b..642953cba25f 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -378,7 +378,7 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = { .detect = vc4_txp_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, + .atomic_create_state = drm_atomic_helper_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index b40d98c9d1d2..6b85053c77d6 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -423,6 +423,13 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) vc4->v3d = v3d; v3d->vc4 = vc4; + /* + * In headless setups where HVS is absent, use V3D as the fallback + * DMA controller for buffer allocations. + */ + if (drm_dev_dma_dev(drm) == drm->dev) + drm_dev_set_dma_dev(drm, dev); + 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"); diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 10718bc73693..eef9ef42dffe 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -38,6 +38,7 @@ static const uint32_t virtio_gpu_formats[] = { DRM_FORMAT_HOST_XRGB8888, + DRM_FORMAT_HOST_ARGB8888, }; static const uint32_t virtio_gpu_cursor_formats[] = { diff --git a/include/drm/bridge/dw_hdmi_qp.h b/include/drm/bridge/dw_hdmi_qp.h index 6ea9c561cfef..ad33f9cafdeb 100644 --- a/include/drm/bridge/dw_hdmi_qp.h +++ b/include/drm/bridge/dw_hdmi_qp.h @@ -16,7 +16,8 @@ struct dw_hdmi_qp_phy_ops { int (*init)(struct dw_hdmi_qp *hdmi, void *data); void (*disable)(struct dw_hdmi_qp *hdmi, void *data); enum drm_connector_status (*read_hpd)(struct dw_hdmi_qp *hdmi, void *data); - void (*setup_hpd)(struct dw_hdmi_qp *hdmi, void *data); + void (*enable_hpd)(struct dw_hdmi_qp *hdmi, void *data); + void (*disable_hpd)(struct dw_hdmi_qp *hdmi, void *data); }; struct dw_hdmi_qp_plat_data { @@ -34,6 +35,7 @@ struct dw_hdmi_qp_plat_data { struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_qp_plat_data *plat_data); +void dw_hdmi_qp_hpd_notify(struct dw_hdmi_qp *hdmi); void dw_hdmi_qp_suspend(struct device *dev, struct dw_hdmi_qp *hdmi); void dw_hdmi_qp_resume(struct device *dev, struct dw_hdmi_qp *hdmi); #endif /* __DW_HDMI_QP__ */ diff --git a/include/drm/gpu_scheduler.h b/include/drm/gpu_scheduler.h index 563d7fb88a6d..3b0978fa8cf0 100644 --- a/include/drm/gpu_scheduler.h +++ b/include/drm/gpu_scheduler.h @@ -101,7 +101,7 @@ struct drm_sched_entity { * * Lock protecting the run-queue (@rq) to which this entity belongs, * @priority, @last_scheduled and the list of schedulers (@sched_list, - * @num_sched_list) and @rr_ts. + * @num_sched_list), @rr_ts and @stopped. */ spinlock_t lock; diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index a103fdc0b3a3..0bb049f91524 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -1627,6 +1627,30 @@ drm_fourcc_canonicalize_nvidia_format_mod(__u64 modifier) #define DRM_FORMAT_MOD_APPLE_GPU_TILED_COMPRESSED fourcc_mod_code(APPLE, 2) /* + * Apple Interchange layout + * + * Apple Interchange is a compressed framebuffer format used exclusively for + * sharing buffers between hardware blocks (GPU, display controller, video + * codecs) on Apple platforms. It is similar to and uses the same compression + * scheme as GPU_TILED_COMPRESSED. There is no known uncompressed variant + * of Apple Interchange. + * + * Unlike the GPU layouts, tile size is not dependent on bit depth. RGB + * images and the chroma components of YUV images use 16x16 tiles. The + * luma component of YUV images uses 32x32 tiles. Compression tiles + * are once again 16x16 with 8 bytes of metadata per compression subtile. + * + * Following the example set by GPU_TILED_{,COMPRESSED}, Interchange images + * are treated as a single plane by software and do not have a meaningful + * stride. + * + * AGX has limited support for Apple Interchange. It cannot be used for Z/S + * buffers or array images, for example. It is intended for scanout buffer + * sharing only. + */ +#define DRM_FORMAT_MOD_APPLE_INTERCHANGE fourcc_mod_code(APPLE, 3) + +/* * AMD modifiers * * Memory layout: diff --git a/include/uapi/drm/panfrost_drm.h b/include/uapi/drm/panfrost_drm.h index 50d5337f35ef..7831c48c59c0 100644 --- a/include/uapi/drm/panfrost_drm.h +++ b/include/uapi/drm/panfrost_drm.h @@ -47,7 +47,7 @@ extern "C" { * them for anything but debugging purpose. */ #define DRM_IOCTL_PANFROST_PERFCNT_ENABLE DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_ENABLE, struct drm_panfrost_perfcnt_enable) -#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump) +#define DRM_IOCTL_PANFROST_PERFCNT_DUMP DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_PERFCNT_DUMP, struct drm_panfrost_perfcnt_dump) #define PANFROST_JD_REQ_FS (1 << 0) #define PANFROST_JD_REQ_CYCLE_COUNT (1 << 1) @@ -270,8 +270,35 @@ struct drm_panfrost_perfcnt_enable { __u32 counterset; }; +/* + * The next two flags describe the state of a perfcnt dump request + * as influenced by a device reset. They are the only values the + * perfcnt_dump ioctl state field can take on. + * Only certain state and ioctl retval combinations are legitimate. + */ + +/* A new perfcnt_enable ioctl should be issued before requesting + * more dumps, because a HW reset failed to recreate perfcnt's + * original state. Otherwise further perfcnt_dump's will fail. + * This flag being set means ioctl's retval is always -EIO. + */ +#define PANFROST_PERFCNT_SESSION_DEAD (1 << 0) + +/* A HW reset happened before or during a sample request, and + * perfcnt's internal state was successfully restored. There + * are two possible outcomes depending on the ioctl's retval: + * 0: A reset happened before a dump was requested, but did + * nonetheless succeed. Counter values are relative to last reset. + * -EAGAIN: A reset happened when a counter values sampling + * request was ongoing. Values are undefined so a new dump ioctl + * should be issued. + */ +#define PANFROST_PERFCNT_SESSION_INTERRUPTED_BY_RESET (1 << 1) + struct drm_panfrost_perfcnt_dump { __u64 buf_ptr; + __u32 state; + __u32 pad; /* MBZ */ }; /* madvise provides a way to tell the kernel in case a buffers contents |
