From 1d22b6033ea113a4c3850dfa2c0770885c81aec8 Mon Sep 17 00:00:00 2001 From: Alvin Šipraga Date: Sun, 12 Jun 2022 16:48:53 +0200 Subject: drm: bridge: adv7511: fix CEC power down control register offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADV7511_REG_CEC_CTRL = 0xE2 register is part of the main register map - not the CEC register map. As such, we shouldn't apply an offset to the register address. Doing so will cause us to address a bogus register for chips with a CEC register map offset (e.g. ADV7533). Fixes: 3b1b975003e4 ("drm: adv7511/33: add HDMI CEC support") Signed-off-by: Alvin Šipraga Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220612144854.2223873-2-alvin@pqrs.dk --- drivers/gpu/drm/bridge/adv7511/adv7511.h | 5 +---- drivers/gpu/drm/bridge/adv7511/adv7511_cec.c | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h index a031a0cd1f18..94de73cbeb2d 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511.h +++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h @@ -394,10 +394,7 @@ void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1); #else static inline int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) { - unsigned int offset = adv7511->type == ADV7533 ? - ADV7533_REG_CEC_OFFSET : 0; - - regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, ADV7511_CEC_CTRL_POWER_DOWN); return 0; } diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c index 0b266f28f150..99964f5a5457 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_cec.c @@ -359,7 +359,7 @@ int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) goto err_cec_alloc; } - regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0); + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, 0); /* cec soft reset */ regmap_write(adv7511->regmap_cec, ADV7511_REG_CEC_SOFT_RESET + offset, 0x01); @@ -386,7 +386,7 @@ err_cec_alloc: dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n", ret); err_cec_parse_dt: - regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, + regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, ADV7511_CEC_CTRL_POWER_DOWN); return ret == -EPROBE_DEFER ? ret : 0; } -- cgit v1.2.3-59-g8ed1b From 40cdb02cb9f965732eb543d47f15bef8d10f0f5f Mon Sep 17 00:00:00 2001 From: Alvin Šipraga Date: Sun, 12 Jun 2022 16:48:54 +0200 Subject: drm: bridge: adv7511: unregister cec i2c device after cec adapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cec_unregister_adapter() assumes that the underlying adapter ops are callable. For example, if the CEC adapter currently has a valid physical address, then the unregistration procedure will invalidate the physical address by setting it to f.f.f.f. Whence the following kernel oops observed after removing the adv7511 module: Unable to handle kernel execution of user memory at virtual address 0000000000000000 Internal error: Oops: 86000004 [#1] PREEMPT_RT SMP Call trace: 0x0 adv7511_cec_adap_log_addr+0x1ac/0x1c8 [adv7511] cec_adap_unconfigure+0x44/0x90 [cec] __cec_s_phys_addr.part.0+0x68/0x230 [cec] __cec_s_phys_addr+0x40/0x50 [cec] cec_unregister_adapter+0xb4/0x118 [cec] adv7511_remove+0x60/0x90 [adv7511] i2c_device_remove+0x34/0xe0 device_release_driver_internal+0x114/0x1f0 driver_detach+0x54/0xe0 bus_remove_driver+0x60/0xd8 driver_unregister+0x34/0x60 i2c_del_driver+0x2c/0x68 adv7511_exit+0x1c/0x67c [adv7511] __arm64_sys_delete_module+0x154/0x288 invoke_syscall+0x48/0x100 el0_svc_common.constprop.0+0x48/0xe8 do_el0_svc+0x28/0x88 el0_svc+0x1c/0x50 el0t_64_sync_handler+0xa8/0xb0 el0t_64_sync+0x15c/0x160 Code: bad PC value ---[ end trace 0000000000000000 ]--- Protect against this scenario by unregistering i2c_cec after unregistering the CEC adapter. Duly disable the CEC clock afterwards too. Fixes: 3b1b975003e4 ("drm: adv7511/33: add HDMI CEC support") Signed-off-by: Alvin Šipraga Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220612144854.2223873-3-alvin@pqrs.dk --- drivers/gpu/drm/bridge/adv7511/adv7511_drv.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 38bf28720f3a..6031bdd92342 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1340,9 +1340,6 @@ static int adv7511_remove(struct i2c_client *i2c) { struct adv7511 *adv7511 = i2c_get_clientdata(i2c); - i2c_unregister_device(adv7511->i2c_cec); - clk_disable_unprepare(adv7511->cec_clk); - adv7511_uninit_regulators(adv7511); drm_bridge_remove(&adv7511->bridge); @@ -1350,6 +1347,8 @@ static int adv7511_remove(struct i2c_client *i2c) adv7511_audio_exit(adv7511); cec_unregister_adapter(adv7511->cec_adap); + i2c_unregister_device(adv7511->i2c_cec); + clk_disable_unprepare(adv7511->cec_clk); i2c_unregister_device(adv7511->i2c_packet); i2c_unregister_device(adv7511->i2c_edid); -- cgit v1.2.3-59-g8ed1b From 540dfd188ea2940582841c1c220bd035a7db0e51 Mon Sep 17 00:00:00 2001 From: Jianglei Nie Date: Tue, 5 Jul 2022 21:25:46 +0800 Subject: drm/nouveau: fix a use-after-free in nouveau_gem_prime_import_sg_table() nouveau_bo_init() is backed by ttm_bo_init() and ferries its return code back to the caller. On failures, ttm will call nouveau_bo_del_ttm() and free the memory.Thus, when nouveau_bo_init() returns an error, the gem object has already been released. Then the call to nouveau_bo_ref() will use the freed "nvbo->bo" and lead to a use-after-free bug. We should delete the call to nouveau_bo_ref() to avoid the use-after-free. Signed-off-by: Jianglei Nie Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Fixes: 019cbd4a4feb ("drm/nouveau: Initialize GEM object before TTM object") Cc: Thierry Reding Cc: # v5.4+ Link: https://patchwork.freedesktop.org/patch/msgid/20220705132546.2247677-1-niejianglei2021@163.com --- drivers/gpu/drm/nouveau/nouveau_prime.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index 347488685f74..9608121e49b7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -71,7 +71,6 @@ struct drm_gem_object *nouveau_gem_prime_import_sg_table(struct drm_device *dev, ret = nouveau_bo_init(nvbo, size, align, NOUVEAU_GEM_DOMAIN_GART, sg, robj); if (ret) { - nouveau_bo_ref(NULL, &nvbo); obj = ERR_PTR(ret); goto unlock; } -- cgit v1.2.3-59-g8ed1b From 6dc548745d5b5102e3c53dc5097296ac270b6c69 Mon Sep 17 00:00:00 2001 From: Jianglei Nie Date: Tue, 5 Jul 2022 17:43:06 +0800 Subject: drm/nouveau/nouveau_bo: fix potential memory leak in nouveau_bo_alloc() nouveau_bo_alloc() allocates a memory chunk for "nvbo" with kzalloc(). When some error occurs, "nvbo" should be released. But when WARN_ON(pi < 0)) equals true, the function return ERR_PTR without releasing the "nvbo", which will lead to a memory leak. We should release the "nvbo" with kfree() if WARN_ON(pi < 0)) equals true. Signed-off-by: Jianglei Nie Signed-off-by: Lyude Paul Reviewed-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20220705094306.2244103-1-niejianglei2021@163.com --- drivers/gpu/drm/nouveau/nouveau_bo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 05076e530e7d..d0887438b07e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -281,8 +281,10 @@ nouveau_bo_alloc(struct nouveau_cli *cli, u64 *size, int *align, u32 domain, break; } - if (WARN_ON(pi < 0)) + if (WARN_ON(pi < 0)) { + kfree(nvbo); return ERR_PTR(-EINVAL); + } /* Disable compression if suitable settings couldn't be found. */ if (nvbo->comp && !vmm->page[pi].comp) { -- cgit v1.2.3-59-g8ed1b From f1bc386b319e93e56453ae27e9e83817bb1f6f95 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 29 Jun 2022 14:34:36 +0200 Subject: drm/vc4: hdmi: Depends on CONFIG_PM We already depend on runtime PM to get the power domains and clocks for most of the devices supported by the vc4 driver, so let's just select it to make sure it's there. Link: https://lore.kernel.org/r/20220629123510.1915022-38-maxime@cerno.tech Acked-by: Thomas Zimmermann Tested-by: Stefan Wahren Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/Kconfig | 1 + drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index 061be9a6619d..b0f3117102ca 100644 --- a/drivers/gpu/drm/vc4/Kconfig +++ b/drivers/gpu/drm/vc4/Kconfig @@ -8,6 +8,7 @@ config DRM_VC4 depends on DRM depends on SND && SND_SOC depends on COMMON_CLK + depends on PM select DRM_DISPLAY_HDMI_HELPER select DRM_DISPLAY_HELPER select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index e078f4ce0b4b..4a2e24421089 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2854,7 +2854,7 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) return 0; } -static int __maybe_unused vc4_hdmi_runtime_suspend(struct device *dev) +static int vc4_hdmi_runtime_suspend(struct device *dev) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); -- cgit v1.2.3-59-g8ed1b From 53565c28e6af2cef6bbf438c34250135e3564459 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 29 Jun 2022 14:34:37 +0200 Subject: drm/vc4: hdmi: Rework power up The current code tries to handle the case where CONFIG_PM isn't selected by first calling our runtime_resume implementation and then properly report the power state to the runtime_pm core. This allows to have a functionning device even if pm_runtime_get_* functions are nops. However, the device power state if CONFIG_PM is enabled is RPM_SUSPENDED, and thus our vc4_hdmi_write() and vc4_hdmi_read() calls in the runtime_pm hooks will now report a warning since the device might not be properly powered. Even more so, we need CONFIG_PM enabled since the previous RaspberryPi have a power domain that needs to be powered up for the HDMI controller to be usable. The previous patch has created a dependency on CONFIG_PM, now we can just assume it's there and only call pm_runtime_resume_and_get() to make sure our device is powered in bind. Link: https://lore.kernel.org/r/20220629123510.1915022-39-maxime@cerno.tech Acked-by: Thomas Zimmermann Tested-by: Stefan Wahren Signed-off-by: Maxime Ripard --- drivers/gpu/drm/vc4/vc4_hdmi.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 4a2e24421089..e77bdc37b16f 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2971,17 +2971,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) vc4_hdmi->disable_4kp60 = true; } + pm_runtime_enable(dev); + /* - * We need to have the device powered up at this point to call - * our reset hook and for the CEC init. + * We need to have the device powered up at this point to call + * our reset hook and for the CEC init. */ - ret = vc4_hdmi_runtime_resume(dev); + ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_put_ddc; - - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); + goto err_disable_runtime_pm; if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") || of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) && @@ -3027,6 +3025,7 @@ err_destroy_conn: err_destroy_encoder: drm_encoder_cleanup(encoder); pm_runtime_put_sync(dev); +err_disable_runtime_pm: pm_runtime_disable(dev); err_put_ddc: put_device(&vc4_hdmi->ddc->dev); -- cgit v1.2.3-59-g8ed1b From 859161b952a453b86362f168fadef72a8ba31a05 Mon Sep 17 00:00:00 2001 From: Stanislav Lisovskiy Date: Tue, 14 Jun 2022 15:30:49 +0300 Subject: drm/i915/dg2: Bump up CDCLK for DG2 We seem to need this W/A same way as for TGL, in order to fix some of the underruns, which we currently have and those not related to PSR. Signed-off-by: Stanislav Lisovskiy Reviewed-by: Uma Shankar Link: https://patchwork.freedesktop.org/patch/msgid/20220614123049.16183-2-stanislav.lisovskiy@intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 6e80162632dd..86a22c3766e5 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -2300,7 +2300,7 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state) min_cdclk = max(min_cdclk, (int)crtc_state->pixel_rate); /* - * HACK. Currently for TGL platforms we calculate + * HACK. Currently for TGL/DG2 platforms we calculate * min_cdclk initially based on pixel_rate divided * by 2, accounting for also plane requirements, * however in some cases the lowest possible CDCLK @@ -2308,7 +2308,7 @@ int intel_crtc_compute_min_cdclk(const struct intel_crtc_state *crtc_state) * Explicitly stating here that this seems to be currently * rather a Hack, than final solution. */ - if (IS_TIGERLAKE(dev_priv)) { + if (IS_TIGERLAKE(dev_priv) || IS_DG2(dev_priv)) { /* * Clamp to max_cdclk_freq in case pixel rate is higher, * in order not to break an 8K, but still leave W/A at place. -- cgit v1.2.3-59-g8ed1b From 9c442b7aabb3953d3bbd156dc68a16cec60d3bff Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 2 Jul 2022 14:32:50 -0500 Subject: drm/sun4i: Update Kconfig defaults and descriptions Allwinner display drivers are split roughly into two generations. The first generation was found on early 32-bit ARM SoCs and contains DE1.0 and a custom HDMI controller. Clarify that these options only apply to a specific list of SoCs, and limit selecting them to 32-bit ARM, to avoid confusion. The second generation, found in A83T and newer SoCs (both 32-bit and 64-bit), contains a DE2.0 and a DesignWare HDMI controller. Since this is the most widely-used generation, enable it by default. The previous default condition (MACH_SUN8I) was limited to 32-bit SoCs. Also enable the DSI controller by default, which is found on 64-bit SoCs as well. Signed-off-by: Samuel Holland Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220702193250.52959-1-samuel@sholland.org --- drivers/gpu/drm/sun4i/Kconfig | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig index 3a43c436c74a..1c2f8909f3cd 100644 --- a/drivers/gpu/drm/sun4i/Kconfig +++ b/drivers/gpu/drm/sun4i/Kconfig @@ -16,23 +16,25 @@ config DRM_SUN4I if DRM_SUN4I config DRM_SUN4I_HDMI - tristate "Allwinner A10 HDMI Controller Support" + tristate "Allwinner A10/A10s/A20/A31 HDMI Controller Support" + depends on ARM || COMPILE_TEST default DRM_SUN4I help - Choose this option if you have an Allwinner SoC with an HDMI - controller. + Choose this option if you have an Allwinner A10/A10s/A20/A31 + SoC with an HDMI controller. config DRM_SUN4I_HDMI_CEC - bool "Allwinner A10 HDMI CEC Support" + bool "Allwinner A10/A10s/A20/A31 HDMI CEC Support" depends on DRM_SUN4I_HDMI select CEC_CORE select CEC_PIN help - Choose this option if you have an Allwinner SoC with an HDMI - controller and want to use CEC. + Choose this option if you have an Allwinner A10/A10s/A20/A31 + SoC with an HDMI controller and want to use CEC. config DRM_SUN4I_BACKEND tristate "Support for Allwinner A10 Display Engine Backend" + depends on ARM || COMPILE_TEST default DRM_SUN4I help Choose this option if you have an Allwinner SoC with the @@ -41,8 +43,8 @@ config DRM_SUN4I_BACKEND selected the module will be called sun4i-backend. config DRM_SUN6I_DSI - tristate "Allwinner A31 MIPI-DSI Controller Support" - default MACH_SUN8I + tristate "Allwinner A31/A64 MIPI-DSI Controller Support" + default DRM_SUN4I select CRC_CCITT select DRM_MIPI_DSI select RESET_CONTROLLER @@ -55,15 +57,17 @@ config DRM_SUN6I_DSI config DRM_SUN8I_DW_HDMI tristate "Support for Allwinner version of DesignWare HDMI" depends on DRM_SUN4I + default DRM_SUN4I select DRM_DW_HDMI help Choose this option if you have an Allwinner SoC with the - DesignWare HDMI controller with custom HDMI PHY. If M is + DesignWare HDMI controller. SoCs that support HDMI and + have a Display Engine 2.0 contain this controller. If M is selected the module will be called sun8i_dw_hdmi. config DRM_SUN8I_MIXER tristate "Support for Allwinner Display Engine 2.0 Mixer" - default MACH_SUN8I + default DRM_SUN4I help Choose this option if you have an Allwinner SoC with the Allwinner Display Engine 2.0, which has a mixer to do some @@ -75,6 +79,6 @@ config DRM_SUN8I_TCON_TOP default DRM_SUN4I if DRM_SUN8I_MIXER!=n help TCON TOP is responsible for configuring display pipeline for - HTMI, TVE and LCD. + HDMI, TVE and LCD. endif -- cgit v1.2.3-59-g8ed1b From 7d1202738efda60155d98b370b3c70d336be0eea Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 4 Jul 2022 13:55:40 +0300 Subject: drm/bridge: Avoid uninitialized variable warning This code works, but technically it uses "num_in_bus_fmts" before it has been initialized so it leads to static checker warnings and probably KMEMsan warnings at run time. Initialize the variable to zero to silence the warning. Fixes: f32df58acc68 ("drm/bridge: Add the necessary bits to support bus format negotiation") Signed-off-by: Dan Carpenter Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/YrrIs3hoGcPVmXc5@kili --- drivers/gpu/drm/drm_bridge.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 6abf7a2407e9..1545c50fd1c8 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -847,8 +847,8 @@ static int select_bus_fmt_recursive(struct drm_bridge *first_bridge, struct drm_connector_state *conn_state, u32 out_bus_fmt) { + unsigned int i, num_in_bus_fmts = 0; struct drm_bridge_state *cur_state; - unsigned int num_in_bus_fmts, i; struct drm_bridge *prev_bridge; u32 *in_bus_fmts; int ret; @@ -969,7 +969,7 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, struct drm_connector *conn = conn_state->connector; struct drm_encoder *encoder = bridge->encoder; struct drm_bridge_state *last_bridge_state; - unsigned int i, num_out_bus_fmts; + unsigned int i, num_out_bus_fmts = 0; struct drm_bridge *last_bridge; u32 *out_bus_fmts; int ret = 0; -- cgit v1.2.3-59-g8ed1b From d72ac4bbf4cc8f6b50526ce400861f176659884f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 8 Jul 2022 11:49:20 +0200 Subject: drm/panel: simple: Make panel_simple_remove() return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit panel_simple_remove() returns zero unconditionally. Make it return no value instead making more obvious what happens in the callers. This is a preparation for making platform and mipi-dsi remove callbacks return void. Signed-off-by: Uwe Kleine-König Reviewed-by: Sam Ravnborg Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708094922.1408248-2-u.kleine-koenig@pengutronix.de --- drivers/gpu/drm/panel/panel-simple.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index ff5e1a44c43a..0ad3d1de4a98 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -696,7 +696,7 @@ free_ddc: return err; } -static int panel_simple_remove(struct device *dev) +static void panel_simple_remove(struct device *dev) { struct panel_simple *panel = dev_get_drvdata(dev); @@ -708,8 +708,6 @@ static int panel_simple_remove(struct device *dev) pm_runtime_disable(dev); if (panel->ddc) put_device(&panel->ddc->dev); - - return 0; } static void panel_simple_shutdown(struct device *dev) @@ -4273,7 +4271,9 @@ static int panel_simple_platform_probe(struct platform_device *pdev) static int panel_simple_platform_remove(struct platform_device *pdev) { - return panel_simple_remove(&pdev->dev); + panel_simple_remove(&pdev->dev); + + return 0; } static void panel_simple_platform_shutdown(struct platform_device *pdev) @@ -4574,7 +4574,9 @@ static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi) if (err < 0) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); - return panel_simple_remove(&dsi->dev); + panel_simple_remove(&dsi->dev); + + return 0; } static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi) -- cgit v1.2.3-59-g8ed1b From 1fd452c403ca2cd9dcbd8465f9f72be52e15ac1a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 8 Jul 2022 11:49:21 +0200 Subject: drm/panel-novatek-nt35510: Emit an error message if power off fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Returning an error code from a mipi_dsi remove callback fails, this is silently ignored. (mipi_dsi_drv_remove() propagates the return value to device_remove() which ignores it.) So emit an error code in the driver remove function and return 0. Signed-off-by: Uwe Kleine-König Reviewed-by: Sam Ravnborg Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708094922.1408248-3-u.kleine-koenig@pengutronix.de --- drivers/gpu/drm/panel/panel-novatek-nt35510.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35510.c b/drivers/gpu/drm/panel/panel-novatek-nt35510.c index 40ea41b0a5dd..1b3e0154bfb8 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35510.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35510.c @@ -974,9 +974,12 @@ static int nt35510_remove(struct mipi_dsi_device *dsi) mipi_dsi_detach(dsi); /* Power off */ ret = nt35510_power_off(nt); + if (ret) + dev_err(&dsi->dev, "Failed to power off\n"); + drm_panel_remove(&nt->panel); - return ret; + return 0; } /* -- cgit v1.2.3-59-g8ed1b From 79abca2b399009eb8d12c652d0f0f7a9c7a06289 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 8 Jul 2022 11:49:22 +0200 Subject: drm/mipi-dsi: Make remove callback return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All implementations return 0 and the return value of mipi_dsi_drv_remove() is ignored anyhow. So change the prototype of the remove function to return no value. This way driver authors are not tempted to assume that passing an error to the upper layer is a good idea. All drivers are adapted accordingly. There is no intended change of behaviour. Signed-off-by: Uwe Kleine-König Reviewed-by: Sam Ravnborg Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708094922.1408248-4-u.kleine-koenig@pengutronix.de --- drivers/gpu/drm/bridge/chipone-icn6211.c | 4 +--- drivers/gpu/drm/bridge/tc358762.c | 4 +--- drivers/gpu/drm/bridge/tc358764.c | 4 +--- drivers/gpu/drm/drm_mipi_dsi.c | 4 +++- drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c | 4 +--- drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c | 4 +--- drivers/gpu/drm/panel/panel-boe-himax8279d.c | 4 +--- drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c | 4 +--- drivers/gpu/drm/panel/panel-dsi-cm.c | 4 +--- drivers/gpu/drm/panel/panel-ebbg-ft8719.c | 4 +--- drivers/gpu/drm/panel/panel-elida-kd35t133.c | 4 +--- drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c | 4 +--- drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c | 4 +--- drivers/gpu/drm/panel/panel-ilitek-ili9881c.c | 4 +--- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 4 +--- drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c | 4 +--- drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | 4 +--- drivers/gpu/drm/panel/panel-khadas-ts050.c | 4 +--- drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c | 4 +--- drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c | 4 +--- drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c | 4 +--- drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c | 4 +--- drivers/gpu/drm/panel/panel-novatek-nt35510.c | 4 +--- drivers/gpu/drm/panel/panel-novatek-nt35560.c | 4 +--- drivers/gpu/drm/panel/panel-novatek-nt35950.c | 4 +--- drivers/gpu/drm/panel/panel-novatek-nt36672a.c | 4 +--- drivers/gpu/drm/panel/panel-orisetech-otm8009a.c | 4 +--- drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c | 4 +--- drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c | 4 +--- drivers/gpu/drm/panel/panel-raydium-rm67191.c | 4 +--- drivers/gpu/drm/panel/panel-raydium-rm68200.c | 4 +--- drivers/gpu/drm/panel/panel-ronbo-rb070d30.c | 4 +--- drivers/gpu/drm/panel/panel-samsung-s6d16d0.c | 4 +--- drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c | 4 +--- drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c | 4 +--- drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c | 3 +-- drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c | 4 +--- drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c | 4 +--- drivers/gpu/drm/panel/panel-samsung-sofef00.c | 4 +--- drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c | 6 ++---- drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c | 4 +--- drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c | 4 +--- drivers/gpu/drm/panel/panel-simple.c | 4 +--- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 4 +--- drivers/gpu/drm/panel/panel-sitronix-st7703.c | 4 +--- drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c | 4 +--- drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c | 4 +--- drivers/gpu/drm/panel/panel-truly-nt35597.c | 3 +-- drivers/gpu/drm/panel/panel-visionox-rm69299.c | 3 +-- drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c | 4 +--- include/drm/drm_mipi_dsi.h | 2 +- 51 files changed, 54 insertions(+), 147 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index 481c86b2406e..b07d2d16c3cf 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -735,14 +735,12 @@ static int chipone_i2c_probe(struct i2c_client *client, return chipone_dsi_host_attach(icn); } -static int chipone_dsi_remove(struct mipi_dsi_device *dsi) +static void chipone_dsi_remove(struct mipi_dsi_device *dsi) { struct chipone *icn = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_bridge_remove(&icn->bridge); - - return 0; } static const struct of_device_id chipone_of_match[] = { diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index 40439da4db49..7f4fce1aa998 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -241,14 +241,12 @@ static int tc358762_probe(struct mipi_dsi_device *dsi) return ret; } -static int tc358762_remove(struct mipi_dsi_device *dsi) +static void tc358762_remove(struct mipi_dsi_device *dsi) { struct tc358762 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_bridge_remove(&ctx->bridge); - - return 0; } static const struct of_device_id tc358762_of_match[] = { diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index fdfb14aca926..53259c12d777 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -381,14 +381,12 @@ static int tc358764_probe(struct mipi_dsi_device *dsi) return ret; } -static int tc358764_remove(struct mipi_dsi_device *dsi) +static void tc358764_remove(struct mipi_dsi_device *dsi) { struct tc358764 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_bridge_remove(&ctx->bridge); - - return 0; } static const struct of_device_id tc358764_of_match[] = { diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index c40bde96cfdf..2e25804d6ffa 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -1236,7 +1236,9 @@ static int mipi_dsi_drv_remove(struct device *dev) struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver); struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); - return drv->remove(dsi); + drv->remove(dsi); + + return 0; } static void mipi_dsi_drv_shutdown(struct device *dev) diff --git a/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c b/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c index 174ff434bd71..b3235781e6ba 100644 --- a/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c +++ b/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c @@ -321,7 +321,7 @@ static int tm5p5_nt35596_probe(struct mipi_dsi_device *dsi) return 0; } -static int tm5p5_nt35596_remove(struct mipi_dsi_device *dsi) +static void tm5p5_nt35596_remove(struct mipi_dsi_device *dsi) { struct tm5p5_nt35596 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -332,8 +332,6 @@ static int tm5p5_nt35596_remove(struct mipi_dsi_device *dsi) "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id tm5p5_nt35596_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c index ef00cd67dc40..ad58840eda41 100644 --- a/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c +++ b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c @@ -410,7 +410,7 @@ static int boe_bf060y8m_aj0_probe(struct mipi_dsi_device *dsi) return 0; } -static int boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi) +static void boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi) { struct boe_bf060y8m_aj0 *boe = mipi_dsi_get_drvdata(dsi); int ret; @@ -420,8 +420,6 @@ static int boe_bf060y8m_aj0_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&boe->panel); - - return 0; } static const struct of_device_id boe_bf060y8m_aj0_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-boe-himax8279d.c b/drivers/gpu/drm/panel/panel-boe-himax8279d.c index 42854bd37fd5..d879b3b14c48 100644 --- a/drivers/gpu/drm/panel/panel-boe-himax8279d.c +++ b/drivers/gpu/drm/panel/panel-boe-himax8279d.c @@ -919,7 +919,7 @@ static int panel_probe(struct mipi_dsi_device *dsi) return err; } -static int panel_remove(struct mipi_dsi_device *dsi) +static void panel_remove(struct mipi_dsi_device *dsi) { struct panel_info *pinfo = mipi_dsi_get_drvdata(dsi); int err; @@ -937,8 +937,6 @@ static int panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); drm_panel_remove(&pinfo->base); - - return 0; } static void panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 07f722f33fc5..857a2f0420d7 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -1622,7 +1622,7 @@ static void boe_panel_shutdown(struct mipi_dsi_device *dsi) drm_panel_unprepare(&boe->base); } -static int boe_panel_remove(struct mipi_dsi_device *dsi) +static void boe_panel_remove(struct mipi_dsi_device *dsi) { struct boe_panel *boe = mipi_dsi_get_drvdata(dsi); int ret; @@ -1635,8 +1635,6 @@ static int boe_panel_remove(struct mipi_dsi_device *dsi) if (boe->base.dev) drm_panel_remove(&boe->base); - - return 0; } static const struct of_device_id boe_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-dsi-cm.c b/drivers/gpu/drm/panel/panel-dsi-cm.c index b0213a518f9d..ba17bcc4461c 100644 --- a/drivers/gpu/drm/panel/panel-dsi-cm.c +++ b/drivers/gpu/drm/panel/panel-dsi-cm.c @@ -579,7 +579,7 @@ err_bl: return r; } -static int dsicm_remove(struct mipi_dsi_device *dsi) +static void dsicm_remove(struct mipi_dsi_device *dsi) { struct panel_drv_data *ddata = mipi_dsi_get_drvdata(dsi); @@ -593,8 +593,6 @@ static int dsicm_remove(struct mipi_dsi_device *dsi) if (ddata->extbldev) put_device(&ddata->extbldev->dev); - - return 0; } static const struct dsic_panel_data taal_data = { diff --git a/drivers/gpu/drm/panel/panel-ebbg-ft8719.c b/drivers/gpu/drm/panel/panel-ebbg-ft8719.c index 386f8321b930..e85d63a176d0 100644 --- a/drivers/gpu/drm/panel/panel-ebbg-ft8719.c +++ b/drivers/gpu/drm/panel/panel-ebbg-ft8719.c @@ -250,7 +250,7 @@ static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi) return 0; } -static int ebbg_ft8719_remove(struct mipi_dsi_device *dsi) +static void ebbg_ft8719_remove(struct mipi_dsi_device *dsi) { struct ebbg_ft8719 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -260,8 +260,6 @@ static int ebbg_ft8719_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id ebbg_ft8719_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-elida-kd35t133.c b/drivers/gpu/drm/panel/panel-elida-kd35t133.c index 01dd555a7f26..eee714cf3f49 100644 --- a/drivers/gpu/drm/panel/panel-elida-kd35t133.c +++ b/drivers/gpu/drm/panel/panel-elida-kd35t133.c @@ -321,7 +321,7 @@ static void kd35t133_shutdown(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); } -static int kd35t133_remove(struct mipi_dsi_device *dsi) +static void kd35t133_remove(struct mipi_dsi_device *dsi) { struct kd35t133 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -333,8 +333,6 @@ static int kd35t133_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id kd35t133_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c index cb0bb3076099..76572c922983 100644 --- a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c +++ b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c @@ -486,14 +486,12 @@ static int k101_im2ba02_dsi_probe(struct mipi_dsi_device *dsi) return 0; } -static int k101_im2ba02_dsi_remove(struct mipi_dsi_device *dsi) +static void k101_im2ba02_dsi_remove(struct mipi_dsi_device *dsi) { struct k101_im2ba02 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id k101_im2ba02_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index ee61d60eceae..df493da50afe 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -233,14 +233,12 @@ static int feiyang_dsi_probe(struct mipi_dsi_device *dsi) return 0; } -static int feiyang_dsi_remove(struct mipi_dsi_device *dsi) +static void feiyang_dsi_remove(struct mipi_dsi_device *dsi) { struct feiyang *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id feiyang_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 596861269774..cbb68caa36f2 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -923,14 +923,12 @@ static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi) return mipi_dsi_attach(dsi); } -static int ili9881c_dsi_remove(struct mipi_dsi_device *dsi) +static void ili9881c_dsi_remove(struct mipi_dsi_device *dsi) { struct ili9881c *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct ili9881c_desc lhr050h41_desc = { diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index f194b62e290c..9992d0d4c0e5 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -506,7 +506,7 @@ static int innolux_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int innolux_panel_remove(struct mipi_dsi_device *dsi) +static void innolux_panel_remove(struct mipi_dsi_device *dsi) { struct innolux_panel *innolux = mipi_dsi_get_drvdata(dsi); int err; @@ -524,8 +524,6 @@ static int innolux_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); innolux_panel_del(innolux); - - return 0; } static void innolux_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c index 31eafbc38ec0..d8765b2294fb 100644 --- a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c +++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c @@ -288,7 +288,7 @@ static int jdi_fhd_r63452_probe(struct mipi_dsi_device *dsi) return 0; } -static int jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi) +static void jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi) { struct jdi_fhd_r63452 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -298,8 +298,6 @@ static int jdi_fhd_r63452_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id jdi_fhd_r63452_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c index 3c86ad262d5e..8f4f137a2af6 100644 --- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c @@ -482,7 +482,7 @@ static int jdi_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int jdi_panel_remove(struct mipi_dsi_device *dsi) +static void jdi_panel_remove(struct mipi_dsi_device *dsi) { struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); int ret; @@ -497,8 +497,6 @@ static int jdi_panel_remove(struct mipi_dsi_device *dsi) ret); jdi_panel_del(jdi); - - return 0; } static void jdi_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-khadas-ts050.c b/drivers/gpu/drm/panel/panel-khadas-ts050.c index a3ec4cbdbf7a..1ab1ebe30882 100644 --- a/drivers/gpu/drm/panel/panel-khadas-ts050.c +++ b/drivers/gpu/drm/panel/panel-khadas-ts050.c @@ -830,7 +830,7 @@ static int khadas_ts050_panel_probe(struct mipi_dsi_device *dsi) return err; } -static int khadas_ts050_panel_remove(struct mipi_dsi_device *dsi) +static void khadas_ts050_panel_remove(struct mipi_dsi_device *dsi) { struct khadas_ts050_panel *khadas_ts050 = mipi_dsi_get_drvdata(dsi); int err; @@ -842,8 +842,6 @@ static int khadas_ts050_panel_remove(struct mipi_dsi_device *dsi) drm_panel_remove(&khadas_ts050->base); drm_panel_disable(&khadas_ts050->base); drm_panel_unprepare(&khadas_ts050->base); - - return 0; } static void khadas_ts050_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c index daccb1fd5fda..17f8d80cf2b3 100644 --- a/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c +++ b/drivers/gpu/drm/panel/panel-kingdisplay-kd097d04.c @@ -415,7 +415,7 @@ static int kingdisplay_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int kingdisplay_panel_remove(struct mipi_dsi_device *dsi) +static void kingdisplay_panel_remove(struct mipi_dsi_device *dsi) { struct kingdisplay_panel *kingdisplay = mipi_dsi_get_drvdata(dsi); int err; @@ -433,8 +433,6 @@ static int kingdisplay_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); kingdisplay_panel_del(kingdisplay); - - return 0; } static void kingdisplay_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c index a5a414920430..5619f186d28c 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk050h3146w.c @@ -628,7 +628,7 @@ static void ltk050h3146w_shutdown(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); } -static int ltk050h3146w_remove(struct mipi_dsi_device *dsi) +static void ltk050h3146w_remove(struct mipi_dsi_device *dsi) { struct ltk050h3146w *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -640,8 +640,6 @@ static int ltk050h3146w_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id ltk050h3146w_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c index 21e48923836d..39e408c9f762 100644 --- a/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c +++ b/drivers/gpu/drm/panel/panel-leadtek-ltk500hd1829.c @@ -477,7 +477,7 @@ static void ltk500hd1829_shutdown(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); } -static int ltk500hd1829_remove(struct mipi_dsi_device *dsi) +static void ltk500hd1829_remove(struct mipi_dsi_device *dsi) { struct ltk500hd1829 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -489,8 +489,6 @@ static int ltk500hd1829_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id ltk500hd1829_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c index 31daae1da9c9..772e3b6acece 100644 --- a/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c +++ b/drivers/gpu/drm/panel/panel-mantix-mlaf057we51.c @@ -336,7 +336,7 @@ static void mantix_shutdown(struct mipi_dsi_device *dsi) drm_panel_disable(&ctx->panel); } -static int mantix_remove(struct mipi_dsi_device *dsi) +static void mantix_remove(struct mipi_dsi_device *dsi) { struct mantix *ctx = mipi_dsi_get_drvdata(dsi); @@ -344,8 +344,6 @@ static int mantix_remove(struct mipi_dsi_device *dsi) mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id mantix_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35510.c b/drivers/gpu/drm/panel/panel-novatek-nt35510.c index 1b3e0154bfb8..c613ad967ba6 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35510.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35510.c @@ -966,7 +966,7 @@ static int nt35510_probe(struct mipi_dsi_device *dsi) return 0; } -static int nt35510_remove(struct mipi_dsi_device *dsi) +static void nt35510_remove(struct mipi_dsi_device *dsi) { struct nt35510 *nt = mipi_dsi_get_drvdata(dsi); int ret; @@ -978,8 +978,6 @@ static int nt35510_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to power off\n"); drm_panel_remove(&nt->panel); - - return 0; } /* diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35560.c b/drivers/gpu/drm/panel/panel-novatek-nt35560.c index 1b6042321ea1..cc7f96d70826 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35560.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35560.c @@ -523,14 +523,12 @@ static int nt35560_probe(struct mipi_dsi_device *dsi) return 0; } -static int nt35560_remove(struct mipi_dsi_device *dsi) +static void nt35560_remove(struct mipi_dsi_device *dsi) { struct nt35560 *nt = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&nt->panel); - - return 0; } static const struct of_device_id nt35560_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c index 288c7fa83ecc..3a844917da07 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c @@ -620,7 +620,7 @@ static int nt35950_probe(struct mipi_dsi_device *dsi) return 0; } -static int nt35950_remove(struct mipi_dsi_device *dsi) +static void nt35950_remove(struct mipi_dsi_device *dsi) { struct nt35950 *nt = mipi_dsi_get_drvdata(dsi); int ret; @@ -639,8 +639,6 @@ static int nt35950_remove(struct mipi_dsi_device *dsi) } drm_panel_remove(&nt->panel); - - return 0; } static const struct nt35950_panel_mode sharp_ls055d1sx04_modes[] = { diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c index 6d6ce42787e2..73bcffa1e0c1 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c @@ -669,7 +669,7 @@ static int nt36672a_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int nt36672a_panel_remove(struct mipi_dsi_device *dsi) +static void nt36672a_panel_remove(struct mipi_dsi_device *dsi) { struct nt36672a_panel *pinfo = mipi_dsi_get_drvdata(dsi); int err; @@ -687,8 +687,6 @@ static int nt36672a_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); drm_panel_remove(&pinfo->base); - - return 0; } static void nt36672a_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c index dfb43b1374e7..b4729a94c34a 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c @@ -497,14 +497,12 @@ static int otm8009a_probe(struct mipi_dsi_device *dsi) return 0; } -static int otm8009a_remove(struct mipi_dsi_device *dsi) +static void otm8009a_remove(struct mipi_dsi_device *dsi) { struct otm8009a *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id orisetech_otm8009a_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c index 198493a6eb6a..493e0504f6f7 100644 --- a/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c +++ b/drivers/gpu/drm/panel/panel-osd-osd101t2587-53ts.c @@ -206,7 +206,7 @@ static int osd101t2587_panel_probe(struct mipi_dsi_device *dsi) return ret; } -static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi) +static void osd101t2587_panel_remove(struct mipi_dsi_device *dsi) { struct osd101t2587_panel *osd101t2587 = mipi_dsi_get_drvdata(dsi); int ret; @@ -221,8 +221,6 @@ static int osd101t2587_panel_remove(struct mipi_dsi_device *dsi) ret = mipi_dsi_detach(dsi); if (ret < 0) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); - - return ret; } static void osd101t2587_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c index 3991f5d950af..8ba6d8287938 100644 --- a/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c +++ b/drivers/gpu/drm/panel/panel-panasonic-vvx10f034n00.c @@ -250,7 +250,7 @@ static int wuxga_nt_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) +static void wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) { struct wuxga_nt_panel *wuxga_nt = mipi_dsi_get_drvdata(dsi); int ret; @@ -264,8 +264,6 @@ static int wuxga_nt_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); wuxga_nt_panel_del(wuxga_nt); - - return 0; } static void wuxga_nt_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67191.c b/drivers/gpu/drm/panel/panel-raydium-rm67191.c index 4e021a572211..dbb1ed4efbed 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm67191.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm67191.c @@ -616,7 +616,7 @@ static int rad_panel_probe(struct mipi_dsi_device *dsi) return ret; } -static int rad_panel_remove(struct mipi_dsi_device *dsi) +static void rad_panel_remove(struct mipi_dsi_device *dsi) { struct rad_panel *rad = mipi_dsi_get_drvdata(dsi); struct device *dev = &dsi->dev; @@ -627,8 +627,6 @@ static int rad_panel_remove(struct mipi_dsi_device *dsi) dev_err(dev, "Failed to detach from host (%d)\n", ret); drm_panel_remove(&rad->panel); - - return 0; } static void rad_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c index 412c0dbcb2b6..5f9b340588fb 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c @@ -412,14 +412,12 @@ static int rm68200_probe(struct mipi_dsi_device *dsi) return 0; } -static int rm68200_remove(struct mipi_dsi_device *dsi) +static void rm68200_remove(struct mipi_dsi_device *dsi) { struct rm68200 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id raydium_rm68200_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c index 1fb579a574d9..a8a98c91b13c 100644 --- a/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c +++ b/drivers/gpu/drm/panel/panel-ronbo-rb070d30.c @@ -208,14 +208,12 @@ static int rb070d30_panel_dsi_probe(struct mipi_dsi_device *dsi) return 0; } -static int rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi) +static void rb070d30_panel_dsi_remove(struct mipi_dsi_device *dsi) { struct rb070d30_panel *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id rb070d30_panel_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c index 70560cac53a9..008e2b0d6652 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -212,14 +212,12 @@ static int s6d16d0_probe(struct mipi_dsi_device *dsi) return ret; } -static int s6d16d0_remove(struct mipi_dsi_device *dsi) +static void s6d16d0_remove(struct mipi_dsi_device *dsi) { struct s6d16d0 *s6 = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&s6->panel); - - return 0; } static const struct of_device_id s6d16d0_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c index 0ab1b7ec84cd..5c621b15e84c 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e3ha2.c @@ -747,15 +747,13 @@ remove_panel: return ret; } -static int s6e3ha2_remove(struct mipi_dsi_device *dsi) +static void s6e3ha2_remove(struct mipi_dsi_device *dsi) { struct s6e3ha2 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); backlight_device_unregister(ctx->bl_dev); - - return 0; } static const struct of_device_id s6e3ha2_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index e38262b67ff7..e06fd35de814 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -488,7 +488,7 @@ remove_panel: return ret; } -static int s6e63j0x03_remove(struct mipi_dsi_device *dsi) +static void s6e63j0x03_remove(struct mipi_dsi_device *dsi) { struct s6e63j0x03 *ctx = mipi_dsi_get_drvdata(dsi); @@ -496,8 +496,6 @@ static int s6e63j0x03_remove(struct mipi_dsi_device *dsi) drm_panel_remove(&ctx->panel); backlight_device_unregister(ctx->bl_dev); - - return 0; } static const struct of_device_id s6e63j0x03_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c index e0f773678168..ed3895e4ca5e 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c @@ -113,11 +113,10 @@ static int s6e63m0_dsi_probe(struct mipi_dsi_device *dsi) return ret; } -static int s6e63m0_dsi_remove(struct mipi_dsi_device *dsi) +static void s6e63m0_dsi_remove(struct mipi_dsi_device *dsi) { mipi_dsi_detach(dsi); s6e63m0_remove(&dsi->dev); - return 0; } static const struct of_device_id s6e63m0_dsi_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c index 29fde3823212..97ff7a18545c 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c @@ -254,7 +254,7 @@ static int s6e88a0_ams452ef01_probe(struct mipi_dsi_device *dsi) return 0; } -static int s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi) +static void s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi) { struct s6e88a0_ams452ef01 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -264,8 +264,6 @@ static int s6e88a0_ams452ef01_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id s6e88a0_ams452ef01_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c index 9b3599d6d2de..54213beafaf5 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8aa0.c @@ -1028,14 +1028,12 @@ static int s6e8aa0_probe(struct mipi_dsi_device *dsi) return ret; } -static int s6e8aa0_remove(struct mipi_dsi_device *dsi) +static void s6e8aa0_remove(struct mipi_dsi_device *dsi) { struct s6e8aa0 *ctx = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id s6e8aa0_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c index 1fb37fda4ba9..1a0d24595faa 100644 --- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c +++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c @@ -305,7 +305,7 @@ static int sofef00_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int sofef00_panel_remove(struct mipi_dsi_device *dsi) +static void sofef00_panel_remove(struct mipi_dsi_device *dsi) { struct sofef00_panel *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -315,8 +315,6 @@ static int sofef00_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id sofef00_panel_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c index f8cd2a42ed13..14851408a5e1 100644 --- a/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-lq101r1sx01.c @@ -391,7 +391,7 @@ static int sharp_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int sharp_panel_remove(struct mipi_dsi_device *dsi) +static void sharp_panel_remove(struct mipi_dsi_device *dsi) { struct sharp_panel *sharp = mipi_dsi_get_drvdata(dsi); int err; @@ -399,7 +399,7 @@ static int sharp_panel_remove(struct mipi_dsi_device *dsi) /* only detach from host for the DSI-LINK2 interface */ if (!sharp) { mipi_dsi_detach(dsi); - return 0; + return; } err = drm_panel_disable(&sharp->base); @@ -411,8 +411,6 @@ static int sharp_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); sharp_panel_del(sharp); - - return 0; } static void sharp_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c index 25829a0a8e80..d1ec80a3e3c7 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls043t1le01.c @@ -305,7 +305,7 @@ static int sharp_nt_panel_probe(struct mipi_dsi_device *dsi) return 0; } -static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi) +static void sharp_nt_panel_remove(struct mipi_dsi_device *dsi) { struct sharp_nt_panel *sharp_nt = mipi_dsi_get_drvdata(dsi); int ret; @@ -319,8 +319,6 @@ static int sharp_nt_panel_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", ret); sharp_nt_panel_del(sharp_nt); - - return 0; } static void sharp_nt_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c index e12570561629..8a4e0c1fe73f 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c @@ -298,7 +298,7 @@ static int sharp_ls060_probe(struct mipi_dsi_device *dsi) return 0; } -static int sharp_ls060_remove(struct mipi_dsi_device *dsi) +static void sharp_ls060_remove(struct mipi_dsi_device *dsi) { struct sharp_ls060 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -308,8 +308,6 @@ static int sharp_ls060_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id sharp_ls060t1sx01_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 0ad3d1de4a98..c1d224cb719d 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -4566,7 +4566,7 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi) return err; } -static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi) +static void panel_simple_dsi_remove(struct mipi_dsi_device *dsi) { int err; @@ -4575,8 +4575,6 @@ static int panel_simple_dsi_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); panel_simple_remove(&dsi->dev); - - return 0; } static void panel_simple_dsi_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 320a2a8fd459..5192d9aa572a 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -387,14 +387,12 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi) return mipi_dsi_attach(dsi); } -static int st7701_dsi_remove(struct mipi_dsi_device *dsi) +static void st7701_dsi_remove(struct mipi_dsi_device *dsi) { struct st7701 *st7701 = mipi_dsi_get_drvdata(dsi); mipi_dsi_detach(dsi); drm_panel_remove(&st7701->panel); - - return 0; } static const struct of_device_id st7701_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index 73f69c929a75..86a472b01360 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -598,7 +598,7 @@ static void st7703_shutdown(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); } -static int st7703_remove(struct mipi_dsi_device *dsi) +static void st7703_remove(struct mipi_dsi_device *dsi) { struct st7703 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -612,8 +612,6 @@ static int st7703_remove(struct mipi_dsi_device *dsi) drm_panel_remove(&ctx->panel); st7703_debugfs_remove(ctx); - - return 0; } static const struct of_device_id st7703_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c index 69f07b15fca4..fa9be3c299c0 100644 --- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c @@ -517,7 +517,7 @@ static int truly_nt35521_probe(struct mipi_dsi_device *dsi) return 0; } -static int truly_nt35521_remove(struct mipi_dsi_device *dsi) +static void truly_nt35521_remove(struct mipi_dsi_device *dsi) { struct truly_nt35521 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -527,8 +527,6 @@ static int truly_nt35521_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id truly_nt35521_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c index 820731be7147..d8487bc6d611 100644 --- a/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c +++ b/drivers/gpu/drm/panel/panel-tdo-tl070wsh30.c @@ -210,7 +210,7 @@ static int tdo_tl070wsh30_panel_probe(struct mipi_dsi_device *dsi) return mipi_dsi_attach(dsi); } -static int tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi) +static void tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi) { struct tdo_tl070wsh30_panel *tdo_tl070wsh30 = mipi_dsi_get_drvdata(dsi); int err; @@ -222,8 +222,6 @@ static int tdo_tl070wsh30_panel_remove(struct mipi_dsi_device *dsi) drm_panel_remove(&tdo_tl070wsh30->base); drm_panel_disable(&tdo_tl070wsh30->base); drm_panel_unprepare(&tdo_tl070wsh30->base); - - return 0; } static void tdo_tl070wsh30_panel_shutdown(struct mipi_dsi_device *dsi) diff --git a/drivers/gpu/drm/panel/panel-truly-nt35597.c b/drivers/gpu/drm/panel/panel-truly-nt35597.c index 9ca5c7ff41d6..b31cffb660a7 100644 --- a/drivers/gpu/drm/panel/panel-truly-nt35597.c +++ b/drivers/gpu/drm/panel/panel-truly-nt35597.c @@ -616,7 +616,7 @@ err_panel_add: return ret; } -static int truly_nt35597_remove(struct mipi_dsi_device *dsi) +static void truly_nt35597_remove(struct mipi_dsi_device *dsi) { struct truly_nt35597 *ctx = mipi_dsi_get_drvdata(dsi); @@ -628,7 +628,6 @@ static int truly_nt35597_remove(struct mipi_dsi_device *dsi) } drm_panel_remove(&ctx->panel); - return 0; } static const struct of_device_id truly_nt35597_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index db2443ac81d3..ec228c269146 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -256,7 +256,7 @@ err_dsi_attach: return ret; } -static int visionox_rm69299_remove(struct mipi_dsi_device *dsi) +static void visionox_rm69299_remove(struct mipi_dsi_device *dsi) { struct visionox_rm69299 *ctx = mipi_dsi_get_drvdata(dsi); @@ -264,7 +264,6 @@ static int visionox_rm69299_remove(struct mipi_dsi_device *dsi) mipi_dsi_device_unregister(ctx->dsi); drm_panel_remove(&ctx->panel); - return 0; } static const struct of_device_id visionox_rm69299_of_match[] = { diff --git a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c index 8177f5a360fb..2c54733ee241 100644 --- a/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c +++ b/drivers/gpu/drm/panel/panel-xinpeng-xpp055c272.c @@ -339,7 +339,7 @@ static void xpp055c272_shutdown(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to disable panel: %d\n", ret); } -static int xpp055c272_remove(struct mipi_dsi_device *dsi) +static void xpp055c272_remove(struct mipi_dsi_device *dsi) { struct xpp055c272 *ctx = mipi_dsi_get_drvdata(dsi); int ret; @@ -351,8 +351,6 @@ static int xpp055c272_remove(struct mipi_dsi_device *dsi) dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); drm_panel_remove(&ctx->panel); - - return 0; } static const struct of_device_id xpp055c272_of_match[] = { diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index 91a164bdd8f3..53e3a8a2f241 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -322,7 +322,7 @@ int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi, struct mipi_dsi_driver { struct device_driver driver; int(*probe)(struct mipi_dsi_device *dsi); - int(*remove)(struct mipi_dsi_device *dsi); + void (*remove)(struct mipi_dsi_device *dsi); void (*shutdown)(struct mipi_dsi_device *dsi); }; -- cgit v1.2.3-59-g8ed1b From 96dc635d5538055cb6ccd7b6e9290dfcfc385f97 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:46 +0200 Subject: drm/fourcc: Add drm_format_info_bpp() helper Add a helper to retrieve the actual number of bits per pixel for a plane, taking into account the number of characters and pixels per block for tiled formats. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/1cae5ebc28513ec1c91c66b00647ce3ca23bfba7.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_fourcc.c | 19 +++++++++++++++++++ include/drm/drm_fourcc.h | 1 + 2 files changed, 20 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 07741b678798..cf48ea0b2cb7 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -370,6 +370,25 @@ unsigned int drm_format_info_block_height(const struct drm_format_info *info, } EXPORT_SYMBOL(drm_format_info_block_height); +/** + * drm_format_info_bpp - number of bits per pixel + * @info: pixel format info + * @plane: plane index + * + * Returns: + * The actual number of bits per pixel, depending on the plane index. + */ +unsigned int drm_format_info_bpp(const struct drm_format_info *info, int plane) +{ + if (!info || plane < 0 || plane >= info->num_planes) + return 0; + + return info->char_per_block[plane] * 8 / + (drm_format_info_block_width(info, plane) * + drm_format_info_block_height(info, plane)); +} +EXPORT_SYMBOL(drm_format_info_bpp); + /** * drm_format_info_min_pitch - computes the minimum required pitch in bytes * @info: pixel format info diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 22aa64d07c79..3800a7ad7f0c 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -313,6 +313,7 @@ unsigned int drm_format_info_block_width(const struct drm_format_info *info, int plane); unsigned int drm_format_info_block_height(const struct drm_format_info *info, int plane); +unsigned int drm_format_info_bpp(const struct drm_format_info *info, int plane); uint64_t drm_format_info_min_pitch(const struct drm_format_info *info, int plane, unsigned int buffer_width); -- cgit v1.2.3-59-g8ed1b From dc1dc76bd9a48c6aba3efae5eadb9a884043966e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:47 +0200 Subject: drm/fourcc: Add drm_format_info.is_color_indexed flag Add a flag to struct drm_format_info to indicate if a format is color-indexed, similar to the existing .is_yuv flag. This way generic code and drivers can just check this flag, instead of checking against a list of fourcc formats. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/90cd390b2b4d481661f966de7b504f1702d80dfd.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_fourcc.c | 2 +- include/drm/drm_fourcc.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index cf48ea0b2cb7..6c76bd821d17 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -132,7 +132,7 @@ EXPORT_SYMBOL(drm_driver_legacy_fb_format); const struct drm_format_info *__drm_format_info(u32 format) { static const struct drm_format_info formats[] = { - { .format = DRM_FORMAT_C8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_C8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, { .format = DRM_FORMAT_R8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R10, .depth = 10, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R12, .depth = 12, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index 3800a7ad7f0c..532ae78ca747 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -138,6 +138,9 @@ struct drm_format_info { /** @is_yuv: Is it a YUV format? */ bool is_yuv; + + /** @is_color_indexed: Is it a color-indexed format? */ + bool is_color_indexed; }; /** -- cgit v1.2.3-59-g8ed1b From 356d2c8e76ebb1134a0685ce4b923d8201337475 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:48 +0200 Subject: drm/client: Use actual bpp when allocating frame buffers When allocating a frame buffer, the number of bits per pixel needed is derived from the deprecated drm_format_info.cpp[] field. While this may work for formats using less than 8 bits per pixel, it does lead to a large overallocation. Reduce memory consumption by using the actual number of bits per pixel instead. Signed-off-by: Geert Uytterhoeven Acked-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/eabcf4f298184dabe46823fcf5ceffa1da0ec7ef.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index af3b7395bf69..2b230b4d6942 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -264,7 +264,7 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u dumb_args.width = width; dumb_args.height = height; - dumb_args.bpp = info->cpp[0] * 8; + dumb_args.bpp = drm_format_info_bpp(info, 0); ret = drm_mode_create_dumb(dev, &dumb_args, client->file); if (ret) goto err_delete; @@ -373,7 +373,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer, int ret; info = drm_format_info(format); - fb_req.bpp = info->cpp[0] * 8; + fb_req.bpp = drm_format_info_bpp(info, 0); fb_req.depth = info->depth; fb_req.width = width; fb_req.height = height; -- cgit v1.2.3-59-g8ed1b From 4c59b0805e9bf873fd3f5dca4fc53d8c220e39b4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:49 +0200 Subject: drm/framebuffer: Use actual bpp for DRM_IOCTL_MODE_GETFB When userspace queries the properties of a frame buffer, the number of bits per pixel is derived from the deprecated drm_format_info.cpp[] field, which does not take into account block sizes. Fix this by using the actual number of bits per pixel instead. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/b612780feef200b7f914a674db28ed227e7cdc64.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_framebuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 4562a8b86579..9899bf1485b2 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -530,7 +530,7 @@ int drm_mode_getfb(struct drm_device *dev, r->height = fb->height; r->width = fb->width; r->depth = fb->format->depth; - r->bpp = fb->format->cpp[0] * 8; + r->bpp = drm_format_info_bpp(fb->format, 0); r->pitch = fb->pitches[0]; /* GET_FB() is an unprivileged ioctl so we must not return a -- cgit v1.2.3-59-g8ed1b From e5bd7e3e4a68f0befe53f59954b25eec9a792d60 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:50 +0200 Subject: drm/fourcc: Add DRM_FORMAT_C[124] Introduce fourcc codes for color-indexed frame buffer formats with two, four, and sixteen colors, and provide a mapping from bits per pixel and depth to fourcc codes. As the number of bits per pixel is less than eight, these rely on proper block handling for the calculation of bits per pixel and pitch. The fill order (the order in which multiple pixels are packed in a byte) is the same order as used for indexed-color (2, 4, and 16 colors) images in the PNG specification, Version 1.2. This order is also the recommended and default order (FillOrder = 1) for palette-color (16 colors) images in the TIFF 6.0 Specification, and is also used for 16-color Linux frame buffer logos. Signed-off-by: Geert Uytterhoeven Reviewed-by: Pekka Paalanen Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/3d88ca7ad32ff3ff3469c10f0b36c312ea233a33.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_fourcc.c | 21 +++++++++++++++++++++ include/uapi/drm/drm_fourcc.h | 3 +++ 2 files changed, 24 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 6c76bd821d17..29f4fe199c4d 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -43,6 +43,21 @@ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) uint32_t fmt = DRM_FORMAT_INVALID; switch (bpp) { + case 1: + if (depth == 1) + fmt = DRM_FORMAT_C1; + break; + + case 2: + if (depth == 2) + fmt = DRM_FORMAT_C2; + break; + + case 4: + if (depth == 4) + fmt = DRM_FORMAT_C4; + break; + case 8: if (depth == 8) fmt = DRM_FORMAT_C8; @@ -132,6 +147,12 @@ EXPORT_SYMBOL(drm_driver_legacy_fb_format); const struct drm_format_info *__drm_format_info(u32 format) { static const struct drm_format_info formats[] = { + { .format = DRM_FORMAT_C1, .depth = 1, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 8, }, .block_h = { 1, }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, + { .format = DRM_FORMAT_C2, .depth = 2, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 4, }, .block_h = { 1, }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, + { .format = DRM_FORMAT_C4, .depth = 4, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 2, }, .block_h = { 1, }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, { .format = DRM_FORMAT_C8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, { .format = DRM_FORMAT_R8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R10, .depth = 10, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index c1b4cfda7507..b9c746c3ebbc 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -99,6 +99,9 @@ extern "C" { #define DRM_FORMAT_INVALID 0 /* color index */ +#define DRM_FORMAT_C1 fourcc_code('C', '1', ' ', ' ') /* [7:0] C0:C1:C2:C3:C4:C5:C6:C7 1:1:1:1:1:1:1:1 eight pixels/byte */ +#define DRM_FORMAT_C2 fourcc_code('C', '2', ' ', ' ') /* [7:0] C0:C1:C2:C3 2:2:2:2 four pixels/byte */ +#define DRM_FORMAT_C4 fourcc_code('C', '4', ' ', ' ') /* [7:0] C0:C1 4:4 two pixels/byte */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ /* 8 bpp Red */ -- cgit v1.2.3-59-g8ed1b From ba71593217b5b03c9e3385b268ff8613a71fc6ce Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:51 +0200 Subject: drm/fb-helper: Add support for DRM_FORMAT_C[124] Add support for color-indexed frame buffer formats with two, four, and sixteen colors to the DRM framebuffer helper functions: 1. Add support for 1, 2, and 4 bits per pixel to the damage helper, 2. For color-indexed modes, the length of the color bitfields must be set to the color depth, else the logo code may pick a logo with too many colors. Drop the incorrect DAC width comment, which originates from the i915 driver. 3. Accept C[124] modes when validating or filling in struct fb_var_screeninfo, and use the correct number of bits per pixel. 4. Set the visual to FB_VISUAL_PSEUDOCOLOR for all color-indexed modes. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/db3e80b445df661ff0cd7e698507a8d24a4c867e.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_fb_helper.c | 101 +++++++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 26 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 2d4cee6a10ff..71edb80fe0fb 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -377,12 +377,31 @@ static void drm_fb_helper_damage_blit_real(struct drm_fb_helper *fb_helper, struct iosys_map *dst) { struct drm_framebuffer *fb = fb_helper->fb; - unsigned int cpp = fb->format->cpp[0]; - size_t offset = clip->y1 * fb->pitches[0] + clip->x1 * cpp; - void *src = fb_helper->fbdev->screen_buffer + offset; - size_t len = (clip->x2 - clip->x1) * cpp; + size_t offset = clip->y1 * fb->pitches[0]; + size_t len = clip->x2 - clip->x1; unsigned int y; + void *src; + switch (drm_format_info_bpp(fb->format, 0)) { + case 1: + offset += clip->x1 / 8; + len = DIV_ROUND_UP(len + clip->x1 % 8, 8); + break; + case 2: + offset += clip->x1 / 4; + len = DIV_ROUND_UP(len + clip->x1 % 4, 4); + break; + case 4: + offset += clip->x1 / 2; + len = DIV_ROUND_UP(len + clip->x1 % 2, 2); + break; + default: + offset += clip->x1 * fb->format->cpp[0]; + len *= fb->format->cpp[0]; + break; + } + + src = fb_helper->fbdev->screen_buffer + offset; iosys_map_incr(dst, offset); /* go to first pixel within clip rect */ for (y = clip->y1; y < clip->y2; y++) { @@ -1274,19 +1293,23 @@ static bool drm_fb_pixel_format_equal(const struct fb_var_screeninfo *var_1, } static void drm_fb_helper_fill_pixel_fmt(struct fb_var_screeninfo *var, - u8 depth) + const struct drm_format_info *format) { - switch (depth) { - case 8: + u8 depth = format->depth; + + if (format->is_color_indexed) { var->red.offset = 0; var->green.offset = 0; var->blue.offset = 0; - var->red.length = 8; /* 8bit DAC */ - var->green.length = 8; - var->blue.length = 8; + var->red.length = depth; + var->green.length = depth; + var->blue.length = depth; var->transp.offset = 0; var->transp.length = 0; - break; + return; + } + + switch (depth) { case 15: var->red.offset = 10; var->green.offset = 5; @@ -1341,7 +1364,9 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, { struct drm_fb_helper *fb_helper = info->par; struct drm_framebuffer *fb = fb_helper->fb; + const struct drm_format_info *format = fb->format; struct drm_device *dev = fb_helper->dev; + unsigned int bpp; if (in_dbg_master()) return -EINVAL; @@ -1351,22 +1376,33 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, var->pixclock = 0; } - if ((drm_format_info_block_width(fb->format, 0) > 1) || - (drm_format_info_block_height(fb->format, 0) > 1)) - return -EINVAL; + switch (format->format) { + case DRM_FORMAT_C1: + case DRM_FORMAT_C2: + case DRM_FORMAT_C4: + /* supported format with sub-byte pixels */ + break; + + default: + if ((drm_format_info_block_width(format, 0) > 1) || + (drm_format_info_block_height(format, 0) > 1)) + return -EINVAL; + break; + } /* * Changes struct fb_var_screeninfo are currently not pushed back * to KMS, hence fail if different settings are requested. */ - if (var->bits_per_pixel > fb->format->cpp[0] * 8 || + bpp = drm_format_info_bpp(format, 0); + if (var->bits_per_pixel > bpp || var->xres > fb->width || var->yres > fb->height || var->xres_virtual > fb->width || var->yres_virtual > fb->height) { drm_dbg_kms(dev, "fb requested width/height/bpp can't fit in current fb " "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel, var->xres_virtual, var->yres_virtual, - fb->width, fb->height, fb->format->cpp[0] * 8); + fb->width, fb->height, bpp); return -EINVAL; } @@ -1381,13 +1417,13 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, !var->blue.length && !var->transp.length && !var->red.msb_right && !var->green.msb_right && !var->blue.msb_right && !var->transp.msb_right) { - drm_fb_helper_fill_pixel_fmt(var, fb->format->depth); + drm_fb_helper_fill_pixel_fmt(var, format); } /* * Likewise, bits_per_pixel should be rounded up to a supported value. */ - var->bits_per_pixel = fb->format->cpp[0] * 8; + var->bits_per_pixel = bpp; /* * drm fbdev emulation doesn't support changing the pixel format at all, @@ -1723,11 +1759,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } static void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, - uint32_t depth) + bool is_color_indexed) { info->fix.type = FB_TYPE_PACKED_PIXELS; - info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : - FB_VISUAL_TRUECOLOR; + info->fix.visual = is_color_indexed ? FB_VISUAL_PSEUDOCOLOR + : FB_VISUAL_TRUECOLOR; info->fix.mmio_start = 0; info->fix.mmio_len = 0; info->fix.type_aux = 0; @@ -1744,19 +1780,31 @@ static void drm_fb_helper_fill_var(struct fb_info *info, uint32_t fb_width, uint32_t fb_height) { struct drm_framebuffer *fb = fb_helper->fb; + const struct drm_format_info *format = fb->format; + + switch (format->format) { + case DRM_FORMAT_C1: + case DRM_FORMAT_C2: + case DRM_FORMAT_C4: + /* supported format with sub-byte pixels */ + break; + + default: + WARN_ON((drm_format_info_block_width(format, 0) > 1) || + (drm_format_info_block_height(format, 0) > 1)); + break; + } - WARN_ON((drm_format_info_block_width(fb->format, 0) > 1) || - (drm_format_info_block_height(fb->format, 0) > 1)); info->pseudo_palette = fb_helper->pseudo_palette; info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; - info->var.bits_per_pixel = fb->format->cpp[0] * 8; + info->var.bits_per_pixel = drm_format_info_bpp(format, 0); info->var.accel_flags = FB_ACCELF_TEXT; info->var.xoffset = 0; info->var.yoffset = 0; info->var.activate = FB_ACTIVATE_NOW; - drm_fb_helper_fill_pixel_fmt(&info->var, fb->format->depth); + drm_fb_helper_fill_pixel_fmt(&info->var, format); info->var.xres = fb_width; info->var.yres = fb_height; @@ -1781,7 +1829,8 @@ void drm_fb_helper_fill_info(struct fb_info *info, { struct drm_framebuffer *fb = fb_helper->fb; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_fix(info, fb->pitches[0], + fb->format->is_color_indexed); drm_fb_helper_fill_var(info, fb_helper, sizes->fb_width, sizes->fb_height); -- cgit v1.2.3-59-g8ed1b From 0a2486f85e0cfa6439116abb9078ab888a619746 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:52 +0200 Subject: drm/gem-fb-helper: Use actual bpp for size calculations The AFBC helpers derive the number of bits per pixel from the deprecated drm_format_info.cpp[] field, which does not take into account block sizes. Fix this by using the actual number of bits per pixel instead. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/b1d938d903157627c4ac101df92a1f3bf8ebe83d.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 61339a9cd010..880a4975507f 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -490,6 +490,8 @@ void drm_gem_fb_end_cpu_access(struct drm_framebuffer *fb, enum dma_data_directi } EXPORT_SYMBOL(drm_gem_fb_end_cpu_access); +// TODO Drop this function and replace by drm_format_info_bpp() once all +// DRM_FORMAT_* provide proper block info in drivers/gpu/drm/drm_fourcc.c static __u32 drm_gem_afbc_get_bpp(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd) { @@ -497,11 +499,6 @@ static __u32 drm_gem_afbc_get_bpp(struct drm_device *dev, info = drm_get_format_info(dev, mode_cmd); - /* use whatever a driver has set */ - if (info->cpp[0]) - return info->cpp[0] * 8; - - /* guess otherwise */ switch (info->format) { case DRM_FORMAT_YUV420_8BIT: return 12; @@ -510,11 +507,8 @@ static __u32 drm_gem_afbc_get_bpp(struct drm_device *dev, case DRM_FORMAT_VUY101010: return 30; default: - break; + return drm_format_info_bpp(info, 0); } - - /* all attempts failed */ - return 0; } static int drm_gem_afbc_min_size(struct drm_device *dev, -- cgit v1.2.3-59-g8ed1b From d093100b425df6fe400881f2e62c3f0be7bf18cf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:54 +0200 Subject: drm/fourcc: Add DRM_FORMAT_R[124] Introduce fourcc codes for single-channel frame buffer formats with two, four, and sixteen brightness levels, where there is a direct relationship between channel value and brightness. As the number of bits per pixel is less than eight, these rely on proper block handling for the calculation of bits per pixel and pitch. The fill order (the order in which multiple pixels are packed in a byte) is the same order as used for grayscale (2, 4, and 16 levels) images in the PNG specification, Version 1.2. This order is also the recommended and default order (FillOrder = 1) for bilevel and grayscale (16 levels) images in the TIFF 6.0 Specification, and is also used for monochrome images in the PBM file format, monochrome Linux frame buffer logos, and BDF and PSF (Linux kernel) font files. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/96561a88e53c59cac72e66642bf4c097aacefd18.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_fourcc.c | 6 ++++++ include/uapi/drm/drm_fourcc.h | 9 +++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 29f4fe199c4d..05e65e9ab0c6 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -154,6 +154,12 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_C4, .depth = 4, .num_planes = 1, .char_per_block = { 1, }, .block_w = { 2, }, .block_h = { 1, }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, { .format = DRM_FORMAT_C8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, + { .format = DRM_FORMAT_R1, .depth = 1, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 8, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_R2, .depth = 2, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 4, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_R4, .depth = 4, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 2, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R10, .depth = 10, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R12, .depth = 12, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 1, .vsub = 1 }, diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 9521057701c4..6190fe3c6cc8 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -104,6 +104,15 @@ extern "C" { #define DRM_FORMAT_C4 fourcc_code('C', '4', ' ', ' ') /* [7:0] C0:C1 4:4 two pixels/byte */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ +/* 1 bpp Red (direct relationship between channel value and brightness) */ +#define DRM_FORMAT_R1 fourcc_code('R', '1', ' ', ' ') /* [7:0] R0:R1:R2:R3:R4:R5:R6:R7 1:1:1:1:1:1:1:1 eight pixels/byte */ + +/* 2 bpp Red (direct relationship between channel value and brightness) */ +#define DRM_FORMAT_R2 fourcc_code('R', '2', ' ', ' ') /* [7:0] R0:R1:R2:R3 2:2:2:2 four pixels/byte */ + +/* 4 bpp Red (direct relationship between channel value and brightness) */ +#define DRM_FORMAT_R4 fourcc_code('R', '4', ' ', ' ') /* [7:0] R0:R1 4:4 two pixels/byte */ + /* 8 bpp Red (direct relationship between channel value and brightness) */ #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ -- cgit v1.2.3-59-g8ed1b From b92db7e4fe740daab858366faff1f992d53d3ff4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:20:55 +0200 Subject: drm/fourcc: Add DRM_FORMAT_D[1248] As Rn covers single-channel formats with a direct relationship between channel value and brightness, and Cn can be any colors, there are currently no fourcc codes to describe single-channel formats with an inverse relationship between channel value and brightness. Introduce fourcc codes for a single-channel frame buffer format with two, four, sixteen, or 256 brightness ("darkness") levels, where there is an inverse relationship between channel value and brightness. As the number of bits per pixel may be less than eight, some of these formats rely on proper block handling for the calculation of bits per pixel and pitch. The fill order (the order in which multiple pixels are packed in a byte) is the same order as used for grayscale (2, 4, and 16 levels) images in the PNG specification, Version 1.2. This order is also the recommended and default order (FillOrder = 1) for bilevel and grayscale (16 levels) images in the TIFF 6.0 Specification, and is also used for monochrome images in the PBM file format, monochrome Linux frame buffer logos, and BDF and PSF (Linux kernel) font files. Signed-off-by: Geert Uytterhoeven Reviewed-by: Javier Martinez Canillas Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/6119f3abeda9baaa88652843960adc032da276b4.1657294931.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_fourcc.c | 7 +++++++ include/uapi/drm/drm_fourcc.h | 12 ++++++++++++ 2 files changed, 19 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index 05e65e9ab0c6..e09331bb3bc7 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -154,6 +154,13 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_C4, .depth = 4, .num_planes = 1, .char_per_block = { 1, }, .block_w = { 2, }, .block_h = { 1, }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, { .format = DRM_FORMAT_C8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1, .is_color_indexed = true }, + { .format = DRM_FORMAT_D1, .depth = 1, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 8, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_D2, .depth = 2, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 4, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_D4, .depth = 4, .num_planes = 1, + .char_per_block = { 1, }, .block_w = { 2, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, + { .format = DRM_FORMAT_D8, .depth = 8, .num_planes = 1, .cpp = { 1, 0, 0 }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R1, .depth = 1, .num_planes = 1, .char_per_block = { 1, }, .block_w = { 8, }, .block_h = { 1, }, .hsub = 1, .vsub = 1 }, { .format = DRM_FORMAT_R2, .depth = 2, .num_planes = 1, diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h index 6190fe3c6cc8..80e0d432029c 100644 --- a/include/uapi/drm/drm_fourcc.h +++ b/include/uapi/drm/drm_fourcc.h @@ -104,6 +104,18 @@ extern "C" { #define DRM_FORMAT_C4 fourcc_code('C', '4', ' ', ' ') /* [7:0] C0:C1 4:4 two pixels/byte */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ +/* 1 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D1 fourcc_code('D', '1', ' ', ' ') /* [7:0] D0:D1:D2:D3:D4:D5:D6:D7 1:1:1:1:1:1:1:1 eight pixels/byte */ + +/* 2 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D2 fourcc_code('D', '2', ' ', ' ') /* [7:0] D0:D1:D2:D3 2:2:2:2 four pixels/byte */ + +/* 4 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D4 fourcc_code('D', '4', ' ', ' ') /* [7:0] D0:D1 4:4 two pixels/byte */ + +/* 8 bpp Darkness (inverse relationship between channel value and brightness) */ +#define DRM_FORMAT_D8 fourcc_code('D', '8', ' ', ' ') /* [7:0] D */ + /* 1 bpp Red (direct relationship between channel value and brightness) */ #define DRM_FORMAT_R1 fourcc_code('R', '1', ' ', ' ') /* [7:0] R0:R1:R2:R3:R4:R5:R6:R7 1:1:1:1:1:1:1:1 eight pixels/byte */ -- cgit v1.2.3-59-g8ed1b From a6aa679a70e9d8fa4ad3f519c060db9bb186e21c Mon Sep 17 00:00:00 2001 From: Martin Jücker Date: Mon, 16 May 2022 21:37:08 +0200 Subject: drm/panel: simple: add support for the Samsung LTL101AL01 panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add timings and panel description for the Samsung LTL101AL01 panel. Signed-off-by: Martin Jücker Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220516193709.10037-2-martin.juecker@gmail.com --- drivers/gpu/drm/panel/panel-simple.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index c1d224cb719d..edd5a0c35437 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -3218,6 +3218,37 @@ static const struct panel_desc rocktech_rk101ii01d_ct = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct display_timing samsung_ltl101al01_timing = { + .pixelclock = { 66663000, 66663000, 66663000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 18, 18, 18 }, + .hback_porch = { 36, 36, 36 }, + .hsync_len = { 16, 16, 16 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 4, 4, 4 }, + .vback_porch = { 16, 16, 16 }, + .vsync_len = { 3, 3, 3 }, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW, +}; + +static const struct panel_desc samsung_ltl101al01 = { + .timings = &samsung_ltl101al01_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 217, + .height = 135, + }, + .delay = { + .prepare = 40, + .enable = 300, + .disable = 200, + .unprepare = 600, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode samsung_ltn101nt05_mode = { .clock = 54030, .hdisplay = 1024, @@ -4161,6 +4192,9 @@ static const struct of_device_id platform_of_match[] = { }, { .compatible = "rocktech,rk101ii01d-ct", .data = &rocktech_rk101ii01d_ct, + }, { + .compatible = "samsung,ltl101al01", + .data = &samsung_ltl101al01, }, { .compatible = "samsung,ltn101nt05", .data = &samsung_ltn101nt05, -- cgit v1.2.3-59-g8ed1b From 2ad5a3ac9dc6c98a045385dc8667e9499577a325 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 8 Jul 2022 21:54:02 +0100 Subject: drm/ingenic: Fix MODULE_LICENSE() string The previous "GPL v2" string is deprecated. For more info, see commit bf7fbeeae6db ("module: Cure the MODULE_LICENSE "GPL" vs. "GPL v2" bogosity") Signed-off-by: Paul Cercueil Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708205406.96473-3-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index eb8208bfe5ab..e435c19d54d5 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1616,4 +1616,4 @@ module_exit(ingenic_drm_exit); MODULE_AUTHOR("Paul Cercueil "); MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From d292dc324425995d7a45b07f3abf9a9919f3a1a9 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 8 Jul 2022 21:54:03 +0100 Subject: drm/ingenic: Add support for the JZ4760(B) Add support for the JZ4760 and JZ4760B SoCs to the ingenic-drm display driver. Signed-off-by: Paul Cercueil Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708205406.96473-4-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index e435c19d54d5..78d0b035e2d7 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1541,6 +1541,32 @@ static const struct jz_soc_info jz4725b_soc_info = { .num_formats_f0 = ARRAY_SIZE(jz4725b_formats_f0), }; +static const struct jz_soc_info jz4760_soc_info = { + .needs_dev_clk = false, + .has_osd = true, + .map_noncoherent = false, + .max_width = 1280, + .max_height = 720, + .max_burst = JZ_LCD_CTRL_BURST_32, + .formats_f1 = jz4770_formats_f1, + .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1), + .formats_f0 = jz4770_formats_f0, + .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0), +}; + +static const struct jz_soc_info jz4760b_soc_info = { + .needs_dev_clk = false, + .has_osd = true, + .map_noncoherent = false, + .max_width = 1280, + .max_height = 720, + .max_burst = JZ_LCD_CTRL_BURST_64, + .formats_f1 = jz4770_formats_f1, + .num_formats_f1 = ARRAY_SIZE(jz4770_formats_f1), + .formats_f0 = jz4770_formats_f0, + .num_formats_f0 = ARRAY_SIZE(jz4770_formats_f0), +}; + static const struct jz_soc_info jz4770_soc_info = { .needs_dev_clk = false, .has_osd = true, @@ -1572,6 +1598,8 @@ static const struct jz_soc_info jz4780_soc_info = { static const struct of_device_id ingenic_drm_of_match[] = { { .compatible = "ingenic,jz4740-lcd", .data = &jz4740_soc_info }, { .compatible = "ingenic,jz4725b-lcd", .data = &jz4725b_soc_info }, + { .compatible = "ingenic,jz4760-lcd", .data = &jz4760_soc_info }, + { .compatible = "ingenic,jz4760b-lcd", .data = &jz4760b_soc_info }, { .compatible = "ingenic,jz4770-lcd", .data = &jz4770_soc_info }, { .compatible = "ingenic,jz4780-lcd", .data = &jz4780_soc_info }, { /* sentinel */ }, -- cgit v1.2.3-59-g8ed1b From c167ee1f75ca5947bafe93ab23b007cb243e5f4a Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 8 Jul 2022 21:54:04 +0100 Subject: drm/ingenic: Don't request full modeset if property is not modified Avoid requesting a full modeset if the sharpness property is not modified, because then we don't actually need it. Signed-off-by: Paul Cercueil Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708205406.96473-5-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-ipu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c index 32a50935aa6d..d13f58ad4769 100644 --- a/drivers/gpu/drm/ingenic/ingenic-ipu.c +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -697,10 +697,12 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, { struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); struct drm_crtc_state *crtc_state; + bool mode_changed; if (property != ipu->sharpness_prop) return -EINVAL; + mode_changed = val != ipu->sharpness; ipu->sharpness = val; if (state->crtc) { @@ -708,7 +710,7 @@ ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, if (WARN_ON(!crtc_state)) return -EINVAL; - crtc_state->mode_changed = true; + crtc_state->mode_changed |= mode_changed; } return 0; -- cgit v1.2.3-59-g8ed1b From 006f3b2f324cb2277bdcb89693296d9647ece251 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 8 Jul 2022 21:54:06 +0100 Subject: drm/ingenic: Use the new PM macros - Use DEFINE_SIMPLE_DEV_PM_OPS() instead of the SIMPLE_DEV_PM_OPS() macro. This makes it possible to remove the __maybe_unused flags on the callback functions. - Since we only have callbacks for suspend/resume, we can conditionally compile the dev_pm_ops structure for when CONFIG_PM_SLEEP is enabled; so use the pm_sleep_ptr() macro instead of pm_ptr(). Signed-off-by: Paul Cercueil Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220708205406.96473-7-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 78d0b035e2d7..4de729bbf152 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1464,21 +1464,22 @@ static int ingenic_drm_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused ingenic_drm_suspend(struct device *dev) +static int ingenic_drm_suspend(struct device *dev) { struct ingenic_drm *priv = dev_get_drvdata(dev); return drm_mode_config_helper_suspend(&priv->drm); } -static int __maybe_unused ingenic_drm_resume(struct device *dev) +static int ingenic_drm_resume(struct device *dev) { struct ingenic_drm *priv = dev_get_drvdata(dev); return drm_mode_config_helper_resume(&priv->drm); } -static SIMPLE_DEV_PM_OPS(ingenic_drm_pm_ops, ingenic_drm_suspend, ingenic_drm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(ingenic_drm_pm_ops, + ingenic_drm_suspend, ingenic_drm_resume); static const u32 jz4740_formats[] = { DRM_FORMAT_XRGB1555, @@ -1609,7 +1610,7 @@ MODULE_DEVICE_TABLE(of, ingenic_drm_of_match); static struct platform_driver ingenic_drm_driver = { .driver = { .name = "ingenic-drm", - .pm = pm_ptr(&ingenic_drm_pm_ops), + .pm = pm_sleep_ptr(&ingenic_drm_pm_ops), .of_match_table = of_match_ptr(ingenic_drm_of_match), }, .probe = ingenic_drm_probe, -- cgit v1.2.3-59-g8ed1b From d395fb1999abb7eaa3fb5fd2107cae0f1e41e6d0 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Fri, 8 Jul 2022 02:30:45 -0700 Subject: Revert "drm/amdgpu: move internal vram_mgr function into the C file" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 708d19d9f362766147cab79eccae60912c6d3068. This is part of a revert of the following commits: commit 708d19d9f362 ("drm/amdgpu: move internal vram_mgr function into the C file") commit 5e3f1e7729ec ("drm/amdgpu: fix start calculation in amdgpu_vram_mgr_new") commit c9cad937c0c5 ("drm/amdgpu: add drm buddy support to amdgpu") [WHY] Few users reported garbaged graphics as soon as x starts, reverting until this can be resolved. Signed-off-by: Arunpravin Paneer Selvam Reviewed-by: Christian König Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220708093047.492662-1-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 29 ---------------------------- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h | 27 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 29 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 7a5e8a7b4a1b..51d9d3a4456c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -50,35 +50,6 @@ to_amdgpu_device(struct amdgpu_vram_mgr *mgr) return container_of(mgr, struct amdgpu_device, mman.vram_mgr); } -static inline struct drm_buddy_block * -amdgpu_vram_mgr_first_block(struct list_head *list) -{ - return list_first_entry_or_null(list, struct drm_buddy_block, link); -} - -static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) -{ - struct drm_buddy_block *block; - u64 start, size; - - block = amdgpu_vram_mgr_first_block(head); - if (!block) - return false; - - while (head != block->link.next) { - start = amdgpu_vram_mgr_block_start(block); - size = amdgpu_vram_mgr_block_size(block); - - block = list_entry(block->link.next, struct drm_buddy_block, link); - if (start + size != amdgpu_vram_mgr_block_start(block)) - return false; - } - - return true; -} - - - /** * DOC: mem_info_vram_total * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h index 4b267bf1c5db..9a2db87186c7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -53,6 +53,33 @@ static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block) return PAGE_SIZE << drm_buddy_block_order(block); } +static inline struct drm_buddy_block * +amdgpu_vram_mgr_first_block(struct list_head *list) +{ + return list_first_entry_or_null(list, struct drm_buddy_block, link); +} + +static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) +{ + struct drm_buddy_block *block; + u64 start, size; + + block = amdgpu_vram_mgr_first_block(head); + if (!block) + return false; + + while (head != block->link.next) { + start = amdgpu_vram_mgr_block_start(block); + size = amdgpu_vram_mgr_block_size(block); + + block = list_entry(block->link.next, struct drm_buddy_block, link); + if (start + size != amdgpu_vram_mgr_block_start(block)) + return false; + } + + return true; +} + static inline struct amdgpu_vram_mgr_resource * to_amdgpu_vram_mgr_resource(struct ttm_resource *res) { -- cgit v1.2.3-59-g8ed1b From 20529e260ff2fb4a00b54a1e625ebebe5d1a6210 Mon Sep 17 00:00:00 2001 From: Arunpravin Paneer Selvam Date: Fri, 8 Jul 2022 02:30:46 -0700 Subject: Revert "drm/amdgpu: fix start calculation in amdgpu_vram_mgr_new" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5e3f1e7729ec7a99e145e9d8ed58963d86cdfb98. This is part of a revert of the following commits: commit 708d19d9f362 ("drm/amdgpu: move internal vram_mgr function into the C file") commit 5e3f1e7729ec ("drm/amdgpu: fix start calculation in amdgpu_vram_mgr_new") commit c9cad937c0c5 ("drm/amdgpu: add drm buddy support to amdgpu") [WHY] Few users reported garbaged graphics as soon as x starts, reverting until this can be resolved. Signed-off-by: Arunpravin Paneer Selvam Reviewed-by: Christian König Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220708093047.492662-2-Arunpravin.PaneerSelvam@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 51d9d3a4456c..49e4092f447f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -496,23 +496,17 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, list_splice_tail(trim_list, &vres->blocks); } - vres->base.start = 0; - list_for_each_entry(block, &vres->blocks, link) { - unsigned long start; - - start = amdgpu_vram_mgr_block_start(block) + - amdgpu_vram_mgr_block_size(block); - start >>= PAGE_SHIFT; - - if (start > vres->base.num_pages) - start -= vres->base.num_pages; - else - start = 0; - vres->base.start = max(vres->base.start, start); - + list_for_each_entry(block, &vres->blocks, link) vis_usage += amdgpu_vram_mgr_vis_size(adev, block); + + block = amdgpu_vram_mgr_first_block(&vres->blocks); + if (!block) { + r = -EINVAL; + goto error_fini; } + vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT; + if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks)) vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS; -- cgit v1.2.3-59-g8ed1b From 347987a2cf0d146484d1c586951ef10028bb1674 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 18 Feb 2022 14:32:53 +0100 Subject: drm/ttm: rename and cleanup ttm_bo_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename ttm_bo_init to ttm_bo_init_validate since that better matches what the function is actually doing. Remove the unused size parameter, move the function's kerneldoc to the implementation and cleanup the whole error handling. Signed-off-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20220707102453.3633-2-christian.koenig@amd.com Reviewed-by: Michael J. Ruhl --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 2 +- drivers/gpu/drm/drm_gem_vram_helper.c | 6 +- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 5 +- drivers/gpu/drm/nouveau/nouveau_bo.c | 6 +- drivers/gpu/drm/qxl/qxl_object.c | 2 +- drivers/gpu/drm/radeon/radeon_object.c | 6 +- drivers/gpu/drm/ttm/ttm_bo.c | 147 ++++++++++++++++++++--------- drivers/gpu/drm/vmwgfx/vmwgfx_bo.c | 12 +-- include/drm/ttm/ttm_bo_api.h | 93 ++---------------- 9 files changed, 129 insertions(+), 150 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 2c82b1d5a0d7..d9cfe259f2a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -591,7 +591,7 @@ int amdgpu_bo_create(struct amdgpu_device *adev, if (!bp->destroy) bp->destroy = &amdgpu_bo_destroy; - r = ttm_bo_init_reserved(&adev->mman.bdev, &bo->tbo, size, bp->type, + r = ttm_bo_init_reserved(&adev->mman.bdev, &bo->tbo, bp->type, &bo->placement, page_align, &ctx, NULL, bp->resv, bp->destroy); if (unlikely(r != 0)) diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index d607043716d3..125160b534be 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -226,9 +226,9 @@ struct drm_gem_vram_object *drm_gem_vram_create(struct drm_device *dev, * A failing ttm_bo_init will call ttm_buffer_object_destroy * to release gbo->bo.base and kfree gbo. */ - ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device, - &gbo->placement, pg_align, false, NULL, NULL, - ttm_buffer_object_destroy); + ret = ttm_bo_init_validate(bdev, &gbo->bo, ttm_bo_type_device, + &gbo->placement, pg_align, false, NULL, NULL, + ttm_buffer_object_destroy); if (ret) return ERR_PTR(ret); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 4c25d9b2f138..70e2ed4e99df 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -1229,9 +1229,8 @@ int __i915_gem_ttm_object_init(struct intel_memory_region *mem, * Similarly, in delayed_destroy, we can't call ttm_bo_put() * until successful initialization. */ - ret = ttm_bo_init_reserved(&i915->bdev, i915_gem_to_ttm(obj), size, - bo_type, &i915_sys_placement, - page_size >> PAGE_SHIFT, + ret = ttm_bo_init_reserved(&i915->bdev, i915_gem_to_ttm(obj), bo_type, + &i915_sys_placement, page_size >> PAGE_SHIFT, &ctx, NULL, NULL, i915_ttm_bo_destroy); if (ret) return i915_ttm_err_to_gem(ret); diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index d0887438b07e..994879d9db74 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -309,9 +309,9 @@ nouveau_bo_init(struct nouveau_bo *nvbo, u64 size, int align, u32 domain, nouveau_bo_placement_set(nvbo, domain, 0); INIT_LIST_HEAD(&nvbo->io_reserve_lru); - ret = ttm_bo_init(nvbo->bo.bdev, &nvbo->bo, size, type, - &nvbo->placement, align >> PAGE_SHIFT, false, sg, - robj, nouveau_bo_del_ttm); + ret = ttm_bo_init_validate(nvbo->bo.bdev, &nvbo->bo, type, + &nvbo->placement, align >> PAGE_SHIFT, false, + sg, robj, nouveau_bo_del_ttm); if (ret) { /* ttm will call nouveau_bo_del_ttm if it fails.. */ return ret; diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index b42a657e4c2f..695d9308d1f0 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -141,7 +141,7 @@ int qxl_bo_create(struct qxl_device *qdev, unsigned long size, qxl_ttm_placement_from_domain(bo, domain); bo->tbo.priority = priority; - r = ttm_bo_init_reserved(&qdev->mman.bdev, &bo->tbo, size, type, + r = ttm_bo_init_reserved(&qdev->mman.bdev, &bo->tbo, type, &bo->placement, 0, &ctx, NULL, NULL, &qxl_ttm_bo_destroy); if (unlikely(r != 0)) { diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 6c4a6802ca96..00c33b24d5d3 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -202,9 +202,9 @@ int radeon_bo_create(struct radeon_device *rdev, radeon_ttm_placement_from_domain(bo, domain); /* Kernel allocation are uninterruptible */ down_read(&rdev->pm.mclk_lock); - r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type, - &bo->placement, page_align, !kernel, sg, resv, - &radeon_ttm_bo_destroy); + r = ttm_bo_init_validate(&rdev->mman.bdev, &bo->tbo, type, + &bo->placement, page_align, !kernel, sg, resv, + &radeon_ttm_bo_destroy); up_read(&rdev->pm.mclk_lock); if (unlikely(r != 0)) { return r; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 296af2b89951..dbd331939e6d 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -915,36 +915,61 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, } EXPORT_SYMBOL(ttm_bo_validate); -int ttm_bo_init_reserved(struct ttm_device *bdev, - struct ttm_buffer_object *bo, - size_t size, - enum ttm_bo_type type, - struct ttm_placement *placement, - uint32_t page_alignment, - struct ttm_operation_ctx *ctx, - struct sg_table *sg, - struct dma_resv *resv, +/** + * ttm_bo_init_reserved + * + * @bdev: Pointer to a ttm_device struct. + * @bo: Pointer to a ttm_buffer_object to be initialized. + * @type: Requested type of buffer object. + * @placement: Initial placement for buffer object. + * @alignment: Data alignment in pages. + * @ctx: TTM operation context for memory allocation. + * @sg: Scatter-gather table. + * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. + * @destroy: Destroy function. Use NULL for kfree(). + * + * This function initializes a pre-allocated struct ttm_buffer_object. + * As this object may be part of a larger structure, this function, + * together with the @destroy function, enables driver-specific objects + * derived from a ttm_buffer_object. + * + * On successful return, the caller owns an object kref to @bo. The kref and + * list_kref are usually set to 1, but note that in some situations, other + * tasks may already be holding references to @bo as well. + * Furthermore, if resv == NULL, the buffer's reservation lock will be held, + * and it is the caller's responsibility to call ttm_bo_unreserve. + * + * If a failure occurs, the function will call the @destroy function. Thus, + * after a failure, dereferencing @bo is illegal and will likely cause memory + * corruption. + * + * Returns + * -ENOMEM: Out of memory. + * -EINVAL: Invalid placement flags. + * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. + */ +int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, struct ttm_operation_ctx *ctx, + struct sg_table *sg, struct dma_resv *resv, void (*destroy) (struct ttm_buffer_object *)) { static const struct ttm_place sys_mem = { .mem_type = TTM_PL_SYSTEM }; - bool locked; int ret; - bo->destroy = destroy; kref_init(&bo->kref); INIT_LIST_HEAD(&bo->ddestroy); bo->bdev = bdev; bo->type = type; - bo->page_alignment = page_alignment; + bo->page_alignment = alignment; + bo->destroy = destroy; bo->pin_count = 0; bo->sg = sg; bo->bulk_move = NULL; - if (resv) { + if (resv) bo->base.resv = resv; - dma_resv_assert_held(bo->base.resv); - } else { + else bo->base.resv = &bo->base._resv; - } atomic_inc(&ttm_glob.bo_count); ret = ttm_resource_alloc(bo, &sys_mem, &bo->resource); @@ -957,50 +982,84 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, * For ttm_bo_type_device buffers, allocate * address space from the device. */ - if (bo->type == ttm_bo_type_device || - bo->type == ttm_bo_type_sg) + if (bo->type == ttm_bo_type_device || bo->type == ttm_bo_type_sg) { ret = drm_vma_offset_add(bdev->vma_manager, &bo->base.vma_node, - bo->resource->num_pages); + PFN_UP(bo->base.size)); + if (ret) + goto err_put; + } /* passed reservation objects should already be locked, * since otherwise lockdep will be angered in radeon. */ - if (!resv) { - locked = dma_resv_trylock(bo->base.resv); - WARN_ON(!locked); - } + if (!resv) + WARN_ON(!dma_resv_trylock(bo->base.resv)); + else + dma_resv_assert_held(resv); - if (likely(!ret)) - ret = ttm_bo_validate(bo, placement, ctx); + ret = ttm_bo_validate(bo, placement, ctx); + if (unlikely(ret)) + goto err_unlock; - if (unlikely(ret)) { - if (!resv) - ttm_bo_unreserve(bo); + return 0; - ttm_bo_put(bo); - return ret; - } +err_unlock: + if (!resv) + dma_resv_unlock(bo->base.resv); +err_put: + ttm_bo_put(bo); return ret; } EXPORT_SYMBOL(ttm_bo_init_reserved); -int ttm_bo_init(struct ttm_device *bdev, - struct ttm_buffer_object *bo, - size_t size, - enum ttm_bo_type type, - struct ttm_placement *placement, - uint32_t page_alignment, - bool interruptible, - struct sg_table *sg, - struct dma_resv *resv, - void (*destroy) (struct ttm_buffer_object *)) +/** + * ttm_bo_init_validate + * + * @bdev: Pointer to a ttm_device struct. + * @bo: Pointer to a ttm_buffer_object to be initialized. + * @type: Requested type of buffer object. + * @placement: Initial placement for buffer object. + * @alignment: Data alignment in pages. + * @interruptible: If needing to sleep to wait for GPU resources, + * sleep interruptible. + * pinned in physical memory. If this behaviour is not desired, this member + * holds a pointer to a persistent shmem object. Typically, this would + * point to the shmem object backing a GEM object if TTM is used to back a + * GEM user interface. + * @sg: Scatter-gather table. + * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. + * @destroy: Destroy function. Use NULL for kfree(). + * + * This function initializes a pre-allocated struct ttm_buffer_object. + * As this object may be part of a larger structure, this function, + * together with the @destroy function, + * enables driver-specific objects derived from a ttm_buffer_object. + * + * On successful return, the caller owns an object kref to @bo. The kref and + * list_kref are usually set to 1, but note that in some situations, other + * tasks may already be holding references to @bo as well. + * + * If a failure occurs, the function will call the @destroy function, Thus, + * after a failure, dereferencing @bo is illegal and will likely cause memory + * corruption. + * + * Returns + * -ENOMEM: Out of memory. + * -EINVAL: Invalid placement flags. + * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. + */ +int ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, bool interruptible, + struct sg_table *sg, struct dma_resv *resv, + void (*destroy) (struct ttm_buffer_object *)) { struct ttm_operation_ctx ctx = { interruptible, false }; int ret; - ret = ttm_bo_init_reserved(bdev, bo, size, type, placement, - page_alignment, &ctx, sg, resv, destroy); + ret = ttm_bo_init_reserved(bdev, bo, type, placement, alignment, &ctx, + sg, resv, destroy); if (ret) return ret; @@ -1009,7 +1068,7 @@ int ttm_bo_init(struct ttm_device *bdev, return 0; } -EXPORT_SYMBOL(ttm_bo_init); +EXPORT_SYMBOL(ttm_bo_init_validate); /* * buffer object vm functions. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c index 85a66014c2b6..eb2fd7694cd1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c @@ -429,9 +429,9 @@ int vmw_bo_create_kernel(struct vmw_private *dev_priv, unsigned long size, drm_gem_private_object_init(vdev, &bo->base, size); - ret = ttm_bo_init_reserved(&dev_priv->bdev, bo, size, - ttm_bo_type_kernel, placement, 0, - &ctx, NULL, NULL, vmw_bo_default_destroy); + ret = ttm_bo_init_reserved(&dev_priv->bdev, bo, ttm_bo_type_kernel, + placement, 0, &ctx, NULL, NULL, + vmw_bo_default_destroy); if (unlikely(ret)) goto error_free; @@ -512,10 +512,8 @@ int vmw_bo_init(struct vmw_private *dev_priv, size = ALIGN(size, PAGE_SIZE); drm_gem_private_object_init(vdev, &vmw_bo->base.base, size); - ret = ttm_bo_init_reserved(bdev, &vmw_bo->base, size, - ttm_bo_type_device, - placement, - 0, &ctx, NULL, NULL, bo_free); + ret = ttm_bo_init_reserved(bdev, &vmw_bo->base, ttm_bo_type_device, + placement, 0, &ctx, NULL, NULL, bo_free); if (unlikely(ret)) { return ret; } diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h index 2d524f8b0802..44a538ee5e2a 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo_api.h @@ -317,93 +317,16 @@ void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched); bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *place); -/** - * ttm_bo_init_reserved - * - * @bdev: Pointer to a ttm_device struct. - * @bo: Pointer to a ttm_buffer_object to be initialized. - * @size: Requested size of buffer object. - * @type: Requested type of buffer object. - * @placement: Initial placement for buffer object. - * @page_alignment: Data alignment in pages. - * @ctx: TTM operation context for memory allocation. - * @sg: Scatter-gather table. - * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. - * @destroy: Destroy function. Use NULL for kfree(). - * - * This function initializes a pre-allocated struct ttm_buffer_object. - * As this object may be part of a larger structure, this function, - * together with the @destroy function, - * enables driver-specific objects derived from a ttm_buffer_object. - * - * On successful return, the caller owns an object kref to @bo. The kref and - * list_kref are usually set to 1, but note that in some situations, other - * tasks may already be holding references to @bo as well. - * Furthermore, if resv == NULL, the buffer's reservation lock will be held, - * and it is the caller's responsibility to call ttm_bo_unreserve. - * - * If a failure occurs, the function will call the @destroy function, or - * kfree() if @destroy is NULL. Thus, after a failure, dereferencing @bo is - * illegal and will likely cause memory corruption. - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid placement flags. - * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. - */ - -int ttm_bo_init_reserved(struct ttm_device *bdev, - struct ttm_buffer_object *bo, - size_t size, enum ttm_bo_type type, - struct ttm_placement *placement, - uint32_t page_alignment, - struct ttm_operation_ctx *ctx, +int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, struct ttm_operation_ctx *ctx, + struct sg_table *sg, struct dma_resv *resv, + void (*destroy) (struct ttm_buffer_object *)); +int ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, bool interruptible, struct sg_table *sg, struct dma_resv *resv, void (*destroy) (struct ttm_buffer_object *)); - -/** - * ttm_bo_init - * - * @bdev: Pointer to a ttm_device struct. - * @bo: Pointer to a ttm_buffer_object to be initialized. - * @size: Requested size of buffer object. - * @type: Requested type of buffer object. - * @placement: Initial placement for buffer object. - * @page_alignment: Data alignment in pages. - * @interruptible: If needing to sleep to wait for GPU resources, - * sleep interruptible. - * pinned in physical memory. If this behaviour is not desired, this member - * holds a pointer to a persistent shmem object. Typically, this would - * point to the shmem object backing a GEM object if TTM is used to back a - * GEM user interface. - * @sg: Scatter-gather table. - * @resv: Pointer to a dma_resv, or NULL to let ttm allocate one. - * @destroy: Destroy function. Use NULL for kfree(). - * - * This function initializes a pre-allocated struct ttm_buffer_object. - * As this object may be part of a larger structure, this function, - * together with the @destroy function, - * enables driver-specific objects derived from a ttm_buffer_object. - * - * On successful return, the caller owns an object kref to @bo. The kref and - * list_kref are usually set to 1, but note that in some situations, other - * tasks may already be holding references to @bo as well. - * - * If a failure occurs, the function will call the @destroy function, or - * kfree() if @destroy is NULL. Thus, after a failure, dereferencing @bo is - * illegal and will likely cause memory corruption. - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid placement flags. - * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources. - */ -int ttm_bo_init(struct ttm_device *bdev, struct ttm_buffer_object *bo, - size_t size, enum ttm_bo_type type, - struct ttm_placement *placement, - uint32_t page_alignment, bool interrubtible, - struct sg_table *sg, struct dma_resv *resv, - void (*destroy) (struct ttm_buffer_object *)); /** * ttm_kmap_obj_virtual -- cgit v1.2.3-59-g8ed1b From 63af82cf5e36b6ba3f3ebcdd1edd9f91934bfa59 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 17 Dec 2021 16:13:09 +0100 Subject: drm/amdgpu: audit bo->resource usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we can at least move and release BOs without backing store. Signed-off-by: Christian König Reviewed-by: Michael J. Ruhl Link: https://patchwork.freedesktop.org/patch/msgid/20220707102453.3633-3-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_object.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index d9cfe259f2a9..677d1dfab37f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -1305,7 +1305,7 @@ void amdgpu_bo_release_notify(struct ttm_buffer_object *bo) if (bo->base.resv == &bo->base._resv) amdgpu_amdkfd_remove_fence_on_pt_pd_bos(abo); - if (bo->resource->mem_type != TTM_PL_VRAM || + if (!bo->resource || bo->resource->mem_type != TTM_PL_VRAM || !(abo->flags & AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE) || adev->in_suspend || adev->shutdown) return; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index be6f76a30ac6..3bddf266e8b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -471,7 +471,8 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, adev = amdgpu_ttm_adev(bo->bdev); - if (old_mem->mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) { + if (!old_mem || (old_mem->mem_type == TTM_PL_SYSTEM && + bo->ttm == NULL)) { ttm_bo_move_null(bo, new_mem); goto out; } -- cgit v1.2.3-59-g8ed1b From 64e257f187a5c76ec5766f50204462c0c483e418 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 17 Dec 2021 16:30:16 +0100 Subject: drm/nouveau: audit bo->resource usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we can at least move and release BOs without backing store. Signed-off-by: Christian König Reviewed-by: Michael J. Ruhl Link: https://patchwork.freedesktop.org/patch/msgid/20220707102453.3633-4-christian.koenig@amd.com --- drivers/gpu/drm/nouveau/nouveau_bo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 994879d9db74..35bb0bb3fe61 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1008,7 +1008,8 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, } /* Fake bo copy. */ - if (old_reg->mem_type == TTM_PL_SYSTEM && !bo->ttm) { + if (!old_reg || (old_reg->mem_type == TTM_PL_SYSTEM && + !bo->ttm)) { ttm_bo_move_null(bo, new_reg); goto out; } -- cgit v1.2.3-59-g8ed1b From 4d8f68548e982e2c9b9ca3b47aea49c47da93a3d Mon Sep 17 00:00:00 2001 From: Christian König Date: Thu, 17 Feb 2022 10:43:16 +0100 Subject: drm/ttm: audit bo->resource usage v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow BOs to exist without backing store. v2: handle ttm_bo_move_memcpy as well. Signed-off-by: Christian König Reviewed-by: Michael J. Ruhl Link: https://patchwork.freedesktop.org/patch/msgid/20220707102453.3633-5-christian.koenig@amd.com --- drivers/gpu/drm/ttm/ttm_bo.c | 16 ++++++++-------- drivers/gpu/drm/ttm/ttm_bo_util.c | 7 +++++-- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index dbd331939e6d..698918a15721 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -117,12 +117,13 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, struct ttm_place *hop) { - struct ttm_resource_manager *old_man, *new_man; struct ttm_device *bdev = bo->bdev; + bool old_use_tt, new_use_tt; int ret; - old_man = ttm_manager_type(bdev, bo->resource->mem_type); - new_man = ttm_manager_type(bdev, mem->mem_type); + old_use_tt = bo->resource && + ttm_manager_type(bdev, bo->resource->mem_type)->use_tt; + new_use_tt = ttm_manager_type(bdev, mem->mem_type)->use_tt; ttm_bo_unmap_virtual(bo); @@ -130,11 +131,11 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, * Create and bind a ttm if required. */ - if (new_man->use_tt) { + if (new_use_tt) { /* Zero init the new TTM structure if the old location should * have used one as well. */ - ret = ttm_tt_create(bo, old_man->use_tt); + ret = ttm_tt_create(bo, old_use_tt); if (ret) goto out_err; @@ -160,8 +161,7 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, return 0; out_err: - new_man = ttm_manager_type(bdev, bo->resource->mem_type); - if (!new_man->use_tt) + if (!old_use_tt) ttm_bo_tt_destroy(bo); return ret; @@ -898,7 +898,7 @@ int ttm_bo_validate(struct ttm_buffer_object *bo, /* * Check whether we need to move buffer. */ - if (!ttm_resource_compat(bo->resource, placement)) { + if (!bo->resource || !ttm_resource_compat(bo->resource, placement)) { ret = ttm_bo_move_buffer(bo, placement, ctx); if (ret) return ret; diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 1cbfb00c1d65..1530982338e9 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -137,8 +137,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, ttm_manager_type(bo->bdev, dst_mem->mem_type); struct ttm_tt *ttm = bo->ttm; struct ttm_resource *src_mem = bo->resource; - struct ttm_resource_manager *src_man = - ttm_manager_type(bdev, src_mem->mem_type); + struct ttm_resource_manager *src_man; union { struct ttm_kmap_iter_tt tt; struct ttm_kmap_iter_linear_io io; @@ -147,6 +146,10 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, bool clear; int ret = 0; + if (!src_mem) + return 0; + + src_man = ttm_manager_type(bdev, src_mem->mem_type); if (ttm && ((ttm->page_flags & TTM_TT_FLAG_SWAPPED) || dst_man->use_tt)) { ret = ttm_tt_populate(bdev, ttm, ctx); -- cgit v1.2.3-59-g8ed1b From ba8f16cd081902350e2d2a00afcc6b77b1815d50 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:44 -0300 Subject: drm: selftest: convert drm_damage_helper selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM damage helper selftest to the KUnit API. Co-developed-by: Arthur Grillo Signed-off-by: Arthur Grillo Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-2-maira.canal@usp.br --- drivers/gpu/drm/Kconfig | 8 +- drivers/gpu/drm/selftests/Makefile | 3 +- drivers/gpu/drm/selftests/drm_modeset_selftests.h | 21 - drivers/gpu/drm/selftests/test-drm_damage_helper.c | 668 --------------------- .../gpu/drm/selftests/test-drm_modeset_common.h | 21 - drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_damage_helper_test.c | 634 +++++++++++++++++++ 7 files changed, 643 insertions(+), 714 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/test-drm_damage_helper.c create mode 100644 drivers/gpu/drm/tests/drm_damage_helper_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 6c2256e8474b..06822ecf51c6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -72,8 +72,14 @@ config DRM_DEBUG_SELFTEST config DRM_KUNIT_TEST tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS - depends on DRM && KUNIT=y + depends on DRM && KUNIT + select PRIME_NUMBERS + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER + select DRM_LIB_RANDOM select DRM_KMS_HELPER + select DRM_BUDDY + select DRM_EXPORT_FOR_TESTS if m default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 5ba5f9138c95..7a1a732e0a1b 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,8 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ test-drm_format.o test-drm_framebuffer.o \ - test-drm_damage_helper.o test-drm_dp_mst_helper.o \ - test-drm_rect.o + test-drm_dp_mst_helper.o test-drm_rect.o obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o \ test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index 782e285ca383..4787b3b70709 100644 --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h @@ -15,26 +15,5 @@ selftest(check_drm_format_block_width, igt_check_drm_format_block_width) selftest(check_drm_format_block_height, igt_check_drm_format_block_height) selftest(check_drm_format_min_pitch, igt_check_drm_format_min_pitch) selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create) -selftest(damage_iter_no_damage, igt_damage_iter_no_damage) -selftest(damage_iter_no_damage_fractional_src, igt_damage_iter_no_damage_fractional_src) -selftest(damage_iter_no_damage_src_moved, igt_damage_iter_no_damage_src_moved) -selftest(damage_iter_no_damage_fractional_src_moved, igt_damage_iter_no_damage_fractional_src_moved) -selftest(damage_iter_no_damage_not_visible, igt_damage_iter_no_damage_not_visible) -selftest(damage_iter_no_damage_no_crtc, igt_damage_iter_no_damage_no_crtc) -selftest(damage_iter_no_damage_no_fb, igt_damage_iter_no_damage_no_fb) -selftest(damage_iter_simple_damage, igt_damage_iter_simple_damage) -selftest(damage_iter_single_damage, igt_damage_iter_single_damage) -selftest(damage_iter_single_damage_intersect_src, igt_damage_iter_single_damage_intersect_src) -selftest(damage_iter_single_damage_outside_src, igt_damage_iter_single_damage_outside_src) -selftest(damage_iter_single_damage_fractional_src, igt_damage_iter_single_damage_fractional_src) -selftest(damage_iter_single_damage_intersect_fractional_src, igt_damage_iter_single_damage_intersect_fractional_src) -selftest(damage_iter_single_damage_outside_fractional_src, igt_damage_iter_single_damage_outside_fractional_src) -selftest(damage_iter_single_damage_src_moved, igt_damage_iter_single_damage_src_moved) -selftest(damage_iter_single_damage_fractional_src_moved, igt_damage_iter_single_damage_fractional_src_moved) -selftest(damage_iter_damage, igt_damage_iter_damage) -selftest(damage_iter_damage_one_intersect, igt_damage_iter_damage_one_intersect) -selftest(damage_iter_damage_one_outside, igt_damage_iter_damage_one_outside) -selftest(damage_iter_damage_src_moved, igt_damage_iter_damage_src_moved) -selftest(damage_iter_damage_not_visible, igt_damage_iter_damage_not_visible) selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode) selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode) diff --git a/drivers/gpu/drm/selftests/test-drm_damage_helper.c b/drivers/gpu/drm/selftests/test-drm_damage_helper.c deleted file mode 100644 index 816e1464a98f..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_damage_helper.c +++ /dev/null @@ -1,668 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Test case for drm_damage_helper functions - */ - -#define pr_fmt(fmt) "drm_damage_helper: " fmt - -#include -#include -#include -#include - -#include "test-drm_modeset_common.h" - -struct drm_driver mock_driver; -static struct drm_device mock_device; -static struct drm_object_properties mock_obj_props; -static struct drm_plane mock_plane; -static struct drm_property mock_prop; - -static void mock_setup(struct drm_plane_state *state) -{ - static bool setup_done = false; - - state->plane = &mock_plane; - - if (setup_done) - return; - - /* just enough so that drm_plane_enable_fb_damage_clips() works */ - mock_device.driver = &mock_driver; - mock_device.mode_config.prop_fb_damage_clips = &mock_prop; - mock_plane.dev = &mock_device; - mock_obj_props.count = 0; - mock_plane.base.properties = &mock_obj_props; - mock_prop.base.id = 1; /* 0 is an invalid id */ - mock_prop.dev = &mock_device; - - drm_plane_enable_fb_damage_clips(&mock_plane); -} - -static void set_plane_src(struct drm_plane_state *state, int x1, int y1, int x2, - int y2) -{ - state->src.x1 = x1; - state->src.y1 = y1; - state->src.x2 = x2; - state->src.y2 = y2; -} - -static void set_damage_clip(struct drm_mode_rect *r, int x1, int y1, int x2, - int y2) -{ - r->x1 = x1; - r->y1 = y1; - r->x2 = x2; - r->y2 = y2; -} - -static void set_damage_blob(struct drm_property_blob *damage_blob, - struct drm_mode_rect *r, uint32_t size) -{ - damage_blob->length = size; - damage_blob->data = r; -} - -static void set_plane_damage(struct drm_plane_state *state, - struct drm_property_blob *damage_blob) -{ - state->fb_damage_clips = damage_blob; -} - -static bool check_damage_clip(struct drm_plane_state *state, struct drm_rect *r, - int x1, int y1, int x2, int y2) -{ - /* - * Round down x1/y1 and round up x2/y2. This is because damage is not in - * 16.16 fixed point so to catch all pixels. - */ - int src_x1 = state->src.x1 >> 16; - int src_y1 = state->src.y1 >> 16; - int src_x2 = (state->src.x2 >> 16) + !!(state->src.x2 & 0xFFFF); - int src_y2 = (state->src.y2 >> 16) + !!(state->src.y2 & 0xFFFF); - - if (x1 >= x2 || y1 >= y2) { - pr_err("Cannot have damage clip with no dimension.\n"); - return false; - } - - if (x1 < src_x1 || y1 < src_y1 || x2 > src_x2 || y2 > src_y2) { - pr_err("Damage cannot be outside rounded plane src.\n"); - return false; - } - - if (r->x1 != x1 || r->y1 != y1 || r->x2 != x2 || r->y2 != y2) { - pr_err("Damage = %d %d %d %d\n", r->x1, r->y1, r->x2, r->y2); - return false; - } - - return true; -} - -const struct drm_framebuffer fb = { - .width = 2048, - .height = 2048 -}; - -/* common mocked structs many tests need */ -#define MOCK_VARIABLES() \ - struct drm_plane_state old_state; \ - struct drm_plane_state state = { \ - .crtc = ZERO_SIZE_PTR, \ - .fb = (struct drm_framebuffer *) &fb, \ - .visible = true, \ - }; \ - mock_setup(&old_state); \ - mock_setup(&state); - -int igt_damage_iter_no_damage(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src same as fb size. */ - set_plane_src(&old_state, 0, 0, fb.width << 16, fb.height << 16); - set_plane_src(&state, 0, 0, fb.width << 16, fb.height << 16); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 0, 0, 2048, 2048)); - - return 0; -} - -int igt_damage_iter_no_damage_fractional_src(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src has fractional part. */ - set_plane_src(&old_state, 0x3fffe, 0x3fffe, - 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); - set_plane_src(&state, 0x3fffe, 0x3fffe, - 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return rounded off plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 3, 3, 1028, 772)); - - return 0; -} - -int igt_damage_iter_no_damage_src_moved(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src moved since old plane state. */ - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 10 << 16, 10 << 16, - (10 + 1024) << 16, (10 + 768) << 16); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 1034, 778)); - - return 0; -} - -int igt_damage_iter_no_damage_fractional_src_moved(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src has fractional part and it moved since old plane state. */ - set_plane_src(&old_state, 0x3fffe, 0x3fffe, - 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); - set_plane_src(&state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773)); - - return 0; -} - -int igt_damage_iter_no_damage_not_visible(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - state.visible = false; - - mock_setup(&old_state); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 0, "Should have no damage."); - - return 0; -} - -int igt_damage_iter_no_damage_no_crtc(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - state.crtc = NULL; - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 0, "Should have no damage."); - - return 0; -} - -int igt_damage_iter_no_damage_no_fb(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_plane_state old_state; - struct drm_rect clip; - uint32_t num_hits = 0; - - struct drm_plane_state state = { - .crtc = ZERO_SIZE_PTR, - .fb = 0, - }; - - mock_setup(&old_state); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 0, "Should have no damage."); - - return 0; -} - -int igt_damage_iter_simple_damage(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - /* Damage set to plane src */ - set_damage_clip(&damage, 0, 0, 1024, 768); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return damage when set."); - FAIL_ON(!check_damage_clip(&state, &clip, 0, 0, 1024, 768)); - - return 0; -} - -int igt_damage_iter_single_damage(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - set_damage_clip(&damage, 256, 192, 768, 576); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return damage when set."); - FAIL_ON(!check_damage_clip(&state, &clip, 256, 192, 768, 576)); - - return 0; -} - -int igt_damage_iter_single_damage_intersect_src(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - /* Damage intersect with plane src. */ - set_damage_clip(&damage, 256, 192, 1360, 768); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return damage clipped to src."); - FAIL_ON(!check_damage_clip(&state, &clip, 256, 192, 1024, 768)); - - return 0; -} - -int igt_damage_iter_single_damage_outside_src(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - /* Damage clip outside plane src */ - set_damage_clip(&damage, 1360, 1360, 1380, 1380); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 0, "Should have no damage."); - - return 0; -} - -int igt_damage_iter_single_damage_fractional_src(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src has fractional part. */ - set_plane_src(&old_state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_plane_src(&state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_damage_clip(&damage, 10, 10, 256, 330); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return damage when set."); - FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 256, 330)); - - return 0; -} - -int igt_damage_iter_single_damage_intersect_fractional_src(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src has fractional part. */ - set_plane_src(&old_state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_plane_src(&state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - /* Damage intersect with plane src. */ - set_damage_clip(&damage, 10, 1, 1360, 330); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return damage clipped to rounded off src."); - FAIL_ON(!check_damage_clip(&state, &clip, 10, 4, 1029, 330)); - - return 0; -} - -int igt_damage_iter_single_damage_outside_fractional_src(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src has fractional part. */ - set_plane_src(&old_state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_plane_src(&state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - /* Damage clip outside plane src */ - set_damage_clip(&damage, 1360, 1360, 1380, 1380); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 0, "Should have no damage."); - - return 0; -} - -int igt_damage_iter_single_damage_src_moved(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src moved since old plane state. */ - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 10 << 16, 10 << 16, - (10 + 1024) << 16, (10 + 768) << 16); - set_damage_clip(&damage, 20, 30, 256, 256); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 10, 10, 1034, 778)); - - return 0; -} - -int igt_damage_iter_single_damage_fractional_src_moved(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - /* Plane src with fractional part moved since old plane state. */ - set_plane_src(&old_state, 0x3fffe, 0x3fffe, - 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); - set_plane_src(&state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - /* Damage intersect with plane src. */ - set_damage_clip(&damage, 20, 30, 1360, 256); - set_damage_blob(&damage_blob, &damage, sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return rounded off plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773)); - - return 0; -} - -int igt_damage_iter_damage(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage[2]; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - /* 2 damage clips. */ - set_damage_clip(&damage[0], 20, 30, 200, 180); - set_damage_clip(&damage[1], 240, 200, 280, 250); - set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) { - if (num_hits == 0) - FAIL_ON(!check_damage_clip(&state, &clip, 20, 30, 200, 180)); - if (num_hits == 1) - FAIL_ON(!check_damage_clip(&state, &clip, 240, 200, 280, 250)); - num_hits++; - } - - FAIL(num_hits != 2, "Should return damage when set."); - - return 0; -} - -int igt_damage_iter_damage_one_intersect(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage[2]; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_plane_src(&state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - /* 2 damage clips, one intersect plane src. */ - set_damage_clip(&damage[0], 20, 30, 200, 180); - set_damage_clip(&damage[1], 2, 2, 1360, 1360); - set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) { - if (num_hits == 0) - FAIL_ON(!check_damage_clip(&state, &clip, 20, 30, 200, 180)); - if (num_hits == 1) - FAIL_ON(!check_damage_clip(&state, &clip, 4, 4, 1029, 773)); - num_hits++; - } - - FAIL(num_hits != 2, "Should return damage when set."); - - return 0; -} - -int igt_damage_iter_damage_one_outside(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage[2]; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0, 0, 1024 << 16, 768 << 16); - set_plane_src(&state, 0, 0, 1024 << 16, 768 << 16); - /* 2 damage clips, one outside plane src. */ - set_damage_clip(&damage[0], 1360, 1360, 1380, 1380); - set_damage_clip(&damage[1], 240, 200, 280, 250); - set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return damage when set."); - FAIL_ON(!check_damage_clip(&state, &clip, 240, 200, 280, 250)); - - return 0; -} - -int igt_damage_iter_damage_src_moved(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage[2]; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - set_plane_src(&old_state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_plane_src(&state, 0x3fffe, 0x3fffe, - 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); - /* 2 damage clips, one outside plane src. */ - set_damage_clip(&damage[0], 1360, 1360, 1380, 1380); - set_damage_clip(&damage[1], 240, 200, 280, 250); - set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 1, "Should return round off plane src as damage."); - FAIL_ON(!check_damage_clip(&state, &clip, 3, 3, 1028, 772)); - - return 0; -} - -int igt_damage_iter_damage_not_visible(void *ignored) -{ - struct drm_atomic_helper_damage_iter iter; - struct drm_property_blob damage_blob; - struct drm_mode_rect damage[2]; - struct drm_rect clip; - uint32_t num_hits = 0; - - MOCK_VARIABLES(); - - state.visible = false; - - set_plane_src(&old_state, 0x40002, 0x40002, - 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); - set_plane_src(&state, 0x3fffe, 0x3fffe, - 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); - /* 2 damage clips, one outside plane src. */ - set_damage_clip(&damage[0], 1360, 1360, 1380, 1380); - set_damage_clip(&damage[1], 240, 200, 280, 250); - set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); - set_plane_damage(&state, &damage_blob); - drm_atomic_helper_damage_iter_init(&iter, &old_state, &state); - drm_atomic_for_each_plane_damage(&iter, &clip) - num_hits++; - - FAIL(num_hits != 0, "Should not return any damage."); - - return 0; -} diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h index cfb51d8da2bc..c29354e59cec 100644 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -25,27 +25,6 @@ int igt_check_drm_format_block_width(void *ignored); int igt_check_drm_format_block_height(void *ignored); int igt_check_drm_format_min_pitch(void *ignored); int igt_check_drm_framebuffer_create(void *ignored); -int igt_damage_iter_no_damage(void *ignored); -int igt_damage_iter_no_damage_fractional_src(void *ignored); -int igt_damage_iter_no_damage_src_moved(void *ignored); -int igt_damage_iter_no_damage_fractional_src_moved(void *ignored); -int igt_damage_iter_no_damage_not_visible(void *ignored); -int igt_damage_iter_no_damage_no_crtc(void *ignored); -int igt_damage_iter_no_damage_no_fb(void *ignored); -int igt_damage_iter_simple_damage(void *ignored); -int igt_damage_iter_single_damage(void *ignored); -int igt_damage_iter_single_damage_intersect_src(void *ignored); -int igt_damage_iter_single_damage_outside_src(void *ignored); -int igt_damage_iter_single_damage_fractional_src(void *ignored); -int igt_damage_iter_single_damage_intersect_fractional_src(void *ignored); -int igt_damage_iter_single_damage_outside_fractional_src(void *ignored); -int igt_damage_iter_single_damage_src_moved(void *ignored); -int igt_damage_iter_single_damage_fractional_src_moved(void *ignored); -int igt_damage_iter_damage(void *ignored); -int igt_damage_iter_damage_one_intersect(void *ignored); -int igt_damage_iter_damage_one_outside(void *ignored); -int igt_damage_iter_damage_src_moved(void *ignored); -int igt_damage_iter_damage_not_visible(void *ignored); int igt_dp_mst_calc_pbn_mode(void *ignored); int igt_dp_mst_sideband_msg_req_decode(void *ignored); diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 2c8273796d9d..2f399e03d50c 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o +obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o diff --git a/drivers/gpu/drm/tests/drm_damage_helper_test.c b/drivers/gpu/drm/tests/drm_damage_helper_test.c new file mode 100644 index 000000000000..bf250bd08d7e --- /dev/null +++ b/drivers/gpu/drm/tests/drm_damage_helper_test.c @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test case for drm_damage_helper functions + * + * Copyright (c) 2022 Maíra Canal + */ + +#include + +#include +#include +#include +#include + +struct drm_damage_mock { + struct drm_driver driver; + struct drm_device device; + struct drm_object_properties obj_props; + struct drm_plane plane; + struct drm_property prop; + struct drm_framebuffer fb; + struct drm_plane_state state; + struct drm_plane_state old_state; +}; + +static int drm_damage_helper_init(struct kunit *test) +{ + struct drm_damage_mock *mock; + + mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mock); + + mock->fb.width = 2048; + mock->fb.height = 2048; + + mock->state.crtc = ZERO_SIZE_PTR; + mock->state.fb = &mock->fb; + mock->state.visible = true; + + mock->old_state.plane = &mock->plane; + mock->state.plane = &mock->plane; + + /* just enough so that drm_plane_enable_fb_damage_clips() works */ + mock->device.driver = &mock->driver; + mock->device.mode_config.prop_fb_damage_clips = &mock->prop; + mock->plane.dev = &mock->device; + mock->obj_props.count = 0; + mock->plane.base.properties = &mock->obj_props; + mock->prop.base.id = 1; /* 0 is an invalid id */ + mock->prop.dev = &mock->device; + + drm_plane_enable_fb_damage_clips(&mock->plane); + + test->priv = mock; + + return 0; +} + +static void set_plane_src(struct drm_plane_state *state, int x1, int y1, int x2, + int y2) +{ + state->src.x1 = x1; + state->src.y1 = y1; + state->src.x2 = x2; + state->src.y2 = y2; +} + +static void set_damage_clip(struct drm_mode_rect *r, int x1, int y1, int x2, + int y2) +{ + r->x1 = x1; + r->y1 = y1; + r->x2 = x2; + r->y2 = y2; +} + +static void set_damage_blob(struct drm_property_blob *damage_blob, + struct drm_mode_rect *r, u32 size) +{ + damage_blob->length = size; + damage_blob->data = r; +} + +static void set_plane_damage(struct drm_plane_state *state, + struct drm_property_blob *damage_blob) +{ + state->fb_damage_clips = damage_blob; +} + +static void check_damage_clip(struct kunit *test, struct drm_rect *r, + int x1, int y1, int x2, int y2) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_plane_state state = mock->state; + + /* + * Round down x1/y1 and round up x2/y2. This is because damage is not in + * 16.16 fixed point so to catch all pixels. + */ + int src_x1 = state.src.x1 >> 16; + int src_y1 = state.src.y1 >> 16; + int src_x2 = (state.src.x2 >> 16) + !!(state.src.x2 & 0xFFFF); + int src_y2 = (state.src.y2 >> 16) + !!(state.src.y2 & 0xFFFF); + + if (x1 >= x2 || y1 >= y2) + KUNIT_FAIL(test, "Cannot have damage clip with no dimension."); + if (x1 < src_x1 || y1 < src_y1 || x2 > src_x2 || y2 > src_y2) + KUNIT_FAIL(test, "Damage cannot be outside rounded plane src."); + if (r->x1 != x1 || r->y1 != y1 || r->x2 != x2 || r->y2 != y2) + KUNIT_FAIL(test, "Damage = %d %d %d %d, want = %d %d %d %d", + r->x1, r->y1, r->x2, r->y2, x1, y1, x2, y2); +} + +static void igt_damage_iter_no_damage(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src same as fb size. */ + set_plane_src(&mock->old_state, 0, 0, mock->fb.width << 16, mock->fb.height << 16); + set_plane_src(&mock->state, 0, 0, mock->fb.width << 16, mock->fb.height << 16); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage."); + check_damage_clip(test, &clip, 0, 0, 2048, 2048); +} + +static void igt_damage_iter_no_damage_fractional_src(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src has fractional part. */ + set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe, + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); + set_plane_src(&mock->state, 0x3fffe, 0x3fffe, + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, + "Should return rounded off plane src as damage."); + check_damage_clip(test, &clip, 3, 3, 1028, 772); +} + +static void igt_damage_iter_no_damage_src_moved(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src moved since old plane state. */ + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 10 << 16, 10 << 16, + (10 + 1024) << 16, (10 + 768) << 16); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage."); + check_damage_clip(test, &clip, 10, 10, 1034, 778); +} + +static void igt_damage_iter_no_damage_fractional_src_moved(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src has fractional part and it moved since old plane state. */ + set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe, + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); + set_plane_src(&mock->state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return plane src as damage."); + check_damage_clip(test, &clip, 4, 4, 1029, 773); +} + +static void igt_damage_iter_no_damage_not_visible(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + mock->state.visible = false; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage."); +} + +static void igt_damage_iter_no_damage_no_crtc(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + mock->state.crtc = NULL; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage."); +} + +static void igt_damage_iter_no_damage_no_fb(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_rect clip; + u32 num_hits = 0; + + mock->state.fb = NULL; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage."); +} + +static void igt_damage_iter_simple_damage(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + /* Damage set to plane src */ + set_damage_clip(&damage, 0, 0, 1024, 768); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set."); + check_damage_clip(test, &clip, 0, 0, 1024, 768); +} + +static void igt_damage_iter_single_damage(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + set_damage_clip(&damage, 256, 192, 768, 576); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set."); + check_damage_clip(test, &clip, 256, 192, 768, 576); +} + +static void igt_damage_iter_single_damage_intersect_src(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + /* Damage intersect with plane src. */ + set_damage_clip(&damage, 256, 192, 1360, 768); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage clipped to src."); + check_damage_clip(test, &clip, 256, 192, 1024, 768); +} + +static void igt_damage_iter_single_damage_outside_src(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + /* Damage clip outside plane src */ + set_damage_clip(&damage, 1360, 1360, 1380, 1380); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage."); +} + +static void igt_damage_iter_single_damage_fractional_src(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src has fractional part. */ + set_plane_src(&mock->old_state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_plane_src(&mock->state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_damage_clip(&damage, 10, 10, 256, 330); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set."); + check_damage_clip(test, &clip, 10, 10, 256, 330); +} + +static void igt_damage_iter_single_damage_intersect_fractional_src(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src has fractional part. */ + set_plane_src(&mock->old_state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_plane_src(&mock->state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + /* Damage intersect with plane src. */ + set_damage_clip(&damage, 10, 1, 1360, 330); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, + "Should return damage clipped to rounded off src."); + check_damage_clip(test, &clip, 10, 4, 1029, 330); +} + +static void igt_damage_iter_single_damage_outside_fractional_src(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src has fractional part. */ + set_plane_src(&mock->old_state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_plane_src(&mock->state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + /* Damage clip outside plane src */ + set_damage_clip(&damage, 1360, 1360, 1380, 1380); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should have no damage."); +} + +static void igt_damage_iter_single_damage_src_moved(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src moved since old plane state. */ + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 10 << 16, 10 << 16, + (10 + 1024) << 16, (10 + 768) << 16); + set_damage_clip(&damage, 20, 30, 256, 256); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, + "Should return plane src as damage."); + check_damage_clip(test, &clip, 10, 10, 1034, 778); +} + +static void igt_damage_iter_single_damage_fractional_src_moved(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage; + struct drm_rect clip; + u32 num_hits = 0; + + /* Plane src with fractional part moved since old plane state. */ + set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe, + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); + set_plane_src(&mock->state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + /* Damage intersect with plane src. */ + set_damage_clip(&damage, 20, 30, 1360, 256); + set_damage_blob(&damage_blob, &damage, sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, + "Should return rounded off plane as damage."); + check_damage_clip(test, &clip, 4, 4, 1029, 773); +} + +static void igt_damage_iter_damage(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage[2]; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + /* 2 damage clips. */ + set_damage_clip(&damage[0], 20, 30, 200, 180); + set_damage_clip(&damage[1], 240, 200, 280, 250); + set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) { + if (num_hits == 0) + check_damage_clip(test, &clip, 20, 30, 200, 180); + if (num_hits == 1) + check_damage_clip(test, &clip, 240, 200, 280, 250); + num_hits++; + } + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 2, "Should return damage when set."); +} + +static void igt_damage_iter_damage_one_intersect(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage[2]; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_plane_src(&mock->state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + /* 2 damage clips, one intersect plane src. */ + set_damage_clip(&damage[0], 20, 30, 200, 180); + set_damage_clip(&damage[1], 2, 2, 1360, 1360); + set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) { + if (num_hits == 0) + check_damage_clip(test, &clip, 20, 30, 200, 180); + if (num_hits == 1) + check_damage_clip(test, &clip, 4, 4, 1029, 773); + num_hits++; + } + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 2, "Should return damage when set."); +} + +static void igt_damage_iter_damage_one_outside(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage[2]; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16); + set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16); + /* 2 damage clips, one outside plane src. */ + set_damage_clip(&damage[0], 1360, 1360, 1380, 1380); + set_damage_clip(&damage[1], 240, 200, 280, 250); + set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return damage when set."); + check_damage_clip(test, &clip, 240, 200, 280, 250); +} + +static void igt_damage_iter_damage_src_moved(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage[2]; + struct drm_rect clip; + u32 num_hits = 0; + + set_plane_src(&mock->old_state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_plane_src(&mock->state, 0x3fffe, 0x3fffe, + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); + /* 2 damage clips, one outside plane src. */ + set_damage_clip(&damage[0], 1360, 1360, 1380, 1380); + set_damage_clip(&damage[1], 240, 200, 280, 250); + set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, + "Should return round off plane src as damage."); + check_damage_clip(test, &clip, 3, 3, 1028, 772); +} + +static void igt_damage_iter_damage_not_visible(struct kunit *test) +{ + struct drm_damage_mock *mock = test->priv; + struct drm_atomic_helper_damage_iter iter; + struct drm_property_blob damage_blob; + struct drm_mode_rect damage[2]; + struct drm_rect clip; + u32 num_hits = 0; + + mock->state.visible = false; + + set_plane_src(&mock->old_state, 0x40002, 0x40002, + 0x40002 + (1024 << 16), 0x40002 + (768 << 16)); + set_plane_src(&mock->state, 0x3fffe, 0x3fffe, + 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16)); + /* 2 damage clips, one outside plane src. */ + set_damage_clip(&damage[0], 1360, 1360, 1380, 1380); + set_damage_clip(&damage[1], 240, 200, 280, 250); + set_damage_blob(&damage_blob, &damage[0], sizeof(damage)); + set_plane_damage(&mock->state, &damage_blob); + drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state); + drm_atomic_for_each_plane_damage(&iter, &clip) + num_hits++; + + KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should not return any damage."); +} + +static struct kunit_case drm_damage_helper_tests[] = { + KUNIT_CASE(igt_damage_iter_no_damage), + KUNIT_CASE(igt_damage_iter_no_damage_fractional_src), + KUNIT_CASE(igt_damage_iter_no_damage_src_moved), + KUNIT_CASE(igt_damage_iter_no_damage_fractional_src_moved), + KUNIT_CASE(igt_damage_iter_no_damage_not_visible), + KUNIT_CASE(igt_damage_iter_no_damage_no_crtc), + KUNIT_CASE(igt_damage_iter_no_damage_no_fb), + KUNIT_CASE(igt_damage_iter_simple_damage), + KUNIT_CASE(igt_damage_iter_single_damage), + KUNIT_CASE(igt_damage_iter_single_damage_intersect_src), + KUNIT_CASE(igt_damage_iter_single_damage_outside_src), + KUNIT_CASE(igt_damage_iter_single_damage_fractional_src), + KUNIT_CASE(igt_damage_iter_single_damage_intersect_fractional_src), + KUNIT_CASE(igt_damage_iter_single_damage_outside_fractional_src), + KUNIT_CASE(igt_damage_iter_single_damage_src_moved), + KUNIT_CASE(igt_damage_iter_single_damage_fractional_src_moved), + KUNIT_CASE(igt_damage_iter_damage), + KUNIT_CASE(igt_damage_iter_damage_one_intersect), + KUNIT_CASE(igt_damage_iter_damage_one_outside), + KUNIT_CASE(igt_damage_iter_damage_src_moved), + KUNIT_CASE(igt_damage_iter_damage_not_visible), + { } +}; + +static struct kunit_suite drm_damage_helper_test_suite = { + .name = "drm_damage_helper", + .init = drm_damage_helper_init, + .test_cases = drm_damage_helper_tests, +}; + +kunit_test_suite(drm_damage_helper_test_suite); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 9f0527d22a52ff3470d8bf0dc79cf42c0677c23c Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:45 -0300 Subject: drm: selftest: convert drm_cmdline_parser selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM cmdline parser selftest to the KUnit API. Co-developed-by: Arthur Grillo Signed-off-by: Arthur Grillo Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-3-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 2 +- drivers/gpu/drm/selftests/drm_cmdline_selftests.h | 68 -- .../gpu/drm/selftests/test-drm_cmdline_parser.c | 1141 -------------------- drivers/gpu/drm/tests/Makefile | 3 +- drivers/gpu/drm/tests/drm_cmdline_parser_test.c | 1078 ++++++++++++++++++ 5 files changed, 1081 insertions(+), 1211 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/drm_cmdline_selftests.h delete mode 100644 drivers/gpu/drm/selftests/test-drm_cmdline_parser.c create mode 100644 drivers/gpu/drm/tests/drm_cmdline_parser_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 7a1a732e0a1b..8633bb9ea717 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -3,5 +3,5 @@ test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ test-drm_format.o test-drm_framebuffer.o \ test-drm_dp_mst_helper.o test-drm_rect.o -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o test-drm_cmdline_parser.o \ +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o \ test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h b/drivers/gpu/drm/selftests/drm_cmdline_selftests.h deleted file mode 100644 index 29e367db6118..000000000000 --- a/drivers/gpu/drm/selftests/drm_cmdline_selftests.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* List each unit test as selftest(function) - * - * The name is used as both an enum and expanded as igt__name to create - * a module parameter. It must be unique and legal for a C identifier. - * - * Tests are executed in order by igt/drm_mm - */ - -#define cmdline_test(test) selftest(test, test) - -cmdline_test(drm_cmdline_test_force_d_only) -cmdline_test(drm_cmdline_test_force_D_only_dvi) -cmdline_test(drm_cmdline_test_force_D_only_hdmi) -cmdline_test(drm_cmdline_test_force_D_only_not_digital) -cmdline_test(drm_cmdline_test_force_e_only) -cmdline_test(drm_cmdline_test_margin_only) -cmdline_test(drm_cmdline_test_interlace_only) -cmdline_test(drm_cmdline_test_res) -cmdline_test(drm_cmdline_test_res_missing_x) -cmdline_test(drm_cmdline_test_res_missing_y) -cmdline_test(drm_cmdline_test_res_bad_y) -cmdline_test(drm_cmdline_test_res_missing_y_bpp) -cmdline_test(drm_cmdline_test_res_vesa) -cmdline_test(drm_cmdline_test_res_vesa_rblank) -cmdline_test(drm_cmdline_test_res_rblank) -cmdline_test(drm_cmdline_test_res_bpp) -cmdline_test(drm_cmdline_test_res_bad_bpp) -cmdline_test(drm_cmdline_test_res_refresh) -cmdline_test(drm_cmdline_test_res_bad_refresh) -cmdline_test(drm_cmdline_test_res_bpp_refresh) -cmdline_test(drm_cmdline_test_res_bpp_refresh_interlaced) -cmdline_test(drm_cmdline_test_res_bpp_refresh_margins) -cmdline_test(drm_cmdline_test_res_bpp_refresh_force_off) -cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_off) -cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on) -cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_analog) -cmdline_test(drm_cmdline_test_res_bpp_refresh_force_on_digital) -cmdline_test(drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on) -cmdline_test(drm_cmdline_test_res_margins_force_on) -cmdline_test(drm_cmdline_test_res_vesa_margins) -cmdline_test(drm_cmdline_test_res_invalid_mode) -cmdline_test(drm_cmdline_test_res_bpp_wrong_place_mode) -cmdline_test(drm_cmdline_test_name) -cmdline_test(drm_cmdline_test_name_bpp) -cmdline_test(drm_cmdline_test_name_refresh) -cmdline_test(drm_cmdline_test_name_bpp_refresh) -cmdline_test(drm_cmdline_test_name_refresh_wrong_mode) -cmdline_test(drm_cmdline_test_name_refresh_invalid_mode) -cmdline_test(drm_cmdline_test_name_option) -cmdline_test(drm_cmdline_test_name_bpp_option) -cmdline_test(drm_cmdline_test_rotate_0) -cmdline_test(drm_cmdline_test_rotate_90) -cmdline_test(drm_cmdline_test_rotate_180) -cmdline_test(drm_cmdline_test_rotate_270) -cmdline_test(drm_cmdline_test_rotate_multiple) -cmdline_test(drm_cmdline_test_rotate_invalid_val) -cmdline_test(drm_cmdline_test_rotate_truncated) -cmdline_test(drm_cmdline_test_hmirror) -cmdline_test(drm_cmdline_test_vmirror) -cmdline_test(drm_cmdline_test_margin_options) -cmdline_test(drm_cmdline_test_multiple_options) -cmdline_test(drm_cmdline_test_invalid_option) -cmdline_test(drm_cmdline_test_bpp_extra_and_option) -cmdline_test(drm_cmdline_test_extra_and_option) -cmdline_test(drm_cmdline_test_freestanding_options) -cmdline_test(drm_cmdline_test_freestanding_force_e_and_options) -cmdline_test(drm_cmdline_test_panel_orientation) diff --git a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c b/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c deleted file mode 100644 index d96cd890def6..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_cmdline_parser.c +++ /dev/null @@ -1,1141 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2019 Bootlin - */ - -#define pr_fmt(fmt) "drm_cmdline: " fmt - -#include -#include - -#include -#include - -#define TESTS "drm_cmdline_selftests.h" -#include "drm_selftest.h" -#include "test-drm_modeset_common.h" - -static const struct drm_connector no_connector = {}; - -static int drm_cmdline_test_force_e_only(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("e", - &no_connector, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_force_D_only_not_digital(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("D", - &no_connector, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static const struct drm_connector connector_hdmi = { - .connector_type = DRM_MODE_CONNECTOR_HDMIB, -}; - -static int drm_cmdline_test_force_D_only_hdmi(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("D", - &connector_hdmi, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL); - - return 0; -} - -static const struct drm_connector connector_dvi = { - .connector_type = DRM_MODE_CONNECTOR_DVII, -}; - -static int drm_cmdline_test_force_D_only_dvi(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("D", - &connector_dvi, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL); - - return 0; -} - -static int drm_cmdline_test_force_d_only(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("d", - &no_connector, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_OFF); - - return 0; -} - -static int drm_cmdline_test_margin_only(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("m", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_interlace_only(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("i", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_missing_x(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("x480", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_missing_y(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("1024x", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_bad_y(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("1024xtest", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_missing_y_bpp(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("1024x-24", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_vesa(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480M", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(!mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_vesa_rblank(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480MR", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(!mode.rb); - FAIL_ON(!mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_rblank(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480R", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(!mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_bpp(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_bad_bpp(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480-test", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_refresh(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480@60", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_bad_refresh(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480@refresh", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_interlaced(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60i", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(!mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_margins(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60m", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(!mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_force_off(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60d", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_OFF); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_force_on_off(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480-24@60de", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_force_on(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60e", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_force_on_analog(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60D", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_force_on_digital(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - static const struct drm_connector connector = { - .connector_type = DRM_MODE_CONNECTOR_DVII, - }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60D", - &connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON_DIGITAL); - - return 0; -} - -static int drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24@60ime", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(!mode.refresh_specified); - FAIL_ON(mode.refresh != 60); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(!mode.interlace); - FAIL_ON(!mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_res_margins_force_on(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480me", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(!mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_res_vesa_margins(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480Mm", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(!mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(!mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_res_invalid_mode(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480f", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_res_bpp_wrong_place_mode(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480e-24", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_name(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC", - &no_connector, - &mode)); - FAIL_ON(strcmp(mode.name, "NTSC")); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - return 0; -} - -static int drm_cmdline_test_name_bpp(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC-24", - &no_connector, - &mode)); - FAIL_ON(strcmp(mode.name, "NTSC")); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - return 0; -} - -static int drm_cmdline_test_name_bpp_refresh(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC-24@60", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_name_refresh(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_name_refresh_wrong_mode(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60m", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_name_refresh_invalid_mode(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("NTSC@60f", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_name_option(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC,rotate=180", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(strcmp(mode.name, "NTSC")); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); - - return 0; -} - -static int drm_cmdline_test_name_bpp_option(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("NTSC-24,rotate=180", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(strcmp(mode.name, "NTSC")); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - return 0; -} - -static int drm_cmdline_test_rotate_0(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=0", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_0); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_rotate_90(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=90", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_90); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_rotate_180(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=180", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_rotate_270(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=270", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_270); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_rotate_multiple(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=0,rotate=90", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_rotate_invalid_val(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=42", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_rotate_truncated(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,rotate=", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_hmirror(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,reflect_x", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != (DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X)); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_vmirror(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,reflect_y", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != (DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y)); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_margin_options(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.tv_margins.right != 14); - FAIL_ON(mode.tv_margins.left != 24); - FAIL_ON(mode.tv_margins.bottom != 36); - FAIL_ON(mode.tv_margins.top != 42); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_multiple_options(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480,rotate=270,reflect_x", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != (DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X)); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_invalid_option(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(drm_mode_parse_command_line_for_connector("720x480,test=42", - &no_connector, - &mode)); - - return 0; -} - -static int drm_cmdline_test_bpp_extra_and_option(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480-24e,rotate=180", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); - - FAIL_ON(mode.refresh_specified); - - FAIL_ON(!mode.bpp_specified); - FAIL_ON(mode.bpp != 24); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_extra_and_option(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("720x480e,rotate=180", - &no_connector, - &mode)); - FAIL_ON(!mode.specified); - FAIL_ON(mode.xres != 720); - FAIL_ON(mode.yres != 480); - FAIL_ON(mode.rotation_reflection != DRM_MODE_ROTATE_180); - - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_freestanding_options(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("margin_right=14,margin_left=24,margin_bottom=36,margin_top=42", - &no_connector, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.tv_margins.right != 14); - FAIL_ON(mode.tv_margins.left != 24); - FAIL_ON(mode.tv_margins.bottom != 36); - FAIL_ON(mode.tv_margins.top != 42); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -static int drm_cmdline_test_freestanding_force_e_and_options(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42", - &no_connector, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.tv_margins.right != 14); - FAIL_ON(mode.tv_margins.left != 24); - FAIL_ON(mode.tv_margins.bottom != 36); - FAIL_ON(mode.tv_margins.top != 42); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_ON); - - return 0; -} - -static int drm_cmdline_test_panel_orientation(void *ignored) -{ - struct drm_cmdline_mode mode = { }; - - FAIL_ON(!drm_mode_parse_command_line_for_connector("panel_orientation=upside_down", - &no_connector, - &mode)); - FAIL_ON(mode.specified); - FAIL_ON(mode.refresh_specified); - FAIL_ON(mode.bpp_specified); - - FAIL_ON(mode.panel_orientation != DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP); - - FAIL_ON(mode.rb); - FAIL_ON(mode.cvt); - FAIL_ON(mode.interlace); - FAIL_ON(mode.margins); - FAIL_ON(mode.force != DRM_FORCE_UNSPECIFIED); - - return 0; -} - -#include "drm_selftest.c" - -static int __init test_drm_cmdline_init(void) -{ - int err; - - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); - - return err > 0 ? 0 : err; -} -module_init(test_drm_cmdline_init); - -MODULE_AUTHOR("Maxime Ripard "); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 2f399e03d50c..b3e73d674c67 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o +obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ + drm_cmdline_parser_test.o diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c new file mode 100644 index 000000000000..59b29cdfdd35 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Bootlin + * Copyright (c) 2022 Maíra Canal + */ + +#include + +#include +#include + +static const struct drm_connector no_connector = {}; + +static void drm_cmdline_test_force_e_only(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "e"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_force_D_only_not_digital(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "D"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static const struct drm_connector connector_hdmi = { + .connector_type = DRM_MODE_CONNECTOR_HDMIB, +}; + +static void drm_cmdline_test_force_D_only_hdmi(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "D"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &connector_hdmi, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON_DIGITAL); +} + +static const struct drm_connector connector_dvi = { + .connector_type = DRM_MODE_CONNECTOR_DVII, +}; + +static void drm_cmdline_test_force_D_only_dvi(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "D"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &connector_dvi, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON_DIGITAL); +} + +static void drm_cmdline_test_force_d_only(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "d"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_OFF); +} + +static void drm_cmdline_test_margin_only(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "m"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_interlace_only(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "i"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_missing_x(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "x480"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_missing_y(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "1024x"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_bad_y(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "1024xtest"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_missing_y_bpp(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "1024x-24"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_vesa(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480M"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_TRUE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_vesa_rblank(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480MR"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_TRUE(test, mode.rb); + KUNIT_EXPECT_TRUE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_rblank(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480R"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_TRUE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_bpp(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_bad_bpp(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-test"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_refresh(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480@60"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_bad_refresh(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480@refresh"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_bpp_refresh(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_bpp_refresh_interlaced(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60i"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_TRUE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_bpp_refresh_margins(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60m"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_TRUE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_bpp_refresh_force_off(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60d"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_OFF); +} + +static void drm_cmdline_test_res_bpp_refresh_force_on_off(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60de"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_bpp_refresh_force_on(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60e"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_res_bpp_refresh_force_on_analog(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60D"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_res_bpp_refresh_force_on_digital(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + static const struct drm_connector connector = { + .connector_type = DRM_MODE_CONNECTOR_DVII, + }; + const char *cmdline = "720x480-24@60D"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON_DIGITAL); +} + +static void drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24@60ime"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_TRUE(test, mode.refresh_specified); + KUNIT_EXPECT_EQ(test, mode.refresh, 60); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_TRUE(test, mode.interlace); + KUNIT_EXPECT_TRUE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_res_margins_force_on(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480me"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_TRUE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_res_vesa_margins(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480Mm"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_TRUE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_TRUE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_res_invalid_mode(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480f"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_res_bpp_wrong_place_mode(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480e-24"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_name(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_STREQ(test, mode.name, "NTSC"); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); +} + +static void drm_cmdline_test_name_bpp(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC-24"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_STREQ(test, mode.name, "NTSC"); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); +} + +static void drm_cmdline_test_name_bpp_refresh(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC-24@60"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_name_refresh(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC@60"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_name_refresh_wrong_mode(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC@60m"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_name_refresh_invalid_mode(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC@60f"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_name_option(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC,rotate=180"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_STREQ(test, mode.name, "NTSC"); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_180); +} + +static void drm_cmdline_test_name_bpp_option(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "NTSC-24,rotate=180"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_STREQ(test, mode.name, "NTSC"); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_180); + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); +} + +static void drm_cmdline_test_rotate_0(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=0"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_0); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_rotate_90(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=90"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_90); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_rotate_180(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=180"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_180); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_rotate_270(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=270"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_270); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_rotate_multiple(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=0,rotate=90"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_rotate_invalid_val(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=42"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_rotate_truncated(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate="; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_hmirror(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,reflect_x"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, (DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_X)); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_vmirror(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,reflect_y"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, (DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y)); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_margin_options(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = + "720x480,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.tv_margins.right, 14); + KUNIT_EXPECT_EQ(test, mode.tv_margins.left, 24); + KUNIT_EXPECT_EQ(test, mode.tv_margins.bottom, 36); + KUNIT_EXPECT_EQ(test, mode.tv_margins.top, 42); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_multiple_options(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,rotate=270,reflect_x"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, (DRM_MODE_ROTATE_270 | DRM_MODE_REFLECT_X)); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_invalid_option(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480,test=42"; + + KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); +} + +static void drm_cmdline_test_bpp_extra_and_option(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480-24e,rotate=180"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_180); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_TRUE(test, mode.bpp_specified); + KUNIT_EXPECT_EQ(test, mode.bpp, 24); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_extra_and_option(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "720x480e,rotate=180"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, 720); + KUNIT_EXPECT_EQ(test, mode.yres, 480); + KUNIT_EXPECT_EQ(test, mode.rotation_reflection, DRM_MODE_ROTATE_180); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_freestanding_options(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "margin_right=14,margin_left=24,margin_bottom=36,margin_top=42"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_EQ(test, mode.tv_margins.right, 14); + KUNIT_EXPECT_EQ(test, mode.tv_margins.left, 24); + KUNIT_EXPECT_EQ(test, mode.tv_margins.bottom, 36); + KUNIT_EXPECT_EQ(test, mode.tv_margins.top, 42); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static void drm_cmdline_test_freestanding_force_e_and_options(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_EQ(test, mode.tv_margins.right, 14); + KUNIT_EXPECT_EQ(test, mode.tv_margins.left, 24); + KUNIT_EXPECT_EQ(test, mode.tv_margins.bottom, 36); + KUNIT_EXPECT_EQ(test, mode.tv_margins.top, 42); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_ON); +} + +static void drm_cmdline_test_panel_orientation(struct kunit *test) +{ + struct drm_cmdline_mode mode = { }; + const char *cmdline = "panel_orientation=upside_down"; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_FALSE(test, mode.specified); + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_EQ(test, mode.panel_orientation, DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_FALSE(test, mode.interlace); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +static struct kunit_case drm_cmdline_parser_tests[] = { + KUNIT_CASE(drm_cmdline_test_force_d_only), + KUNIT_CASE(drm_cmdline_test_force_D_only_dvi), + KUNIT_CASE(drm_cmdline_test_force_D_only_hdmi), + KUNIT_CASE(drm_cmdline_test_force_D_only_not_digital), + KUNIT_CASE(drm_cmdline_test_force_e_only), + KUNIT_CASE(drm_cmdline_test_margin_only), + KUNIT_CASE(drm_cmdline_test_interlace_only), + KUNIT_CASE(drm_cmdline_test_res), + KUNIT_CASE(drm_cmdline_test_res_missing_x), + KUNIT_CASE(drm_cmdline_test_res_missing_y), + KUNIT_CASE(drm_cmdline_test_res_bad_y), + KUNIT_CASE(drm_cmdline_test_res_missing_y_bpp), + KUNIT_CASE(drm_cmdline_test_res_vesa), + KUNIT_CASE(drm_cmdline_test_res_vesa_rblank), + KUNIT_CASE(drm_cmdline_test_res_rblank), + KUNIT_CASE(drm_cmdline_test_res_bpp), + KUNIT_CASE(drm_cmdline_test_res_bad_bpp), + KUNIT_CASE(drm_cmdline_test_res_refresh), + KUNIT_CASE(drm_cmdline_test_res_bad_refresh), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_interlaced), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_margins), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_off), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on_off), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on_analog), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on_digital), + KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on), + KUNIT_CASE(drm_cmdline_test_res_margins_force_on), + KUNIT_CASE(drm_cmdline_test_res_vesa_margins), + KUNIT_CASE(drm_cmdline_test_res_invalid_mode), + KUNIT_CASE(drm_cmdline_test_res_bpp_wrong_place_mode), + KUNIT_CASE(drm_cmdline_test_name), + KUNIT_CASE(drm_cmdline_test_name_bpp), + KUNIT_CASE(drm_cmdline_test_name_refresh), + KUNIT_CASE(drm_cmdline_test_name_bpp_refresh), + KUNIT_CASE(drm_cmdline_test_name_refresh_wrong_mode), + KUNIT_CASE(drm_cmdline_test_name_refresh_invalid_mode), + KUNIT_CASE(drm_cmdline_test_name_option), + KUNIT_CASE(drm_cmdline_test_name_bpp_option), + KUNIT_CASE(drm_cmdline_test_rotate_0), + KUNIT_CASE(drm_cmdline_test_rotate_90), + KUNIT_CASE(drm_cmdline_test_rotate_180), + KUNIT_CASE(drm_cmdline_test_rotate_270), + KUNIT_CASE(drm_cmdline_test_rotate_multiple), + KUNIT_CASE(drm_cmdline_test_rotate_invalid_val), + KUNIT_CASE(drm_cmdline_test_rotate_truncated), + KUNIT_CASE(drm_cmdline_test_hmirror), + KUNIT_CASE(drm_cmdline_test_vmirror), + KUNIT_CASE(drm_cmdline_test_margin_options), + KUNIT_CASE(drm_cmdline_test_multiple_options), + KUNIT_CASE(drm_cmdline_test_invalid_option), + KUNIT_CASE(drm_cmdline_test_bpp_extra_and_option), + KUNIT_CASE(drm_cmdline_test_extra_and_option), + KUNIT_CASE(drm_cmdline_test_freestanding_options), + KUNIT_CASE(drm_cmdline_test_freestanding_force_e_and_options), + KUNIT_CASE(drm_cmdline_test_panel_orientation), + {} +}; + +static struct kunit_suite drm_cmdline_parser_test_suite = { + .name = "drm_cmdline_parser", + .test_cases = drm_cmdline_parser_tests +}; + +kunit_test_suite(drm_cmdline_parser_test_suite); + +MODULE_AUTHOR("Maxime Ripard "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 93de485cd822d429e91f197a820f02ee57d38fd5 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:46 -0300 Subject: drm: selftest: convert drm_rect selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM rect selftest to the KUnit API. Co-developed-by: Carlos Veras Signed-off-by: Carlos Veras Co-developed-by: Matheus Vieira Signed-off-by: Matheus Vieira Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-4-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 2 +- drivers/gpu/drm/selftests/drm_modeset_selftests.h | 4 - .../gpu/drm/selftests/test-drm_modeset_common.h | 4 - drivers/gpu/drm/selftests/test-drm_rect.c | 223 --------------------- drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_rect_test.c | 214 ++++++++++++++++++++ 6 files changed, 216 insertions(+), 233 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/test-drm_rect.c create mode 100644 drivers/gpu/drm/tests/drm_rect_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 8633bb9ea717..8a794914e328 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ test-drm_format.o test-drm_framebuffer.o \ - test-drm_dp_mst_helper.o test-drm_rect.o + test-drm_dp_mst_helper.o obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o \ test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index 4787b3b70709..a3ca90307364 100644 --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h @@ -6,10 +6,6 @@ * * Tests are executed in order by igt/drm_selftests_helper */ -selftest(drm_rect_clip_scaled_div_by_zero, igt_drm_rect_clip_scaled_div_by_zero) -selftest(drm_rect_clip_scaled_not_clipped, igt_drm_rect_clip_scaled_not_clipped) -selftest(drm_rect_clip_scaled_clipped, igt_drm_rect_clip_scaled_clipped) -selftest(drm_rect_clip_scaled_signed_vs_unsigned, igt_drm_rect_clip_scaled_signed_vs_unsigned) selftest(check_plane_state, igt_check_plane_state) selftest(check_drm_format_block_width, igt_check_drm_format_block_width) selftest(check_drm_format_block_height, igt_check_drm_format_block_height) diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h index c29354e59cec..42a10d7da51c 100644 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -16,10 +16,6 @@ #define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") -int igt_drm_rect_clip_scaled_div_by_zero(void *ignored); -int igt_drm_rect_clip_scaled_not_clipped(void *ignored); -int igt_drm_rect_clip_scaled_clipped(void *ignored); -int igt_drm_rect_clip_scaled_signed_vs_unsigned(void *ignored); int igt_check_plane_state(void *ignored); int igt_check_drm_format_block_width(void *ignored); int igt_check_drm_format_block_height(void *ignored); diff --git a/drivers/gpu/drm/selftests/test-drm_rect.c b/drivers/gpu/drm/selftests/test-drm_rect.c deleted file mode 100644 index 3a5ff38321f4..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_rect.c +++ /dev/null @@ -1,223 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Test cases for the drm_rect functions - */ - -#define pr_fmt(fmt) "drm_rect: " fmt - -#include - -#include - -#include "test-drm_modeset_common.h" - -int igt_drm_rect_clip_scaled_div_by_zero(void *ignored) -{ - struct drm_rect src, dst, clip; - bool visible; - - /* - * Make sure we don't divide by zero when dst - * width/height is zero and dst and clip do not intersect. - */ - drm_rect_init(&src, 0, 0, 0, 0); - drm_rect_init(&dst, 0, 0, 0, 0); - drm_rect_init(&clip, 1, 1, 1, 1); - visible = drm_rect_clip_scaled(&src, &dst, &clip); - FAIL(visible, "Destination not be visible\n"); - FAIL(drm_rect_visible(&src), "Source should not be visible\n"); - - drm_rect_init(&src, 0, 0, 0, 0); - drm_rect_init(&dst, 3, 3, 0, 0); - drm_rect_init(&clip, 1, 1, 1, 1); - visible = drm_rect_clip_scaled(&src, &dst, &clip); - FAIL(visible, "Destination not be visible\n"); - FAIL(drm_rect_visible(&src), "Source should not be visible\n"); - - return 0; -} - -int igt_drm_rect_clip_scaled_not_clipped(void *ignored) -{ - struct drm_rect src, dst, clip; - bool visible; - - /* 1:1 scaling */ - drm_rect_init(&src, 0, 0, 1 << 16, 1 << 16); - drm_rect_init(&dst, 0, 0, 1, 1); - drm_rect_init(&clip, 0, 0, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 0 || src.x2 != 1 << 16 || - src.y1 != 0 || src.y2 != 1 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 0 || dst.x2 != 1 || - dst.y1 != 0 || dst.y2 != 1, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 2:1 scaling */ - drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); - drm_rect_init(&dst, 0, 0, 1, 1); - drm_rect_init(&clip, 0, 0, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 0 || src.x2 != 2 << 16 || - src.y1 != 0 || src.y2 != 2 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 0 || dst.x2 != 1 || - dst.y1 != 0 || dst.y2 != 1, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 1:2 scaling */ - drm_rect_init(&src, 0, 0, 1 << 16, 1 << 16); - drm_rect_init(&dst, 0, 0, 2, 2); - drm_rect_init(&clip, 0, 0, 2, 2); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 0 || src.x2 != 1 << 16 || - src.y1 != 0 || src.y2 != 1 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 0 || dst.x2 != 2 || - dst.y1 != 0 || dst.y2 != 2, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - return 0; -} - -int igt_drm_rect_clip_scaled_clipped(void *ignored) -{ - struct drm_rect src, dst, clip; - bool visible; - - /* 1:1 scaling top/left clip */ - drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); - drm_rect_init(&dst, 0, 0, 2, 2); - drm_rect_init(&clip, 0, 0, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 0 || src.x2 != 1 << 16 || - src.y1 != 0 || src.y2 != 1 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 0 || dst.x2 != 1 || - dst.y1 != 0 || dst.y2 != 1, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 1:1 scaling bottom/right clip */ - drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); - drm_rect_init(&dst, 0, 0, 2, 2); - drm_rect_init(&clip, 1, 1, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 1 << 16 || src.x2 != 2 << 16 || - src.y1 != 1 << 16 || src.y2 != 2 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 1 || dst.x2 != 2 || - dst.y1 != 1 || dst.y2 != 2, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 2:1 scaling top/left clip */ - drm_rect_init(&src, 0, 0, 4 << 16, 4 << 16); - drm_rect_init(&dst, 0, 0, 2, 2); - drm_rect_init(&clip, 0, 0, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 0 || src.x2 != 2 << 16 || - src.y1 != 0 || src.y2 != 2 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 0 || dst.x2 != 1 || - dst.y1 != 0 || dst.y2 != 1, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 2:1 scaling bottom/right clip */ - drm_rect_init(&src, 0, 0, 4 << 16, 4 << 16); - drm_rect_init(&dst, 0, 0, 2, 2); - drm_rect_init(&clip, 1, 1, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 2 << 16 || src.x2 != 4 << 16 || - src.y1 != 2 << 16 || src.y2 != 4 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 1 || dst.x2 != 2 || - dst.y1 != 1 || dst.y2 != 2, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 1:2 scaling top/left clip */ - drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); - drm_rect_init(&dst, 0, 0, 4, 4); - drm_rect_init(&clip, 0, 0, 2, 2); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 0 || src.x2 != 1 << 16 || - src.y1 != 0 || src.y2 != 1 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 0 || dst.x2 != 2 || - dst.y1 != 0 || dst.y2 != 2, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - /* 1:2 scaling bottom/right clip */ - drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); - drm_rect_init(&dst, 0, 0, 4, 4); - drm_rect_init(&clip, 2, 2, 2, 2); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(src.x1 != 1 << 16 || src.x2 != 2 << 16 || - src.y1 != 1 << 16 || src.y2 != 2 << 16, - "Source badly clipped\n"); - FAIL(dst.x1 != 2 || dst.x2 != 4 || - dst.y1 != 2 || dst.y2 != 4, - "Destination badly clipped\n"); - FAIL(!visible, "Destination should be visible\n"); - FAIL(!drm_rect_visible(&src), "Source should be visible\n"); - - return 0; -} - -int igt_drm_rect_clip_scaled_signed_vs_unsigned(void *ignored) -{ - struct drm_rect src, dst, clip; - bool visible; - - /* - * 'clip.x2 - dst.x1 >= dst width' could result a negative - * src rectangle width which is no longer expected by the - * code as it's using unsigned types. This could lead to - * the clipped source rectangle appering visible when it - * should have been fully clipped. Make sure both rectangles - * end up invisible. - */ - drm_rect_init(&src, 0, 0, INT_MAX, INT_MAX); - drm_rect_init(&dst, 0, 0, 2, 2); - drm_rect_init(&clip, 3, 3, 1, 1); - - visible = drm_rect_clip_scaled(&src, &dst, &clip); - - FAIL(visible, "Destination should not be visible\n"); - FAIL(drm_rect_visible(&src), "Source should not be visible\n"); - - return 0; -} diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index b3e73d674c67..f2eced30a955 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ - drm_cmdline_parser_test.o + drm_cmdline_parser_test.o drm_rect_test.o diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c new file mode 100644 index 000000000000..c1dbefd49a4c --- /dev/null +++ b/drivers/gpu/drm/tests/drm_rect_test.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for the drm_rect functions + * + * Copyright (c) 2022 Maíra Canal + */ + +#include + +#include + +static void igt_drm_rect_clip_scaled_div_by_zero(struct kunit *test) +{ + struct drm_rect src, dst, clip; + bool visible; + + /* + * Make sure we don't divide by zero when dst + * width/height is zero and dst and clip do not intersect. + */ + drm_rect_init(&src, 0, 0, 0, 0); + drm_rect_init(&dst, 0, 0, 0, 0); + drm_rect_init(&clip, 1, 1, 1, 1); + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, visible, "Destination not be visible\n"); + KUNIT_EXPECT_FALSE_MSG(test, drm_rect_visible(&src), "Source should not be visible\n"); + + drm_rect_init(&src, 0, 0, 0, 0); + drm_rect_init(&dst, 3, 3, 0, 0); + drm_rect_init(&clip, 1, 1, 1, 1); + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, visible, "Destination not be visible\n"); + KUNIT_EXPECT_FALSE_MSG(test, drm_rect_visible(&src), "Source should not be visible\n"); +} + +static void igt_drm_rect_clip_scaled_not_clipped(struct kunit *test) +{ + struct drm_rect src, dst, clip; + bool visible; + + /* 1:1 scaling */ + drm_rect_init(&src, 0, 0, 1 << 16, 1 << 16); + drm_rect_init(&dst, 0, 0, 1, 1); + drm_rect_init(&clip, 0, 0, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 || + src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 || + dst.y1 != 0 || dst.y2 != 1, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 2:1 scaling */ + drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); + drm_rect_init(&dst, 0, 0, 1, 1); + drm_rect_init(&clip, 0, 0, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 2 << 16 || + src.y1 != 0 || src.y2 != 2 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 || + dst.y1 != 0 || dst.y2 != 1, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 1:2 scaling */ + drm_rect_init(&src, 0, 0, 1 << 16, 1 << 16); + drm_rect_init(&dst, 0, 0, 2, 2); + drm_rect_init(&clip, 0, 0, 2, 2); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 || + src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 2 || + dst.y1 != 0 || dst.y2 != 2, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); +} + +static void igt_drm_rect_clip_scaled_clipped(struct kunit *test) +{ + struct drm_rect src, dst, clip; + bool visible; + + /* 1:1 scaling top/left clip */ + drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); + drm_rect_init(&dst, 0, 0, 2, 2); + drm_rect_init(&clip, 0, 0, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 || + src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 || + dst.y1 != 0 || dst.y2 != 1, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 1:1 scaling bottom/right clip */ + drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); + drm_rect_init(&dst, 0, 0, 2, 2); + drm_rect_init(&clip, 1, 1, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 1 << 16 || src.x2 != 2 << 16 || + src.y1 != 1 << 16 || src.y2 != 2 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 1 || dst.x2 != 2 || dst.y1 != 1 || + dst.y2 != 2, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 2:1 scaling top/left clip */ + drm_rect_init(&src, 0, 0, 4 << 16, 4 << 16); + drm_rect_init(&dst, 0, 0, 2, 2); + drm_rect_init(&clip, 0, 0, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 2 << 16 || + src.y1 != 0 || src.y2 != 2 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 1 || dst.y1 != 0 || + dst.y2 != 1, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 2:1 scaling bottom/right clip */ + drm_rect_init(&src, 0, 0, 4 << 16, 4 << 16); + drm_rect_init(&dst, 0, 0, 2, 2); + drm_rect_init(&clip, 1, 1, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 2 << 16 || src.x2 != 4 << 16 || + src.y1 != 2 << 16 || src.y2 != 4 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 1 || dst.x2 != 2 || dst.y1 != 1 || + dst.y2 != 2, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 1:2 scaling top/left clip */ + drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); + drm_rect_init(&dst, 0, 0, 4, 4); + drm_rect_init(&clip, 0, 0, 2, 2); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 0 || src.x2 != 1 << 16 || + src.y1 != 0 || src.y2 != 1 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 0 || dst.x2 != 2 || dst.y1 != 0 || + dst.y2 != 2, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); + + /* 1:2 scaling bottom/right clip */ + drm_rect_init(&src, 0, 0, 2 << 16, 2 << 16); + drm_rect_init(&dst, 0, 0, 4, 4); + drm_rect_init(&clip, 2, 2, 2, 2); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, src.x1 != 1 << 16 || src.x2 != 2 << 16 || + src.y1 != 1 << 16 || src.y2 != 2 << 16, "Source badly clipped\n"); + KUNIT_EXPECT_FALSE_MSG(test, dst.x1 != 2 || dst.x2 != 4 || dst.y1 != 2 || + dst.y2 != 4, "Destination badly clipped\n"); + KUNIT_EXPECT_TRUE_MSG(test, visible, "Destination should be visible\n"); + KUNIT_EXPECT_TRUE_MSG(test, drm_rect_visible(&src), "Source should be visible\n"); +} + +static void igt_drm_rect_clip_scaled_signed_vs_unsigned(struct kunit *test) +{ + struct drm_rect src, dst, clip; + bool visible; + + /* + * 'clip.x2 - dst.x1 >= dst width' could result a negative + * src rectangle width which is no longer expected by the + * code as it's using unsigned types. This could lead to + * the clipped source rectangle appering visible when it + * should have been fully clipped. Make sure both rectangles + * end up invisible. + */ + drm_rect_init(&src, 0, 0, INT_MAX, INT_MAX); + drm_rect_init(&dst, 0, 0, 2, 2); + drm_rect_init(&clip, 3, 3, 1, 1); + + visible = drm_rect_clip_scaled(&src, &dst, &clip); + + KUNIT_EXPECT_FALSE_MSG(test, visible, "Destination should not be visible\n"); + KUNIT_EXPECT_FALSE_MSG(test, drm_rect_visible(&src), "Source should not be visible\n"); +} + +static struct kunit_case drm_rect_tests[] = { + KUNIT_CASE(igt_drm_rect_clip_scaled_div_by_zero), + KUNIT_CASE(igt_drm_rect_clip_scaled_not_clipped), + KUNIT_CASE(igt_drm_rect_clip_scaled_clipped), + KUNIT_CASE(igt_drm_rect_clip_scaled_signed_vs_unsigned), + { } +}; + +static struct kunit_suite drm_rect_test_suite = { + .name = "drm_rect", + .test_cases = drm_rect_tests, +}; + +kunit_test_suite(drm_rect_test_suite); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 0421bb0baa84fc93bbb659fe682e19266730f9f1 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:47 -0300 Subject: drm: selftest: convert drm_format selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM format selftest to the KUnit API. Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-5-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 3 +- drivers/gpu/drm/selftests/drm_modeset_selftests.h | 3 - drivers/gpu/drm/selftests/test-drm_format.c | 280 -------------------- .../gpu/drm/selftests/test-drm_modeset_common.h | 3 - drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_format_test.c | 287 +++++++++++++++++++++ 6 files changed, 289 insertions(+), 289 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/test-drm_format.c create mode 100644 drivers/gpu/drm/tests/drm_format_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 8a794914e328..b7f252d886d0 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ - test-drm_format.o test-drm_framebuffer.o \ - test-drm_dp_mst_helper.o + test-drm_framebuffer.o test-drm_dp_mst_helper.o obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o \ test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index a3ca90307364..63061ef55eff 100644 --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h @@ -7,9 +7,6 @@ * Tests are executed in order by igt/drm_selftests_helper */ selftest(check_plane_state, igt_check_plane_state) -selftest(check_drm_format_block_width, igt_check_drm_format_block_width) -selftest(check_drm_format_block_height, igt_check_drm_format_block_height) -selftest(check_drm_format_min_pitch, igt_check_drm_format_min_pitch) selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create) selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode) selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode) diff --git a/drivers/gpu/drm/selftests/test-drm_format.c b/drivers/gpu/drm/selftests/test-drm_format.c deleted file mode 100644 index c5e212afa27a..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_format.c +++ /dev/null @@ -1,280 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Test cases for the drm_format functions - */ - -#define pr_fmt(fmt) "drm_format: " fmt - -#include -#include - -#include - -#include "test-drm_modeset_common.h" - -int igt_check_drm_format_block_width(void *ignored) -{ - const struct drm_format_info *info = NULL; - - /* Test invalid arguments */ - FAIL_ON(drm_format_info_block_width(info, 0) != 0); - FAIL_ON(drm_format_info_block_width(info, -1) != 0); - FAIL_ON(drm_format_info_block_width(info, 1) != 0); - - /* Test 1 plane format */ - info = drm_format_info(DRM_FORMAT_XRGB4444); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_width(info, 0) != 1); - FAIL_ON(drm_format_info_block_width(info, 1) != 0); - FAIL_ON(drm_format_info_block_width(info, -1) != 0); - - /* Test 2 planes format */ - info = drm_format_info(DRM_FORMAT_NV12); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_width(info, 0) != 1); - FAIL_ON(drm_format_info_block_width(info, 1) != 1); - FAIL_ON(drm_format_info_block_width(info, 2) != 0); - FAIL_ON(drm_format_info_block_width(info, -1) != 0); - - /* Test 3 planes format */ - info = drm_format_info(DRM_FORMAT_YUV422); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_width(info, 0) != 1); - FAIL_ON(drm_format_info_block_width(info, 1) != 1); - FAIL_ON(drm_format_info_block_width(info, 2) != 1); - FAIL_ON(drm_format_info_block_width(info, 3) != 0); - FAIL_ON(drm_format_info_block_width(info, -1) != 0); - - /* Test a tiled format */ - info = drm_format_info(DRM_FORMAT_X0L0); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_width(info, 0) != 2); - FAIL_ON(drm_format_info_block_width(info, 1) != 0); - FAIL_ON(drm_format_info_block_width(info, -1) != 0); - - return 0; -} - -int igt_check_drm_format_block_height(void *ignored) -{ - const struct drm_format_info *info = NULL; - - /* Test invalid arguments */ - FAIL_ON(drm_format_info_block_height(info, 0) != 0); - FAIL_ON(drm_format_info_block_height(info, -1) != 0); - FAIL_ON(drm_format_info_block_height(info, 1) != 0); - - /* Test 1 plane format */ - info = drm_format_info(DRM_FORMAT_XRGB4444); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_height(info, 0) != 1); - FAIL_ON(drm_format_info_block_height(info, 1) != 0); - FAIL_ON(drm_format_info_block_height(info, -1) != 0); - - /* Test 2 planes format */ - info = drm_format_info(DRM_FORMAT_NV12); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_height(info, 0) != 1); - FAIL_ON(drm_format_info_block_height(info, 1) != 1); - FAIL_ON(drm_format_info_block_height(info, 2) != 0); - FAIL_ON(drm_format_info_block_height(info, -1) != 0); - - /* Test 3 planes format */ - info = drm_format_info(DRM_FORMAT_YUV422); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_height(info, 0) != 1); - FAIL_ON(drm_format_info_block_height(info, 1) != 1); - FAIL_ON(drm_format_info_block_height(info, 2) != 1); - FAIL_ON(drm_format_info_block_height(info, 3) != 0); - FAIL_ON(drm_format_info_block_height(info, -1) != 0); - - /* Test a tiled format */ - info = drm_format_info(DRM_FORMAT_X0L0); - FAIL_ON(!info); - FAIL_ON(drm_format_info_block_height(info, 0) != 2); - FAIL_ON(drm_format_info_block_height(info, 1) != 0); - FAIL_ON(drm_format_info_block_height(info, -1) != 0); - - return 0; -} - -int igt_check_drm_format_min_pitch(void *ignored) -{ - const struct drm_format_info *info = NULL; - - /* Test invalid arguments */ - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - - /* Test 1 plane 8 bits per pixel format */ - info = drm_format_info(DRM_FORMAT_RGB332); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 1); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 640); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 1024); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 1920); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 4096); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 671); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX); - FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)) != - (uint64_t)(UINT_MAX - 1)); - - /* Test 1 plane 16 bits per pixel format */ - info = drm_format_info(DRM_FORMAT_XRGB4444); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 4); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 1280); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 2048); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 3840); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 8192); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 1342); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX * 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)) != - (uint64_t)(UINT_MAX - 1) * 2); - - /* Test 1 plane 24 bits per pixel format */ - info = drm_format_info(DRM_FORMAT_RGB888); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 3); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 6); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 1920); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 3072); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 5760); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 12288); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 2013); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX * 3); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX - 1) != - (uint64_t)(UINT_MAX - 1) * 3); - - /* Test 1 plane 32 bits per pixel format */ - info = drm_format_info(DRM_FORMAT_ABGR8888); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 4); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 8); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 2560); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 4096); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 7680); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 16384); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 2684); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX * 4); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX - 1) != - (uint64_t)(UINT_MAX - 1) * 4); - - /* Test 2 planes format */ - info = drm_format_info(DRM_FORMAT_NV12); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 2, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 1); - FAIL_ON(drm_format_info_min_pitch(info, 1, 1) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 1, 1) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 640); - FAIL_ON(drm_format_info_min_pitch(info, 1, 320) != 640); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 1024); - FAIL_ON(drm_format_info_min_pitch(info, 1, 512) != 1024); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 1920); - FAIL_ON(drm_format_info_min_pitch(info, 1, 960) != 1920); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 4096); - FAIL_ON(drm_format_info_min_pitch(info, 1, 2048) != 4096); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 671); - FAIL_ON(drm_format_info_min_pitch(info, 1, 336) != 672); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX); - FAIL_ON(drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) != - (uint64_t)UINT_MAX + 1); - FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)) != - (uint64_t)(UINT_MAX - 1)); - FAIL_ON(drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) != - (uint64_t)(UINT_MAX - 1)); - - /* Test 3 planes 8 bits per pixel format */ - info = drm_format_info(DRM_FORMAT_YUV422); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 2, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 3, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 1); - FAIL_ON(drm_format_info_min_pitch(info, 1, 1) != 1); - FAIL_ON(drm_format_info_min_pitch(info, 2, 1) != 1); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 1, 2) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 2, 2) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 640); - FAIL_ON(drm_format_info_min_pitch(info, 1, 320) != 320); - FAIL_ON(drm_format_info_min_pitch(info, 2, 320) != 320); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 1024); - FAIL_ON(drm_format_info_min_pitch(info, 1, 512) != 512); - FAIL_ON(drm_format_info_min_pitch(info, 2, 512) != 512); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 1920); - FAIL_ON(drm_format_info_min_pitch(info, 1, 960) != 960); - FAIL_ON(drm_format_info_min_pitch(info, 2, 960) != 960); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 4096); - FAIL_ON(drm_format_info_min_pitch(info, 1, 2048) != 2048); - FAIL_ON(drm_format_info_min_pitch(info, 2, 2048) != 2048); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 671); - FAIL_ON(drm_format_info_min_pitch(info, 1, 336) != 336); - FAIL_ON(drm_format_info_min_pitch(info, 2, 336) != 336); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX); - FAIL_ON(drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1) != - (uint64_t)UINT_MAX / 2 + 1); - FAIL_ON(drm_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1) != - (uint64_t)UINT_MAX / 2 + 1); - FAIL_ON(drm_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2) != - (uint64_t)(UINT_MAX - 1) / 2); - FAIL_ON(drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2) != - (uint64_t)(UINT_MAX - 1) / 2); - FAIL_ON(drm_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2) != - (uint64_t)(UINT_MAX - 1) / 2); - - /* Test tiled format */ - info = drm_format_info(DRM_FORMAT_X0L2); - FAIL_ON(!info); - FAIL_ON(drm_format_info_min_pitch(info, 0, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, -1, 0) != 0); - FAIL_ON(drm_format_info_min_pitch(info, 1, 0) != 0); - - FAIL_ON(drm_format_info_min_pitch(info, 0, 1) != 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, 2) != 4); - FAIL_ON(drm_format_info_min_pitch(info, 0, 640) != 1280); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1024) != 2048); - FAIL_ON(drm_format_info_min_pitch(info, 0, 1920) != 3840); - FAIL_ON(drm_format_info_min_pitch(info, 0, 4096) != 8192); - FAIL_ON(drm_format_info_min_pitch(info, 0, 671) != 1342); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX) != - (uint64_t)UINT_MAX * 2); - FAIL_ON(drm_format_info_min_pitch(info, 0, UINT_MAX - 1) != - (uint64_t)(UINT_MAX - 1) * 2); - - return 0; -} diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h index 42a10d7da51c..5709d967a5c4 100644 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -17,9 +17,6 @@ #define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") int igt_check_plane_state(void *ignored); -int igt_check_drm_format_block_width(void *ignored); -int igt_check_drm_format_block_height(void *ignored); -int igt_check_drm_format_min_pitch(void *ignored); int igt_check_drm_framebuffer_create(void *ignored); int igt_dp_mst_calc_pbn_mode(void *ignored); int igt_dp_mst_sideband_msg_req_decode(void *ignored); diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index f2eced30a955..1aa1627cb5e6 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ - drm_cmdline_parser_test.o drm_rect_test.o + drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o diff --git a/drivers/gpu/drm/tests/drm_format_test.c b/drivers/gpu/drm/tests/drm_format_test.c new file mode 100644 index 000000000000..056cb8599d6d --- /dev/null +++ b/drivers/gpu/drm/tests/drm_format_test.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for the drm_format functions + * + * Copyright (c) 2022 Maíra Canal + */ + +#include + +#include + +static void igt_check_drm_format_block_width(struct kunit *test) +{ + const struct drm_format_info *info = NULL; + + /* Test invalid arguments */ + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 1)); + + /* Test 1 plane format */ + info = drm_format_info(DRM_FORMAT_XRGB4444); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1)); + + /* Test 2 planes format */ + info = drm_format_info(DRM_FORMAT_NV12); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 0)); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 2)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1)); + + /* Test 3 planes format */ + info = drm_format_info(DRM_FORMAT_YUV422); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 0)); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 1)); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_width(info, 2)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 3)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1)); + + /* Test a tiled format */ + info = drm_format_info(DRM_FORMAT_X0L0); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_EQ(test, drm_format_info_block_width(info, 0), 2); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, 1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_width(info, -1)); +} + +static void igt_check_drm_format_block_height(struct kunit *test) +{ + const struct drm_format_info *info = NULL; + + /* Test invalid arguments */ + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, -1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 1)); + + /* Test 1 plane format */ + info = drm_format_info(DRM_FORMAT_XRGB4444); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, -1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 1)); + + /* Test 2 planes format */ + info = drm_format_info(DRM_FORMAT_NV12); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 0)); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 2)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, -1)); + + /* Test 3 planes format */ + info = drm_format_info(DRM_FORMAT_YUV422); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 0)); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 1)); + KUNIT_EXPECT_TRUE(test, drm_format_info_block_height(info, 2)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 3)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, -1)); + + /* Test a tiled format */ + info = drm_format_info(DRM_FORMAT_X0L0); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_EQ(test, drm_format_info_block_height(info, 0), 2); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, 1)); + KUNIT_EXPECT_FALSE(test, drm_format_info_block_height(info, -1)); +} + +static void igt_check_drm_format_min_pitch(struct kunit *test) +{ + const struct drm_format_info *info = NULL; + + /* Test invalid arguments */ + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + + /* Test 1 plane 8 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_RGB332); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 640); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 1024); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 1920); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 4096); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 671); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)), + (uint64_t)(UINT_MAX - 1)); + + /* Test 1 plane 16 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_XRGB4444); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 4); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 1280); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 2048); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 3840); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 8192); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 1342); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX * 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)), + (uint64_t)(UINT_MAX - 1) * 2); + + /* Test 1 plane 24 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_RGB888); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 3); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 6); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 1920); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 3072); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 5760); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 12288); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 2013); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX * 3); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)), + (uint64_t)(UINT_MAX - 1) * 3); + + /* Test 1 plane 32 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_ABGR8888); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 4); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 8); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 2560); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 4096); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 7680); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 16384); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 2684); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX * 4); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)), + (uint64_t)(UINT_MAX - 1) * 4); + + /* Test 2 planes format */ + info = drm_format_info(DRM_FORMAT_NV12); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 2, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 1), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 1), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 640); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 320), 640); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 1024); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 512), 1024); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 1920); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 960), 1920); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 4096); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 2048), 4096); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 671); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 336), 672); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1), + (uint64_t)UINT_MAX + 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1)), + (uint64_t)(UINT_MAX - 1)); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2), + (uint64_t)(UINT_MAX - 1)); + + /* Test 3 planes 8 bits per pixel format */ + info = drm_format_info(DRM_FORMAT_YUV422); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 2, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 3, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 1), 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 1), 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 2), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 2), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 640); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 320), 320); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 320), 320); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 1024); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 512), 512); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 512), 512); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 1920); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 960), 960); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 960), 960); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 4096); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 2048), 2048); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 2048), 2048); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 671); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, 336), 336); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, 336), 336); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, UINT_MAX / 2 + 1), + (uint64_t)UINT_MAX / 2 + 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, UINT_MAX / 2 + 1), + (uint64_t)UINT_MAX / 2 + 1); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, (UINT_MAX - 1) / 2), + (uint64_t)(UINT_MAX - 1) / 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 1, (UINT_MAX - 1) / 2), + (uint64_t)(UINT_MAX - 1) / 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 2, (UINT_MAX - 1) / 2), + (uint64_t)(UINT_MAX - 1) / 2); + + /* Test tiled format */ + info = drm_format_info(DRM_FORMAT_X0L2); + KUNIT_EXPECT_TRUE(test, info); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 0, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, -1, 0)); + KUNIT_EXPECT_FALSE(test, drm_format_info_min_pitch(info, 1, 0)); + + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1), 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 2), 4); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 640), 1280); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1024), 2048); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 1920), 3840); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 4096), 8192); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, 671), 1342); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX), + (uint64_t)UINT_MAX * 2); + KUNIT_EXPECT_EQ(test, drm_format_info_min_pitch(info, 0, UINT_MAX - 1), + (uint64_t)(UINT_MAX - 1) * 2); +} + +static struct kunit_case drm_format_tests[] = { + KUNIT_CASE(igt_check_drm_format_block_width), + KUNIT_CASE(igt_check_drm_format_block_height), + KUNIT_CASE(igt_check_drm_format_min_pitch), + { } +}; + +static struct kunit_suite drm_format_test_suite = { + .name = "drm_format", + .test_cases = drm_format_tests, +}; + +kunit_test_suite(drm_format_test_suite); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 913b1e7ca77e0d04b74921e7328e3446f3a481be Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:48 -0300 Subject: drm: selftest: convert drm_plane_helper selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM plane helper selftest to the KUnit API. Co-developed-by: Djakson C. G. Filho Signed-off-by: Djakson C. G. Filho Co-developed-by: Anderson Fraga Signed-off-by: Anderson Fraga Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-6-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 4 +- drivers/gpu/drm/selftests/drm_modeset_selftests.h | 1 - .../gpu/drm/selftests/test-drm_modeset_common.h | 1 - drivers/gpu/drm/selftests/test-drm_plane_helper.c | 224 ------------------- drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_plane_helper_test.c | 238 +++++++++++++++++++++ 6 files changed, 241 insertions(+), 229 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/test-drm_plane_helper.c create mode 100644 drivers/gpu/drm/tests/drm_plane_helper_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index b7f252d886d0..9e0ccb482841 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -test-drm_modeset-y := test-drm_modeset_common.o test-drm_plane_helper.o \ - test-drm_framebuffer.o test-drm_dp_mst_helper.o +test-drm_modeset-y := test-drm_modeset_common.o test-drm_framebuffer.o \ + test-drm_dp_mst_helper.o obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o \ test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index 63061ef55eff..22e467f6465a 100644 --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h @@ -6,7 +6,6 @@ * * Tests are executed in order by igt/drm_selftests_helper */ -selftest(check_plane_state, igt_check_plane_state) selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create) selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode) selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode) diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h index 5709d967a5c4..790f3cf31f0d 100644 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -16,7 +16,6 @@ #define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") -int igt_check_plane_state(void *ignored); int igt_check_drm_framebuffer_create(void *ignored); int igt_dp_mst_calc_pbn_mode(void *ignored); int igt_dp_mst_sideband_msg_req_decode(void *ignored); diff --git a/drivers/gpu/drm/selftests/test-drm_plane_helper.c b/drivers/gpu/drm/selftests/test-drm_plane_helper.c deleted file mode 100644 index 64e8938ab194..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_plane_helper.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Test cases for the drm_plane_helper functions - */ - -#define pr_fmt(fmt) "drm_plane_helper: " fmt - -#include -#include -#include -#include - -#include "test-drm_modeset_common.h" - -static void set_src(struct drm_plane_state *plane_state, - unsigned src_x, unsigned src_y, - unsigned src_w, unsigned src_h) -{ - plane_state->src_x = src_x; - plane_state->src_y = src_y; - plane_state->src_w = src_w; - plane_state->src_h = src_h; -} - -static bool check_src_eq(struct drm_plane_state *plane_state, - unsigned src_x, unsigned src_y, - unsigned src_w, unsigned src_h) -{ - if (plane_state->src.x1 < 0) { - pr_err("src x coordinate %x should never be below 0.\n", plane_state->src.x1); - drm_rect_debug_print("src: ", &plane_state->src, true); - return false; - } - if (plane_state->src.y1 < 0) { - pr_err("src y coordinate %x should never be below 0.\n", plane_state->src.y1); - drm_rect_debug_print("src: ", &plane_state->src, true); - return false; - } - - if (plane_state->src.x1 != src_x || - plane_state->src.y1 != src_y || - drm_rect_width(&plane_state->src) != src_w || - drm_rect_height(&plane_state->src) != src_h) { - drm_rect_debug_print("src: ", &plane_state->src, true); - return false; - } - - return true; -} - -static void set_crtc(struct drm_plane_state *plane_state, - int crtc_x, int crtc_y, - unsigned crtc_w, unsigned crtc_h) -{ - plane_state->crtc_x = crtc_x; - plane_state->crtc_y = crtc_y; - plane_state->crtc_w = crtc_w; - plane_state->crtc_h = crtc_h; -} - -static bool check_crtc_eq(struct drm_plane_state *plane_state, - int crtc_x, int crtc_y, - unsigned crtc_w, unsigned crtc_h) -{ - if (plane_state->dst.x1 != crtc_x || - plane_state->dst.y1 != crtc_y || - drm_rect_width(&plane_state->dst) != crtc_w || - drm_rect_height(&plane_state->dst) != crtc_h) { - drm_rect_debug_print("dst: ", &plane_state->dst, false); - - return false; - } - - return true; -} - -int igt_check_plane_state(void *ignored) -{ - int ret; - - static const struct drm_crtc_state crtc_state = { - .crtc = ZERO_SIZE_PTR, - .enable = true, - .active = true, - .mode = { - DRM_MODE("1024x768", 0, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) - }, - }; - static struct drm_plane plane = { - .dev = NULL - }; - static struct drm_framebuffer fb = { - .width = 2048, - .height = 2048 - }; - static struct drm_plane_state plane_state = { - .plane = &plane, - .crtc = ZERO_SIZE_PTR, - .fb = &fb, - .rotation = DRM_MODE_ROTATE_0 - }; - - /* Simple clipping, no scaling. */ - set_src(&plane_state, 0, 0, fb.width << 16, fb.height << 16); - set_crtc(&plane_state, 0, 0, fb.width, fb.height); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, false); - FAIL(ret < 0, "Simple clipping check should pass\n"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 1024 << 16, 768 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1024, 768)); - - /* Rotated clipping + reflection, no scaling. */ - plane_state.rotation = DRM_MODE_ROTATE_90 | DRM_MODE_REFLECT_X; - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, false); - FAIL(ret < 0, "Rotated clipping check should pass\n"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 768 << 16, 1024 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1024, 768)); - plane_state.rotation = DRM_MODE_ROTATE_0; - - /* Check whether positioning works correctly. */ - set_src(&plane_state, 0, 0, 1023 << 16, 767 << 16); - set_crtc(&plane_state, 0, 0, 1023, 767); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - false, false); - FAIL(!ret, "Should not be able to position on the crtc with can_position=false\n"); - - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, false); - FAIL(ret < 0, "Simple positioning should work\n"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 1023 << 16, 767 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1023, 767)); - - /* Simple scaling tests. */ - set_src(&plane_state, 0, 0, 512 << 16, 384 << 16); - set_crtc(&plane_state, 0, 0, 1024, 768); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - 0x8001, - DRM_PLANE_HELPER_NO_SCALING, - false, false); - FAIL(!ret, "Upscaling out of range should fail.\n"); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - 0x8000, - DRM_PLANE_HELPER_NO_SCALING, - false, false); - FAIL(ret < 0, "Upscaling exactly 2x should work\n"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 512 << 16, 384 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1024, 768)); - - set_src(&plane_state, 0, 0, 2048 << 16, 1536 << 16); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - 0x1ffff, false, false); - FAIL(!ret, "Downscaling out of range should fail.\n"); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - 0x20000, false, false); - FAIL(ret < 0, "Should succeed with exact scaling limit\n"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 2048 << 16, 1536 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1024, 768)); - - /* Testing rounding errors. */ - set_src(&plane_state, 0, 0, 0x40001, 0x40001); - set_crtc(&plane_state, 1022, 766, 4, 4); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - 0x10001, - true, false); - FAIL(ret < 0, "Should succeed by clipping to exact multiple"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 2 << 16, 2 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 1022, 766, 2, 2)); - - set_src(&plane_state, 0x20001, 0x20001, 0x4040001, 0x3040001); - set_crtc(&plane_state, -2, -2, 1028, 772); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - DRM_PLANE_HELPER_NO_SCALING, - 0x10001, - false, false); - FAIL(ret < 0, "Should succeed by clipping to exact multiple"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0x40002, 0x40002, 1024 << 16, 768 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1024, 768)); - - set_src(&plane_state, 0, 0, 0x3ffff, 0x3ffff); - set_crtc(&plane_state, 1022, 766, 4, 4); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - 0xffff, - DRM_PLANE_HELPER_NO_SCALING, - true, false); - FAIL(ret < 0, "Should succeed by clipping to exact multiple"); - FAIL_ON(!plane_state.visible); - /* Should not be rounded to 0x20001, which would be upscaling. */ - FAIL_ON(!check_src_eq(&plane_state, 0, 0, 2 << 16, 2 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 1022, 766, 2, 2)); - - set_src(&plane_state, 0x1ffff, 0x1ffff, 0x403ffff, 0x303ffff); - set_crtc(&plane_state, -2, -2, 1028, 772); - ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, - 0xffff, - DRM_PLANE_HELPER_NO_SCALING, - false, false); - FAIL(ret < 0, "Should succeed by clipping to exact multiple"); - FAIL_ON(!plane_state.visible); - FAIL_ON(!check_src_eq(&plane_state, 0x3fffe, 0x3fffe, 1024 << 16, 768 << 16)); - FAIL_ON(!check_crtc_eq(&plane_state, 0, 0, 1024, 768)); - - return 0; -} diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 1aa1627cb5e6..4d44006a4f23 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ - drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o + drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o diff --git a/drivers/gpu/drm/tests/drm_plane_helper_test.c b/drivers/gpu/drm/tests/drm_plane_helper_test.c new file mode 100644 index 000000000000..e298766cd41f --- /dev/null +++ b/drivers/gpu/drm/tests/drm_plane_helper_test.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for the drm_plane_helper functions + * + * Copyright (c) 2022 Maíra Canal + */ + +#include + +#include +#include +#include +#include + +static void set_src(struct drm_plane_state *plane_state, + unsigned int src_x, unsigned int src_y, + unsigned int src_w, unsigned int src_h) +{ + plane_state->src_x = src_x; + plane_state->src_y = src_y; + plane_state->src_w = src_w; + plane_state->src_h = src_h; +} + +static bool check_src_eq(struct drm_plane_state *plane_state, + unsigned int src_x, unsigned int src_y, + unsigned int src_w, unsigned int src_h) +{ + if (plane_state->src.x1 < 0) { + pr_err("src x coordinate %x should never be below 0.\n", plane_state->src.x1); + drm_rect_debug_print("src: ", &plane_state->src, true); + return false; + } + if (plane_state->src.y1 < 0) { + pr_err("src y coordinate %x should never be below 0.\n", plane_state->src.y1); + drm_rect_debug_print("src: ", &plane_state->src, true); + return false; + } + + if (plane_state->src.x1 != src_x || + plane_state->src.y1 != src_y || + drm_rect_width(&plane_state->src) != src_w || + drm_rect_height(&plane_state->src) != src_h) { + drm_rect_debug_print("src: ", &plane_state->src, true); + return false; + } + + return true; +} + +static void set_crtc(struct drm_plane_state *plane_state, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h) +{ + plane_state->crtc_x = crtc_x; + plane_state->crtc_y = crtc_y; + plane_state->crtc_w = crtc_w; + plane_state->crtc_h = crtc_h; +} + +static bool check_crtc_eq(struct drm_plane_state *plane_state, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h) +{ + if (plane_state->dst.x1 != crtc_x || + plane_state->dst.y1 != crtc_y || + drm_rect_width(&plane_state->dst) != crtc_w || + drm_rect_height(&plane_state->dst) != crtc_h) { + drm_rect_debug_print("dst: ", &plane_state->dst, false); + + return false; + } + + return true; +} + +static void igt_check_plane_state(struct kunit *test) +{ + int ret; + + static const struct drm_crtc_state crtc_state = { + .crtc = ZERO_SIZE_PTR, + .enable = true, + .active = true, + .mode = { + DRM_MODE("1024x768", 0, 65000, 1024, 1048, 1184, 1344, 0, 768, 771, + 777, 806, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) + }, + }; + static struct drm_plane plane = { + .dev = NULL + }; + static struct drm_framebuffer fb = { + .width = 2048, + .height = 2048 + }; + static struct drm_plane_state plane_state = { + .plane = &plane, + .crtc = ZERO_SIZE_PTR, + .fb = &fb, + .rotation = DRM_MODE_ROTATE_0 + }; + + /* Simple clipping, no scaling. */ + set_src(&plane_state, 0, 0, fb.width << 16, fb.height << 16); + set_crtc(&plane_state, 0, 0, fb.width, fb.height); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Simple clipping check should pass\n"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 1024 << 16, 768 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1024, 768)); + + /* Rotated clipping + reflection, no scaling. */ + plane_state.rotation = DRM_MODE_ROTATE_90 | DRM_MODE_REFLECT_X; + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Rotated clipping check should pass\n"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 768 << 16, 1024 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1024, 768)); + plane_state.rotation = DRM_MODE_ROTATE_0; + + /* Check whether positioning works correctly. */ + set_src(&plane_state, 0, 0, 1023 << 16, 767 << 16); + set_crtc(&plane_state, 0, 0, 1023, 767); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, false); + KUNIT_EXPECT_TRUE_MSG(test, ret, + "Should not be able to position on the crtc with can_position=false\n"); + + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Simple positioning should work\n"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 1023 << 16, 767 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1023, 767)); + + /* Simple scaling tests. */ + set_src(&plane_state, 0, 0, 512 << 16, 384 << 16); + set_crtc(&plane_state, 0, 0, 1024, 768); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + 0x8001, + DRM_PLANE_HELPER_NO_SCALING, + false, false); + KUNIT_EXPECT_TRUE_MSG(test, ret, "Upscaling out of range should fail.\n"); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + 0x8000, + DRM_PLANE_HELPER_NO_SCALING, + false, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Upscaling exactly 2x should work\n"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 512 << 16, 384 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1024, 768)); + + set_src(&plane_state, 0, 0, 2048 << 16, 1536 << 16); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + 0x1ffff, false, false); + KUNIT_EXPECT_TRUE_MSG(test, ret, "Downscaling out of range should fail.\n"); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + 0x20000, false, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Should succeed with exact scaling limit\n"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 2048 << 16, 1536 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1024, 768)); + + /* Testing rounding errors. */ + set_src(&plane_state, 0, 0, 0x40001, 0x40001); + set_crtc(&plane_state, 1022, 766, 4, 4); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + 0x10001, + true, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Should succeed by clipping to exact multiple"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 2 << 16, 2 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 1022, 766, 2, 2)); + + set_src(&plane_state, 0x20001, 0x20001, 0x4040001, 0x3040001); + set_crtc(&plane_state, -2, -2, 1028, 772); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + 0x10001, + false, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Should succeed by clipping to exact multiple"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0x40002, 0x40002, + 1024 << 16, 768 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1024, 768)); + + set_src(&plane_state, 0, 0, 0x3ffff, 0x3ffff); + set_crtc(&plane_state, 1022, 766, 4, 4); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + 0xffff, + DRM_PLANE_HELPER_NO_SCALING, + true, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Should succeed by clipping to exact multiple"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + /* Should not be rounded to 0x20001, which would be upscaling. */ + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0, 0, 2 << 16, 2 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 1022, 766, 2, 2)); + + set_src(&plane_state, 0x1ffff, 0x1ffff, 0x403ffff, 0x303ffff); + set_crtc(&plane_state, -2, -2, 1028, 772); + ret = drm_atomic_helper_check_plane_state(&plane_state, &crtc_state, + 0xffff, + DRM_PLANE_HELPER_NO_SCALING, + false, false); + KUNIT_EXPECT_FALSE_MSG(test, ret, 0, "Should succeed by clipping to exact multiple"); + KUNIT_EXPECT_TRUE(test, plane_state.visible); + KUNIT_EXPECT_TRUE(test, check_src_eq(&plane_state, 0x3fffe, 0x3fffe, + 1024 << 16, 768 << 16)); + KUNIT_EXPECT_TRUE(test, check_crtc_eq(&plane_state, 0, 0, 1024, 768)); +} + +static struct kunit_case drm_plane_helper_test[] = { + KUNIT_CASE(igt_check_plane_state), + {} +}; + +static struct kunit_suite drm_plane_helper_test_suite = { + .name = "drm_plane_helper", + .test_cases = drm_plane_helper_test, +}; + +kunit_test_suite(drm_plane_helper_test_suite); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 848b3c0be2b944f633fe9beb28ad28f15c39f7a1 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:49 -0300 Subject: drm: selftest: convert drm_dp_mst_helper selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM DP MST helper selftest to the KUnit API. Co-developed-by: Rubens Gomes Neto Signed-off-by: Rubens Gomes Neto Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-7-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 3 +- drivers/gpu/drm/selftests/drm_modeset_selftests.h | 2 - drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c | 275 -------------------- .../gpu/drm/selftests/test-drm_modeset_common.h | 2 - drivers/gpu/drm/tests/Makefile | 3 +- drivers/gpu/drm/tests/drm_dp_mst_helper_test.c | 286 +++++++++++++++++++++ 6 files changed, 289 insertions(+), 282 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c create mode 100644 drivers/gpu/drm/tests/drm_dp_mst_helper_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 9e0ccb482841..1539f55db9a7 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -test-drm_modeset-y := test-drm_modeset_common.o test-drm_framebuffer.o \ - test-drm_dp_mst_helper.o +test-drm_modeset-y := test-drm_modeset_common.o test-drm_framebuffer.o obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o \ test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h index 22e467f6465a..40a29b8cf386 100644 --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h +++ b/drivers/gpu/drm/selftests/drm_modeset_selftests.h @@ -7,5 +7,3 @@ * Tests are executed in order by igt/drm_selftests_helper */ selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create) -selftest(dp_mst_calc_pbn_mode, igt_dp_mst_calc_pbn_mode) -selftest(dp_mst_sideband_msg_req_decode, igt_dp_mst_sideband_msg_req_decode) diff --git a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c b/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c deleted file mode 100644 index 4caa9be900ac..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_dp_mst_helper.c +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Test cases for the DRM DP MST helpers - */ - -#define PREFIX_STR "[drm_dp_mst_helper]" - -#include - -#include -#include - -#include "../display/drm_dp_mst_topology_internal.h" -#include "test-drm_modeset_common.h" - -int igt_dp_mst_calc_pbn_mode(void *ignored) -{ - int pbn, i; - const struct { - int rate; - int bpp; - int expected; - bool dsc; - } test_params[] = { - { 154000, 30, 689, false }, - { 234000, 30, 1047, false }, - { 297000, 24, 1063, false }, - { 332880, 24, 50, true }, - { 324540, 24, 49, true }, - }; - - for (i = 0; i < ARRAY_SIZE(test_params); i++) { - pbn = drm_dp_calc_pbn_mode(test_params[i].rate, - test_params[i].bpp, - test_params[i].dsc); - FAIL(pbn != test_params[i].expected, - "Expected PBN %d for clock %d bpp %d, got %d\n", - test_params[i].expected, test_params[i].rate, - test_params[i].bpp, pbn); - } - - return 0; -} - -static bool -sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in, - const struct drm_dp_sideband_msg_req_body *out) -{ - const struct drm_dp_remote_i2c_read_tx *txin, *txout; - int i; - - if (in->req_type != out->req_type) - return false; - - switch (in->req_type) { - /* - * Compare struct members manually for request types which can't be - * compared simply using memcmp(). This is because said request types - * contain pointers to other allocated structs - */ - case DP_REMOTE_I2C_READ: -#define IN in->u.i2c_read -#define OUT out->u.i2c_read - if (IN.num_bytes_read != OUT.num_bytes_read || - IN.num_transactions != OUT.num_transactions || - IN.port_number != OUT.port_number || - IN.read_i2c_device_id != OUT.read_i2c_device_id) - return false; - - for (i = 0; i < IN.num_transactions; i++) { - txin = &IN.transactions[i]; - txout = &OUT.transactions[i]; - - if (txin->i2c_dev_id != txout->i2c_dev_id || - txin->no_stop_bit != txout->no_stop_bit || - txin->num_bytes != txout->num_bytes || - txin->i2c_transaction_delay != - txout->i2c_transaction_delay) - return false; - - if (memcmp(txin->bytes, txout->bytes, - txin->num_bytes) != 0) - return false; - } - break; -#undef IN -#undef OUT - - case DP_REMOTE_DPCD_WRITE: -#define IN in->u.dpcd_write -#define OUT out->u.dpcd_write - if (IN.dpcd_address != OUT.dpcd_address || - IN.num_bytes != OUT.num_bytes || - IN.port_number != OUT.port_number) - return false; - - return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0; -#undef IN -#undef OUT - - case DP_REMOTE_I2C_WRITE: -#define IN in->u.i2c_write -#define OUT out->u.i2c_write - if (IN.port_number != OUT.port_number || - IN.write_i2c_device_id != OUT.write_i2c_device_id || - IN.num_bytes != OUT.num_bytes) - return false; - - return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0; -#undef IN -#undef OUT - - default: - return memcmp(in, out, sizeof(*in)) == 0; - } - - return true; -} - -static bool -sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in) -{ - struct drm_dp_sideband_msg_req_body *out; - struct drm_printer p = drm_err_printer(PREFIX_STR); - struct drm_dp_sideband_msg_tx *txmsg; - int i, ret; - bool result = true; - - out = kzalloc(sizeof(*out), GFP_KERNEL); - if (!out) - return false; - - txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); - if (!txmsg) { - kfree(out); - return false; - } - - drm_dp_encode_sideband_req(in, txmsg); - ret = drm_dp_decode_sideband_req(txmsg, out); - if (ret < 0) { - drm_printf(&p, "Failed to decode sideband request: %d\n", - ret); - result = false; - goto out; - } - - if (!sideband_msg_req_equal(in, out)) { - drm_printf(&p, "Encode/decode failed, expected:\n"); - drm_dp_dump_sideband_msg_req_body(in, 1, &p); - drm_printf(&p, "Got:\n"); - drm_dp_dump_sideband_msg_req_body(out, 1, &p); - result = false; - goto out; - } - - switch (in->req_type) { - case DP_REMOTE_DPCD_WRITE: - kfree(out->u.dpcd_write.bytes); - break; - case DP_REMOTE_I2C_READ: - for (i = 0; i < out->u.i2c_read.num_transactions; i++) - kfree(out->u.i2c_read.transactions[i].bytes); - break; - case DP_REMOTE_I2C_WRITE: - kfree(out->u.i2c_write.bytes); - break; - } - - /* Clear everything but the req_type for the input */ - memset(&in->u, 0, sizeof(in->u)); - -out: - kfree(out); - kfree(txmsg); - return result; -} - -int igt_dp_mst_sideband_msg_req_decode(void *unused) -{ - struct drm_dp_sideband_msg_req_body in = { 0 }; - u8 data[] = { 0xff, 0x0, 0xdd }; - int i; - -#define DO_TEST() FAIL_ON(!sideband_msg_req_encode_decode(&in)) - - in.req_type = DP_ENUM_PATH_RESOURCES; - in.u.port_num.port_number = 5; - DO_TEST(); - - in.req_type = DP_POWER_UP_PHY; - in.u.port_num.port_number = 5; - DO_TEST(); - - in.req_type = DP_POWER_DOWN_PHY; - in.u.port_num.port_number = 5; - DO_TEST(); - - in.req_type = DP_ALLOCATE_PAYLOAD; - in.u.allocate_payload.number_sdp_streams = 3; - for (i = 0; i < in.u.allocate_payload.number_sdp_streams; i++) - in.u.allocate_payload.sdp_stream_sink[i] = i + 1; - DO_TEST(); - in.u.allocate_payload.port_number = 0xf; - DO_TEST(); - in.u.allocate_payload.vcpi = 0x7f; - DO_TEST(); - in.u.allocate_payload.pbn = U16_MAX; - DO_TEST(); - - in.req_type = DP_QUERY_PAYLOAD; - in.u.query_payload.port_number = 0xf; - DO_TEST(); - in.u.query_payload.vcpi = 0x7f; - DO_TEST(); - - in.req_type = DP_REMOTE_DPCD_READ; - in.u.dpcd_read.port_number = 0xf; - DO_TEST(); - in.u.dpcd_read.dpcd_address = 0xfedcb; - DO_TEST(); - in.u.dpcd_read.num_bytes = U8_MAX; - DO_TEST(); - - in.req_type = DP_REMOTE_DPCD_WRITE; - in.u.dpcd_write.port_number = 0xf; - DO_TEST(); - in.u.dpcd_write.dpcd_address = 0xfedcb; - DO_TEST(); - in.u.dpcd_write.num_bytes = ARRAY_SIZE(data); - in.u.dpcd_write.bytes = data; - DO_TEST(); - - in.req_type = DP_REMOTE_I2C_READ; - in.u.i2c_read.port_number = 0xf; - DO_TEST(); - in.u.i2c_read.read_i2c_device_id = 0x7f; - DO_TEST(); - in.u.i2c_read.num_transactions = 3; - in.u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3; - for (i = 0; i < in.u.i2c_read.num_transactions; i++) { - in.u.i2c_read.transactions[i].bytes = data; - in.u.i2c_read.transactions[i].num_bytes = ARRAY_SIZE(data); - in.u.i2c_read.transactions[i].i2c_dev_id = 0x7f & ~i; - in.u.i2c_read.transactions[i].i2c_transaction_delay = 0xf & ~i; - } - DO_TEST(); - - in.req_type = DP_REMOTE_I2C_WRITE; - in.u.i2c_write.port_number = 0xf; - DO_TEST(); - in.u.i2c_write.write_i2c_device_id = 0x7f; - DO_TEST(); - in.u.i2c_write.num_bytes = ARRAY_SIZE(data); - in.u.i2c_write.bytes = data; - DO_TEST(); - - in.req_type = DP_QUERY_STREAM_ENC_STATUS; - in.u.enc_status.stream_id = 1; - DO_TEST(); - get_random_bytes(in.u.enc_status.client_id, - sizeof(in.u.enc_status.client_id)); - DO_TEST(); - in.u.enc_status.stream_event = 3; - DO_TEST(); - in.u.enc_status.valid_stream_event = 0; - DO_TEST(); - in.u.enc_status.stream_behavior = 3; - DO_TEST(); - in.u.enc_status.valid_stream_behavior = 1; - DO_TEST(); - -#undef DO_TEST - return 0; -} diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h index 790f3cf31f0d..3feb2fea1a6b 100644 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h +++ b/drivers/gpu/drm/selftests/test-drm_modeset_common.h @@ -17,7 +17,5 @@ #define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") int igt_check_drm_framebuffer_create(void *ignored); -int igt_dp_mst_calc_pbn_mode(void *ignored); -int igt_dp_mst_sideband_msg_req_decode(void *ignored); #endif diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 4d44006a4f23..5ffacd3eacf3 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ - drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o + drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \ + drm_dp_mst_helper_test.o diff --git a/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c b/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c new file mode 100644 index 000000000000..1d2fade56227 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_dp_mst_helper_test.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test cases for the DRM DP MST helpers + * + * Copyright (c) 2022 Maíra Canal + */ + +#define PREFIX_STR "[drm_dp_mst_helper]" + +#include + +#include + +#include +#include + +#include "../display/drm_dp_mst_topology_internal.h" + +static void igt_dp_mst_calc_pbn_mode(struct kunit *test) +{ + int pbn, i; + const struct { + int rate; + int bpp; + int expected; + bool dsc; + } test_params[] = { + { 154000, 30, 689, false }, + { 234000, 30, 1047, false }, + { 297000, 24, 1063, false }, + { 332880, 24, 50, true }, + { 324540, 24, 49, true }, + }; + + for (i = 0; i < ARRAY_SIZE(test_params); i++) { + pbn = drm_dp_calc_pbn_mode(test_params[i].rate, + test_params[i].bpp, + test_params[i].dsc); + KUNIT_EXPECT_EQ_MSG(test, pbn, test_params[i].expected, + "Expected PBN %d for clock %d bpp %d, got %d\n", + test_params[i].expected, test_params[i].rate, + test_params[i].bpp, pbn); + } +} + +static bool +sideband_msg_req_equal(const struct drm_dp_sideband_msg_req_body *in, + const struct drm_dp_sideband_msg_req_body *out) +{ + const struct drm_dp_remote_i2c_read_tx *txin, *txout; + int i; + + if (in->req_type != out->req_type) + return false; + + switch (in->req_type) { + /* + * Compare struct members manually for request types which can't be + * compared simply using memcmp(). This is because said request types + * contain pointers to other allocated structs + */ + case DP_REMOTE_I2C_READ: +#define IN in->u.i2c_read +#define OUT out->u.i2c_read + if (IN.num_bytes_read != OUT.num_bytes_read || + IN.num_transactions != OUT.num_transactions || + IN.port_number != OUT.port_number || + IN.read_i2c_device_id != OUT.read_i2c_device_id) + return false; + + for (i = 0; i < IN.num_transactions; i++) { + txin = &IN.transactions[i]; + txout = &OUT.transactions[i]; + + if (txin->i2c_dev_id != txout->i2c_dev_id || + txin->no_stop_bit != txout->no_stop_bit || + txin->num_bytes != txout->num_bytes || + txin->i2c_transaction_delay != + txout->i2c_transaction_delay) + return false; + + if (memcmp(txin->bytes, txout->bytes, + txin->num_bytes) != 0) + return false; + } + break; +#undef IN +#undef OUT + + case DP_REMOTE_DPCD_WRITE: +#define IN in->u.dpcd_write +#define OUT out->u.dpcd_write + if (IN.dpcd_address != OUT.dpcd_address || + IN.num_bytes != OUT.num_bytes || + IN.port_number != OUT.port_number) + return false; + + return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0; +#undef IN +#undef OUT + + case DP_REMOTE_I2C_WRITE: +#define IN in->u.i2c_write +#define OUT out->u.i2c_write + if (IN.port_number != OUT.port_number || + IN.write_i2c_device_id != OUT.write_i2c_device_id || + IN.num_bytes != OUT.num_bytes) + return false; + + return memcmp(IN.bytes, OUT.bytes, IN.num_bytes) == 0; +#undef IN +#undef OUT + + default: + return memcmp(in, out, sizeof(*in)) == 0; + } + + return true; +} + +static bool +sideband_msg_req_encode_decode(struct drm_dp_sideband_msg_req_body *in) +{ + struct drm_dp_sideband_msg_req_body *out; + struct drm_printer p = drm_err_printer(PREFIX_STR); + struct drm_dp_sideband_msg_tx *txmsg; + int i, ret; + bool result = true; + + out = kzalloc(sizeof(*out), GFP_KERNEL); + if (!out) + return false; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + kfree(out); + return false; + } + + drm_dp_encode_sideband_req(in, txmsg); + ret = drm_dp_decode_sideband_req(txmsg, out); + if (ret < 0) { + drm_printf(&p, "Failed to decode sideband request: %d\n", + ret); + result = false; + goto out; + } + + if (!sideband_msg_req_equal(in, out)) { + drm_printf(&p, "Encode/decode failed, expected:\n"); + drm_dp_dump_sideband_msg_req_body(in, 1, &p); + drm_printf(&p, "Got:\n"); + drm_dp_dump_sideband_msg_req_body(out, 1, &p); + result = false; + goto out; + } + + switch (in->req_type) { + case DP_REMOTE_DPCD_WRITE: + kfree(out->u.dpcd_write.bytes); + break; + case DP_REMOTE_I2C_READ: + for (i = 0; i < out->u.i2c_read.num_transactions; i++) + kfree(out->u.i2c_read.transactions[i].bytes); + break; + case DP_REMOTE_I2C_WRITE: + kfree(out->u.i2c_write.bytes); + break; + } + + /* Clear everything but the req_type for the input */ + memset(&in->u, 0, sizeof(in->u)); + +out: + kfree(out); + kfree(txmsg); + return result; +} + +static void igt_dp_mst_sideband_msg_req_decode(struct kunit *test) +{ + struct drm_dp_sideband_msg_req_body in = { 0 }; + u8 data[] = { 0xff, 0x0, 0xdd }; + int i; + + in.req_type = DP_ENUM_PATH_RESOURCES; + in.u.port_num.port_number = 5; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_POWER_UP_PHY; + in.u.port_num.port_number = 5; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_POWER_DOWN_PHY; + in.u.port_num.port_number = 5; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_ALLOCATE_PAYLOAD; + in.u.allocate_payload.number_sdp_streams = 3; + for (i = 0; i < in.u.allocate_payload.number_sdp_streams; i++) + in.u.allocate_payload.sdp_stream_sink[i] = i + 1; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.allocate_payload.port_number = 0xf; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.allocate_payload.vcpi = 0x7f; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.allocate_payload.pbn = U16_MAX; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_QUERY_PAYLOAD; + in.u.query_payload.port_number = 0xf; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.query_payload.vcpi = 0x7f; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_REMOTE_DPCD_READ; + in.u.dpcd_read.port_number = 0xf; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.dpcd_read.dpcd_address = 0xfedcb; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.dpcd_read.num_bytes = U8_MAX; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_REMOTE_DPCD_WRITE; + in.u.dpcd_write.port_number = 0xf; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.dpcd_write.dpcd_address = 0xfedcb; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.dpcd_write.num_bytes = ARRAY_SIZE(data); + in.u.dpcd_write.bytes = data; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_REMOTE_I2C_READ; + in.u.i2c_read.port_number = 0xf; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.i2c_read.read_i2c_device_id = 0x7f; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.i2c_read.num_transactions = 3; + in.u.i2c_read.num_bytes_read = ARRAY_SIZE(data) * 3; + for (i = 0; i < in.u.i2c_read.num_transactions; i++) { + in.u.i2c_read.transactions[i].bytes = data; + in.u.i2c_read.transactions[i].num_bytes = ARRAY_SIZE(data); + in.u.i2c_read.transactions[i].i2c_dev_id = 0x7f & ~i; + in.u.i2c_read.transactions[i].i2c_transaction_delay = 0xf & ~i; + } + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_REMOTE_I2C_WRITE; + in.u.i2c_write.port_number = 0xf; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.i2c_write.write_i2c_device_id = 0x7f; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.i2c_write.num_bytes = ARRAY_SIZE(data); + in.u.i2c_write.bytes = data; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + + in.req_type = DP_QUERY_STREAM_ENC_STATUS; + in.u.enc_status.stream_id = 1; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + get_random_bytes(in.u.enc_status.client_id, + sizeof(in.u.enc_status.client_id)); + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.enc_status.stream_event = 3; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.enc_status.valid_stream_event = 0; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.enc_status.stream_behavior = 3; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); + in.u.enc_status.valid_stream_behavior = 1; + KUNIT_EXPECT_TRUE(test, sideband_msg_req_encode_decode(&in)); +} + +static struct kunit_case drm_dp_mst_helper_tests[] = { + KUNIT_CASE(igt_dp_mst_calc_pbn_mode), + KUNIT_CASE(igt_dp_mst_sideband_msg_req_decode), + { } +}; + +static struct kunit_suite drm_dp_mst_helper_test_suite = { + .name = "drm_dp_mst_helper", + .test_cases = drm_dp_mst_helper_tests, +}; + +kunit_test_suite(drm_dp_mst_helper_test_suite); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 9eb11f52a6285c0516046e11d45fa4be8714a1f7 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:50 -0300 Subject: drm: selftest: convert drm_framebuffer selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM framebuffer selftest to the KUnit API. Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-8-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 5 +- drivers/gpu/drm/selftests/drm_modeset_selftests.h | 9 - drivers/gpu/drm/selftests/test-drm_framebuffer.c | 350 ------------------- .../gpu/drm/selftests/test-drm_modeset_common.c | 32 -- .../gpu/drm/selftests/test-drm_modeset_common.h | 21 -- drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_framebuffer_test.c | 375 +++++++++++++++++++++ 7 files changed, 377 insertions(+), 417 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/drm_modeset_selftests.h delete mode 100644 drivers/gpu/drm/selftests/test-drm_framebuffer.c delete mode 100644 drivers/gpu/drm/selftests/test-drm_modeset_common.c delete mode 100644 drivers/gpu/drm/selftests/test-drm_modeset_common.h create mode 100644 drivers/gpu/drm/tests/drm_framebuffer_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index 1539f55db9a7..f7db628b60cb 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,5 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -test-drm_modeset-y := test-drm_modeset_common.o test-drm_framebuffer.o - -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_modeset.o \ - test-drm_buddy.o +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_buddy.o diff --git a/drivers/gpu/drm/selftests/drm_modeset_selftests.h b/drivers/gpu/drm/selftests/drm_modeset_selftests.h deleted file mode 100644 index 40a29b8cf386..000000000000 --- a/drivers/gpu/drm/selftests/drm_modeset_selftests.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* List each unit test as selftest(name, function) - * - * The name is used as both an enum and expanded as igt__name to create - * a module parameter. It must be unique and legal for a C identifier. - * - * Tests are executed in order by igt/drm_selftests_helper - */ -selftest(check_drm_framebuffer_create, igt_check_drm_framebuffer_create) diff --git a/drivers/gpu/drm/selftests/test-drm_framebuffer.c b/drivers/gpu/drm/selftests/test-drm_framebuffer.c deleted file mode 100644 index f6d66285c5fc..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_framebuffer.c +++ /dev/null @@ -1,350 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Test cases for the drm_framebuffer functions - */ - -#include - -#include -#include -#include -#include - -#include "../drm_crtc_internal.h" - -#include "test-drm_modeset_common.h" - -#define MIN_WIDTH 4 -#define MAX_WIDTH 4096 -#define MIN_HEIGHT 4 -#define MAX_HEIGHT 4096 - -struct drm_framebuffer_test { - int buffer_created; - struct drm_mode_fb_cmd2 cmd; - const char *name; -}; - -static struct drm_framebuffer_test createbuffer_tests[] = { -{ .buffer_created = 1, .name = "ABGR8888 normal sizes", - .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 }, - } -}, -{ .buffer_created = 1, .name = "ABGR8888 max sizes", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 pitch less than min required", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Invalid width", - .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "No pixel format", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0, - .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Width 0", - .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Height 0", - .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - } -}, -{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, - .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - } -}, -{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, - .flags = DRM_MODE_FB_MODIFIERS, .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, - .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, - } -}, -{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, - .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, - .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 1, .name = "NV12 Normal sizes", - .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 }, - } -}, -{ .buffer_created = 1, .name = "NV12 Max sizes", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 0, .name = "NV12 Invalid pitch", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 }, - } -}, -{ .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, - .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 0, .name = "NV12 different modifier per-plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, - .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, - .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, - DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, - .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, - DRM_FORMAT_MOD_SAMSUNG_64_32_TILE }, - .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, - } -}, -{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS", - .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, - .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, - } -}, -{ .buffer_created = 1, .name = "YVU420 Normal sizes", - .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, - .pitches = { 600, 300, 300 }, - } -}, -{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", - .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, - } -}, -{ .buffer_created = 1, .name = "YVU420 Max sizes", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), - DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 0, .name = "YVU420 Invalid pitch", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1, - DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 1, .name = "YVU420 Different pitches", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, - DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, - } -}, -{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH + MAX_WIDTH * MAX_HEIGHT, - MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, - } -}, -{ .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 0, .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 0, .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 1, .name = "YVU420 Valid modifier", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR, - AFBC_FORMAT_MOD_SPARSE }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, - .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, - .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, - AFBC_FORMAT_MOD_SPARSE }, - .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, - } -}, -{ .buffer_created = 1, .name = "X0L2 Normal sizes", - .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 } - } -}, -{ .buffer_created = 1, .name = "X0L2 Max sizes", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 } - } -}, -{ .buffer_created = 0, .name = "X0L2 Invalid pitch", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 } - } -}, -{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } - } -}, -{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } - } -}, -{ .buffer_created = 1, .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 }, - .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } - } -}, -{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, - .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, - } -}, -{ .buffer_created = 1, .name = "X0L2 Valid modifier", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, - .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, - .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, - } -}, -{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane", - .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, - .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 }, - .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, - .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, - .flags = DRM_MODE_FB_MODIFIERS, - } -}, -}; - -static struct drm_framebuffer *fb_create_mock(struct drm_device *dev, - struct drm_file *file_priv, - const struct drm_mode_fb_cmd2 *mode_cmd) -{ - int *buffer_created = dev->dev_private; - *buffer_created = 1; - return ERR_PTR(-EINVAL); -} - -static struct drm_mode_config_funcs mock_config_funcs = { - .fb_create = fb_create_mock, -}; - -static struct drm_device mock_drm_device = { - .mode_config = { - .min_width = MIN_WIDTH, - .max_width = MAX_WIDTH, - .min_height = MIN_HEIGHT, - .max_height = MAX_HEIGHT, - .funcs = &mock_config_funcs, - }, -}; - -static int execute_drm_mode_fb_cmd2(struct drm_mode_fb_cmd2 *r) -{ - int buffer_created = 0; - - mock_drm_device.dev_private = &buffer_created; - drm_internal_framebuffer_create(&mock_drm_device, r, NULL); - return buffer_created; -} - -int igt_check_drm_framebuffer_create(void *ignored) -{ - int i = 0; - - for (i = 0; i < ARRAY_SIZE(createbuffer_tests); i++) { - FAIL(createbuffer_tests[i].buffer_created != - execute_drm_mode_fb_cmd2(&createbuffer_tests[i].cmd), - "Test %d: \"%s\" failed\n", i, createbuffer_tests[i].name); - } - - return 0; -} diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.c b/drivers/gpu/drm/selftests/test-drm_modeset_common.c deleted file mode 100644 index 2a7f93774006..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.c +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Common file for modeset selftests. - */ - -#include - -#include "test-drm_modeset_common.h" - -#define TESTS "drm_modeset_selftests.h" -#include "drm_selftest.h" - -#include "drm_selftest.c" - -static int __init test_drm_modeset_init(void) -{ - int err; - - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); - - return err > 0 ? 0 : err; -} - -static void __exit test_drm_modeset_exit(void) -{ -} - -module_init(test_drm_modeset_init); -module_exit(test_drm_modeset_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/selftests/test-drm_modeset_common.h b/drivers/gpu/drm/selftests/test-drm_modeset_common.h deleted file mode 100644 index 3feb2fea1a6b..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_modeset_common.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef __TEST_DRM_MODESET_COMMON_H__ -#define __TEST_DRM_MODESET_COMMON_H__ - -#include -#include - -#define FAIL(test, msg, ...) \ - do { \ - if (test) { \ - pr_err("%s/%u: " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - return -EINVAL; \ - } \ - } while (0) - -#define FAIL_ON(x) FAIL((x), "%s", "FAIL_ON(" __stringify(x) ")\n") - -int igt_check_drm_framebuffer_create(void *ignored); - -#endif diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 5ffacd3eacf3..4cbfd9098d56 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \ - drm_dp_mst_helper_test.o + drm_dp_mst_helper_test.o drm_framebuffer_test.o diff --git a/drivers/gpu/drm/tests/drm_framebuffer_test.c b/drivers/gpu/drm/tests/drm_framebuffer_test.c new file mode 100644 index 000000000000..ec7a08ba4056 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_framebuffer_test.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Test cases for the drm_framebuffer functions + * + * Copyright (c) 2022 Maíra Canal + */ + +#include + +#include +#include +#include +#include + +#include "../drm_crtc_internal.h" + +#define MIN_WIDTH 4 +#define MAX_WIDTH 4096 +#define MIN_HEIGHT 4 +#define MAX_HEIGHT 4096 + +struct drm_framebuffer_test { + int buffer_created; + struct drm_mode_fb_cmd2 cmd; + const char *name; +}; + +static struct drm_framebuffer_test createbuffer_tests[] = { +{ .buffer_created = 1, .name = "ABGR8888 normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 pitch greater than min required", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 pitch less than min required", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Invalid width", + .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "No pixel format", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Width 0", + .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Height 0", + .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Large buffer offset", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 }, + } +}, +{ .buffer_created = 0, + .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 }, + .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888, + .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 Normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 Max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Invalid pitch", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 different modifier per-plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, + DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, + DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Modifier for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, + DRM_FORMAT_MOD_SAMSUNG_64_32_TILE }, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 0, .name = "NV12 Handle for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { MAX_WIDTH, MAX_WIDTH, 0 }, + } +}, +{ .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12, + .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { 600, 300, 300 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), + DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Invalid pitch", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1, + DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Different pitches", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, + DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH + + MAX_WIDTH * MAX_HEIGHT, MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1, + DIV_ROUND_UP(MAX_WIDTH, 2) + 7 }, + } +}, +{ .buffer_created = 0, + .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, + .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, + .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 1, .name = "YVU420 Valid modifier", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, + AFBC_FORMAT_MOD_SPARSE }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Different modifiers per plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR, + AFBC_FORMAT_MOD_SPARSE }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420, + .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, + AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE }, + .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) }, + } +}, +{ .buffer_created = 1, .name = "X0L2 Normal sizes", + .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 } + } +}, +{ .buffer_created = 1, .name = "X0L2 Max sizes", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 } + } +}, +{ .buffer_created = 0, .name = "X0L2 Invalid pitch", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 } + } +}, +{ .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } + } +}, +{ .buffer_created = 0, .name = "X0L2 Handle for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } + } +}, +{ .buffer_created = 1, + .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 }, + .pitches = { 2 * MAX_WIDTH + 1, 0, 0 } + } +}, +{ .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, + } +}, +{ .buffer_created = 1, .name = "X0L2 Valid modifier", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2, + .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS, + } +}, +{ .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane", + .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, + .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 }, + .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }, + .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 }, + .flags = DRM_MODE_FB_MODIFIERS, + } +}, +}; + +static struct drm_framebuffer *fb_create_mock(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int *buffer_created = dev->dev_private; + *buffer_created = 1; + return ERR_PTR(-EINVAL); +} + +static struct drm_mode_config_funcs mock_config_funcs = { + .fb_create = fb_create_mock, +}; + +static struct drm_device mock_drm_device = { + .mode_config = { + .min_width = MIN_WIDTH, + .max_width = MAX_WIDTH, + .min_height = MIN_HEIGHT, + .max_height = MAX_HEIGHT, + .funcs = &mock_config_funcs, + }, +}; + +static int execute_drm_mode_fb_cmd2(struct drm_mode_fb_cmd2 *r) +{ + int buffer_created = 0; + + mock_drm_device.dev_private = &buffer_created; + drm_internal_framebuffer_create(&mock_drm_device, r, NULL); + return buffer_created; +} + +static void igt_check_drm_framebuffer_create(struct kunit *test) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(createbuffer_tests); i++) { + KUNIT_EXPECT_EQ_MSG(test, createbuffer_tests[i].buffer_created, + execute_drm_mode_fb_cmd2(&createbuffer_tests[i].cmd), + "Test %d: \"%s\" failed\n", i, createbuffer_tests[i].name); + } +} + +static struct kunit_case drm_framebuffer_tests[] = { + KUNIT_CASE(igt_check_drm_framebuffer_create), + { } +}; + +static struct kunit_suite drm_framebuffer_test_suite = { + .name = "drm_framebuffer", + .test_cases = drm_framebuffer_tests, +}; + +kunit_test_suite(drm_framebuffer_test_suite); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 932da861956ac425ba4e65f7696458f96d833883 Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Fri, 8 Jul 2022 17:30:51 -0300 Subject: drm: selftest: convert drm_buddy selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM buddy selftest to the KUnit API. Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-9-maira.canal@usp.br --- drivers/gpu/drm/selftests/Makefile | 2 +- drivers/gpu/drm/selftests/drm_buddy_selftests.h | 15 - drivers/gpu/drm/selftests/test-drm_buddy.c | 994 ------------------------ drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_buddy_test.c | 756 ++++++++++++++++++ 5 files changed, 758 insertions(+), 1011 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/drm_buddy_selftests.h delete mode 100644 drivers/gpu/drm/selftests/test-drm_buddy.c create mode 100644 drivers/gpu/drm/tests/drm_buddy_test.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile index f7db628b60cb..a4ebecb8146b 100644 --- a/drivers/gpu/drm/selftests/Makefile +++ b/drivers/gpu/drm/selftests/Makefile @@ -1,2 +1,2 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o test-drm_buddy.o +obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o diff --git a/drivers/gpu/drm/selftests/drm_buddy_selftests.h b/drivers/gpu/drm/selftests/drm_buddy_selftests.h deleted file mode 100644 index 455b756c4ae5..000000000000 --- a/drivers/gpu/drm/selftests/drm_buddy_selftests.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* List each unit test as selftest(name, function) - * - * The name is used as both an enum and expanded as igt__name to create - * a module parameter. It must be unique and legal for a C identifier. - * - * Tests are executed in order by igt/drm_buddy - */ -selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */ -selftest(buddy_alloc_limit, igt_buddy_alloc_limit) -selftest(buddy_alloc_range, igt_buddy_alloc_range) -selftest(buddy_alloc_optimistic, igt_buddy_alloc_optimistic) -selftest(buddy_alloc_pessimistic, igt_buddy_alloc_pessimistic) -selftest(buddy_alloc_smoke, igt_buddy_alloc_smoke) -selftest(buddy_alloc_pathological, igt_buddy_alloc_pathological) diff --git a/drivers/gpu/drm/selftests/test-drm_buddy.c b/drivers/gpu/drm/selftests/test-drm_buddy.c deleted file mode 100644 index aca0c491040f..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_buddy.c +++ /dev/null @@ -1,994 +0,0 @@ -// SPDX-License-Identifier: MIT -/* - * Copyright © 2019 Intel Corporation - */ - -#define pr_fmt(fmt) "drm_buddy: " fmt - -#include -#include -#include - -#include - -#include "../lib/drm_random.h" - -#define TESTS "drm_buddy_selftests.h" -#include "drm_selftest.h" - -#define IGT_TIMEOUT(name__) \ - unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT - -static unsigned int random_seed; - -static inline u64 get_size(int order, u64 chunk_size) -{ - return (1 << order) * chunk_size; -} - -__printf(2, 3) -static bool __igt_timeout(unsigned long timeout, const char *fmt, ...) -{ - va_list va; - - if (!signal_pending(current)) { - cond_resched(); - if (time_before(jiffies, timeout)) - return false; - } - - if (fmt) { - va_start(va, fmt); - vprintk(fmt, va); - va_end(va); - } - - return true; -} - -static inline const char *yesno(bool v) -{ - return v ? "yes" : "no"; -} - -static void __igt_dump_block(struct drm_buddy *mm, - struct drm_buddy_block *block, - bool buddy) -{ - pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n", - block->header, - drm_buddy_block_state(block), - drm_buddy_block_order(block), - drm_buddy_block_offset(block), - drm_buddy_block_size(mm, block), - yesno(!block->parent), - yesno(buddy)); -} - -static void igt_dump_block(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - struct drm_buddy_block *buddy; - - __igt_dump_block(mm, block, false); - - buddy = drm_get_buddy(block); - if (buddy) - __igt_dump_block(mm, buddy, true); -} - -static int igt_check_block(struct drm_buddy *mm, - struct drm_buddy_block *block) -{ - struct drm_buddy_block *buddy; - unsigned int block_state; - u64 block_size; - u64 offset; - int err = 0; - - block_state = drm_buddy_block_state(block); - - if (block_state != DRM_BUDDY_ALLOCATED && - block_state != DRM_BUDDY_FREE && - block_state != DRM_BUDDY_SPLIT) { - pr_err("block state mismatch\n"); - err = -EINVAL; - } - - block_size = drm_buddy_block_size(mm, block); - offset = drm_buddy_block_offset(block); - - if (block_size < mm->chunk_size) { - pr_err("block size smaller than min size\n"); - err = -EINVAL; - } - - if (!is_power_of_2(block_size)) { - pr_err("block size not power of two\n"); - err = -EINVAL; - } - - if (!IS_ALIGNED(block_size, mm->chunk_size)) { - pr_err("block size not aligned to min size\n"); - err = -EINVAL; - } - - if (!IS_ALIGNED(offset, mm->chunk_size)) { - pr_err("block offset not aligned to min size\n"); - err = -EINVAL; - } - - if (!IS_ALIGNED(offset, block_size)) { - pr_err("block offset not aligned to block size\n"); - err = -EINVAL; - } - - buddy = drm_get_buddy(block); - - if (!buddy && block->parent) { - pr_err("buddy has gone fishing\n"); - err = -EINVAL; - } - - if (buddy) { - if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) { - pr_err("buddy has wrong offset\n"); - err = -EINVAL; - } - - if (drm_buddy_block_size(mm, buddy) != block_size) { - pr_err("buddy size mismatch\n"); - err = -EINVAL; - } - - if (drm_buddy_block_state(buddy) == block_state && - block_state == DRM_BUDDY_FREE) { - pr_err("block and its buddy are free\n"); - err = -EINVAL; - } - } - - return err; -} - -static int igt_check_blocks(struct drm_buddy *mm, - struct list_head *blocks, - u64 expected_size, - bool is_contiguous) -{ - struct drm_buddy_block *block; - struct drm_buddy_block *prev; - u64 total; - int err = 0; - - block = NULL; - prev = NULL; - total = 0; - - list_for_each_entry(block, blocks, link) { - err = igt_check_block(mm, block); - - if (!drm_buddy_block_is_allocated(block)) { - pr_err("block not allocated\n"), - err = -EINVAL; - } - - if (is_contiguous && prev) { - u64 prev_block_size; - u64 prev_offset; - u64 offset; - - prev_offset = drm_buddy_block_offset(prev); - prev_block_size = drm_buddy_block_size(mm, prev); - offset = drm_buddy_block_offset(block); - - if (offset != (prev_offset + prev_block_size)) { - pr_err("block offset mismatch\n"); - err = -EINVAL; - } - } - - if (err) - break; - - total += drm_buddy_block_size(mm, block); - prev = block; - } - - if (!err) { - if (total != expected_size) { - pr_err("size mismatch, expected=%llx, found=%llx\n", - expected_size, total); - err = -EINVAL; - } - return err; - } - - if (prev) { - pr_err("prev block, dump:\n"); - igt_dump_block(mm, prev); - } - - pr_err("bad block, dump:\n"); - igt_dump_block(mm, block); - - return err; -} - -static int igt_check_mm(struct drm_buddy *mm) -{ - struct drm_buddy_block *root; - struct drm_buddy_block *prev; - unsigned int i; - u64 total; - int err = 0; - - if (!mm->n_roots) { - pr_err("n_roots is zero\n"); - return -EINVAL; - } - - if (mm->n_roots != hweight64(mm->size)) { - pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n", - mm->n_roots, hweight64(mm->size)); - return -EINVAL; - } - - root = NULL; - prev = NULL; - total = 0; - - for (i = 0; i < mm->n_roots; ++i) { - struct drm_buddy_block *block; - unsigned int order; - - root = mm->roots[i]; - if (!root) { - pr_err("root(%u) is NULL\n", i); - err = -EINVAL; - break; - } - - err = igt_check_block(mm, root); - - if (!drm_buddy_block_is_free(root)) { - pr_err("root not free\n"); - err = -EINVAL; - } - - order = drm_buddy_block_order(root); - - if (!i) { - if (order != mm->max_order) { - pr_err("max order root missing\n"); - err = -EINVAL; - } - } - - if (prev) { - u64 prev_block_size; - u64 prev_offset; - u64 offset; - - prev_offset = drm_buddy_block_offset(prev); - prev_block_size = drm_buddy_block_size(mm, prev); - offset = drm_buddy_block_offset(root); - - if (offset != (prev_offset + prev_block_size)) { - pr_err("root offset mismatch\n"); - err = -EINVAL; - } - } - - block = list_first_entry_or_null(&mm->free_list[order], - struct drm_buddy_block, - link); - if (block != root) { - pr_err("root mismatch at order=%u\n", order); - err = -EINVAL; - } - - if (err) - break; - - prev = root; - total += drm_buddy_block_size(mm, root); - } - - if (!err) { - if (total != mm->size) { - pr_err("expected mm size=%llx, found=%llx\n", mm->size, - total); - err = -EINVAL; - } - return err; - } - - if (prev) { - pr_err("prev root(%u), dump:\n", i - 1); - igt_dump_block(mm, prev); - } - - if (root) { - pr_err("bad root(%u), dump:\n", i); - igt_dump_block(mm, root); - } - - return err; -} - -static void igt_mm_config(u64 *size, u64 *chunk_size) -{ - DRM_RND_STATE(prng, random_seed); - u32 s, ms; - - /* Nothing fancy, just try to get an interesting bit pattern */ - - prandom_seed_state(&prng, random_seed); - - /* Let size be a random number of pages up to 8 GB (2M pages) */ - s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng); - /* Let the chunk size be a random power of 2 less than size */ - ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng)); - /* Round size down to the chunk size */ - s &= -ms; - - /* Convert from pages to bytes */ - *chunk_size = (u64)ms << 12; - *size = (u64)s << 12; -} - -static int igt_buddy_alloc_pathological(void *arg) -{ - u64 mm_size, size, min_page_size, start = 0; - struct drm_buddy_block *block; - const int max_order = 3; - unsigned long flags = 0; - int order, top, err; - struct drm_buddy mm; - LIST_HEAD(blocks); - LIST_HEAD(holes); - LIST_HEAD(tmp); - - /* - * Create a pot-sized mm, then allocate one of each possible - * order within. This should leave the mm with exactly one - * page left. Free the largest block, then whittle down again. - * Eventually we will have a fully 50% fragmented mm. - */ - - mm_size = PAGE_SIZE << max_order; - err = drm_buddy_init(&mm, mm_size, PAGE_SIZE); - if (err) { - pr_err("buddy_init failed(%d)\n", err); - return err; - } - BUG_ON(mm.max_order != max_order); - - for (top = max_order; top; top--) { - /* Make room by freeing the largest allocated block */ - block = list_first_entry_or_null(&blocks, typeof(*block), link); - if (block) { - list_del(&block->link); - drm_buddy_free_block(&mm, block); - } - - for (order = top; order--; ) { - size = min_page_size = get_size(order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, - min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc hit -ENOMEM with order=%d, top=%d\n", - order, top); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - } - - /* There should be one final page for this sub-allocation */ - size = min_page_size = get_size(0, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc hit -ENOMEM for hole\n"); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &holes); - - size = min_page_size = get_size(top, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (!err) { - pr_info("buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", - top, max_order); - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - err = -EINVAL; - goto err; - } - } - - drm_buddy_free_list(&mm, &holes); - - /* Nothing larger than blocks of chunk_size now available */ - for (order = 1; order <= max_order; order++) { - size = min_page_size = get_size(order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (!err) { - pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!", - order); - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - err = -EINVAL; - goto err; - } - } - - if (err) - err = 0; - -err: - list_splice_tail(&holes, &blocks); - drm_buddy_free_list(&mm, &blocks); - drm_buddy_fini(&mm); - return err; -} - -static int igt_buddy_alloc_smoke(void *arg) -{ - u64 mm_size, min_page_size, chunk_size, start = 0; - unsigned long flags = 0; - struct drm_buddy mm; - int *order; - int err, i; - - DRM_RND_STATE(prng, random_seed); - IGT_TIMEOUT(end_time); - - igt_mm_config(&mm_size, &chunk_size); - - err = drm_buddy_init(&mm, mm_size, chunk_size); - if (err) { - pr_err("buddy_init failed(%d)\n", err); - return err; - } - - order = drm_random_order(mm.max_order + 1, &prng); - if (!order) { - err = -ENOMEM; - goto out_fini; - } - - for (i = 0; i <= mm.max_order; ++i) { - struct drm_buddy_block *block; - int max_order = order[i]; - bool timeout = false; - LIST_HEAD(blocks); - u64 total, size; - LIST_HEAD(tmp); - int order; - - err = igt_check_mm(&mm); - if (err) { - pr_err("pre-mm check failed, abort\n"); - break; - } - - order = max_order; - total = 0; - - do { -retry: - size = min_page_size = get_size(order, chunk_size); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, - min_page_size, &tmp, flags); - if (err) { - if (err == -ENOMEM) { - pr_info("buddy_alloc hit -ENOMEM with order=%d\n", - order); - } else { - if (order--) { - err = 0; - goto retry; - } - - pr_err("buddy_alloc with order=%d failed(%d)\n", - order, err); - } - - break; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - break; - } - - list_move_tail(&block->link, &blocks); - - if (drm_buddy_block_order(block) != order) { - pr_err("buddy_alloc order mismatch\n"); - err = -EINVAL; - break; - } - - total += drm_buddy_block_size(&mm, block); - - if (__igt_timeout(end_time, NULL)) { - timeout = true; - break; - } - } while (total < mm.size); - - if (!err) - err = igt_check_blocks(&mm, &blocks, total, false); - - drm_buddy_free_list(&mm, &blocks); - - if (!err) { - err = igt_check_mm(&mm); - if (err) - pr_err("post-mm check failed\n"); - } - - if (err || timeout) - break; - - cond_resched(); - } - - if (err == -ENOMEM) - err = 0; - - kfree(order); -out_fini: - drm_buddy_fini(&mm); - - return err; -} - -static int igt_buddy_alloc_pessimistic(void *arg) -{ - u64 mm_size, size, min_page_size, start = 0; - struct drm_buddy_block *block, *bn; - const unsigned int max_order = 16; - unsigned long flags = 0; - struct drm_buddy mm; - unsigned int order; - LIST_HEAD(blocks); - LIST_HEAD(tmp); - int err; - - /* - * Create a pot-sized mm, then allocate one of each possible - * order within. This should leave the mm with exactly one - * page left. - */ - - mm_size = PAGE_SIZE << max_order; - err = drm_buddy_init(&mm, mm_size, PAGE_SIZE); - if (err) { - pr_err("buddy_init failed(%d)\n", err); - return err; - } - BUG_ON(mm.max_order != max_order); - - for (order = 0; order < max_order; order++) { - size = min_page_size = get_size(order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc hit -ENOMEM with order=%d\n", - order); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - } - - /* And now the last remaining block available */ - size = min_page_size = get_size(0, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc hit -ENOMEM on final alloc\n"); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - - /* Should be completely full! */ - for (order = max_order; order--; ) { - size = min_page_size = get_size(order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (!err) { - pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!", - order); - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - err = -EINVAL; - goto err; - } - } - - block = list_last_entry(&blocks, typeof(*block), link); - list_del(&block->link); - drm_buddy_free_block(&mm, block); - - /* As we free in increasing size, we make available larger blocks */ - order = 1; - list_for_each_entry_safe(block, bn, &blocks, link) { - list_del(&block->link); - drm_buddy_free_block(&mm, block); - - size = min_page_size = get_size(order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n", - order); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_del(&block->link); - drm_buddy_free_block(&mm, block); - order++; - } - - /* To confirm, now the whole mm should be available */ - size = min_page_size = get_size(max_order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n", - max_order); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_del(&block->link); - drm_buddy_free_block(&mm, block); - -err: - drm_buddy_free_list(&mm, &blocks); - drm_buddy_fini(&mm); - return err; -} - -static int igt_buddy_alloc_optimistic(void *arg) -{ - u64 mm_size, size, min_page_size, start = 0; - struct drm_buddy_block *block; - unsigned long flags = 0; - const int max_order = 16; - struct drm_buddy mm; - LIST_HEAD(blocks); - LIST_HEAD(tmp); - int order, err; - - /* - * Create a mm with one block of each order available, and - * try to allocate them all. - */ - - mm_size = PAGE_SIZE * ((1 << (max_order + 1)) - 1); - err = drm_buddy_init(&mm, - mm_size, - PAGE_SIZE); - if (err) { - pr_err("buddy_init failed(%d)\n", err); - return err; - } - - BUG_ON(mm.max_order != max_order); - - for (order = 0; order <= max_order; order++) { - size = min_page_size = get_size(order, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (err) { - pr_info("buddy_alloc hit -ENOMEM with order=%d\n", - order); - goto err; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - } - - /* Should be completely full! */ - size = min_page_size = get_size(0, PAGE_SIZE); - err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags); - if (!err) { - pr_info("buddy_alloc unexpectedly succeeded, it should be full!"); - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_blocks has no blocks\n"); - err = -EINVAL; - goto err; - } - - list_move_tail(&block->link, &blocks); - err = -EINVAL; - goto err; - } else { - err = 0; - } - -err: - drm_buddy_free_list(&mm, &blocks); - drm_buddy_fini(&mm); - return err; -} - -static int igt_buddy_alloc_range(void *arg) -{ - unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION; - u64 offset, size, rem, chunk_size, end; - unsigned long page_num; - struct drm_buddy mm; - LIST_HEAD(blocks); - int err; - - igt_mm_config(&size, &chunk_size); - - err = drm_buddy_init(&mm, size, chunk_size); - if (err) { - pr_err("buddy_init failed(%d)\n", err); - return err; - } - - err = igt_check_mm(&mm); - if (err) { - pr_err("pre-mm check failed, abort, abort, abort!\n"); - goto err_fini; - } - - rem = mm.size; - offset = 0; - - for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { - struct drm_buddy_block *block; - LIST_HEAD(tmp); - - size = min(page_num * mm.chunk_size, rem); - end = offset + size; - - err = drm_buddy_alloc_blocks(&mm, offset, end, size, mm.chunk_size, &tmp, flags); - if (err) { - if (err == -ENOMEM) { - pr_info("alloc_range hit -ENOMEM with size=%llx\n", - size); - } else { - pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n", - offset, size, err); - } - - break; - } - - block = list_first_entry_or_null(&tmp, - struct drm_buddy_block, - link); - if (!block) { - pr_err("alloc_range has no blocks\n"); - err = -EINVAL; - break; - } - - if (drm_buddy_block_offset(block) != offset) { - pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n", - drm_buddy_block_offset(block), offset); - err = -EINVAL; - } - - if (!err) - err = igt_check_blocks(&mm, &tmp, size, true); - - list_splice_tail(&tmp, &blocks); - - if (err) - break; - - offset += size; - - rem -= size; - if (!rem) - break; - - cond_resched(); - } - - if (err == -ENOMEM) - err = 0; - - drm_buddy_free_list(&mm, &blocks); - - if (!err) { - err = igt_check_mm(&mm); - if (err) - pr_err("post-mm check failed\n"); - } - -err_fini: - drm_buddy_fini(&mm); - - return err; -} - -static int igt_buddy_alloc_limit(void *arg) -{ - u64 size = U64_MAX, start = 0; - struct drm_buddy_block *block; - unsigned long flags = 0; - LIST_HEAD(allocated); - struct drm_buddy mm; - int err; - - err = drm_buddy_init(&mm, size, PAGE_SIZE); - if (err) - return err; - - if (mm.max_order != DRM_BUDDY_MAX_ORDER) { - pr_err("mm.max_order(%d) != %d\n", - mm.max_order, DRM_BUDDY_MAX_ORDER); - err = -EINVAL; - goto out_fini; - } - - size = mm.chunk_size << mm.max_order; - err = drm_buddy_alloc_blocks(&mm, start, size, size, - PAGE_SIZE, &allocated, flags); - - if (unlikely(err)) - goto out_free; - - block = list_first_entry_or_null(&allocated, - struct drm_buddy_block, - link); - - if (!block) { - err = -EINVAL; - goto out_fini; - } - - if (drm_buddy_block_order(block) != mm.max_order) { - pr_err("block order(%d) != %d\n", - drm_buddy_block_order(block), mm.max_order); - err = -EINVAL; - goto out_free; - } - - if (drm_buddy_block_size(&mm, block) != - BIT_ULL(mm.max_order) * PAGE_SIZE) { - pr_err("block size(%llu) != %llu\n", - drm_buddy_block_size(&mm, block), - BIT_ULL(mm.max_order) * PAGE_SIZE); - err = -EINVAL; - goto out_free; - } - -out_free: - drm_buddy_free_list(&mm, &allocated); -out_fini: - drm_buddy_fini(&mm); - return err; -} - -static int igt_sanitycheck(void *ignored) -{ - pr_info("%s - ok!\n", __func__); - return 0; -} - -#include "drm_selftest.c" - -static int __init test_drm_buddy_init(void) -{ - int err; - - while (!random_seed) - random_seed = get_random_int(); - - pr_info("Testing DRM buddy manager (struct drm_buddy), with random_seed=0x%x\n", - random_seed); - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); - - return err > 0 ? 0 : err; -} - -static void __exit test_drm_buddy_exit(void) -{ -} - -module_init(test_drm_buddy_init); -module_exit(test_drm_buddy_exit); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 4cbfd9098d56..cff59189598f 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \ - drm_dp_mst_helper_test.o drm_framebuffer_test.o + drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o diff --git a/drivers/gpu/drm/tests/drm_buddy_test.c b/drivers/gpu/drm/tests/drm_buddy_test.c new file mode 100644 index 000000000000..d76f83833e75 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_buddy_test.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2019 Intel Corporation + * Copyright © 2022 Maíra Canal + */ + +#include + +#include +#include + +#include + +#include "../lib/drm_random.h" + +#define IGT_TIMEOUT(name__) \ + unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT + +static unsigned int random_seed; + +static inline u64 get_size(int order, u64 chunk_size) +{ + return (1 << order) * chunk_size; +} + +__printf(2, 3) +static bool __igt_timeout(unsigned long timeout, const char *fmt, ...) +{ + va_list va; + + if (!signal_pending(current)) { + cond_resched(); + if (time_before(jiffies, timeout)) + return false; + } + + if (fmt) { + va_start(va, fmt); + vprintk(fmt, va); + va_end(va); + } + + return true; +} + +static void __igt_dump_block(struct kunit *test, struct drm_buddy *mm, + struct drm_buddy_block *block, bool buddy) +{ + kunit_err(test, "block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%d buddy=%d\n", + block->header, drm_buddy_block_state(block), + drm_buddy_block_order(block), drm_buddy_block_offset(block), + drm_buddy_block_size(mm, block), !block->parent, buddy); +} + +static void igt_dump_block(struct kunit *test, struct drm_buddy *mm, + struct drm_buddy_block *block) +{ + struct drm_buddy_block *buddy; + + __igt_dump_block(test, mm, block, false); + + buddy = drm_get_buddy(block); + if (buddy) + __igt_dump_block(test, mm, buddy, true); +} + +static int igt_check_block(struct kunit *test, struct drm_buddy *mm, + struct drm_buddy_block *block) +{ + struct drm_buddy_block *buddy; + unsigned int block_state; + u64 block_size; + u64 offset; + int err = 0; + + block_state = drm_buddy_block_state(block); + + if (block_state != DRM_BUDDY_ALLOCATED && + block_state != DRM_BUDDY_FREE && block_state != DRM_BUDDY_SPLIT) { + kunit_err(test, "block state mismatch\n"); + err = -EINVAL; + } + + block_size = drm_buddy_block_size(mm, block); + offset = drm_buddy_block_offset(block); + + if (block_size < mm->chunk_size) { + kunit_err(test, "block size smaller than min size\n"); + err = -EINVAL; + } + + if (!is_power_of_2(block_size)) { + kunit_err(test, "block size not power of two\n"); + err = -EINVAL; + } + + if (!IS_ALIGNED(block_size, mm->chunk_size)) { + kunit_err(test, "block size not aligned to min size\n"); + err = -EINVAL; + } + + if (!IS_ALIGNED(offset, mm->chunk_size)) { + kunit_err(test, "block offset not aligned to min size\n"); + err = -EINVAL; + } + + if (!IS_ALIGNED(offset, block_size)) { + kunit_err(test, "block offset not aligned to block size\n"); + err = -EINVAL; + } + + buddy = drm_get_buddy(block); + + if (!buddy && block->parent) { + kunit_err(test, "buddy has gone fishing\n"); + err = -EINVAL; + } + + if (buddy) { + if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) { + kunit_err(test, "buddy has wrong offset\n"); + err = -EINVAL; + } + + if (drm_buddy_block_size(mm, buddy) != block_size) { + kunit_err(test, "buddy size mismatch\n"); + err = -EINVAL; + } + + if (drm_buddy_block_state(buddy) == block_state && + block_state == DRM_BUDDY_FREE) { + kunit_err(test, "block and its buddy are free\n"); + err = -EINVAL; + } + } + + return err; +} + +static int igt_check_blocks(struct kunit *test, struct drm_buddy *mm, + struct list_head *blocks, u64 expected_size, bool is_contiguous) +{ + struct drm_buddy_block *block; + struct drm_buddy_block *prev; + u64 total; + int err = 0; + + block = NULL; + prev = NULL; + total = 0; + + list_for_each_entry(block, blocks, link) { + err = igt_check_block(test, mm, block); + + if (!drm_buddy_block_is_allocated(block)) { + kunit_err(test, "block not allocated\n"); + err = -EINVAL; + } + + if (is_contiguous && prev) { + u64 prev_block_size; + u64 prev_offset; + u64 offset; + + prev_offset = drm_buddy_block_offset(prev); + prev_block_size = drm_buddy_block_size(mm, prev); + offset = drm_buddy_block_offset(block); + + if (offset != (prev_offset + prev_block_size)) { + kunit_err(test, "block offset mismatch\n"); + err = -EINVAL; + } + } + + if (err) + break; + + total += drm_buddy_block_size(mm, block); + prev = block; + } + + if (!err) { + if (total != expected_size) { + kunit_err(test, "size mismatch, expected=%llx, found=%llx\n", + expected_size, total); + err = -EINVAL; + } + return err; + } + + if (prev) { + kunit_err(test, "prev block, dump:\n"); + igt_dump_block(test, mm, prev); + } + + kunit_err(test, "bad block, dump:\n"); + igt_dump_block(test, mm, block); + + return err; +} + +static int igt_check_mm(struct kunit *test, struct drm_buddy *mm) +{ + struct drm_buddy_block *root; + struct drm_buddy_block *prev; + unsigned int i; + u64 total; + int err = 0; + + if (!mm->n_roots) { + kunit_err(test, "n_roots is zero\n"); + return -EINVAL; + } + + if (mm->n_roots != hweight64(mm->size)) { + kunit_err(test, "n_roots mismatch, n_roots=%u, expected=%lu\n", + mm->n_roots, hweight64(mm->size)); + return -EINVAL; + } + + root = NULL; + prev = NULL; + total = 0; + + for (i = 0; i < mm->n_roots; ++i) { + struct drm_buddy_block *block; + unsigned int order; + + root = mm->roots[i]; + if (!root) { + kunit_err(test, "root(%u) is NULL\n", i); + err = -EINVAL; + break; + } + + err = igt_check_block(test, mm, root); + + if (!drm_buddy_block_is_free(root)) { + kunit_err(test, "root not free\n"); + err = -EINVAL; + } + + order = drm_buddy_block_order(root); + + if (!i) { + if (order != mm->max_order) { + kunit_err(test, "max order root missing\n"); + err = -EINVAL; + } + } + + if (prev) { + u64 prev_block_size; + u64 prev_offset; + u64 offset; + + prev_offset = drm_buddy_block_offset(prev); + prev_block_size = drm_buddy_block_size(mm, prev); + offset = drm_buddy_block_offset(root); + + if (offset != (prev_offset + prev_block_size)) { + kunit_err(test, "root offset mismatch\n"); + err = -EINVAL; + } + } + + block = list_first_entry_or_null(&mm->free_list[order], + struct drm_buddy_block, link); + if (block != root) { + kunit_err(test, "root mismatch at order=%u\n", order); + err = -EINVAL; + } + + if (err) + break; + + prev = root; + total += drm_buddy_block_size(mm, root); + } + + if (!err) { + if (total != mm->size) { + kunit_err(test, "expected mm size=%llx, found=%llx\n", + mm->size, total); + err = -EINVAL; + } + return err; + } + + if (prev) { + kunit_err(test, "prev root(%u), dump:\n", i - 1); + igt_dump_block(test, mm, prev); + } + + if (root) { + kunit_err(test, "bad root(%u), dump:\n", i); + igt_dump_block(test, mm, root); + } + + return err; +} + +static void igt_mm_config(u64 *size, u64 *chunk_size) +{ + DRM_RND_STATE(prng, random_seed); + u32 s, ms; + + /* Nothing fancy, just try to get an interesting bit pattern */ + + prandom_seed_state(&prng, random_seed); + + /* Let size be a random number of pages up to 8 GB (2M pages) */ + s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng); + /* Let the chunk size be a random power of 2 less than size */ + ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng)); + /* Round size down to the chunk size */ + s &= -ms; + + /* Convert from pages to bytes */ + *chunk_size = (u64)ms << 12; + *size = (u64)s << 12; +} + +static void igt_buddy_alloc_pathological(struct kunit *test) +{ + u64 mm_size, size, start = 0; + struct drm_buddy_block *block; + const int max_order = 3; + unsigned long flags = 0; + int order, top; + struct drm_buddy mm; + LIST_HEAD(blocks); + LIST_HEAD(holes); + LIST_HEAD(tmp); + + /* + * Create a pot-sized mm, then allocate one of each possible + * order within. This should leave the mm with exactly one + * page left. Free the largest block, then whittle down again. + * Eventually we will have a fully 50% fragmented mm. + */ + + mm_size = PAGE_SIZE << max_order; + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE), + "buddy_init failed\n"); + + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); + + for (top = max_order; top; top--) { + /* Make room by freeing the largest allocated block */ + block = list_first_entry_or_null(&blocks, typeof(*block), link); + if (block) { + list_del(&block->link); + drm_buddy_free_block(&mm, block); + } + + for (order = top; order--;) { + size = get_size(order, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, + mm_size, size, size, + &tmp, flags), + "buddy_alloc hit -ENOMEM with order=%d, top=%d\n", + order, top); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_move_tail(&block->link, &blocks); + } + + /* There should be one final page for this sub-allocation */ + size = get_size(0, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc hit -ENOMEM for hole\n"); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_move_tail(&block->link, &holes); + + size = get_size(top, PAGE_SIZE); + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!", + top, max_order); + } + + drm_buddy_free_list(&mm, &holes); + + /* Nothing larger than blocks of chunk_size now available */ + for (order = 1; order <= max_order; order++) { + size = get_size(order, PAGE_SIZE); + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc unexpectedly succeeded at order %d, it should be full!", + order); + } + + list_splice_tail(&holes, &blocks); + drm_buddy_free_list(&mm, &blocks); + drm_buddy_fini(&mm); +} + +static void igt_buddy_alloc_smoke(struct kunit *test) +{ + u64 mm_size, chunk_size, start = 0; + unsigned long flags = 0; + struct drm_buddy mm; + int *order; + int i; + + DRM_RND_STATE(prng, random_seed); + IGT_TIMEOUT(end_time); + + igt_mm_config(&mm_size, &chunk_size); + + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, chunk_size), + "buddy_init failed\n"); + + order = drm_random_order(mm.max_order + 1, &prng); + KUNIT_ASSERT_TRUE(test, order); + + for (i = 0; i <= mm.max_order; ++i) { + struct drm_buddy_block *block; + int max_order = order[i]; + bool timeout = false; + LIST_HEAD(blocks); + u64 total, size; + LIST_HEAD(tmp); + int order, err; + + KUNIT_ASSERT_FALSE_MSG(test, igt_check_mm(test, &mm), + "pre-mm check failed, abort\n"); + + order = max_order; + total = 0; + + do { +retry: + size = get_size(order, chunk_size); + err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags); + if (err) { + if (err == -ENOMEM) { + KUNIT_FAIL(test, "buddy_alloc hit -ENOMEM with order=%d\n", + order); + } else { + if (order--) { + err = 0; + goto retry; + } + + KUNIT_FAIL(test, "buddy_alloc with order=%d failed\n", + order); + } + + break; + } + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_move_tail(&block->link, &blocks); + KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), order, + "buddy_alloc order mismatch\n"); + + total += drm_buddy_block_size(&mm, block); + + if (__igt_timeout(end_time, NULL)) { + timeout = true; + break; + } + } while (total < mm.size); + + if (!err) + err = igt_check_blocks(test, &mm, &blocks, total, false); + + drm_buddy_free_list(&mm, &blocks); + + if (!err) { + KUNIT_EXPECT_FALSE_MSG(test, igt_check_mm(test, &mm), + "post-mm check failed\n"); + } + + if (err || timeout) + break; + + cond_resched(); + } + + kfree(order); + drm_buddy_fini(&mm); +} + +static void igt_buddy_alloc_pessimistic(struct kunit *test) +{ + u64 mm_size, size, start = 0; + struct drm_buddy_block *block, *bn; + const unsigned int max_order = 16; + unsigned long flags = 0; + struct drm_buddy mm; + unsigned int order; + LIST_HEAD(blocks); + LIST_HEAD(tmp); + + /* + * Create a pot-sized mm, then allocate one of each possible + * order within. This should leave the mm with exactly one + * page left. + */ + + mm_size = PAGE_SIZE << max_order; + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE), + "buddy_init failed\n"); + + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); + + for (order = 0; order < max_order; order++) { + size = get_size(order, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc hit -ENOMEM with order=%d\n", + order); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_move_tail(&block->link, &blocks); + } + + /* And now the last remaining block available */ + size = get_size(0, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc hit -ENOMEM on final alloc\n"); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_move_tail(&block->link, &blocks); + + /* Should be completely full! */ + for (order = max_order; order--;) { + size = get_size(order, PAGE_SIZE); + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc unexpectedly succeeded, it should be full!"); + } + + block = list_last_entry(&blocks, typeof(*block), link); + list_del(&block->link); + drm_buddy_free_block(&mm, block); + + /* As we free in increasing size, we make available larger blocks */ + order = 1; + list_for_each_entry_safe(block, bn, &blocks, link) { + list_del(&block->link); + drm_buddy_free_block(&mm, block); + + size = get_size(order, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc hit -ENOMEM with order=%d\n", + order); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_del(&block->link); + drm_buddy_free_block(&mm, block); + order++; + } + + /* To confirm, now the whole mm should be available */ + size = get_size(max_order, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc (realloc) hit -ENOMEM with order=%d\n", + max_order); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_del(&block->link); + drm_buddy_free_block(&mm, block); + drm_buddy_free_list(&mm, &blocks); + drm_buddy_fini(&mm); +} + +static void igt_buddy_alloc_optimistic(struct kunit *test) +{ + u64 mm_size, size, start = 0; + struct drm_buddy_block *block; + unsigned long flags = 0; + const int max_order = 16; + struct drm_buddy mm; + LIST_HEAD(blocks); + LIST_HEAD(tmp); + int order; + + /* + * Create a mm with one block of each order available, and + * try to allocate them all. + */ + + mm_size = PAGE_SIZE * ((1 << (max_order + 1)) - 1); + + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE), + "buddy_init failed\n"); + + KUNIT_EXPECT_EQ(test, mm.max_order, max_order); + + for (order = 0; order <= max_order; order++) { + size = get_size(order, PAGE_SIZE); + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc hit -ENOMEM with order=%d\n", + order); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n"); + + list_move_tail(&block->link, &blocks); + } + + /* Should be completely full! */ + size = get_size(0, PAGE_SIZE); + KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size, + size, size, &tmp, flags), + "buddy_alloc unexpectedly succeeded, it should be full!"); + + drm_buddy_free_list(&mm, &blocks); + drm_buddy_fini(&mm); +} + +static void igt_buddy_alloc_range(struct kunit *test) +{ + unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION; + u64 offset, size, rem, chunk_size, end; + unsigned long page_num; + struct drm_buddy mm; + LIST_HEAD(blocks); + + igt_mm_config(&size, &chunk_size); + + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, size, chunk_size), + "buddy_init failed"); + + KUNIT_ASSERT_FALSE_MSG(test, igt_check_mm(test, &mm), + "pre-mm check failed, abort!"); + + rem = mm.size; + offset = 0; + + for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) { + struct drm_buddy_block *block; + LIST_HEAD(tmp); + + size = min(page_num * mm.chunk_size, rem); + end = offset + size; + + KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, offset, end, + size, mm.chunk_size, + &tmp, flags), + "alloc_range with offset=%llx, size=%llx failed\n", offset, size); + + block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link); + KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_range has no blocks\n"); + + KUNIT_ASSERT_EQ_MSG(test, drm_buddy_block_offset(block), offset, + "alloc_range start offset mismatch, found=%llx, expected=%llx\n", + drm_buddy_block_offset(block), offset); + + KUNIT_ASSERT_FALSE(test, igt_check_blocks(test, &mm, &tmp, size, true)); + + list_splice_tail(&tmp, &blocks); + + offset += size; + + rem -= size; + if (!rem) + break; + + cond_resched(); + } + + drm_buddy_free_list(&mm, &blocks); + + KUNIT_EXPECT_FALSE_MSG(test, igt_check_mm(test, &mm), "post-mm check failed\n"); + + drm_buddy_fini(&mm); +} + +static void igt_buddy_alloc_limit(struct kunit *test) +{ + u64 size = U64_MAX, start = 0; + struct drm_buddy_block *block; + unsigned long flags = 0; + LIST_HEAD(allocated); + struct drm_buddy mm; + + KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, size, PAGE_SIZE)); + + KUNIT_EXPECT_EQ_MSG(test, mm.max_order, DRM_BUDDY_MAX_ORDER, + "mm.max_order(%d) != %d\n", mm.max_order, + DRM_BUDDY_MAX_ORDER); + + size = mm.chunk_size << mm.max_order; + KUNIT_EXPECT_FALSE(test, drm_buddy_alloc_blocks(&mm, start, size, size, + PAGE_SIZE, &allocated, flags)); + + block = list_first_entry_or_null(&allocated, struct drm_buddy_block, link); + KUNIT_EXPECT_TRUE(test, block); + + KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), mm.max_order, + "block order(%d) != %d\n", + drm_buddy_block_order(block), mm.max_order); + + KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_size(&mm, block), + BIT_ULL(mm.max_order) * PAGE_SIZE, + "block size(%llu) != %llu\n", + drm_buddy_block_size(&mm, block), + BIT_ULL(mm.max_order) * PAGE_SIZE); + + drm_buddy_free_list(&mm, &allocated); + drm_buddy_fini(&mm); +} + +static int drm_buddy_init_test(struct kunit *test) +{ + while (!random_seed) + random_seed = get_random_int(); + + return 0; +} + +static struct kunit_case drm_buddy_tests[] = { + KUNIT_CASE(igt_buddy_alloc_limit), + KUNIT_CASE(igt_buddy_alloc_range), + KUNIT_CASE(igt_buddy_alloc_optimistic), + KUNIT_CASE(igt_buddy_alloc_pessimistic), + KUNIT_CASE(igt_buddy_alloc_smoke), + KUNIT_CASE(igt_buddy_alloc_pathological), + {} +}; + +static struct kunit_suite drm_buddy_test_suite = { + .name = "drm_buddy", + .init = drm_buddy_init_test, + .test_cases = drm_buddy_tests, +}; + +kunit_test_suite(drm_buddy_test_suite); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From fc8d29e298cf47e07c2764ec1c340c1df8e50431 Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Fri, 8 Jul 2022 17:30:52 -0300 Subject: drm: selftest: convert drm_mm selftest to KUnit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Considering the current adoption of the KUnit framework, convert the DRM mm selftest to the KUnit API. Signed-off-by: Arthur Grillo Tested-by: David Gow Acked-by: Daniel Latypov Reviewed-by: Javier Martinez Canillas Signed-off-by: Maíra Canal Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-10-maira.canal@usp.br --- Documentation/gpu/todo.rst | 11 - drivers/gpu/drm/Kconfig | 20 - drivers/gpu/drm/Makefile | 1 - drivers/gpu/drm/selftests/Makefile | 2 - drivers/gpu/drm/selftests/drm_mm_selftests.h | 28 - drivers/gpu/drm/selftests/drm_selftest.c | 109 -- drivers/gpu/drm/selftests/drm_selftest.h | 41 - drivers/gpu/drm/selftests/test-drm_mm.c | 2487 -------------------------- drivers/gpu/drm/tests/Makefile | 2 +- drivers/gpu/drm/tests/drm_mm_test.c | 2255 +++++++++++++++++++++++ 10 files changed, 2256 insertions(+), 2700 deletions(-) delete mode 100644 drivers/gpu/drm/selftests/Makefile delete mode 100644 drivers/gpu/drm/selftests/drm_mm_selftests.h delete mode 100644 drivers/gpu/drm/selftests/drm_selftest.c delete mode 100644 drivers/gpu/drm/selftests/drm_selftest.h delete mode 100644 drivers/gpu/drm/selftests/test-drm_mm.c create mode 100644 drivers/gpu/drm/tests/drm_mm_test.c (limited to 'drivers/gpu/drm') diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 513b20ccef1e..10bfb50908d1 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -617,17 +617,6 @@ Contact: Javier Martinez Canillas Level: Intermediate -Convert Kernel Selftests (kselftest) to KUnit tests when appropriate --------------------------------------------------------------------- - -Many of the `Kselftest `_ -tests in DRM could be converted to Kunit tests instead, since that framework -is more suitable for unit testing. - -Contact: Javier Martinez Canillas - -Level: Starter - Enable trinity for DRM ---------------------- diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 06822ecf51c6..1c91e1e861a5 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -50,26 +50,6 @@ config DRM_DEBUG_MM If in doubt, say "N". -config DRM_DEBUG_SELFTEST - tristate "kselftests for DRM" - depends on DRM - depends on DEBUG_KERNEL - select PRIME_NUMBERS - select DRM_DISPLAY_DP_HELPER - select DRM_DISPLAY_HELPER - select DRM_LIB_RANDOM - select DRM_KMS_HELPER - select DRM_BUDDY - select DRM_EXPORT_FOR_TESTS if m - default n - help - This option provides kernel modules that can be used to run - various selftests on parts of the DRM api. This option is not - useful for distributions or general kernels, but only for kernel - developers working on DRM and associated drivers. - - If in doubt, say "N". - config DRM_KUNIT_TEST tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS depends on DRM && KUNIT diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index e7af358e6dda..25016dcab55e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -75,7 +75,6 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o # Drivers and the rest # -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/ obj-$(CONFIG_DRM_KUNIT_TEST) += tests/ obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o diff --git a/drivers/gpu/drm/selftests/Makefile b/drivers/gpu/drm/selftests/Makefile deleted file mode 100644 index a4ebecb8146b..000000000000 --- a/drivers/gpu/drm/selftests/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o diff --git a/drivers/gpu/drm/selftests/drm_mm_selftests.h b/drivers/gpu/drm/selftests/drm_mm_selftests.h deleted file mode 100644 index 8c87c964176b..000000000000 --- a/drivers/gpu/drm/selftests/drm_mm_selftests.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* List each unit test as selftest(name, function) - * - * The name is used as both an enum and expanded as igt__name to create - * a module parameter. It must be unique and legal for a C identifier. - * - * Tests are executed in order by igt/drm_mm - */ -selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */ -selftest(init, igt_init) -selftest(debug, igt_debug) -selftest(reserve, igt_reserve) -selftest(insert, igt_insert) -selftest(replace, igt_replace) -selftest(insert_range, igt_insert_range) -selftest(align, igt_align) -selftest(frag, igt_frag) -selftest(align32, igt_align32) -selftest(align64, igt_align64) -selftest(evict, igt_evict) -selftest(evict_range, igt_evict_range) -selftest(bottomup, igt_bottomup) -selftest(lowest, igt_lowest) -selftest(topdown, igt_topdown) -selftest(highest, igt_highest) -selftest(color, igt_color) -selftest(color_evict, igt_color_evict) -selftest(color_evict_range, igt_color_evict_range) diff --git a/drivers/gpu/drm/selftests/drm_selftest.c b/drivers/gpu/drm/selftests/drm_selftest.c deleted file mode 100644 index e29ed9faef5b..000000000000 --- a/drivers/gpu/drm/selftests/drm_selftest.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright © 2016 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include - -#define selftest(name, func) __idx_##name, -enum { -#include TESTS -}; -#undef selftest - -#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f }, -static struct drm_selftest { - bool enabled; - const char *name; - int (*func)(void *); -} selftests[] = { -#include TESTS -}; -#undef selftest - -/* Embed the line number into the parameter name so that we can order tests */ -#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n)) -#define selftest_0(n, func, id) \ -module_param_named(id, selftests[__idx_##n].enabled, bool, 0400); -#define selftest(n, func) selftest_0(n, func, param(n)) -#include TESTS -#undef selftest - -static void set_default_test_all(struct drm_selftest *st, unsigned long count) -{ - unsigned long i; - - for (i = 0; i < count; i++) - if (st[i].enabled) - return; - - for (i = 0; i < count; i++) - st[i].enabled = true; -} - -static int run_selftests(struct drm_selftest *st, - unsigned long count, - void *data) -{ - int err = 0; - - set_default_test_all(st, count); - - /* Tests are listed in natural order in drm_*_selftests.h */ - for (; count--; st++) { - if (!st->enabled) - continue; - - pr_debug("drm: Running %s\n", st->name); - err = st->func(data); - if (err) - break; - } - - if (WARN(err > 0 || err == -ENOTTY, - "%s returned %d, conflicting with selftest's magic values!\n", - st->name, err)) - err = -1; - - rcu_barrier(); - return err; -} - -static int __maybe_unused -__drm_subtests(const char *caller, - const struct drm_subtest *st, - int count, - void *data) -{ - int err; - - for (; count--; st++) { - pr_debug("Running %s/%s\n", caller, st->name); - err = st->func(data); - if (err) { - pr_err("%s: %s failed with error %d\n", - caller, st->name, err); - return err; - } - } - - return 0; -} diff --git a/drivers/gpu/drm/selftests/drm_selftest.h b/drivers/gpu/drm/selftests/drm_selftest.h deleted file mode 100644 index c784ec02ff53..000000000000 --- a/drivers/gpu/drm/selftests/drm_selftest.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © 2016 Intel Corporation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef __DRM_SELFTEST_H__ -#define __DRM_SELFTEST_H__ - -struct drm_subtest { - int (*func)(void *data); - const char *name; -}; - -static int __drm_subtests(const char *caller, - const struct drm_subtest *st, - int count, - void *data); -#define drm_subtests(T, data) \ - __drm_subtests(__func__, T, ARRAY_SIZE(T), data) - -#define SUBTEST(x) { x, #x } - -#endif /* __DRM_SELFTEST_H__ */ diff --git a/drivers/gpu/drm/selftests/test-drm_mm.c b/drivers/gpu/drm/selftests/test-drm_mm.c deleted file mode 100644 index b768b53c4aee..000000000000 --- a/drivers/gpu/drm/selftests/test-drm_mm.c +++ /dev/null @@ -1,2487 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Test cases for the drm_mm range manager - */ - -#define pr_fmt(fmt) "drm_mm: " fmt - -#include -#include -#include -#include -#include -#include - -#include - -#include "../lib/drm_random.h" - -#define TESTS "drm_mm_selftests.h" -#include "drm_selftest.h" - -static unsigned int random_seed; -static unsigned int max_iterations = 8192; -static unsigned int max_prime = 128; - -enum { - BEST, - BOTTOMUP, - TOPDOWN, - EVICT, -}; - -static const struct insert_mode { - const char *name; - enum drm_mm_insert_mode mode; -} insert_modes[] = { - [BEST] = { "best", DRM_MM_INSERT_BEST }, - [BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW }, - [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH }, - [EVICT] = { "evict", DRM_MM_INSERT_EVICT }, - {} -}, evict_modes[] = { - { "bottom-up", DRM_MM_INSERT_LOW }, - { "top-down", DRM_MM_INSERT_HIGH }, - {} -}; - -static int igt_sanitycheck(void *ignored) -{ - pr_info("%s - ok!\n", __func__); - return 0; -} - -static bool assert_no_holes(const struct drm_mm *mm) -{ - struct drm_mm_node *hole; - u64 hole_start, __always_unused hole_end; - unsigned long count; - - count = 0; - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) - count++; - if (count) { - pr_err("Expected to find no holes (after reserve), found %lu instead\n", count); - return false; - } - - drm_mm_for_each_node(hole, mm) { - if (drm_mm_hole_follows(hole)) { - pr_err("Hole follows node, expected none!\n"); - return false; - } - } - - return true; -} - -static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end) -{ - struct drm_mm_node *hole; - u64 hole_start, hole_end; - unsigned long count; - bool ok = true; - - if (end <= start) - return true; - - count = 0; - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (start != hole_start || end != hole_end) { - if (ok) - pr_err("empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n", - hole_start, hole_end, - start, end); - ok = false; - } - count++; - } - if (count != 1) { - pr_err("Expected to find one hole, found %lu instead\n", count); - ok = false; - } - - return ok; -} - -static bool assert_continuous(const struct drm_mm *mm, u64 size) -{ - struct drm_mm_node *node, *check, *found; - unsigned long n; - u64 addr; - - if (!assert_no_holes(mm)) - return false; - - n = 0; - addr = 0; - drm_mm_for_each_node(node, mm) { - if (node->start != addr) { - pr_err("node[%ld] list out of order, expected %llx found %llx\n", - n, addr, node->start); - return false; - } - - if (node->size != size) { - pr_err("node[%ld].size incorrect, expected %llx, found %llx\n", - n, size, node->size); - return false; - } - - if (drm_mm_hole_follows(node)) { - pr_err("node[%ld] is followed by a hole!\n", n); - return false; - } - - found = NULL; - drm_mm_for_each_node_in_range(check, mm, addr, addr + size) { - if (node != check) { - pr_err("lookup return wrong node, expected start %llx, found %llx\n", - node->start, check->start); - return false; - } - found = check; - } - if (!found) { - pr_err("lookup failed for node %llx + %llx\n", - addr, size); - return false; - } - - addr += size; - n++; - } - - return true; -} - -static u64 misalignment(struct drm_mm_node *node, u64 alignment) -{ - u64 rem; - - if (!alignment) - return 0; - - div64_u64_rem(node->start, alignment, &rem); - return rem; -} - -static bool assert_node(struct drm_mm_node *node, struct drm_mm *mm, - u64 size, u64 alignment, unsigned long color) -{ - bool ok = true; - - if (!drm_mm_node_allocated(node) || node->mm != mm) { - pr_err("node not allocated\n"); - ok = false; - } - - if (node->size != size) { - pr_err("node has wrong size, found %llu, expected %llu\n", - node->size, size); - ok = false; - } - - if (misalignment(node, alignment)) { - pr_err("node is misaligned, start %llx rem %llu, expected alignment %llu\n", - node->start, misalignment(node, alignment), alignment); - ok = false; - } - - if (node->color != color) { - pr_err("node has wrong color, found %lu, expected %lu\n", - node->color, color); - ok = false; - } - - return ok; -} - -#define show_mm(mm) do { \ - struct drm_printer __p = drm_debug_printer(__func__); \ - drm_mm_print((mm), &__p); } while (0) - -static int igt_init(void *ignored) -{ - const unsigned int size = 4096; - struct drm_mm mm; - struct drm_mm_node tmp; - int ret = -EINVAL; - - /* Start with some simple checks on initialising the struct drm_mm */ - memset(&mm, 0, sizeof(mm)); - if (drm_mm_initialized(&mm)) { - pr_err("zeroed mm claims to be initialized\n"); - return ret; - } - - memset(&mm, 0xff, sizeof(mm)); - drm_mm_init(&mm, 0, size); - if (!drm_mm_initialized(&mm)) { - pr_err("mm claims not to be initialized\n"); - goto out; - } - - if (!drm_mm_clean(&mm)) { - pr_err("mm not empty on creation\n"); - goto out; - } - - /* After creation, it should all be one massive hole */ - if (!assert_one_hole(&mm, 0, size)) { - ret = -EINVAL; - goto out; - } - - memset(&tmp, 0, sizeof(tmp)); - tmp.start = 0; - tmp.size = size; - ret = drm_mm_reserve_node(&mm, &tmp); - if (ret) { - pr_err("failed to reserve whole drm_mm\n"); - goto out; - } - - /* After filling the range entirely, there should be no holes */ - if (!assert_no_holes(&mm)) { - ret = -EINVAL; - goto out; - } - - /* And then after emptying it again, the massive hole should be back */ - drm_mm_remove_node(&tmp); - if (!assert_one_hole(&mm, 0, size)) { - ret = -EINVAL; - goto out; - } - -out: - if (ret) - show_mm(&mm); - drm_mm_takedown(&mm); - return ret; -} - -static int igt_debug(void *ignored) -{ - struct drm_mm mm; - struct drm_mm_node nodes[2]; - int ret; - - /* Create a small drm_mm with a couple of nodes and a few holes, and - * check that the debug iterator doesn't explode over a trivial drm_mm. - */ - - drm_mm_init(&mm, 0, 4096); - - memset(nodes, 0, sizeof(nodes)); - nodes[0].start = 512; - nodes[0].size = 1024; - ret = drm_mm_reserve_node(&mm, &nodes[0]); - if (ret) { - pr_err("failed to reserve node[0] {start=%lld, size=%lld)\n", - nodes[0].start, nodes[0].size); - return ret; - } - - nodes[1].size = 1024; - nodes[1].start = 4096 - 512 - nodes[1].size; - ret = drm_mm_reserve_node(&mm, &nodes[1]); - if (ret) { - pr_err("failed to reserve node[1] {start=%lld, size=%lld)\n", - nodes[1].start, nodes[1].size); - return ret; - } - - show_mm(&mm); - return 0; -} - -static struct drm_mm_node *set_node(struct drm_mm_node *node, - u64 start, u64 size) -{ - node->start = start; - node->size = size; - return node; -} - -static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node) -{ - int err; - - err = drm_mm_reserve_node(mm, node); - if (likely(err == -ENOSPC)) - return true; - - if (!err) { - pr_err("impossible reserve succeeded, node %llu + %llu\n", - node->start, node->size); - drm_mm_remove_node(node); - } else { - pr_err("impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n", - err, -ENOSPC, node->start, node->size); - } - return false; -} - -static bool check_reserve_boundaries(struct drm_mm *mm, - unsigned int count, - u64 size) -{ - const struct boundary { - u64 start, size; - const char *name; - } boundaries[] = { -#define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" } - B(0, 0), - B(-size, 0), - B(size, 0), - B(size * count, 0), - B(-size, size), - B(-size, -size), - B(-size, 2*size), - B(0, -size), - B(size, -size), - B(count*size, size), - B(count*size, -size), - B(count*size, count*size), - B(count*size, -count*size), - B(count*size, -(count+1)*size), - B((count+1)*size, size), - B((count+1)*size, -size), - B((count+1)*size, -2*size), -#undef B - }; - struct drm_mm_node tmp = {}; - int n; - - for (n = 0; n < ARRAY_SIZE(boundaries); n++) { - if (!expect_reserve_fail(mm, - set_node(&tmp, - boundaries[n].start, - boundaries[n].size))) { - pr_err("boundary[%d:%s] failed, count=%u, size=%lld\n", - n, boundaries[n].name, count, size); - return false; - } - } - - return true; -} - -static int __igt_reserve(unsigned int count, u64 size) -{ - DRM_RND_STATE(prng, random_seed); - struct drm_mm mm; - struct drm_mm_node tmp, *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - int ret, err; - - /* For exercising drm_mm_reserve_node(), we want to check that - * reservations outside of the drm_mm range are rejected, and to - * overlapping and otherwise already occupied ranges. Afterwards, - * the tree and nodes should be intact. - */ - - DRM_MM_BUG_ON(!count); - DRM_MM_BUG_ON(!size); - - ret = -ENOMEM; - order = drm_random_order(count, &prng); - if (!order) - goto err; - - nodes = vzalloc(array_size(count, sizeof(*nodes))); - if (!nodes) - goto err_order; - - ret = -EINVAL; - drm_mm_init(&mm, 0, count * size); - - if (!check_reserve_boundaries(&mm, count, size)) - goto out; - - for (n = 0; n < count; n++) { - nodes[n].start = order[n] * size; - nodes[n].size = size; - - err = drm_mm_reserve_node(&mm, &nodes[n]); - if (err) { - pr_err("reserve failed, step %d, start %llu\n", - n, nodes[n].start); - ret = err; - goto out; - } - - if (!drm_mm_node_allocated(&nodes[n])) { - pr_err("reserved node not allocated! step %d, start %llu\n", - n, nodes[n].start); - goto out; - } - - if (!expect_reserve_fail(&mm, &nodes[n])) - goto out; - } - - /* After random insertion the nodes should be in order */ - if (!assert_continuous(&mm, size)) - goto out; - - /* Repeated use should then fail */ - drm_random_reorder(order, count, &prng); - for (n = 0; n < count; n++) { - if (!expect_reserve_fail(&mm, - set_node(&tmp, order[n] * size, 1))) - goto out; - - /* Remove and reinsert should work */ - drm_mm_remove_node(&nodes[order[n]]); - err = drm_mm_reserve_node(&mm, &nodes[order[n]]); - if (err) { - pr_err("reserve failed, step %d, start %llu\n", - n, nodes[n].start); - ret = err; - goto out; - } - } - - if (!assert_continuous(&mm, size)) - goto out; - - /* Overlapping use should then fail */ - for (n = 0; n < count; n++) { - if (!expect_reserve_fail(&mm, set_node(&tmp, 0, size*count))) - goto out; - } - for (n = 0; n < count; n++) { - if (!expect_reserve_fail(&mm, - set_node(&tmp, - size * n, - size * (count - n)))) - goto out; - } - - /* Remove several, reinsert, check full */ - for_each_prime_number(n, min(max_prime, count)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - } - - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - err = drm_mm_reserve_node(&mm, node); - if (err) { - pr_err("reserve failed, step %d/%d, start %llu\n", - m, n, node->start); - ret = err; - goto out; - } - } - - o += n; - - if (!assert_continuous(&mm, size)) - goto out; - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); -err_order: - kfree(order); -err: - return ret; -} - -static int igt_reserve(void *ignored) -{ - const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); - int n, ret; - - for_each_prime_number_from(n, 1, 54) { - u64 size = BIT_ULL(n); - - ret = __igt_reserve(count, size - 1); - if (ret) - return ret; - - ret = __igt_reserve(count, size); - if (ret) - return ret; - - ret = __igt_reserve(count, size + 1); - if (ret) - return ret; - - cond_resched(); - } - - return 0; -} - -static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, - u64 size, u64 alignment, unsigned long color, - const struct insert_mode *mode) -{ - int err; - - err = drm_mm_insert_node_generic(mm, node, - size, alignment, color, - mode->mode); - if (err) { - pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n", - size, alignment, color, mode->name, err); - return false; - } - - if (!assert_node(node, mm, size, alignment, color)) { - drm_mm_remove_node(node); - return false; - } - - return true; -} - -static bool expect_insert_fail(struct drm_mm *mm, u64 size) -{ - struct drm_mm_node tmp = {}; - int err; - - err = drm_mm_insert_node(mm, &tmp, size); - if (likely(err == -ENOSPC)) - return true; - - if (!err) { - pr_err("impossible insert succeeded, node %llu + %llu\n", - tmp.start, tmp.size); - drm_mm_remove_node(&tmp); - } else { - pr_err("impossible insert failed with wrong error %d [expected %d], size %llu\n", - err, -ENOSPC, size); - } - return false; -} - -static int __igt_insert(unsigned int count, u64 size, bool replace) -{ - DRM_RND_STATE(prng, random_seed); - const struct insert_mode *mode; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - int ret; - - /* Fill a range with lots of nodes, check it doesn't fail too early */ - - DRM_MM_BUG_ON(!count); - DRM_MM_BUG_ON(!size); - - ret = -ENOMEM; - nodes = vmalloc(array_size(count, sizeof(*nodes))); - if (!nodes) - goto err; - - order = drm_random_order(count, &prng); - if (!order) - goto err_nodes; - - ret = -EINVAL; - drm_mm_init(&mm, 0, count * size); - - for (mode = insert_modes; mode->name; mode++) { - for (n = 0; n < count; n++) { - struct drm_mm_node tmp; - - node = replace ? &tmp : &nodes[n]; - memset(node, 0, sizeof(*node)); - if (!expect_insert(&mm, node, size, 0, n, mode)) { - pr_err("%s insert failed, size %llu step %d\n", - mode->name, size, n); - goto out; - } - - if (replace) { - drm_mm_replace_node(&tmp, &nodes[n]); - if (drm_mm_node_allocated(&tmp)) { - pr_err("replaced old-node still allocated! step %d\n", - n); - goto out; - } - - if (!assert_node(&nodes[n], &mm, size, 0, n)) { - pr_err("replaced node did not inherit parameters, size %llu step %d\n", - size, n); - goto out; - } - - if (tmp.start != nodes[n].start) { - pr_err("replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n", - tmp.start, size, - nodes[n].start, nodes[n].size); - goto out; - } - } - } - - /* After random insertion the nodes should be in order */ - if (!assert_continuous(&mm, size)) - goto out; - - /* Repeated use should then fail */ - if (!expect_insert_fail(&mm, size)) - goto out; - - /* Remove one and reinsert, as the only hole it should refill itself */ - for (n = 0; n < count; n++) { - u64 addr = nodes[n].start; - - drm_mm_remove_node(&nodes[n]); - if (!expect_insert(&mm, &nodes[n], size, 0, n, mode)) { - pr_err("%s reinsert failed, size %llu step %d\n", - mode->name, size, n); - goto out; - } - - if (nodes[n].start != addr) { - pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n", - mode->name, n, addr, nodes[n].start); - goto out; - } - - if (!assert_continuous(&mm, size)) - goto out; - } - - /* Remove several, reinsert, check full */ - for_each_prime_number(n, min(max_prime, count)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - } - - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - if (!expect_insert(&mm, node, size, 0, n, mode)) { - pr_err("%s multiple reinsert failed, size %llu step %d\n", - mode->name, size, n); - goto out; - } - } - - o += n; - - if (!assert_continuous(&mm, size)) - goto out; - - if (!expect_insert_fail(&mm, size)) - goto out; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -err: - return ret; -} - -static int igt_insert(void *ignored) -{ - const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); - unsigned int n; - int ret; - - for_each_prime_number_from(n, 1, 54) { - u64 size = BIT_ULL(n); - - ret = __igt_insert(count, size - 1, false); - if (ret) - return ret; - - ret = __igt_insert(count, size, false); - if (ret) - return ret; - - ret = __igt_insert(count, size + 1, false); - if (ret) - return ret; - - cond_resched(); - } - - return 0; -} - -static int igt_replace(void *ignored) -{ - const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); - unsigned int n; - int ret; - - /* Reuse igt_insert to exercise replacement by inserting a dummy node, - * then replacing it with the intended node. We want to check that - * the tree is intact and all the information we need is carried - * across to the target node. - */ - - for_each_prime_number_from(n, 1, 54) { - u64 size = BIT_ULL(n); - - ret = __igt_insert(count, size - 1, true); - if (ret) - return ret; - - ret = __igt_insert(count, size, true); - if (ret) - return ret; - - ret = __igt_insert(count, size + 1, true); - if (ret) - return ret; - - cond_resched(); - } - - return 0; -} - -static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node, - u64 size, u64 alignment, unsigned long color, - u64 range_start, u64 range_end, - const struct insert_mode *mode) -{ - int err; - - err = drm_mm_insert_node_in_range(mm, node, - size, alignment, color, - range_start, range_end, - mode->mode); - if (err) { - pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n", - size, alignment, color, mode->name, - range_start, range_end, err); - return false; - } - - if (!assert_node(node, mm, size, alignment, color)) { - drm_mm_remove_node(node); - return false; - } - - return true; -} - -static bool expect_insert_in_range_fail(struct drm_mm *mm, - u64 size, - u64 range_start, - u64 range_end) -{ - struct drm_mm_node tmp = {}; - int err; - - err = drm_mm_insert_node_in_range(mm, &tmp, - size, 0, 0, - range_start, range_end, - 0); - if (likely(err == -ENOSPC)) - return true; - - if (!err) { - pr_err("impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n", - tmp.start, tmp.size, range_start, range_end); - drm_mm_remove_node(&tmp); - } else { - pr_err("impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n", - err, -ENOSPC, size, range_start, range_end); - } - - return false; -} - -static bool assert_contiguous_in_range(struct drm_mm *mm, - u64 size, - u64 start, - u64 end) -{ - struct drm_mm_node *node; - unsigned int n; - - if (!expect_insert_in_range_fail(mm, size, start, end)) - return false; - - n = div64_u64(start + size - 1, size); - drm_mm_for_each_node(node, mm) { - if (node->start < start || node->start + node->size > end) { - pr_err("node %d out of range, address [%llx + %llu], range [%llx, %llx]\n", - n, node->start, node->start + node->size, start, end); - return false; - } - - if (node->start != n * size) { - pr_err("node %d out of order, expected start %llx, found %llx\n", - n, n * size, node->start); - return false; - } - - if (node->size != size) { - pr_err("node %d has wrong size, expected size %llx, found %llx\n", - n, size, node->size); - return false; - } - - if (drm_mm_hole_follows(node) && - drm_mm_hole_node_end(node) < end) { - pr_err("node %d is followed by a hole!\n", n); - return false; - } - - n++; - } - - if (start > 0) { - node = __drm_mm_interval_first(mm, 0, start - 1); - if (drm_mm_node_allocated(node)) { - pr_err("node before start: node=%llx+%llu, start=%llx\n", - node->start, node->size, start); - return false; - } - } - - if (end < U64_MAX) { - node = __drm_mm_interval_first(mm, end, U64_MAX); - if (drm_mm_node_allocated(node)) { - pr_err("node after end: node=%llx+%llu, end=%llx\n", - node->start, node->size, end); - return false; - } - } - - return true; -} - -static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) -{ - const struct insert_mode *mode; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int n, start_n, end_n; - int ret; - - DRM_MM_BUG_ON(!count); - DRM_MM_BUG_ON(!size); - DRM_MM_BUG_ON(end <= start); - - /* Very similar to __igt_insert(), but now instead of populating the - * full range of the drm_mm, we try to fill a small portion of it. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(count, sizeof(*nodes))); - if (!nodes) - goto err; - - ret = -EINVAL; - drm_mm_init(&mm, 0, count * size); - - start_n = div64_u64(start + size - 1, size); - end_n = div64_u64(end - size, size); - - for (mode = insert_modes; mode->name; mode++) { - for (n = start_n; n <= end_n; n++) { - if (!expect_insert_in_range(&mm, &nodes[n], - size, size, n, - start, end, mode)) { - pr_err("%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n", - mode->name, size, n, - start_n, end_n, - start, end); - goto out; - } - } - - if (!assert_contiguous_in_range(&mm, size, start, end)) { - pr_err("%s: range [%llx, %llx] not full after initialisation, size=%llu\n", - mode->name, start, end, size); - goto out; - } - - /* Remove one and reinsert, it should refill itself */ - for (n = start_n; n <= end_n; n++) { - u64 addr = nodes[n].start; - - drm_mm_remove_node(&nodes[n]); - if (!expect_insert_in_range(&mm, &nodes[n], - size, size, n, - start, end, mode)) { - pr_err("%s reinsert failed, step %d\n", mode->name, n); - goto out; - } - - if (nodes[n].start != addr) { - pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n", - mode->name, n, addr, nodes[n].start); - goto out; - } - } - - if (!assert_contiguous_in_range(&mm, size, start, end)) { - pr_err("%s: range [%llx, %llx] not full after reinsertion, size=%llu\n", - mode->name, start, end, size); - goto out; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); -err: - return ret; -} - -static int insert_outside_range(void) -{ - struct drm_mm mm; - const unsigned int start = 1024; - const unsigned int end = 2048; - const unsigned int size = end - start; - - drm_mm_init(&mm, start, size); - - if (!expect_insert_in_range_fail(&mm, 1, 0, start)) - return -EINVAL; - - if (!expect_insert_in_range_fail(&mm, size, - start - size/2, start + (size+1)/2)) - return -EINVAL; - - if (!expect_insert_in_range_fail(&mm, size, - end - (size+1)/2, end + size/2)) - return -EINVAL; - - if (!expect_insert_in_range_fail(&mm, 1, end, end + size)) - return -EINVAL; - - drm_mm_takedown(&mm); - return 0; -} - -static int igt_insert_range(void *ignored) -{ - const unsigned int count = min_t(unsigned int, BIT(13), max_iterations); - unsigned int n; - int ret; - - /* Check that requests outside the bounds of drm_mm are rejected. */ - ret = insert_outside_range(); - if (ret) - return ret; - - for_each_prime_number_from(n, 1, 50) { - const u64 size = BIT_ULL(n); - const u64 max = count * size; - - ret = __igt_insert_range(count, size, 0, max); - if (ret) - return ret; - - ret = __igt_insert_range(count, size, 1, max); - if (ret) - return ret; - - ret = __igt_insert_range(count, size, 0, max - 1); - if (ret) - return ret; - - ret = __igt_insert_range(count, size, 0, max/2); - if (ret) - return ret; - - ret = __igt_insert_range(count, size, max/2, max); - if (ret) - return ret; - - ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1); - if (ret) - return ret; - - cond_resched(); - } - - return 0; -} - -static int prepare_igt_frag(struct drm_mm *mm, - struct drm_mm_node *nodes, - unsigned int num_insert, - const struct insert_mode *mode) -{ - unsigned int size = 4096; - unsigned int i; - - for (i = 0; i < num_insert; i++) { - if (!expect_insert(mm, &nodes[i], size, 0, i, - mode) != 0) { - pr_err("%s insert failed\n", mode->name); - return -EINVAL; - } - } - - /* introduce fragmentation by freeing every other node */ - for (i = 0; i < num_insert; i++) { - if (i % 2 == 0) - drm_mm_remove_node(&nodes[i]); - } - - return 0; - -} - -static u64 get_insert_time(struct drm_mm *mm, - unsigned int num_insert, - struct drm_mm_node *nodes, - const struct insert_mode *mode) -{ - unsigned int size = 8192; - ktime_t start; - unsigned int i; - - start = ktime_get(); - for (i = 0; i < num_insert; i++) { - if (!expect_insert(mm, &nodes[i], size, 0, i, mode) != 0) { - pr_err("%s insert failed\n", mode->name); - return 0; - } - } - - return ktime_to_ns(ktime_sub(ktime_get(), start)); -} - -static int igt_frag(void *ignored) -{ - struct drm_mm mm; - const struct insert_mode *mode; - struct drm_mm_node *nodes, *node, *next; - unsigned int insert_size = 10000; - unsigned int scale_factor = 4; - int ret = -EINVAL; - - /* We need 4 * insert_size nodes to hold intermediate allocated - * drm_mm nodes. - * 1 times for prepare_igt_frag() - * 1 times for get_insert_time() - * 2 times for get_insert_time() - */ - nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes))); - if (!nodes) - return -ENOMEM; - - /* For BOTTOMUP and TOPDOWN, we first fragment the - * address space using prepare_igt_frag() and then try to verify - * that that insertions scale quadratically from 10k to 20k insertions - */ - drm_mm_init(&mm, 1, U64_MAX - 2); - for (mode = insert_modes; mode->name; mode++) { - u64 insert_time1, insert_time2; - - if (mode->mode != DRM_MM_INSERT_LOW && - mode->mode != DRM_MM_INSERT_HIGH) - continue; - - ret = prepare_igt_frag(&mm, nodes, insert_size, mode); - if (ret) - goto err; - - insert_time1 = get_insert_time(&mm, insert_size, - nodes + insert_size, mode); - if (insert_time1 == 0) - goto err; - - insert_time2 = get_insert_time(&mm, (insert_size * 2), - nodes + insert_size * 2, mode); - if (insert_time2 == 0) - goto err; - - pr_info("%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n", - mode->name, insert_size, insert_size * 2, - insert_time1, insert_time2); - - if (insert_time2 > (scale_factor * insert_time1)) { - pr_err("%s fragmented insert took %llu nsecs more\n", - mode->name, - insert_time2 - (scale_factor * insert_time1)); - goto err; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - } - - ret = 0; -err: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); - - return ret; -} - -static int igt_align(void *ignored) -{ - const struct insert_mode *mode; - const unsigned int max_count = min(8192u, max_prime); - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int prime; - int ret = -EINVAL; - - /* For each of the possible insertion modes, we pick a few - * arbitrary alignments and check that the inserted node - * meets our requirements. - */ - - nodes = vzalloc(array_size(max_count, sizeof(*nodes))); - if (!nodes) - goto err; - - drm_mm_init(&mm, 1, U64_MAX - 2); - - for (mode = insert_modes; mode->name; mode++) { - unsigned int i = 0; - - for_each_prime_number_from(prime, 1, max_count) { - u64 size = next_prime_number(prime); - - if (!expect_insert(&mm, &nodes[i], - size, prime, i, - mode)) { - pr_err("%s insert failed with alignment=%d", - mode->name, prime); - goto out; - } - - i++; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - vfree(nodes); -err: - return ret; -} - -static int igt_align_pot(int max) -{ - struct drm_mm mm; - struct drm_mm_node *node, *next; - int bit; - int ret = -EINVAL; - - /* Check that we can align to the full u64 address space */ - - drm_mm_init(&mm, 1, U64_MAX - 2); - - for (bit = max - 1; bit; bit--) { - u64 align, size; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - ret = -ENOMEM; - goto out; - } - - align = BIT_ULL(bit); - size = BIT_ULL(bit-1) + 1; - if (!expect_insert(&mm, node, - size, align, bit, - &insert_modes[0])) { - pr_err("insert failed with alignment=%llx [%d]", - align, bit); - goto out; - } - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) { - drm_mm_remove_node(node); - kfree(node); - } - drm_mm_takedown(&mm); - return ret; -} - -static int igt_align32(void *ignored) -{ - return igt_align_pot(32); -} - -static int igt_align64(void *ignored) -{ - return igt_align_pot(64); -} - -static void show_scan(const struct drm_mm_scan *scan) -{ - pr_info("scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n", - scan->hit_start, scan->hit_end, - scan->size, scan->alignment, scan->color); -} - -static void show_holes(const struct drm_mm *mm, int count) -{ - u64 hole_start, hole_end; - struct drm_mm_node *hole; - - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - struct drm_mm_node *next = list_next_entry(hole, node_list); - const char *node1 = NULL, *node2 = NULL; - - if (drm_mm_node_allocated(hole)) - node1 = kasprintf(GFP_KERNEL, - "[%llx + %lld, color=%ld], ", - hole->start, hole->size, hole->color); - - if (drm_mm_node_allocated(next)) - node2 = kasprintf(GFP_KERNEL, - ", [%llx + %lld, color=%ld]", - next->start, next->size, next->color); - - pr_info("%sHole [%llx - %llx, size %lld]%s\n", - node1, - hole_start, hole_end, hole_end - hole_start, - node2); - - kfree(node2); - kfree(node1); - - if (!--count) - break; - } -} - -struct evict_node { - struct drm_mm_node node; - struct list_head link; -}; - -static bool evict_nodes(struct drm_mm_scan *scan, - struct evict_node *nodes, - unsigned int *order, - unsigned int count, - bool use_color, - struct list_head *evict_list) -{ - struct evict_node *e, *en; - unsigned int i; - - for (i = 0; i < count; i++) { - e = &nodes[order ? order[i] : i]; - list_add(&e->link, evict_list); - if (drm_mm_scan_add_block(scan, &e->node)) - break; - } - list_for_each_entry_safe(e, en, evict_list, link) { - if (!drm_mm_scan_remove_block(scan, &e->node)) - list_del(&e->link); - } - if (list_empty(evict_list)) { - pr_err("Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n", - scan->size, count, scan->alignment, scan->color); - return false; - } - - list_for_each_entry(e, evict_list, link) - drm_mm_remove_node(&e->node); - - if (use_color) { - struct drm_mm_node *node; - - while ((node = drm_mm_scan_color_evict(scan))) { - e = container_of(node, typeof(*e), node); - drm_mm_remove_node(&e->node); - list_add(&e->link, evict_list); - } - } else { - if (drm_mm_scan_color_evict(scan)) { - pr_err("drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); - return false; - } - } - - return true; -} - -static bool evict_nothing(struct drm_mm *mm, - unsigned int total_size, - struct evict_node *nodes) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - struct drm_mm_node *node; - unsigned int n; - - drm_mm_scan_init(&scan, mm, 1, 0, 0, 0); - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - list_add(&e->link, &evict_list); - drm_mm_scan_add_block(&scan, &e->node); - } - list_for_each_entry(e, &evict_list, link) - drm_mm_scan_remove_block(&scan, &e->node); - - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - - if (!drm_mm_node_allocated(&e->node)) { - pr_err("node[%d] no longer allocated!\n", n); - return false; - } - - e->link.next = NULL; - } - - drm_mm_for_each_node(node, mm) { - e = container_of(node, typeof(*e), node); - e->link.next = &e->link; - } - - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - - if (!e->link.next) { - pr_err("node[%d] no longer connected!\n", n); - return false; - } - } - - return assert_continuous(mm, nodes[0].node.size); -} - -static bool evict_everything(struct drm_mm *mm, - unsigned int total_size, - struct evict_node *nodes) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - unsigned int n; - int err; - - drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0); - for (n = 0; n < total_size; n++) { - e = &nodes[n]; - list_add(&e->link, &evict_list); - if (drm_mm_scan_add_block(&scan, &e->node)) - break; - } - - err = 0; - list_for_each_entry(e, &evict_list, link) { - if (!drm_mm_scan_remove_block(&scan, &e->node)) { - if (!err) { - pr_err("Node %lld not marked for eviction!\n", - e->node.start); - err = -EINVAL; - } - } - } - if (err) - return false; - - list_for_each_entry(e, &evict_list, link) - drm_mm_remove_node(&e->node); - - if (!assert_one_hole(mm, 0, total_size)) - return false; - - list_for_each_entry(e, &evict_list, link) { - err = drm_mm_reserve_node(mm, &e->node); - if (err) { - pr_err("Failed to reinsert node after eviction: start=%llx\n", - e->node.start); - return false; - } - } - - return assert_continuous(mm, nodes[0].node.size); -} - -static int evict_something(struct drm_mm *mm, - u64 range_start, u64 range_end, - struct evict_node *nodes, - unsigned int *order, - unsigned int count, - unsigned int size, - unsigned int alignment, - const struct insert_mode *mode) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - struct drm_mm_node tmp; - int err; - - drm_mm_scan_init_with_range(&scan, mm, - size, alignment, 0, - range_start, range_end, - mode->mode); - if (!evict_nodes(&scan, - nodes, order, count, false, - &evict_list)) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, - DRM_MM_INSERT_EVICT); - if (err) { - pr_err("Failed to insert into eviction hole: size=%d, align=%d\n", - size, alignment); - show_scan(&scan); - show_holes(mm, 3); - return err; - } - - if (tmp.start < range_start || tmp.start + tmp.size > range_end) { - pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", - tmp.start, tmp.size, range_start, range_end); - err = -EINVAL; - } - - if (!assert_node(&tmp, mm, size, alignment, 0) || - drm_mm_hole_follows(&tmp)) { - pr_err("Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n", - tmp.size, size, - alignment, misalignment(&tmp, alignment), - tmp.start, drm_mm_hole_follows(&tmp)); - err = -EINVAL; - } - - drm_mm_remove_node(&tmp); - if (err) - return err; - - list_for_each_entry(e, &evict_list, link) { - err = drm_mm_reserve_node(mm, &e->node); - if (err) { - pr_err("Failed to reinsert node after eviction: start=%llx\n", - e->node.start); - return err; - } - } - - if (!assert_continuous(mm, nodes[0].node.size)) { - pr_err("range is no longer continuous\n"); - return -EINVAL; - } - - return 0; -} - -static int igt_evict(void *ignored) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int size = 8192; - const struct insert_mode *mode; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - int ret, err; - - /* Here we populate a full drm_mm and then try and insert a new node - * by evicting other nodes in a random order. The drm_mm_scan should - * pick the first matching hole it finds from the random list. We - * repeat that for different allocation strategies, alignments and - * sizes to try and stress the hole finder. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(size, sizeof(*nodes))); - if (!nodes) - goto err; - - order = drm_random_order(size, &prng); - if (!order) - goto err_nodes; - - ret = -EINVAL; - drm_mm_init(&mm, 0, size); - for (n = 0; n < size; n++) { - err = drm_mm_insert_node(&mm, &nodes[n].node, 1); - if (err) { - pr_err("insert failed, step %d\n", n); - ret = err; - goto out; - } - } - - /* First check that using the scanner doesn't break the mm */ - if (!evict_nothing(&mm, size, nodes)) { - pr_err("evict_nothing() failed\n"); - goto out; - } - if (!evict_everything(&mm, size, nodes)) { - pr_err("evict_everything() failed\n"); - goto out; - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= size; n <<= 1) { - drm_random_reorder(order, size, &prng); - err = evict_something(&mm, 0, U64_MAX, - nodes, order, size, - n, 1, - mode); - if (err) { - pr_err("%s evict_something(size=%u) failed\n", - mode->name, n); - ret = err; - goto out; - } - } - - for (n = 1; n < size; n <<= 1) { - drm_random_reorder(order, size, &prng); - err = evict_something(&mm, 0, U64_MAX, - nodes, order, size, - size/2, n, - mode); - if (err) { - pr_err("%s evict_something(size=%u, alignment=%u) failed\n", - mode->name, size/2, n); - ret = err; - goto out; - } - } - - for_each_prime_number_from(n, 1, min(size, max_prime)) { - unsigned int nsize = (size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, size, &prng); - err = evict_something(&mm, 0, U64_MAX, - nodes, order, size, - nsize, n, - mode); - if (err) { - pr_err("%s evict_something(size=%u, alignment=%u) failed\n", - mode->name, nsize, n); - ret = err; - goto out; - } - } - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -err: - return ret; -} - -static int igt_evict_range(void *ignored) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int size = 8192; - const unsigned int range_size = size / 2; - const unsigned int range_start = size / 4; - const unsigned int range_end = range_start + range_size; - const struct insert_mode *mode; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - int ret, err; - - /* Like igt_evict() but now we are limiting the search to a - * small portion of the full drm_mm. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(size, sizeof(*nodes))); - if (!nodes) - goto err; - - order = drm_random_order(size, &prng); - if (!order) - goto err_nodes; - - ret = -EINVAL; - drm_mm_init(&mm, 0, size); - for (n = 0; n < size; n++) { - err = drm_mm_insert_node(&mm, &nodes[n].node, 1); - if (err) { - pr_err("insert failed, step %d\n", n); - ret = err; - goto out; - } - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= range_size; n <<= 1) { - drm_random_reorder(order, size, &prng); - err = evict_something(&mm, range_start, range_end, - nodes, order, size, - n, 1, - mode); - if (err) { - pr_err("%s evict_something(size=%u) failed with range [%u, %u]\n", - mode->name, n, range_start, range_end); - goto out; - } - } - - for (n = 1; n <= range_size; n <<= 1) { - drm_random_reorder(order, size, &prng); - err = evict_something(&mm, range_start, range_end, - nodes, order, size, - range_size/2, n, - mode); - if (err) { - pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", - mode->name, range_size/2, n, range_start, range_end); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(range_size, max_prime)) { - unsigned int nsize = (range_size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, size, &prng); - err = evict_something(&mm, range_start, range_end, - nodes, order, size, - nsize, n, - mode); - if (err) { - pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", - mode->name, nsize, n, range_start, range_end); - goto out; - } - } - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -err: - return ret; -} - -static unsigned int node_index(const struct drm_mm_node *node) -{ - return div64_u64(node->start, node->size); -} - -static int igt_topdown(void *ignored) -{ - const struct insert_mode *topdown = &insert_modes[TOPDOWN]; - DRM_RND_STATE(prng, random_seed); - const unsigned int count = 8192; - unsigned int size; - unsigned long *bitmap; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - int ret; - - /* When allocating top-down, we expect to be returned a node - * from a suitable hole at the top of the drm_mm. We check that - * the returned node does match the highest available slot. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(count, sizeof(*nodes))); - if (!nodes) - goto err; - - bitmap = bitmap_zalloc(count, GFP_KERNEL); - if (!bitmap) - goto err_nodes; - - order = drm_random_order(count, &prng); - if (!order) - goto err_bitmap; - - ret = -EINVAL; - for (size = 1; size <= 64; size <<= 1) { - drm_mm_init(&mm, 0, size*count); - for (n = 0; n < count; n++) { - if (!expect_insert(&mm, &nodes[n], - size, 0, n, - topdown)) { - pr_err("insert failed, size %u step %d\n", size, n); - goto out; - } - - if (drm_mm_hole_follows(&nodes[n])) { - pr_err("hole after topdown insert %d, start=%llx\n, size=%u", - n, nodes[n].start, size); - goto out; - } - - if (!assert_one_hole(&mm, 0, size*(count - n - 1))) - goto out; - } - - if (!assert_continuous(&mm, size)) - goto out; - - drm_random_reorder(order, count, &prng); - for_each_prime_number_from(n, 1, min(count, max_prime)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - __set_bit(node_index(node), bitmap); - } - - for (m = 0; m < n; m++) { - unsigned int last; - - node = &nodes[order[(o + m) % count]]; - if (!expect_insert(&mm, node, - size, 0, 0, - topdown)) { - pr_err("insert failed, step %d/%d\n", m, n); - goto out; - } - - if (drm_mm_hole_follows(node)) { - pr_err("hole after topdown insert %d/%d, start=%llx\n", - m, n, node->start); - goto out; - } - - last = find_last_bit(bitmap, count); - if (node_index(node) != last) { - pr_err("node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n", - m, n, size, last, node_index(node)); - goto out; - } - - __clear_bit(last, bitmap); - } - - DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); - - o += n; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_bitmap: - bitmap_free(bitmap); -err_nodes: - vfree(nodes); -err: - return ret; -} - -static int igt_bottomup(void *ignored) -{ - const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; - DRM_RND_STATE(prng, random_seed); - const unsigned int count = 8192; - unsigned int size; - unsigned long *bitmap; - struct drm_mm mm; - struct drm_mm_node *nodes, *node, *next; - unsigned int *order, n, m, o = 0; - int ret; - - /* Like igt_topdown, but instead of searching for the last hole, - * we search for the first. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(count, sizeof(*nodes))); - if (!nodes) - goto err; - - bitmap = bitmap_zalloc(count, GFP_KERNEL); - if (!bitmap) - goto err_nodes; - - order = drm_random_order(count, &prng); - if (!order) - goto err_bitmap; - - ret = -EINVAL; - for (size = 1; size <= 64; size <<= 1) { - drm_mm_init(&mm, 0, size*count); - for (n = 0; n < count; n++) { - if (!expect_insert(&mm, &nodes[n], - size, 0, n, - bottomup)) { - pr_err("bottomup insert failed, size %u step %d\n", size, n); - goto out; - } - - if (!assert_one_hole(&mm, size*(n + 1), size*count)) - goto out; - } - - if (!assert_continuous(&mm, size)) - goto out; - - drm_random_reorder(order, count, &prng); - for_each_prime_number_from(n, 1, min(count, max_prime)) { - for (m = 0; m < n; m++) { - node = &nodes[order[(o + m) % count]]; - drm_mm_remove_node(node); - __set_bit(node_index(node), bitmap); - } - - for (m = 0; m < n; m++) { - unsigned int first; - - node = &nodes[order[(o + m) % count]]; - if (!expect_insert(&mm, node, - size, 0, 0, - bottomup)) { - pr_err("insert failed, step %d/%d\n", m, n); - goto out; - } - - first = find_first_bit(bitmap, count); - if (node_index(node) != first) { - pr_err("node %d/%d not inserted into bottom hole, expected %d, found %d\n", - m, n, first, node_index(node)); - goto out; - } - __clear_bit(first, bitmap); - } - - DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); - - o += n; - } - - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - DRM_MM_BUG_ON(!drm_mm_clean(&mm)); - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_bitmap: - bitmap_free(bitmap); -err_nodes: - vfree(nodes); -err: - return ret; -} - -static int __igt_once(unsigned int mode) -{ - struct drm_mm mm; - struct drm_mm_node rsvd_lo, rsvd_hi, node; - int err; - - drm_mm_init(&mm, 0, 7); - - memset(&rsvd_lo, 0, sizeof(rsvd_lo)); - rsvd_lo.start = 1; - rsvd_lo.size = 1; - err = drm_mm_reserve_node(&mm, &rsvd_lo); - if (err) { - pr_err("Could not reserve low node\n"); - goto err; - } - - memset(&rsvd_hi, 0, sizeof(rsvd_hi)); - rsvd_hi.start = 5; - rsvd_hi.size = 1; - err = drm_mm_reserve_node(&mm, &rsvd_hi); - if (err) { - pr_err("Could not reserve low node\n"); - goto err_lo; - } - - if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) { - pr_err("Expected a hole after lo and high nodes!\n"); - err = -EINVAL; - goto err_hi; - } - - memset(&node, 0, sizeof(node)); - err = drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode); - if (err) { - pr_err("Could not insert the node into the available hole!\n"); - err = -EINVAL; - goto err_hi; - } - - drm_mm_remove_node(&node); -err_hi: - drm_mm_remove_node(&rsvd_hi); -err_lo: - drm_mm_remove_node(&rsvd_lo); -err: - drm_mm_takedown(&mm); - return err; -} - -static int igt_lowest(void *ignored) -{ - return __igt_once(DRM_MM_INSERT_LOW); -} - -static int igt_highest(void *ignored) -{ - return __igt_once(DRM_MM_INSERT_HIGH); -} - -static void separate_adjacent_colors(const struct drm_mm_node *node, - unsigned long color, - u64 *start, - u64 *end) -{ - if (drm_mm_node_allocated(node) && node->color != color) - ++*start; - - node = list_next_entry(node, node_list); - if (drm_mm_node_allocated(node) && node->color != color) - --*end; -} - -static bool colors_abutt(const struct drm_mm_node *node) -{ - if (!drm_mm_hole_follows(node) && - drm_mm_node_allocated(list_next_entry(node, node_list))) { - pr_err("colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n", - node->color, node->start, node->size, - list_next_entry(node, node_list)->color, - list_next_entry(node, node_list)->start, - list_next_entry(node, node_list)->size); - return true; - } - - return false; -} - -static int igt_color(void *ignored) -{ - const unsigned int count = min(4096u, max_iterations); - const struct insert_mode *mode; - struct drm_mm mm; - struct drm_mm_node *node, *nn; - unsigned int n; - int ret = -EINVAL, err; - - /* Color adjustment complicates everything. First we just check - * that when we insert a node we apply any color_adjustment callback. - * The callback we use should ensure that there is a gap between - * any two nodes, and so after each insertion we check that those - * holes are inserted and that they are preserved. - */ - - drm_mm_init(&mm, 0, U64_MAX); - - for (n = 1; n <= count; n++) { - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - ret = -ENOMEM; - goto out; - } - - if (!expect_insert(&mm, node, - n, 0, n, - &insert_modes[0])) { - pr_err("insert failed, step %d\n", n); - kfree(node); - goto out; - } - } - - drm_mm_for_each_node_safe(node, nn, &mm) { - if (node->color != node->size) { - pr_err("invalid color stored: expected %lld, found %ld\n", - node->size, node->color); - - goto out; - } - - drm_mm_remove_node(node); - kfree(node); - } - - /* Now, let's start experimenting with applying a color callback */ - mm.color_adjust = separate_adjacent_colors; - for (mode = insert_modes; mode->name; mode++) { - u64 last; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - ret = -ENOMEM; - goto out; - } - - node->size = 1 + 2*count; - node->color = node->size; - - err = drm_mm_reserve_node(&mm, node); - if (err) { - pr_err("initial reserve failed!\n"); - ret = err; - goto out; - } - - last = node->start + node->size; - - for (n = 1; n <= count; n++) { - int rem; - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - ret = -ENOMEM; - goto out; - } - - node->start = last; - node->size = n + count; - node->color = node->size; - - err = drm_mm_reserve_node(&mm, node); - if (err != -ENOSPC) { - pr_err("reserve %d did not report color overlap! err=%d\n", - n, err); - goto out; - } - - node->start += n + 1; - rem = misalignment(node, n + count); - node->start += n + count - rem; - - err = drm_mm_reserve_node(&mm, node); - if (err) { - pr_err("reserve %d failed, err=%d\n", n, err); - ret = err; - goto out; - } - - last = node->start + node->size; - } - - for (n = 1; n <= count; n++) { - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) { - ret = -ENOMEM; - goto out; - } - - if (!expect_insert(&mm, node, - n, n, n, - mode)) { - pr_err("%s insert failed, step %d\n", - mode->name, n); - kfree(node); - goto out; - } - } - - drm_mm_for_each_node_safe(node, nn, &mm) { - u64 rem; - - if (node->color != node->size) { - pr_err("%s invalid color stored: expected %lld, found %ld\n", - mode->name, node->size, node->color); - - goto out; - } - - if (colors_abutt(node)) - goto out; - - div64_u64_rem(node->start, node->size, &rem); - if (rem) { - pr_err("%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n", - mode->name, node->start, node->size, rem); - goto out; - } - - drm_mm_remove_node(node); - kfree(node); - } - - cond_resched(); - } - - ret = 0; -out: - drm_mm_for_each_node_safe(node, nn, &mm) { - drm_mm_remove_node(node); - kfree(node); - } - drm_mm_takedown(&mm); - return ret; -} - -static int evict_color(struct drm_mm *mm, - u64 range_start, u64 range_end, - struct evict_node *nodes, - unsigned int *order, - unsigned int count, - unsigned int size, - unsigned int alignment, - unsigned long color, - const struct insert_mode *mode) -{ - struct drm_mm_scan scan; - LIST_HEAD(evict_list); - struct evict_node *e; - struct drm_mm_node tmp; - int err; - - drm_mm_scan_init_with_range(&scan, mm, - size, alignment, color, - range_start, range_end, - mode->mode); - if (!evict_nodes(&scan, - nodes, order, count, true, - &evict_list)) - return -EINVAL; - - memset(&tmp, 0, sizeof(tmp)); - err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, - DRM_MM_INSERT_EVICT); - if (err) { - pr_err("Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", - size, alignment, color, err); - show_scan(&scan); - show_holes(mm, 3); - return err; - } - - if (tmp.start < range_start || tmp.start + tmp.size > range_end) { - pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", - tmp.start, tmp.size, range_start, range_end); - err = -EINVAL; - } - - if (colors_abutt(&tmp)) - err = -EINVAL; - - if (!assert_node(&tmp, mm, size, alignment, color)) { - pr_err("Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n", - tmp.size, size, - alignment, misalignment(&tmp, alignment), tmp.start); - err = -EINVAL; - } - - drm_mm_remove_node(&tmp); - if (err) - return err; - - list_for_each_entry(e, &evict_list, link) { - err = drm_mm_reserve_node(mm, &e->node); - if (err) { - pr_err("Failed to reinsert node after eviction: start=%llx\n", - e->node.start); - return err; - } - } - - cond_resched(); - return 0; -} - -static int igt_color_evict(void *ignored) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int total_size = min(8192u, max_iterations); - const struct insert_mode *mode; - unsigned long color = 0; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - int ret, err; - - /* Check that the drm_mm_scan also honours color adjustment when - * choosing its victims to create a hole. Our color_adjust does not - * allow two nodes to be placed together without an intervening hole - * enlarging the set of victims that must be evicted. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(total_size, sizeof(*nodes))); - if (!nodes) - goto err; - - order = drm_random_order(total_size, &prng); - if (!order) - goto err_nodes; - - ret = -EINVAL; - drm_mm_init(&mm, 0, 2*total_size - 1); - mm.color_adjust = separate_adjacent_colors; - for (n = 0; n < total_size; n++) { - if (!expect_insert(&mm, &nodes[n].node, - 1, 0, color++, - &insert_modes[0])) { - pr_err("insert failed, step %d\n", n); - goto out; - } - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= total_size; n <<= 1) { - drm_random_reorder(order, total_size, &prng); - err = evict_color(&mm, 0, U64_MAX, - nodes, order, total_size, - n, 1, color++, - mode); - if (err) { - pr_err("%s evict_color(size=%u) failed\n", - mode->name, n); - goto out; - } - } - - for (n = 1; n < total_size; n <<= 1) { - drm_random_reorder(order, total_size, &prng); - err = evict_color(&mm, 0, U64_MAX, - nodes, order, total_size, - total_size/2, n, color++, - mode); - if (err) { - pr_err("%s evict_color(size=%u, alignment=%u) failed\n", - mode->name, total_size/2, n); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(total_size, max_prime)) { - unsigned int nsize = (total_size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, total_size, &prng); - err = evict_color(&mm, 0, U64_MAX, - nodes, order, total_size, - nsize, n, color++, - mode); - if (err) { - pr_err("%s evict_color(size=%u, alignment=%u) failed\n", - mode->name, nsize, n); - goto out; - } - } - - cond_resched(); - } - - ret = 0; -out: - if (ret) - show_mm(&mm); - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -err: - return ret; -} - -static int igt_color_evict_range(void *ignored) -{ - DRM_RND_STATE(prng, random_seed); - const unsigned int total_size = 8192; - const unsigned int range_size = total_size / 2; - const unsigned int range_start = total_size / 4; - const unsigned int range_end = range_start + range_size; - const struct insert_mode *mode; - unsigned long color = 0; - struct drm_mm mm; - struct evict_node *nodes; - struct drm_mm_node *node, *next; - unsigned int *order, n; - int ret, err; - - /* Like igt_color_evict(), but limited to small portion of the full - * drm_mm range. - */ - - ret = -ENOMEM; - nodes = vzalloc(array_size(total_size, sizeof(*nodes))); - if (!nodes) - goto err; - - order = drm_random_order(total_size, &prng); - if (!order) - goto err_nodes; - - ret = -EINVAL; - drm_mm_init(&mm, 0, 2*total_size - 1); - mm.color_adjust = separate_adjacent_colors; - for (n = 0; n < total_size; n++) { - if (!expect_insert(&mm, &nodes[n].node, - 1, 0, color++, - &insert_modes[0])) { - pr_err("insert failed, step %d\n", n); - goto out; - } - } - - for (mode = evict_modes; mode->name; mode++) { - for (n = 1; n <= range_size; n <<= 1) { - drm_random_reorder(order, range_size, &prng); - err = evict_color(&mm, range_start, range_end, - nodes, order, total_size, - n, 1, color++, - mode); - if (err) { - pr_err("%s evict_color(size=%u) failed for range [%x, %x]\n", - mode->name, n, range_start, range_end); - goto out; - } - } - - for (n = 1; n < range_size; n <<= 1) { - drm_random_reorder(order, total_size, &prng); - err = evict_color(&mm, range_start, range_end, - nodes, order, total_size, - range_size/2, n, color++, - mode); - if (err) { - pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", - mode->name, total_size/2, n, range_start, range_end); - goto out; - } - } - - for_each_prime_number_from(n, 1, min(range_size, max_prime)) { - unsigned int nsize = (range_size - n + 1) / 2; - - DRM_MM_BUG_ON(!nsize); - - drm_random_reorder(order, total_size, &prng); - err = evict_color(&mm, range_start, range_end, - nodes, order, total_size, - nsize, n, color++, - mode); - if (err) { - pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", - mode->name, nsize, n, range_start, range_end); - goto out; - } - } - - cond_resched(); - } - - ret = 0; -out: - if (ret) - show_mm(&mm); - drm_mm_for_each_node_safe(node, next, &mm) - drm_mm_remove_node(node); - drm_mm_takedown(&mm); - kfree(order); -err_nodes: - vfree(nodes); -err: - return ret; -} - -#include "drm_selftest.c" - -static int __init test_drm_mm_init(void) -{ - int err; - - while (!random_seed) - random_seed = get_random_int(); - - pr_info("Testing DRM range manager (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u\n", - random_seed, max_iterations, max_prime); - err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL); - - return err > 0 ? 0 : err; -} - -static void __exit test_drm_mm_exit(void) -{ -} - -module_init(test_drm_mm_init); -module_exit(test_drm_mm_exit); - -module_param(random_seed, uint, 0400); -module_param(max_iterations, uint, 0400); -module_param(max_prime, uint, 0400); - -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index cff59189598f..91b70f7d2769 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -2,4 +2,4 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \ - drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o + drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o drm_mm_test.o diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c new file mode 100644 index 000000000000..1e2c1aa524bd --- /dev/null +++ b/drivers/gpu/drm/tests/drm_mm_test.c @@ -0,0 +1,2255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test cases for the drm_mm range manager + * + * Copyright (c) 2022 Arthur Grillo + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "../lib/drm_random.h" + +static unsigned int random_seed; +static unsigned int max_iterations = 8192; +static unsigned int max_prime = 128; + +enum { + BEST, + BOTTOMUP, + TOPDOWN, + EVICT, +}; + +static const struct insert_mode { + const char *name; + enum drm_mm_insert_mode mode; +} insert_modes[] = { + [BEST] = { "best", DRM_MM_INSERT_BEST }, + [BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW }, + [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH }, + [EVICT] = { "evict", DRM_MM_INSERT_EVICT }, + {} +}, evict_modes[] = { + { "bottom-up", DRM_MM_INSERT_LOW }, + { "top-down", DRM_MM_INSERT_HIGH }, + {} +}; + +static bool assert_no_holes(struct kunit *test, const struct drm_mm *mm) +{ + struct drm_mm_node *hole; + u64 hole_start, __always_unused hole_end; + unsigned long count; + + count = 0; + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) + count++; + if (count) { + KUNIT_FAIL(test, + "Expected to find no holes (after reserve), found %lu instead\n", count); + return false; + } + + drm_mm_for_each_node(hole, mm) { + if (drm_mm_hole_follows(hole)) { + KUNIT_FAIL(test, "Hole follows node, expected none!\n"); + return false; + } + } + + return true; +} + +static bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 start, u64 end) +{ + struct drm_mm_node *hole; + u64 hole_start, hole_end; + unsigned long count; + bool ok = true; + + if (end <= start) + return true; + + count = 0; + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { + if (start != hole_start || end != hole_end) { + if (ok) + KUNIT_FAIL(test, + "empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n", + hole_start, hole_end, start, end); + ok = false; + } + count++; + } + if (count != 1) { + KUNIT_FAIL(test, "Expected to find one hole, found %lu instead\n", count); + ok = false; + } + + return ok; +} + +static bool assert_continuous(struct kunit *test, const struct drm_mm *mm, u64 size) +{ + struct drm_mm_node *node, *check, *found; + unsigned long n; + u64 addr; + + if (!assert_no_holes(test, mm)) + return false; + + n = 0; + addr = 0; + drm_mm_for_each_node(node, mm) { + if (node->start != addr) { + KUNIT_FAIL(test, "node[%ld] list out of order, expected %llx found %llx\n", + n, addr, node->start); + return false; + } + + if (node->size != size) { + KUNIT_FAIL(test, "node[%ld].size incorrect, expected %llx, found %llx\n", + n, size, node->size); + return false; + } + + if (drm_mm_hole_follows(node)) { + KUNIT_FAIL(test, "node[%ld] is followed by a hole!\n", n); + return false; + } + + found = NULL; + drm_mm_for_each_node_in_range(check, mm, addr, addr + size) { + if (node != check) { + KUNIT_FAIL(test, + "lookup return wrong node, expected start %llx, found %llx\n", + node->start, check->start); + return false; + } + found = check; + } + if (!found) { + KUNIT_FAIL(test, "lookup failed for node %llx + %llx\n", addr, size); + return false; + } + + addr += size; + n++; + } + + return true; +} + +static u64 misalignment(struct drm_mm_node *node, u64 alignment) +{ + u64 rem; + + if (!alignment) + return 0; + + div64_u64_rem(node->start, alignment, &rem); + return rem; +} + +static bool assert_node(struct kunit *test, struct drm_mm_node *node, struct drm_mm *mm, + u64 size, u64 alignment, unsigned long color) +{ + bool ok = true; + + if (!drm_mm_node_allocated(node) || node->mm != mm) { + KUNIT_FAIL(test, "node not allocated\n"); + ok = false; + } + + if (node->size != size) { + KUNIT_FAIL(test, "node has wrong size, found %llu, expected %llu\n", + node->size, size); + ok = false; + } + + if (misalignment(node, alignment)) { + KUNIT_FAIL(test, + "node is misaligned, start %llx rem %llu, expected alignment %llu\n", + node->start, misalignment(node, alignment), alignment); + ok = false; + } + + if (node->color != color) { + KUNIT_FAIL(test, "node has wrong color, found %lu, expected %lu\n", + node->color, color); + ok = false; + } + + return ok; +} + +static void igt_mm_init(struct kunit *test) +{ + const unsigned int size = 4096; + struct drm_mm mm; + struct drm_mm_node tmp; + + /* Start with some simple checks on initialising the struct drm_mm */ + memset(&mm, 0, sizeof(mm)); + KUNIT_ASSERT_FALSE_MSG(test, drm_mm_initialized(&mm), + "zeroed mm claims to be initialized\n"); + + memset(&mm, 0xff, sizeof(mm)); + drm_mm_init(&mm, 0, size); + if (!drm_mm_initialized(&mm)) { + KUNIT_FAIL(test, "mm claims not to be initialized\n"); + goto out; + } + + if (!drm_mm_clean(&mm)) { + KUNIT_FAIL(test, "mm not empty on creation\n"); + goto out; + } + + /* After creation, it should all be one massive hole */ + if (!assert_one_hole(test, &mm, 0, size)) { + KUNIT_FAIL(test, ""); + goto out; + } + + memset(&tmp, 0, sizeof(tmp)); + tmp.start = 0; + tmp.size = size; + if (drm_mm_reserve_node(&mm, &tmp)) { + KUNIT_FAIL(test, "failed to reserve whole drm_mm\n"); + goto out; + } + + /* After filling the range entirely, there should be no holes */ + if (!assert_no_holes(test, &mm)) { + KUNIT_FAIL(test, ""); + goto out; + } + + /* And then after emptying it again, the massive hole should be back */ + drm_mm_remove_node(&tmp); + if (!assert_one_hole(test, &mm, 0, size)) { + KUNIT_FAIL(test, ""); + goto out; + } + +out: + drm_mm_takedown(&mm); +} + +static void igt_mm_debug(struct kunit *test) +{ + struct drm_mm mm; + struct drm_mm_node nodes[2]; + + /* Create a small drm_mm with a couple of nodes and a few holes, and + * check that the debug iterator doesn't explode over a trivial drm_mm. + */ + + drm_mm_init(&mm, 0, 4096); + + memset(nodes, 0, sizeof(nodes)); + nodes[0].start = 512; + nodes[0].size = 1024; + KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[0]), + "failed to reserve node[0] {start=%lld, size=%lld)\n", + nodes[0].start, nodes[0].size); + + nodes[1].size = 1024; + nodes[1].start = 4096 - 512 - nodes[1].size; + KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]), + "failed to reserve node[0] {start=%lld, size=%lld)\n", + nodes[0].start, nodes[0].size); +} + +static struct drm_mm_node *set_node(struct drm_mm_node *node, + u64 start, u64 size) +{ + node->start = start; + node->size = size; + return node; +} + +static bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node) +{ + int err; + + err = drm_mm_reserve_node(mm, node); + if (likely(err == -ENOSPC)) + return true; + + if (!err) { + KUNIT_FAIL(test, "impossible reserve succeeded, node %llu + %llu\n", + node->start, node->size); + drm_mm_remove_node(node); + } else { + KUNIT_FAIL(test, + "impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n", + err, -ENOSPC, node->start, node->size); + } + return false; +} + +static bool check_reserve_boundaries(struct kunit *test, struct drm_mm *mm, + unsigned int count, + u64 size) +{ + const struct boundary { + u64 start, size; + const char *name; + } boundaries[] = { +#define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" } + B(0, 0), + B(-size, 0), + B(size, 0), + B(size * count, 0), + B(-size, size), + B(-size, -size), + B(-size, 2 * size), + B(0, -size), + B(size, -size), + B(count * size, size), + B(count * size, -size), + B(count * size, count * size), + B(count * size, -count * size), + B(count * size, -(count + 1) * size), + B((count + 1) * size, size), + B((count + 1) * size, -size), + B((count + 1) * size, -2 * size), +#undef B + }; + struct drm_mm_node tmp = {}; + int n; + + for (n = 0; n < ARRAY_SIZE(boundaries); n++) { + if (!expect_reserve_fail(test, mm, set_node(&tmp, boundaries[n].start, + boundaries[n].size))) { + KUNIT_FAIL(test, "boundary[%d:%s] failed, count=%u, size=%lld\n", + n, boundaries[n].name, count, size); + return false; + } + } + + return true; +} + +static int __igt_reserve(struct kunit *test, unsigned int count, u64 size) +{ + DRM_RND_STATE(prng, random_seed); + struct drm_mm mm; + struct drm_mm_node tmp, *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret, err; + + /* For exercising drm_mm_reserve_node(struct kunit *test, ), we want to check that + * reservations outside of the drm_mm range are rejected, and to + * overlapping and otherwise already occupied ranges. Afterwards, + * the tree and nodes should be intact. + */ + + DRM_MM_BUG_ON(!count); + DRM_MM_BUG_ON(!size); + + ret = -ENOMEM; + order = drm_random_order(count, &prng); + if (!order) + goto err; + + nodes = vzalloc(array_size(count, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + ret = -EINVAL; + drm_mm_init(&mm, 0, count * size); + + if (!check_reserve_boundaries(test, &mm, count, size)) + goto out; + + for (n = 0; n < count; n++) { + nodes[n].start = order[n] * size; + nodes[n].size = size; + + err = drm_mm_reserve_node(&mm, &nodes[n]); + if (err) { + KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n", + n, nodes[n].start); + ret = err; + goto out; + } + + if (!drm_mm_node_allocated(&nodes[n])) { + KUNIT_FAIL(test, "reserved node not allocated! step %d, start %llu\n", + n, nodes[n].start); + goto out; + } + + if (!expect_reserve_fail(test, &mm, &nodes[n])) + goto out; + } + + /* After random insertion the nodes should be in order */ + if (!assert_continuous(test, &mm, size)) + goto out; + + /* Repeated use should then fail */ + drm_random_reorder(order, count, &prng); + for (n = 0; n < count; n++) { + if (!expect_reserve_fail(test, &mm, set_node(&tmp, order[n] * size, 1))) + goto out; + + /* Remove and reinsert should work */ + drm_mm_remove_node(&nodes[order[n]]); + err = drm_mm_reserve_node(&mm, &nodes[order[n]]); + if (err) { + KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n", + n, nodes[n].start); + ret = err; + goto out; + } + } + + if (!assert_continuous(test, &mm, size)) + goto out; + + /* Overlapping use should then fail */ + for (n = 0; n < count; n++) { + if (!expect_reserve_fail(test, &mm, set_node(&tmp, 0, size * count))) + goto out; + } + for (n = 0; n < count; n++) { + if (!expect_reserve_fail(test, &mm, set_node(&tmp, size * n, size * (count - n)))) + goto out; + } + + /* Remove several, reinsert, check full */ + for_each_prime_number(n, min(max_prime, count)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + } + + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + err = drm_mm_reserve_node(&mm, node); + if (err) { + KUNIT_FAIL(test, "reserve failed, step %d/%d, start %llu\n", + m, n, node->start); + ret = err; + goto out; + } + } + + o += n; + + if (!assert_continuous(test, &mm, size)) + goto out; + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); + kfree(order); +err: + return ret; +} + +static void igt_mm_reserve(struct kunit *test) +{ + const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); + int n; + + for_each_prime_number_from(n, 1, 54) { + u64 size = BIT_ULL(n); + + KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size - 1)); + KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size)); + KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size + 1)); + + cond_resched(); + } +} + +static bool expect_insert(struct kunit *test, struct drm_mm *mm, + struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color, + const struct insert_mode *mode) +{ + int err; + + err = drm_mm_insert_node_generic(mm, node, + size, alignment, color, + mode->mode); + if (err) { + KUNIT_FAIL(test, + "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n", + size, alignment, color, mode->name, err); + return false; + } + + if (!assert_node(test, node, mm, size, alignment, color)) { + drm_mm_remove_node(node); + return false; + } + + return true; +} + +static bool expect_insert_fail(struct kunit *test, struct drm_mm *mm, u64 size) +{ + struct drm_mm_node tmp = {}; + int err; + + err = drm_mm_insert_node(mm, &tmp, size); + if (likely(err == -ENOSPC)) + return true; + + if (!err) { + KUNIT_FAIL(test, "impossible insert succeeded, node %llu + %llu\n", + tmp.start, tmp.size); + drm_mm_remove_node(&tmp); + } else { + KUNIT_FAIL(test, + "impossible insert failed with wrong error %d [expected %d], size %llu\n", + err, -ENOSPC, size); + } + return false; +} + +static int __igt_insert(struct kunit *test, unsigned int count, u64 size, bool replace) +{ + DRM_RND_STATE(prng, random_seed); + const struct insert_mode *mode; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + int ret; + + /* Fill a range with lots of nodes, check it doesn't fail too early */ + + DRM_MM_BUG_ON(!count); + DRM_MM_BUG_ON(!size); + + ret = -ENOMEM; + nodes = vmalloc(array_size(count, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + order = drm_random_order(count, &prng); + if (!order) + goto err_nodes; + + ret = -EINVAL; + drm_mm_init(&mm, 0, count * size); + + for (mode = insert_modes; mode->name; mode++) { + for (n = 0; n < count; n++) { + struct drm_mm_node tmp; + + node = replace ? &tmp : &nodes[n]; + memset(node, 0, sizeof(*node)); + if (!expect_insert(test, &mm, node, size, 0, n, mode)) { + KUNIT_FAIL(test, "%s insert failed, size %llu step %d\n", + mode->name, size, n); + goto out; + } + + if (replace) { + drm_mm_replace_node(&tmp, &nodes[n]); + if (drm_mm_node_allocated(&tmp)) { + KUNIT_FAIL(test, + "replaced old-node still allocated! step %d\n", + n); + goto out; + } + + if (!assert_node(test, &nodes[n], &mm, size, 0, n)) { + KUNIT_FAIL(test, + "replaced node did not inherit parameters, size %llu step %d\n", + size, n); + goto out; + } + + if (tmp.start != nodes[n].start) { + KUNIT_FAIL(test, + "replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n", + tmp.start, size, nodes[n].start, nodes[n].size); + goto out; + } + } + } + + /* After random insertion the nodes should be in order */ + if (!assert_continuous(test, &mm, size)) + goto out; + + /* Repeated use should then fail */ + if (!expect_insert_fail(test, &mm, size)) + goto out; + + /* Remove one and reinsert, as the only hole it should refill itself */ + for (n = 0; n < count; n++) { + u64 addr = nodes[n].start; + + drm_mm_remove_node(&nodes[n]); + if (!expect_insert(test, &mm, &nodes[n], size, 0, n, mode)) { + KUNIT_FAIL(test, "%s reinsert failed, size %llu step %d\n", + mode->name, size, n); + goto out; + } + + if (nodes[n].start != addr) { + KUNIT_FAIL(test, + "%s reinsert node moved, step %d, expected %llx, found %llx\n", + mode->name, n, addr, nodes[n].start); + goto out; + } + + if (!assert_continuous(test, &mm, size)) + goto out; + } + + /* Remove several, reinsert, check full */ + for_each_prime_number(n, min(max_prime, count)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + } + + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(test, &mm, node, size, 0, n, mode)) { + KUNIT_FAIL(test, + "%s multiple reinsert failed, size %llu step %d\n", + mode->name, size, n); + goto out; + } + } + + o += n; + + if (!assert_continuous(test, &mm, size)) + goto out; + + if (!expect_insert_fail(test, &mm, size)) + goto out; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + + cond_resched(); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); + return ret; +} + +static void igt_mm_insert(struct kunit *test) +{ + const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); + unsigned int n; + + for_each_prime_number_from(n, 1, 54) { + u64 size = BIT_ULL(n); + + KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size - 1, false)); + KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size, false)); + KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size + 1, false)); + + cond_resched(); + } +} + +static void igt_mm_replace(struct kunit *test) +{ + const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); + unsigned int n; + + /* Reuse igt_insert to exercise replacement by inserting a dummy node, + * then replacing it with the intended node. We want to check that + * the tree is intact and all the information we need is carried + * across to the target node. + */ + + for_each_prime_number_from(n, 1, 54) { + u64 size = BIT_ULL(n); + + KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size - 1, true)); + KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size, true)); + KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size + 1, true)); + + cond_resched(); + } +} + +static bool expect_insert_in_range(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node, + u64 size, u64 alignment, unsigned long color, + u64 range_start, u64 range_end, const struct insert_mode *mode) +{ + int err; + + err = drm_mm_insert_node_in_range(mm, node, + size, alignment, color, + range_start, range_end, + mode->mode); + if (err) { + KUNIT_FAIL(test, + "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n", + size, alignment, color, mode->name, + range_start, range_end, err); + return false; + } + + if (!assert_node(test, node, mm, size, alignment, color)) { + drm_mm_remove_node(node); + return false; + } + + return true; +} + +static bool expect_insert_in_range_fail(struct kunit *test, struct drm_mm *mm, + u64 size, u64 range_start, u64 range_end) +{ + struct drm_mm_node tmp = {}; + int err; + + err = drm_mm_insert_node_in_range(mm, &tmp, size, 0, 0, range_start, range_end, + 0); + if (likely(err == -ENOSPC)) + return true; + + if (!err) { + KUNIT_FAIL(test, + "impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n", + tmp.start, tmp.size, range_start, range_end); + drm_mm_remove_node(&tmp); + } else { + KUNIT_FAIL(test, + "impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n", + err, -ENOSPC, size, range_start, range_end); + } + + return false; +} + +static bool assert_contiguous_in_range(struct kunit *test, struct drm_mm *mm, + u64 size, u64 start, u64 end) +{ + struct drm_mm_node *node; + unsigned int n; + + if (!expect_insert_in_range_fail(test, mm, size, start, end)) + return false; + + n = div64_u64(start + size - 1, size); + drm_mm_for_each_node(node, mm) { + if (node->start < start || node->start + node->size > end) { + KUNIT_FAIL(test, + "node %d out of range, address [%llx + %llu], range [%llx, %llx]\n", + n, node->start, node->start + node->size, start, end); + return false; + } + + if (node->start != n * size) { + KUNIT_FAIL(test, "node %d out of order, expected start %llx, found %llx\n", + n, n * size, node->start); + return false; + } + + if (node->size != size) { + KUNIT_FAIL(test, "node %d has wrong size, expected size %llx, found %llx\n", + n, size, node->size); + return false; + } + + if (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) { + KUNIT_FAIL(test, "node %d is followed by a hole!\n", n); + return false; + } + + n++; + } + + if (start > 0) { + node = __drm_mm_interval_first(mm, 0, start - 1); + if (drm_mm_node_allocated(node)) { + KUNIT_FAIL(test, "node before start: node=%llx+%llu, start=%llx\n", + node->start, node->size, start); + return false; + } + } + + if (end < U64_MAX) { + node = __drm_mm_interval_first(mm, end, U64_MAX); + if (drm_mm_node_allocated(node)) { + KUNIT_FAIL(test, "node after end: node=%llx+%llu, end=%llx\n", + node->start, node->size, end); + return false; + } + } + + return true; +} + +static int __igt_insert_range(struct kunit *test, unsigned int count, u64 size, u64 start, u64 end) +{ + const struct insert_mode *mode; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int n, start_n, end_n; + int ret; + + DRM_MM_BUG_ON(!count); + DRM_MM_BUG_ON(!size); + DRM_MM_BUG_ON(end <= start); + + /* Very similar to __igt_insert(struct kunit *test, ), but now instead of populating the + * full range of the drm_mm, we try to fill a small portion of it. + */ + + ret = -ENOMEM; + nodes = vzalloc(array_size(count, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + ret = -EINVAL; + drm_mm_init(&mm, 0, count * size); + + start_n = div64_u64(start + size - 1, size); + end_n = div64_u64(end - size, size); + + for (mode = insert_modes; mode->name; mode++) { + for (n = start_n; n <= end_n; n++) { + if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n, + start, end, mode)) { + KUNIT_FAIL(test, + "%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n", + mode->name, size, n, start_n, end_n, start, end); + goto out; + } + } + + if (!assert_contiguous_in_range(test, &mm, size, start, end)) { + KUNIT_FAIL(test, + "%s: range [%llx, %llx] not full after initialisation, size=%llu\n", + mode->name, start, end, size); + goto out; + } + + /* Remove one and reinsert, it should refill itself */ + for (n = start_n; n <= end_n; n++) { + u64 addr = nodes[n].start; + + drm_mm_remove_node(&nodes[n]); + if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n, + start, end, mode)) { + KUNIT_FAIL(test, "%s reinsert failed, step %d\n", mode->name, n); + goto out; + } + + if (nodes[n].start != addr) { + KUNIT_FAIL(test, + "%s reinsert node moved, step %d, expected %llx, found %llx\n", + mode->name, n, addr, nodes[n].start); + goto out; + } + } + + if (!assert_contiguous_in_range(test, &mm, size, start, end)) { + KUNIT_FAIL(test, + "%s: range [%llx, %llx] not full after reinsertion, size=%llu\n", + mode->name, start, end, size); + goto out; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + + cond_resched(); + } + + ret = 0; +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); + return ret; +} + +static int insert_outside_range(struct kunit *test) +{ + struct drm_mm mm; + const unsigned int start = 1024; + const unsigned int end = 2048; + const unsigned int size = end - start; + + drm_mm_init(&mm, start, size); + + if (!expect_insert_in_range_fail(test, &mm, 1, 0, start)) + return -EINVAL; + + if (!expect_insert_in_range_fail(test, &mm, size, + start - size / 2, start + (size + 1) / 2)) + return -EINVAL; + + if (!expect_insert_in_range_fail(test, &mm, size, + end - (size + 1) / 2, end + size / 2)) + return -EINVAL; + + if (!expect_insert_in_range_fail(test, &mm, 1, end, end + size)) + return -EINVAL; + + drm_mm_takedown(&mm); + return 0; +} + +static void igt_mm_insert_range(struct kunit *test) +{ + const unsigned int count = min_t(unsigned int, BIT(13), max_iterations); + unsigned int n; + + /* Check that requests outside the bounds of drm_mm are rejected. */ + KUNIT_ASSERT_FALSE(test, insert_outside_range(test)); + + for_each_prime_number_from(n, 1, 50) { + const u64 size = BIT_ULL(n); + const u64 max = count * size; + + KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max)); + KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 1, max)); + KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max - 1)); + KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max / 2)); + KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, max / 2, max / 2)); + KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, + max / 4 + 1, 3 * max / 4 - 1)); + + cond_resched(); + } +} + +static int prepare_igt_frag(struct kunit *test, struct drm_mm *mm, + struct drm_mm_node *nodes, unsigned int num_insert, + const struct insert_mode *mode) +{ + unsigned int size = 4096; + unsigned int i; + + for (i = 0; i < num_insert; i++) { + if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) { + KUNIT_FAIL(test, "%s insert failed\n", mode->name); + return -EINVAL; + } + } + + /* introduce fragmentation by freeing every other node */ + for (i = 0; i < num_insert; i++) { + if (i % 2 == 0) + drm_mm_remove_node(&nodes[i]); + } + + return 0; +} + +static u64 get_insert_time(struct kunit *test, struct drm_mm *mm, + unsigned int num_insert, struct drm_mm_node *nodes, + const struct insert_mode *mode) +{ + unsigned int size = 8192; + ktime_t start; + unsigned int i; + + start = ktime_get(); + for (i = 0; i < num_insert; i++) { + if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) { + KUNIT_FAIL(test, "%s insert failed\n", mode->name); + return 0; + } + } + + return ktime_to_ns(ktime_sub(ktime_get(), start)); +} + +static void igt_mm_frag(struct kunit *test) +{ + struct drm_mm mm; + const struct insert_mode *mode; + struct drm_mm_node *nodes, *node, *next; + unsigned int insert_size = 10000; + unsigned int scale_factor = 4; + + /* We need 4 * insert_size nodes to hold intermediate allocated + * drm_mm nodes. + * 1 times for prepare_igt_frag(struct kunit *test, ) + * 1 times for get_insert_time(struct kunit *test, ) + * 2 times for get_insert_time(struct kunit *test, ) + */ + nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + /* For BOTTOMUP and TOPDOWN, we first fragment the + * address space using prepare_igt_frag(struct kunit *test, ) and then try to verify + * that insertions scale quadratically from 10k to 20k insertions + */ + drm_mm_init(&mm, 1, U64_MAX - 2); + for (mode = insert_modes; mode->name; mode++) { + u64 insert_time1, insert_time2; + + if (mode->mode != DRM_MM_INSERT_LOW && + mode->mode != DRM_MM_INSERT_HIGH) + continue; + + if (prepare_igt_frag(test, &mm, nodes, insert_size, mode)) + goto err; + + insert_time1 = get_insert_time(test, &mm, insert_size, + nodes + insert_size, mode); + if (insert_time1 == 0) + goto err; + + insert_time2 = get_insert_time(test, &mm, (insert_size * 2), + nodes + insert_size * 2, mode); + if (insert_time2 == 0) + goto err; + + kunit_info(test, "%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n", + mode->name, insert_size, insert_size * 2, insert_time1, insert_time2); + + if (insert_time2 > (scale_factor * insert_time1)) { + KUNIT_FAIL(test, "%s fragmented insert took %llu nsecs more\n", + mode->name, insert_time2 - (scale_factor * insert_time1)); + goto err; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + } + +err: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); +} + +static void igt_mm_align(struct kunit *test) +{ + const struct insert_mode *mode; + const unsigned int max_count = min(8192u, max_prime); + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int prime; + + /* For each of the possible insertion modes, we pick a few + * arbitrary alignments and check that the inserted node + * meets our requirements. + */ + + nodes = vzalloc(array_size(max_count, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + drm_mm_init(&mm, 1, U64_MAX - 2); + + for (mode = insert_modes; mode->name; mode++) { + unsigned int i = 0; + + for_each_prime_number_from(prime, 1, max_count) { + u64 size = next_prime_number(prime); + + if (!expect_insert(test, &mm, &nodes[i], size, prime, i, mode)) { + KUNIT_FAIL(test, "%s insert failed with alignment=%d", + mode->name, prime); + goto out; + } + + i++; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + vfree(nodes); +} + +static void igt_align_pot(struct kunit *test, int max) +{ + struct drm_mm mm; + struct drm_mm_node *node, *next; + int bit; + + /* Check that we can align to the full u64 address space */ + + drm_mm_init(&mm, 1, U64_MAX - 2); + + for (bit = max - 1; bit; bit--) { + u64 align, size; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + KUNIT_FAIL(test, "failed to allocate node"); + goto out; + } + + align = BIT_ULL(bit); + size = BIT_ULL(bit - 1) + 1; + if (!expect_insert(test, &mm, node, size, align, bit, &insert_modes[0])) { + KUNIT_FAIL(test, "insert failed with alignment=%llx [%d]", align, bit); + goto out; + } + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) { + drm_mm_remove_node(node); + kfree(node); + } + drm_mm_takedown(&mm); +} + +static void igt_mm_align32(struct kunit *test) +{ + igt_align_pot(test, 32); +} + +static void igt_mm_align64(struct kunit *test) +{ + igt_align_pot(test, 64); +} + +static void show_scan(struct kunit *test, const struct drm_mm_scan *scan) +{ + kunit_info(test, "scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n", + scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color); +} + +static void show_holes(struct kunit *test, const struct drm_mm *mm, int count) +{ + u64 hole_start, hole_end; + struct drm_mm_node *hole; + + drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { + struct drm_mm_node *next = list_next_entry(hole, node_list); + const char *node1 = NULL, *node2 = NULL; + + if (drm_mm_node_allocated(hole)) + node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ", + hole->start, hole->size, hole->color); + + if (drm_mm_node_allocated(next)) + node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]", + next->start, next->size, next->color); + + kunit_info(test, "%sHole [%llx - %llx, size %lld]%s\n", node1, + hole_start, hole_end, hole_end - hole_start, node2); + + kfree(node2); + kfree(node1); + + if (!--count) + break; + } +} + +struct evict_node { + struct drm_mm_node node; + struct list_head link; +}; + +static bool evict_nodes(struct kunit *test, struct drm_mm_scan *scan, + struct evict_node *nodes, unsigned int *order, unsigned int count, + bool use_color, struct list_head *evict_list) +{ + struct evict_node *e, *en; + unsigned int i; + + for (i = 0; i < count; i++) { + e = &nodes[order ? order[i] : i]; + list_add(&e->link, evict_list); + if (drm_mm_scan_add_block(scan, &e->node)) + break; + } + list_for_each_entry_safe(e, en, evict_list, link) { + if (!drm_mm_scan_remove_block(scan, &e->node)) + list_del(&e->link); + } + if (list_empty(evict_list)) { + KUNIT_FAIL(test, + "Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n", + scan->size, count, scan->alignment, scan->color); + return false; + } + + list_for_each_entry(e, evict_list, link) + drm_mm_remove_node(&e->node); + + if (use_color) { + struct drm_mm_node *node; + + while ((node = drm_mm_scan_color_evict(scan))) { + e = container_of(node, typeof(*e), node); + drm_mm_remove_node(&e->node); + list_add(&e->link, evict_list); + } + } else { + if (drm_mm_scan_color_evict(scan)) { + KUNIT_FAIL(test, + "drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); + return false; + } + } + + return true; +} + +static bool evict_nothing(struct kunit *test, struct drm_mm *mm, + unsigned int total_size, struct evict_node *nodes) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + struct drm_mm_node *node; + unsigned int n; + + drm_mm_scan_init(&scan, mm, 1, 0, 0, 0); + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + list_add(&e->link, &evict_list); + drm_mm_scan_add_block(&scan, &e->node); + } + list_for_each_entry(e, &evict_list, link) + drm_mm_scan_remove_block(&scan, &e->node); + + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + + if (!drm_mm_node_allocated(&e->node)) { + KUNIT_FAIL(test, "node[%d] no longer allocated!\n", n); + return false; + } + + e->link.next = NULL; + } + + drm_mm_for_each_node(node, mm) { + e = container_of(node, typeof(*e), node); + e->link.next = &e->link; + } + + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + + if (!e->link.next) { + KUNIT_FAIL(test, "node[%d] no longer connected!\n", n); + return false; + } + } + + return assert_continuous(test, mm, nodes[0].node.size); +} + +static bool evict_everything(struct kunit *test, struct drm_mm *mm, + unsigned int total_size, struct evict_node *nodes) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + unsigned int n; + int err; + + drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0); + for (n = 0; n < total_size; n++) { + e = &nodes[n]; + list_add(&e->link, &evict_list); + if (drm_mm_scan_add_block(&scan, &e->node)) + break; + } + + err = 0; + list_for_each_entry(e, &evict_list, link) { + if (!drm_mm_scan_remove_block(&scan, &e->node)) { + if (!err) { + KUNIT_FAIL(test, "Node %lld not marked for eviction!\n", + e->node.start); + err = -EINVAL; + } + } + } + if (err) + return false; + + list_for_each_entry(e, &evict_list, link) + drm_mm_remove_node(&e->node); + + if (!assert_one_hole(test, mm, 0, total_size)) + return false; + + list_for_each_entry(e, &evict_list, link) { + err = drm_mm_reserve_node(mm, &e->node); + if (err) { + KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n", + e->node.start); + return false; + } + } + + return assert_continuous(test, mm, nodes[0].node.size); +} + +static int evict_something(struct kunit *test, struct drm_mm *mm, + u64 range_start, u64 range_end, struct evict_node *nodes, + unsigned int *order, unsigned int count, unsigned int size, + unsigned int alignment, const struct insert_mode *mode) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + struct drm_mm_node tmp; + int err; + + drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start, + range_end, mode->mode); + if (!evict_nodes(test, &scan, nodes, order, count, false, &evict_list)) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, + DRM_MM_INSERT_EVICT); + if (err) { + KUNIT_FAIL(test, "Failed to insert into eviction hole: size=%d, align=%d\n", + size, alignment); + show_scan(test, &scan); + show_holes(test, mm, 3); + return err; + } + + if (tmp.start < range_start || tmp.start + tmp.size > range_end) { + KUNIT_FAIL(test, + "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", + tmp.start, tmp.size, range_start, range_end); + err = -EINVAL; + } + + if (!assert_node(test, &tmp, mm, size, alignment, 0) || + drm_mm_hole_follows(&tmp)) { + KUNIT_FAIL(test, + "Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n", + tmp.size, size, alignment, misalignment(&tmp, alignment), + tmp.start, drm_mm_hole_follows(&tmp)); + err = -EINVAL; + } + + drm_mm_remove_node(&tmp); + if (err) + return err; + + list_for_each_entry(e, &evict_list, link) { + err = drm_mm_reserve_node(mm, &e->node); + if (err) { + KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n", + e->node.start); + return err; + } + } + + if (!assert_continuous(test, mm, nodes[0].node.size)) { + KUNIT_FAIL(test, "range is no longer continuous\n"); + return -EINVAL; + } + + return 0; +} + +static void igt_mm_evict(struct kunit *test) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int size = 8192; + const struct insert_mode *mode; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + + /* Here we populate a full drm_mm and then try and insert a new node + * by evicting other nodes in a random order. The drm_mm_scan should + * pick the first matching hole it finds from the random list. We + * repeat that for different allocation strategies, alignments and + * sizes to try and stress the hole finder. + */ + + nodes = vzalloc(array_size(size, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + order = drm_random_order(size, &prng); + if (!order) + goto err_nodes; + + drm_mm_init(&mm, 0, size); + for (n = 0; n < size; n++) { + if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) { + KUNIT_FAIL(test, "insert failed, step %d\n", n); + goto out; + } + } + + /* First check that using the scanner doesn't break the mm */ + if (!evict_nothing(test, &mm, size, nodes)) { + KUNIT_FAIL(test, "evict_nothing() failed\n"); + goto out; + } + if (!evict_everything(test, &mm, size, nodes)) { + KUNIT_FAIL(test, "evict_everything() failed\n"); + goto out; + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= size; n <<= 1) { + drm_random_reorder(order, size, &prng); + if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, n, 1, + mode)) { + KUNIT_FAIL(test, "%s evict_something(size=%u) failed\n", + mode->name, n); + goto out; + } + } + + for (n = 1; n < size; n <<= 1) { + drm_random_reorder(order, size, &prng); + if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, + size / 2, n, mode)) { + KUNIT_FAIL(test, + "%s evict_something(size=%u, alignment=%u) failed\n", + mode->name, size / 2, n); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(size, max_prime)) { + unsigned int nsize = (size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, size, &prng); + if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, + nsize, n, mode)) { + KUNIT_FAIL(test, + "%s evict_something(size=%u, alignment=%u) failed\n", + mode->name, nsize, n); + goto out; + } + } + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +} + +static void igt_mm_evict_range(struct kunit *test) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int size = 8192; + const unsigned int range_size = size / 2; + const unsigned int range_start = size / 4; + const unsigned int range_end = range_start + range_size; + const struct insert_mode *mode; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + + /* Like igt_evict() but now we are limiting the search to a + * small portion of the full drm_mm. + */ + + nodes = vzalloc(array_size(size, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + order = drm_random_order(size, &prng); + if (!order) + goto err_nodes; + + drm_mm_init(&mm, 0, size); + for (n = 0; n < size; n++) { + if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) { + KUNIT_FAIL(test, "insert failed, step %d\n", n); + goto out; + } + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= range_size; n <<= 1) { + drm_random_reorder(order, size, &prng); + if (evict_something(test, &mm, range_start, range_end, nodes, + order, size, n, 1, mode)) { + KUNIT_FAIL(test, + "%s evict_something(size=%u) failed with range [%u, %u]\n", + mode->name, n, range_start, range_end); + goto out; + } + } + + for (n = 1; n <= range_size; n <<= 1) { + drm_random_reorder(order, size, &prng); + if (evict_something(test, &mm, range_start, range_end, nodes, + order, size, range_size / 2, n, mode)) { + KUNIT_FAIL(test, + "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", + mode->name, range_size / 2, n, range_start, range_end); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(range_size, max_prime)) { + unsigned int nsize = (range_size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, size, &prng); + if (evict_something(test, &mm, range_start, range_end, nodes, + order, size, nsize, n, mode)) { + KUNIT_FAIL(test, + "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n", + mode->name, nsize, n, range_start, range_end); + goto out; + } + } + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +} + +static unsigned int node_index(const struct drm_mm_node *node) +{ + return div64_u64(node->start, node->size); +} + +static void igt_mm_topdown(struct kunit *test) +{ + const struct insert_mode *topdown = &insert_modes[TOPDOWN]; + + DRM_RND_STATE(prng, random_seed); + const unsigned int count = 8192; + unsigned int size; + unsigned long *bitmap; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + + /* When allocating top-down, we expect to be returned a node + * from a suitable hole at the top of the drm_mm. We check that + * the returned node does match the highest available slot. + */ + + nodes = vzalloc(array_size(count, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + bitmap = bitmap_zalloc(count, GFP_KERNEL); + if (!bitmap) + goto err_nodes; + + order = drm_random_order(count, &prng); + if (!order) + goto err_bitmap; + + for (size = 1; size <= 64; size <<= 1) { + drm_mm_init(&mm, 0, size * count); + for (n = 0; n < count; n++) { + if (!expect_insert(test, &mm, &nodes[n], size, 0, n, topdown)) { + KUNIT_FAIL(test, "insert failed, size %u step %d\n", size, n); + goto out; + } + + if (drm_mm_hole_follows(&nodes[n])) { + KUNIT_FAIL(test, + "hole after topdown insert %d, start=%llx\n, size=%u", + n, nodes[n].start, size); + goto out; + } + + if (!assert_one_hole(test, &mm, 0, size * (count - n - 1))) + goto out; + } + + if (!assert_continuous(test, &mm, size)) + goto out; + + drm_random_reorder(order, count, &prng); + for_each_prime_number_from(n, 1, min(count, max_prime)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + __set_bit(node_index(node), bitmap); + } + + for (m = 0; m < n; m++) { + unsigned int last; + + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(test, &mm, node, size, 0, 0, topdown)) { + KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n); + goto out; + } + + if (drm_mm_hole_follows(node)) { + KUNIT_FAIL(test, + "hole after topdown insert %d/%d, start=%llx\n", + m, n, node->start); + goto out; + } + + last = find_last_bit(bitmap, count); + if (node_index(node) != last) { + KUNIT_FAIL(test, + "node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n", + m, n, size, last, node_index(node)); + goto out; + } + + __clear_bit(last, bitmap); + } + + DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); + + o += n; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_bitmap: + bitmap_free(bitmap); +err_nodes: + vfree(nodes); +} + +static void igt_mm_bottomup(struct kunit *test) +{ + const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; + + DRM_RND_STATE(prng, random_seed); + const unsigned int count = 8192; + unsigned int size; + unsigned long *bitmap; + struct drm_mm mm; + struct drm_mm_node *nodes, *node, *next; + unsigned int *order, n, m, o = 0; + + /* Like igt_topdown, but instead of searching for the last hole, + * we search for the first. + */ + + nodes = vzalloc(array_size(count, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + bitmap = bitmap_zalloc(count, GFP_KERNEL); + if (!bitmap) + goto err_nodes; + + order = drm_random_order(count, &prng); + if (!order) + goto err_bitmap; + + for (size = 1; size <= 64; size <<= 1) { + drm_mm_init(&mm, 0, size * count); + for (n = 0; n < count; n++) { + if (!expect_insert(test, &mm, &nodes[n], size, 0, n, bottomup)) { + KUNIT_FAIL(test, + "bottomup insert failed, size %u step %d\n", size, n); + goto out; + } + + if (!assert_one_hole(test, &mm, size * (n + 1), size * count)) + goto out; + } + + if (!assert_continuous(test, &mm, size)) + goto out; + + drm_random_reorder(order, count, &prng); + for_each_prime_number_from(n, 1, min(count, max_prime)) { + for (m = 0; m < n; m++) { + node = &nodes[order[(o + m) % count]]; + drm_mm_remove_node(node); + __set_bit(node_index(node), bitmap); + } + + for (m = 0; m < n; m++) { + unsigned int first; + + node = &nodes[order[(o + m) % count]]; + if (!expect_insert(test, &mm, node, size, 0, 0, bottomup)) { + KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n); + goto out; + } + + first = find_first_bit(bitmap, count); + if (node_index(node) != first) { + KUNIT_FAIL(test, + "node %d/%d not inserted into bottom hole, expected %d, found %d\n", + m, n, first, node_index(node)); + goto out; + } + __clear_bit(first, bitmap); + } + + DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count); + + o += n; + } + + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + DRM_MM_BUG_ON(!drm_mm_clean(&mm)); + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_bitmap: + bitmap_free(bitmap); +err_nodes: + vfree(nodes); +} + +static void __igt_once(struct kunit *test, unsigned int mode) +{ + struct drm_mm mm; + struct drm_mm_node rsvd_lo, rsvd_hi, node; + + drm_mm_init(&mm, 0, 7); + + memset(&rsvd_lo, 0, sizeof(rsvd_lo)); + rsvd_lo.start = 1; + rsvd_lo.size = 1; + if (drm_mm_reserve_node(&mm, &rsvd_lo)) { + KUNIT_FAIL(test, "Could not reserve low node\n"); + goto err; + } + + memset(&rsvd_hi, 0, sizeof(rsvd_hi)); + rsvd_hi.start = 5; + rsvd_hi.size = 1; + if (drm_mm_reserve_node(&mm, &rsvd_hi)) { + KUNIT_FAIL(test, "Could not reserve low node\n"); + goto err_lo; + } + + if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) { + KUNIT_FAIL(test, "Expected a hole after lo and high nodes!\n"); + goto err_hi; + } + + memset(&node, 0, sizeof(node)); + if (drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode)) { + KUNIT_FAIL(test, "Could not insert the node into the available hole!\n"); + goto err_hi; + } + + drm_mm_remove_node(&node); +err_hi: + drm_mm_remove_node(&rsvd_hi); +err_lo: + drm_mm_remove_node(&rsvd_lo); +err: + drm_mm_takedown(&mm); +} + +static void igt_mm_lowest(struct kunit *test) +{ + __igt_once(test, DRM_MM_INSERT_LOW); +} + +static void igt_mm_highest(struct kunit *test) +{ + __igt_once(test, DRM_MM_INSERT_HIGH); +} + +static void separate_adjacent_colors(const struct drm_mm_node *node, + unsigned long color, u64 *start, u64 *end) +{ + if (drm_mm_node_allocated(node) && node->color != color) + ++*start; + + node = list_next_entry(node, node_list); + if (drm_mm_node_allocated(node) && node->color != color) + --*end; +} + +static bool colors_abutt(struct kunit *test, const struct drm_mm_node *node) +{ + if (!drm_mm_hole_follows(node) && + drm_mm_node_allocated(list_next_entry(node, node_list))) { + KUNIT_FAIL(test, "colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n", + node->color, node->start, node->size, + list_next_entry(node, node_list)->color, + list_next_entry(node, node_list)->start, + list_next_entry(node, node_list)->size); + return true; + } + + return false; +} + +static void igt_mm_color(struct kunit *test) +{ + const unsigned int count = min(4096u, max_iterations); + const struct insert_mode *mode; + struct drm_mm mm; + struct drm_mm_node *node, *nn; + unsigned int n; + + /* Color adjustment complicates everything. First we just check + * that when we insert a node we apply any color_adjustment callback. + * The callback we use should ensure that there is a gap between + * any two nodes, and so after each insertion we check that those + * holes are inserted and that they are preserved. + */ + + drm_mm_init(&mm, 0, U64_MAX); + + for (n = 1; n <= count; n++) { + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + goto out; + + if (!expect_insert(test, &mm, node, n, 0, n, &insert_modes[0])) { + KUNIT_FAIL(test, "insert failed, step %d\n", n); + kfree(node); + goto out; + } + } + + drm_mm_for_each_node_safe(node, nn, &mm) { + if (node->color != node->size) { + KUNIT_FAIL(test, "invalid color stored: expected %lld, found %ld\n", + node->size, node->color); + + goto out; + } + + drm_mm_remove_node(node); + kfree(node); + } + + /* Now, let's start experimenting with applying a color callback */ + mm.color_adjust = separate_adjacent_colors; + for (mode = insert_modes; mode->name; mode++) { + u64 last; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + goto out; + + node->size = 1 + 2 * count; + node->color = node->size; + + if (drm_mm_reserve_node(&mm, node)) { + KUNIT_FAIL(test, "initial reserve failed!\n"); + goto out; + } + + last = node->start + node->size; + + for (n = 1; n <= count; n++) { + int rem; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + goto out; + + node->start = last; + node->size = n + count; + node->color = node->size; + + if (drm_mm_reserve_node(&mm, node) != -ENOSPC) { + KUNIT_FAIL(test, "reserve %d did not report color overlap!", n); + goto out; + } + + node->start += n + 1; + rem = misalignment(node, n + count); + node->start += n + count - rem; + + if (drm_mm_reserve_node(&mm, node)) { + KUNIT_FAIL(test, "reserve %d failed", n); + goto out; + } + + last = node->start + node->size; + } + + for (n = 1; n <= count; n++) { + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + goto out; + + if (!expect_insert(test, &mm, node, n, n, n, mode)) { + KUNIT_FAIL(test, "%s insert failed, step %d\n", mode->name, n); + kfree(node); + goto out; + } + } + + drm_mm_for_each_node_safe(node, nn, &mm) { + u64 rem; + + if (node->color != node->size) { + KUNIT_FAIL(test, + "%s invalid color stored: expected %lld, found %ld\n", + mode->name, node->size, node->color); + + goto out; + } + + if (colors_abutt(test, node)) + goto out; + + div64_u64_rem(node->start, node->size, &rem); + if (rem) { + KUNIT_FAIL(test, + "%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n", + mode->name, node->start, node->size, rem); + goto out; + } + + drm_mm_remove_node(node); + kfree(node); + } + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, nn, &mm) { + drm_mm_remove_node(node); + kfree(node); + } + drm_mm_takedown(&mm); +} + +static int evict_color(struct kunit *test, struct drm_mm *mm, u64 range_start, + u64 range_end, struct evict_node *nodes, unsigned int *order, + unsigned int count, unsigned int size, unsigned int alignment, + unsigned long color, const struct insert_mode *mode) +{ + struct drm_mm_scan scan; + LIST_HEAD(evict_list); + struct evict_node *e; + struct drm_mm_node tmp; + int err; + + drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start, + range_end, mode->mode); + if (!evict_nodes(test, &scan, nodes, order, count, true, &evict_list)) + return -EINVAL; + + memset(&tmp, 0, sizeof(tmp)); + err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, + DRM_MM_INSERT_EVICT); + if (err) { + KUNIT_FAIL(test, + "Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", + size, alignment, color, err); + show_scan(test, &scan); + show_holes(test, mm, 3); + return err; + } + + if (tmp.start < range_start || tmp.start + tmp.size > range_end) { + KUNIT_FAIL(test, + "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", + tmp.start, tmp.size, range_start, range_end); + err = -EINVAL; + } + + if (colors_abutt(test, &tmp)) + err = -EINVAL; + + if (!assert_node(test, &tmp, mm, size, alignment, color)) { + KUNIT_FAIL(test, + "Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n", + tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start); + err = -EINVAL; + } + + drm_mm_remove_node(&tmp); + if (err) + return err; + + list_for_each_entry(e, &evict_list, link) { + err = drm_mm_reserve_node(mm, &e->node); + if (err) { + KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n", + e->node.start); + return err; + } + } + + cond_resched(); + return 0; +} + +static void igt_mm_color_evict(struct kunit *test) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int total_size = min(8192u, max_iterations); + const struct insert_mode *mode; + unsigned long color = 0; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + + /* Check that the drm_mm_scan also honours color adjustment when + * choosing its victims to create a hole. Our color_adjust does not + * allow two nodes to be placed together without an intervening hole + * enlarging the set of victims that must be evicted. + */ + + nodes = vzalloc(array_size(total_size, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + order = drm_random_order(total_size, &prng); + if (!order) + goto err_nodes; + + drm_mm_init(&mm, 0, 2 * total_size - 1); + mm.color_adjust = separate_adjacent_colors; + for (n = 0; n < total_size; n++) { + if (!expect_insert(test, &mm, &nodes[n].node, + 1, 0, color++, + &insert_modes[0])) { + KUNIT_FAIL(test, "insert failed, step %d\n", n); + goto out; + } + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= total_size; n <<= 1) { + drm_random_reorder(order, total_size, &prng); + if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size, + n, 1, color++, mode)) { + KUNIT_FAIL(test, "%s evict_color(size=%u) failed\n", mode->name, n); + goto out; + } + } + + for (n = 1; n < total_size; n <<= 1) { + drm_random_reorder(order, total_size, &prng); + if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size, + total_size / 2, n, color++, mode)) { + KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n", + mode->name, total_size / 2, n); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(total_size, max_prime)) { + unsigned int nsize = (total_size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, total_size, &prng); + if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size, + nsize, n, color++, mode)) { + KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n", + mode->name, nsize, n); + goto out; + } + } + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +} + +static void igt_mm_color_evict_range(struct kunit *test) +{ + DRM_RND_STATE(prng, random_seed); + const unsigned int total_size = 8192; + const unsigned int range_size = total_size / 2; + const unsigned int range_start = total_size / 4; + const unsigned int range_end = range_start + range_size; + const struct insert_mode *mode; + unsigned long color = 0; + struct drm_mm mm; + struct evict_node *nodes; + struct drm_mm_node *node, *next; + unsigned int *order, n; + + /* Like igt_color_evict(), but limited to small portion of the full + * drm_mm range. + */ + + nodes = vzalloc(array_size(total_size, sizeof(*nodes))); + KUNIT_ASSERT_TRUE(test, nodes); + + order = drm_random_order(total_size, &prng); + if (!order) + goto err_nodes; + + drm_mm_init(&mm, 0, 2 * total_size - 1); + mm.color_adjust = separate_adjacent_colors; + for (n = 0; n < total_size; n++) { + if (!expect_insert(test, &mm, &nodes[n].node, + 1, 0, color++, + &insert_modes[0])) { + KUNIT_FAIL(test, "insert failed, step %d\n", n); + goto out; + } + } + + for (mode = evict_modes; mode->name; mode++) { + for (n = 1; n <= range_size; n <<= 1) { + drm_random_reorder(order, range_size, &prng); + if (evict_color(test, &mm, range_start, range_end, nodes, order, + total_size, n, 1, color++, mode)) { + KUNIT_FAIL(test, + "%s evict_color(size=%u) failed for range [%x, %x]\n", + mode->name, n, range_start, range_end); + goto out; + } + } + + for (n = 1; n < range_size; n <<= 1) { + drm_random_reorder(order, total_size, &prng); + if (evict_color(test, &mm, range_start, range_end, nodes, order, + total_size, range_size / 2, n, color++, mode)) { + KUNIT_FAIL(test, + "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", + mode->name, total_size / 2, n, range_start, range_end); + goto out; + } + } + + for_each_prime_number_from(n, 1, min(range_size, max_prime)) { + unsigned int nsize = (range_size - n + 1) / 2; + + DRM_MM_BUG_ON(!nsize); + + drm_random_reorder(order, total_size, &prng); + if (evict_color(test, &mm, range_start, range_end, nodes, order, + total_size, nsize, n, color++, mode)) { + KUNIT_FAIL(test, + "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n", + mode->name, nsize, n, range_start, range_end); + goto out; + } + } + + cond_resched(); + } + +out: + drm_mm_for_each_node_safe(node, next, &mm) + drm_mm_remove_node(node); + drm_mm_takedown(&mm); + kfree(order); +err_nodes: + vfree(nodes); +} + +static int drm_mm_init_test(struct kunit *test) +{ + while (!random_seed) + random_seed = get_random_int(); + + return 0; +} + +module_param(random_seed, uint, 0400); +module_param(max_iterations, uint, 0400); +module_param(max_prime, uint, 0400); + +static struct kunit_case drm_mm_tests[] = { + KUNIT_CASE(igt_mm_init), + KUNIT_CASE(igt_mm_debug), + KUNIT_CASE(igt_mm_reserve), + KUNIT_CASE(igt_mm_insert), + KUNIT_CASE(igt_mm_replace), + KUNIT_CASE(igt_mm_insert_range), + KUNIT_CASE(igt_mm_frag), + KUNIT_CASE(igt_mm_align), + KUNIT_CASE(igt_mm_align32), + KUNIT_CASE(igt_mm_align64), + KUNIT_CASE(igt_mm_evict), + KUNIT_CASE(igt_mm_evict_range), + KUNIT_CASE(igt_mm_topdown), + KUNIT_CASE(igt_mm_bottomup), + KUNIT_CASE(igt_mm_lowest), + KUNIT_CASE(igt_mm_highest), + KUNIT_CASE(igt_mm_color), + KUNIT_CASE(igt_mm_color_evict), + KUNIT_CASE(igt_mm_color_evict_range), + {} +}; + +static struct kunit_suite drm_mm_test_suite = { + .name = "drm_mm", + .init = drm_mm_init_test, + .test_cases = drm_mm_tests, +}; + +kunit_test_suite(drm_mm_test_suite); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 75f664903d8672897333b86bb450335ec6486ad5 Mon Sep 17 00:00:00 2001 From: Jouni Högander Date: Mon, 11 Jul 2022 14:17:50 +0300 Subject: drm/i915/display: Ensure PSR gets disabled if no encoders in new state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently PSR is left enabled when all planes are disabled if there is no attached encoder in new state. This seems to be causing FIFO underruns. Fix this by checking if encoder exists in new crtc state and disable PSR if it doesn't. v2: Unify disable logic with existing Cc: Mika Kahola Reported-by: Stanislav Lisovskiy Signed-off-by: Jouni Högander Signed-off-by: Stanislav Lisovskiy Reviewed-by: Stanislav Lisovskiy Signed-off-by: Stanislav Lisovskiy Link: https://patchwork.freedesktop.org/patch/msgid/20220711111750.881552-1-jouni.hogander@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 53 +++++++++++++++++++------------- 1 file changed, 31 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index e6a870641cd2..90599dd1cb1b 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1863,36 +1863,45 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc) { struct drm_i915_private *i915 = to_i915(state->base.dev); - const struct intel_crtc_state *crtc_state = - intel_atomic_get_new_crtc_state(state, crtc); - struct intel_encoder *encoder; + struct intel_crtc_state *new_crtc_state, *old_crtc_state; + int i; if (!HAS_PSR(i915)) return; - for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, - crtc_state->uapi.encoder_mask) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct intel_psr *psr = &intel_dp->psr; - bool needs_to_disable = false; + for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, + new_crtc_state, i) { + struct intel_encoder *encoder; + u32 old_new_encoder_mask = old_crtc_state->uapi.encoder_mask | + new_crtc_state->uapi.encoder_mask; - mutex_lock(&psr->lock); + for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, + old_new_encoder_mask) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_psr *psr = &intel_dp->psr; + bool needs_to_disable = false; - /* - * Reasons to disable: - * - PSR disabled in new state - * - All planes will go inactive - * - Changing between PSR versions - */ - needs_to_disable |= intel_crtc_needs_modeset(crtc_state); - needs_to_disable |= !crtc_state->has_psr; - needs_to_disable |= !crtc_state->active_planes; - needs_to_disable |= crtc_state->has_psr2 != psr->psr2_enabled; + mutex_lock(&psr->lock); - if (psr->enabled && needs_to_disable) - intel_psr_disable_locked(intel_dp); + /* + * Reasons to disable: + * - PSR disabled in new state + * - All planes will go inactive + * - Changing between PSR versions + * - Encoder isn't present in new mask + */ + needs_to_disable |= intel_crtc_needs_modeset(new_crtc_state); + needs_to_disable |= !new_crtc_state->has_psr; + needs_to_disable |= !new_crtc_state->active_planes; + needs_to_disable |= new_crtc_state->has_psr2 != psr->psr2_enabled; + needs_to_disable |= !(new_crtc_state->uapi.encoder_mask & + drm_encoder_mask(&(encoder)->base)); - mutex_unlock(&psr->lock); + if (psr->enabled && needs_to_disable) + intel_psr_disable_locked(intel_dp); + + mutex_unlock(&psr->lock); + } } } -- cgit v1.2.3-59-g8ed1b From 81cb44274c654e49f55e6690079540b0bd177db0 Mon Sep 17 00:00:00 2001 From: Matt Roper Date: Fri, 8 Jul 2022 14:58:04 -0700 Subject: drm/i915: Add Wa_14016291713 We already disable FBC when PSR2 is enabled on display version 12 and above; this new workaround now requires that we do the same with PSR1 on display versions 12 and 13. Signed-off-by: Matt Roper Reviewed-by: Arun R Murthy Link: https://patchwork.freedesktop.org/patch/msgid/20220708215804.2889246-2-matthew.d.roper@intel.com --- drivers/gpu/drm/i915/display/intel_fbc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_fbc.c b/drivers/gpu/drm/i915/display/intel_fbc.c index 8b807284cde1..33f52feb622a 100644 --- a/drivers/gpu/drm/i915/display/intel_fbc.c +++ b/drivers/gpu/drm/i915/display/intel_fbc.c @@ -1097,6 +1097,12 @@ static int intel_fbc_check_plane(struct intel_atomic_state *state, return 0; } + /* Wa_14016291713 */ + if (IS_DISPLAY_VER(i915, 12, 13) && crtc_state->has_psr) { + plane_state->no_fbc_reason = "PSR1 enabled (Wa_14016291713)"; + return 0; + } + if (!pixel_format_is_valid(plane_state)) { plane_state->no_fbc_reason = "pixel format not supported"; return 0; -- cgit v1.2.3-59-g8ed1b From 668a8f17b5290d04ef7343636a5588a0692731a1 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:31 +0200 Subject: drm/mipi-dsi: Detach devices when removing the host Whenever the MIPI-DSI host is unregistered, the code of mipi_dsi_host_unregister() loops over every device currently found on that bus and will unregister it. However, it doesn't detach it from the bus first, which leads to all kind of resource leaks if the host wants to perform some clean up whenever a device is detached. Fixes: 068a00233969 ("drm: Add MIPI DSI bus support") Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-2-maxime@cerno.tech --- drivers/gpu/drm/drm_mipi_dsi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index 2e25804d6ffa..3ec02748d56f 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -346,6 +346,7 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv) { struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); + mipi_dsi_detach(dsi); mipi_dsi_device_unregister(dsi); return 0; -- cgit v1.2.3-59-g8ed1b From 917dd05418148f05d8860b8106da8dcd1d258aaf Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:32 +0200 Subject: drm/crtc: Introduce drmm_crtc_init_with_planes The DRM-managed function to register a CRTC is drmm_crtc_alloc_with_planes(), which will allocate the underlying structure and initialisation the CRTC. However, we might want to separate the structure creation and the CRTC initialisation, for example if the structure is shared across multiple DRM entities, for example an encoder and a connector. Let's create an helper to only initialise a CRTC that would be passed as an argument. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-3-maxime@cerno.tech --- drivers/gpu/drm/drm_crtc.c | 94 ++++++++++++++++++++++++++++++++++++++++------ include/drm/drm_crtc.h | 9 +++++ 2 files changed, 91 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index cad2a7e5166f..df9bf3c9206e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -343,9 +343,10 @@ static int __drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc * * The @primary and @cursor planes are only relevant for legacy uAPI, see * &drm_crtc.primary and &drm_crtc.cursor. * - * Note: consider using drmm_crtc_alloc_with_planes() instead of - * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure - * take care of cleanup and deallocation. + * Note: consider using drmm_crtc_alloc_with_planes() or + * drmm_crtc_init_with_planes() instead of drm_crtc_init_with_planes() + * to let the DRM managed resource infrastructure take care of cleanup + * and deallocation. * * Returns: * Zero on success, error code on failure. @@ -370,14 +371,88 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_crtc_init_with_planes); -static void drmm_crtc_alloc_with_planes_cleanup(struct drm_device *dev, - void *ptr) +static void drmm_crtc_init_with_planes_cleanup(struct drm_device *dev, + void *ptr) { struct drm_crtc *crtc = ptr; drm_crtc_cleanup(crtc); } +__printf(6, 0) +static int __drmm_crtc_init_with_planes(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, + va_list args) +{ + int ret; + + drm_WARN_ON(dev, funcs && funcs->destroy); + + ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, args); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(dev, drmm_crtc_init_with_planes_cleanup, + crtc); + if (ret) + return ret; + + return 0; +} + +/** + * drmm_crtc_init_with_planes - Initialise a new CRTC object with + * specified primary and cursor planes. + * @dev: DRM device + * @crtc: CRTC object to init + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC + * @funcs: callbacks for the new CRTC + * @name: printf style format string for the CRTC name, or NULL for default name + * + * Inits a new object created as base part of a driver crtc object. Drivers + * should use this function instead of drm_crtc_init(), which is only provided + * for backwards compatibility with drivers which do not yet support universal + * planes). For really simple hardware which has only 1 plane look at + * drm_simple_display_pipe_init() instead. + * + * Cleanup is automatically handled through registering + * drmm_crtc_cleanup() with drmm_add_action(). The crtc structure should + * be allocated with drmm_kzalloc(). + * + * The @drm_crtc_funcs.destroy hook must be NULL. + * + * The @primary and @cursor planes are only relevant for legacy uAPI, see + * &drm_crtc.primary and &drm_crtc.cursor. + * + * Returns: + * Zero on success, error code on failure. + */ +int drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...) +{ + va_list ap; + int ret; + + va_start(ap, name); + ret = __drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, ap); + va_end(ap); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_crtc_init_with_planes); + void *__drmm_crtc_alloc_with_planes(struct drm_device *dev, size_t size, size_t offset, struct drm_plane *primary, @@ -400,17 +475,12 @@ void *__drmm_crtc_alloc_with_planes(struct drm_device *dev, crtc = container + offset; va_start(ap, name); - ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, - name, ap); + ret = __drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, + name, ap); va_end(ap); if (ret) return ERR_PTR(ret); - ret = drmm_add_action_or_reset(dev, drmm_crtc_alloc_with_planes_cleanup, - crtc); - if (ret) - return ERR_PTR(ret); - return container; } EXPORT_SYMBOL(__drmm_crtc_alloc_with_planes); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index ffc1cde331d3..8e1cbc75143e 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1216,6 +1216,15 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_plane *cursor, const struct drm_crtc_funcs *funcs, const char *name, ...); + +__printf(6, 7) +int drmm_crtc_init_with_planes(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_plane *primary, + struct drm_plane *cursor, + const struct drm_crtc_funcs *funcs, + const char *name, ...); + void drm_crtc_cleanup(struct drm_crtc *crtc); __printf(7, 8) -- cgit v1.2.3-59-g8ed1b From f134c9cd9ce829cc66d4d32c57b76de1aab54fe9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:33 +0200 Subject: drm/encoder: Introduce drmm_encoder_init The DRM-managed function to register an encoder is drmm_encoder_alloc() and its variants, which will allocate the underlying structure and initialisation the encoder. However, we might want to separate the structure creation and the encoder initialisation, for example if the structure is shared across multiple DRM entities, for example an encoder and a connector. Let's create an helper to only initialise an encoder that would be passed as an argument. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-4-maxime@cerno.tech --- drivers/gpu/drm/drm_encoder.c | 75 ++++++++++++++++++++++++++++++++++++------- include/drm/drm_encoder.h | 6 ++++ 2 files changed, 70 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index a940024c8087..1143bc7f3252 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" @@ -148,9 +149,9 @@ out_put: * the encoder structure. The encoder structure should not be allocated with * devm_kzalloc(). * - * Note: consider using drmm_encoder_alloc() instead of drm_encoder_init() to - * let the DRM managed resource infrastructure take care of cleanup and - * deallocation. + * Note: consider using drmm_encoder_alloc() or drmm_encoder_init() + * instead of drm_encoder_init() to let the DRM managed resource + * infrastructure take care of cleanup and deallocation. * * Returns: * Zero on success, error code on failure. @@ -212,6 +213,30 @@ static void drmm_encoder_alloc_release(struct drm_device *dev, void *ptr) drm_encoder_cleanup(encoder); } +__printf(5, 0) +static int __drmm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, + const char *name, + va_list args) +{ + int ret; + + if (drm_WARN_ON(dev, funcs && funcs->destroy)) + return -EINVAL; + + ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, args); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder); + if (ret) + return ret; + + return 0; +} + void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...) @@ -221,9 +246,6 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, va_list ap; int ret; - if (WARN_ON(funcs && funcs->destroy)) - return ERR_PTR(-EINVAL); - container = drmm_kzalloc(dev, size, GFP_KERNEL); if (!container) return ERR_PTR(-ENOMEM); @@ -231,19 +253,50 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, encoder = container + offset; va_start(ap, name); - ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap); + ret = __drmm_encoder_init(dev, encoder, funcs, encoder_type, name, ap); va_end(ap); if (ret) return ERR_PTR(ret); - ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder); - if (ret) - return ERR_PTR(ret); - return container; } EXPORT_SYMBOL(__drmm_encoder_alloc); +/** + * drmm_encoder_init - Initialize a preallocated encoder + * @dev: drm device + * @encoder: the encoder to init + * @funcs: callbacks for this encoder (optional) + * @encoder_type: user visible type of the encoder + * @name: printf style format string for the encoder name, or NULL for default name + * + * Initializes a preallocated encoder. Encoder should be subclassed as + * part of driver encoder objects. Cleanup is automatically handled + * through registering drm_encoder_cleanup() with drmm_add_action(). The + * encoder structure should be allocated with drmm_kzalloc(). + * + * The @drm_encoder_funcs.destroy hook must be NULL. + * + * Returns: + * Zero on success, error code on failure. + */ +int drmm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...) +{ + va_list ap; + int ret; + + va_start(ap, name); + ret = __drmm_encoder_init(dev, encoder, funcs, encoder_type, name, ap); + va_end(ap); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_encoder_init); + static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) { struct drm_connector *connector; diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 6e91a0280f31..3a09682af685 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -194,6 +194,12 @@ int drm_encoder_init(struct drm_device *dev, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...); +__printf(5, 6) +int drmm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...); + __printf(6, 7) void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, -- cgit v1.2.3-59-g8ed1b From d71d8a4b8d10e4e2002d21940a768b389d594637 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:34 +0200 Subject: drm/connector: Reorder headers Unlike most of the other files in DRM, and Linux in general, the headers in drm_connector.c aren't sorted alphabetically. Let's fix that. Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-5-maxime@cerno.tech --- drivers/gpu/drm/drm_connector.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 1ab083b35e3b..d64ee257330e 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -22,15 +22,15 @@ #include #include +#include #include #include +#include #include -#include #include -#include -#include #include #include +#include #include #include -- cgit v1.2.3-59-g8ed1b From 00ec947c144b374ceb3fe1b4bd2e9ea7454dd630 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:35 +0200 Subject: drm/connector: Mention the cleanup after drm_connector_init Unlike encoders and CRTCs, the drm_connector_init() and drm_connector_init_with_ddc() don't mention how the cleanup is supposed to be done. Let's add it. Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-6-maxime@cerno.tech --- drivers/gpu/drm/drm_connector.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index d64ee257330e..5e98e8651780 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -224,6 +224,10 @@ void drm_connector_free_work_fn(struct work_struct *work) * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * + * At driver unload time the driver's &drm_connector_funcs.destroy hook + * should call drm_connector_cleanup() and free the connector structure. + * The connector structure should not be allocated with devm_kzalloc(). + * * Returns: * Zero on success, error code on failure. */ @@ -347,6 +351,10 @@ EXPORT_SYMBOL(drm_connector_init); * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * + * At driver unload time the driver's &drm_connector_funcs.destroy hook + * should call drm_connector_cleanup() and free the connector structure. + * The connector structure should not be allocated with devm_kzalloc(). + * * Ensures that the ddc field of the connector is correctly set. * * Returns: -- cgit v1.2.3-59-g8ed1b From d87fbea50b819b76f180bc8420c5f9efa0670deb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:36 +0200 Subject: drm/connector: Clarify when drm_connector_unregister is needed The current documentation for drm_connector_unregister() mentions that it's needed for connectors that have been registered through drm_dev_register(). However, this was a typo and was meant to be drm_connector_register(), which only applies to connectors registered after drm_dev_register() has been called. In addition, it was also mentioning that connectors are unregistered automatically when drm_dev_unregister() is called. This part is a bit misleading, since it might make it appear that drm_connector_unregister() applies either to all connectors, or none of them. After discussing it with Daniel, it appears that we always need to call drm_connector_unregister() on connectors that have been registered with drm_connector_register(), but only those. drm_connector_init() already mentions that it only needs drm_connector_cleanup(), so let's clarify the drm_connector_register() and drm_connector_unregister() documentation to point at each other, and remove the misleading part about drm_dev_unregister(). Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-7-maxime@cerno.tech --- drivers/gpu/drm/drm_connector.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 5e98e8651780..8818fd8fba88 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -525,6 +525,9 @@ EXPORT_SYMBOL(drm_connector_cleanup); * e.g. DP MST connectors. All other connectors will be registered automatically * when calling drm_dev_register(). * + * When the connector is no longer available, callers must call + * drm_connector_unregister(). + * * Returns: * Zero on success, error code on failure. */ @@ -581,9 +584,8 @@ EXPORT_SYMBOL(drm_connector_register); * @connector: the connector to unregister * * Unregister userspace interfaces for a connector. Only call this for - * connectors which have registered explicitly by calling drm_dev_register(), - * since connectors are unregistered automatically when drm_dev_unregister() is - * called. + * connectors which have been registered explicitly by calling + * drm_connector_register(). */ void drm_connector_unregister(struct drm_connector *connector) { -- cgit v1.2.3-59-g8ed1b From b11af8a25b576cad006411fd904f88199ff9b5ff Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:37 +0200 Subject: drm/connector: Consolidate Connector Initialization We're going to add a DRM-managed connector initialization function. Since we'll need both the with and without the DDC pointer, having a single function that takes an optional pointer is easier to maintain. Let's create a static function that will back both existing variants, and will be reused by the DRM-managed variant. Acked-by: Thomas Zimmermann Suggested-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-8-maxime@cerno.tech --- drivers/gpu/drm/drm_connector.c | 65 +++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 31 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 8818fd8fba88..bbdac23bc5c5 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -214,27 +214,11 @@ void drm_connector_free_work_fn(struct work_struct *work) } } -/** - * drm_connector_init - Init a preallocated connector - * @dev: DRM device - * @connector: the connector to init - * @funcs: callbacks for this connector - * @connector_type: user visible type of the connector - * - * Initialises a preallocated connector. Connectors should be - * subclassed as part of driver connector objects. - * - * At driver unload time the driver's &drm_connector_funcs.destroy hook - * should call drm_connector_cleanup() and free the connector structure. - * The connector structure should not be allocated with devm_kzalloc(). - * - * Returns: - * Zero on success, error code on failure. - */ -int drm_connector_init(struct drm_device *dev, - struct drm_connector *connector, - const struct drm_connector_funcs *funcs, - int connector_type) +static int __drm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type, + struct i2c_adapter *ddc) { struct drm_mode_config *config = &dev->mode_config; int ret; @@ -282,6 +266,9 @@ int drm_connector_init(struct drm_device *dev, goto out_put_type_id; } + /* provide ddc symlink in sysfs */ + connector->ddc = ddc; + INIT_LIST_HEAD(&connector->global_connector_list_entry); INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); @@ -338,6 +325,31 @@ out_put: return ret; } + +/** + * drm_connector_init - Init a preallocated connector + * @dev: DRM device + * @connector: the connector to init + * @funcs: callbacks for this connector + * @connector_type: user visible type of the connector + * + * Initialises a preallocated connector. Connectors should be + * subclassed as part of driver connector objects. + * + * At driver unload time the driver's &drm_connector_funcs.destroy hook + * should call drm_connector_cleanup() and free the connector structure. + * The connector structure should not be allocated with devm_kzalloc(). + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type) +{ + return __drm_connector_init(dev, connector, funcs, connector_type, NULL); +} EXPORT_SYMBOL(drm_connector_init); /** @@ -366,16 +378,7 @@ int drm_connector_init_with_ddc(struct drm_device *dev, int connector_type, struct i2c_adapter *ddc) { - int ret; - - ret = drm_connector_init(dev, connector, funcs, connector_type); - if (ret) - return ret; - - /* provide ddc symlink in sysfs */ - connector->ddc = ddc; - - return ret; + return __drm_connector_init(dev, connector, funcs, connector_type, ddc); } EXPORT_SYMBOL(drm_connector_init_with_ddc); -- cgit v1.2.3-59-g8ed1b From a961b197d72601b0363fd7614f9b98d5721f9c6c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:38 +0200 Subject: drm/connector: Check for destroy implementation Connectors need to be cleaned up with a call to drm_connector_cleanup() in their drm_connector_funcs.destroy implementation. Let's check for this and complain if there's no such function. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-9-maxime@cerno.tech --- drivers/gpu/drm/drm_connector.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index bbdac23bc5c5..dfd170273f5c 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -348,6 +348,9 @@ int drm_connector_init(struct drm_device *dev, const struct drm_connector_funcs *funcs, int connector_type) { + if (drm_WARN_ON(dev, !(funcs && funcs->destroy))) + return -EINVAL; + return __drm_connector_init(dev, connector, funcs, connector_type, NULL); } EXPORT_SYMBOL(drm_connector_init); @@ -378,6 +381,9 @@ int drm_connector_init_with_ddc(struct drm_device *dev, int connector_type, struct i2c_adapter *ddc) { + if (drm_WARN_ON(dev, !(funcs && funcs->destroy))) + return -EINVAL; + return __drm_connector_init(dev, connector, funcs, connector_type, ddc); } EXPORT_SYMBOL(drm_connector_init_with_ddc); -- cgit v1.2.3-59-g8ed1b From 35a3b82f1bdd60e454de34f984a3f09b38f64b61 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:39 +0200 Subject: drm/connector: Introduce drmm_connector_init Unlike other DRM entities, there's no helper to create a DRM-managed initialisation of a connector. Let's create an helper to initialise a connector that would be passed as an argument, and handle the cleanup through a DRM-managed action. Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-10-maxime@cerno.tech --- drivers/gpu/drm/drm_connector.c | 60 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_connector.h | 5 ++++ 2 files changed, 65 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index dfd170273f5c..e3142c8142b3 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -340,6 +341,10 @@ out_put: * should call drm_connector_cleanup() and free the connector structure. * The connector structure should not be allocated with devm_kzalloc(). * + * Note: consider using drmm_connector_init() instead of + * drm_connector_init() to let the DRM managed resource infrastructure + * take care of cleanup and deallocation. + * * Returns: * Zero on success, error code on failure. */ @@ -372,6 +377,10 @@ EXPORT_SYMBOL(drm_connector_init); * * Ensures that the ddc field of the connector is correctly set. * + * Note: consider using drmm_connector_init() instead of + * drm_connector_init_with_ddc() to let the DRM managed resource + * infrastructure take care of cleanup and deallocation. + * * Returns: * Zero on success, error code on failure. */ @@ -388,6 +397,57 @@ int drm_connector_init_with_ddc(struct drm_device *dev, } EXPORT_SYMBOL(drm_connector_init_with_ddc); +static void drm_connector_cleanup_action(struct drm_device *dev, + void *ptr) +{ + struct drm_connector *connector = ptr; + + drm_connector_cleanup(connector); +} + +/** + * drmm_connector_init - Init a preallocated connector + * @dev: DRM device + * @connector: the connector to init + * @funcs: callbacks for this connector + * @connector_type: user visible type of the connector + * @ddc: optional pointer to the associated ddc adapter + * + * Initialises a preallocated connector. Connectors should be + * subclassed as part of driver connector objects. + * + * Cleanup is automatically handled with a call to + * drm_connector_cleanup() in a DRM-managed action. + * + * The connector structure should be allocated with drmm_kzalloc(). + * + * Returns: + * Zero on success, error code on failure. + */ +int drmm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type, + struct i2c_adapter *ddc) +{ + int ret; + + if (drm_WARN_ON(dev, funcs && funcs->destroy)) + return -EINVAL; + + ret = __drm_connector_init(dev, connector, funcs, connector_type, NULL); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(dev, drm_connector_cleanup_action, + connector); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_connector_init); + /** * drm_connector_attach_edid_property - attach edid property. * @connector: the connector diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index a1705d6b3fba..2c6fa746efac 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1677,6 +1677,11 @@ int drm_connector_init_with_ddc(struct drm_device *dev, const struct drm_connector_funcs *funcs, int connector_type, struct i2c_adapter *ddc); +int drmm_connector_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + int connector_type, + struct i2c_adapter *ddc); void drm_connector_attach_edid_property(struct drm_connector *connector); int drm_connector_register(struct drm_connector *connector); void drm_connector_unregister(struct drm_connector *connector); -- cgit v1.2.3-59-g8ed1b From abea75e9207e7744f524a5373e9c149226e17a5d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:40 +0200 Subject: drm/bridge: panel: Introduce drmm_panel_bridge_add Unlike what can be found for other entities, there's no DRM-managed function to create a panel_bridge instance from a panel. Let's introduce one. Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-11-maxime@cerno.tech --- drivers/gpu/drm/bridge/panel.c | 39 +++++++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 2 ++ 2 files changed, 41 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 4277bf4f032b..7d34ebfe611d 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -367,6 +368,44 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, } EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); +static void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr) +{ + struct drm_bridge *bridge = ptr; + + drm_panel_bridge_remove(bridge); +} + +/** + * drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and + * &drm_connector that just calls the + * appropriate functions from &drm_panel. + * + * @drm: DRM device to tie the bridge lifetime to + * @panel: The drm_panel being wrapped. Must be non-NULL. + * + * This is the DRM-managed version of drm_panel_bridge_add() which + * automatically calls drm_panel_bridge_remove() when @dev is cleaned + * up. + */ +struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, + struct drm_panel *panel) +{ + struct drm_bridge *bridge; + int ret; + + bridge = drm_panel_bridge_add_typed(panel, panel->connector_type); + if (IS_ERR(bridge)) + return bridge; + + ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release, + bridge); + if (ret) + return ERR_PTR(ret); + + return bridge; +} +EXPORT_SYMBOL(drmm_panel_bridge_add); + /** * drm_panel_bridge_connector - return the connector for the panel bridge * @bridge: The drm_bridge. diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index d434ab416ad4..e37a419ac2b4 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -930,6 +930,8 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, struct drm_panel *panel, u32 connector_type); +struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, + struct drm_panel *panel); struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge); #else static inline bool drm_bridge_is_panel(const struct drm_bridge *bridge) -- cgit v1.2.3-59-g8ed1b From ae9f1f2ca093906f06311ade42772eeb021af39f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:41 +0200 Subject: drm/bridge: panel: Introduce drmm_of_get_bridge Unlike what can be found for other DRM entities, we don't have a DRM-managed function equivalent to devm_drm_of_get_bridge(). Let's create it. Acked-by: Sam Ravnborg Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-12-maxime@cerno.tech --- drivers/gpu/drm/bridge/panel.c | 35 +++++++++++++++++++++++++++++++++++ include/drm/drm_bridge.h | 2 ++ 2 files changed, 37 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 7d34ebfe611d..216af76d0042 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -459,4 +459,39 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, return bridge; } EXPORT_SYMBOL(devm_drm_of_get_bridge); + +/** + * drmm_of_get_bridge - Return next bridge in the chain + * @drm: device to tie the bridge lifetime to + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * + * Given a DT node's port and endpoint number, finds the connected node + * and returns the associated bridge if any, or creates and returns a + * drm panel bridge instance if a panel is connected. + * + * Returns a drmm managed pointer to the bridge if successful, or an error + * pointer otherwise. + */ +struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, + struct device_node *np, + u32 port, u32 endpoint) +{ + struct drm_bridge *bridge; + struct drm_panel *panel; + int ret; + + ret = drm_of_find_panel_or_bridge(np, port, endpoint, + &panel, &bridge); + if (ret) + return ERR_PTR(ret); + + if (panel) + bridge = drmm_panel_bridge_add(drm, panel); + + return bridge; +} +EXPORT_SYMBOL(drmm_of_get_bridge); + #endif diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index e37a419ac2b4..dba5d81e3b4a 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -949,6 +949,8 @@ static inline int drm_panel_bridge_set_orientation(struct drm_connector *connect #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL_BRIDGE) struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node, u32 port, u32 endpoint); +struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node, + u32 port, u32 endpoint); #else static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node, -- cgit v1.2.3-59-g8ed1b From 6cf61bf49c9bdb9ba2d33be812d90dd406326c6c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:42 +0200 Subject: drm/vc4: drv: Call component_unbind_all() While we were using the component framework to deal with all the DRM subdevices, we were not calling component_unbind_all(). This leads to none of the subdevices freeing up their resources as part of their unbind() or device managed hooks. Fixes: c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.") Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-13-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.c | 14 ++++++++++++-- drivers/gpu/drm/vc4/vc4_drv.h | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 292d1b6a01b6..6b8dfa1e7650 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -267,6 +267,13 @@ static void vc4_match_add_drivers(struct device *dev, } } +static void vc4_component_unbind_all(void *ptr) +{ + struct vc4_dev *vc4 = ptr; + + component_unbind_all(vc4->dev, &vc4->base); +} + static const struct of_device_id vc4_dma_range_matches[] = { { .compatible = "brcm,bcm2711-hvs" }, { .compatible = "brcm,bcm2835-hvs" }, @@ -310,6 +317,7 @@ static int vc4_drm_bind(struct device *dev) if (IS_ERR(vc4)) return PTR_ERR(vc4); vc4->is_vc5 = is_vc5; + vc4->dev = dev; drm = &vc4->base; platform_set_drvdata(pdev, drm); @@ -360,6 +368,10 @@ static int vc4_drm_bind(struct device *dev) if (ret) return ret; + ret = devm_add_action_or_reset(dev, vc4_component_unbind_all, vc4); + if (ret) + return ret; + ret = vc4_plane_create_additional_planes(drm); if (ret) goto unbind_all; @@ -380,8 +392,6 @@ static int vc4_drm_bind(struct device *dev) return 0; unbind_all: - component_unbind_all(dev, drm); - return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 1beb96b77b8c..950056b83843 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -76,6 +76,7 @@ struct vc4_perfmon { struct vc4_dev { struct drm_device base; + struct device *dev; bool is_vc5; -- cgit v1.2.3-59-g8ed1b From 4a9551237d44cb051c0a3d7a18d11d6be27ef1d9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:43 +0200 Subject: drm/vc4: drv: Use drm_dev_unplug When our KMS driver is unbound, the device is no longer there but we might still have users with an opened fd to the KMS device. To avoid any issue in such a situation, every device access needs to be protected by calls to drm_dev_enter() and drm_dev_exit(), and the driver needs to call drm_dev_unplug(). We'll add calls to drm_dev_enter()/drm_dev_exit() in subsequent patches changing the relevant drivers, but let's start by calling drm_dev_unplug(). Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-14-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 6b8dfa1e7650..79a37d18aeb1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -399,8 +399,7 @@ static void vc4_drm_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - drm_dev_unregister(drm); - + drm_dev_unplug(drm); drm_atomic_helper_shutdown(drm); } -- cgit v1.2.3-59-g8ed1b From 68e4a69aec4dac161c620665693809b3e69db72f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:44 +0200 Subject: drm/vc4: crtc: Create vblank reporting function We'll need that code in the HVS driver, so let's create a shared function to reuse it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-15-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 23 +++++++++++++++-------- drivers/gpu/drm/vc4/vc4_drv.h | 1 + 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 029be98660b3..faad9e564772 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -544,6 +544,20 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc) return 0; } +void vc4_crtc_send_vblank(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned long flags; + + if (!crtc->state || !crtc->state->event) + return; + + spin_lock_irqsave(&dev->event_lock, flags); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags); +} + static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_atomic_state *state) { @@ -567,14 +581,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc, * Make sure we issue a vblank event after disabling the CRTC if * someone was waiting it. */ - if (crtc->state->event) { - unsigned long flags; - - spin_lock_irqsave(&dev->event_lock, flags); - drm_crtc_send_vblank_event(crtc, crtc->state->event); - crtc->state->event = NULL; - spin_unlock_irqrestore(&dev->event_lock, flags); - } + vc4_crtc_send_vblank(crtc); } static void vc4_crtc_atomic_enable(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 950056b83843..de326cf10564 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -862,6 +862,7 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); void vc4_crtc_reset(struct drm_crtc *crtc); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); +void vc4_crtc_send_vblank(struct drm_crtc *crtc); void vc4_crtc_get_margins(struct drm_crtc_state *state, unsigned int *left, unsigned int *right, unsigned int *top, unsigned int *bottom); -- cgit v1.2.3-59-g8ed1b From 969cfae1f01d03aee7a87e41d5313e65db4a9c0c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:45 +0200 Subject: drm/vc4: hvs: Protect device resources after removal Whenever the device and driver are unbound, the main device and all the subdevices will be removed by calling their unbind() method. However, the DRM device itself will only be freed when the last user will have closed it. It means that there is a time window where the device and its resources aren't there anymore, but the userspace can still call into our driver. Fortunately, the DRM framework provides the drm_dev_enter() and drm_dev_exit() functions to make sure our underlying device is still there for the section protected by those calls. Let's add them to the HVS driver. Acked-by: Thomas Zimmermann Reviewed-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-16-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hvs.c | 99 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index fbaa741dda5f..f2d6e62e7585 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -25,6 +25,7 @@ #include #include +#include #include #include "vc4_drv.h" @@ -66,11 +67,15 @@ static const struct debugfs_reg32 hvs_regs[] = { void vc4_hvs_dump_state(struct vc4_hvs *hvs) { + struct drm_device *drm = &hvs->vc4->base; struct drm_printer p = drm_info_printer(&hvs->pdev->dev); - int i; + int idx, i; drm_print_regset32(&p, &hvs->regset); + if (!drm_dev_enter(drm, &idx)) + return; + DRM_INFO("HVS ctx:\n"); for (i = 0; i < 64; i += 4) { DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n", @@ -80,6 +85,8 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs) readl((u32 __iomem *)hvs->dlist + i + 2), readl((u32 __iomem *)hvs->dlist + i + 3)); } + + drm_dev_exit(idx); } static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data) @@ -175,6 +182,11 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, int ret, i; u32 __iomem *dst_kernel; + /* + * NOTE: We don't need a call to drm_dev_enter()/drm_dev_exit() + * here since that function is only called from vc4_hvs_bind(). + */ + ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS); if (ret) { DRM_ERROR("Failed to allocate space for filter kernel: %d\n", @@ -199,10 +211,15 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, static void vc4_hvs_lut_load(struct vc4_hvs *hvs, struct vc4_crtc *vc4_crtc) { + struct drm_device *drm = &hvs->vc4->base; struct drm_crtc *crtc = &vc4_crtc->base; struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + int idx; u32 i; + if (!drm_dev_enter(drm, &idx)) + return; + /* The LUT memory is laid out with each HVS channel in order, * each of which takes 256 writes for R, 256 for G, then 256 * for B. @@ -217,6 +234,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs, HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); for (i = 0; i < crtc->gamma_size; i++) HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); + + drm_dev_exit(idx); } static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, @@ -238,7 +257,12 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs, u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) { + struct drm_device *drm = &hvs->vc4->base; u8 field = 0; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return 0; switch (fifo) { case 0: @@ -255,6 +279,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo) break; } + drm_dev_exit(idx); return field; } @@ -267,6 +292,12 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output) if (!vc4->is_vc5) return output; + /* + * NOTE: We should probably use drm_dev_enter()/drm_dev_exit() + * here, but this function is only used during the DRM device + * initialization, so we should be fine. + */ + switch (output) { case 0: return 0; @@ -315,12 +346,17 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, struct drm_display_mode *mode, bool oneshot) { struct vc4_dev *vc4 = hvs->vc4; + struct drm_device *drm = &vc4->base; struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); unsigned int chan = vc4_crtc_state->assigned_channel; bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; u32 dispbkgndx; u32 dispctrl; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return -ENODEV; HVS_WRITE(SCALER_DISPCTRLX(chan), 0); HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET); @@ -362,14 +398,22 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc, */ vc4_hvs_lut_load(hvs, vc4_crtc); + drm_dev_exit(idx); + return 0; } void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) { - if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE) + struct drm_device *drm = &hvs->vc4->base; + int idx; + + if (!drm_dev_enter(drm, &idx)) return; + if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE) + goto out; + HVS_WRITE(SCALER_DISPCTRLX(chan), HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET); HVS_WRITE(SCALER_DISPCTRLX(chan), @@ -385,6 +429,9 @@ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan) WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != SCALER_DISPSTATX_EMPTY); + +out: + drm_dev_exit(idx); } int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) @@ -426,9 +473,15 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc) struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_hvs *hvs = vc4->hvs; struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return; HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), vc4_state->mm.start); + + drm_dev_exit(idx); } static void vc4_hvs_update_dlist(struct drm_crtc *crtc) @@ -513,6 +566,12 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, bool enable_bg_fill = false; u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; u32 __iomem *dlist_next = dlist_start; + int idx; + + if (!drm_dev_enter(dev, &idx)) { + vc4_crtc_send_vblank(crtc); + return; + } if (debug_dump_regs) { DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); @@ -583,26 +642,44 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); vc4_hvs_dump_state(hvs); } + + drm_dev_exit(idx); } void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel) { - u32 dispctrl = HVS_READ(SCALER_DISPCTRL); + struct drm_device *drm = &hvs->vc4->base; + u32 dispctrl; + int idx; + if (!drm_dev_enter(drm, &idx)) + return; + + dispctrl = HVS_READ(SCALER_DISPCTRL); dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel); HVS_WRITE(SCALER_DISPCTRL, dispctrl); + + drm_dev_exit(idx); } void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel) { - u32 dispctrl = HVS_READ(SCALER_DISPCTRL); + struct drm_device *drm = &hvs->vc4->base; + u32 dispctrl; + int idx; + if (!drm_dev_enter(drm, &idx)) + return; + + dispctrl = HVS_READ(SCALER_DISPCTRL); dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel); HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_EUFLOW(channel)); HVS_WRITE(SCALER_DISPCTRL, dispctrl); + + drm_dev_exit(idx); } static void vc4_hvs_report_underrun(struct drm_device *dev) @@ -623,6 +700,17 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) u32 control; u32 status; + /* + * NOTE: We don't need to protect the register access using + * drm_dev_enter() there because the interrupt handler lifetime + * is tied to the device itself, and not to the DRM device. + * + * So when the device will be gone, one of the first thing we + * will be doing will be to unregister the interrupt handler, + * and then unregister the DRM device. drm_dev_enter() would + * thus always succeed if we are here. + */ + status = HVS_READ(SCALER_DISPSTAT); control = HVS_READ(SCALER_DISPCTRL); @@ -655,10 +743,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) u32 dispctrl; u32 reg; - hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); + hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL); if (!hvs) return -ENOMEM; - hvs->vc4 = vc4; hvs->pdev = pdev; -- cgit v1.2.3-59-g8ed1b From 398e7ceae6886eaa29babcc0ac8f343e48262716 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:46 +0200 Subject: drm/vc4: hvs: Remove planes currently allocated before taking down When the HVS driver is unbound, a lot of memory allocations in the LBM and DLIST RAM are still assigned to planes that are still allocated. Thus, we hit a warning when calling drm_mm_takedown() since the memory pool is not completely free of allocations. Let's free all the currently live entries before calling drm_mm_takedown(). Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-17-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hvs.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index f2d6e62e7585..a62f222255ce 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -873,11 +873,18 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_hvs *hvs = vc4->hvs; + struct drm_mm_node *node, *next; if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter)) drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter); + drm_mm_for_each_node_safe(node, next, &vc4->hvs->dlist_mm) + drm_mm_remove_node(node); + drm_mm_takedown(&vc4->hvs->dlist_mm); + + drm_mm_for_each_node_safe(node, next, &vc4->hvs->lbm_mm) + drm_mm_remove_node(node); drm_mm_takedown(&vc4->hvs->lbm_mm); clk_disable_unprepare(hvs->core_clk); -- cgit v1.2.3-59-g8ed1b From 77c5fb12061f2d8fa28bb0216c9a19a0a6f47ef7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:47 +0200 Subject: drm/vc4: plane: Take possible_crtcs as an argument vc4_plane_init() currently initialises the plane with no possible CRTCs, and will expect the caller to set it up by itself. Let's change that logic a bit to follow the syntax of drm_universal_plane_init() and pass the possible CRTCs bitmask as an argument to the function instead. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-18-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- drivers/gpu/drm/vc4/vc4_drv.h | 3 ++- drivers/gpu/drm/vc4/vc4_plane.c | 15 +++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index faad9e564772..d391e894ee6c 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1244,7 +1244,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, * requirement of the plane configuration, and reject ones * that will take too much. */ - primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY); + primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0); if (IS_ERR(primary_plane)) { dev_err(drm->dev, "failed to construct primary plane\n"); return PTR_ERR(primary_plane); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index de326cf10564..d935aa3e4409 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -960,7 +960,8 @@ int vc4_kms_load(struct drm_device *dev); /* vc4_plane.c */ struct drm_plane *vc4_plane_init(struct drm_device *dev, - enum drm_plane_type type); + enum drm_plane_type type, + uint32_t possible_crtcs); int vc4_plane_create_additional_planes(struct drm_device *dev); u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist); u32 vc4_plane_dlist_size(const struct drm_plane_state *state); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index f27e87a23df7..a344762d86eb 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -1492,7 +1492,8 @@ static const struct drm_plane_funcs vc4_plane_funcs = { }; struct drm_plane *vc4_plane_init(struct drm_device *dev, - enum drm_plane_type type) + enum drm_plane_type type, + uint32_t possible_crtcs) { struct vc4_dev *vc4 = to_vc4_dev(dev); struct drm_plane *plane = NULL; @@ -1523,7 +1524,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, } plane = &vc4_plane->base; - ret = drm_universal_plane_init(dev, plane, 0, + ret = drm_universal_plane_init(dev, plane, possible_crtcs, &vc4_plane_funcs, formats, num_formats, modifiers, type, NULL); @@ -1575,13 +1576,11 @@ int vc4_plane_create_additional_planes(struct drm_device *drm) */ for (i = 0; i < 16; i++) { struct drm_plane *plane = - vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY); + vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY, + GENMASK(drm->mode_config.num_crtc - 1, 0)); if (IS_ERR(plane)) continue; - - plane->possible_crtcs = - GENMASK(drm->mode_config.num_crtc - 1, 0); } drm_for_each_crtc(crtc, drm) { @@ -1589,9 +1588,9 @@ int vc4_plane_create_additional_planes(struct drm_device *drm) * since we overlay planes on the CRTC in the order they were * initialized. */ - cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR); + cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR, + drm_crtc_mask(crtc)); if (!IS_ERR(cursor_plane)) { - cursor_plane->possible_crtcs = drm_crtc_mask(crtc); crtc->cursor = cursor_plane; } } -- cgit v1.2.3-59-g8ed1b From 02792a93103a99abc611e36cdeda9bcfa8924fd2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:48 +0200 Subject: drm/vc4: crtc: Remove manual plane removal on error When vc4_crtc_bind() fails after vc4_crtc_init() has been called, we have a loop undoing the plane creation and calling destroy on each plane registered and matching the possible_crtcs mask. However, this is redundant with what drm_mode_config_cleanup() is doing, so let's remove it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-19-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index d391e894ee6c..03a799ba9ee8 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1317,7 +1317,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) IRQF_SHARED, "vc4 crtc", vc4_crtc); if (ret) - goto err_destroy_planes; + return ret; platform_set_drvdata(pdev, vc4_crtc); @@ -1325,15 +1325,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) &vc4_crtc->regset); return 0; - -err_destroy_planes: - list_for_each_entry_safe(destroy_plane, temp, - &drm->mode_config.plane_list, head) { - if (destroy_plane->possible_crtcs == drm_crtc_mask(crtc)) - destroy_plane->funcs->destroy(destroy_plane); - } - - return ret; } static void vc4_crtc_unbind(struct device *dev, struct device *master, -- cgit v1.2.3-59-g8ed1b From 9872c7a319219346d30a0862424e5495a4e97e0e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:49 +0200 Subject: drm/vc4: plane: Switch to drmm_universal_plane_alloc() Let's switch to drmm_universal_plane_alloc() for our plane allocation and initialisation to make the driver a bit simpler. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-20-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 1 - drivers/gpu/drm/vc4/vc4_plane.c | 23 ++++++++--------------- 2 files changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 03a799ba9ee8..d7cc006b22c9 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1282,7 +1282,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) const struct vc4_pv_data *pv_data; struct vc4_crtc *vc4_crtc; struct drm_crtc *crtc; - struct drm_plane *destroy_plane, *temp; int ret; vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index a344762d86eb..82a650268f19 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -1483,8 +1483,6 @@ static bool vc4_format_mod_supported(struct drm_plane *plane, static const struct drm_plane_funcs vc4_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = drm_plane_cleanup, - .set_property = NULL, .reset = vc4_plane_reset, .atomic_duplicate_state = vc4_plane_duplicate_state, .atomic_destroy_state = vc4_plane_destroy_state, @@ -1496,11 +1494,10 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, uint32_t possible_crtcs) { struct vc4_dev *vc4 = to_vc4_dev(dev); - struct drm_plane *plane = NULL; + struct drm_plane *plane; struct vc4_plane *vc4_plane; u32 formats[ARRAY_SIZE(hvs_formats)]; int num_formats = 0; - int ret = 0; unsigned i; static const uint64_t modifiers[] = { DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED, @@ -1511,11 +1508,6 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, DRM_FORMAT_MOD_INVALID }; - vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane), - GFP_KERNEL); - if (!vc4_plane) - return ERR_PTR(-ENOMEM); - for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { if (!hvs_formats[i].hvs5_only || vc4->is_vc5) { formats[num_formats] = hvs_formats[i].drm; @@ -1523,13 +1515,14 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, } } + vc4_plane = drmm_universal_plane_alloc(dev, struct vc4_plane, base, + possible_crtcs, + &vc4_plane_funcs, + formats, num_formats, + modifiers, type, NULL); + if (IS_ERR(vc4_plane)) + return ERR_CAST(vc4_plane); plane = &vc4_plane->base; - ret = drm_universal_plane_init(dev, plane, possible_crtcs, - &vc4_plane_funcs, - formats, num_formats, - modifiers, type, NULL); - if (ret) - return ERR_PTR(ret); if (vc4->is_vc5) drm_plane_helper_add(plane, &vc5_plane_helper_funcs); -- cgit v1.2.3-59-g8ed1b From 6bad4774157c9df64e1b04a8d893e282e16a9c23 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:50 +0200 Subject: drm/vc4: crtc: Move debugfs_name to crtc_data All the CRTCs, including the TXP, have a debugfs file and name so we can consolidate it into vc4_crtc_data. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-21-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 18 +++++++++--------- drivers/gpu/drm/vc4/vc4_drv.h | 4 ++-- drivers/gpu/drm/vc4/vc4_txp.c | 1 + 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index d7cc006b22c9..bcce61879d53 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1084,10 +1084,10 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { static const struct vc4_pv_data bcm2835_pv0_data = { .base = { + .debugfs_name = "crtc0_regs", .hvs_available_channels = BIT(0), .hvs_output = 0, }, - .debugfs_name = "crtc0_regs", .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { @@ -1098,10 +1098,10 @@ static const struct vc4_pv_data bcm2835_pv0_data = { static const struct vc4_pv_data bcm2835_pv1_data = { .base = { + .debugfs_name = "crtc1_regs", .hvs_available_channels = BIT(2), .hvs_output = 2, }, - .debugfs_name = "crtc1_regs", .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { @@ -1112,10 +1112,10 @@ static const struct vc4_pv_data bcm2835_pv1_data = { static const struct vc4_pv_data bcm2835_pv2_data = { .base = { + .debugfs_name = "crtc2_regs", .hvs_available_channels = BIT(1), .hvs_output = 1, }, - .debugfs_name = "crtc2_regs", .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { @@ -1126,10 +1126,10 @@ static const struct vc4_pv_data bcm2835_pv2_data = { static const struct vc4_pv_data bcm2711_pv0_data = { .base = { + .debugfs_name = "crtc0_regs", .hvs_available_channels = BIT(0), .hvs_output = 0, }, - .debugfs_name = "crtc0_regs", .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { @@ -1140,10 +1140,10 @@ static const struct vc4_pv_data bcm2711_pv0_data = { static const struct vc4_pv_data bcm2711_pv1_data = { .base = { + .debugfs_name = "crtc1_regs", .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), .hvs_output = 3, }, - .debugfs_name = "crtc1_regs", .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { @@ -1154,10 +1154,10 @@ static const struct vc4_pv_data bcm2711_pv1_data = { static const struct vc4_pv_data bcm2711_pv2_data = { .base = { + .debugfs_name = "crtc2_regs", .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), .hvs_output = 4, }, - .debugfs_name = "crtc2_regs", .fifo_depth = 256, .pixels_per_clock = 2, .encoder_types = { @@ -1167,10 +1167,10 @@ static const struct vc4_pv_data bcm2711_pv2_data = { static const struct vc4_pv_data bcm2711_pv3_data = { .base = { + .debugfs_name = "crtc3_regs", .hvs_available_channels = BIT(1), .hvs_output = 1, }, - .debugfs_name = "crtc3_regs", .fifo_depth = 64, .pixels_per_clock = 1, .encoder_types = { @@ -1180,10 +1180,10 @@ static const struct vc4_pv_data bcm2711_pv3_data = { static const struct vc4_pv_data bcm2711_pv4_data = { .base = { + .debugfs_name = "crtc4_regs", .hvs_available_channels = BIT(0) | BIT(1) | BIT(2), .hvs_output = 5, }, - .debugfs_name = "crtc4_regs", .fifo_depth = 64, .pixels_per_clock = 2, .encoder_types = { @@ -1320,7 +1320,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) platform_set_drvdata(pdev, vc4_crtc); - vc4_debugfs_add_regset32(drm, pv_data->debugfs_name, + vc4_debugfs_add_regset32(drm, pv_data->base.debugfs_name, &vc4_crtc->regset); return 0; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index d935aa3e4409..443894e1715d 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -459,6 +459,8 @@ to_vc4_encoder(struct drm_encoder *encoder) } struct vc4_crtc_data { + const char *debugfs_name; + /* Bitmask of channels (FIFOs) of the HVS that the output can source from */ unsigned int hvs_available_channels; @@ -476,8 +478,6 @@ struct vc4_pv_data { u8 pixels_per_clock; enum vc4_encoder_type encoder_types[4]; - const char *debugfs_name; - }; struct vc4_crtc { diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index d20b0bc51a18..20e08e31aa1b 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -461,6 +461,7 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) } static const struct vc4_crtc_data vc4_txp_crtc_data = { + .debugfs_name = "txp_regs", .hvs_available_channels = BIT(2), .hvs_output = 2, }; -- cgit v1.2.3-59-g8ed1b From 7cc4214c27cf8d664358db402a179bf6ab2db862 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:51 +0200 Subject: drm/vc4: crtc: Switch to drmm_kzalloc Our internal structure that stores the DRM entities structure is allocated through a device-managed kzalloc. This means that this will eventually be freed whenever the device is removed. In our case, the most likely source of removal is that the main device is going to be unbound, and component_unbind_all() is being run. However, it occurs while the DRM device is still registered, which will create dangling pointers, eventually resulting in use-after-free. Switch to a DRM-managed allocation to keep our structure until the DRM driver doesn't need it anymore. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-22-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index bcce61879d53..d5158a6c0f21 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1284,7 +1284,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) struct drm_crtc *crtc; int ret; - vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL); + vc4_crtc = drmm_kzalloc(drm, sizeof(*vc4_crtc), GFP_KERNEL); if (!vc4_crtc) return -ENOMEM; crtc = &vc4_crtc->base; -- cgit v1.2.3-59-g8ed1b From 77ef4c1702fb370da126ff39b94e80a74ce9f095 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:52 +0200 Subject: drm/vc4: crtc: Switch to DRM-managed CRTC initialization The current code will call drm_crtc_cleanup() when the device is unbound. However, by then, there might still be some references held to that CRTC, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-23-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_crtc.c | 16 ++++++---------- drivers/gpu/drm/vc4/vc4_drv.h | 1 - drivers/gpu/drm/vc4/vc4_txp.c | 1 - 3 files changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index d5158a6c0f21..3768a2a57ca9 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -206,11 +206,6 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc, return ret; } -void vc4_crtc_destroy(struct drm_crtc *crtc) -{ - drm_crtc_cleanup(crtc); -} - static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format) { const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc); @@ -1059,7 +1054,6 @@ void vc4_crtc_reset(struct drm_crtc *crtc) static const struct drm_crtc_funcs vc4_crtc_funcs = { .set_config = drm_atomic_helper_set_config, - .destroy = vc4_crtc_destroy, .page_flip = vc4_page_flip, .set_property = NULL, .cursor_set = NULL, /* handled by drm_mode_cursor_universal */ @@ -1237,6 +1231,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, struct drm_crtc *crtc = &vc4_crtc->base; struct drm_plane *primary_plane; unsigned int i; + int ret; /* For now, we create just the primary and the legacy cursor * planes. We should be able to stack more planes on easily, @@ -1251,8 +1246,11 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, } spin_lock_init(&vc4_crtc->irq_lock); - drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, - crtc_funcs, NULL); + ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL, + crtc_funcs, NULL); + if (ret) + return ret; + drm_crtc_helper_add(crtc, crtc_helper_funcs); if (!vc4->is_vc5) { @@ -1332,8 +1330,6 @@ static void vc4_crtc_unbind(struct device *dev, struct device *master, struct platform_device *pdev = to_platform_device(dev); struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev); - vc4_crtc_destroy(&vc4_crtc->base); - CRTC_WRITE(PV_INTEN, 0); platform_set_drvdata(pdev, NULL); diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 443894e1715d..116e54fc1363 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -851,7 +851,6 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc); int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, const struct drm_crtc_funcs *crtc_funcs, const struct drm_crtc_helper_funcs *crtc_helper_funcs); -void vc4_crtc_destroy(struct drm_crtc *crtc); int vc4_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 20e08e31aa1b..448d48e7e99f 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -384,7 +384,6 @@ static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {} static const struct drm_crtc_funcs vc4_txp_crtc_funcs = { .set_config = drm_atomic_helper_set_config, - .destroy = vc4_crtc_destroy, .page_flip = vc4_page_flip, .reset = vc4_crtc_reset, .atomic_duplicate_state = vc4_crtc_duplicate_state, -- cgit v1.2.3-59-g8ed1b From 693e35dcde748387c6dc538de6c3d78b3d2da866 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:53 +0200 Subject: drm/vc4: dpi: Remove vc4_dev dpi pointer There's no user for that pointer so let's just get rid of it. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-24-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 7 ------- drivers/gpu/drm/vc4/vc4_drv.h | 1 - 2 files changed, 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index ef5e3921062c..d3625dcb5dcb 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -269,7 +269,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dpi *dpi; struct vc4_dpi_encoder *vc4_dpi_encoder; int ret; @@ -328,8 +327,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, dpi); - vc4->dpi = dpi; - vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset); return 0; @@ -343,8 +340,6 @@ err_destroy_encoder: static void vc4_dpi_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dpi *dpi = dev_get_drvdata(dev); drm_of_panel_bridge_remove(dev->of_node, 0, 0); @@ -352,8 +347,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, drm_encoder_cleanup(dpi->encoder); clk_disable_unprepare(dpi->core_clock); - - vc4->dpi = NULL; } static const struct component_ops vc4_dpi_ops = { diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 116e54fc1363..835d286c2802 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -84,7 +84,6 @@ struct vc4_dev { struct vc4_hvs *hvs; struct vc4_v3d *v3d; - struct vc4_dpi *dpi; struct vc4_vec *vec; struct vc4_txp *txp; -- cgit v1.2.3-59-g8ed1b From 7c9a4bab7aeb4be917d4202a99a780b40eaa78c7 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:54 +0200 Subject: drm/vc4: dpi: Embed DRM structures into the private structure The VC4 DPI driver private structure contains only a pointer to the encoder it implements. This makes the overall structure somewhat inconsistent with the rest of the driver, and complicates its initialisation without any apparent gain. Let's embed the drm_encoder structure (through the vc4_encoder one) into struct vc4_dpi to fix both issues. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-25-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 49 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index d3625dcb5dcb..7c4bfbd7ed9f 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -84,9 +84,9 @@ /* General DPI hardware state. */ struct vc4_dpi { - struct platform_device *pdev; + struct vc4_encoder encoder; - struct drm_encoder *encoder; + struct platform_device *pdev; void __iomem *regs; @@ -96,21 +96,15 @@ struct vc4_dpi { struct debugfs_regset32 regset; }; -#define DPI_READ(offset) readl(dpi->regs + (offset)) -#define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset)) - -/* VC4 DPI encoder KMS struct */ -struct vc4_dpi_encoder { - struct vc4_encoder base; - struct vc4_dpi *dpi; -}; - -static inline struct vc4_dpi_encoder * -to_vc4_dpi_encoder(struct drm_encoder *encoder) +static inline struct vc4_dpi * +to_vc4_dpi(struct drm_encoder *encoder) { - return container_of(encoder, struct vc4_dpi_encoder, base.base); + return container_of(encoder, struct vc4_dpi, encoder.base); } +#define DPI_READ(offset) readl(dpi->regs + (offset)) +#define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset)) + static const struct debugfs_reg32 dpi_regs[] = { VC4_REG32(DPI_C), VC4_REG32(DPI_ID), @@ -118,8 +112,7 @@ static const struct debugfs_reg32 dpi_regs[] = { static void vc4_dpi_encoder_disable(struct drm_encoder *encoder) { - struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); - struct vc4_dpi *dpi = vc4_encoder->dpi; + struct vc4_dpi *dpi = to_vc4_dpi(encoder); clk_disable_unprepare(dpi->pixel_clock); } @@ -128,8 +121,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_display_mode *mode = &encoder->crtc->mode; - struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder); - struct vc4_dpi *dpi = vc4_encoder->dpi; + struct vc4_dpi *dpi = to_vc4_dpi(encoder); struct drm_connector_list_iter conn_iter; struct drm_connector *connector = NULL, *connector_scan; u32 dpi_c = DPI_ENABLE; @@ -262,7 +254,7 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) return PTR_ERR(bridge); } - return drm_bridge_attach(dpi->encoder, bridge, NULL, 0); + return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0); } static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) @@ -270,21 +262,12 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); struct vc4_dpi *dpi; - struct vc4_dpi_encoder *vc4_dpi_encoder; int ret; dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); if (!dpi) return -ENOMEM; - - vc4_dpi_encoder = devm_kzalloc(dev, sizeof(*vc4_dpi_encoder), - GFP_KERNEL); - if (!vc4_dpi_encoder) - return -ENOMEM; - vc4_dpi_encoder->base.type = VC4_ENCODER_TYPE_DPI; - vc4_dpi_encoder->dpi = dpi; - dpi->encoder = &vc4_dpi_encoder->base.base; - + dpi->encoder.type = VC4_ENCODER_TYPE_DPI; dpi->pdev = pdev; dpi->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(dpi->regs)) @@ -318,8 +301,8 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) if (ret) DRM_ERROR("Failed to turn on core clock: %d\n", ret); - drm_simple_encoder_init(drm, dpi->encoder, DRM_MODE_ENCODER_DPI); - drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs); + drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI); + drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs); ret = vc4_dpi_init_bridge(dpi); if (ret) @@ -332,7 +315,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) return 0; err_destroy_encoder: - drm_encoder_cleanup(dpi->encoder); + drm_encoder_cleanup(&dpi->encoder.base); clk_disable_unprepare(dpi->core_clock); return ret; } @@ -344,7 +327,7 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, drm_of_panel_bridge_remove(dev->of_node, 0, 0); - drm_encoder_cleanup(dpi->encoder); + drm_encoder_cleanup(&dpi->encoder.base); clk_disable_unprepare(dpi->core_clock); } -- cgit v1.2.3-59-g8ed1b From 7f0ba8f98a4ec3bee6a5d612e676a75791c0f127 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:55 +0200 Subject: drm/vc4: dpi: Switch to drmm_kzalloc Our internal structure that stores the DRM entities structure is allocated through a device-managed kzalloc. This means that this will eventually be freed whenever the device is removed. In our case, the most likely source of removal is that the main device is going to be unbound, and component_unbind_all() is being run. However, it occurs while the DRM device is still registered, which will create dangling pointers, eventually resulting in use-after-free. Switch to a DRM-managed allocation to keep our structure until the DRM driver doesn't need it anymore. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-26-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 7c4bfbd7ed9f..7a1112c12be4 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -264,9 +264,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) struct vc4_dpi *dpi; int ret; - dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL); + dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL); if (!dpi) return -ENOMEM; + dpi->encoder.type = VC4_ENCODER_TYPE_DPI; dpi->pdev = pdev; dpi->regs = vc4_ioremap_regs(pdev, 0); -- cgit v1.2.3-59-g8ed1b From ff5b18ce8a5b4fccabd46ed7f17ef3e7f2d1f8e0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:56 +0200 Subject: drm/vc4: dpi: Return an error if we can't enable our clock If we fail to enable the DPI clock, we just ignore the error and moves forward. Let's return an error instead. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-27-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 7a1112c12be4..7028a789919d 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -290,6 +290,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) DRM_ERROR("Failed to get core clock: %d\n", ret); return ret; } + dpi->pixel_clock = devm_clk_get(dev, "pixel"); if (IS_ERR(dpi->pixel_clock)) { ret = PTR_ERR(dpi->pixel_clock); @@ -299,8 +300,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) } ret = clk_prepare_enable(dpi->core_clock); - if (ret) + if (ret) { DRM_ERROR("Failed to turn on core clock: %d\n", ret); + return ret; + } drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI); drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs); -- cgit v1.2.3-59-g8ed1b From 5801eda938f5f98cbdb6bc83e2c35499735f006a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:57 +0200 Subject: drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Since we have a managed call to create our panel_bridge instance, the call to drm_of_panel_bridge_remove() at unbind is both redundant and dangerous since it might lead to a use-after-free. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-28-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 7028a789919d..f18b05cee8bc 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -329,8 +329,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, { struct vc4_dpi *dpi = dev_get_drvdata(dev); - drm_of_panel_bridge_remove(dev->of_node, 0, 0); - drm_encoder_cleanup(&dpi->encoder.base); clk_disable_unprepare(dpi->core_clock); -- cgit v1.2.3-59-g8ed1b From 77932adf2a2c9625da8bc8885b626690ee500601 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:58 +0200 Subject: drm/vc4: dpi: Add action to disable the clock The DPI controller has two clocks called core and pixel, the core clock being enabled at bind time. Adding a device-managed action will make the error path easier, so let's create one to disable it. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-29-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index f18b05cee8bc..c4ea99f85e5b 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -257,6 +257,13 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0); } +static void vc4_dpi_disable_clock(void *ptr) +{ + struct vc4_dpi *dpi = ptr; + + clk_disable_unprepare(dpi->core_clock); +} + static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -305,6 +312,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) return ret; } + ret = devm_add_action_or_reset(dev, vc4_dpi_disable_clock, dpi); + if (ret) + return ret; + drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI); drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs); @@ -320,7 +331,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) err_destroy_encoder: drm_encoder_cleanup(&dpi->encoder.base); - clk_disable_unprepare(dpi->core_clock); return ret; } @@ -330,8 +340,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master, struct vc4_dpi *dpi = dev_get_drvdata(dev); drm_encoder_cleanup(&dpi->encoder.base); - - clk_disable_unprepare(dpi->core_clock); } static const struct component_ops vc4_dpi_ops = { -- cgit v1.2.3-59-g8ed1b From e126d318002639c979f23764f5d496a78e9bd85e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:59 +0200 Subject: drm/vc4: dpi: Switch to DRM-managed encoder initialization The current code will call drm_encoder_cleanup() when the device is unbound. However, by then, there might still be some references held to that encoder, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-30-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index c4ea99f85e5b..ceb14aea6c05 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -316,35 +316,28 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI); + ret = drmm_encoder_init(drm, &dpi->encoder.base, + NULL, + DRM_MODE_ENCODER_DPI, + NULL); + if (ret) + return ret; + drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs); ret = vc4_dpi_init_bridge(dpi); if (ret) - goto err_destroy_encoder; + return ret; dev_set_drvdata(dev, dpi); vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset); return 0; - -err_destroy_encoder: - drm_encoder_cleanup(&dpi->encoder.base); - return ret; -} - -static void vc4_dpi_unbind(struct device *dev, struct device *master, - void *data) -{ - struct vc4_dpi *dpi = dev_get_drvdata(dev); - - drm_encoder_cleanup(&dpi->encoder.base); } static const struct component_ops vc4_dpi_ops = { .bind = vc4_dpi_bind, - .unbind = vc4_dpi_unbind, }; static int vc4_dpi_dev_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 055af0235aef8110a1c44f5fc04c5c206e9e58fb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:00 +0200 Subject: drm/vc4: dpi: Switch to drmm_of_get_bridge The current code uses a device-managed function to retrieve the next bridge downstream. However, that means that it will be removed at unbind time, where the DRM device is still very much live and might still have some applications that still have it open. Switch to a DRM-managed variant to clean everything up once the DRM device has been last closed. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-31-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index ceb14aea6c05..660feea0534d 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -240,10 +240,11 @@ static const struct of_device_id vc4_dpi_dt_match[] = { */ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi) { + struct drm_device *drm = dpi->encoder.base.dev; struct device *dev = &dpi->pdev->dev; struct drm_bridge *bridge; - bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); + bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0); if (IS_ERR(bridge)) { /* If nothing was connected in the DT, that's not an * error. -- cgit v1.2.3-59-g8ed1b From 71b1bd4c2bd404b28cc499fa37d02e32f55de865 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:01 +0200 Subject: drm/vc4: dpi: Protect device resources Our current code now mixes some resources whose lifetime are tied to the device (clocks, IO mappings, etc.) and some that are tied to the DRM device (encoder, bridge). The device one will be freed at unbind time, but the DRM one will only be freed when the last user of the DRM device closes its file handle. So we end up with a time window during which we can call the encoder hooks, but we don't have access to the underlying resources and device. Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so that we bail out if we are during that window. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-32-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dpi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 660feea0534d..7286a3333538 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -112,9 +113,16 @@ static const struct debugfs_reg32 dpi_regs[] = { static void vc4_dpi_encoder_disable(struct drm_encoder *encoder) { + struct drm_device *dev = encoder->dev; struct vc4_dpi *dpi = to_vc4_dpi(encoder); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return; clk_disable_unprepare(dpi->pixel_clock); + + drm_dev_exit(idx); } static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) @@ -125,6 +133,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) struct drm_connector_list_iter conn_iter; struct drm_connector *connector = NULL, *connector_scan; u32 dpi_c = DPI_ENABLE; + int idx; int ret; /* Look up the connector attached to DPI so we can get the @@ -204,6 +213,9 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) dpi_c |= DPI_VSYNC_DISABLE; } + if (!drm_dev_enter(dev, &idx)) + return; + DPI_WRITE(DPI_C, dpi_c); ret = clk_set_rate(dpi->pixel_clock, mode->clock * 1000); @@ -213,6 +225,8 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder) ret = clk_prepare_enable(dpi->pixel_clock); if (ret) DRM_ERROR("Failed to set clock rate: %d\n", ret); + + drm_dev_exit(idx); } static enum drm_mode_status vc4_dpi_encoder_mode_valid(struct drm_encoder *encoder, -- cgit v1.2.3-59-g8ed1b From 73b68b28a9e819e600ca2d0640721ae61bdaf55a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:02 +0200 Subject: drm/vc4: dsi: Embed DRM structures into the private structure The VC4 DSI driver private structure contains only a pointer to the encoder it implements. This makes the overall structure somewhat inconsistent with the rest of the driver, and complicates its initialisation without any apparent gain. Let's embed the drm_encoder structure (through the vc4_encoder one) into struct vc4_dsi to fix both issues. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-33-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dsi.c | 58 ++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 36 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index b7b2c76770dc..72889524540e 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -549,10 +549,11 @@ struct vc4_dsi_variant { /* General DSI hardware state. */ struct vc4_dsi { + struct vc4_encoder encoder; + struct mipi_dsi_host dsi_host; + struct platform_device *pdev; - struct mipi_dsi_host dsi_host; - struct drm_encoder *encoder; struct drm_bridge *bridge; struct list_head bridge_chain; @@ -600,6 +601,12 @@ struct vc4_dsi { #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) +static inline struct vc4_dsi * +to_vc4_dsi(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vc4_dsi, encoder.base); +} + static inline void dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) { @@ -644,18 +651,6 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val) DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val) #define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit) -/* VC4 DSI encoder KMS struct */ -struct vc4_dsi_encoder { - struct vc4_encoder base; - struct vc4_dsi *dsi; -}; - -static inline struct vc4_dsi_encoder * -to_vc4_dsi_encoder(struct drm_encoder *encoder) -{ - return container_of(encoder, struct vc4_dsi_encoder, base.base); -} - static const struct debugfs_reg32 dsi0_regs[] = { VC4_REG32(DSI0_CTRL), VC4_REG32(DSI0_STAT), @@ -795,8 +790,7 @@ dsi_esc_timing(u32 ns) static void vc4_dsi_encoder_disable(struct drm_encoder *encoder) { - struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); - struct vc4_dsi *dsi = vc4_encoder->dsi; + struct vc4_dsi *dsi = to_vc4_dsi(encoder); struct device *dev = &dsi->pdev->dev; struct drm_bridge *iter; @@ -839,8 +833,7 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); - struct vc4_dsi *dsi = vc4_encoder->dsi; + struct vc4_dsi *dsi = to_vc4_dsi(encoder); struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock); unsigned long parent_rate = clk_get_rate(phy_parent); unsigned long pixel_clock_hz = mode->clock * 1000; @@ -875,8 +868,7 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder, static void vc4_dsi_encoder_enable(struct drm_encoder *encoder) { struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; - struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); - struct vc4_dsi *dsi = vc4_encoder->dsi; + struct vc4_dsi *dsi = to_vc4_dsi(encoder); struct device *dev = &dsi->pdev->dev; bool debug_dump_regs = false; struct drm_bridge *iter; @@ -1569,21 +1561,14 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); struct vc4_dsi *dsi = dev_get_drvdata(dev); - struct vc4_dsi_encoder *vc4_dsi_encoder; + struct drm_encoder *encoder = &dsi->encoder.base; int ret; dsi->variant = of_device_get_match_data(dev); - vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder), - GFP_KERNEL); - if (!vc4_dsi_encoder) - return -ENOMEM; - INIT_LIST_HEAD(&dsi->bridge_chain); - vc4_dsi_encoder->base.type = dsi->variant->port ? - VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; - vc4_dsi_encoder->dsi = dsi; - dsi->encoder = &vc4_dsi_encoder->base.base; + dsi->encoder.type = dsi->variant->port ? + VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; dsi->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(dsi->regs)) @@ -1702,10 +1687,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - drm_simple_encoder_init(drm, dsi->encoder, DRM_MODE_ENCODER_DSI); - drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); + drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI); + drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs); - ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, 0); + ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0); if (ret) return ret; /* Disable the atomic helper calls into the bridge. We @@ -1713,7 +1698,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) * from our driver, since we need to sequence them within the * encoder's enable/disable paths. */ - list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain); + list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain); vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); @@ -1726,6 +1711,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, void *data) { struct vc4_dsi *dsi = dev_get_drvdata(dev); + struct drm_encoder *encoder = &dsi->encoder.base; pm_runtime_disable(dev); @@ -1733,8 +1719,8 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, * Restore the bridge_chain so the bridge detach procedure can happen * normally. */ - list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain); - drm_encoder_cleanup(dsi->encoder); + list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain); + drm_encoder_cleanup(encoder); } static const struct component_ops vc4_dsi_ops = { -- cgit v1.2.3-59-g8ed1b From 6f0ca506c30e2b2c8d3b86fe33989c5257aaa34b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:03 +0200 Subject: drm/vc4: dsi: Switch to DRM-managed encoder initialization The current code will call drm_encoder_cleanup() when the device is unbound. However, by then, there might still be some references held to that encoder, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-34-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dsi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 72889524540e..1a55b7ea52a8 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1687,7 +1687,13 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI); + ret = drmm_encoder_init(drm, encoder, + NULL, + DRM_MODE_ENCODER_DSI, + NULL); + if (ret) + return ret; + drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs); ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0); @@ -1720,7 +1726,6 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, * normally. */ list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain); - drm_encoder_cleanup(encoder); } static const struct component_ops vc4_dsi_ops = { -- cgit v1.2.3-59-g8ed1b From 2a87d48c3342ede66b15649bcbbaf999ccc9a83a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:04 +0200 Subject: drm/vc4: dsi: Switch to drmm_of_get_bridge The current code uses a device-managed function to retrieve the next bridge downstream. However, that means that it will be removed at unbind time, where the DRM device is still very much live and might still have some applications that still have it open. Switch to a DRM-managed variant to clean everything up once the DRM device has been last closed. Acked-by: Thomas Zimmermann Reviewed-by: Dave Stevenson Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-35-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 1a55b7ea52a8..13266ff334d0 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1672,7 +1672,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) return ret; } - dsi->bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0); + dsi->bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0); if (IS_ERR(dsi->bridge)) return PTR_ERR(dsi->bridge); -- cgit v1.2.3-59-g8ed1b From 6eda15642989e32ff5e70281cabf7b596fe4f280 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:05 +0200 Subject: drm/vc4: dsi: Fix the driver structure lifetime The vc4_dsi structure is currently allocated through a device-managed allocation. This can lead to use-after-free issues however in the unbinding path since the DRM entities will stick around, but the underlying structure has been freed. However, we can't just fix it by using a DRM-managed allocation like we did for the other drivers since the DSI case is a bit more intricate. Indeed, the structure will be allocated at probe time, when we don't have a DRM device yet, to be able to register the DSI bus driver. We will then reuse it at bind time to register our KMS entities in the framework. In order to work around both constraints, we can use a kref to track the users of the structure (DSI host, and KMS), and then put our structure when the DSI host will have been unregistered, and through a DRM-managed action that will execute once we won't need the KMS entities anymore. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-36-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dsi.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 13266ff334d0..4f5bdb9a328b 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -552,6 +552,8 @@ struct vc4_dsi { struct vc4_encoder encoder; struct mipi_dsi_host dsi_host; + struct kref kref; + struct platform_device *pdev; struct drm_bridge *bridge; @@ -1556,6 +1558,31 @@ static void vc4_dsi_dma_chan_release(void *ptr) dsi->reg_dma_chan = NULL; } +static void vc4_dsi_release(struct kref *kref) +{ + struct vc4_dsi *dsi = + container_of(kref, struct vc4_dsi, kref); + + kfree(dsi); +} + +static void vc4_dsi_get(struct vc4_dsi *dsi) +{ + kref_get(&dsi->kref); +} + +static void vc4_dsi_put(struct vc4_dsi *dsi) +{ + kref_put(&dsi->kref, &vc4_dsi_release); +} + +static void vc4_dsi_release_action(struct drm_device *drm, void *ptr) +{ + struct vc4_dsi *dsi = ptr; + + vc4_dsi_put(dsi); +} + static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -1564,6 +1591,12 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) struct drm_encoder *encoder = &dsi->encoder.base; int ret; + vc4_dsi_get(dsi); + + ret = drmm_add_action_or_reset(drm, vc4_dsi_release_action, dsi); + if (ret) + return ret; + dsi->variant = of_device_get_match_data(dev); INIT_LIST_HEAD(&dsi->bridge_chain); @@ -1738,11 +1771,12 @@ static int vc4_dsi_dev_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct vc4_dsi *dsi; - dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); + dsi = kzalloc(sizeof(*dsi), GFP_KERNEL); if (!dsi) return -ENOMEM; dev_set_drvdata(dev, dsi); + kref_init(&dsi->kref); dsi->pdev = pdev; dsi->dsi_host.ops = &vc4_dsi_host_ops; dsi->dsi_host.dev = dev; @@ -1757,6 +1791,8 @@ static int vc4_dsi_dev_remove(struct platform_device *pdev) struct vc4_dsi *dsi = dev_get_drvdata(dev); mipi_dsi_host_unregister(&dsi->dsi_host); + vc4_dsi_put(dsi); + return 0; } -- cgit v1.2.3-59-g8ed1b From 7b44e4de72dcd5a5769ad19c5cc2cc2b28c0a778 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:06 +0200 Subject: drm/vc4: dsi: Switch to devm_pm_runtime_enable devm_pm_runtime_enable() simplifies the driver a bit since it will call pm_runtime_disable() automatically through a device-managed action. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-37-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_dsi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 4f5bdb9a328b..52c3215fef49 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1729,6 +1729,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0); if (ret) return ret; @@ -1741,8 +1745,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); - pm_runtime_enable(dev); - return 0; } @@ -1752,8 +1754,6 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, struct vc4_dsi *dsi = dev_get_drvdata(dev); struct drm_encoder *encoder = &dsi->encoder.base; - pm_runtime_disable(dev); - /* * Restore the bridge_chain so the bridge detach procedure can happen * normally. -- cgit v1.2.3-59-g8ed1b From b4f2c70c1a7a0a7708c9755a9946452ee23adeb6 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:07 +0200 Subject: drm/vc4: hdmi: Switch to drmm_kzalloc Our internal structure that stores the DRM entities structure is allocated through a device-managed kzalloc. This means that this will eventually be freed whenever the device is removed. In our case, the most likely source of removal is that the main device is going to be unbound, and component_unbind_all() is being run. However, it occurs while the DRM device is still registered, which will create dangling pointers, eventually resulting in use-after-free. Switch to a DRM-managed allocation to keep our structure until the DRM driver doesn't need it anymore. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-38-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 73fb2f91c3e4..fba549edcfc5 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2907,9 +2907,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) struct device_node *ddc_node; int ret; - vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL); + vc4_hdmi = drmm_kzalloc(drm, sizeof(*vc4_hdmi), GFP_KERNEL); if (!vc4_hdmi) return -ENOMEM; + mutex_init(&vc4_hdmi->mutex); spin_lock_init(&vc4_hdmi->hw_lock); INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq); -- cgit v1.2.3-59-g8ed1b From 7a951e3a26920ae424f563f63925bf478b37d00b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:08 +0200 Subject: drm/vc4: hdmi: Remove call to drm_connector_unregister() drm_connector_unregister() is only to be used for connectors that have been registered through drm_connector_register() after drm_dev_register() has been called. This is our case here so let's remove the call. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-39-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index fba549edcfc5..05f769474903 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -267,12 +267,6 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force) return connector_status_disconnected; } -static void vc4_hdmi_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) { struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector); @@ -380,7 +374,7 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .detect = vc4_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = vc4_hdmi_connector_destroy, + .destroy = drm_connector_cleanup, .reset = vc4_hdmi_connector_reset, .atomic_duplicate_state = vc4_hdmi_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -3022,7 +3016,7 @@ err_free_cec: err_free_hotplug: vc4_hdmi_hotplug_exit(vc4_hdmi); err_destroy_conn: - vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + drm_connector_cleanup(&vc4_hdmi->connector); err_destroy_encoder: drm_encoder_cleanup(encoder); pm_runtime_put_sync(dev); @@ -3066,7 +3060,7 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, vc4_hdmi_audio_exit(vc4_hdmi); vc4_hdmi_cec_exit(vc4_hdmi); vc4_hdmi_hotplug_exit(vc4_hdmi); - vc4_hdmi_connector_destroy(&vc4_hdmi->connector); + drm_connector_cleanup(&vc4_hdmi->connector); drm_encoder_cleanup(&vc4_hdmi->encoder.base); pm_runtime_disable(dev); -- cgit v1.2.3-59-g8ed1b From 56924791cd5de99374a027cf00ce9b6e55d74203 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:09 +0200 Subject: drm/vc4: hdmi: Switch to DRM-managed encoder initialization The current code will call drm_encoder_cleanup() when the device is unbound. However, by then, there might still be some references held to that encoder, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-40-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 05f769474903..6c44faea4af1 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2984,12 +2984,18 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) clk_prepare_enable(vc4_hdmi->pixel_bvb_clock); } - drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); + ret = drmm_encoder_init(drm, encoder, + NULL, + DRM_MODE_ENCODER_TMDS, + NULL); + if (ret) + goto err_put_runtime_pm; + drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs); ret = vc4_hdmi_connector_init(drm, vc4_hdmi); if (ret) - goto err_destroy_encoder; + goto err_put_runtime_pm; ret = vc4_hdmi_hotplug_init(vc4_hdmi); if (ret) @@ -3017,8 +3023,7 @@ err_free_hotplug: vc4_hdmi_hotplug_exit(vc4_hdmi); err_destroy_conn: drm_connector_cleanup(&vc4_hdmi->connector); -err_destroy_encoder: - drm_encoder_cleanup(encoder); +err_put_runtime_pm: pm_runtime_put_sync(dev); err_disable_runtime_pm: pm_runtime_disable(dev); @@ -3061,7 +3066,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, vc4_hdmi_cec_exit(vc4_hdmi); vc4_hdmi_hotplug_exit(vc4_hdmi); drm_connector_cleanup(&vc4_hdmi->connector); - drm_encoder_cleanup(&vc4_hdmi->encoder.base); pm_runtime_disable(dev); -- cgit v1.2.3-59-g8ed1b From 19d094c3a17d228381d4f1a25158a7b91b0e294f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:10 +0200 Subject: drm/vc4: hdmi: Switch to DRM-managed connector initialization The current code will call drm_connector_unregister() and drm_connector_cleanup() when the device is unbound. However, by then, there might still be some references held to that connector, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-41-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 6c44faea4af1..283fea78f3cf 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -374,7 +374,6 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector) static const struct drm_connector_funcs vc4_hdmi_connector_funcs = { .detect = vc4_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, .reset = vc4_hdmi_connector_reset, .atomic_duplicate_state = vc4_hdmi_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -392,10 +391,13 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, struct drm_encoder *encoder = &vc4_hdmi->encoder.base; int ret; - drm_connector_init_with_ddc(dev, connector, - &vc4_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA, - vc4_hdmi->ddc); + ret = drmm_connector_init(dev, connector, + &vc4_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + vc4_hdmi->ddc); + if (ret) + return ret; + drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs); /* @@ -2999,7 +3001,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ret = vc4_hdmi_hotplug_init(vc4_hdmi); if (ret) - goto err_destroy_conn; + goto err_put_runtime_pm; ret = vc4_hdmi_cec_init(vc4_hdmi); if (ret) @@ -3021,8 +3023,6 @@ err_free_cec: vc4_hdmi_cec_exit(vc4_hdmi); err_free_hotplug: vc4_hdmi_hotplug_exit(vc4_hdmi); -err_destroy_conn: - drm_connector_cleanup(&vc4_hdmi->connector); err_put_runtime_pm: pm_runtime_put_sync(dev); err_disable_runtime_pm: @@ -3065,7 +3065,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, vc4_hdmi_audio_exit(vc4_hdmi); vc4_hdmi_cec_exit(vc4_hdmi); vc4_hdmi_hotplug_exit(vc4_hdmi); - drm_connector_cleanup(&vc4_hdmi->connector); pm_runtime_disable(dev); -- cgit v1.2.3-59-g8ed1b From 59f9d46af43c884b92f690934263196387437c7e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:11 +0200 Subject: drm/vc4: hdmi: Switch to device-managed ALSA initialization The current code to unregister our ALSA device needs to be undone manually when we remove the HDMI driver. Since ALSA doesn't seem to support any mechanism to defer freeing something until the last user of the ALSA device is gone, we can either use a device-managed or a DRM-managed action. The consistent way would be to use a DRM-managed one, just like pretty much any framework-facing structure should be doing. However, ALSA does a lot of allocation and registration using device-managed calls. Thus, if we're going that way, by the time the DRM-managed action would run all of those allocation would have been freed and we would end up with a use-after-free. Thus, let's do a device-managed action. It's been tested with KASAN enabled and doesn't seem to trigger any issue, so it's as good as anything. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-42-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 43 +++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 283fea78f3cf..bf7d56bbcba0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2056,6 +2056,14 @@ static struct hdmi_codec_pdata vc4_hdmi_codec_pdata = { .i2s = 1, }; +static void vc4_hdmi_audio_codec_release(void *ptr) +{ + struct vc4_hdmi *vc4_hdmi = ptr; + + platform_device_unregister(vc4_hdmi->audio.codec_pdev); + vc4_hdmi->audio.codec_pdev = NULL; +} + static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) { const struct vc4_hdmi_register *mai_data = @@ -2097,6 +2105,30 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; vc4_hdmi->audio.dma_data.maxburst = 2; + /* + * NOTE: Strictly speaking, we should probably use a DRM-managed + * registration there to avoid removing all the audio components + * by the time the driver doesn't have any user anymore. + * + * However, the ASoC core uses a number of devm_kzalloc calls + * when registering, even when using non-device-managed + * functions (such as in snd_soc_register_component()). + * + * If we call snd_soc_unregister_component() in a DRM-managed + * action, the device-managed actions have already been executed + * and thus we would access memory that has been freed. + * + * Using device-managed hooks here probably leaves us open to a + * bunch of issues if userspace still has a handle on the ALSA + * device when the device is removed. However, this is mitigated + * by the use of drm_dev_enter()/drm_dev_exit() in the audio + * path to prevent the access to the device resources if it + * isn't there anymore. + * + * Then, the vc4_hdmi structure is DRM-managed and thus only + * freed whenever the last user has closed the DRM device file. + * It should thus outlive ALSA in most situations. + */ ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0); if (ret) { dev_err(dev, "Could not register PCM component: %d\n", ret); @@ -2120,6 +2152,10 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) } vc4_hdmi->audio.codec_pdev = codec_pdev; + ret = devm_add_action_or_reset(dev, vc4_hdmi_audio_codec_release, vc4_hdmi); + if (ret) + return ret; + dai_link->cpus = &vc4_hdmi->audio.cpu; dai_link->codecs = &vc4_hdmi->audio.codec; dai_link->platforms = &vc4_hdmi->audio.platform; @@ -2158,12 +2194,6 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) } -static void vc4_hdmi_audio_exit(struct vc4_hdmi *vc4_hdmi) -{ - platform_device_unregister(vc4_hdmi->audio.codec_pdev); - vc4_hdmi->audio.codec_pdev = NULL; -} - static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv) { struct vc4_hdmi *vc4_hdmi = priv; @@ -3062,7 +3092,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, kfree(vc4_hdmi->hdmi_regset.regs); kfree(vc4_hdmi->hd_regset.regs); - vc4_hdmi_audio_exit(vc4_hdmi); vc4_hdmi_cec_exit(vc4_hdmi); vc4_hdmi_hotplug_exit(vc4_hdmi); -- cgit v1.2.3-59-g8ed1b From 015653f7af7cb7fa56e9a88af2a15c814a39b6b4 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:12 +0200 Subject: drm/vc4: hdmi: Switch to device-managed CEC initialization The current code to unregister our CEC device needs to be undone manually when we remove the HDMI driver. Since the CEC framework will allocate its main structure, and will defer its deallocation to when the last user will have closed it, we don't really need to take any particular measure to prevent any use-after-free and can thus use any managed action. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-43-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 94 ++++++++++++++++++++++-------------------- 1 file changed, 50 insertions(+), 44 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index bf7d56bbcba0..83ca1c02dde2 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2576,6 +2576,14 @@ static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = { .adap_transmit = vc4_hdmi_cec_adap_transmit, }; +static void vc4_hdmi_cec_release(void *ptr) +{ + struct vc4_hdmi *vc4_hdmi = ptr; + + cec_unregister_adapter(vc4_hdmi->cec_adap); + vc4_hdmi->cec_adap = NULL; +} + static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) { struct cec_connector_info conn_info; @@ -2600,70 +2608,71 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) cec_s_conn_info(vc4_hdmi->cec_adap, &conn_info); if (vc4_hdmi->variant->external_irq_controller) { - ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"), - vc4_cec_irq_handler_rx_bare, - vc4_cec_irq_handler_rx_thread, 0, - "vc4 hdmi cec rx", vc4_hdmi); + ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"), + vc4_cec_irq_handler_rx_bare, + vc4_cec_irq_handler_rx_thread, 0, + "vc4 hdmi cec rx", vc4_hdmi); if (ret) goto err_delete_cec_adap; - ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-tx"), - vc4_cec_irq_handler_tx_bare, - vc4_cec_irq_handler_tx_thread, 0, - "vc4 hdmi cec tx", vc4_hdmi); + ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"), + vc4_cec_irq_handler_tx_bare, + vc4_cec_irq_handler_tx_thread, 0, + "vc4 hdmi cec tx", vc4_hdmi); if (ret) - goto err_remove_cec_rx_handler; + goto err_delete_cec_adap; } else { - ret = request_threaded_irq(platform_get_irq(pdev, 0), - vc4_cec_irq_handler, - vc4_cec_irq_handler_thread, 0, - "vc4 hdmi cec", vc4_hdmi); + ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0), + vc4_cec_irq_handler, + vc4_cec_irq_handler_thread, 0, + "vc4 hdmi cec", vc4_hdmi); if (ret) goto err_delete_cec_adap; } ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev); if (ret < 0) - goto err_remove_handlers; - - return 0; + goto err_delete_cec_adap; -err_remove_handlers: - if (vc4_hdmi->variant->external_irq_controller) - free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi); - else - free_irq(platform_get_irq(pdev, 0), vc4_hdmi); + /* + * NOTE: Strictly speaking, we should probably use a DRM-managed + * registration there to avoid removing the CEC adapter by the + * time the DRM driver doesn't have any user anymore. + * + * However, the CEC framework already cleans up the CEC adapter + * only when the last user has closed its file descriptor, so we + * don't need to handle it in DRM. + * + * By the time the device-managed hook is executed, we will give + * up our reference to the CEC adapter and therefore don't + * really care when it's actually freed. + * + * There's still a problematic sequence: if we unregister our + * CEC adapter, but the userspace keeps a handle on the CEC + * adapter but not the DRM device for some reason. In such a + * case, our vc4_hdmi structure will be freed, but the + * cec_adapter structure will have a dangling pointer to what + * used to be our HDMI controller. If we get a CEC call at that + * moment, we could end up with a use-after-free. Fortunately, + * the CEC framework already handles this too, by calling + * cec_is_registered() in cec_ioctl() and cec_poll(). + */ + ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi); + if (ret) + return ret; -err_remove_cec_rx_handler: - if (vc4_hdmi->variant->external_irq_controller) - free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi); + return 0; err_delete_cec_adap: cec_delete_adapter(vc4_hdmi->cec_adap); return ret; } - -static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) -{ - struct platform_device *pdev = vc4_hdmi->pdev; - - if (vc4_hdmi->variant->external_irq_controller) { - free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi); - free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi); - } else { - free_irq(platform_get_irq(pdev, 0), vc4_hdmi); - } - - cec_unregister_adapter(vc4_hdmi->cec_adap); -} #else static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) { return 0; } - -static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {}; #endif static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, @@ -3039,7 +3048,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ret = vc4_hdmi_audio_init(vc4_hdmi); if (ret) - goto err_free_cec; + goto err_free_hotplug; vc4_debugfs_add_file(drm, variant->debugfs_name, vc4_hdmi_debugfs_regs, @@ -3049,8 +3058,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) return 0; -err_free_cec: - vc4_hdmi_cec_exit(vc4_hdmi); err_free_hotplug: vc4_hdmi_hotplug_exit(vc4_hdmi); err_put_runtime_pm: @@ -3092,7 +3099,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, kfree(vc4_hdmi->hdmi_regset.regs); kfree(vc4_hdmi->hd_regset.regs); - vc4_hdmi_cec_exit(vc4_hdmi); vc4_hdmi_hotplug_exit(vc4_hdmi); pm_runtime_disable(dev); -- cgit v1.2.3-59-g8ed1b From d3a84242f897506509d1bb29b8e64166958fb991 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:13 +0200 Subject: drm/vc4: hdmi: Use a device-managed action for DDC The reference to the DDC controller device needs to be put back when we're done with it. Let's use a device-managed action to simplify the driver. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-44-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 83ca1c02dde2..274f17880455 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2932,6 +2932,13 @@ static int vc4_hdmi_runtime_resume(struct device *dev) return 0; } +static void vc4_hdmi_put_ddc_device(void *ptr) +{ + struct vc4_hdmi *vc4_hdmi = ptr; + + put_device(&vc4_hdmi->ddc->dev); +} + static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) { const struct vc4_hdmi_variant *variant = of_device_get_match_data(dev); @@ -2987,13 +2994,16 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) return -EPROBE_DEFER; } + ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc_device, vc4_hdmi); + if (ret) + return ret; + /* Only use the GPIO HPD pin if present in the DT, otherwise * we'll use the HDMI core's register. */ vc4_hdmi->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN); if (IS_ERR(vc4_hdmi->hpd_gpio)) { - ret = PTR_ERR(vc4_hdmi->hpd_gpio); - goto err_put_ddc; + return PTR_ERR(vc4_hdmi->hpd_gpio); } vc4_hdmi->disable_wifi_frequencies = @@ -3064,8 +3074,6 @@ err_put_runtime_pm: pm_runtime_put_sync(dev); err_disable_runtime_pm: pm_runtime_disable(dev); -err_put_ddc: - put_device(&vc4_hdmi->ddc->dev); return ret; } @@ -3102,8 +3110,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, vc4_hdmi_hotplug_exit(vc4_hdmi); pm_runtime_disable(dev); - - put_device(&vc4_hdmi->ddc->dev); } static const struct component_ops vc4_hdmi_ops = { -- cgit v1.2.3-59-g8ed1b From a3dbb1c016576838a1591d2341641cb4067840e8 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:14 +0200 Subject: drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets The current code to build the registers set later exposed in debugfs for the HDMI controller relies on traditional allocations, that are later free'd as part of the driver unbind hook. Since krealloc doesn't have a DRM-managed equivalent, let's add an action to free the buffer later on. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-45-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 46 ++++++++++++++++++++++++++---------------- drivers/gpu/drm/vc4/vc4_hdmi.h | 3 ++- 2 files changed, 31 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 274f17880455..c93100fe4b42 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2675,7 +2675,15 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi) } #endif -static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, +static void vc4_hdmi_free_regset(struct drm_device *drm, void *ptr) +{ + struct debugfs_reg32 *regs = ptr; + + kfree(regs); +} + +static int vc4_hdmi_build_regset(struct drm_device *drm, + struct vc4_hdmi *vc4_hdmi, struct debugfs_regset32 *regset, enum vc4_hdmi_regs reg) { @@ -2683,6 +2691,7 @@ static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, struct debugfs_reg32 *regs, *new_regs; unsigned int count = 0; unsigned int i; + int ret; regs = kcalloc(variant->num_registers, sizeof(*regs), GFP_KERNEL); @@ -2708,10 +2717,15 @@ static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi, regset->regs = new_regs; regset->nregs = count; + ret = drmm_add_action_or_reset(drm, vc4_hdmi_free_regset, new_regs); + if (ret) + return ret; + return 0; } -static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) +static int vc4_hdmi_init_resources(struct drm_device *drm, + struct vc4_hdmi *vc4_hdmi) { struct platform_device *pdev = vc4_hdmi->pdev; struct device *dev = &pdev->dev; @@ -2725,11 +2739,11 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) if (IS_ERR(vc4_hdmi->hd_regs)) return PTR_ERR(vc4_hdmi->hd_regs); - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); if (ret) return ret; @@ -2752,7 +2766,8 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) return 0; } -static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) +static int vc5_hdmi_init_resources(struct drm_device *drm, + struct vc4_hdmi *vc4_hdmi) { struct platform_device *pdev = vc4_hdmi->pdev; struct device *dev = &pdev->dev; @@ -2854,35 +2869,35 @@ static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi) return PTR_ERR(vc4_hdmi->reset); } - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->cec_regset, VC5_CEC); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->cec_regset, VC5_CEC); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->csc_regset, VC5_CSC); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->csc_regset, VC5_CSC); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->dvp_regset, VC5_DVP); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->dvp_regset, VC5_DVP); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->phy_regset, VC5_PHY); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->phy_regset, VC5_PHY); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->ram_regset, VC5_RAM); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->ram_regset, VC5_RAM); if (ret) return ret; - ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->rm_regset, VC5_RM); + ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->rm_regset, VC5_RM); if (ret) return ret; @@ -2977,7 +2992,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK) vc4_hdmi->scdc_enabled = true; - ret = variant->init_resources(vc4_hdmi); + ret = variant->init_resources(drm, vc4_hdmi); if (ret) return ret; @@ -3104,9 +3119,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0); vc4_hdmi = dev_get_drvdata(dev); - kfree(vc4_hdmi->hdmi_regset.regs); - kfree(vc4_hdmi->hd_regset.regs); - vc4_hdmi_hotplug_exit(vc4_hdmi); pm_runtime_disable(dev); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h index c3ed2b07df23..99b0bc1297be 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.h +++ b/drivers/gpu/drm/vc4/vc4_hdmi.h @@ -58,7 +58,8 @@ struct vc4_hdmi_variant { /* Callback to get the resources (memory region, interrupts, * clocks, etc) for that variant. */ - int (*init_resources)(struct vc4_hdmi *vc4_hdmi); + int (*init_resources)(struct drm_device *drm, + struct vc4_hdmi *vc4_hdmi); /* Callback to reset the HDMI block */ void (*reset)(struct vc4_hdmi *vc4_hdmi); -- cgit v1.2.3-59-g8ed1b From af8a3b125b682c4c582bb25342af4759d9b4a1e5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:15 +0200 Subject: drm/vc4: hdmi: Use devm to register hotplug interrupts Commit 776efe800fed ("drm/vc4: hdmi: Drop devm interrupt handler for hotplug interrupts") dropped the device-managed interrupt registration because it was creating bugs and races whenever an interrupt was coming in while the device was removed. However, our latest patches to the HDMI controller driver fix this as well, so we can use device-managed interrupt handlers again. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-46-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index c93100fe4b42..6275db463afd 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2216,21 +2216,19 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi) unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected"); unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed"); - ret = request_threaded_irq(hpd_con, - NULL, - vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, - "vc4 hdmi hpd connected", vc4_hdmi); + ret = devm_request_threaded_irq(&pdev->dev, hpd_con, + NULL, + vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, + "vc4 hdmi hpd connected", vc4_hdmi); if (ret) return ret; - ret = request_threaded_irq(hpd_rm, - NULL, - vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, - "vc4 hdmi hpd disconnected", vc4_hdmi); - if (ret) { - free_irq(hpd_con, vc4_hdmi); + ret = devm_request_threaded_irq(&pdev->dev, hpd_rm, + NULL, + vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT, + "vc4 hdmi hpd disconnected", vc4_hdmi); + if (ret) return ret; - } connector->polled = DRM_CONNECTOR_POLL_HPD; } @@ -2238,16 +2236,6 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi) return 0; } -static void vc4_hdmi_hotplug_exit(struct vc4_hdmi *vc4_hdmi) -{ - struct platform_device *pdev = vc4_hdmi->pdev; - - if (vc4_hdmi->variant->external_irq_controller) { - free_irq(platform_get_irq_byname(pdev, "hpd-connected"), vc4_hdmi); - free_irq(platform_get_irq_byname(pdev, "hpd-removed"), vc4_hdmi); - } -} - #ifdef CONFIG_DRM_VC4_HDMI_CEC static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv) { @@ -3069,11 +3057,11 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) ret = vc4_hdmi_cec_init(vc4_hdmi); if (ret) - goto err_free_hotplug; + goto err_put_runtime_pm; ret = vc4_hdmi_audio_init(vc4_hdmi); if (ret) - goto err_free_hotplug; + goto err_put_runtime_pm; vc4_debugfs_add_file(drm, variant->debugfs_name, vc4_hdmi_debugfs_regs, @@ -3083,8 +3071,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) return 0; -err_free_hotplug: - vc4_hdmi_hotplug_exit(vc4_hdmi); err_put_runtime_pm: pm_runtime_put_sync(dev); err_disable_runtime_pm: @@ -3096,8 +3082,6 @@ err_disable_runtime_pm: static void vc4_hdmi_unbind(struct device *dev, struct device *master, void *data) { - struct vc4_hdmi *vc4_hdmi; - /* * ASoC makes it a bit hard to retrieve a pointer to the * vc4_hdmi structure. Registering the card will overwrite our @@ -3117,9 +3101,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, */ BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0); BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0); - vc4_hdmi = dev_get_drvdata(dev); - - vc4_hdmi_hotplug_exit(vc4_hdmi); pm_runtime_disable(dev); } -- cgit v1.2.3-59-g8ed1b From 0c9d0ddd9cf4b43f8b3faf752c5a1aaa665ed099 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:16 +0200 Subject: drm/vc4: hdmi: Move audio structure offset checks The HDMI driver unbind hook doesn't have any ALSA-related code anymore, so let's move the ALSA sanity checks and comments we have to some other part of the driver dedicated to ALSA. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-47-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 6275db463afd..a130b7d48e46 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -2076,6 +2076,26 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi) int index, len; int ret; + /* + * ASoC makes it a bit hard to retrieve a pointer to the + * vc4_hdmi structure. Registering the card will overwrite our + * device drvdata with a pointer to the snd_soc_card structure, + * which can then be used to retrieve whatever drvdata we want + * to associate. + * + * However, that doesn't fly in the case where we wouldn't + * register an ASoC card (because of an old DT that is missing + * the dmas properties for example), then the card isn't + * registered and the device drvdata wouldn't be set. + * + * We can deal with both cases by making sure a snd_soc_card + * pointer and a vc4_hdmi structure are pointing to the same + * memory address, so we can treat them indistinctly without any + * issue. + */ + BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0); + BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0); + if (!of_find_property(dev->of_node, "dmas", &len) || !len) { dev_warn(dev, "'dmas' DT property is missing or empty, no HDMI audio\n"); @@ -3082,26 +3102,6 @@ err_disable_runtime_pm: static void vc4_hdmi_unbind(struct device *dev, struct device *master, void *data) { - /* - * ASoC makes it a bit hard to retrieve a pointer to the - * vc4_hdmi structure. Registering the card will overwrite our - * device drvdata with a pointer to the snd_soc_card structure, - * which can then be used to retrieve whatever drvdata we want - * to associate. - * - * However, that doesn't fly in the case where we wouldn't - * register an ASoC card (because of an old DT that is missing - * the dmas properties for example), then the card isn't - * registered and the device drvdata wouldn't be set. - * - * We can deal with both cases by making sure a snd_soc_card - * pointer and a vc4_hdmi structure are pointing to the same - * memory address, so we can treat them indistinctly without any - * issue. - */ - BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0); - BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0); - pm_runtime_disable(dev); } -- cgit v1.2.3-59-g8ed1b From cd00ed5187bff03d4c2ba143a5993383dd6ed66c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:17 +0200 Subject: drm/vc4: hdmi: Protect device resources after removal Whenever the device and driver are unbound, the main device and all the subdevices will be removed by calling their unbind() method. However, the DRM device itself will only be freed when the last user will have closed it. It means that there is a time window where the device and its resources aren't there anymore, but the userspace can still call into our driver. Fortunately, the DRM framework provides the drm_dev_enter() and drm_dev_exit() functions to make sure our underlying device is still there for the section protected by those calls. Let's add them to the HDMI driver. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-48-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 308 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 291 insertions(+), 17 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index a130b7d48e46..a826faf8b02d 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -146,7 +147,12 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; struct vc4_hdmi *vc4_hdmi = node->info_ent->data; + struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_printer p = drm_seq_file_printer(m); + int idx; + + if (!drm_dev_enter(drm, &idx)) + return -ENODEV; drm_print_regset32(&p, &vc4_hdmi->hdmi_regset); drm_print_regset32(&p, &vc4_hdmi->hd_regset); @@ -157,12 +163,23 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused) drm_print_regset32(&p, &vc4_hdmi->ram_regset); drm_print_regset32(&p, &vc4_hdmi->rm_regset); + drm_dev_exit(idx); + return 0; } static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi) { + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; + + /* + * We can be called by our bind callback, when the + * connector->dev pointer might not be initialised yet. + */ + if (drm && !drm_dev_enter(drm, &idx)) + return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -179,11 +196,23 @@ static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi) HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + if (drm) + drm_dev_exit(idx); } static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi) { + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; + + /* + * We can be called by our bind callback, when the + * connector->dev pointer might not be initialised yet. + */ + if (drm && !drm_dev_enter(drm, &idx)) + return; reset_control_reset(vc4_hdmi->reset); @@ -195,15 +224,31 @@ static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi) HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + if (drm) + drm_dev_exit(idx); } #ifdef CONFIG_DRM_VC4_HDMI_CEC static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) { - unsigned long cec_rate = clk_get_rate(vc4_hdmi->cec_clock); + struct drm_device *drm = vc4_hdmi->connector.dev; + unsigned long cec_rate; unsigned long flags; u16 clk_cnt; u32 value; + int idx; + + /* + * This function is called by our runtime_resume implementation + * and thus at bind time, when we haven't registered our + * connector yet and thus don't have a pointer to the DRM + * device. + */ + if (drm && !drm_dev_enter(drm, &idx)) + return; + + cec_rate = clk_get_rate(vc4_hdmi->cec_clock); spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -219,6 +264,9 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) HDMI_WRITE(HDMI_CEC_CNTRL_1, value); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + if (drm) + drm_dev_exit(idx); } #else static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {} @@ -440,25 +488,34 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder, bool poll) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; u32 packet_id = type - 0x80; unsigned long flags; + int ret = 0; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return -ENODEV; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id)); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); - if (!poll) - return 0; + if (poll) { + ret = wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) & + BIT(packet_id)), 100); + } - return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) & - BIT(packet_id)), 100); + drm_dev_exit(idx); + return ret; } static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, union hdmi_infoframe *frame) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; u32 packet_id = frame->any.type - 0x80; const struct vc4_hdmi_register *ram_packet_start = &vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START]; @@ -471,6 +528,10 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, unsigned long flags; ssize_t len, i; int ret; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) & VC4_HDMI_RAM_PACKET_ENABLE), @@ -478,12 +539,12 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); if (len < 0) - return; + goto out; ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true); if (ret) { DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret); - return; + goto out; } spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -519,6 +580,9 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, BIT(packet_id)), 100); if (ret) DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret); + +out: + drm_dev_exit(idx); } static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame, @@ -668,8 +732,10 @@ static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder, static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; unsigned long flags; + int idx; lockdep_assert_held(&vc4_hdmi->mutex); @@ -681,6 +747,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) vc4_hdmi->output_format)) return; + if (!drm_dev_enter(drm, &idx)) + return; + drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true); drm_scdc_set_scrambling(vc4_hdmi->ddc, true); @@ -689,6 +758,8 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) VC5_HDMI_SCRAMBLER_CTL_ENABLE); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + drm_dev_exit(idx); + vc4_hdmi->scdc_enabled = true; queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work, @@ -698,7 +769,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; lockdep_assert_held(&vc4_hdmi->mutex); @@ -710,6 +783,9 @@ static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder) if (delayed_work_pending(&vc4_hdmi->scrambling_work)) cancel_delayed_work_sync(&vc4_hdmi->scrambling_work); + if (!drm_dev_enter(drm, &idx)) + return; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) & ~VC5_HDMI_SCRAMBLER_CTL_ENABLE); @@ -717,6 +793,8 @@ static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder) drm_scdc_set_scrambling(vc4_hdmi->ddc, false); drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false); + + drm_dev_exit(idx); } static void vc4_hdmi_scrambling_wq(struct work_struct *work) @@ -739,12 +817,17 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; mutex_lock(&vc4_hdmi->mutex); vc4_hdmi->packet_ram_enabled = false; + if (!drm_dev_enter(drm, &idx)) + goto out; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0); @@ -762,6 +845,9 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder, vc4_hdmi_disable_scrambling(encoder); + drm_dev_exit(idx); + +out: mutex_unlock(&vc4_hdmi->mutex); } @@ -769,11 +855,16 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; int ret; + int idx; mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) + goto out; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX); @@ -789,6 +880,9 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder, if (ret < 0) DRM_ERROR("Failed to release power domain: %d\n", ret); + drm_dev_exit(idx); + +out: mutex_unlock(&vc4_hdmi->mutex); } @@ -796,8 +890,13 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, const struct drm_display_mode *mode) { + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; u32 csc_ctl; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -832,6 +931,8 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + drm_dev_exit(idx); } /* @@ -916,6 +1017,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, const struct drm_display_mode *mode) { + struct drm_device *drm = vc4_hdmi->connector.dev; struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(state); unsigned long flags; @@ -924,6 +1026,10 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, u32 csc_chan_ctl = 0; u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM, VC5_MT_CP_CSC_CTL_MODE); + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -966,12 +1072,15 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_CSC_CTL, csc_ctl); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + drm_dev_exit(idx); } static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, struct drm_display_mode *mode) { + struct drm_device *drm = vc4_hdmi->connector.dev; bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; @@ -991,6 +1100,10 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, VC4_HDMI_VERTB_VBP)); unsigned long flags; u32 reg; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -1023,12 +1136,15 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_MISC_CONTROL, reg); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + drm_dev_exit(idx); } static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, struct drm_connector_state *state, struct drm_display_mode *mode) { + struct drm_device *drm = vc4_hdmi->connector.dev; const struct vc4_hdmi_connector_state *vc4_state = conn_state_to_vc4_hdmi_conn_state(state); bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; @@ -1052,6 +1168,10 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, unsigned char gcp; bool gcp_en; u32 reg; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -1128,13 +1248,20 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi, HDMI_WRITE(HDMI_CLOCK_STOP, 0); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + drm_dev_exit(idx); } static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi) { + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; u32 drift; int ret; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -1163,12 +1290,15 @@ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi) VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1); WARN_ONCE(ret, "Timeout waiting for " "VC4_HDMI_FIFO_CTL_RECENTER_DONE"); + + drm_dev_exit(idx); } static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_connector *connector = &vc4_hdmi->connector; struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, connector); @@ -1179,9 +1309,13 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, unsigned long bvb_rate, hsm_rate; unsigned long flags; int ret; + int idx; mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) + goto out; + /* * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must * be faster than pixel clock, infinitesimally faster, tested in @@ -1202,13 +1336,13 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate); if (ret) { DRM_ERROR("Failed to set HSM clock rate: %d\n", ret); - goto out; + goto err_dev_exit; } ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to retain power domain: %d\n", ret); - goto out; + goto err_dev_exit; } ret = clk_set_rate(vc4_hdmi->pixel_clock, tmds_char_rate); @@ -1260,6 +1394,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder, if (vc4_hdmi->variant->set_timings) vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode); + drm_dev_exit(idx); + mutex_unlock(&vc4_hdmi->mutex); return; @@ -1268,6 +1404,8 @@ err_disable_pixel_clock: clk_disable_unprepare(vc4_hdmi->pixel_clock); err_put_runtime_pm: pm_runtime_put(&vc4_hdmi->pdev->dev); +err_dev_exit: + drm_dev_exit(idx); out: mutex_unlock(&vc4_hdmi->mutex); return; @@ -1277,14 +1415,19 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_connector *connector = &vc4_hdmi->connector; struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, connector); unsigned long flags; + int idx; mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) + return; + if (vc4_hdmi->variant->csc_setup) vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode); @@ -1292,6 +1435,8 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder, HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + drm_dev_exit(idx); + mutex_unlock(&vc4_hdmi->mutex); } @@ -1299,15 +1444,20 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode; struct drm_display_info *display = &vc4_hdmi->connector.display_info; bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC; bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC; unsigned long flags; int ret; + int idx; mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) + return; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_VID_CTL, @@ -1366,6 +1516,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder, vc4_hdmi_recenter_fifo(vc4_hdmi); vc4_hdmi_enable_scrambling(encoder); + drm_dev_exit(idx); mutex_unlock(&vc4_hdmi->mutex); } @@ -1714,13 +1865,20 @@ static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi) { + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; u32 hotplug; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return false; spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); hotplug = HDMI_READ(HDMI_HOTPLUG); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + drm_dev_exit(idx); + return !!(hotplug & VC4_HDMI_HOTPLUG_CONNECTED); } @@ -1728,10 +1886,16 @@ static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi) static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate) { - u32 hsm_clock = clk_get_rate(vc4_hdmi->audio_clock); + struct drm_device *drm = vc4_hdmi->connector.dev; + u32 hsm_clock; unsigned long flags; unsigned long n, m; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + hsm_clock = clk_get_rate(vc4_hdmi->audio_clock); rational_best_approximation(hsm_clock, samplerate, VC4_HD_MAI_SMP_N_MASK >> VC4_HD_MAI_SMP_N_SHIFT, @@ -1744,6 +1908,8 @@ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi, VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + + drm_dev_exit(idx); } static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate) @@ -1799,13 +1965,21 @@ static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi) static int vc4_hdmi_audio_startup(struct device *dev, void *data) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int ret = 0; + int idx; mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) { + ret = -ENODEV; + goto out; + } + if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { - mutex_unlock(&vc4_hdmi->mutex); - return -ENODEV; + ret = -ENODEV; + goto out_dev_exit; } vc4_hdmi->audio.streaming = true; @@ -1822,9 +1996,12 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data) if (vc4_hdmi->variant->phy_rng_enable) vc4_hdmi->variant->phy_rng_enable(vc4_hdmi); +out_dev_exit: + drm_dev_exit(idx); +out: mutex_unlock(&vc4_hdmi->mutex); - return 0; + return ret; } static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) @@ -1853,10 +2030,15 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi) static void vc4_hdmi_audio_shutdown(struct device *dev, void *data) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) + goto out; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_MAI_CTL, @@ -1872,6 +2054,9 @@ static void vc4_hdmi_audio_shutdown(struct device *dev, void *data) vc4_hdmi->audio.streaming = false; vc4_hdmi_audio_reset(vc4_hdmi); + drm_dev_exit(idx); + +out: mutex_unlock(&vc4_hdmi->mutex); } @@ -1919,6 +2104,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, struct hdmi_codec_params *params) { struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev); + struct drm_device *drm = vc4_hdmi->connector.dev; struct drm_encoder *encoder = &vc4_hdmi->encoder.base; unsigned int sample_rate = params->sample_rate; unsigned int channels = params->channels; @@ -1927,15 +2113,22 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, u32 channel_map; u32 mai_audio_format; u32 mai_sample_rate; + int ret = 0; + int idx; dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, sample_rate, params->sample_width, channels); mutex_lock(&vc4_hdmi->mutex); + if (!drm_dev_enter(drm, &idx)) { + ret = -ENODEV; + goto out; + } + if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) { - mutex_unlock(&vc4_hdmi->mutex); - return -EINVAL; + ret = -EINVAL; + goto out_dev_exit; } vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate); @@ -1992,9 +2185,12 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data, memcpy(&vc4_hdmi->audio.infoframe, ¶ms->cea, sizeof(params->cea)); vc4_hdmi_set_audio_infoframe(encoder); +out_dev_exit: + drm_dev_exit(idx); +out: mutex_unlock(&vc4_hdmi->mutex); - return 0; + return ret; } static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = { @@ -2329,6 +2525,17 @@ static irqreturn_t vc4_cec_irq_handler_tx_bare_locked(struct vc4_hdmi *vc4_hdmi) { u32 cntrl1; + /* + * We don't need to protect the register access using + * drm_dev_enter() there because the interrupt handler lifetime + * is tied to the device itself, and not to the DRM device. + * + * So when the device will be gone, one of the first thing we + * will be doing will be to unregister the interrupt handler, + * and then unregister the DRM device. drm_dev_enter() would + * thus always succeed if we are here. + */ + lockdep_assert_held(&vc4_hdmi->hw_lock); cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); @@ -2357,6 +2564,17 @@ static irqreturn_t vc4_cec_irq_handler_rx_bare_locked(struct vc4_hdmi *vc4_hdmi) lockdep_assert_held(&vc4_hdmi->hw_lock); + /* + * We don't need to protect the register access using + * drm_dev_enter() there because the interrupt handler lifetime + * is tied to the device itself, and not to the DRM device. + * + * So when the device will be gone, one of the first thing we + * will be doing will be to unregister the interrupt handler, + * and then unregister the DRM device. drm_dev_enter() would + * thus always succeed if we are here. + */ + vc4_hdmi->cec_rx_msg.len = 0; cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1); vc4_cec_read_msg(vc4_hdmi, cntrl1); @@ -2388,6 +2606,17 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) irqreturn_t ret; u32 cntrl5; + /* + * We don't need to protect the register access using + * drm_dev_enter() there because the interrupt handler lifetime + * is tied to the device itself, and not to the DRM device. + * + * So when the device will be gone, one of the first thing we + * will be doing will be to unregister the interrupt handler, + * and then unregister the DRM device. drm_dev_enter() would + * thus always succeed if we are here. + */ + if (!(stat & VC4_HDMI_CPU_CEC)) return IRQ_NONE; @@ -2408,11 +2637,13 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv) static int vc4_hdmi_cec_enable(struct cec_adapter *adap) { struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct drm_device *drm = vc4_hdmi->connector.dev; /* clock period in microseconds */ const u32 usecs = 1000000 / CEC_CLOCK_FREQ; unsigned long flags; u32 val; int ret; + int idx; /* * NOTE: This function should really take vc4_hdmi->mutex, but doing so @@ -2425,9 +2656,19 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap) * keep it in mind if we were to change that assumption. */ + if (!drm_dev_enter(drm, &idx)) + /* + * We can't return an error code, because the CEC + * framework will emit WARN_ON messages at unbind + * otherwise. + */ + return 0; + ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev); - if (ret) + if (ret) { + drm_dev_exit(idx); return ret; + } spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); @@ -2463,13 +2704,25 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap) spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + drm_dev_exit(idx); + return 0; } static int vc4_hdmi_cec_disable(struct cec_adapter *adap) { struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; + + if (!drm_dev_enter(drm, &idx)) + /* + * We can't return an error code, because the CEC + * framework will emit WARN_ON messages at unbind + * otherwise. + */ + return 0; /* * NOTE: This function should really take vc4_hdmi->mutex, but doing so @@ -2494,6 +2747,8 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap) pm_runtime_put(&vc4_hdmi->pdev->dev); + drm_dev_exit(idx); + return 0; } @@ -2508,7 +2763,9 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) { struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap); + struct drm_device *drm = vc4_hdmi->connector.dev; unsigned long flags; + int idx; /* * NOTE: This function should really take vc4_hdmi->mutex, but doing so @@ -2521,12 +2778,22 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr) * keep it in mind if we were to change that assumption. */ + if (!drm_dev_enter(drm, &idx)) + /* + * We can't return an error code, because the CEC + * framework will emit WARN_ON messages at unbind + * otherwise. + */ + return 0; + spin_lock_irqsave(&vc4_hdmi->hw_lock, flags); HDMI_WRITE(HDMI_CEC_CNTRL_1, (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) | (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT); spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + drm_dev_exit(idx); + return 0; } @@ -2538,6 +2805,7 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, unsigned long flags; u32 val; unsigned int i; + int idx; /* * NOTE: This function should really take vc4_hdmi->mutex, but doing so @@ -2550,8 +2818,12 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, * keep it in mind if we were to change that assumption. */ + if (!drm_dev_enter(dev, &idx)) + return -ENODEV; + if (msg->len > 16) { drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len); + drm_dev_exit(idx); return -ENOMEM; } @@ -2575,6 +2847,8 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags); + drm_dev_exit(idx); + return 0; } -- cgit v1.2.3-59-g8ed1b From 932d860f46722cd91a9d012c94b2d32013a8ea15 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:18 +0200 Subject: drm/vc4: hdmi: Switch to devm_pm_runtime_enable devm_pm_runtime_enable() simplifies the driver a bit since it will call pm_runtime_disable() automatically through a device-managed action. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-49-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_hdmi.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index a826faf8b02d..166d22d62d99 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -3314,7 +3314,9 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) vc4_hdmi->disable_4kp60 = true; } - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; /* * We need to have the device powered up at this point to call @@ -3322,7 +3324,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) */ ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_disable_runtime_pm; + return ret; if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") || of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) && @@ -3367,21 +3369,12 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) err_put_runtime_pm: pm_runtime_put_sync(dev); -err_disable_runtime_pm: - pm_runtime_disable(dev); return ret; } -static void vc4_hdmi_unbind(struct device *dev, struct device *master, - void *data) -{ - pm_runtime_disable(dev); -} - static const struct component_ops vc4_hdmi_ops = { .bind = vc4_hdmi_bind, - .unbind = vc4_hdmi_unbind, }; static int vc4_hdmi_dev_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 121b1a5f47e5bcb6baafd2aea00761af2ca5ff36 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:19 +0200 Subject: drm/vc4: txp: Remove vc4_dev txp pointer There's no user for that pointer so let's just get rid of it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-50-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.h | 1 - drivers/gpu/drm/vc4/vc4_txp.c | 6 ------ 2 files changed, 7 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 835d286c2802..0eb5f8a669fe 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -85,7 +85,6 @@ struct vc4_dev { struct vc4_hvs *hvs; struct vc4_v3d *v3d; struct vc4_vec *vec; - struct vc4_txp *txp; struct vc4_hang_state *hang_state; diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 448d48e7e99f..483a03a07ed2 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -469,7 +469,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_crtc *vc4_crtc; struct vc4_txp *txp; struct drm_crtc *crtc; @@ -523,7 +522,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) return ret; dev_set_drvdata(dev, txp); - vc4->txp = txp; vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset); @@ -533,13 +531,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) static void vc4_txp_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_txp *txp = dev_get_drvdata(dev); vc4_txp_connector_destroy(&txp->connector.base); - - vc4->txp = NULL; } static const struct component_ops vc4_txp_ops = { -- cgit v1.2.3-59-g8ed1b From 260ad78e9df95aa6154d85e7a9247aef28664854 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:20 +0200 Subject: drm/vc4: txp: Remove duplicate regset There's already a regset in the vc4_crtc structure so there's no need to duplicate it in vc4_txp. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-51-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_txp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 483a03a07ed2..19e37924ce77 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -155,7 +155,6 @@ struct vc4_txp { struct drm_writeback_connector connector; void __iomem *regs; - struct debugfs_regset32 regset; }; static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder) @@ -494,9 +493,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) txp->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(txp->regs)) return PTR_ERR(txp->regs); - txp->regset.base = txp->regs; - txp->regset.regs = txp_regs; - txp->regset.nregs = ARRAY_SIZE(txp_regs); + vc4_crtc->regset.base = txp->regs; + vc4_crtc->regset.regs = txp_regs; + vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs); drm_connector_helper_add(&txp->connector.base, &vc4_txp_connector_helper_funcs); @@ -523,7 +522,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, txp); - vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset); + vc4_debugfs_add_regset32(drm, "txp_regs", &vc4_crtc->regset); return 0; } -- cgit v1.2.3-59-g8ed1b From b0da34c7970f6a5f156fd8036fbb83c999903628 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:21 +0200 Subject: drm/vc4: txp: Switch to drmm_kzalloc Our internal structure that stores the DRM entities structure is allocated through a device-managed kzalloc. This means that this will eventually be freed whenever the device is removed. In our case, the most likely source of removal is that the main device is going to be unbound, and component_unbind_all() is being run. However, it occurs while the DRM device is still registered, which will create dangling pointers, eventually resulting in use-after-free. Switch to a DRM-managed allocation to keep our structure until the DRM driver doesn't need it anymore. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-52-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_txp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 19e37924ce77..160f9d0ec6f0 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -478,7 +478,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) if (irq < 0) return irq; - txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL); + txp = drmm_kzalloc(drm, sizeof(*txp), GFP_KERNEL); if (!txp) return -ENOMEM; vc4_crtc = &txp->base; -- cgit v1.2.3-59-g8ed1b From d67210bbe5f3ec53fa6a46fbaad87d7a65ab544c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:22 +0200 Subject: drm/vc4: txp: Remove call to drm_connector_unregister() drm_connector_unregister() is only to be used for connectors that have been registered through drm_connector_register() after drm_dev_register() has been called. This is our case here so let's remove the call. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-53-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_txp.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 160f9d0ec6f0..7e5212bcf8e6 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -336,16 +336,10 @@ vc4_txp_connector_detect(struct drm_connector *connector, bool force) return connector_status_connected; } -static void vc4_txp_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static const struct drm_connector_funcs vc4_txp_connector_funcs = { .detect = vc4_txp_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = vc4_txp_connector_destroy, + .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -532,7 +526,7 @@ static void vc4_txp_unbind(struct device *dev, struct device *master, { struct vc4_txp *txp = dev_get_drvdata(dev); - vc4_txp_connector_destroy(&txp->connector.base); + drm_connector_cleanup(&txp->connector.base); } static const struct component_ops vc4_txp_ops = { -- cgit v1.2.3-59-g8ed1b From b7345c9799da578c150fde3072446e4049c39c41 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:23 +0200 Subject: drm/vc4: txp: Protect device resources Our current code now mixes some resources whose lifetime are tied to the device (clocks, IO mappings, etc.) and some that are tied to the DRM device (encoder, bridge). The device one will be freed at unbind time, but the DRM one will only be freed when the last user of the DRM device closes its file handle. So we end up with a time window during which we can call the encoder hooks, but we don't have access to the underlying resources and device. Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so that we bail out if we are during that window. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-54-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_txp.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 7e5212bcf8e6..f6f61cf5f3f7 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -275,6 +276,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, struct drm_atomic_state *state) { + struct drm_device *drm = conn->dev; struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, conn); struct vc4_txp *txp = connector_to_vc4_txp(conn); @@ -282,6 +284,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, struct drm_display_mode *mode; struct drm_framebuffer *fb; u32 ctrl; + int idx; int i; if (WARN_ON(!conn_state->writeback_job)) @@ -311,6 +314,9 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, */ ctrl |= TXP_ALPHA_INVERT; + if (!drm_dev_enter(drm, &idx)) + return; + gem = drm_fb_cma_get_gem_obj(fb, 0); TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]); TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]); @@ -321,6 +327,8 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, TXP_WRITE(TXP_DST_CTRL, ctrl); drm_writeback_queue_job(&txp->connector, conn_state); + + drm_dev_exit(idx); } static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = { @@ -347,7 +355,12 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = { static void vc4_txp_encoder_disable(struct drm_encoder *encoder) { + struct drm_device *drm = encoder->dev; struct vc4_txp *txp = encoder_to_vc4_txp(encoder); + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; if (TXP_READ(TXP_DST_CTRL) & TXP_BUSY) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); @@ -362,6 +375,8 @@ static void vc4_txp_encoder_disable(struct drm_encoder *encoder) } TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN); + + drm_dev_exit(idx); } static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { @@ -445,6 +460,16 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) struct vc4_txp *txp = data; struct vc4_crtc *vc4_crtc = &txp->base; + /* + * We don't need to protect the register access using + * drm_dev_enter() there because the interrupt handler lifetime + * is tied to the device itself, and not to the DRM device. + * + * So when the device will be gone, one of the first thing we + * will be doing will be to unregister the interrupt handler, + * and then unregister the DRM device. drm_dev_enter() would + * thus always succeed if we are here. + */ TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI); vc4_crtc_handle_vblank(vc4_crtc); drm_writeback_signal_completion(&txp->connector, 0); -- cgit v1.2.3-59-g8ed1b From a0883e4df276b3ac9532d4de6fa7e1186cfa161c Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:24 +0200 Subject: drm/vc4: vec: Remove vc4_dev vec pointer There's no user for that pointer so let's just get rid of it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-55-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_drv.h | 1 - drivers/gpu/drm/vc4/vc4_vec.c | 7 ------- 2 files changed, 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 0eb5f8a669fe..b7ccdc2b09de 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -84,7 +84,6 @@ struct vc4_dev { struct vc4_hvs *hvs; struct vc4_v3d *v3d; - struct vc4_vec *vec; struct vc4_hang_state *hang_state; diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 11fc3d6f66b1..99fe40c8cf81 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -532,7 +532,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_vec *vec; struct vc4_vec_encoder *vc4_vec_encoder; int ret; @@ -585,8 +584,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, vec); - vc4->vec = vec; - vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset); return 0; @@ -601,15 +598,11 @@ err_destroy_encoder: static void vc4_vec_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_vec *vec = dev_get_drvdata(dev); vc4_vec_connector_destroy(vec->connector); drm_encoder_cleanup(vec->encoder); pm_runtime_disable(dev); - - vc4->vec = NULL; } static const struct component_ops vc4_vec_ops = { -- cgit v1.2.3-59-g8ed1b From 9780315b068204bae921512154fefe84d321079e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:25 +0200 Subject: drm/vc4: vec: Embed DRM structures into the private structure The VC4 VEC driver private structure contains only a pointer to the encoder and connector it implements. This makes the overall structure somewhat inconsistent with the rest of the driver, and complicates its initialisation without any apparent gain. Let's embed the drm_encoder structure (through the vc4_encoder one) and drm_connector into struct vc4_vec to fix both issues. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-56-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 83 +++++++++++-------------------------------- 1 file changed, 21 insertions(+), 62 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 99fe40c8cf81..2c96d5adcf49 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -160,12 +160,12 @@ struct vc4_vec_variant { /* General VEC hardware state. */ struct vc4_vec { + struct vc4_encoder encoder; + struct drm_connector connector; + struct platform_device *pdev; const struct vc4_vec_variant *variant; - struct drm_encoder *encoder; - struct drm_connector *connector; - void __iomem *regs; struct clk *clock; @@ -178,30 +178,12 @@ struct vc4_vec { #define VEC_READ(offset) readl(vec->regs + (offset)) #define VEC_WRITE(offset, val) writel(val, vec->regs + (offset)) -/* VC4 VEC encoder KMS struct */ -struct vc4_vec_encoder { - struct vc4_encoder base; - struct vc4_vec *vec; -}; - -static inline struct vc4_vec_encoder * -to_vc4_vec_encoder(struct drm_encoder *encoder) +static inline struct vc4_vec * +encoder_to_vc4_vec(struct drm_encoder *encoder) { - return container_of(encoder, struct vc4_vec_encoder, base.base); + return container_of(encoder, struct vc4_vec, encoder.base); } -/* VC4 VEC connector KMS struct */ -struct vc4_vec_connector { - struct drm_connector base; - struct vc4_vec *vec; - - /* Since the connector is attached to just the one encoder, - * this is the reference to it so we can do the best_encoder() - * hook. - */ - struct drm_encoder *encoder; -}; - enum vc4_vec_tv_mode_id { VC4_VEC_TV_MODE_NTSC, VC4_VEC_TV_MODE_NTSC_J, @@ -343,22 +325,12 @@ static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = .get_modes = vc4_vec_connector_get_modes, }; -static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev, - struct vc4_vec *vec) +static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) { - struct drm_connector *connector = NULL; - struct vc4_vec_connector *vec_connector; + struct drm_connector *connector = &vec->connector; - vec_connector = devm_kzalloc(dev->dev, sizeof(*vec_connector), - GFP_KERNEL); - if (!vec_connector) - return ERR_PTR(-ENOMEM); - - connector = &vec_connector->base; connector->interlace_allowed = true; - vec_connector->encoder = vec->encoder; - vec_connector->vec = vec; drm_connector_init(dev, connector, &vc4_vec_connector_funcs, DRM_MODE_CONNECTOR_Composite); @@ -369,15 +341,14 @@ static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev, VC4_VEC_TV_MODE_NTSC); vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC]; - drm_connector_attach_encoder(connector, vec->encoder); + drm_connector_attach_encoder(connector, &vec->encoder.base); - return connector; + return 0; } static void vc4_vec_encoder_disable(struct drm_encoder *encoder) { - struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); - struct vc4_vec *vec = vc4_vec_encoder->vec; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); int ret; VEC_WRITE(VEC_CFG, 0); @@ -398,8 +369,7 @@ static void vc4_vec_encoder_disable(struct drm_encoder *encoder) static void vc4_vec_encoder_enable(struct drm_encoder *encoder) { - struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); - struct vc4_vec *vec = vc4_vec_encoder->vec; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); int ret; ret = pm_runtime_get_sync(&vec->pdev->dev); @@ -474,8 +444,7 @@ static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); - struct vc4_vec *vec = vc4_vec_encoder->vec; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode]; } @@ -533,7 +502,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); struct vc4_vec *vec; - struct vc4_vec_encoder *vc4_vec_encoder; int ret; ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names), @@ -545,14 +513,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) if (!vec) return -ENOMEM; - vc4_vec_encoder = devm_kzalloc(dev, sizeof(*vc4_vec_encoder), - GFP_KERNEL); - if (!vc4_vec_encoder) - return -ENOMEM; - vc4_vec_encoder->base.type = VC4_ENCODER_TYPE_VEC; - vc4_vec_encoder->vec = vec; - vec->encoder = &vc4_vec_encoder->base.base; - + vec->encoder.type = VC4_ENCODER_TYPE_VEC; vec->pdev = pdev; vec->variant = (const struct vc4_vec_variant *) of_device_get_match_data(dev); @@ -573,14 +534,12 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) pm_runtime_enable(dev); - drm_simple_encoder_init(drm, vec->encoder, DRM_MODE_ENCODER_TVDAC); - drm_encoder_helper_add(vec->encoder, &vc4_vec_encoder_helper_funcs); + drm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC); + drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs); - vec->connector = vc4_vec_connector_init(drm, vec); - if (IS_ERR(vec->connector)) { - ret = PTR_ERR(vec->connector); + ret = vc4_vec_connector_init(drm, vec); + if (ret) goto err_destroy_encoder; - } dev_set_drvdata(dev, vec); @@ -589,7 +548,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) return 0; err_destroy_encoder: - drm_encoder_cleanup(vec->encoder); + drm_encoder_cleanup(&vec->encoder.base); pm_runtime_disable(dev); return ret; @@ -600,8 +559,8 @@ static void vc4_vec_unbind(struct device *dev, struct device *master, { struct vc4_vec *vec = dev_get_drvdata(dev); - vc4_vec_connector_destroy(vec->connector); - drm_encoder_cleanup(vec->encoder); + vc4_vec_connector_destroy(&vec->connector); + drm_encoder_cleanup(&vec->encoder.base); pm_runtime_disable(dev); } -- cgit v1.2.3-59-g8ed1b From fe7289e2184485c5dc51cc49a94e442d14d08255 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:26 +0200 Subject: drm/vc4: vec: Switch to drmm_kzalloc Our internal structure that stores the DRM entities structure is allocated through a device-managed kzalloc. This means that this will eventually be freed whenever the device is removed. In our case, the most likely source of removal is that the main device is going to be unbound, and component_unbind_all() is being run. However, it occurs while the DRM device is still registered, which will create dangling pointers, eventually resulting in use-after-free. Switch to a DRM-managed allocation to keep our structure until the DRM driver doesn't need it anymore. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-57-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 2c96d5adcf49..a051b25337c0 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -509,7 +509,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - vec = devm_kzalloc(dev, sizeof(*vec), GFP_KERNEL); + vec = drmm_kzalloc(drm, sizeof(*vec), GFP_KERNEL); if (!vec) return -ENOMEM; -- cgit v1.2.3-59-g8ed1b From cd2444a7953972ed495b8c0e8f6202d6ad11fb0d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:27 +0200 Subject: drm/vc4: vec: Remove call to drm_connector_unregister() drm_connector_unregister() is only to be used for connectors that have been registered through drm_connector_register() after drm_dev_register() has been called. This is our case here so let's remove the call. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-58-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index a051b25337c0..2e9dc09fc6f0 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -289,12 +289,6 @@ vc4_vec_connector_detect(struct drm_connector *connector, bool force) return connector_status_unknown; } -static void vc4_vec_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static int vc4_vec_connector_get_modes(struct drm_connector *connector) { struct drm_connector_state *state = connector->state; @@ -315,7 +309,7 @@ static int vc4_vec_connector_get_modes(struct drm_connector *connector) static const struct drm_connector_funcs vc4_vec_connector_funcs = { .detect = vc4_vec_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = vc4_vec_connector_destroy, + .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -559,7 +553,7 @@ static void vc4_vec_unbind(struct device *dev, struct device *master, { struct vc4_vec *vec = dev_get_drvdata(dev); - vc4_vec_connector_destroy(&vec->connector); + drm_connector_cleanup(&vec->connector); drm_encoder_cleanup(&vec->encoder.base); pm_runtime_disable(dev); } -- cgit v1.2.3-59-g8ed1b From c62f432f2ed97305f96d393c5e3756ca1cd3c1de Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:28 +0200 Subject: drm/vc4: vec: Switch to DRM-managed encoder initialization The current code will call drm_encoder_cleanup() when the device is unbound. However, by then, there might still be some references held to that encoder, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-59-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 2e9dc09fc6f0..518b66bcc0b5 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -528,12 +528,18 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) pm_runtime_enable(dev); - drm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC); + ret = drmm_encoder_init(drm, &vec->encoder.base, + NULL, + DRM_MODE_ENCODER_TVDAC, + NULL); + if (ret) + goto err_put_runtime_pm; + drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs); ret = vc4_vec_connector_init(drm, vec); if (ret) - goto err_destroy_encoder; + goto err_put_runtime_pm; dev_set_drvdata(dev, vec); @@ -541,8 +547,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) return 0; -err_destroy_encoder: - drm_encoder_cleanup(&vec->encoder.base); +err_put_runtime_pm: pm_runtime_disable(dev); return ret; @@ -554,7 +559,6 @@ static void vc4_vec_unbind(struct device *dev, struct device *master, struct vc4_vec *vec = dev_get_drvdata(dev); drm_connector_cleanup(&vec->connector); - drm_encoder_cleanup(&vec->encoder.base); pm_runtime_disable(dev); } -- cgit v1.2.3-59-g8ed1b From 881f6d945b34e5604003ba1cc85c9dfbb7e31af2 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:29 +0200 Subject: drm/vc4: vec: Switch to DRM-managed connector initialization The current code will call drm_connector_unregister() and drm_connector_cleanup() when the device is unbound. However, by then, there might still be some references held to that connector, including by the userspace that might still have the DRM device open. Let's switch to a DRM-managed initialization to clean up after ourselves only once the DRM device has been last closed. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-60-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 518b66bcc0b5..4b843b18c006 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -309,7 +309,6 @@ static int vc4_vec_connector_get_modes(struct drm_connector *connector) static const struct drm_connector_funcs vc4_vec_connector_funcs = { .detect = vc4_vec_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -322,12 +321,15 @@ static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) { struct drm_connector *connector = &vec->connector; + int ret; connector->interlace_allowed = true; + ret = drmm_connector_init(dev, connector, &vc4_vec_connector_funcs, + DRM_MODE_CONNECTOR_Composite, NULL); + if (ret) + return ret; - drm_connector_init(dev, connector, &vc4_vec_connector_funcs, - DRM_MODE_CONNECTOR_Composite); drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs); drm_object_attach_property(&connector->base, @@ -556,9 +558,6 @@ err_put_runtime_pm: static void vc4_vec_unbind(struct device *dev, struct device *master, void *data) { - struct vc4_vec *vec = dev_get_drvdata(dev); - - drm_connector_cleanup(&vec->connector); pm_runtime_disable(dev); } -- cgit v1.2.3-59-g8ed1b From f0601ef8631ceeab10c7af69b248fc106e0ecf09 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:30 +0200 Subject: drm/vc4: vec: Protect device resources after removal Whenever the device and driver are unbound, the main device and all the subdevices will be removed by calling their unbind() method. However, the DRM device itself will only be freed when the last user will have closed it. It means that there is a time window where the device and its resources aren't there anymore, but the userspace can still call into our driver. Fortunately, the DRM framework provides the drm_dev_enter() and drm_dev_exit() functions to make sure our underlying device is still there for the section protected by those calls. Let's add them to the VEC driver. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-61-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 67 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index 4b843b18c006..df6adef96050 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -14,6 +14,7 @@ */ #include +#include #include #include #include @@ -225,14 +226,30 @@ static const struct debugfs_reg32 vec_regs[] = { static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec) { + struct drm_device *drm = vec->connector.dev; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN); VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); + + drm_dev_exit(idx); } static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec) { + struct drm_device *drm = vec->connector.dev; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD); VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); + + drm_dev_exit(idx); } static const struct drm_display_mode ntsc_mode = { @@ -244,17 +261,33 @@ static const struct drm_display_mode ntsc_mode = { static void vc4_vec_pal_mode_set(struct vc4_vec *vec) { + struct drm_device *drm = vec->connector.dev; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); + + drm_dev_exit(idx); } static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec) { + struct drm_device *drm = vec->connector.dev; + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ); VEC_WRITE(VEC_FREQ3_2, 0x223b); VEC_WRITE(VEC_FREQ1_0, 0x61d1); + + drm_dev_exit(idx); } static const struct drm_display_mode pal_mode = { @@ -344,8 +377,12 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec) static void vc4_vec_encoder_disable(struct drm_encoder *encoder) { + struct drm_device *drm = encoder->dev; struct vc4_vec *vec = encoder_to_vc4_vec(encoder); - int ret; + int idx, ret; + + if (!drm_dev_enter(drm, &idx)) + return; VEC_WRITE(VEC_CFG, 0); VEC_WRITE(VEC_DAC_MISC, @@ -359,19 +396,29 @@ static void vc4_vec_encoder_disable(struct drm_encoder *encoder) ret = pm_runtime_put(&vec->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to release power domain: %d\n", ret); - return; + goto err_dev_exit; } + + drm_dev_exit(idx); + return; + +err_dev_exit: + drm_dev_exit(idx); } static void vc4_vec_encoder_enable(struct drm_encoder *encoder) { + struct drm_device *drm = encoder->dev; struct vc4_vec *vec = encoder_to_vc4_vec(encoder); - int ret; + int idx, ret; + + if (!drm_dev_enter(drm, &idx)) + return; ret = pm_runtime_get_sync(&vec->pdev->dev); if (ret < 0) { DRM_ERROR("Failed to retain power domain: %d\n", ret); - return; + goto err_dev_exit; } /* @@ -384,13 +431,13 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) ret = clk_set_rate(vec->clock, 108000000); if (ret) { DRM_ERROR("Failed to set clock rate: %d\n", ret); - return; + goto err_put_runtime_pm; } ret = clk_prepare_enable(vec->clock); if (ret) { DRM_ERROR("Failed to turn on core clock: %d\n", ret); - return; + goto err_put_runtime_pm; } /* Reset the different blocks */ @@ -426,6 +473,14 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) VEC_WRITE(VEC_DAC_MISC, VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N); VEC_WRITE(VEC_CFG, VEC_CFG_VEC_EN); + + drm_dev_exit(idx); + return; + +err_put_runtime_pm: + pm_runtime_put(&vec->pdev->dev); +err_dev_exit: + drm_dev_exit(idx); } -- cgit v1.2.3-59-g8ed1b From 4e06fd9c7c8a2815c63ff132fd392d76e42436e9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:31 +0200 Subject: drm/vc4: vec: Switch to devm_pm_runtime_enable devm_pm_runtime_enable() simplifies the driver a bit since it will call pm_runtime_disable() automatically through a device-managed action. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-62-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_vec.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index df6adef96050..d356ffa52866 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -583,42 +583,32 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) return ret; } - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = drmm_encoder_init(drm, &vec->encoder.base, NULL, DRM_MODE_ENCODER_TVDAC, NULL); if (ret) - goto err_put_runtime_pm; + return ret; drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs); ret = vc4_vec_connector_init(drm, vec); if (ret) - goto err_put_runtime_pm; + return ret; dev_set_drvdata(dev, vec); vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset); return 0; - -err_put_runtime_pm: - pm_runtime_disable(dev); - - return ret; -} - -static void vc4_vec_unbind(struct device *dev, struct device *master, - void *data) -{ - pm_runtime_disable(dev); } static const struct component_ops vc4_vec_ops = { .bind = vc4_vec_bind, - .unbind = vc4_vec_unbind, }; static int vc4_vec_dev_probe(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From be919b89b28e0fa3c4eff39dd96350bc7803f194 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:32 +0200 Subject: drm/vc4: debugfs: Protect device resources Our current code now mixes some resources whose lifetime are tied to the device (clocks, IO mappings, etc.) and some that are tied to the DRM device (encoder, bridge). The device one will be freed at unbind time, but the DRM one will only be freed when the last user of the DRM device closes its file handle. So we end up with a time window during which we can call the encoder hooks, but we don't have access to the underlying resources and device. Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so that we bail out if we are during that window. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-63-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_debugfs.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index ba2d8ea562af..d6350a8ca048 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -3,6 +3,8 @@ * Copyright © 2014 Broadcom */ +#include + #include #include #include @@ -41,11 +43,18 @@ vc4_debugfs_init(struct drm_minor *minor) static int vc4_debugfs_regset32(struct seq_file *m, void *unused) { struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *drm = node->minor->dev; struct debugfs_regset32 *regset = node->info_ent->data; struct drm_printer p = drm_seq_file_printer(m); + int idx; + + if (!drm_dev_enter(drm, &idx)) + return -ENODEV; drm_print_regset32(&p, regset); + drm_dev_exit(idx); + return 0; } -- cgit v1.2.3-59-g8ed1b From fe3b0f784cc8bb208fada872c13e6bd694bb1669 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:33 +0200 Subject: drm/vc4: debugfs: Return an error on failure vc4_debugfs_add_file() can fail, so let's propagate its error code instead of silencing it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-64-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_debugfs.c | 20 +++++++++++--------- drivers/gpu/drm/vc4/vc4_drv.h | 30 ++++++++++++++++-------------- 2 files changed, 27 insertions(+), 23 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index d6350a8ca048..b857fb9c94bc 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -67,10 +67,10 @@ static int vc4_debugfs_regset32(struct seq_file *m, void *unused) * track the request and delay it to be called on each minor during * vc4_debugfs_init(). */ -void vc4_debugfs_add_file(struct drm_device *dev, - const char *name, - int (*show)(struct seq_file*, void*), - void *data) +int vc4_debugfs_add_file(struct drm_device *dev, + const char *name, + int (*show)(struct seq_file*, void*), + void *data) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -78,18 +78,20 @@ void vc4_debugfs_add_file(struct drm_device *dev, devm_kzalloc(dev->dev, sizeof(*entry), GFP_KERNEL); if (!entry) - return; + return -ENOMEM; entry->info.name = name; entry->info.show = show; entry->info.data = data; list_add(&entry->link, &vc4->debugfs_list); + + return 0; } -void vc4_debugfs_add_regset32(struct drm_device *drm, - const char *name, - struct debugfs_regset32 *regset) +int vc4_debugfs_add_regset32(struct drm_device *drm, + const char *name, + struct debugfs_regset32 *regset) { - vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset); + return vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset); } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index b7ccdc2b09de..5d857816426b 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -866,25 +866,27 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state, /* vc4_debugfs.c */ void vc4_debugfs_init(struct drm_minor *minor); #ifdef CONFIG_DEBUG_FS -void vc4_debugfs_add_file(struct drm_device *drm, - const char *filename, - int (*show)(struct seq_file*, void*), - void *data); -void vc4_debugfs_add_regset32(struct drm_device *drm, - const char *filename, - struct debugfs_regset32 *regset); +int vc4_debugfs_add_file(struct drm_device *drm, + const char *filename, + int (*show)(struct seq_file*, void*), + void *data); +int vc4_debugfs_add_regset32(struct drm_device *drm, + const char *filename, + struct debugfs_regset32 *regset); #else -static inline void vc4_debugfs_add_file(struct drm_device *drm, - const char *filename, - int (*show)(struct seq_file*, void*), - void *data) +static inline int vc4_debugfs_add_file(struct drm_device *drm, + const char *filename, + int (*show)(struct seq_file*, void*), + void *data) { + return 0; } -static inline void vc4_debugfs_add_regset32(struct drm_device *drm, - const char *filename, - struct debugfs_regset32 *regset) +static inline int vc4_debugfs_add_regset32(struct drm_device *drm, + const char *filename, + struct debugfs_regset32 *regset) { + return 0; } #endif -- cgit v1.2.3-59-g8ed1b From 445b287e18ca374b5498e0f7cecb6c5f673c4edb Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:34 +0200 Subject: drm/vc4: debugfs: Simplify debugfs registration The vc4 has a custom API to allow components to register a debugfs file before the DRM driver has been registered and the debugfs_init hook has been called. However, the .late_register hook allows to have the debugfs file creation deferred after that time already. Let's remove our custom code to only register later our debugfs entries as part of either debugfs_init or after it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-65-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_bo.c | 20 ++++++++++++--- drivers/gpu/drm/vc4/vc4_crtc.c | 19 ++++++++++++--- drivers/gpu/drm/vc4/vc4_debugfs.c | 51 +++++++++++++-------------------------- drivers/gpu/drm/vc4/vc4_dpi.c | 21 +++++++++++++--- drivers/gpu/drm/vc4/vc4_drv.h | 12 ++++++--- drivers/gpu/drm/vc4/vc4_dsi.c | 22 ++++++++++++++--- drivers/gpu/drm/vc4/vc4_hdmi.c | 26 ++++++++++++++++---- drivers/gpu/drm/vc4/vc4_hvs.c | 39 +++++++++++++++++++++++++----- drivers/gpu/drm/vc4/vc4_txp.c | 3 +-- drivers/gpu/drm/vc4/vc4_v3d.c | 25 ++++++++++++++++--- drivers/gpu/drm/vc4/vc4_vec.c | 22 ++++++++++++++--- 11 files changed, 191 insertions(+), 69 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 0846d56f74f2..9706fec47bcd 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -984,6 +984,23 @@ int vc4_get_tiling_ioctl(struct drm_device *dev, void *data, return 0; } +int vc4_bo_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *drm = minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + int ret; + + if (!vc4->v3d) + return -ENODEV; + + ret = vc4_debugfs_add_file(minor, "bo_stats", + vc4_bo_stats_debugfs, NULL); + if (ret) + return ret; + + return 0; +} + static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused); int vc4_bo_cache_init(struct drm_device *dev) { @@ -1008,9 +1025,6 @@ int vc4_bo_cache_init(struct drm_device *dev) vc4->bo_labels[i].name = bo_type_names[i]; mutex_init(&vc4->bo_lock); - - vc4_debugfs_add_file(dev, "bo_stats", vc4_bo_stats_debugfs, NULL); - INIT_LIST_HEAD(&vc4->bo_cache.time_list); INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 3768a2a57ca9..d3efa7e5b5ed 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -1052,6 +1052,21 @@ void vc4_crtc_reset(struct drm_crtc *crtc) __drm_atomic_helper_crtc_reset(crtc, &vc4_crtc_state->base); } +int vc4_crtc_late_register(struct drm_crtc *crtc) +{ + struct drm_device *drm = crtc->dev; + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc); + int ret; + + ret = vc4_debugfs_add_regset32(drm->primary, crtc_data->debugfs_name, + &vc4_crtc->regset); + if (ret) + return ret; + + return 0; +} + static const struct drm_crtc_funcs vc4_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = vc4_page_flip, @@ -1064,6 +1079,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = { .enable_vblank = vc4_enable_vblank, .disable_vblank = vc4_disable_vblank, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .late_register = vc4_crtc_late_register, }; static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { @@ -1318,9 +1334,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data) platform_set_drvdata(pdev, vc4_crtc); - vc4_debugfs_add_regset32(drm, pv_data->base.debugfs_name, - &vc4_crtc->regset); - return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index b857fb9c94bc..19cda4f91a82 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -14,11 +14,6 @@ #include "vc4_drv.h" #include "vc4_regs.h" -struct vc4_debugfs_info_entry { - struct list_head link; - struct drm_info_list info; -}; - /* * Called at drm_dev_register() time on each of the minors registered * by the DRM device, to attach the debugfs files. @@ -27,16 +22,13 @@ void vc4_debugfs_init(struct drm_minor *minor) { struct vc4_dev *vc4 = to_vc4_dev(minor->dev); - struct vc4_debugfs_info_entry *entry; + struct drm_device *drm = &vc4->base; - if (!of_device_is_compatible(vc4->hvs->pdev->dev.of_node, - "brcm,bcm2711-vc5")) - debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR, - minor->debugfs_root, &vc4->load_tracker_enabled); + drm_WARN_ON(drm, vc4_hvs_debugfs_init(minor)); - list_for_each_entry(entry, &vc4->debugfs_list, link) { - drm_debugfs_create_files(&entry->info, 1, - minor->debugfs_root, minor); + if (vc4->v3d) { + drm_WARN_ON(drm, vc4_bo_debugfs_init(minor)); + drm_WARN_ON(drm, vc4_v3d_debugfs_init(minor)); } } @@ -58,40 +50,31 @@ static int vc4_debugfs_regset32(struct seq_file *m, void *unused) return 0; } -/* - * Registers a debugfs file with a callback function for a vc4 component. - * - * This is like drm_debugfs_create_files(), but that can only be - * called a given DRM minor, while the various VC4 components want to - * register their debugfs files during the component bind process. We - * track the request and delay it to be called on each minor during - * vc4_debugfs_init(). - */ -int vc4_debugfs_add_file(struct drm_device *dev, +int vc4_debugfs_add_file(struct drm_minor *minor, const char *name, int (*show)(struct seq_file*, void*), void *data) { - struct vc4_dev *vc4 = to_vc4_dev(dev); - - struct vc4_debugfs_info_entry *entry = - devm_kzalloc(dev->dev, sizeof(*entry), GFP_KERNEL); + struct drm_device *dev = minor->dev; + struct dentry *root = minor->debugfs_root; + struct drm_info_list *file; - if (!entry) + file = drmm_kzalloc(dev, sizeof(*file), GFP_KERNEL); + if (!file) return -ENOMEM; - entry->info.name = name; - entry->info.show = show; - entry->info.data = data; + file->name = name; + file->show = show; + file->data = data; - list_add(&entry->link, &vc4->debugfs_list); + drm_debugfs_create_files(file, 1, root, minor); return 0; } -int vc4_debugfs_add_regset32(struct drm_device *drm, +int vc4_debugfs_add_regset32(struct drm_minor *minor, const char *name, struct debugfs_regset32 *regset) { - return vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset); + return vc4_debugfs_add_file(minor, name, vc4_debugfs_regset32, regset); } diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 7286a3333538..1f8f44b7b5a5 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -244,6 +244,23 @@ static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = { .mode_valid = vc4_dpi_encoder_mode_valid, }; +static int vc4_dpi_late_register(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_dpi *dpi = to_vc4_dpi(encoder); + int ret; + + ret = vc4_debugfs_add_regset32(drm->primary, "dpi_regs", &dpi->regset); + if (ret) + return ret; + + return 0; +} + +static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = { + .late_register = vc4_dpi_late_register, +}; + static const struct of_device_id vc4_dpi_dt_match[] = { { .compatible = "brcm,bcm2835-dpi", .data = NULL }, {} @@ -332,7 +349,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) return ret; ret = drmm_encoder_init(drm, &dpi->encoder.base, - NULL, + &vc4_dpi_encoder_funcs, DRM_MODE_ENCODER_DPI, NULL); if (ret) @@ -346,8 +363,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, dpi); - vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset); - return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 5d857816426b..1649350b9efd 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -841,6 +841,7 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo); void vc4_bo_dec_usecnt(struct vc4_bo *bo); void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo); void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo); +int vc4_bo_debugfs_init(struct drm_minor *minor); /* vc4_crtc.c */ extern struct platform_driver vc4_crtc_driver; @@ -859,6 +860,7 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc, void vc4_crtc_reset(struct drm_crtc *crtc); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); void vc4_crtc_send_vblank(struct drm_crtc *crtc); +int vc4_crtc_late_register(struct drm_crtc *crtc); void vc4_crtc_get_margins(struct drm_crtc_state *state, unsigned int *left, unsigned int *right, unsigned int *top, unsigned int *bottom); @@ -866,15 +868,15 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state, /* vc4_debugfs.c */ void vc4_debugfs_init(struct drm_minor *minor); #ifdef CONFIG_DEBUG_FS -int vc4_debugfs_add_file(struct drm_device *drm, +int vc4_debugfs_add_file(struct drm_minor *minor, const char *filename, int (*show)(struct seq_file*, void*), void *data); -int vc4_debugfs_add_regset32(struct drm_device *drm, +int vc4_debugfs_add_regset32(struct drm_minor *minor, const char *filename, struct debugfs_regset32 *regset); #else -static inline int vc4_debugfs_add_file(struct drm_device *drm, +static inline int vc4_debugfs_add_file(struct drm_minor *minor, const char *filename, int (*show)(struct seq_file*, void*), void *data) @@ -882,7 +884,7 @@ static inline int vc4_debugfs_add_file(struct drm_device *drm, return 0; } -static inline int vc4_debugfs_add_regset32(struct drm_device *drm, +static inline int vc4_debugfs_add_regset32(struct drm_minor *minor, const char *filename, struct debugfs_regset32 *regset) { @@ -952,6 +954,7 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state) void vc4_hvs_dump_state(struct vc4_hvs *hvs); void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel); void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel); +int vc4_hvs_debugfs_init(struct drm_minor *minor); /* vc4_kms.c */ int vc4_kms_load(struct drm_device *dev); @@ -974,6 +977,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used); void vc4_v3d_bin_bo_put(struct vc4_dev *vc4); int vc4_v3d_pm_get(struct vc4_dev *vc4); void vc4_v3d_pm_put(struct vc4_dev *vc4); +int vc4_v3d_debugfs_init(struct drm_minor *minor); /* vc4_validate.c */ int diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index 52c3215fef49..878e05d79e81 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1372,6 +1372,24 @@ static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = { .mode_fixup = vc4_dsi_encoder_mode_fixup, }; +static int vc4_dsi_late_register(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_dsi *dsi = to_vc4_dsi(encoder); + int ret; + + ret = vc4_debugfs_add_regset32(drm->primary, dsi->variant->debugfs_name, + &dsi->regset); + if (ret) + return ret; + + return 0; +} + +static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { + .late_register = vc4_dsi_late_register, +}; + static const struct vc4_dsi_variant bcm2711_dsi1_variant = { .port = 1, .debugfs_name = "dsi1_regs", @@ -1721,7 +1739,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) return ret; ret = drmm_encoder_init(drm, encoder, - NULL, + &vc4_dsi_encoder_funcs, DRM_MODE_ENCODER_DSI, NULL); if (ret) @@ -1743,8 +1761,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data) */ list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain); - vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); - return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 166d22d62d99..4acc865576f0 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -1839,6 +1839,26 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { .mode_valid = vc4_hdmi_encoder_mode_valid, }; +static int vc4_hdmi_late_register(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder); + const struct vc4_hdmi_variant *variant = vc4_hdmi->variant; + int ret; + + ret = vc4_debugfs_add_file(drm->primary, variant->debugfs_name, + vc4_hdmi_debugfs_regs, + vc4_hdmi); + if (ret) + return ret; + + return 0; +} + +static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = { + .late_register = vc4_hdmi_late_register, +}; + static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask) { int i; @@ -3335,7 +3355,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) } ret = drmm_encoder_init(drm, encoder, - NULL, + &vc4_hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret) @@ -3359,10 +3379,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (ret) goto err_put_runtime_pm; - vc4_debugfs_add_file(drm, variant->debugfs_name, - vc4_hdmi_debugfs_regs, - vc4_hdmi); - pm_runtime_put_sync(dev); return 0; diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index a62f222255ce..9e823e0de197 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -733,6 +733,39 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data) return irqret; } +int vc4_hvs_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *drm = minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_hvs *hvs = vc4->hvs; + int ret; + + if (!vc4->hvs) + return -ENODEV; + + if (!vc4->is_vc5) + debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR, + minor->debugfs_root, + &vc4->load_tracker_enabled); + + ret = vc4_debugfs_add_file(minor, "hvs_dlists", + vc4_hvs_debugfs_dlist, NULL); + if (ret) + return ret; + + ret = vc4_debugfs_add_file(minor, "hvs_underrun", + vc4_hvs_debugfs_underrun, NULL); + if (ret) + return ret; + + ret = vc4_debugfs_add_regset32(minor, "hvs_regs", + &hvs->regset); + if (ret) + return ret; + + return 0; +} + static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -858,12 +891,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) if (ret) return ret; - vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset); - vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun, - NULL); - vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist, - NULL); - return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index f6f61cf5f3f7..913e26dd62d4 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -398,6 +398,7 @@ static const struct drm_crtc_funcs vc4_txp_crtc_funcs = { .atomic_destroy_state = vc4_crtc_destroy_state, .enable_vblank = vc4_txp_enable_vblank, .disable_vblank = vc4_txp_disable_vblank, + .late_register = vc4_crtc_late_register, }; static int vc4_txp_atomic_check(struct drm_crtc *crtc, @@ -541,8 +542,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, txp); - vc4_debugfs_add_regset32(drm, "txp_regs", &vc4_crtc->regset); - return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index cc714dcfe1f2..e7990bed0a96 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -401,6 +401,28 @@ static int vc4_v3d_runtime_resume(struct device *dev) } #endif +int vc4_v3d_debugfs_init(struct drm_minor *minor) +{ + struct drm_device *drm = minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_v3d *v3d = vc4->v3d; + int ret; + + if (!vc4->v3d) + return -ENODEV; + + ret = vc4_debugfs_add_file(minor, "v3d_ident", + vc4_v3d_debugfs_ident, NULL); + if (ret) + return ret; + + ret = vc4_debugfs_add_regset32(minor, "v3d_regs", &v3d->regset); + if (ret) + return ret; + + return 0; +} + static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); @@ -477,9 +499,6 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */ pm_runtime_enable(dev); - vc4_debugfs_add_file(drm, "v3d_ident", vc4_v3d_debugfs_ident, NULL); - vc4_debugfs_add_regset32(drm, "v3d_regs", &v3d->regset); - return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index d356ffa52866..4a788c1c9058 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -523,6 +523,24 @@ static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { .atomic_mode_set = vc4_vec_encoder_atomic_mode_set, }; +static int vc4_vec_late_register(struct drm_encoder *encoder) +{ + struct drm_device *drm = encoder->dev; + struct vc4_vec *vec = encoder_to_vc4_vec(encoder); + int ret; + + ret = vc4_debugfs_add_regset32(drm->primary, "vec_regs", + &vec->regset); + if (ret) + return ret; + + return 0; +} + +static const struct drm_encoder_funcs vc4_vec_encoder_funcs = { + .late_register = vc4_vec_late_register, +}; + static const struct vc4_vec_variant bcm2835_vec_variant = { .dac_config = VEC_DAC_CONFIG_DAC_CTRL(0xc) | VEC_DAC_CONFIG_DRIVER_CTRL(0xc) | @@ -588,7 +606,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) return ret; ret = drmm_encoder_init(drm, &vec->encoder.base, - NULL, + &vc4_vec_encoder_funcs, DRM_MODE_ENCODER_TVDAC, NULL); if (ret) @@ -602,8 +620,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data) dev_set_drvdata(dev, vec); - vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset); - return 0; } -- cgit v1.2.3-59-g8ed1b From 374146cad469102b683939009f18fa88f9b9b6c9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:35 +0200 Subject: drm/vc4: Switch to drmm_mutex_init mutex_init is supposed to be balanced by a call to mutex_destroy that we were never doing in the vc4 driver. Since a DRM-managed mutex_init variant has been introduced, let's just switch to it. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-66-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_bo.c | 15 +++++++++++++-- drivers/gpu/drm/vc4/vc4_drv.c | 4 +++- drivers/gpu/drm/vc4/vc4_gem.c | 10 ++++++++-- drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++++- 4 files changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 9706fec47bcd..2ccf96b764db 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -394,6 +394,7 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) { struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_bo *bo; + int ret; if (WARN_ON_ONCE(vc4->is_vc5)) return ERR_PTR(-ENODEV); @@ -404,7 +405,11 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) bo->madv = VC4_MADV_WILLNEED; refcount_set(&bo->usecnt, 0); - mutex_init(&bo->madv_lock); + + ret = drmm_mutex_init(dev, &bo->madv_lock); + if (ret) + return ERR_PTR(ret); + mutex_lock(&vc4->bo_lock); bo->label = VC4_BO_TYPE_KERNEL; vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; @@ -1005,6 +1010,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused); int vc4_bo_cache_init(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + int ret; int i; if (WARN_ON_ONCE(vc4->is_vc5)) @@ -1024,7 +1030,12 @@ int vc4_bo_cache_init(struct drm_device *dev) for (i = 0; i < VC4_BO_TYPE_COUNT; i++) vc4->bo_labels[i].name = bo_type_names[i]; - mutex_init(&vc4->bo_lock); + ret = drmm_mutex_init(dev, &vc4->bo_lock); + if (ret) { + kfree(vc4->bo_labels); + return ret; + } + INIT_LIST_HEAD(&vc4->bo_cache.time_list); INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work); diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 79a37d18aeb1..89975bdd607e 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -324,7 +324,9 @@ static int vc4_drm_bind(struct device *dev) INIT_LIST_HEAD(&vc4->debugfs_list); if (!is_vc5) { - mutex_init(&vc4->bin_bo_lock); + ret = drmm_mutex_init(drm, &vc4->bin_bo_lock); + if (ret) + return ret; ret = vc4_bo_cache_init(drm); if (ret) diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index fe10d9c3fff8..7acb43972e69 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -1308,6 +1308,7 @@ static void vc4_gem_destroy(struct drm_device *dev, void *unused); int vc4_gem_init(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); + int ret; if (WARN_ON_ONCE(vc4->is_vc5)) return -ENODEV; @@ -1325,10 +1326,15 @@ int vc4_gem_init(struct drm_device *dev) INIT_WORK(&vc4->job_done_work, vc4_job_done_work); - mutex_init(&vc4->power_lock); + ret = drmm_mutex_init(dev, &vc4->power_lock); + if (ret) + return ret; INIT_LIST_HEAD(&vc4->purgeable.list); - mutex_init(&vc4->purgeable.lock); + + ret = drmm_mutex_init(dev, &vc4->purgeable.lock); + if (ret) + return ret; return drmm_add_action_or_reset(dev, vc4_gem_destroy, NULL); } diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 4acc865576f0..6f61a1b8a1a3 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -3270,7 +3270,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) if (!vc4_hdmi) return -ENOMEM; - mutex_init(&vc4_hdmi->mutex); + ret = drmm_mutex_init(drm, &vc4_hdmi->mutex); + if (ret) + return ret; + spin_lock_init(&vc4_hdmi->hw_lock); INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq); -- cgit v1.2.3-59-g8ed1b From 57d68ee8f719efa05bcec64625a9898231ed43c5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:36 +0200 Subject: drm/vc4: perfmon: Add missing mutex_destroy vc4_perfmon_open_file() will instantiate a mutex for that file instance, but we never call mutex_destroy () in vc4_perfmon_close_file(). Let's add that missing call. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-67-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_perfmon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_perfmon.c b/drivers/gpu/drm/vc4/vc4_perfmon.c index 79a74184d732..c4ac2c946238 100644 --- a/drivers/gpu/drm/vc4/vc4_perfmon.c +++ b/drivers/gpu/drm/vc4/vc4_perfmon.c @@ -133,6 +133,7 @@ void vc4_perfmon_close_file(struct vc4_file *vc4file) idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL); idr_destroy(&vc4file->perfmon.idr); mutex_unlock(&vc4file->perfmon.lock); + mutex_destroy(&vc4file->perfmon.lock); } int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data, -- cgit v1.2.3-59-g8ed1b From 9b6f461582e6b07e2ce93227a3e4661846e594ad Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:37 +0200 Subject: drm/vc4: v3d: Stop disabling interrupts The vc4_irq_disable(), among other things, will call disable_irq() to complete any in-flight interrupts. This requires its counterpart, vc4_irq_enable(), to call enable_irq() which causes issues addressed in a later patch. However, vc4_irq_disable() is called by two callees: vc4_irq_uninstall() and vc4_v3d_runtime_suspend(). vc4_irq_uninstall() also calls free_irq() which already disables the interrupt line. We thus don't require an explicit disable_irq() for that call site. vc4_v3d_runtime_suspend() doesn't have any other code. However, the rest of vc4_irq_disable() masks the interrupts coming from the v3d, so explictly disabling the interrupt line is also redundant. The only thing we really care about is thus to make sure we don't have any handler in-flight, as suggested by the comment. We can thus replace disable_irq() by synchronize_irq(). Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-68-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_irq.c | 2 +- drivers/gpu/drm/vc4/vc4_v3d.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 2eacfb6773d2..a9fc63d9a7f0 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -295,7 +295,7 @@ vc4_irq_disable(struct drm_device *dev) V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); /* Finish any interrupt handler still in flight. */ - disable_irq(vc4->irq); + synchronize_irq(vc4->irq); cancel_work_sync(&vc4->overflow_mem_work); } diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index e7990bed0a96..a2da0db73a5c 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -393,8 +393,6 @@ static int vc4_v3d_runtime_resume(struct device *dev) vc4_v3d_init_hw(&vc4->base); - /* We disabled the IRQ as part of vc4_irq_uninstall in suspend. */ - enable_irq(vc4->irq); vc4_irq_enable(&vc4->base); return 0; -- cgit v1.2.3-59-g8ed1b From 266cff37d7fcec0443e720878242cb52d728138b Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:38 +0200 Subject: drm/vc4: v3d: Rework the runtime_pm setup At bind time, vc4_v3d_bind() will read a register to retrieve the v3d version and make sure it's a version we're compatible with. However, the v3d has an optional clock that is enabled only after the register read-out and a power domain that wasn't enabled at all in the bind implementation. This was working fine at boot because both were enabled, but resulted in the version check failing if we were unbinding and rebinding the driver because the unbinding would have turned them off. The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the register access to power up the power domain though. Indeed, the runtime_resume implementation will enable the clock mentioned above, call vc4_v3d_init_hw() and then vc4_irq_enable(). Prior to the previous patch, vc4_irq_enable() needed to occur after our call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable() used to call enable_irq() and vc4_irq_install() will call request_irq(). vc4_irq_install() will also do some register access, so needs the power domain to be on. So we ended up in a situation where vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called before, and vc4_irq_install() needed vc4_v3d_runtime_resume(). The previous patch removed the enable_irq() call in vc4_irq_enable() and thus removed the dependency of vc4_v3d_runtime_resume() on vc4_irq_install(). Thus, we can now rework our bind implementation to call pm_runtime_resume_and_get() before our register access to make sure the power domain is on. vc4_v3d_runtime_resume() also takes care of turning the clock on and calling vc4_v3d_init_hw() so we can remove them from bind. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-69-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_v3d.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index a2da0db73a5c..d82c86865079 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -463,41 +463,48 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) } } + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + vc4->irq = ret; + + pm_runtime_enable(dev); + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err_disable_runtime_pm; + if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0); - return -EINVAL; + ret = -EINVAL; + goto err_put_runtime_pm; } - ret = clk_prepare_enable(v3d->clk); - if (ret != 0) - return ret; - /* Reset the binner overflow address/size at setup, to be sure * we don't reuse an old one. */ V3D_WRITE(V3D_BPOA, 0); V3D_WRITE(V3D_BPOS, 0); - vc4_v3d_init_hw(drm); - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - vc4->irq = ret; - ret = vc4_irq_install(drm, vc4->irq); if (ret) { DRM_ERROR("Failed to install IRQ handler\n"); - return ret; + goto err_put_runtime_pm; } - pm_runtime_set_active(dev); pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */ - pm_runtime_enable(dev); return 0; + +err_put_runtime_pm: + pm_runtime_put(dev); + +err_disable_runtime_pm: + pm_runtime_disable(dev); + + return ret; } static void vc4_v3d_unbind(struct device *dev, struct device *master, -- cgit v1.2.3-59-g8ed1b From 887ddf3251928dc39bfc58c5c62083d38a633c14 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:39:39 +0200 Subject: drm/vc4: v3d: Switch to devm_pm_runtime_enable devm_pm_runtime_enable() simplifies the driver a bit since it will call pm_runtime_disable() automatically through a device-managed action. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-70-maxime@cerno.tech --- drivers/gpu/drm/vc4/vc4_v3d.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index d82c86865079..40f04157ea39 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -468,11 +468,13 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) return ret; vc4->irq = ret; - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_disable_runtime_pm; + return ret; if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) { DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n", @@ -501,9 +503,6 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) err_put_runtime_pm: pm_runtime_put(dev); -err_disable_runtime_pm: - pm_runtime_disable(dev); - return ret; } @@ -513,8 +512,6 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master, struct drm_device *drm = dev_get_drvdata(master); struct vc4_dev *vc4 = to_vc4_dev(drm); - pm_runtime_disable(dev); - vc4_irq_uninstall(drm); /* Disable the binner's overflow memory address, so the next -- cgit v1.2.3-59-g8ed1b From 94dc3471d1b2b58b3728558d0e3f264e9ce6ff59 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 5 Jul 2022 12:02:13 +0200 Subject: drm: Use size_t type for len variable in drm_copy_field() The strlen() function returns a size_t which is an unsigned int on 32-bit arches and an unsigned long on 64-bit arches. But in the drm_copy_field() function, the strlen() return value is assigned to an 'int len' variable. Later, the len variable is passed as copy_from_user() third argument that is an unsigned long parameter as well. In theory, this can lead to an integer overflow via type conversion. Since the assignment happens to a signed int lvalue instead of a size_t lvalue. In practice though, that's unlikely since the values copied are set by DRM drivers and not controlled by userspace. But using a size_t for len is the correct thing to do anyways. Signed-off-by: Javier Martinez Canillas Tested-by: Peter Robinson Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220705100215.572498-2-javierm@redhat.com --- drivers/gpu/drm/drm_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 8faad23dc1d8..e1b9a03e619c 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -472,7 +472,7 @@ EXPORT_SYMBOL(drm_invalid_op); */ static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value) { - int len; + size_t len; /* don't overflow userbuf */ len = strlen(value); -- cgit v1.2.3-59-g8ed1b From f6ee30407e883042482ad4ad30da5eaba47872ee Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 5 Jul 2022 12:02:14 +0200 Subject: drm: Prevent drm_copy_field() to attempt copying a NULL pointer There are some struct drm_driver fields that are required by drivers since drm_copy_field() attempts to copy them to user-space via DRM_IOCTL_VERSION. But it can be possible that a driver has a bug and did not set some of the fields, which leads to drm_copy_field() attempting to copy a NULL pointer: [ +10.395966] Unable to handle kernel access to user memory outside uaccess routines at virtual address 0000000000000000 [ +0.010955] Mem abort info: [ +0.002835] ESR = 0x0000000096000004 [ +0.003872] EC = 0x25: DABT (current EL), IL = 32 bits [ +0.005395] SET = 0, FnV = 0 [ +0.003113] EA = 0, S1PTW = 0 [ +0.003182] FSC = 0x04: level 0 translation fault [ +0.004964] Data abort info: [ +0.002919] ISV = 0, ISS = 0x00000004 [ +0.003886] CM = 0, WnR = 0 [ +0.003040] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000115dad000 [ +0.006536] [0000000000000000] pgd=0000000000000000, p4d=0000000000000000 [ +0.006925] Internal error: Oops: 96000004 [#1] SMP ... [ +0.011113] pstate: 80400005 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ +0.007061] pc : __pi_strlen+0x14/0x150 [ +0.003895] lr : drm_copy_field+0x30/0x1a4 [ +0.004156] sp : ffff8000094b3a50 [ +0.003355] x29: ffff8000094b3a50 x28: ffff8000094b3b70 x27: 0000000000000040 [ +0.007242] x26: ffff443743c2ba00 x25: 0000000000000000 x24: 0000000000000040 [ +0.007243] x23: ffff443743c2ba00 x22: ffff8000094b3b70 x21: 0000000000000000 [ +0.007241] x20: 0000000000000000 x19: ffff8000094b3b90 x18: 0000000000000000 [ +0.007241] x17: 0000000000000000 x16: 0000000000000000 x15: 0000aaab14b9af40 [ +0.007241] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ +0.007239] x11: 0000000000000000 x10: 0000000000000000 x9 : ffffa524ad67d4d8 [ +0.007242] x8 : 0101010101010101 x7 : 7f7f7f7f7f7f7f7f x6 : 6c6e6263606e7141 [ +0.007239] x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000 [ +0.007241] x2 : 0000000000000000 x1 : ffff8000094b3b90 x0 : 0000000000000000 [ +0.007240] Call trace: [ +0.002475] __pi_strlen+0x14/0x150 [ +0.003537] drm_version+0x84/0xac [ +0.003448] drm_ioctl_kernel+0xa8/0x16c [ +0.003975] drm_ioctl+0x270/0x580 [ +0.003448] __arm64_sys_ioctl+0xb8/0xfc [ +0.003978] invoke_syscall+0x78/0x100 [ +0.003799] el0_svc_common.constprop.0+0x4c/0xf4 [ +0.004767] do_el0_svc+0x38/0x4c [ +0.003357] el0_svc+0x34/0x100 [ +0.003185] el0t_64_sync_handler+0x11c/0x150 [ +0.004418] el0t_64_sync+0x190/0x194 [ +0.003716] Code: 92402c04 b200c3e8 f13fc09f 5400088c (a9400c02) [ +0.006180] ---[ end trace 0000000000000000 ]--- Reported-by: Peter Robinson Signed-off-by: Javier Martinez Canillas Acked-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220705100215.572498-3-javierm@redhat.com --- drivers/gpu/drm/drm_ioctl.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index e1b9a03e619c..ca2a6e6101dc 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -474,6 +474,12 @@ static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value) { size_t len; + /* don't attempt to copy a NULL pointer */ + if (WARN_ONCE(!value, "BUG: the value to copy was not set!")) { + *buf_len = 0; + return 0; + } + /* don't overflow userbuf */ len = strlen(value); if (len > *buf_len) -- cgit v1.2.3-59-g8ed1b From 55b3d6a63fb58e154f0a1ab3930cdd003a39ba9b Mon Sep 17 00:00:00 2001 From: Christian König Date: Thu, 14 Jul 2022 14:44:17 +0200 Subject: drm/amdgpu: reapply "fix start calculation in amdgpu_vram_mgr_new"" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This re-applys commit 5e3f1e7729ec7a99e145e9d8ed58963d86cdfb98. The original problem this was reverted for was found and the correct fix will be merged to drm-misc-next-fixes. Signed-off-by: Christian König Reviewed-by: Arunpravin Paneer Selvam Link: https://patchwork.freedesktop.org/patch/msgid/20220714132315.587217-1-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 49e4092f447f..51d9d3a4456c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -496,16 +496,22 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, list_splice_tail(trim_list, &vres->blocks); } - list_for_each_entry(block, &vres->blocks, link) - vis_usage += amdgpu_vram_mgr_vis_size(adev, block); + vres->base.start = 0; + list_for_each_entry(block, &vres->blocks, link) { + unsigned long start; - block = amdgpu_vram_mgr_first_block(&vres->blocks); - if (!block) { - r = -EINVAL; - goto error_fini; - } + start = amdgpu_vram_mgr_block_start(block) + + amdgpu_vram_mgr_block_size(block); + start >>= PAGE_SHIFT; - vres->base.start = amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT; + if (start > vres->base.num_pages) + start -= vres->base.num_pages; + else + start = 0; + vres->base.start = max(vres->base.start, start); + + vis_usage += amdgpu_vram_mgr_vis_size(adev, block); + } if (amdgpu_is_vram_mgr_blocks_contiguous(&vres->blocks)) vres->base.placement |= TTM_PL_FLAG_CONTIGUOUS; -- cgit v1.2.3-59-g8ed1b From 59dad4a0d1862dcd15405f3217a59b1fac01daf0 Mon Sep 17 00:00:00 2001 From: Christian König Date: Thu, 14 Jul 2022 14:48:20 +0200 Subject: drm/amdgpu: re-apply "move internal vram_mgr function into the C file"" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This re-applys commit 708d19d9f362766147cab79eccae60912c6d3068. The original problem this was reverted for was found and the correct fix will be merged to drm-misc-next-fixes. Signed-off-by: Christian König Reviewed-by: Arunpravin Paneer Selvam Link: https://patchwork.freedesktop.org/patch/msgid/20220714132315.587217-2-christian.koenig@amd.com --- drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c | 29 ++++++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h | 27 -------------------------- 2 files changed, 29 insertions(+), 27 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index 51d9d3a4456c..7a5e8a7b4a1b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -50,6 +50,35 @@ to_amdgpu_device(struct amdgpu_vram_mgr *mgr) return container_of(mgr, struct amdgpu_device, mman.vram_mgr); } +static inline struct drm_buddy_block * +amdgpu_vram_mgr_first_block(struct list_head *list) +{ + return list_first_entry_or_null(list, struct drm_buddy_block, link); +} + +static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) +{ + struct drm_buddy_block *block; + u64 start, size; + + block = amdgpu_vram_mgr_first_block(head); + if (!block) + return false; + + while (head != block->link.next) { + start = amdgpu_vram_mgr_block_start(block); + size = amdgpu_vram_mgr_block_size(block); + + block = list_entry(block->link.next, struct drm_buddy_block, link); + if (start + size != amdgpu_vram_mgr_block_start(block)) + return false; + } + + return true; +} + + + /** * DOC: mem_info_vram_total * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h index 9a2db87186c7..4b267bf1c5db 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.h @@ -53,33 +53,6 @@ static inline u64 amdgpu_vram_mgr_block_size(struct drm_buddy_block *block) return PAGE_SIZE << drm_buddy_block_order(block); } -static inline struct drm_buddy_block * -amdgpu_vram_mgr_first_block(struct list_head *list) -{ - return list_first_entry_or_null(list, struct drm_buddy_block, link); -} - -static inline bool amdgpu_is_vram_mgr_blocks_contiguous(struct list_head *head) -{ - struct drm_buddy_block *block; - u64 start, size; - - block = amdgpu_vram_mgr_first_block(head); - if (!block) - return false; - - while (head != block->link.next) { - start = amdgpu_vram_mgr_block_start(block); - size = amdgpu_vram_mgr_block_size(block); - - block = list_entry(block->link.next, struct drm_buddy_block, link); - if (start + size != amdgpu_vram_mgr_block_start(block)) - return false; - } - - return true; -} - static inline struct amdgpu_vram_mgr_resource * to_amdgpu_vram_mgr_resource(struct ttm_resource *res) { -- cgit v1.2.3-59-g8ed1b From 1dab4561a341afdbaafe0ce6091106d0c63c79e0 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 29 Jun 2022 21:39:59 -0700 Subject: drm/i915/reset: Handle reset timeouts under unrelated kernel hangs When resuming after hibernate sometimes we see hangs in unrelated kernel subsystems. These hangs often result in the following i915 trace: i915 0000:00:02.0: [drm] *ERROR* \ intel_gt_reset_global timed out, cancelling all in-flight rendering implying our reset task has been starved by the hanging kernel subsystem, causing us to inappropiately declare the system as wedged beyond recovery. The trace would be caused by our synchronize_srcu_expedited() taking more than the allowed 5s due to the unrelated kernel hang. But we neither need to perform that synchronisation inside the reset watchdog, nor do we need such a short timeout before declaring the device as unrecoverable. v2: Restore watchdog timeout to the previous 5 seconds (Ashutosh) Bug: https://gitlab.freedesktop.org/drm/intel/-/issues/3575 Signed-off-by: Chris Wilson Signed-off-by: Ashutosh Dixit Reviewed-by: Ashutosh Dixit Signed-off-by: Matthew Auld Link: https://patchwork.freedesktop.org/patch/msgid/20220630043959.5708-1-ashutosh.dixit@intel.com --- drivers/gpu/drm/i915/gt/intel_reset.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index c68d36fb5bbd..1211774e1d91 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -1281,9 +1281,6 @@ static void intel_gt_reset_global(struct intel_gt *gt, intel_wedge_on_timeout(&w, gt, 5 * HZ) { intel_display_prepare_reset(gt->i915); - /* Flush everyone using a resource about to be clobbered */ - synchronize_srcu_expedited(>->reset.backoff_srcu); - intel_gt_reset(gt, engine_mask, reason); intel_display_finish_reset(gt->i915); @@ -1392,6 +1389,9 @@ void intel_gt_handle_error(struct intel_gt *gt, } } + /* Flush everyone using a resource about to be clobbered */ + synchronize_srcu_expedited(>->reset.backoff_srcu); + intel_gt_reset_global(gt, engine_mask, msg); if (!intel_uc_uses_guc_submission(>->uc)) { -- cgit v1.2.3-59-g8ed1b From bd8408e69a47f8e9f5d95c5d919413fafecc1e73 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sat, 2 Jul 2022 11:39:04 -0400 Subject: drm/nouveau/bios: set info only when the return is not 0 clang static analysis reports drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c:68:17: warning: The right operand of '*' is a garbage value [core.UndefinedBinaryOperatorResult] switch (!!data * *ver) { ^ ~~~~ A switch statement with only a default should be reduced to an if. If nvbios_pmuEp() returns 0, via the data variable, the output info parameter is not used. So set info only when data is not 0. The struct nvbios_pmuE only has the type and data elements. Since both of these are explicitly set, memset is not needed. So remove it. Signed-off-by: Tom Rix Reviewed-by: Lyude Paul Signed-off-by: Lyude Paul Link: https://patchwork.freedesktop.org/patch/msgid/20220702153904.1696595-1-trix@redhat.com --- drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c index b4a308f3cf7b..49e2664a734c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/pmu.c @@ -64,12 +64,9 @@ nvbios_pmuEp(struct nvkm_bios *bios, int idx, u8 *ver, u8 *hdr, struct nvbios_pmuE *info) { u32 data = nvbios_pmuEe(bios, idx, ver, hdr); - memset(info, 0x00, sizeof(*info)); - switch (!!data * *ver) { - default: + if (data) { info->type = nvbios_rd08(bios, data + 0x00); info->data = nvbios_rd32(bios, data + 0x02); - break; } return data; } -- cgit v1.2.3-59-g8ed1b From e6d9767faf378e543b064ff204d77753451930df Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:50 +0200 Subject: drm/via: Rename via_drv to via_dri1 The via driver implements the DRI1 interface, and we have a new implementation of the via driver coming that supports atomic modesetting. It is not acceptable just to replace the existing driver so this is first step to make it a single-file implementation allowing it to stay without interfering with the new driver. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-2-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dri1.c | 124 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/via/via_drv.c | 124 ----------------------------------------- 3 files changed, 125 insertions(+), 125 deletions(-) create mode 100644 drivers/gpu/drm/via/via_dri1.c delete mode 100644 drivers/gpu/drm/via/via_drv.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index 84db4eee7828..acfd916541ea 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o +via-y := via_irq.o via_dri1.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c new file mode 100644 index 000000000000..5da38082821f --- /dev/null +++ b/drivers/gpu/drm/via/via_dri1.c @@ -0,0 +1,124 @@ +/* + * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include + +#include "via_drv.h" + + +static int via_driver_open(struct drm_device *dev, struct drm_file *file) +{ + struct via_file_private *file_priv; + + DRM_DEBUG_DRIVER("\n"); + file_priv = kmalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) + return -ENOMEM; + + file->driver_priv = file_priv; + + INIT_LIST_HEAD(&file_priv->obj_list); + + return 0; +} + +static void via_driver_postclose(struct drm_device *dev, struct drm_file *file) +{ + struct via_file_private *file_priv = file->driver_priv; + + kfree(file_priv); +} + +static struct pci_device_id pciidlist[] = { + viadrv_PCI_IDS +}; + +static const struct file_operations via_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = drm_legacy_mmap, + .poll = drm_poll, + .compat_ioctl = drm_compat_ioctl, + .llseek = noop_llseek, +}; + +static struct drm_driver driver = { + .driver_features = + DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_LEGACY, + .load = via_driver_load, + .unload = via_driver_unload, + .open = via_driver_open, + .preclose = via_reclaim_buffers_locked, + .postclose = via_driver_postclose, + .context_dtor = via_final_context, + .get_vblank_counter = via_get_vblank_counter, + .enable_vblank = via_enable_vblank, + .disable_vblank = via_disable_vblank, + .irq_preinstall = via_driver_irq_preinstall, + .irq_postinstall = via_driver_irq_postinstall, + .irq_uninstall = via_driver_irq_uninstall, + .irq_handler = via_driver_irq_handler, + .dma_quiescent = via_driver_dma_quiescent, + .lastclose = via_lastclose, + .ioctls = via_ioctls, + .fops = &via_driver_fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, +}; + +static struct pci_driver via_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, +}; + +static int __init via_init(void) +{ + driver.num_ioctls = via_max_ioctl; + via_init_command_verifier(); + return drm_legacy_pci_init(&driver, &via_pci_driver); +} + +static void __exit via_exit(void) +{ + drm_legacy_pci_exit(&driver, &via_pci_driver); +} + +module_init(via_init); +module_exit(via_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c deleted file mode 100644 index 5da38082821f..000000000000 --- a/drivers/gpu/drm/via/via_drv.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include -#include -#include -#include - -#include "via_drv.h" - - -static int via_driver_open(struct drm_device *dev, struct drm_file *file) -{ - struct via_file_private *file_priv; - - DRM_DEBUG_DRIVER("\n"); - file_priv = kmalloc(sizeof(*file_priv), GFP_KERNEL); - if (!file_priv) - return -ENOMEM; - - file->driver_priv = file_priv; - - INIT_LIST_HEAD(&file_priv->obj_list); - - return 0; -} - -static void via_driver_postclose(struct drm_device *dev, struct drm_file *file) -{ - struct via_file_private *file_priv = file->driver_priv; - - kfree(file_priv); -} - -static struct pci_device_id pciidlist[] = { - viadrv_PCI_IDS -}; - -static const struct file_operations via_driver_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, - .mmap = drm_legacy_mmap, - .poll = drm_poll, - .compat_ioctl = drm_compat_ioctl, - .llseek = noop_llseek, -}; - -static struct drm_driver driver = { - .driver_features = - DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_LEGACY, - .load = via_driver_load, - .unload = via_driver_unload, - .open = via_driver_open, - .preclose = via_reclaim_buffers_locked, - .postclose = via_driver_postclose, - .context_dtor = via_final_context, - .get_vblank_counter = via_get_vblank_counter, - .enable_vblank = via_enable_vblank, - .disable_vblank = via_disable_vblank, - .irq_preinstall = via_driver_irq_preinstall, - .irq_postinstall = via_driver_irq_postinstall, - .irq_uninstall = via_driver_irq_uninstall, - .irq_handler = via_driver_irq_handler, - .dma_quiescent = via_driver_dma_quiescent, - .lastclose = via_lastclose, - .ioctls = via_ioctls, - .fops = &via_driver_fops, - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCHLEVEL, -}; - -static struct pci_driver via_pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, -}; - -static int __init via_init(void) -{ - driver.num_ioctls = via_max_ioctl; - via_init_command_verifier(); - return drm_legacy_pci_init(&driver, &via_pci_driver); -} - -static void __exit via_exit(void) -{ - drm_legacy_pci_exit(&driver, &via_pci_driver); -} - -module_init(via_init); -module_exit(via_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL and additional rights"); -- cgit v1.2.3-59-g8ed1b From 9a4537a7824f6011f628bffdfb57ec0bdc83da39 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:51 +0200 Subject: drm/via: Embed via_dma in via_dri1 Moved the copyright notices so all copyrights are kept. A few variables was made static as there are no more users outside this file. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-3-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dma.c | 744 ----------------------------------------- drivers/gpu/drm/via/via_dri1.c | 703 +++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/via/via_drv.h | 3 - 4 files changed, 703 insertions(+), 749 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_dma.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index acfd916541ea..824198c0c2ea 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_irq.o via_dri1.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o +via-y := via_irq.o via_dri1.o via_map.o via_mm.o via_verifier.o via_video.o via_dmablit.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dma.c b/drivers/gpu/drm/via/via_dma.c deleted file mode 100644 index 177b0499abf1..000000000000 --- a/drivers/gpu/drm/via/via_dma.c +++ /dev/null @@ -1,744 +0,0 @@ -/* via_dma.c -- DMA support for the VIA Unichrome/Pro - * - * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. - * All Rights Reserved. - * - * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. - * All Rights Reserved. - * - * Copyright 2004 The Unichrome project. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Tungsten Graphics, - * Erdi Chen, - * Thomas Hellstrom. - */ - -#include -#include - -#include -#include -#include -#include - -#include "via_drv.h" -#include "via_3d_reg.h" - -#define CMDBUF_ALIGNMENT_SIZE (0x100) -#define CMDBUF_ALIGNMENT_MASK (0x0ff) - -/* defines for VIA 3D registers */ -#define VIA_REG_STATUS 0x400 -#define VIA_REG_TRANSET 0x43C -#define VIA_REG_TRANSPACE 0x440 - -/* VIA_REG_STATUS(0x400): Engine Status */ -#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */ -#define VIA_2D_ENG_BUSY 0x00000001 /* 2D Engine is busy */ -#define VIA_3D_ENG_BUSY 0x00000002 /* 3D Engine is busy */ -#define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */ - -#define SetReg2DAGP(nReg, nData) { \ - *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ - *((uint32_t *)(vb) + 1) = (nData); \ - vb = ((uint32_t *)vb) + 2; \ - dev_priv->dma_low += 8; \ -} - -#define via_flush_write_combine() mb() - -#define VIA_OUT_RING_QW(w1, w2) do { \ - *vb++ = (w1); \ - *vb++ = (w2); \ - dev_priv->dma_low += 8; \ -} while (0) - -static void via_cmdbuf_start(drm_via_private_t *dev_priv); -static void via_cmdbuf_pause(drm_via_private_t *dev_priv); -static void via_cmdbuf_reset(drm_via_private_t *dev_priv); -static void via_cmdbuf_rewind(drm_via_private_t *dev_priv); -static int via_wait_idle(drm_via_private_t *dev_priv); -static void via_pad_cache(drm_via_private_t *dev_priv, int qwords); - -/* - * Free space in command buffer. - */ - -static uint32_t via_cmdbuf_space(drm_via_private_t *dev_priv) -{ - uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; - uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; - - return ((hw_addr <= dev_priv->dma_low) ? - (dev_priv->dma_high + hw_addr - dev_priv->dma_low) : - (hw_addr - dev_priv->dma_low)); -} - -/* - * How much does the command regulator lag behind? - */ - -static uint32_t via_cmdbuf_lag(drm_via_private_t *dev_priv) -{ - uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; - uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; - - return ((hw_addr <= dev_priv->dma_low) ? - (dev_priv->dma_low - hw_addr) : - (dev_priv->dma_wrap + dev_priv->dma_low - hw_addr)); -} - -/* - * Check that the given size fits in the buffer, otherwise wait. - */ - -static inline int -via_cmdbuf_wait(drm_via_private_t *dev_priv, unsigned int size) -{ - uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; - uint32_t cur_addr, hw_addr, next_addr; - volatile uint32_t *hw_addr_ptr; - uint32_t count; - hw_addr_ptr = dev_priv->hw_addr_ptr; - cur_addr = dev_priv->dma_low; - next_addr = cur_addr + size + 512 * 1024; - count = 1000000; - do { - hw_addr = *hw_addr_ptr - agp_base; - if (count-- == 0) { - DRM_ERROR - ("via_cmdbuf_wait timed out hw %x cur_addr %x next_addr %x\n", - hw_addr, cur_addr, next_addr); - return -1; - } - if ((cur_addr < hw_addr) && (next_addr >= hw_addr)) - msleep(1); - } while ((cur_addr < hw_addr) && (next_addr >= hw_addr)); - return 0; -} - -/* - * Checks whether buffer head has reach the end. Rewind the ring buffer - * when necessary. - * - * Returns virtual pointer to ring buffer. - */ - -static inline uint32_t *via_check_dma(drm_via_private_t * dev_priv, - unsigned int size) -{ - if ((dev_priv->dma_low + size + 4 * CMDBUF_ALIGNMENT_SIZE) > - dev_priv->dma_high) { - via_cmdbuf_rewind(dev_priv); - } - if (via_cmdbuf_wait(dev_priv, size) != 0) - return NULL; - - return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low); -} - -int via_dma_cleanup(struct drm_device *dev) -{ - if (dev->dev_private) { - drm_via_private_t *dev_priv = - (drm_via_private_t *) dev->dev_private; - - if (dev_priv->ring.virtual_start) { - via_cmdbuf_reset(dev_priv); - - drm_legacy_ioremapfree(&dev_priv->ring.map, dev); - dev_priv->ring.virtual_start = NULL; - } - - } - - return 0; -} - -static int via_initialize(struct drm_device *dev, - drm_via_private_t *dev_priv, - drm_via_dma_init_t *init) -{ - if (!dev_priv || !dev_priv->mmio) { - DRM_ERROR("via_dma_init called before via_map_init\n"); - return -EFAULT; - } - - if (dev_priv->ring.virtual_start != NULL) { - DRM_ERROR("called again without calling cleanup\n"); - return -EFAULT; - } - - if (!dev->agp || !dev->agp->base) { - DRM_ERROR("called with no agp memory available\n"); - return -EFAULT; - } - - if (dev_priv->chipset == VIA_DX9_0) { - DRM_ERROR("AGP DMA is not supported on this chip\n"); - return -EINVAL; - } - - dev_priv->ring.map.offset = dev->agp->base + init->offset; - dev_priv->ring.map.size = init->size; - dev_priv->ring.map.type = 0; - dev_priv->ring.map.flags = 0; - dev_priv->ring.map.mtrr = 0; - - drm_legacy_ioremap(&dev_priv->ring.map, dev); - - if (dev_priv->ring.map.handle == NULL) { - via_dma_cleanup(dev); - DRM_ERROR("can not ioremap virtual address for" - " ring buffer\n"); - return -ENOMEM; - } - - dev_priv->ring.virtual_start = dev_priv->ring.map.handle; - - dev_priv->dma_ptr = dev_priv->ring.virtual_start; - dev_priv->dma_low = 0; - dev_priv->dma_high = init->size; - dev_priv->dma_wrap = init->size; - dev_priv->dma_offset = init->offset; - dev_priv->last_pause_ptr = NULL; - dev_priv->hw_addr_ptr = - (volatile uint32_t *)((char *)dev_priv->mmio->handle + - init->reg_pause_addr); - - via_cmdbuf_start(dev_priv); - - return 0; -} - -static int via_dma_init(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_dma_init_t *init = data; - int retcode = 0; - - switch (init->func) { - case VIA_INIT_DMA: - if (!capable(CAP_SYS_ADMIN)) - retcode = -EPERM; - else - retcode = via_initialize(dev, dev_priv, init); - break; - case VIA_CLEANUP_DMA: - if (!capable(CAP_SYS_ADMIN)) - retcode = -EPERM; - else - retcode = via_dma_cleanup(dev); - break; - case VIA_DMA_INITIALIZED: - retcode = (dev_priv->ring.virtual_start != NULL) ? - 0 : -EFAULT; - break; - default: - retcode = -EINVAL; - break; - } - - return retcode; -} - -static int via_dispatch_cmdbuffer(struct drm_device *dev, drm_via_cmdbuffer_t *cmd) -{ - drm_via_private_t *dev_priv; - uint32_t *vb; - int ret; - - dev_priv = (drm_via_private_t *) dev->dev_private; - - if (dev_priv->ring.virtual_start == NULL) { - DRM_ERROR("called without initializing AGP ring buffer.\n"); - return -EFAULT; - } - - if (cmd->size > VIA_PCI_BUF_SIZE) - return -ENOMEM; - - if (copy_from_user(dev_priv->pci_buf, cmd->buf, cmd->size)) - return -EFAULT; - - /* - * Running this function on AGP memory is dead slow. Therefore - * we run it on a temporary cacheable system memory buffer and - * copy it to AGP memory when ready. - */ - - if ((ret = - via_verify_command_stream((uint32_t *) dev_priv->pci_buf, - cmd->size, dev, 1))) { - return ret; - } - - vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size); - if (vb == NULL) - return -EAGAIN; - - memcpy(vb, dev_priv->pci_buf, cmd->size); - - dev_priv->dma_low += cmd->size; - - /* - * Small submissions somehow stalls the CPU. (AGP cache effects?) - * pad to greater size. - */ - - if (cmd->size < 0x100) - via_pad_cache(dev_priv, (0x100 - cmd->size) >> 3); - via_cmdbuf_pause(dev_priv); - - return 0; -} - -int via_driver_dma_quiescent(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = dev->dev_private; - - if (!via_wait_idle(dev_priv)) - return -EBUSY; - return 0; -} - -static int via_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - return via_driver_dma_quiescent(dev); -} - -static int via_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_cmdbuffer_t *cmdbuf = data; - int ret; - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size); - - ret = via_dispatch_cmdbuffer(dev, cmdbuf); - return ret; -} - -static int via_dispatch_pci_cmdbuffer(struct drm_device *dev, - drm_via_cmdbuffer_t *cmd) -{ - drm_via_private_t *dev_priv = dev->dev_private; - int ret; - - if (cmd->size > VIA_PCI_BUF_SIZE) - return -ENOMEM; - if (copy_from_user(dev_priv->pci_buf, cmd->buf, cmd->size)) - return -EFAULT; - - if ((ret = - via_verify_command_stream((uint32_t *) dev_priv->pci_buf, - cmd->size, dev, 0))) { - return ret; - } - - ret = - via_parse_command_stream(dev, (const uint32_t *)dev_priv->pci_buf, - cmd->size); - return ret; -} - -static int via_pci_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_cmdbuffer_t *cmdbuf = data; - int ret; - - LOCK_TEST_WITH_RETURN(dev, file_priv); - - DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size); - - ret = via_dispatch_pci_cmdbuffer(dev, cmdbuf); - return ret; -} - -static inline uint32_t *via_align_buffer(drm_via_private_t *dev_priv, - uint32_t * vb, int qw_count) -{ - for (; qw_count > 0; --qw_count) - VIA_OUT_RING_QW(HC_DUMMY, HC_DUMMY); - return vb; -} - -/* - * This function is used internally by ring buffer management code. - * - * Returns virtual pointer to ring buffer. - */ -static inline uint32_t *via_get_dma(drm_via_private_t *dev_priv) -{ - return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low); -} - -/* - * Hooks a segment of data into the tail of the ring-buffer by - * modifying the pause address stored in the buffer itself. If - * the regulator has already paused, restart it. - */ -static int via_hook_segment(drm_via_private_t *dev_priv, - uint32_t pause_addr_hi, uint32_t pause_addr_lo, - int no_pci_fire) -{ - int paused, count; - volatile uint32_t *paused_at = dev_priv->last_pause_ptr; - uint32_t reader, ptr; - uint32_t diff; - - paused = 0; - via_flush_write_combine(); - (void) *(volatile uint32_t *)(via_get_dma(dev_priv) - 1); - - *paused_at = pause_addr_lo; - via_flush_write_combine(); - (void) *paused_at; - - reader = *(dev_priv->hw_addr_ptr); - ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) + - dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; - - dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; - - /* - * If there is a possibility that the command reader will - * miss the new pause address and pause on the old one, - * In that case we need to program the new start address - * using PCI. - */ - - diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; - count = 10000000; - while (diff == 0 && count--) { - paused = (via_read(dev_priv, 0x41c) & 0x80000000); - if (paused) - break; - reader = *(dev_priv->hw_addr_ptr); - diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; - } - - paused = via_read(dev_priv, 0x41c) & 0x80000000; - - if (paused && !no_pci_fire) { - reader = *(dev_priv->hw_addr_ptr); - diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; - diff &= (dev_priv->dma_high - 1); - if (diff != 0 && diff < (dev_priv->dma_high >> 1)) { - DRM_ERROR("Paused at incorrect address. " - "0x%08x, 0x%08x 0x%08x\n", - ptr, reader, dev_priv->dma_diff); - } else if (diff == 0) { - /* - * There is a concern that these writes may stall the PCI bus - * if the GPU is not idle. However, idling the GPU first - * doesn't make a difference. - */ - - via_write(dev_priv, VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); - via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_hi); - via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_lo); - via_read(dev_priv, VIA_REG_TRANSPACE); - } - } - return paused; -} - -static int via_wait_idle(drm_via_private_t *dev_priv) -{ - int count = 10000000; - - while (!(via_read(dev_priv, VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && --count) - ; - - while (count && (via_read(dev_priv, VIA_REG_STATUS) & - (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | - VIA_3D_ENG_BUSY))) - --count; - return count; -} - -static uint32_t *via_align_cmd(drm_via_private_t *dev_priv, uint32_t cmd_type, - uint32_t addr, uint32_t *cmd_addr_hi, - uint32_t *cmd_addr_lo, int skip_wait) -{ - uint32_t agp_base; - uint32_t cmd_addr, addr_lo, addr_hi; - uint32_t *vb; - uint32_t qw_pad_count; - - if (!skip_wait) - via_cmdbuf_wait(dev_priv, 2 * CMDBUF_ALIGNMENT_SIZE); - - vb = via_get_dma(dev_priv); - VIA_OUT_RING_QW(HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | - (VIA_REG_TRANSPACE >> 2), HC_ParaType_PreCR << 16); - agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; - qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) - - ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3); - - cmd_addr = (addr) ? addr : - agp_base + dev_priv->dma_low - 8 + (qw_pad_count << 3); - addr_lo = ((HC_SubA_HAGPBpL << 24) | (cmd_type & HC_HAGPBpID_MASK) | - (cmd_addr & HC_HAGPBpL_MASK)); - addr_hi = ((HC_SubA_HAGPBpH << 24) | (cmd_addr >> 24)); - - vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1); - VIA_OUT_RING_QW(*cmd_addr_hi = addr_hi, *cmd_addr_lo = addr_lo); - return vb; -} - -static void via_cmdbuf_start(drm_via_private_t *dev_priv) -{ - uint32_t pause_addr_lo, pause_addr_hi; - uint32_t start_addr, start_addr_lo; - uint32_t end_addr, end_addr_lo; - uint32_t command; - uint32_t agp_base; - uint32_t ptr; - uint32_t reader; - int count; - - dev_priv->dma_low = 0; - - agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; - start_addr = agp_base; - end_addr = agp_base + dev_priv->dma_high; - - start_addr_lo = ((HC_SubA_HAGPBstL << 24) | (start_addr & 0xFFFFFF)); - end_addr_lo = ((HC_SubA_HAGPBendL << 24) | (end_addr & 0xFFFFFF)); - command = ((HC_SubA_HAGPCMNT << 24) | (start_addr >> 24) | - ((end_addr & 0xff000000) >> 16)); - - dev_priv->last_pause_ptr = - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, - &pause_addr_hi, &pause_addr_lo, 1) - 1; - - via_flush_write_combine(); - (void) *(volatile uint32_t *)dev_priv->last_pause_ptr; - - via_write(dev_priv, VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); - via_write(dev_priv, VIA_REG_TRANSPACE, command); - via_write(dev_priv, VIA_REG_TRANSPACE, start_addr_lo); - via_write(dev_priv, VIA_REG_TRANSPACE, end_addr_lo); - - via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_hi); - via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_lo); - wmb(); - via_write(dev_priv, VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); - via_read(dev_priv, VIA_REG_TRANSPACE); - - dev_priv->dma_diff = 0; - - count = 10000000; - while (!(via_read(dev_priv, 0x41c) & 0x80000000) && count--); - - reader = *(dev_priv->hw_addr_ptr); - ptr = ((volatile char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) + - dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; - - /* - * This is the difference between where we tell the - * command reader to pause and where it actually pauses. - * This differs between hw implementation so we need to - * detect it. - */ - - dev_priv->dma_diff = ptr - reader; -} - -static void via_pad_cache(drm_via_private_t *dev_priv, int qwords) -{ - uint32_t *vb; - - via_cmdbuf_wait(dev_priv, qwords + 2); - vb = via_get_dma(dev_priv); - VIA_OUT_RING_QW(HC_HEADER2, HC_ParaType_NotTex << 16); - via_align_buffer(dev_priv, vb, qwords); -} - -static inline void via_dummy_bitblt(drm_via_private_t *dev_priv) -{ - uint32_t *vb = via_get_dma(dev_priv); - SetReg2DAGP(0x0C, (0 | (0 << 16))); - SetReg2DAGP(0x10, 0 | (0 << 16)); - SetReg2DAGP(0x0, 0x1 | 0x2000 | 0xAA000000); -} - -static void via_cmdbuf_jump(drm_via_private_t *dev_priv) -{ - uint32_t pause_addr_lo, pause_addr_hi; - uint32_t jump_addr_lo, jump_addr_hi; - volatile uint32_t *last_pause_ptr; - uint32_t dma_low_save1, dma_low_save2; - - via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, - &jump_addr_lo, 0); - - dev_priv->dma_wrap = dev_priv->dma_low; - - /* - * Wrap command buffer to the beginning. - */ - - dev_priv->dma_low = 0; - if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) - DRM_ERROR("via_cmdbuf_jump failed\n"); - - via_dummy_bitblt(dev_priv); - via_dummy_bitblt(dev_priv); - - last_pause_ptr = - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo, 0) - 1; - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo, 0); - - *last_pause_ptr = pause_addr_lo; - dma_low_save1 = dev_priv->dma_low; - - /* - * Now, set a trap that will pause the regulator if it tries to rerun the old - * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause - * and reissues the jump command over PCI, while the regulator has already taken the jump - * and actually paused at the current buffer end). - * There appears to be no other way to detect this condition, since the hw_addr_pointer - * does not seem to get updated immediately when a jump occurs. - */ - - last_pause_ptr = - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo, 0) - 1; - via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, - &pause_addr_lo, 0); - *last_pause_ptr = pause_addr_lo; - - dma_low_save2 = dev_priv->dma_low; - dev_priv->dma_low = dma_low_save1; - via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0); - dev_priv->dma_low = dma_low_save2; - via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); -} - - -static void via_cmdbuf_rewind(drm_via_private_t *dev_priv) -{ - via_cmdbuf_jump(dev_priv); -} - -static void via_cmdbuf_flush(drm_via_private_t *dev_priv, uint32_t cmd_type) -{ - uint32_t pause_addr_lo, pause_addr_hi; - - via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0); - via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); -} - -static void via_cmdbuf_pause(drm_via_private_t *dev_priv) -{ - via_cmdbuf_flush(dev_priv, HC_HAGPBpID_PAUSE); -} - -static void via_cmdbuf_reset(drm_via_private_t *dev_priv) -{ - via_cmdbuf_flush(dev_priv, HC_HAGPBpID_STOP); - via_wait_idle(dev_priv); -} - -/* - * User interface to the space and lag functions. - */ - -static int via_cmdbuf_size(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_cmdbuf_size_t *d_siz = data; - int ret = 0; - uint32_t tmp_size, count; - drm_via_private_t *dev_priv; - - DRM_DEBUG("\n"); - LOCK_TEST_WITH_RETURN(dev, file_priv); - - dev_priv = (drm_via_private_t *) dev->dev_private; - - if (dev_priv->ring.virtual_start == NULL) { - DRM_ERROR("called without initializing AGP ring buffer.\n"); - return -EFAULT; - } - - count = 1000000; - tmp_size = d_siz->size; - switch (d_siz->func) { - case VIA_CMDBUF_SPACE: - while (((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz->size) - && --count) { - if (!d_siz->wait) - break; - } - if (!count) { - DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n"); - ret = -EAGAIN; - } - break; - case VIA_CMDBUF_LAG: - while (((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz->size) - && --count) { - if (!d_siz->wait) - break; - } - if (!count) { - DRM_ERROR("VIA_CMDBUF_LAG timed out.\n"); - ret = -EAGAIN; - } - break; - default: - ret = -EFAULT; - } - d_siz->size = tmp_size; - - return ret; -} - -const struct drm_ioctl_desc via_ioctls[] = { - DRM_IOCTL_DEF_DRV(VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_FREEMEM, via_mem_free, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_AGP_INIT, via_agp_init, DRM_AUTH|DRM_MASTER), - DRM_IOCTL_DEF_DRV(VIA_FB_INIT, via_fb_init, DRM_AUTH|DRM_MASTER), - DRM_IOCTL_DEF_DRV(VIA_MAP_INIT, via_map_init, DRM_AUTH|DRM_MASTER), - DRM_IOCTL_DEF_DRV(VIA_DEC_FUTEX, via_decoder_futex, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_DMA_INIT, via_dma_init, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_CMDBUFFER, via_cmdbuffer, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_FLUSH, via_flush_ioctl, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_PCICMD, via_pci_cmdbuffer, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_CMDBUF_SIZE, via_cmdbuf_size, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_WAIT_IRQ, via_wait_irq, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_DMA_BLIT, via_dma_blit, DRM_AUTH), - DRM_IOCTL_DEF_DRV(VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH) -}; - -int via_max_ioctl = ARRAY_SIZE(via_ioctls); diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index 5da38082821f..d5d4a64437da 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -1,7 +1,10 @@ /* * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * + * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. All Rights Reserved. + * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. All Rights Reserved. + * Copyright 2004 The Unichrome project. All Rights Reserved. + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -22,6 +25,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include #include #include @@ -31,8 +35,705 @@ #include #include "via_drv.h" +#include "via_3d_reg.h" + +#define CMDBUF_ALIGNMENT_SIZE (0x100) +#define CMDBUF_ALIGNMENT_MASK (0x0ff) + +/* defines for VIA 3D registers */ +#define VIA_REG_STATUS 0x400 +#define VIA_REG_TRANSET 0x43C +#define VIA_REG_TRANSPACE 0x440 + +/* VIA_REG_STATUS(0x400): Engine Status */ +#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */ +#define VIA_2D_ENG_BUSY 0x00000001 /* 2D Engine is busy */ +#define VIA_3D_ENG_BUSY 0x00000002 /* 3D Engine is busy */ +#define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */ + +#define SetReg2DAGP(nReg, nData) { \ + *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ + *((uint32_t *)(vb) + 1) = (nData); \ + vb = ((uint32_t *)vb) + 2; \ + dev_priv->dma_low += 8; \ +} + +#define via_flush_write_combine() mb() + +#define VIA_OUT_RING_QW(w1, w2) do { \ + *vb++ = (w1); \ + *vb++ = (w2); \ + dev_priv->dma_low += 8; \ +} while (0) + +static void via_cmdbuf_start(drm_via_private_t *dev_priv); +static void via_cmdbuf_pause(drm_via_private_t *dev_priv); +static void via_cmdbuf_reset(drm_via_private_t *dev_priv); +static void via_cmdbuf_rewind(drm_via_private_t *dev_priv); +static int via_wait_idle(drm_via_private_t *dev_priv); +static void via_pad_cache(drm_via_private_t *dev_priv, int qwords); + +/* + * Free space in command buffer. + */ + +static uint32_t via_cmdbuf_space(drm_via_private_t *dev_priv) +{ + uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; + uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; + + return ((hw_addr <= dev_priv->dma_low) ? + (dev_priv->dma_high + hw_addr - dev_priv->dma_low) : + (hw_addr - dev_priv->dma_low)); +} + +/* + * How much does the command regulator lag behind? + */ + +static uint32_t via_cmdbuf_lag(drm_via_private_t *dev_priv) +{ + uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; + uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; + + return ((hw_addr <= dev_priv->dma_low) ? + (dev_priv->dma_low - hw_addr) : + (dev_priv->dma_wrap + dev_priv->dma_low - hw_addr)); +} + +/* + * Check that the given size fits in the buffer, otherwise wait. + */ + +static inline int +via_cmdbuf_wait(drm_via_private_t *dev_priv, unsigned int size) +{ + uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; + uint32_t cur_addr, hw_addr, next_addr; + volatile uint32_t *hw_addr_ptr; + uint32_t count; + hw_addr_ptr = dev_priv->hw_addr_ptr; + cur_addr = dev_priv->dma_low; + next_addr = cur_addr + size + 512 * 1024; + count = 1000000; + do { + hw_addr = *hw_addr_ptr - agp_base; + if (count-- == 0) { + DRM_ERROR + ("via_cmdbuf_wait timed out hw %x cur_addr %x next_addr %x\n", + hw_addr, cur_addr, next_addr); + return -1; + } + if ((cur_addr < hw_addr) && (next_addr >= hw_addr)) + msleep(1); + } while ((cur_addr < hw_addr) && (next_addr >= hw_addr)); + return 0; +} + +/* + * Checks whether buffer head has reach the end. Rewind the ring buffer + * when necessary. + * + * Returns virtual pointer to ring buffer. + */ + +static inline uint32_t *via_check_dma(drm_via_private_t * dev_priv, + unsigned int size) +{ + if ((dev_priv->dma_low + size + 4 * CMDBUF_ALIGNMENT_SIZE) > + dev_priv->dma_high) { + via_cmdbuf_rewind(dev_priv); + } + if (via_cmdbuf_wait(dev_priv, size) != 0) + return NULL; + + return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low); +} + +int via_dma_cleanup(struct drm_device *dev) +{ + if (dev->dev_private) { + drm_via_private_t *dev_priv = + (drm_via_private_t *) dev->dev_private; + + if (dev_priv->ring.virtual_start) { + via_cmdbuf_reset(dev_priv); + + drm_legacy_ioremapfree(&dev_priv->ring.map, dev); + dev_priv->ring.virtual_start = NULL; + } + + } + + return 0; +} + +static int via_initialize(struct drm_device *dev, + drm_via_private_t *dev_priv, + drm_via_dma_init_t *init) +{ + if (!dev_priv || !dev_priv->mmio) { + DRM_ERROR("via_dma_init called before via_map_init\n"); + return -EFAULT; + } + + if (dev_priv->ring.virtual_start != NULL) { + DRM_ERROR("called again without calling cleanup\n"); + return -EFAULT; + } + + if (!dev->agp || !dev->agp->base) { + DRM_ERROR("called with no agp memory available\n"); + return -EFAULT; + } + + if (dev_priv->chipset == VIA_DX9_0) { + DRM_ERROR("AGP DMA is not supported on this chip\n"); + return -EINVAL; + } + + dev_priv->ring.map.offset = dev->agp->base + init->offset; + dev_priv->ring.map.size = init->size; + dev_priv->ring.map.type = 0; + dev_priv->ring.map.flags = 0; + dev_priv->ring.map.mtrr = 0; + + drm_legacy_ioremap(&dev_priv->ring.map, dev); + + if (dev_priv->ring.map.handle == NULL) { + via_dma_cleanup(dev); + DRM_ERROR("can not ioremap virtual address for" + " ring buffer\n"); + return -ENOMEM; + } + + dev_priv->ring.virtual_start = dev_priv->ring.map.handle; + + dev_priv->dma_ptr = dev_priv->ring.virtual_start; + dev_priv->dma_low = 0; + dev_priv->dma_high = init->size; + dev_priv->dma_wrap = init->size; + dev_priv->dma_offset = init->offset; + dev_priv->last_pause_ptr = NULL; + dev_priv->hw_addr_ptr = + (volatile uint32_t *)((char *)dev_priv->mmio->handle + + init->reg_pause_addr); + + via_cmdbuf_start(dev_priv); + + return 0; +} + +static int via_dma_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_dma_init_t *init = data; + int retcode = 0; + + switch (init->func) { + case VIA_INIT_DMA: + if (!capable(CAP_SYS_ADMIN)) + retcode = -EPERM; + else + retcode = via_initialize(dev, dev_priv, init); + break; + case VIA_CLEANUP_DMA: + if (!capable(CAP_SYS_ADMIN)) + retcode = -EPERM; + else + retcode = via_dma_cleanup(dev); + break; + case VIA_DMA_INITIALIZED: + retcode = (dev_priv->ring.virtual_start != NULL) ? + 0 : -EFAULT; + break; + default: + retcode = -EINVAL; + break; + } + + return retcode; +} + +static int via_dispatch_cmdbuffer(struct drm_device *dev, drm_via_cmdbuffer_t *cmd) +{ + drm_via_private_t *dev_priv; + uint32_t *vb; + int ret; + + dev_priv = (drm_via_private_t *) dev->dev_private; + + if (dev_priv->ring.virtual_start == NULL) { + DRM_ERROR("called without initializing AGP ring buffer.\n"); + return -EFAULT; + } + + if (cmd->size > VIA_PCI_BUF_SIZE) + return -ENOMEM; + + if (copy_from_user(dev_priv->pci_buf, cmd->buf, cmd->size)) + return -EFAULT; + + /* + * Running this function on AGP memory is dead slow. Therefore + * we run it on a temporary cacheable system memory buffer and + * copy it to AGP memory when ready. + */ + + if ((ret = + via_verify_command_stream((uint32_t *) dev_priv->pci_buf, + cmd->size, dev, 1))) { + return ret; + } + + vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size); + if (vb == NULL) + return -EAGAIN; + + memcpy(vb, dev_priv->pci_buf, cmd->size); + + dev_priv->dma_low += cmd->size; + + /* + * Small submissions somehow stalls the CPU. (AGP cache effects?) + * pad to greater size. + */ + + if (cmd->size < 0x100) + via_pad_cache(dev_priv, (0x100 - cmd->size) >> 3); + via_cmdbuf_pause(dev_priv); + + return 0; +} + +int via_driver_dma_quiescent(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = dev->dev_private; + + if (!via_wait_idle(dev_priv)) + return -EBUSY; + return 0; +} + +static int via_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + return via_driver_dma_quiescent(dev); +} + +static int via_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_cmdbuffer_t *cmdbuf = data; + int ret; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size); + + ret = via_dispatch_cmdbuffer(dev, cmdbuf); + return ret; +} + +static int via_dispatch_pci_cmdbuffer(struct drm_device *dev, + drm_via_cmdbuffer_t *cmd) +{ + drm_via_private_t *dev_priv = dev->dev_private; + int ret; + + if (cmd->size > VIA_PCI_BUF_SIZE) + return -ENOMEM; + if (copy_from_user(dev_priv->pci_buf, cmd->buf, cmd->size)) + return -EFAULT; + + if ((ret = + via_verify_command_stream((uint32_t *) dev_priv->pci_buf, + cmd->size, dev, 0))) { + return ret; + } + ret = + via_parse_command_stream(dev, (const uint32_t *)dev_priv->pci_buf, + cmd->size); + return ret; +} + +static int via_pci_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_cmdbuffer_t *cmdbuf = data; + int ret; + + LOCK_TEST_WITH_RETURN(dev, file_priv); + + DRM_DEBUG("buf %p size %lu\n", cmdbuf->buf, cmdbuf->size); + + ret = via_dispatch_pci_cmdbuffer(dev, cmdbuf); + return ret; +} + +static inline uint32_t *via_align_buffer(drm_via_private_t *dev_priv, + uint32_t * vb, int qw_count) +{ + for (; qw_count > 0; --qw_count) + VIA_OUT_RING_QW(HC_DUMMY, HC_DUMMY); + return vb; +} + +/* + * This function is used internally by ring buffer management code. + * + * Returns virtual pointer to ring buffer. + */ +static inline uint32_t *via_get_dma(drm_via_private_t *dev_priv) +{ + return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low); +} + +/* + * Hooks a segment of data into the tail of the ring-buffer by + * modifying the pause address stored in the buffer itself. If + * the regulator has already paused, restart it. + */ +static int via_hook_segment(drm_via_private_t *dev_priv, + uint32_t pause_addr_hi, uint32_t pause_addr_lo, + int no_pci_fire) +{ + int paused, count; + volatile uint32_t *paused_at = dev_priv->last_pause_ptr; + uint32_t reader, ptr; + uint32_t diff; + + paused = 0; + via_flush_write_combine(); + (void) *(volatile uint32_t *)(via_get_dma(dev_priv) - 1); + + *paused_at = pause_addr_lo; + via_flush_write_combine(); + (void) *paused_at; + + reader = *(dev_priv->hw_addr_ptr); + ptr = ((volatile char *)paused_at - dev_priv->dma_ptr) + + dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; + + dev_priv->last_pause_ptr = via_get_dma(dev_priv) - 1; + + /* + * If there is a possibility that the command reader will + * miss the new pause address and pause on the old one, + * In that case we need to program the new start address + * using PCI. + */ + + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + count = 10000000; + while (diff == 0 && count--) { + paused = (via_read(dev_priv, 0x41c) & 0x80000000); + if (paused) + break; + reader = *(dev_priv->hw_addr_ptr); + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + } + + paused = via_read(dev_priv, 0x41c) & 0x80000000; + + if (paused && !no_pci_fire) { + reader = *(dev_priv->hw_addr_ptr); + diff = (uint32_t) (ptr - reader) - dev_priv->dma_diff; + diff &= (dev_priv->dma_high - 1); + if (diff != 0 && diff < (dev_priv->dma_high >> 1)) { + DRM_ERROR("Paused at incorrect address. " + "0x%08x, 0x%08x 0x%08x\n", + ptr, reader, dev_priv->dma_diff); + } else if (diff == 0) { + /* + * There is a concern that these writes may stall the PCI bus + * if the GPU is not idle. However, idling the GPU first + * doesn't make a difference. + */ + + via_write(dev_priv, VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); + via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_hi); + via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_lo); + via_read(dev_priv, VIA_REG_TRANSPACE); + } + } + return paused; +} + +static int via_wait_idle(drm_via_private_t *dev_priv) +{ + int count = 10000000; + + while (!(via_read(dev_priv, VIA_REG_STATUS) & VIA_VR_QUEUE_BUSY) && --count) + ; + + while (count && (via_read(dev_priv, VIA_REG_STATUS) & + (VIA_CMD_RGTR_BUSY | VIA_2D_ENG_BUSY | + VIA_3D_ENG_BUSY))) + --count; + return count; +} + +static uint32_t *via_align_cmd(drm_via_private_t *dev_priv, uint32_t cmd_type, + uint32_t addr, uint32_t *cmd_addr_hi, + uint32_t *cmd_addr_lo, int skip_wait) +{ + uint32_t agp_base; + uint32_t cmd_addr, addr_lo, addr_hi; + uint32_t *vb; + uint32_t qw_pad_count; + + if (!skip_wait) + via_cmdbuf_wait(dev_priv, 2 * CMDBUF_ALIGNMENT_SIZE); + + vb = via_get_dma(dev_priv); + VIA_OUT_RING_QW(HC_HEADER2 | ((VIA_REG_TRANSET >> 2) << 12) | + (VIA_REG_TRANSPACE >> 2), HC_ParaType_PreCR << 16); + agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; + qw_pad_count = (CMDBUF_ALIGNMENT_SIZE >> 3) - + ((dev_priv->dma_low & CMDBUF_ALIGNMENT_MASK) >> 3); + + cmd_addr = (addr) ? addr : + agp_base + dev_priv->dma_low - 8 + (qw_pad_count << 3); + addr_lo = ((HC_SubA_HAGPBpL << 24) | (cmd_type & HC_HAGPBpID_MASK) | + (cmd_addr & HC_HAGPBpL_MASK)); + addr_hi = ((HC_SubA_HAGPBpH << 24) | (cmd_addr >> 24)); + + vb = via_align_buffer(dev_priv, vb, qw_pad_count - 1); + VIA_OUT_RING_QW(*cmd_addr_hi = addr_hi, *cmd_addr_lo = addr_lo); + return vb; +} + +static void via_cmdbuf_start(drm_via_private_t *dev_priv) +{ + uint32_t pause_addr_lo, pause_addr_hi; + uint32_t start_addr, start_addr_lo; + uint32_t end_addr, end_addr_lo; + uint32_t command; + uint32_t agp_base; + uint32_t ptr; + uint32_t reader; + int count; + + dev_priv->dma_low = 0; + + agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; + start_addr = agp_base; + end_addr = agp_base + dev_priv->dma_high; + + start_addr_lo = ((HC_SubA_HAGPBstL << 24) | (start_addr & 0xFFFFFF)); + end_addr_lo = ((HC_SubA_HAGPBendL << 24) | (end_addr & 0xFFFFFF)); + command = ((HC_SubA_HAGPCMNT << 24) | (start_addr >> 24) | + ((end_addr & 0xff000000) >> 16)); + + dev_priv->last_pause_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, + &pause_addr_hi, &pause_addr_lo, 1) - 1; + + via_flush_write_combine(); + (void) *(volatile uint32_t *)dev_priv->last_pause_ptr; + + via_write(dev_priv, VIA_REG_TRANSET, (HC_ParaType_PreCR << 16)); + via_write(dev_priv, VIA_REG_TRANSPACE, command); + via_write(dev_priv, VIA_REG_TRANSPACE, start_addr_lo); + via_write(dev_priv, VIA_REG_TRANSPACE, end_addr_lo); + + via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_hi); + via_write(dev_priv, VIA_REG_TRANSPACE, pause_addr_lo); + wmb(); + via_write(dev_priv, VIA_REG_TRANSPACE, command | HC_HAGPCMNT_MASK); + via_read(dev_priv, VIA_REG_TRANSPACE); + + dev_priv->dma_diff = 0; + + count = 10000000; + while (!(via_read(dev_priv, 0x41c) & 0x80000000) && count--); + + reader = *(dev_priv->hw_addr_ptr); + ptr = ((volatile char *)dev_priv->last_pause_ptr - dev_priv->dma_ptr) + + dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr + 4; + + /* + * This is the difference between where we tell the + * command reader to pause and where it actually pauses. + * This differs between hw implementation so we need to + * detect it. + */ + + dev_priv->dma_diff = ptr - reader; +} + +static void via_pad_cache(drm_via_private_t *dev_priv, int qwords) +{ + uint32_t *vb; + + via_cmdbuf_wait(dev_priv, qwords + 2); + vb = via_get_dma(dev_priv); + VIA_OUT_RING_QW(HC_HEADER2, HC_ParaType_NotTex << 16); + via_align_buffer(dev_priv, vb, qwords); +} + +static inline void via_dummy_bitblt(drm_via_private_t *dev_priv) +{ + uint32_t *vb = via_get_dma(dev_priv); + SetReg2DAGP(0x0C, (0 | (0 << 16))); + SetReg2DAGP(0x10, 0 | (0 << 16)); + SetReg2DAGP(0x0, 0x1 | 0x2000 | 0xAA000000); +} + +static void via_cmdbuf_jump(drm_via_private_t *dev_priv) +{ + uint32_t pause_addr_lo, pause_addr_hi; + uint32_t jump_addr_lo, jump_addr_hi; + volatile uint32_t *last_pause_ptr; + uint32_t dma_low_save1, dma_low_save2; + + via_align_cmd(dev_priv, HC_HAGPBpID_JUMP, 0, &jump_addr_hi, + &jump_addr_lo, 0); + + dev_priv->dma_wrap = dev_priv->dma_low; + + /* + * Wrap command buffer to the beginning. + */ + + dev_priv->dma_low = 0; + if (via_cmdbuf_wait(dev_priv, CMDBUF_ALIGNMENT_SIZE) != 0) + DRM_ERROR("via_cmdbuf_jump failed\n"); + + via_dummy_bitblt(dev_priv); + via_dummy_bitblt(dev_priv); + + last_pause_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0) - 1; + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + + *last_pause_ptr = pause_addr_lo; + dma_low_save1 = dev_priv->dma_low; + + /* + * Now, set a trap that will pause the regulator if it tries to rerun the old + * command buffer. (Which may happen if via_hook_segment detecs a command regulator pause + * and reissues the jump command over PCI, while the regulator has already taken the jump + * and actually paused at the current buffer end). + * There appears to be no other way to detect this condition, since the hw_addr_pointer + * does not seem to get updated immediately when a jump occurs. + */ + + last_pause_ptr = + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0) - 1; + via_align_cmd(dev_priv, HC_HAGPBpID_PAUSE, 0, &pause_addr_hi, + &pause_addr_lo, 0); + *last_pause_ptr = pause_addr_lo; + + dma_low_save2 = dev_priv->dma_low; + dev_priv->dma_low = dma_low_save1; + via_hook_segment(dev_priv, jump_addr_hi, jump_addr_lo, 0); + dev_priv->dma_low = dma_low_save2; + via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); +} + + +static void via_cmdbuf_rewind(drm_via_private_t *dev_priv) +{ + via_cmdbuf_jump(dev_priv); +} + +static void via_cmdbuf_flush(drm_via_private_t *dev_priv, uint32_t cmd_type) +{ + uint32_t pause_addr_lo, pause_addr_hi; + + via_align_cmd(dev_priv, cmd_type, 0, &pause_addr_hi, &pause_addr_lo, 0); + via_hook_segment(dev_priv, pause_addr_hi, pause_addr_lo, 0); +} + +static void via_cmdbuf_pause(drm_via_private_t *dev_priv) +{ + via_cmdbuf_flush(dev_priv, HC_HAGPBpID_PAUSE); +} + +static void via_cmdbuf_reset(drm_via_private_t *dev_priv) +{ + via_cmdbuf_flush(dev_priv, HC_HAGPBpID_STOP); + via_wait_idle(dev_priv); +} + +/* + * User interface to the space and lag functions. + */ + +static int via_cmdbuf_size(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_cmdbuf_size_t *d_siz = data; + int ret = 0; + uint32_t tmp_size, count; + drm_via_private_t *dev_priv; + + DRM_DEBUG("\n"); + LOCK_TEST_WITH_RETURN(dev, file_priv); + + dev_priv = (drm_via_private_t *) dev->dev_private; + + if (dev_priv->ring.virtual_start == NULL) { + DRM_ERROR("called without initializing AGP ring buffer.\n"); + return -EFAULT; + } + + count = 1000000; + tmp_size = d_siz->size; + switch (d_siz->func) { + case VIA_CMDBUF_SPACE: + while (((tmp_size = via_cmdbuf_space(dev_priv)) < d_siz->size) + && --count) { + if (!d_siz->wait) + break; + } + if (!count) { + DRM_ERROR("VIA_CMDBUF_SPACE timed out.\n"); + ret = -EAGAIN; + } + break; + case VIA_CMDBUF_LAG: + while (((tmp_size = via_cmdbuf_lag(dev_priv)) > d_siz->size) + && --count) { + if (!d_siz->wait) + break; + } + if (!count) { + DRM_ERROR("VIA_CMDBUF_LAG timed out.\n"); + ret = -EAGAIN; + } + break; + default: + ret = -EFAULT; + } + d_siz->size = tmp_size; + + return ret; +} + +static const struct drm_ioctl_desc via_ioctls[] = { + DRM_IOCTL_DEF_DRV(VIA_ALLOCMEM, via_mem_alloc, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_FREEMEM, via_mem_free, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_AGP_INIT, via_agp_init, DRM_AUTH|DRM_MASTER), + DRM_IOCTL_DEF_DRV(VIA_FB_INIT, via_fb_init, DRM_AUTH|DRM_MASTER), + DRM_IOCTL_DEF_DRV(VIA_MAP_INIT, via_map_init, DRM_AUTH|DRM_MASTER), + DRM_IOCTL_DEF_DRV(VIA_DEC_FUTEX, via_decoder_futex, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_DMA_INIT, via_dma_init, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_CMDBUFFER, via_cmdbuffer, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_FLUSH, via_flush_ioctl, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_PCICMD, via_pci_cmdbuffer, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_CMDBUF_SIZE, via_cmdbuf_size, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_WAIT_IRQ, via_wait_irq, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_DMA_BLIT, via_dma_blit, DRM_AUTH), + DRM_IOCTL_DEF_DRV(VIA_BLIT_SYNC, via_dma_blit_sync, DRM_AUTH) +}; +static int via_max_ioctl = ARRAY_SIZE(via_ioctls); static int via_driver_open(struct drm_device *dev, struct drm_file *file) { struct via_file_private *file_priv; diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index d5ad1b05bf77..5757422ea634 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -183,9 +183,6 @@ do { \ remove_wait_queue(&(queue), &entry); \ } while (0) -extern const struct drm_ioctl_desc via_ioctls[]; -extern int via_max_ioctl; - extern int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv); -- cgit v1.2.3-59-g8ed1b From fa0cc3ca1df89e96c20c2aa229363bfc1670068b Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:52 +0200 Subject: drm/via: Embed via_map in via_dri1 A few functions has no external use and are made static. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-4-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dri1.c | 102 +++++++++++++++++++++++++++++++ drivers/gpu/drm/via/via_drv.h | 4 -- drivers/gpu/drm/via/via_map.c | 132 ----------------------------------------- 4 files changed, 103 insertions(+), 137 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_map.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index 824198c0c2ea..be12cbfa4d7f 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_irq.o via_dri1.o via_map.o via_mm.o via_verifier.o via_video.o via_dmablit.o +via-y := via_irq.o via_dri1.o via_mm.o via_verifier.o via_video.o via_dmablit.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index d5d4a64437da..2fa22359b1f6 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "via_drv.h" @@ -66,6 +67,107 @@ dev_priv->dma_low += 8; \ } while (0) +static int via_do_init_map(struct drm_device *dev, drm_via_init_t *init) +{ + drm_via_private_t *dev_priv = dev->dev_private; + + DRM_DEBUG("\n"); + + dev_priv->sarea = drm_legacy_getsarea(dev); + if (!dev_priv->sarea) { + DRM_ERROR("could not find sarea!\n"); + dev->dev_private = (void *)dev_priv; + via_do_cleanup_map(dev); + return -EINVAL; + } + + dev_priv->fb = drm_legacy_findmap(dev, init->fb_offset); + if (!dev_priv->fb) { + DRM_ERROR("could not find framebuffer!\n"); + dev->dev_private = (void *)dev_priv; + via_do_cleanup_map(dev); + return -EINVAL; + } + dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset); + if (!dev_priv->mmio) { + DRM_ERROR("could not find mmio region!\n"); + dev->dev_private = (void *)dev_priv; + via_do_cleanup_map(dev); + return -EINVAL; + } + + dev_priv->sarea_priv = + (drm_via_sarea_t *) ((u8 *) dev_priv->sarea->handle + + init->sarea_priv_offset); + + dev_priv->agpAddr = init->agpAddr; + + via_init_futex(dev_priv); + + via_init_dmablit(dev); + + dev->dev_private = (void *)dev_priv; + return 0; +} + +int via_do_cleanup_map(struct drm_device *dev) +{ + via_dma_cleanup(dev); + + return 0; +} + +static int via_map_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_init_t *init = data; + + DRM_DEBUG("\n"); + + switch (init->func) { + case VIA_INIT_MAP: + return via_do_init_map(dev, init); + case VIA_CLEANUP_MAP: + return via_do_cleanup_map(dev); + } + + return -EINVAL; +} + +static int via_driver_load(struct drm_device *dev, unsigned long chipset) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + drm_via_private_t *dev_priv; + int ret = 0; + + dev_priv = kzalloc(sizeof(drm_via_private_t), GFP_KERNEL); + if (dev_priv == NULL) + return -ENOMEM; + + idr_init(&dev_priv->object_idr); + dev->dev_private = (void *)dev_priv; + + dev_priv->chipset = chipset; + + pci_set_master(pdev); + + ret = drm_vblank_init(dev, 1); + if (ret) { + kfree(dev_priv); + return ret; + } + + return 0; +} + +static void via_driver_unload(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = dev->dev_private; + + idr_destroy(&dev_priv->object_idr); + + kfree(dev_priv); +} + static void via_cmdbuf_start(drm_via_private_t *dev_priv); static void via_cmdbuf_pause(drm_via_private_t *dev_priv); static void via_cmdbuf_reset(drm_via_private_t *dev_priv); diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 5757422ea634..4eaad17c0e02 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -187,15 +187,11 @@ extern int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file extern int via_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int via_map_init(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int via_driver_load(struct drm_device *dev, unsigned long chipset); -extern void via_driver_unload(struct drm_device *dev); - extern int via_init_context(struct drm_device *dev, int context); extern int via_final_context(struct drm_device *dev, int context); diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c deleted file mode 100644 index a9f6b0c11966..000000000000 --- a/drivers/gpu/drm/via/via_map.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -#include - -#include -#include -#include - -#include "via_drv.h" - -static int via_do_init_map(struct drm_device *dev, drm_via_init_t *init) -{ - drm_via_private_t *dev_priv = dev->dev_private; - - DRM_DEBUG("\n"); - - dev_priv->sarea = drm_legacy_getsarea(dev); - if (!dev_priv->sarea) { - DRM_ERROR("could not find sarea!\n"); - dev->dev_private = (void *)dev_priv; - via_do_cleanup_map(dev); - return -EINVAL; - } - - dev_priv->fb = drm_legacy_findmap(dev, init->fb_offset); - if (!dev_priv->fb) { - DRM_ERROR("could not find framebuffer!\n"); - dev->dev_private = (void *)dev_priv; - via_do_cleanup_map(dev); - return -EINVAL; - } - dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset); - if (!dev_priv->mmio) { - DRM_ERROR("could not find mmio region!\n"); - dev->dev_private = (void *)dev_priv; - via_do_cleanup_map(dev); - return -EINVAL; - } - - dev_priv->sarea_priv = - (drm_via_sarea_t *) ((u8 *) dev_priv->sarea->handle + - init->sarea_priv_offset); - - dev_priv->agpAddr = init->agpAddr; - - via_init_futex(dev_priv); - - via_init_dmablit(dev); - - dev->dev_private = (void *)dev_priv; - return 0; -} - -int via_do_cleanup_map(struct drm_device *dev) -{ - via_dma_cleanup(dev); - - return 0; -} - -int via_map_init(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_init_t *init = data; - - DRM_DEBUG("\n"); - - switch (init->func) { - case VIA_INIT_MAP: - return via_do_init_map(dev, init); - case VIA_CLEANUP_MAP: - return via_do_cleanup_map(dev); - } - - return -EINVAL; -} - -int via_driver_load(struct drm_device *dev, unsigned long chipset) -{ - struct pci_dev *pdev = to_pci_dev(dev->dev); - drm_via_private_t *dev_priv; - int ret = 0; - - dev_priv = kzalloc(sizeof(drm_via_private_t), GFP_KERNEL); - if (dev_priv == NULL) - return -ENOMEM; - - idr_init(&dev_priv->object_idr); - dev->dev_private = (void *)dev_priv; - - dev_priv->chipset = chipset; - - pci_set_master(pdev); - - ret = drm_vblank_init(dev, 1); - if (ret) { - kfree(dev_priv); - return ret; - } - - return 0; -} - -void via_driver_unload(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = dev->dev_private; - - idr_destroy(&dev_priv->object_idr); - - kfree(dev_priv); -} -- cgit v1.2.3-59-g8ed1b From 67034c8e004982764dd5b5df3d57a4179902f60d Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:53 +0200 Subject: drm/via: Embed via_mm in via_dri1 All functions was made static as there are no external users. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-5-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dri1.c | 208 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/via/via_drv.h | 9 -- drivers/gpu/drm/via/via_mm.c | 241 ----------------------------------------- 4 files changed, 209 insertions(+), 251 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_mm.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index be12cbfa4d7f..daadc0d3233f 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_irq.o via_dri1.o via_mm.o via_verifier.o via_video.o via_dmablit.o +via-y := via_irq.o via_dri1.o via_verifier.o via_video.o via_dmablit.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index 2fa22359b1f6..3ebe17649ec2 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -2,6 +2,7 @@ * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. All Rights Reserved. + * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. All Rights Reserved. * Copyright 2004 The Unichrome project. All Rights Reserved. * @@ -67,6 +68,213 @@ dev_priv->dma_low += 8; \ } while (0) +#define VIA_MM_ALIGN_SHIFT 4 +#define VIA_MM_ALIGN_MASK ((1 << VIA_MM_ALIGN_SHIFT) - 1) + +struct via_memblock { + struct drm_mm_node mm_node; + struct list_head owner_list; +}; + +static int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_agp_t *agp = data; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + mutex_lock(&dev->struct_mutex); + drm_mm_init(&dev_priv->agp_mm, 0, agp->size >> VIA_MM_ALIGN_SHIFT); + + dev_priv->agp_initialized = 1; + dev_priv->agp_offset = agp->offset; + mutex_unlock(&dev->struct_mutex); + + DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size); + return 0; +} + +static int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_fb_t *fb = data; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + mutex_lock(&dev->struct_mutex); + drm_mm_init(&dev_priv->vram_mm, 0, fb->size >> VIA_MM_ALIGN_SHIFT); + + dev_priv->vram_initialized = 1; + dev_priv->vram_offset = fb->offset; + + mutex_unlock(&dev->struct_mutex); + DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size); + + return 0; + +} + +static int via_final_context(struct drm_device *dev, int context) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + via_release_futex(dev_priv, context); + + /* Linux specific until context tracking code gets ported to BSD */ + /* Last context, perform cleanup */ + if (list_is_singular(&dev->ctxlist)) { + DRM_DEBUG("Last Context\n"); + drm_legacy_irq_uninstall(dev); + via_cleanup_futex(dev_priv); + via_do_cleanup_map(dev); + } + return 1; +} + +static void via_lastclose(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + + if (!dev_priv) + return; + + mutex_lock(&dev->struct_mutex); + if (dev_priv->vram_initialized) { + drm_mm_takedown(&dev_priv->vram_mm); + dev_priv->vram_initialized = 0; + } + if (dev_priv->agp_initialized) { + drm_mm_takedown(&dev_priv->agp_mm); + dev_priv->agp_initialized = 0; + } + mutex_unlock(&dev->struct_mutex); +} + +static int via_mem_alloc(struct drm_device *dev, void *data, + struct drm_file *file) +{ + drm_via_mem_t *mem = data; + int retval = 0, user_key; + struct via_memblock *item; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + struct via_file_private *file_priv = file->driver_priv; + unsigned long tmpSize; + + if (mem->type > VIA_MEM_AGP) { + DRM_ERROR("Unknown memory type allocation\n"); + return -EINVAL; + } + mutex_lock(&dev->struct_mutex); + if (0 == ((mem->type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : + dev_priv->agp_initialized)) { + mutex_unlock(&dev->struct_mutex); + DRM_ERROR + ("Attempt to allocate from uninitialized memory manager.\n"); + return -EINVAL; + } + + item = kzalloc(sizeof(*item), GFP_KERNEL); + if (!item) { + retval = -ENOMEM; + goto fail_alloc; + } + + tmpSize = (mem->size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; + if (mem->type == VIA_MEM_AGP) + retval = drm_mm_insert_node(&dev_priv->agp_mm, + &item->mm_node, + tmpSize); + else + retval = drm_mm_insert_node(&dev_priv->vram_mm, + &item->mm_node, + tmpSize); + if (retval) + goto fail_alloc; + + retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); + if (retval < 0) + goto fail_idr; + user_key = retval; + + list_add(&item->owner_list, &file_priv->obj_list); + mutex_unlock(&dev->struct_mutex); + + mem->offset = ((mem->type == VIA_MEM_VIDEO) ? + dev_priv->vram_offset : dev_priv->agp_offset) + + ((item->mm_node.start) << VIA_MM_ALIGN_SHIFT); + mem->index = user_key; + + return 0; + +fail_idr: + drm_mm_remove_node(&item->mm_node); +fail_alloc: + kfree(item); + mutex_unlock(&dev->struct_mutex); + + mem->offset = 0; + mem->size = 0; + mem->index = 0; + DRM_DEBUG("Video memory allocation failed\n"); + + return retval; +} + +static int via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_private_t *dev_priv = dev->dev_private; + drm_via_mem_t *mem = data; + struct via_memblock *obj; + + mutex_lock(&dev->struct_mutex); + obj = idr_find(&dev_priv->object_idr, mem->index); + if (obj == NULL) { + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + idr_remove(&dev_priv->object_idr, mem->index); + list_del(&obj->owner_list); + drm_mm_remove_node(&obj->mm_node); + kfree(obj); + mutex_unlock(&dev->struct_mutex); + + DRM_DEBUG("free = 0x%lx\n", mem->index); + + return 0; +} + + +static void via_reclaim_buffers_locked(struct drm_device *dev, + struct drm_file *file) +{ + struct via_file_private *file_priv = file->driver_priv; + struct via_memblock *entry, *next; + + if (!(dev->master && file->master->lock.hw_lock)) + return; + + drm_legacy_idlelock_take(&file->master->lock); + + mutex_lock(&dev->struct_mutex); + if (list_empty(&file_priv->obj_list)) { + mutex_unlock(&dev->struct_mutex); + drm_legacy_idlelock_release(&file->master->lock); + + return; + } + + via_driver_dma_quiescent(dev); + + list_for_each_entry_safe(entry, next, &file_priv->obj_list, + owner_list) { + list_del(&entry->owner_list); + drm_mm_remove_node(&entry->mm_node); + kfree(entry); + } + mutex_unlock(&dev->struct_mutex); + + drm_legacy_idlelock_release(&file->master->lock); + + return; +} + static int via_do_init_map(struct drm_device *dev, drm_via_init_t *init) { drm_via_private_t *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 4eaad17c0e02..c1a5fdf0bc4b 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -183,17 +183,12 @@ do { \ remove_wait_queue(&(queue), &entry); \ } while (0) -extern int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int via_mem_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_init_context(struct drm_device *dev, int context); -extern int via_final_context(struct drm_device *dev, int context); extern int via_do_cleanup_map(struct drm_device *dev); extern u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe); @@ -212,10 +207,6 @@ extern void via_init_futex(drm_via_private_t *dev_priv); extern void via_cleanup_futex(drm_via_private_t *dev_priv); extern void via_release_futex(drm_via_private_t *dev_priv, int context); -extern void via_reclaim_buffers_locked(struct drm_device *dev, - struct drm_file *file_priv); -extern void via_lastclose(struct drm_device *dev); - extern void via_dmablit_handler(struct drm_device *dev, int engine, int from_irq); extern void via_init_dmablit(struct drm_device *dev); diff --git a/drivers/gpu/drm/via/via_mm.c b/drivers/gpu/drm/via/via_mm.c deleted file mode 100644 index c9afa1a51f23..000000000000 --- a/drivers/gpu/drm/via/via_mm.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -/* - * Authors: Thomas Hellström - */ - -#include - -#include -#include -#include - -#include "via_drv.h" - -#define VIA_MM_ALIGN_SHIFT 4 -#define VIA_MM_ALIGN_MASK ((1 << VIA_MM_ALIGN_SHIFT) - 1) - -struct via_memblock { - struct drm_mm_node mm_node; - struct list_head owner_list; -}; - -int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_agp_t *agp = data; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - - mutex_lock(&dev->struct_mutex); - drm_mm_init(&dev_priv->agp_mm, 0, agp->size >> VIA_MM_ALIGN_SHIFT); - - dev_priv->agp_initialized = 1; - dev_priv->agp_offset = agp->offset; - mutex_unlock(&dev->struct_mutex); - - DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size); - return 0; -} - -int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_fb_t *fb = data; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - - mutex_lock(&dev->struct_mutex); - drm_mm_init(&dev_priv->vram_mm, 0, fb->size >> VIA_MM_ALIGN_SHIFT); - - dev_priv->vram_initialized = 1; - dev_priv->vram_offset = fb->offset; - - mutex_unlock(&dev->struct_mutex); - DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size); - - return 0; - -} - -int via_final_context(struct drm_device *dev, int context) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - - via_release_futex(dev_priv, context); - - /* Linux specific until context tracking code gets ported to BSD */ - /* Last context, perform cleanup */ - if (list_is_singular(&dev->ctxlist)) { - DRM_DEBUG("Last Context\n"); - drm_legacy_irq_uninstall(dev); - via_cleanup_futex(dev_priv); - via_do_cleanup_map(dev); - } - return 1; -} - -void via_lastclose(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - - if (!dev_priv) - return; - - mutex_lock(&dev->struct_mutex); - if (dev_priv->vram_initialized) { - drm_mm_takedown(&dev_priv->vram_mm); - dev_priv->vram_initialized = 0; - } - if (dev_priv->agp_initialized) { - drm_mm_takedown(&dev_priv->agp_mm); - dev_priv->agp_initialized = 0; - } - mutex_unlock(&dev->struct_mutex); -} - -int via_mem_alloc(struct drm_device *dev, void *data, - struct drm_file *file) -{ - drm_via_mem_t *mem = data; - int retval = 0, user_key; - struct via_memblock *item; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - struct via_file_private *file_priv = file->driver_priv; - unsigned long tmpSize; - - if (mem->type > VIA_MEM_AGP) { - DRM_ERROR("Unknown memory type allocation\n"); - return -EINVAL; - } - mutex_lock(&dev->struct_mutex); - if (0 == ((mem->type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : - dev_priv->agp_initialized)) { - mutex_unlock(&dev->struct_mutex); - DRM_ERROR - ("Attempt to allocate from uninitialized memory manager.\n"); - return -EINVAL; - } - - item = kzalloc(sizeof(*item), GFP_KERNEL); - if (!item) { - retval = -ENOMEM; - goto fail_alloc; - } - - tmpSize = (mem->size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; - if (mem->type == VIA_MEM_AGP) - retval = drm_mm_insert_node(&dev_priv->agp_mm, - &item->mm_node, - tmpSize); - else - retval = drm_mm_insert_node(&dev_priv->vram_mm, - &item->mm_node, - tmpSize); - if (retval) - goto fail_alloc; - - retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); - if (retval < 0) - goto fail_idr; - user_key = retval; - - list_add(&item->owner_list, &file_priv->obj_list); - mutex_unlock(&dev->struct_mutex); - - mem->offset = ((mem->type == VIA_MEM_VIDEO) ? - dev_priv->vram_offset : dev_priv->agp_offset) + - ((item->mm_node.start) << VIA_MM_ALIGN_SHIFT); - mem->index = user_key; - - return 0; - -fail_idr: - drm_mm_remove_node(&item->mm_node); -fail_alloc: - kfree(item); - mutex_unlock(&dev->struct_mutex); - - mem->offset = 0; - mem->size = 0; - mem->index = 0; - DRM_DEBUG("Video memory allocation failed\n"); - - return retval; -} - -int via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_private_t *dev_priv = dev->dev_private; - drm_via_mem_t *mem = data; - struct via_memblock *obj; - - mutex_lock(&dev->struct_mutex); - obj = idr_find(&dev_priv->object_idr, mem->index); - if (obj == NULL) { - mutex_unlock(&dev->struct_mutex); - return -EINVAL; - } - - idr_remove(&dev_priv->object_idr, mem->index); - list_del(&obj->owner_list); - drm_mm_remove_node(&obj->mm_node); - kfree(obj); - mutex_unlock(&dev->struct_mutex); - - DRM_DEBUG("free = 0x%lx\n", mem->index); - - return 0; -} - - -void via_reclaim_buffers_locked(struct drm_device *dev, - struct drm_file *file) -{ - struct via_file_private *file_priv = file->driver_priv; - struct via_memblock *entry, *next; - - if (!(dev->master && file->master->lock.hw_lock)) - return; - - drm_legacy_idlelock_take(&file->master->lock); - - mutex_lock(&dev->struct_mutex); - if (list_empty(&file_priv->obj_list)) { - mutex_unlock(&dev->struct_mutex); - drm_legacy_idlelock_release(&file->master->lock); - - return; - } - - via_driver_dma_quiescent(dev); - - list_for_each_entry_safe(entry, next, &file_priv->obj_list, - owner_list) { - list_del(&entry->owner_list); - drm_mm_remove_node(&entry->mm_node); - kfree(entry); - } - mutex_unlock(&dev->struct_mutex); - - drm_legacy_idlelock_release(&file->master->lock); - - return; -} -- cgit v1.2.3-59-g8ed1b From e85e4ae0157193703ec8d3ca95c669ddeb2023ee Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:54 +0200 Subject: drm/via: Embed via_video in via_dri1 All functions are made static as there are no more external users. The file had a new copyright that is kept. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-6-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dri1.c | 66 ++++++++++++++++++++++++++++- drivers/gpu/drm/via/via_drv.h | 4 -- drivers/gpu/drm/via/via_video.c | 94 ----------------------------------------- 4 files changed, 66 insertions(+), 100 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_video.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index daadc0d3233f..398be21c6df8 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_irq.o via_dri1.o via_verifier.o via_video.o via_dmablit.o +via-y := via_irq.o via_dri1.o via_verifier.o via_dmablit.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index 3ebe17649ec2..4c5517330110 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -5,7 +5,8 @@ * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. All Rights Reserved. * Copyright 2004 The Unichrome project. All Rights Reserved. - * + * Copyright 2005 Thomas Hellstrom. All Rights Reserved. + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation @@ -76,6 +77,69 @@ struct via_memblock { struct list_head owner_list; }; +static void via_init_futex(drm_via_private_t *dev_priv) +{ + unsigned int i; + + DRM_DEBUG("\n"); + + for (i = 0; i < VIA_NR_XVMC_LOCKS; ++i) { + init_waitqueue_head(&(dev_priv->decoder_queue[i])); + XVMCLOCKPTR(dev_priv->sarea_priv, i)->lock = 0; + } +} + +static void via_cleanup_futex(drm_via_private_t *dev_priv) +{ +} + +static void via_release_futex(drm_via_private_t *dev_priv, int context) +{ + unsigned int i; + volatile int *lock; + + if (!dev_priv->sarea_priv) + return; + + for (i = 0; i < VIA_NR_XVMC_LOCKS; ++i) { + lock = (volatile int *)XVMCLOCKPTR(dev_priv->sarea_priv, i); + if ((_DRM_LOCKING_CONTEXT(*lock) == context)) { + if (_DRM_LOCK_IS_HELD(*lock) + && (*lock & _DRM_LOCK_CONT)) { + wake_up(&(dev_priv->decoder_queue[i])); + } + *lock = 0; + } + } +} + +static int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_futex_t *fx = data; + volatile int *lock; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; + int ret = 0; + + DRM_DEBUG("\n"); + + if (fx->lock >= VIA_NR_XVMC_LOCKS) + return -EFAULT; + + lock = (volatile int *)XVMCLOCKPTR(sAPriv, fx->lock); + + switch (fx->func) { + case VIA_FUTEX_WAIT: + VIA_WAIT_ON(ret, dev_priv->decoder_queue[fx->lock], + (fx->ms / 10) * (HZ / 100), *lock != fx->val); + return ret; + case VIA_FUTEX_WAKE: + wake_up(&(dev_priv->decoder_queue[fx->lock])); + return 0; + } + return 0; +} + static int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_via_agp_t *agp = data; diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index c1a5fdf0bc4b..312e86b850f6 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -183,7 +183,6 @@ do { \ remove_wait_queue(&(queue), &entry); \ } while (0) -extern int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv); @@ -203,9 +202,6 @@ extern void via_driver_irq_uninstall(struct drm_device *dev); extern int via_dma_cleanup(struct drm_device *dev); extern void via_init_command_verifier(void); extern int via_driver_dma_quiescent(struct drm_device *dev); -extern void via_init_futex(drm_via_private_t *dev_priv); -extern void via_cleanup_futex(drm_via_private_t *dev_priv); -extern void via_release_futex(drm_via_private_t *dev_priv, int context); extern void via_dmablit_handler(struct drm_device *dev, int engine, int from_irq); extern void via_init_dmablit(struct drm_device *dev); diff --git a/drivers/gpu/drm/via/via_video.c b/drivers/gpu/drm/via/via_video.c deleted file mode 100644 index 53b1f58f99b4..000000000000 --- a/drivers/gpu/drm/via/via_video.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2005 Thomas Hellstrom. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Thomas Hellstrom 2005. - * - * Video and XvMC related functions. - */ - -#include -#include - -#include "via_drv.h" - -void via_init_futex(drm_via_private_t *dev_priv) -{ - unsigned int i; - - DRM_DEBUG("\n"); - - for (i = 0; i < VIA_NR_XVMC_LOCKS; ++i) { - init_waitqueue_head(&(dev_priv->decoder_queue[i])); - XVMCLOCKPTR(dev_priv->sarea_priv, i)->lock = 0; - } -} - -void via_cleanup_futex(drm_via_private_t *dev_priv) -{ -} - -void via_release_futex(drm_via_private_t *dev_priv, int context) -{ - unsigned int i; - volatile int *lock; - - if (!dev_priv->sarea_priv) - return; - - for (i = 0; i < VIA_NR_XVMC_LOCKS; ++i) { - lock = (volatile int *)XVMCLOCKPTR(dev_priv->sarea_priv, i); - if ((_DRM_LOCKING_CONTEXT(*lock) == context)) { - if (_DRM_LOCK_IS_HELD(*lock) - && (*lock & _DRM_LOCK_CONT)) { - wake_up(&(dev_priv->decoder_queue[i])); - } - *lock = 0; - } - } -} - -int via_decoder_futex(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_futex_t *fx = data; - volatile int *lock; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; - int ret = 0; - - DRM_DEBUG("\n"); - - if (fx->lock >= VIA_NR_XVMC_LOCKS) - return -EFAULT; - - lock = (volatile int *)XVMCLOCKPTR(sAPriv, fx->lock); - - switch (fx->func) { - case VIA_FUTEX_WAIT: - VIA_WAIT_ON(ret, dev_priv->decoder_queue[fx->lock], - (fx->ms / 10) * (HZ / 100), *lock != fx->val); - return ret; - case VIA_FUTEX_WAKE: - wake_up(&(dev_priv->decoder_queue[fx->lock])); - return 0; - } - return 0; -} -- cgit v1.2.3-59-g8ed1b From b0b3a879258ef43cf15b0bac30b92e7c1c4ebbae Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:55 +0200 Subject: drm/via: Embed via_irq in via_dri1 All functions are made static as there are no more external users. The file had new copyrights that are kept. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-7-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dri1.c | 347 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/via/via_drv.h | 9 - drivers/gpu/drm/via/via_irq.c | 388 ----------------------------------------- 4 files changed, 348 insertions(+), 398 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_irq.c (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index 398be21c6df8..dd75ea8a1423 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_irq.o via_dri1.o via_verifier.o via_dmablit.o +via-y := via_dri1.o via_verifier.o via_dmablit.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index 4c5517330110..32aae172c6f3 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -1,10 +1,12 @@ /* * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * Copyright 2002 Tungsten Graphics, Inc. * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. All Rights Reserved. * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. All Rights Reserved. * Copyright 2004 The Unichrome project. All Rights Reserved. + * Copyright 2004 BEAM Ltd. * Copyright 2005 Thomas Hellstrom. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -77,6 +79,351 @@ struct via_memblock { struct list_head owner_list; }; +#define VIA_REG_INTERRUPT 0x200 + +/* VIA_REG_INTERRUPT */ +#define VIA_IRQ_GLOBAL (1 << 31) +#define VIA_IRQ_VBLANK_ENABLE (1 << 19) +#define VIA_IRQ_VBLANK_PENDING (1 << 3) +#define VIA_IRQ_HQV0_ENABLE (1 << 11) +#define VIA_IRQ_HQV1_ENABLE (1 << 25) +#define VIA_IRQ_HQV0_PENDING (1 << 9) +#define VIA_IRQ_HQV1_PENDING (1 << 10) +#define VIA_IRQ_DMA0_DD_ENABLE (1 << 20) +#define VIA_IRQ_DMA0_TD_ENABLE (1 << 21) +#define VIA_IRQ_DMA1_DD_ENABLE (1 << 22) +#define VIA_IRQ_DMA1_TD_ENABLE (1 << 23) +#define VIA_IRQ_DMA0_DD_PENDING (1 << 4) +#define VIA_IRQ_DMA0_TD_PENDING (1 << 5) +#define VIA_IRQ_DMA1_DD_PENDING (1 << 6) +#define VIA_IRQ_DMA1_TD_PENDING (1 << 7) + +/* + * Device-specific IRQs go here. This type might need to be extended with + * the register if there are multiple IRQ control registers. + * Currently we activate the HQV interrupts of Unichrome Pro group A. + */ + +static maskarray_t via_pro_group_a_irqs[] = { + {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, + 0x00000000 }, + {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, + 0x00000000 }, + {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, + {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, +}; +static int via_num_pro_group_a = ARRAY_SIZE(via_pro_group_a_irqs); +static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; + +static maskarray_t via_unichrome_irqs[] = { + {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, + {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, + VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} +}; +static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs); +static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; + + +static u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe) +{ + drm_via_private_t *dev_priv = dev->dev_private; + + if (pipe != 0) + return 0; + + return atomic_read(&dev_priv->vbl_received); +} + +static irqreturn_t via_driver_irq_handler(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *) arg; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + u32 status; + int handled = 0; + ktime_t cur_vblank; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int i; + + status = via_read(dev_priv, VIA_REG_INTERRUPT); + if (status & VIA_IRQ_VBLANK_PENDING) { + atomic_inc(&dev_priv->vbl_received); + if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) { + cur_vblank = ktime_get(); + if (dev_priv->last_vblank_valid) { + dev_priv->nsec_per_vblank = + ktime_sub(cur_vblank, + dev_priv->last_vblank) >> 4; + } + dev_priv->last_vblank = cur_vblank; + dev_priv->last_vblank_valid = 1; + } + if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) { + DRM_DEBUG("nsec per vblank is: %llu\n", + ktime_to_ns(dev_priv->nsec_per_vblank)); + } + drm_handle_vblank(dev, 0); + handled = 1; + } + + for (i = 0; i < dev_priv->num_irqs; ++i) { + if (status & cur_irq->pending_mask) { + atomic_inc(&cur_irq->irq_received); + wake_up(&cur_irq->irq_queue); + handled = 1; + if (dev_priv->irq_map[drm_via_irq_dma0_td] == i) + via_dmablit_handler(dev, 0, 1); + else if (dev_priv->irq_map[drm_via_irq_dma1_td] == i) + via_dmablit_handler(dev, 1, 1); + } + cur_irq++; + } + + /* Acknowledge interrupts */ + via_write(dev_priv, VIA_REG_INTERRUPT, status); + + + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t *dev_priv) +{ + u32 status; + + if (dev_priv) { + /* Acknowledge interrupts */ + status = via_read(dev_priv, VIA_REG_INTERRUPT); + via_write(dev_priv, VIA_REG_INTERRUPT, status | + dev_priv->irq_pending_mask); + } +} + +static int via_enable_vblank(struct drm_device *dev, unsigned int pipe) +{ + drm_via_private_t *dev_priv = dev->dev_private; + u32 status; + + if (pipe != 0) { + DRM_ERROR("%s: bad crtc %u\n", __func__, pipe); + return -EINVAL; + } + + status = via_read(dev_priv, VIA_REG_INTERRUPT); + via_write(dev_priv, VIA_REG_INTERRUPT, status | VIA_IRQ_VBLANK_ENABLE); + + via_write8(dev_priv, 0x83d4, 0x11); + via_write8_mask(dev_priv, 0x83d5, 0x30, 0x30); + + return 0; +} + +static void via_disable_vblank(struct drm_device *dev, unsigned int pipe) +{ + drm_via_private_t *dev_priv = dev->dev_private; + u32 status; + + status = via_read(dev_priv, VIA_REG_INTERRUPT); + via_write(dev_priv, VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBLANK_ENABLE); + + via_write8(dev_priv, 0x83d4, 0x11); + via_write8_mask(dev_priv, 0x83d5, 0x30, 0); + + if (pipe != 0) + DRM_ERROR("%s: bad crtc %u\n", __func__, pipe); +} + +static int +via_driver_irq_wait(struct drm_device *dev, unsigned int irq, int force_sequence, + unsigned int *sequence) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + unsigned int cur_irq_sequence; + drm_via_irq_t *cur_irq; + int ret = 0; + maskarray_t *masks; + int real_irq; + + DRM_DEBUG("\n"); + + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } + + if (irq >= drm_via_irq_num) { + DRM_ERROR("Trying to wait on unknown irq %d\n", irq); + return -EINVAL; + } + + real_irq = dev_priv->irq_map[irq]; + + if (real_irq < 0) { + DRM_ERROR("Video IRQ %d not available on this hardware.\n", + irq); + return -EINVAL; + } + + masks = dev_priv->irq_masks; + cur_irq = dev_priv->via_irqs + real_irq; + + if (masks[real_irq][2] && !force_sequence) { + VIA_WAIT_ON(ret, cur_irq->irq_queue, 3 * HZ, + ((via_read(dev_priv, masks[irq][2]) & masks[irq][3]) == + masks[irq][4])); + cur_irq_sequence = atomic_read(&cur_irq->irq_received); + } else { + VIA_WAIT_ON(ret, cur_irq->irq_queue, 3 * HZ, + (((cur_irq_sequence = + atomic_read(&cur_irq->irq_received)) - + *sequence) <= (1 << 23))); + } + *sequence = cur_irq_sequence; + return ret; +} + + +/* + * drm_dma.h hooks + */ + +static void via_driver_irq_preinstall(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + u32 status; + drm_via_irq_t *cur_irq; + int i; + + DRM_DEBUG("dev_priv: %p\n", dev_priv); + if (dev_priv) { + cur_irq = dev_priv->via_irqs; + + dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE; + dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING; + + if (dev_priv->chipset == VIA_PRO_GROUP_A || + dev_priv->chipset == VIA_DX9_0) { + dev_priv->irq_masks = via_pro_group_a_irqs; + dev_priv->num_irqs = via_num_pro_group_a; + dev_priv->irq_map = via_irqmap_pro_group_a; + } else { + dev_priv->irq_masks = via_unichrome_irqs; + dev_priv->num_irqs = via_num_unichrome; + dev_priv->irq_map = via_irqmap_unichrome; + } + + for (i = 0; i < dev_priv->num_irqs; ++i) { + atomic_set(&cur_irq->irq_received, 0); + cur_irq->enable_mask = dev_priv->irq_masks[i][0]; + cur_irq->pending_mask = dev_priv->irq_masks[i][1]; + init_waitqueue_head(&cur_irq->irq_queue); + dev_priv->irq_enable_mask |= cur_irq->enable_mask; + dev_priv->irq_pending_mask |= cur_irq->pending_mask; + cur_irq++; + + DRM_DEBUG("Initializing IRQ %d\n", i); + } + + dev_priv->last_vblank_valid = 0; + + /* Clear VSync interrupt regs */ + status = via_read(dev_priv, VIA_REG_INTERRUPT); + via_write(dev_priv, VIA_REG_INTERRUPT, status & + ~(dev_priv->irq_enable_mask)); + + /* Clear bits if they're already high */ + viadrv_acknowledge_irqs(dev_priv); + } +} + +static int via_driver_irq_postinstall(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + u32 status; + + DRM_DEBUG("fun: %s\n", __func__); + if (!dev_priv) + return -EINVAL; + + status = via_read(dev_priv, VIA_REG_INTERRUPT); + via_write(dev_priv, VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL + | dev_priv->irq_enable_mask); + + /* Some magic, oh for some data sheets ! */ + via_write8(dev_priv, 0x83d4, 0x11); + via_write8_mask(dev_priv, 0x83d5, 0x30, 0x30); + + return 0; +} + +static void via_driver_irq_uninstall(struct drm_device *dev) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + u32 status; + + DRM_DEBUG("\n"); + if (dev_priv) { + + /* Some more magic, oh for some data sheets ! */ + + via_write8(dev_priv, 0x83d4, 0x11); + via_write8_mask(dev_priv, 0x83d5, 0x30, 0); + + status = via_read(dev_priv, VIA_REG_INTERRUPT); + via_write(dev_priv, VIA_REG_INTERRUPT, status & + ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask)); + } +} + +static int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_irqwait_t *irqwait = data; + struct timespec64 now; + int ret = 0; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int force_sequence; + + if (irqwait->request.irq >= dev_priv->num_irqs) { + DRM_ERROR("Trying to wait on unknown irq %d\n", + irqwait->request.irq); + return -EINVAL; + } + + cur_irq += irqwait->request.irq; + + switch (irqwait->request.type & ~VIA_IRQ_FLAGS_MASK) { + case VIA_IRQ_RELATIVE: + irqwait->request.sequence += + atomic_read(&cur_irq->irq_received); + irqwait->request.type &= ~_DRM_VBLANK_RELATIVE; + break; + case VIA_IRQ_ABSOLUTE: + break; + default: + return -EINVAL; + } + + if (irqwait->request.type & VIA_IRQ_SIGNAL) { + DRM_ERROR("Signals on Via IRQs not implemented yet.\n"); + return -EINVAL; + } + + force_sequence = (irqwait->request.type & VIA_IRQ_FORCE_SEQUENCE); + + ret = via_driver_irq_wait(dev, irqwait->request.irq, force_sequence, + &irqwait->request.sequence); + ktime_get_ts64(&now); + irqwait->reply.tval_sec = now.tv_sec; + irqwait->reply.tval_usec = now.tv_nsec / NSEC_PER_USEC; + + return ret; +} + static void via_init_futex(drm_via_private_t *dev_priv) { unsigned int i; diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 312e86b850f6..a1bbe3d5247e 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -183,21 +183,12 @@ do { \ remove_wait_queue(&(queue), &entry); \ } while (0) -extern int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int via_init_context(struct drm_device *dev, int context); extern int via_do_cleanup_map(struct drm_device *dev); -extern u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe); -extern int via_enable_vblank(struct drm_device *dev, unsigned int pipe); -extern void via_disable_vblank(struct drm_device *dev, unsigned int pipe); - -extern irqreturn_t via_driver_irq_handler(int irq, void *arg); -extern void via_driver_irq_preinstall(struct drm_device *dev); -extern int via_driver_irq_postinstall(struct drm_device *dev); -extern void via_driver_irq_uninstall(struct drm_device *dev); extern int via_dma_cleanup(struct drm_device *dev); extern void via_init_command_verifier(void); diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c deleted file mode 100644 index faeae5d881fb..000000000000 --- a/drivers/gpu/drm/via/via_irq.c +++ /dev/null @@ -1,388 +0,0 @@ -/* via_irq.c - * - * Copyright 2004 BEAM Ltd. - * Copyright 2002 Tungsten Graphics, Inc. - * Copyright 2005 Thomas Hellstrom. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * BEAM LTD, TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Terry Barnaby - * Keith Whitwell - * Thomas Hellstrom - * - * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank - * interrupt, as well as an infrastructure to handle other interrupts of the chip. - * The refresh rate is also calculated for video playback sync purposes. - */ - -#include -#include -#include - -#include "via_drv.h" - -#define VIA_REG_INTERRUPT 0x200 - -/* VIA_REG_INTERRUPT */ -#define VIA_IRQ_GLOBAL (1 << 31) -#define VIA_IRQ_VBLANK_ENABLE (1 << 19) -#define VIA_IRQ_VBLANK_PENDING (1 << 3) -#define VIA_IRQ_HQV0_ENABLE (1 << 11) -#define VIA_IRQ_HQV1_ENABLE (1 << 25) -#define VIA_IRQ_HQV0_PENDING (1 << 9) -#define VIA_IRQ_HQV1_PENDING (1 << 10) -#define VIA_IRQ_DMA0_DD_ENABLE (1 << 20) -#define VIA_IRQ_DMA0_TD_ENABLE (1 << 21) -#define VIA_IRQ_DMA1_DD_ENABLE (1 << 22) -#define VIA_IRQ_DMA1_TD_ENABLE (1 << 23) -#define VIA_IRQ_DMA0_DD_PENDING (1 << 4) -#define VIA_IRQ_DMA0_TD_PENDING (1 << 5) -#define VIA_IRQ_DMA1_DD_PENDING (1 << 6) -#define VIA_IRQ_DMA1_TD_PENDING (1 << 7) - - -/* - * Device-specific IRQs go here. This type might need to be extended with - * the register if there are multiple IRQ control registers. - * Currently we activate the HQV interrupts of Unichrome Pro group A. - */ - -static maskarray_t via_pro_group_a_irqs[] = { - {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, - 0x00000000 }, - {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, - 0x00000000 }, - {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, - {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, -}; -static int via_num_pro_group_a = ARRAY_SIZE(via_pro_group_a_irqs); -static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3}; - -static maskarray_t via_unichrome_irqs[] = { - {VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}, - {VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1, - VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008} -}; -static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs); -static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; - - -u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe) -{ - drm_via_private_t *dev_priv = dev->dev_private; - - if (pipe != 0) - return 0; - - return atomic_read(&dev_priv->vbl_received); -} - -irqreturn_t via_driver_irq_handler(int irq, void *arg) -{ - struct drm_device *dev = (struct drm_device *) arg; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - int handled = 0; - ktime_t cur_vblank; - drm_via_irq_t *cur_irq = dev_priv->via_irqs; - int i; - - status = via_read(dev_priv, VIA_REG_INTERRUPT); - if (status & VIA_IRQ_VBLANK_PENDING) { - atomic_inc(&dev_priv->vbl_received); - if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) { - cur_vblank = ktime_get(); - if (dev_priv->last_vblank_valid) { - dev_priv->nsec_per_vblank = - ktime_sub(cur_vblank, - dev_priv->last_vblank) >> 4; - } - dev_priv->last_vblank = cur_vblank; - dev_priv->last_vblank_valid = 1; - } - if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) { - DRM_DEBUG("nsec per vblank is: %llu\n", - ktime_to_ns(dev_priv->nsec_per_vblank)); - } - drm_handle_vblank(dev, 0); - handled = 1; - } - - for (i = 0; i < dev_priv->num_irqs; ++i) { - if (status & cur_irq->pending_mask) { - atomic_inc(&cur_irq->irq_received); - wake_up(&cur_irq->irq_queue); - handled = 1; - if (dev_priv->irq_map[drm_via_irq_dma0_td] == i) - via_dmablit_handler(dev, 0, 1); - else if (dev_priv->irq_map[drm_via_irq_dma1_td] == i) - via_dmablit_handler(dev, 1, 1); - } - cur_irq++; - } - - /* Acknowledge interrupts */ - via_write(dev_priv, VIA_REG_INTERRUPT, status); - - - if (handled) - return IRQ_HANDLED; - else - return IRQ_NONE; -} - -static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t *dev_priv) -{ - u32 status; - - if (dev_priv) { - /* Acknowledge interrupts */ - status = via_read(dev_priv, VIA_REG_INTERRUPT); - via_write(dev_priv, VIA_REG_INTERRUPT, status | - dev_priv->irq_pending_mask); - } -} - -int via_enable_vblank(struct drm_device *dev, unsigned int pipe) -{ - drm_via_private_t *dev_priv = dev->dev_private; - u32 status; - - if (pipe != 0) { - DRM_ERROR("%s: bad crtc %u\n", __func__, pipe); - return -EINVAL; - } - - status = via_read(dev_priv, VIA_REG_INTERRUPT); - via_write(dev_priv, VIA_REG_INTERRUPT, status | VIA_IRQ_VBLANK_ENABLE); - - via_write8(dev_priv, 0x83d4, 0x11); - via_write8_mask(dev_priv, 0x83d5, 0x30, 0x30); - - return 0; -} - -void via_disable_vblank(struct drm_device *dev, unsigned int pipe) -{ - drm_via_private_t *dev_priv = dev->dev_private; - u32 status; - - status = via_read(dev_priv, VIA_REG_INTERRUPT); - via_write(dev_priv, VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBLANK_ENABLE); - - via_write8(dev_priv, 0x83d4, 0x11); - via_write8_mask(dev_priv, 0x83d5, 0x30, 0); - - if (pipe != 0) - DRM_ERROR("%s: bad crtc %u\n", __func__, pipe); -} - -static int -via_driver_irq_wait(struct drm_device *dev, unsigned int irq, int force_sequence, - unsigned int *sequence) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - unsigned int cur_irq_sequence; - drm_via_irq_t *cur_irq; - int ret = 0; - maskarray_t *masks; - int real_irq; - - DRM_DEBUG("\n"); - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - if (irq >= drm_via_irq_num) { - DRM_ERROR("Trying to wait on unknown irq %d\n", irq); - return -EINVAL; - } - - real_irq = dev_priv->irq_map[irq]; - - if (real_irq < 0) { - DRM_ERROR("Video IRQ %d not available on this hardware.\n", - irq); - return -EINVAL; - } - - masks = dev_priv->irq_masks; - cur_irq = dev_priv->via_irqs + real_irq; - - if (masks[real_irq][2] && !force_sequence) { - VIA_WAIT_ON(ret, cur_irq->irq_queue, 3 * HZ, - ((via_read(dev_priv, masks[irq][2]) & masks[irq][3]) == - masks[irq][4])); - cur_irq_sequence = atomic_read(&cur_irq->irq_received); - } else { - VIA_WAIT_ON(ret, cur_irq->irq_queue, 3 * HZ, - (((cur_irq_sequence = - atomic_read(&cur_irq->irq_received)) - - *sequence) <= (1 << 23))); - } - *sequence = cur_irq_sequence; - return ret; -} - - -/* - * drm_dma.h hooks - */ - -void via_driver_irq_preinstall(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - drm_via_irq_t *cur_irq; - int i; - - DRM_DEBUG("dev_priv: %p\n", dev_priv); - if (dev_priv) { - cur_irq = dev_priv->via_irqs; - - dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE; - dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING; - - if (dev_priv->chipset == VIA_PRO_GROUP_A || - dev_priv->chipset == VIA_DX9_0) { - dev_priv->irq_masks = via_pro_group_a_irqs; - dev_priv->num_irqs = via_num_pro_group_a; - dev_priv->irq_map = via_irqmap_pro_group_a; - } else { - dev_priv->irq_masks = via_unichrome_irqs; - dev_priv->num_irqs = via_num_unichrome; - dev_priv->irq_map = via_irqmap_unichrome; - } - - for (i = 0; i < dev_priv->num_irqs; ++i) { - atomic_set(&cur_irq->irq_received, 0); - cur_irq->enable_mask = dev_priv->irq_masks[i][0]; - cur_irq->pending_mask = dev_priv->irq_masks[i][1]; - init_waitqueue_head(&cur_irq->irq_queue); - dev_priv->irq_enable_mask |= cur_irq->enable_mask; - dev_priv->irq_pending_mask |= cur_irq->pending_mask; - cur_irq++; - - DRM_DEBUG("Initializing IRQ %d\n", i); - } - - dev_priv->last_vblank_valid = 0; - - /* Clear VSync interrupt regs */ - status = via_read(dev_priv, VIA_REG_INTERRUPT); - via_write(dev_priv, VIA_REG_INTERRUPT, status & - ~(dev_priv->irq_enable_mask)); - - /* Clear bits if they're already high */ - viadrv_acknowledge_irqs(dev_priv); - } -} - -int via_driver_irq_postinstall(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - - DRM_DEBUG("fun: %s\n", __func__); - if (!dev_priv) - return -EINVAL; - - status = via_read(dev_priv, VIA_REG_INTERRUPT); - via_write(dev_priv, VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL - | dev_priv->irq_enable_mask); - - /* Some magic, oh for some data sheets ! */ - via_write8(dev_priv, 0x83d4, 0x11); - via_write8_mask(dev_priv, 0x83d5, 0x30, 0x30); - - return 0; -} - -void via_driver_irq_uninstall(struct drm_device *dev) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status; - - DRM_DEBUG("\n"); - if (dev_priv) { - - /* Some more magic, oh for some data sheets ! */ - - via_write8(dev_priv, 0x83d4, 0x11); - via_write8_mask(dev_priv, 0x83d5, 0x30, 0); - - status = via_read(dev_priv, VIA_REG_INTERRUPT); - via_write(dev_priv, VIA_REG_INTERRUPT, status & - ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask)); - } -} - -int via_wait_irq(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_irqwait_t *irqwait = data; - struct timespec64 now; - int ret = 0; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_irq_t *cur_irq = dev_priv->via_irqs; - int force_sequence; - - if (irqwait->request.irq >= dev_priv->num_irqs) { - DRM_ERROR("Trying to wait on unknown irq %d\n", - irqwait->request.irq); - return -EINVAL; - } - - cur_irq += irqwait->request.irq; - - switch (irqwait->request.type & ~VIA_IRQ_FLAGS_MASK) { - case VIA_IRQ_RELATIVE: - irqwait->request.sequence += - atomic_read(&cur_irq->irq_received); - irqwait->request.type &= ~_DRM_VBLANK_RELATIVE; - break; - case VIA_IRQ_ABSOLUTE: - break; - default: - return -EINVAL; - } - - if (irqwait->request.type & VIA_IRQ_SIGNAL) { - DRM_ERROR("Signals on Via IRQs not implemented yet.\n"); - return -EINVAL; - } - - force_sequence = (irqwait->request.type & VIA_IRQ_FORCE_SEQUENCE); - - ret = via_driver_irq_wait(dev, irqwait->request.irq, force_sequence, - &irqwait->request.sequence); - ktime_get_ts64(&now); - irqwait->reply.tval_sec = now.tv_sec; - irqwait->reply.tval_usec = now.tv_nsec / NSEC_PER_USEC; - - return ret; -} -- cgit v1.2.3-59-g8ed1b From f99995a4d79b630d39541b1b053f4102f8594caf Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:56 +0200 Subject: drm/via: Embed via_dmablit in via_dri1 Embed some of the header file in via_drv.h and the rest in via_dri1.c While embedding deleted extra empty lines and functions that has no external users are made static. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-8-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dmablit.c | 807 -------------------------------------- drivers/gpu/drm/via/via_dmablit.h | 140 ------- drivers/gpu/drm/via/via_dri1.c | 786 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/via/via_drv.h | 56 ++- 5 files changed, 835 insertions(+), 956 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_dmablit.c delete mode 100644 drivers/gpu/drm/via/via_dmablit.h (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index dd75ea8a1423..66125b150478 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_dri1.o via_verifier.o via_dmablit.o +via-y := via_dri1.o via_verifier.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dmablit.c b/drivers/gpu/drm/via/via_dmablit.c deleted file mode 100644 index e016a4d62090..000000000000 --- a/drivers/gpu/drm/via/via_dmablit.c +++ /dev/null @@ -1,807 +0,0 @@ -/* via_dmablit.c -- PCI DMA BitBlt support for the VIA Unichrome/Pro - * - * Copyright (C) 2005 Thomas Hellstrom, All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Hellstrom. - * Partially based on code obtained from Digeo Inc. - */ - - -/* - * Unmaps the DMA mappings. - * FIXME: Is this a NoOp on x86? Also - * FIXME: What happens if this one is called and a pending blit has previously done - * the same DMA mappings? - */ - -#include -#include -#include -#include - -#include -#include - -#include "via_dmablit.h" -#include "via_drv.h" - -#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) -#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) -#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) - -typedef struct _drm_via_descriptor { - uint32_t mem_addr; - uint32_t dev_addr; - uint32_t size; - uint32_t next; -} drm_via_descriptor_t; - - -/* - * Unmap a DMA mapping. - */ - - - -static void -via_unmap_blit_from_device(struct pci_dev *pdev, drm_via_sg_info_t *vsg) -{ - int num_desc = vsg->num_desc; - unsigned cur_descriptor_page = num_desc / vsg->descriptors_per_page; - unsigned descriptor_this_page = num_desc % vsg->descriptors_per_page; - drm_via_descriptor_t *desc_ptr = vsg->desc_pages[cur_descriptor_page] + - descriptor_this_page; - dma_addr_t next = vsg->chain_start; - - while (num_desc--) { - if (descriptor_this_page-- == 0) { - cur_descriptor_page--; - descriptor_this_page = vsg->descriptors_per_page - 1; - desc_ptr = vsg->desc_pages[cur_descriptor_page] + - descriptor_this_page; - } - dma_unmap_single(&pdev->dev, next, sizeof(*desc_ptr), DMA_TO_DEVICE); - dma_unmap_page(&pdev->dev, desc_ptr->mem_addr, desc_ptr->size, vsg->direction); - next = (dma_addr_t) desc_ptr->next; - desc_ptr--; - } -} - -/* - * If mode = 0, count how many descriptors are needed. - * If mode = 1, Map the DMA pages for the device, put together and map also the descriptors. - * Descriptors are run in reverse order by the hardware because we are not allowed to update the - * 'next' field without syncing calls when the descriptor is already mapped. - */ - -static void -via_map_blit_for_device(struct pci_dev *pdev, - const drm_via_dmablit_t *xfer, - drm_via_sg_info_t *vsg, - int mode) -{ - unsigned cur_descriptor_page = 0; - unsigned num_descriptors_this_page = 0; - unsigned char *mem_addr = xfer->mem_addr; - unsigned char *cur_mem; - unsigned char *first_addr = (unsigned char *)VIA_PGDN(mem_addr); - uint32_t fb_addr = xfer->fb_addr; - uint32_t cur_fb; - unsigned long line_len; - unsigned remaining_len; - int num_desc = 0; - int cur_line; - dma_addr_t next = 0 | VIA_DMA_DPR_EC; - drm_via_descriptor_t *desc_ptr = NULL; - - if (mode == 1) - desc_ptr = vsg->desc_pages[cur_descriptor_page]; - - for (cur_line = 0; cur_line < xfer->num_lines; ++cur_line) { - - line_len = xfer->line_length; - cur_fb = fb_addr; - cur_mem = mem_addr; - - while (line_len > 0) { - - remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len); - line_len -= remaining_len; - - if (mode == 1) { - desc_ptr->mem_addr = - dma_map_page(&pdev->dev, - vsg->pages[VIA_PFN(cur_mem) - - VIA_PFN(first_addr)], - VIA_PGOFF(cur_mem), remaining_len, - vsg->direction); - desc_ptr->dev_addr = cur_fb; - - desc_ptr->size = remaining_len; - desc_ptr->next = (uint32_t) next; - next = dma_map_single(&pdev->dev, desc_ptr, sizeof(*desc_ptr), - DMA_TO_DEVICE); - desc_ptr++; - if (++num_descriptors_this_page >= vsg->descriptors_per_page) { - num_descriptors_this_page = 0; - desc_ptr = vsg->desc_pages[++cur_descriptor_page]; - } - } - - num_desc++; - cur_mem += remaining_len; - cur_fb += remaining_len; - } - - mem_addr += xfer->mem_stride; - fb_addr += xfer->fb_stride; - } - - if (mode == 1) { - vsg->chain_start = next; - vsg->state = dr_via_device_mapped; - } - vsg->num_desc = num_desc; -} - -/* - * Function that frees up all resources for a blit. It is usable even if the - * blit info has only been partially built as long as the status enum is consistent - * with the actual status of the used resources. - */ - - -static void -via_free_sg_info(struct pci_dev *pdev, drm_via_sg_info_t *vsg) -{ - int i; - - switch (vsg->state) { - case dr_via_device_mapped: - via_unmap_blit_from_device(pdev, vsg); - fallthrough; - case dr_via_desc_pages_alloc: - for (i = 0; i < vsg->num_desc_pages; ++i) { - if (vsg->desc_pages[i] != NULL) - free_page((unsigned long)vsg->desc_pages[i]); - } - kfree(vsg->desc_pages); - fallthrough; - case dr_via_pages_locked: - unpin_user_pages_dirty_lock(vsg->pages, vsg->num_pages, - (vsg->direction == DMA_FROM_DEVICE)); - fallthrough; - case dr_via_pages_alloc: - vfree(vsg->pages); - fallthrough; - default: - vsg->state = dr_via_sg_init; - } - vfree(vsg->bounce_buffer); - vsg->bounce_buffer = NULL; - vsg->free_on_sequence = 0; -} - -/* - * Fire a blit engine. - */ - -static void -via_fire_dmablit(struct drm_device *dev, drm_via_sg_info_t *vsg, int engine) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - - via_write(dev_priv, VIA_PCI_DMA_MAR0 + engine*0x10, 0); - via_write(dev_priv, VIA_PCI_DMA_DAR0 + engine*0x10, 0); - via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_DD | VIA_DMA_CSR_TD | - VIA_DMA_CSR_DE); - via_write(dev_priv, VIA_PCI_DMA_MR0 + engine*0x04, VIA_DMA_MR_CM | VIA_DMA_MR_TDIE); - via_write(dev_priv, VIA_PCI_DMA_BCR0 + engine*0x10, 0); - via_write(dev_priv, VIA_PCI_DMA_DPR0 + engine*0x10, vsg->chain_start); - wmb(); - via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_DE | VIA_DMA_CSR_TS); - via_read(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04); -} - -/* - * Obtain a page pointer array and lock all pages into system memory. A segmentation violation will - * occur here if the calling user does not have access to the submitted address. - */ - -static int -via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) -{ - int ret; - unsigned long first_pfn = VIA_PFN(xfer->mem_addr); - vsg->num_pages = VIA_PFN(xfer->mem_addr + (xfer->num_lines * xfer->mem_stride - 1)) - - first_pfn + 1; - - vsg->pages = vzalloc(array_size(sizeof(struct page *), vsg->num_pages)); - if (NULL == vsg->pages) - return -ENOMEM; - ret = pin_user_pages_fast((unsigned long)xfer->mem_addr, - vsg->num_pages, - vsg->direction == DMA_FROM_DEVICE ? FOLL_WRITE : 0, - vsg->pages); - if (ret != vsg->num_pages) { - if (ret < 0) - return ret; - vsg->state = dr_via_pages_locked; - return -EINVAL; - } - vsg->state = dr_via_pages_locked; - DRM_DEBUG("DMA pages locked\n"); - return 0; -} - -/* - * Allocate DMA capable memory for the blit descriptor chain, and an array that keeps track of the - * pages we allocate. We don't want to use kmalloc for the descriptor chain because it may be - * quite large for some blits, and pages don't need to be contiguous. - */ - -static int -via_alloc_desc_pages(drm_via_sg_info_t *vsg) -{ - int i; - - vsg->descriptors_per_page = PAGE_SIZE / sizeof(drm_via_descriptor_t); - vsg->num_desc_pages = (vsg->num_desc + vsg->descriptors_per_page - 1) / - vsg->descriptors_per_page; - - if (NULL == (vsg->desc_pages = kcalloc(vsg->num_desc_pages, sizeof(void *), GFP_KERNEL))) - return -ENOMEM; - - vsg->state = dr_via_desc_pages_alloc; - for (i = 0; i < vsg->num_desc_pages; ++i) { - if (NULL == (vsg->desc_pages[i] = - (drm_via_descriptor_t *) __get_free_page(GFP_KERNEL))) - return -ENOMEM; - } - DRM_DEBUG("Allocated %d pages for %d descriptors.\n", vsg->num_desc_pages, - vsg->num_desc); - return 0; -} - -static void -via_abort_dmablit(struct drm_device *dev, int engine) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - - via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TA); -} - -static void -via_dmablit_engine_off(struct drm_device *dev, int engine) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - - via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD | VIA_DMA_CSR_DD); -} - - - -/* - * The dmablit part of the IRQ handler. Trying to do only reasonably fast things here. - * The rest, like unmapping and freeing memory for done blits is done in a separate workqueue - * task. Basically the task of the interrupt handler is to submit a new blit to the engine, while - * the workqueue task takes care of processing associated with the old blit. - */ - -void -via_dmablit_handler(struct drm_device *dev, int engine, int from_irq) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - drm_via_blitq_t *blitq = dev_priv->blit_queues + engine; - int cur; - int done_transfer; - unsigned long irqsave = 0; - uint32_t status = 0; - - DRM_DEBUG("DMA blit handler called. engine = %d, from_irq = %d, blitq = 0x%lx\n", - engine, from_irq, (unsigned long) blitq); - - if (from_irq) - spin_lock(&blitq->blit_lock); - else - spin_lock_irqsave(&blitq->blit_lock, irqsave); - - done_transfer = blitq->is_active && - ((status = via_read(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04)) & VIA_DMA_CSR_TD); - done_transfer = done_transfer || (blitq->aborting && !(status & VIA_DMA_CSR_DE)); - - cur = blitq->cur; - if (done_transfer) { - - blitq->blits[cur]->aborted = blitq->aborting; - blitq->done_blit_handle++; - wake_up(blitq->blit_queue + cur); - - cur++; - if (cur >= VIA_NUM_BLIT_SLOTS) - cur = 0; - blitq->cur = cur; - - /* - * Clear transfer done flag. - */ - - via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD); - - blitq->is_active = 0; - blitq->aborting = 0; - schedule_work(&blitq->wq); - - } else if (blitq->is_active && time_after_eq(jiffies, blitq->end)) { - - /* - * Abort transfer after one second. - */ - - via_abort_dmablit(dev, engine); - blitq->aborting = 1; - blitq->end = jiffies + HZ; - } - - if (!blitq->is_active) { - if (blitq->num_outstanding) { - via_fire_dmablit(dev, blitq->blits[cur], engine); - blitq->is_active = 1; - blitq->cur = cur; - blitq->num_outstanding--; - blitq->end = jiffies + HZ; - if (!timer_pending(&blitq->poll_timer)) - mod_timer(&blitq->poll_timer, jiffies + 1); - } else { - if (timer_pending(&blitq->poll_timer)) - del_timer(&blitq->poll_timer); - via_dmablit_engine_off(dev, engine); - } - } - - if (from_irq) - spin_unlock(&blitq->blit_lock); - else - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); -} - - - -/* - * Check whether this blit is still active, performing necessary locking. - */ - -static int -via_dmablit_active(drm_via_blitq_t *blitq, int engine, uint32_t handle, wait_queue_head_t **queue) -{ - unsigned long irqsave; - uint32_t slot; - int active; - - spin_lock_irqsave(&blitq->blit_lock, irqsave); - - /* - * Allow for handle wraparounds. - */ - - active = ((blitq->done_blit_handle - handle) > (1 << 23)) && - ((blitq->cur_blit_handle - handle) <= (1 << 23)); - - if (queue && active) { - slot = handle - blitq->done_blit_handle + blitq->cur - 1; - if (slot >= VIA_NUM_BLIT_SLOTS) - slot -= VIA_NUM_BLIT_SLOTS; - *queue = blitq->blit_queue + slot; - } - - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); - - return active; -} - -/* - * Sync. Wait for at least three seconds for the blit to be performed. - */ - -static int -via_dmablit_sync(struct drm_device *dev, uint32_t handle, int engine) -{ - - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - drm_via_blitq_t *blitq = dev_priv->blit_queues + engine; - wait_queue_head_t *queue; - int ret = 0; - - if (via_dmablit_active(blitq, engine, handle, &queue)) { - VIA_WAIT_ON(ret, *queue, 3 * HZ, - !via_dmablit_active(blitq, engine, handle, NULL)); - } - DRM_DEBUG("DMA blit sync handle 0x%x engine %d returned %d\n", - handle, engine, ret); - - return ret; -} - - -/* - * A timer that regularly polls the blit engine in cases where we don't have interrupts: - * a) Broken hardware (typically those that don't have any video capture facility). - * b) Blit abort. The hardware doesn't send an interrupt when a blit is aborted. - * The timer and hardware IRQ's can and do work in parallel. If the hardware has - * irqs, it will shorten the latency somewhat. - */ - - - -static void -via_dmablit_timer(struct timer_list *t) -{ - drm_via_blitq_t *blitq = from_timer(blitq, t, poll_timer); - struct drm_device *dev = blitq->dev; - int engine = (int) - (blitq - ((drm_via_private_t *)dev->dev_private)->blit_queues); - - DRM_DEBUG("Polling timer called for engine %d, jiffies %lu\n", engine, - (unsigned long) jiffies); - - via_dmablit_handler(dev, engine, 0); - - if (!timer_pending(&blitq->poll_timer)) { - mod_timer(&blitq->poll_timer, jiffies + 1); - - /* - * Rerun handler to delete timer if engines are off, and - * to shorten abort latency. This is a little nasty. - */ - - via_dmablit_handler(dev, engine, 0); - - } -} - - - - -/* - * Workqueue task that frees data and mappings associated with a blit. - * Also wakes up waiting processes. Each of these tasks handles one - * blit engine only and may not be called on each interrupt. - */ - - -static void -via_dmablit_workqueue(struct work_struct *work) -{ - drm_via_blitq_t *blitq = container_of(work, drm_via_blitq_t, wq); - struct drm_device *dev = blitq->dev; - struct pci_dev *pdev = to_pci_dev(dev->dev); - unsigned long irqsave; - drm_via_sg_info_t *cur_sg; - int cur_released; - - - DRM_DEBUG("Workqueue task called for blit engine %ld\n", (unsigned long) - (blitq - ((drm_via_private_t *)dev->dev_private)->blit_queues)); - - spin_lock_irqsave(&blitq->blit_lock, irqsave); - - while (blitq->serviced != blitq->cur) { - - cur_released = blitq->serviced++; - - DRM_DEBUG("Releasing blit slot %d\n", cur_released); - - if (blitq->serviced >= VIA_NUM_BLIT_SLOTS) - blitq->serviced = 0; - - cur_sg = blitq->blits[cur_released]; - blitq->num_free++; - - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); - - wake_up(&blitq->busy_queue); - - via_free_sg_info(pdev, cur_sg); - kfree(cur_sg); - - spin_lock_irqsave(&blitq->blit_lock, irqsave); - } - - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); -} - - -/* - * Init all blit engines. Currently we use two, but some hardware have 4. - */ - - -void -via_init_dmablit(struct drm_device *dev) -{ - int i, j; - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - struct pci_dev *pdev = to_pci_dev(dev->dev); - drm_via_blitq_t *blitq; - - pci_set_master(pdev); - - for (i = 0; i < VIA_NUM_BLIT_ENGINES; ++i) { - blitq = dev_priv->blit_queues + i; - blitq->dev = dev; - blitq->cur_blit_handle = 0; - blitq->done_blit_handle = 0; - blitq->head = 0; - blitq->cur = 0; - blitq->serviced = 0; - blitq->num_free = VIA_NUM_BLIT_SLOTS - 1; - blitq->num_outstanding = 0; - blitq->is_active = 0; - blitq->aborting = 0; - spin_lock_init(&blitq->blit_lock); - for (j = 0; j < VIA_NUM_BLIT_SLOTS; ++j) - init_waitqueue_head(blitq->blit_queue + j); - init_waitqueue_head(&blitq->busy_queue); - INIT_WORK(&blitq->wq, via_dmablit_workqueue); - timer_setup(&blitq->poll_timer, via_dmablit_timer, 0); - } -} - -/* - * Build all info and do all mappings required for a blit. - */ - - -static int -via_build_sg_info(struct drm_device *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) -{ - struct pci_dev *pdev = to_pci_dev(dev->dev); - int draw = xfer->to_fb; - int ret = 0; - - vsg->direction = (draw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; - vsg->bounce_buffer = NULL; - - vsg->state = dr_via_sg_init; - - if (xfer->num_lines <= 0 || xfer->line_length <= 0) { - DRM_ERROR("Zero size bitblt.\n"); - return -EINVAL; - } - - /* - * Below check is a driver limitation, not a hardware one. We - * don't want to lock unused pages, and don't want to incoporate the - * extra logic of avoiding them. Make sure there are no. - * (Not a big limitation anyway.) - */ - - if ((xfer->mem_stride - xfer->line_length) > 2*PAGE_SIZE) { - DRM_ERROR("Too large system memory stride. Stride: %d, " - "Length: %d\n", xfer->mem_stride, xfer->line_length); - return -EINVAL; - } - - if ((xfer->mem_stride == xfer->line_length) && - (xfer->fb_stride == xfer->line_length)) { - xfer->mem_stride *= xfer->num_lines; - xfer->line_length = xfer->mem_stride; - xfer->fb_stride = xfer->mem_stride; - xfer->num_lines = 1; - } - - /* - * Don't lock an arbitrary large number of pages, since that causes a - * DOS security hole. - */ - - if (xfer->num_lines > 2048 || (xfer->num_lines*xfer->mem_stride > (2048*2048*4))) { - DRM_ERROR("Too large PCI DMA bitblt.\n"); - return -EINVAL; - } - - /* - * we allow a negative fb stride to allow flipping of images in - * transfer. - */ - - if (xfer->mem_stride < xfer->line_length || - abs(xfer->fb_stride) < xfer->line_length) { - DRM_ERROR("Invalid frame-buffer / memory stride.\n"); - return -EINVAL; - } - - /* - * A hardware bug seems to be worked around if system memory addresses start on - * 16 byte boundaries. This seems a bit restrictive however. VIA is contacted - * about this. Meanwhile, impose the following restrictions: - */ - -#ifdef VIA_BUGFREE - if ((((unsigned long)xfer->mem_addr & 3) != ((unsigned long)xfer->fb_addr & 3)) || - ((xfer->num_lines > 1) && ((xfer->mem_stride & 3) != (xfer->fb_stride & 3)))) { - DRM_ERROR("Invalid DRM bitblt alignment.\n"); - return -EINVAL; - } -#else - if ((((unsigned long)xfer->mem_addr & 15) || - ((unsigned long)xfer->fb_addr & 3)) || - ((xfer->num_lines > 1) && - ((xfer->mem_stride & 15) || (xfer->fb_stride & 3)))) { - DRM_ERROR("Invalid DRM bitblt alignment.\n"); - return -EINVAL; - } -#endif - - if (0 != (ret = via_lock_all_dma_pages(vsg, xfer))) { - DRM_ERROR("Could not lock DMA pages.\n"); - via_free_sg_info(pdev, vsg); - return ret; - } - - via_map_blit_for_device(pdev, xfer, vsg, 0); - if (0 != (ret = via_alloc_desc_pages(vsg))) { - DRM_ERROR("Could not allocate DMA descriptor pages.\n"); - via_free_sg_info(pdev, vsg); - return ret; - } - via_map_blit_for_device(pdev, xfer, vsg, 1); - - return 0; -} - - -/* - * Reserve one free slot in the blit queue. Will wait for one second for one - * to become available. Otherwise -EBUSY is returned. - */ - -static int -via_dmablit_grab_slot(drm_via_blitq_t *blitq, int engine) -{ - int ret = 0; - unsigned long irqsave; - - DRM_DEBUG("Num free is %d\n", blitq->num_free); - spin_lock_irqsave(&blitq->blit_lock, irqsave); - while (blitq->num_free == 0) { - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); - - VIA_WAIT_ON(ret, blitq->busy_queue, HZ, blitq->num_free > 0); - if (ret) - return (-EINTR == ret) ? -EAGAIN : ret; - - spin_lock_irqsave(&blitq->blit_lock, irqsave); - } - - blitq->num_free--; - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); - - return 0; -} - -/* - * Hand back a free slot if we changed our mind. - */ - -static void -via_dmablit_release_slot(drm_via_blitq_t *blitq) -{ - unsigned long irqsave; - - spin_lock_irqsave(&blitq->blit_lock, irqsave); - blitq->num_free++; - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); - wake_up(&blitq->busy_queue); -} - -/* - * Grab a free slot. Build blit info and queue a blit. - */ - - -static int -via_dmablit(struct drm_device *dev, drm_via_dmablit_t *xfer) -{ - drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; - drm_via_sg_info_t *vsg; - drm_via_blitq_t *blitq; - int ret; - int engine; - unsigned long irqsave; - - if (dev_priv == NULL) { - DRM_ERROR("Called without initialization.\n"); - return -EINVAL; - } - - engine = (xfer->to_fb) ? 0 : 1; - blitq = dev_priv->blit_queues + engine; - if (0 != (ret = via_dmablit_grab_slot(blitq, engine))) - return ret; - if (NULL == (vsg = kmalloc(sizeof(*vsg), GFP_KERNEL))) { - via_dmablit_release_slot(blitq); - return -ENOMEM; - } - if (0 != (ret = via_build_sg_info(dev, vsg, xfer))) { - via_dmablit_release_slot(blitq); - kfree(vsg); - return ret; - } - spin_lock_irqsave(&blitq->blit_lock, irqsave); - - blitq->blits[blitq->head++] = vsg; - if (blitq->head >= VIA_NUM_BLIT_SLOTS) - blitq->head = 0; - blitq->num_outstanding++; - xfer->sync.sync_handle = ++blitq->cur_blit_handle; - - spin_unlock_irqrestore(&blitq->blit_lock, irqsave); - xfer->sync.engine = engine; - - via_dmablit_handler(dev, engine, 0); - - return 0; -} - -/* - * Sync on a previously submitted blit. Note that the X server use signals extensively, and - * that there is a very big probability that this IOCTL will be interrupted by a signal. In that - * case it returns with -EAGAIN for the signal to be delivered. - * The caller should then reissue the IOCTL. This is similar to what is being done for drmGetLock(). - */ - -int -via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_blitsync_t *sync = data; - int err; - - if (sync->engine >= VIA_NUM_BLIT_ENGINES) - return -EINVAL; - - err = via_dmablit_sync(dev, sync->sync_handle, sync->engine); - - if (-EINTR == err) - err = -EAGAIN; - - return err; -} - - -/* - * Queue a blit and hand back a handle to be used for sync. This IOCTL may be interrupted by a signal - * while waiting for a free slot in the blit queue. In that case it returns with -EAGAIN and should - * be reissued. See the above IOCTL code. - */ - -int -via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv) -{ - drm_via_dmablit_t *xfer = data; - int err; - - err = via_dmablit(dev, xfer); - - return err; -} diff --git a/drivers/gpu/drm/via/via_dmablit.h b/drivers/gpu/drm/via/via_dmablit.h deleted file mode 100644 index 9b662a327cef..000000000000 --- a/drivers/gpu/drm/via/via_dmablit.h +++ /dev/null @@ -1,140 +0,0 @@ -/* via_dmablit.h -- PCI DMA BitBlt support for the VIA Unichrome/Pro - * - * Copyright 2005 Thomas Hellstrom. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: - * Thomas Hellstrom. - * Register info from Digeo Inc. - */ - -#ifndef _VIA_DMABLIT_H -#define _VIA_DMABLIT_H - -#include - -#define VIA_NUM_BLIT_ENGINES 2 -#define VIA_NUM_BLIT_SLOTS 8 - -struct _drm_via_descriptor; - -typedef struct _drm_via_sg_info { - struct page **pages; - unsigned long num_pages; - struct _drm_via_descriptor **desc_pages; - int num_desc_pages; - int num_desc; - enum dma_data_direction direction; - unsigned char *bounce_buffer; - dma_addr_t chain_start; - uint32_t free_on_sequence; - unsigned int descriptors_per_page; - int aborted; - enum { - dr_via_device_mapped, - dr_via_desc_pages_alloc, - dr_via_pages_locked, - dr_via_pages_alloc, - dr_via_sg_init - } state; -} drm_via_sg_info_t; - -typedef struct _drm_via_blitq { - struct drm_device *dev; - uint32_t cur_blit_handle; - uint32_t done_blit_handle; - unsigned serviced; - unsigned head; - unsigned cur; - unsigned num_free; - unsigned num_outstanding; - unsigned long end; - int aborting; - int is_active; - drm_via_sg_info_t *blits[VIA_NUM_BLIT_SLOTS]; - spinlock_t blit_lock; - wait_queue_head_t blit_queue[VIA_NUM_BLIT_SLOTS]; - wait_queue_head_t busy_queue; - struct work_struct wq; - struct timer_list poll_timer; -} drm_via_blitq_t; - - -/* - * PCI DMA Registers - * Channels 2 & 3 don't seem to be implemented in hardware. - */ - -#define VIA_PCI_DMA_MAR0 0xE40 /* Memory Address Register of Channel 0 */ -#define VIA_PCI_DMA_DAR0 0xE44 /* Device Address Register of Channel 0 */ -#define VIA_PCI_DMA_BCR0 0xE48 /* Byte Count Register of Channel 0 */ -#define VIA_PCI_DMA_DPR0 0xE4C /* Descriptor Pointer Register of Channel 0 */ - -#define VIA_PCI_DMA_MAR1 0xE50 /* Memory Address Register of Channel 1 */ -#define VIA_PCI_DMA_DAR1 0xE54 /* Device Address Register of Channel 1 */ -#define VIA_PCI_DMA_BCR1 0xE58 /* Byte Count Register of Channel 1 */ -#define VIA_PCI_DMA_DPR1 0xE5C /* Descriptor Pointer Register of Channel 1 */ - -#define VIA_PCI_DMA_MAR2 0xE60 /* Memory Address Register of Channel 2 */ -#define VIA_PCI_DMA_DAR2 0xE64 /* Device Address Register of Channel 2 */ -#define VIA_PCI_DMA_BCR2 0xE68 /* Byte Count Register of Channel 2 */ -#define VIA_PCI_DMA_DPR2 0xE6C /* Descriptor Pointer Register of Channel 2 */ - -#define VIA_PCI_DMA_MAR3 0xE70 /* Memory Address Register of Channel 3 */ -#define VIA_PCI_DMA_DAR3 0xE74 /* Device Address Register of Channel 3 */ -#define VIA_PCI_DMA_BCR3 0xE78 /* Byte Count Register of Channel 3 */ -#define VIA_PCI_DMA_DPR3 0xE7C /* Descriptor Pointer Register of Channel 3 */ - -#define VIA_PCI_DMA_MR0 0xE80 /* Mode Register of Channel 0 */ -#define VIA_PCI_DMA_MR1 0xE84 /* Mode Register of Channel 1 */ -#define VIA_PCI_DMA_MR2 0xE88 /* Mode Register of Channel 2 */ -#define VIA_PCI_DMA_MR3 0xE8C /* Mode Register of Channel 3 */ - -#define VIA_PCI_DMA_CSR0 0xE90 /* Command/Status Register of Channel 0 */ -#define VIA_PCI_DMA_CSR1 0xE94 /* Command/Status Register of Channel 1 */ -#define VIA_PCI_DMA_CSR2 0xE98 /* Command/Status Register of Channel 2 */ -#define VIA_PCI_DMA_CSR3 0xE9C /* Command/Status Register of Channel 3 */ - -#define VIA_PCI_DMA_PTR 0xEA0 /* Priority Type Register */ - -/* Define for DMA engine */ -/* DPR */ -#define VIA_DMA_DPR_EC (1<<1) /* end of chain */ -#define VIA_DMA_DPR_DDIE (1<<2) /* descriptor done interrupt enable */ -#define VIA_DMA_DPR_DT (1<<3) /* direction of transfer (RO) */ - -/* MR */ -#define VIA_DMA_MR_CM (1<<0) /* chaining mode */ -#define VIA_DMA_MR_TDIE (1<<1) /* transfer done interrupt enable */ -#define VIA_DMA_MR_HENDMACMD (1<<7) /* ? */ - -/* CSR */ -#define VIA_DMA_CSR_DE (1<<0) /* DMA enable */ -#define VIA_DMA_CSR_TS (1<<1) /* transfer start */ -#define VIA_DMA_CSR_TA (1<<2) /* transfer abort */ -#define VIA_DMA_CSR_TD (1<<3) /* transfer done */ -#define VIA_DMA_CSR_DD (1<<4) /* descriptor done */ -#define VIA_DMA_DPR_EC (1<<1) /* end of chain */ - - - -#endif diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index 32aae172c6f3..f8b1f837feb1 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -98,6 +99,62 @@ struct via_memblock { #define VIA_IRQ_DMA1_DD_PENDING (1 << 6) #define VIA_IRQ_DMA1_TD_PENDING (1 << 7) +/* + * PCI DMA Registers + * Channels 2 & 3 don't seem to be implemented in hardware. + */ + +#define VIA_PCI_DMA_MAR0 0xE40 /* Memory Address Register of Channel 0 */ +#define VIA_PCI_DMA_DAR0 0xE44 /* Device Address Register of Channel 0 */ +#define VIA_PCI_DMA_BCR0 0xE48 /* Byte Count Register of Channel 0 */ +#define VIA_PCI_DMA_DPR0 0xE4C /* Descriptor Pointer Register of Channel 0 */ + +#define VIA_PCI_DMA_MAR1 0xE50 /* Memory Address Register of Channel 1 */ +#define VIA_PCI_DMA_DAR1 0xE54 /* Device Address Register of Channel 1 */ +#define VIA_PCI_DMA_BCR1 0xE58 /* Byte Count Register of Channel 1 */ +#define VIA_PCI_DMA_DPR1 0xE5C /* Descriptor Pointer Register of Channel 1 */ + +#define VIA_PCI_DMA_MAR2 0xE60 /* Memory Address Register of Channel 2 */ +#define VIA_PCI_DMA_DAR2 0xE64 /* Device Address Register of Channel 2 */ +#define VIA_PCI_DMA_BCR2 0xE68 /* Byte Count Register of Channel 2 */ +#define VIA_PCI_DMA_DPR2 0xE6C /* Descriptor Pointer Register of Channel 2 */ + +#define VIA_PCI_DMA_MAR3 0xE70 /* Memory Address Register of Channel 3 */ +#define VIA_PCI_DMA_DAR3 0xE74 /* Device Address Register of Channel 3 */ +#define VIA_PCI_DMA_BCR3 0xE78 /* Byte Count Register of Channel 3 */ +#define VIA_PCI_DMA_DPR3 0xE7C /* Descriptor Pointer Register of Channel 3 */ + +#define VIA_PCI_DMA_MR0 0xE80 /* Mode Register of Channel 0 */ +#define VIA_PCI_DMA_MR1 0xE84 /* Mode Register of Channel 1 */ +#define VIA_PCI_DMA_MR2 0xE88 /* Mode Register of Channel 2 */ +#define VIA_PCI_DMA_MR3 0xE8C /* Mode Register of Channel 3 */ + +#define VIA_PCI_DMA_CSR0 0xE90 /* Command/Status Register of Channel 0 */ +#define VIA_PCI_DMA_CSR1 0xE94 /* Command/Status Register of Channel 1 */ +#define VIA_PCI_DMA_CSR2 0xE98 /* Command/Status Register of Channel 2 */ +#define VIA_PCI_DMA_CSR3 0xE9C /* Command/Status Register of Channel 3 */ + +#define VIA_PCI_DMA_PTR 0xEA0 /* Priority Type Register */ + +/* Define for DMA engine */ +/* DPR */ +#define VIA_DMA_DPR_EC (1<<1) /* end of chain */ +#define VIA_DMA_DPR_DDIE (1<<2) /* descriptor done interrupt enable */ +#define VIA_DMA_DPR_DT (1<<3) /* direction of transfer (RO) */ + +/* MR */ +#define VIA_DMA_MR_CM (1<<0) /* chaining mode */ +#define VIA_DMA_MR_TDIE (1<<1) /* transfer done interrupt enable */ +#define VIA_DMA_MR_HENDMACMD (1<<7) /* ? */ + +/* CSR */ +#define VIA_DMA_CSR_DE (1<<0) /* DMA enable */ +#define VIA_DMA_CSR_TS (1<<1) /* transfer start */ +#define VIA_DMA_CSR_TA (1<<2) /* transfer abort */ +#define VIA_DMA_CSR_TD (1<<3) /* transfer done */ +#define VIA_DMA_CSR_DD (1<<4) /* descriptor done */ +#define VIA_DMA_DPR_EC (1<<1) /* end of chain */ + /* * Device-specific IRQs go here. This type might need to be extended with * the register if there are multiple IRQ control registers. @@ -127,6 +184,735 @@ static int via_num_unichrome = ARRAY_SIZE(via_unichrome_irqs); static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1}; +/* + * Unmaps the DMA mappings. + * FIXME: Is this a NoOp on x86? Also + * FIXME: What happens if this one is called and a pending blit has previously done + * the same DMA mappings? + */ +#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK) +#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK) +#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT) + +typedef struct _drm_via_descriptor { + uint32_t mem_addr; + uint32_t dev_addr; + uint32_t size; + uint32_t next; +} drm_via_descriptor_t; + + +/* + * Unmap a DMA mapping. + */ +static void +via_unmap_blit_from_device(struct pci_dev *pdev, drm_via_sg_info_t *vsg) +{ + int num_desc = vsg->num_desc; + unsigned cur_descriptor_page = num_desc / vsg->descriptors_per_page; + unsigned descriptor_this_page = num_desc % vsg->descriptors_per_page; + drm_via_descriptor_t *desc_ptr = vsg->desc_pages[cur_descriptor_page] + + descriptor_this_page; + dma_addr_t next = vsg->chain_start; + + while (num_desc--) { + if (descriptor_this_page-- == 0) { + cur_descriptor_page--; + descriptor_this_page = vsg->descriptors_per_page - 1; + desc_ptr = vsg->desc_pages[cur_descriptor_page] + + descriptor_this_page; + } + dma_unmap_single(&pdev->dev, next, sizeof(*desc_ptr), DMA_TO_DEVICE); + dma_unmap_page(&pdev->dev, desc_ptr->mem_addr, desc_ptr->size, vsg->direction); + next = (dma_addr_t) desc_ptr->next; + desc_ptr--; + } +} + +/* + * If mode = 0, count how many descriptors are needed. + * If mode = 1, Map the DMA pages for the device, put together and map also the descriptors. + * Descriptors are run in reverse order by the hardware because we are not allowed to update the + * 'next' field without syncing calls when the descriptor is already mapped. + */ +static void +via_map_blit_for_device(struct pci_dev *pdev, + const drm_via_dmablit_t *xfer, + drm_via_sg_info_t *vsg, + int mode) +{ + unsigned cur_descriptor_page = 0; + unsigned num_descriptors_this_page = 0; + unsigned char *mem_addr = xfer->mem_addr; + unsigned char *cur_mem; + unsigned char *first_addr = (unsigned char *)VIA_PGDN(mem_addr); + uint32_t fb_addr = xfer->fb_addr; + uint32_t cur_fb; + unsigned long line_len; + unsigned remaining_len; + int num_desc = 0; + int cur_line; + dma_addr_t next = 0 | VIA_DMA_DPR_EC; + drm_via_descriptor_t *desc_ptr = NULL; + + if (mode == 1) + desc_ptr = vsg->desc_pages[cur_descriptor_page]; + + for (cur_line = 0; cur_line < xfer->num_lines; ++cur_line) { + + line_len = xfer->line_length; + cur_fb = fb_addr; + cur_mem = mem_addr; + + while (line_len > 0) { + + remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len); + line_len -= remaining_len; + + if (mode == 1) { + desc_ptr->mem_addr = + dma_map_page(&pdev->dev, + vsg->pages[VIA_PFN(cur_mem) - + VIA_PFN(first_addr)], + VIA_PGOFF(cur_mem), remaining_len, + vsg->direction); + desc_ptr->dev_addr = cur_fb; + + desc_ptr->size = remaining_len; + desc_ptr->next = (uint32_t) next; + next = dma_map_single(&pdev->dev, desc_ptr, sizeof(*desc_ptr), + DMA_TO_DEVICE); + desc_ptr++; + if (++num_descriptors_this_page >= vsg->descriptors_per_page) { + num_descriptors_this_page = 0; + desc_ptr = vsg->desc_pages[++cur_descriptor_page]; + } + } + + num_desc++; + cur_mem += remaining_len; + cur_fb += remaining_len; + } + + mem_addr += xfer->mem_stride; + fb_addr += xfer->fb_stride; + } + + if (mode == 1) { + vsg->chain_start = next; + vsg->state = dr_via_device_mapped; + } + vsg->num_desc = num_desc; +} + +/* + * Function that frees up all resources for a blit. It is usable even if the + * blit info has only been partially built as long as the status enum is consistent + * with the actual status of the used resources. + */ +static void +via_free_sg_info(struct pci_dev *pdev, drm_via_sg_info_t *vsg) +{ + int i; + + switch (vsg->state) { + case dr_via_device_mapped: + via_unmap_blit_from_device(pdev, vsg); + fallthrough; + case dr_via_desc_pages_alloc: + for (i = 0; i < vsg->num_desc_pages; ++i) { + if (vsg->desc_pages[i] != NULL) + free_page((unsigned long)vsg->desc_pages[i]); + } + kfree(vsg->desc_pages); + fallthrough; + case dr_via_pages_locked: + unpin_user_pages_dirty_lock(vsg->pages, vsg->num_pages, + (vsg->direction == DMA_FROM_DEVICE)); + fallthrough; + case dr_via_pages_alloc: + vfree(vsg->pages); + fallthrough; + default: + vsg->state = dr_via_sg_init; + } + vfree(vsg->bounce_buffer); + vsg->bounce_buffer = NULL; + vsg->free_on_sequence = 0; +} + +/* + * Fire a blit engine. + */ +static void +via_fire_dmablit(struct drm_device *dev, drm_via_sg_info_t *vsg, int engine) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + + via_write(dev_priv, VIA_PCI_DMA_MAR0 + engine*0x10, 0); + via_write(dev_priv, VIA_PCI_DMA_DAR0 + engine*0x10, 0); + via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_DD | VIA_DMA_CSR_TD | + VIA_DMA_CSR_DE); + via_write(dev_priv, VIA_PCI_DMA_MR0 + engine*0x04, VIA_DMA_MR_CM | VIA_DMA_MR_TDIE); + via_write(dev_priv, VIA_PCI_DMA_BCR0 + engine*0x10, 0); + via_write(dev_priv, VIA_PCI_DMA_DPR0 + engine*0x10, vsg->chain_start); + wmb(); + via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_DE | VIA_DMA_CSR_TS); + via_read(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04); +} + +/* + * Obtain a page pointer array and lock all pages into system memory. A segmentation violation will + * occur here if the calling user does not have access to the submitted address. + */ +static int +via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) +{ + int ret; + unsigned long first_pfn = VIA_PFN(xfer->mem_addr); + vsg->num_pages = VIA_PFN(xfer->mem_addr + (xfer->num_lines * xfer->mem_stride - 1)) - + first_pfn + 1; + + vsg->pages = vzalloc(array_size(sizeof(struct page *), vsg->num_pages)); + if (NULL == vsg->pages) + return -ENOMEM; + ret = pin_user_pages_fast((unsigned long)xfer->mem_addr, + vsg->num_pages, + vsg->direction == DMA_FROM_DEVICE ? FOLL_WRITE : 0, + vsg->pages); + if (ret != vsg->num_pages) { + if (ret < 0) + return ret; + vsg->state = dr_via_pages_locked; + return -EINVAL; + } + vsg->state = dr_via_pages_locked; + DRM_DEBUG("DMA pages locked\n"); + return 0; +} + +/* + * Allocate DMA capable memory for the blit descriptor chain, and an array that keeps track of the + * pages we allocate. We don't want to use kmalloc for the descriptor chain because it may be + * quite large for some blits, and pages don't need to be contiguous. + */ +static int +via_alloc_desc_pages(drm_via_sg_info_t *vsg) +{ + int i; + + vsg->descriptors_per_page = PAGE_SIZE / sizeof(drm_via_descriptor_t); + vsg->num_desc_pages = (vsg->num_desc + vsg->descriptors_per_page - 1) / + vsg->descriptors_per_page; + + if (NULL == (vsg->desc_pages = kcalloc(vsg->num_desc_pages, sizeof(void *), GFP_KERNEL))) + return -ENOMEM; + + vsg->state = dr_via_desc_pages_alloc; + for (i = 0; i < vsg->num_desc_pages; ++i) { + if (NULL == (vsg->desc_pages[i] = + (drm_via_descriptor_t *) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + } + DRM_DEBUG("Allocated %d pages for %d descriptors.\n", vsg->num_desc_pages, + vsg->num_desc); + return 0; +} + +static void +via_abort_dmablit(struct drm_device *dev, int engine) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + + via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TA); +} + +static void +via_dmablit_engine_off(struct drm_device *dev, int engine) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + + via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD | VIA_DMA_CSR_DD); +} + +/* + * The dmablit part of the IRQ handler. Trying to do only reasonably fast things here. + * The rest, like unmapping and freeing memory for done blits is done in a separate workqueue + * task. Basically the task of the interrupt handler is to submit a new blit to the engine, while + * the workqueue task takes care of processing associated with the old blit. + */ +static void +via_dmablit_handler(struct drm_device *dev, int engine, int from_irq) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + drm_via_blitq_t *blitq = dev_priv->blit_queues + engine; + int cur; + int done_transfer; + unsigned long irqsave = 0; + uint32_t status = 0; + + DRM_DEBUG("DMA blit handler called. engine = %d, from_irq = %d, blitq = 0x%lx\n", + engine, from_irq, (unsigned long) blitq); + + if (from_irq) + spin_lock(&blitq->blit_lock); + else + spin_lock_irqsave(&blitq->blit_lock, irqsave); + + done_transfer = blitq->is_active && + ((status = via_read(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04)) & VIA_DMA_CSR_TD); + done_transfer = done_transfer || (blitq->aborting && !(status & VIA_DMA_CSR_DE)); + + cur = blitq->cur; + if (done_transfer) { + + blitq->blits[cur]->aborted = blitq->aborting; + blitq->done_blit_handle++; + wake_up(blitq->blit_queue + cur); + + cur++; + if (cur >= VIA_NUM_BLIT_SLOTS) + cur = 0; + blitq->cur = cur; + + /* + * Clear transfer done flag. + */ + + via_write(dev_priv, VIA_PCI_DMA_CSR0 + engine*0x04, VIA_DMA_CSR_TD); + + blitq->is_active = 0; + blitq->aborting = 0; + schedule_work(&blitq->wq); + + } else if (blitq->is_active && time_after_eq(jiffies, blitq->end)) { + + /* + * Abort transfer after one second. + */ + + via_abort_dmablit(dev, engine); + blitq->aborting = 1; + blitq->end = jiffies + HZ; + } + + if (!blitq->is_active) { + if (blitq->num_outstanding) { + via_fire_dmablit(dev, blitq->blits[cur], engine); + blitq->is_active = 1; + blitq->cur = cur; + blitq->num_outstanding--; + blitq->end = jiffies + HZ; + if (!timer_pending(&blitq->poll_timer)) + mod_timer(&blitq->poll_timer, jiffies + 1); + } else { + if (timer_pending(&blitq->poll_timer)) + del_timer(&blitq->poll_timer); + via_dmablit_engine_off(dev, engine); + } + } + + if (from_irq) + spin_unlock(&blitq->blit_lock); + else + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); +} + +/* + * Check whether this blit is still active, performing necessary locking. + */ +static int +via_dmablit_active(drm_via_blitq_t *blitq, int engine, uint32_t handle, wait_queue_head_t **queue) +{ + unsigned long irqsave; + uint32_t slot; + int active; + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + + /* + * Allow for handle wraparounds. + */ + + active = ((blitq->done_blit_handle - handle) > (1 << 23)) && + ((blitq->cur_blit_handle - handle) <= (1 << 23)); + + if (queue && active) { + slot = handle - blitq->done_blit_handle + blitq->cur - 1; + if (slot >= VIA_NUM_BLIT_SLOTS) + slot -= VIA_NUM_BLIT_SLOTS; + *queue = blitq->blit_queue + slot; + } + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + + return active; +} + +/* + * Sync. Wait for at least three seconds for the blit to be performed. + */ +static int +via_dmablit_sync(struct drm_device *dev, uint32_t handle, int engine) +{ + + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + drm_via_blitq_t *blitq = dev_priv->blit_queues + engine; + wait_queue_head_t *queue; + int ret = 0; + + if (via_dmablit_active(blitq, engine, handle, &queue)) { + VIA_WAIT_ON(ret, *queue, 3 * HZ, + !via_dmablit_active(blitq, engine, handle, NULL)); + } + DRM_DEBUG("DMA blit sync handle 0x%x engine %d returned %d\n", + handle, engine, ret); + + return ret; +} + +/* + * A timer that regularly polls the blit engine in cases where we don't have interrupts: + * a) Broken hardware (typically those that don't have any video capture facility). + * b) Blit abort. The hardware doesn't send an interrupt when a blit is aborted. + * The timer and hardware IRQ's can and do work in parallel. If the hardware has + * irqs, it will shorten the latency somewhat. + */ +static void +via_dmablit_timer(struct timer_list *t) +{ + drm_via_blitq_t *blitq = from_timer(blitq, t, poll_timer); + struct drm_device *dev = blitq->dev; + int engine = (int) + (blitq - ((drm_via_private_t *)dev->dev_private)->blit_queues); + + DRM_DEBUG("Polling timer called for engine %d, jiffies %lu\n", engine, + (unsigned long) jiffies); + + via_dmablit_handler(dev, engine, 0); + + if (!timer_pending(&blitq->poll_timer)) { + mod_timer(&blitq->poll_timer, jiffies + 1); + + /* + * Rerun handler to delete timer if engines are off, and + * to shorten abort latency. This is a little nasty. + */ + + via_dmablit_handler(dev, engine, 0); + + } +} + +/* + * Workqueue task that frees data and mappings associated with a blit. + * Also wakes up waiting processes. Each of these tasks handles one + * blit engine only and may not be called on each interrupt. + */ +static void +via_dmablit_workqueue(struct work_struct *work) +{ + drm_via_blitq_t *blitq = container_of(work, drm_via_blitq_t, wq); + struct drm_device *dev = blitq->dev; + struct pci_dev *pdev = to_pci_dev(dev->dev); + unsigned long irqsave; + drm_via_sg_info_t *cur_sg; + int cur_released; + + + DRM_DEBUG("Workqueue task called for blit engine %ld\n", (unsigned long) + (blitq - ((drm_via_private_t *)dev->dev_private)->blit_queues)); + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + + while (blitq->serviced != blitq->cur) { + + cur_released = blitq->serviced++; + + DRM_DEBUG("Releasing blit slot %d\n", cur_released); + + if (blitq->serviced >= VIA_NUM_BLIT_SLOTS) + blitq->serviced = 0; + + cur_sg = blitq->blits[cur_released]; + blitq->num_free++; + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + + wake_up(&blitq->busy_queue); + + via_free_sg_info(pdev, cur_sg); + kfree(cur_sg); + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + } + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); +} + +/* + * Init all blit engines. Currently we use two, but some hardware have 4. + */ +static void +via_init_dmablit(struct drm_device *dev) +{ + int i, j; + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + struct pci_dev *pdev = to_pci_dev(dev->dev); + drm_via_blitq_t *blitq; + + pci_set_master(pdev); + + for (i = 0; i < VIA_NUM_BLIT_ENGINES; ++i) { + blitq = dev_priv->blit_queues + i; + blitq->dev = dev; + blitq->cur_blit_handle = 0; + blitq->done_blit_handle = 0; + blitq->head = 0; + blitq->cur = 0; + blitq->serviced = 0; + blitq->num_free = VIA_NUM_BLIT_SLOTS - 1; + blitq->num_outstanding = 0; + blitq->is_active = 0; + blitq->aborting = 0; + spin_lock_init(&blitq->blit_lock); + for (j = 0; j < VIA_NUM_BLIT_SLOTS; ++j) + init_waitqueue_head(blitq->blit_queue + j); + init_waitqueue_head(&blitq->busy_queue); + INIT_WORK(&blitq->wq, via_dmablit_workqueue); + timer_setup(&blitq->poll_timer, via_dmablit_timer, 0); + } +} + +/* + * Build all info and do all mappings required for a blit. + */ +static int +via_build_sg_info(struct drm_device *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + int draw = xfer->to_fb; + int ret = 0; + + vsg->direction = (draw) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + vsg->bounce_buffer = NULL; + + vsg->state = dr_via_sg_init; + + if (xfer->num_lines <= 0 || xfer->line_length <= 0) { + DRM_ERROR("Zero size bitblt.\n"); + return -EINVAL; + } + + /* + * Below check is a driver limitation, not a hardware one. We + * don't want to lock unused pages, and don't want to incoporate the + * extra logic of avoiding them. Make sure there are no. + * (Not a big limitation anyway.) + */ + + if ((xfer->mem_stride - xfer->line_length) > 2*PAGE_SIZE) { + DRM_ERROR("Too large system memory stride. Stride: %d, " + "Length: %d\n", xfer->mem_stride, xfer->line_length); + return -EINVAL; + } + + if ((xfer->mem_stride == xfer->line_length) && + (xfer->fb_stride == xfer->line_length)) { + xfer->mem_stride *= xfer->num_lines; + xfer->line_length = xfer->mem_stride; + xfer->fb_stride = xfer->mem_stride; + xfer->num_lines = 1; + } + + /* + * Don't lock an arbitrary large number of pages, since that causes a + * DOS security hole. + */ + + if (xfer->num_lines > 2048 || (xfer->num_lines*xfer->mem_stride > (2048*2048*4))) { + DRM_ERROR("Too large PCI DMA bitblt.\n"); + return -EINVAL; + } + + /* + * we allow a negative fb stride to allow flipping of images in + * transfer. + */ + + if (xfer->mem_stride < xfer->line_length || + abs(xfer->fb_stride) < xfer->line_length) { + DRM_ERROR("Invalid frame-buffer / memory stride.\n"); + return -EINVAL; + } + + /* + * A hardware bug seems to be worked around if system memory addresses start on + * 16 byte boundaries. This seems a bit restrictive however. VIA is contacted + * about this. Meanwhile, impose the following restrictions: + */ + +#ifdef VIA_BUGFREE + if ((((unsigned long)xfer->mem_addr & 3) != ((unsigned long)xfer->fb_addr & 3)) || + ((xfer->num_lines > 1) && ((xfer->mem_stride & 3) != (xfer->fb_stride & 3)))) { + DRM_ERROR("Invalid DRM bitblt alignment.\n"); + return -EINVAL; + } +#else + if ((((unsigned long)xfer->mem_addr & 15) || + ((unsigned long)xfer->fb_addr & 3)) || + ((xfer->num_lines > 1) && + ((xfer->mem_stride & 15) || (xfer->fb_stride & 3)))) { + DRM_ERROR("Invalid DRM bitblt alignment.\n"); + return -EINVAL; + } +#endif + + if (0 != (ret = via_lock_all_dma_pages(vsg, xfer))) { + DRM_ERROR("Could not lock DMA pages.\n"); + via_free_sg_info(pdev, vsg); + return ret; + } + + via_map_blit_for_device(pdev, xfer, vsg, 0); + if (0 != (ret = via_alloc_desc_pages(vsg))) { + DRM_ERROR("Could not allocate DMA descriptor pages.\n"); + via_free_sg_info(pdev, vsg); + return ret; + } + via_map_blit_for_device(pdev, xfer, vsg, 1); + + return 0; +} + +/* + * Reserve one free slot in the blit queue. Will wait for one second for one + * to become available. Otherwise -EBUSY is returned. + */ +static int +via_dmablit_grab_slot(drm_via_blitq_t *blitq, int engine) +{ + int ret = 0; + unsigned long irqsave; + + DRM_DEBUG("Num free is %d\n", blitq->num_free); + spin_lock_irqsave(&blitq->blit_lock, irqsave); + while (blitq->num_free == 0) { + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + + VIA_WAIT_ON(ret, blitq->busy_queue, HZ, blitq->num_free > 0); + if (ret) + return (-EINTR == ret) ? -EAGAIN : ret; + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + } + + blitq->num_free--; + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + + return 0; +} + +/* + * Hand back a free slot if we changed our mind. + */ +static void +via_dmablit_release_slot(drm_via_blitq_t *blitq) +{ + unsigned long irqsave; + + spin_lock_irqsave(&blitq->blit_lock, irqsave); + blitq->num_free++; + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + wake_up(&blitq->busy_queue); +} + +/* + * Grab a free slot. Build blit info and queue a blit. + */ +static int +via_dmablit(struct drm_device *dev, drm_via_dmablit_t *xfer) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private; + drm_via_sg_info_t *vsg; + drm_via_blitq_t *blitq; + int ret; + int engine; + unsigned long irqsave; + + if (dev_priv == NULL) { + DRM_ERROR("Called without initialization.\n"); + return -EINVAL; + } + + engine = (xfer->to_fb) ? 0 : 1; + blitq = dev_priv->blit_queues + engine; + if (0 != (ret = via_dmablit_grab_slot(blitq, engine))) + return ret; + if (NULL == (vsg = kmalloc(sizeof(*vsg), GFP_KERNEL))) { + via_dmablit_release_slot(blitq); + return -ENOMEM; + } + if (0 != (ret = via_build_sg_info(dev, vsg, xfer))) { + via_dmablit_release_slot(blitq); + kfree(vsg); + return ret; + } + spin_lock_irqsave(&blitq->blit_lock, irqsave); + + blitq->blits[blitq->head++] = vsg; + if (blitq->head >= VIA_NUM_BLIT_SLOTS) + blitq->head = 0; + blitq->num_outstanding++; + xfer->sync.sync_handle = ++blitq->cur_blit_handle; + + spin_unlock_irqrestore(&blitq->blit_lock, irqsave); + xfer->sync.engine = engine; + + via_dmablit_handler(dev, engine, 0); + + return 0; +} + +/* + * Sync on a previously submitted blit. Note that the X server use signals extensively, and + * that there is a very big probability that this IOCTL will be interrupted by a signal. In that + * case it returns with -EAGAIN for the signal to be delivered. + * The caller should then reissue the IOCTL. This is similar to what is being done for drmGetLock(). + */ +static int +via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_blitsync_t *sync = data; + int err; + + if (sync->engine >= VIA_NUM_BLIT_ENGINES) + return -EINVAL; + + err = via_dmablit_sync(dev, sync->sync_handle, sync->engine); + + if (-EINTR == err) + err = -EAGAIN; + + return err; +} + +/* + * Queue a blit and hand back a handle to be used for sync. This IOCTL may be interrupted by a signal + * while waiting for a free slot in the blit queue. In that case it returns with -EAGAIN and should + * be reissued. See the above IOCTL code. + */ +static int +via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv) +{ + drm_via_dmablit_t *xfer = data; + int err; + + err = via_dmablit(dev, xfer); + + return err; +} + static u32 via_get_vblank_counter(struct drm_device *dev, unsigned int pipe) { drm_via_private_t *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index a1bbe3d5247e..6b26decfadb8 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -24,6 +24,7 @@ #ifndef _VIA_DRV_H_ #define _VIA_DRV_H_ +#include #include #include #include @@ -47,12 +48,57 @@ #include "via_verifier.h" -#include "via_dmablit.h" - #define VIA_PCI_BUF_SIZE 60000 #define VIA_FIRE_BUF_SIZE 1024 #define VIA_NUM_IRQS 4 + +#define VIA_NUM_BLIT_ENGINES 2 +#define VIA_NUM_BLIT_SLOTS 8 + +struct _drm_via_descriptor; + +typedef struct _drm_via_sg_info { + struct page **pages; + unsigned long num_pages; + struct _drm_via_descriptor **desc_pages; + int num_desc_pages; + int num_desc; + enum dma_data_direction direction; + unsigned char *bounce_buffer; + dma_addr_t chain_start; + uint32_t free_on_sequence; + unsigned int descriptors_per_page; + int aborted; + enum { + dr_via_device_mapped, + dr_via_desc_pages_alloc, + dr_via_pages_locked, + dr_via_pages_alloc, + dr_via_sg_init + } state; +} drm_via_sg_info_t; + +typedef struct _drm_via_blitq { + struct drm_device *dev; + uint32_t cur_blit_handle; + uint32_t done_blit_handle; + unsigned serviced; + unsigned head; + unsigned cur; + unsigned num_free; + unsigned num_outstanding; + unsigned long end; + int aborting; + int is_active; + drm_via_sg_info_t *blits[VIA_NUM_BLIT_SLOTS]; + spinlock_t blit_lock; + wait_queue_head_t blit_queue[VIA_NUM_BLIT_SLOTS]; + wait_queue_head_t busy_queue; + struct work_struct wq; + struct timer_list poll_timer; +} drm_via_blitq_t; + typedef struct drm_via_ring_buffer { drm_local_map_t map; char *virtual_start; @@ -183,9 +229,6 @@ do { \ remove_wait_queue(&(queue), &entry); \ } while (0) -extern int via_dma_blit_sync(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern int via_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv); - extern int via_init_context(struct drm_device *dev, int context); extern int via_do_cleanup_map(struct drm_device *dev); @@ -194,7 +237,4 @@ extern int via_dma_cleanup(struct drm_device *dev); extern void via_init_command_verifier(void); extern int via_driver_dma_quiescent(struct drm_device *dev); -extern void via_dmablit_handler(struct drm_device *dev, int engine, int from_irq); -extern void via_init_dmablit(struct drm_device *dev); - #endif -- cgit v1.2.3-59-g8ed1b From 8f8ed7f8384f71f841bc70cbdbc2dbeeb7f4bcd5 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:57 +0200 Subject: drm/via: Embed via_verifier in via_dri1 Embed the header file in via_drv.h and the code in via_dri1. All functions are made static as there are no more external users. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-9-sam@ravnborg.org --- drivers/gpu/drm/via/Makefile | 2 +- drivers/gpu/drm/via/via_dri1.c | 1071 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/via/via_drv.h | 29 +- drivers/gpu/drm/via/via_verifier.c | 1110 ------------------------------------ drivers/gpu/drm/via/via_verifier.h | 62 -- 5 files changed, 1099 insertions(+), 1175 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_verifier.c delete mode 100644 drivers/gpu/drm/via/via_verifier.h (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index 66125b150478..8b978dd51a25 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -3,6 +3,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -via-y := via_dri1.o via_verifier.o +via-y := via_dri1.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index f8b1f837feb1..f9285cc3e1c7 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -201,7 +201,1078 @@ typedef struct _drm_via_descriptor { uint32_t next; } drm_via_descriptor_t; +typedef enum { + state_command, + state_header2, + state_header1, + state_vheader5, + state_vheader6, + state_error +} verifier_state_t; + +typedef enum { + no_check = 0, + check_for_header2, + check_for_header1, + check_for_header2_err, + check_for_header1_err, + check_for_fire, + check_z_buffer_addr0, + check_z_buffer_addr1, + check_z_buffer_addr_mode, + check_destination_addr0, + check_destination_addr1, + check_destination_addr_mode, + check_for_dummy, + check_for_dd, + check_texture_addr0, + check_texture_addr1, + check_texture_addr2, + check_texture_addr3, + check_texture_addr4, + check_texture_addr5, + check_texture_addr6, + check_texture_addr7, + check_texture_addr8, + check_texture_addr_mode, + check_for_vertex_count, + check_number_texunits, + forbidden_command +} hazard_t; +/* + * Associates each hazard above with a possible multi-command + * sequence. For example an address that is split over multiple + * commands and that needs to be checked at the first command + * that does not include any part of the address. + */ + +static drm_via_sequence_t seqs[] = { + no_sequence, + no_sequence, + no_sequence, + no_sequence, + no_sequence, + no_sequence, + z_address, + z_address, + z_address, + dest_address, + dest_address, + dest_address, + no_sequence, + no_sequence, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + tex_address, + no_sequence +}; + +typedef struct { + unsigned int code; + hazard_t hz; +} hz_init_t; + +static hz_init_t init_table1[] = { + {0xf2, check_for_header2_err}, + {0xf0, check_for_header1_err}, + {0xee, check_for_fire}, + {0xcc, check_for_dummy}, + {0xdd, check_for_dd}, + {0x00, no_check}, + {0x10, check_z_buffer_addr0}, + {0x11, check_z_buffer_addr1}, + {0x12, check_z_buffer_addr_mode}, + {0x13, no_check}, + {0x14, no_check}, + {0x15, no_check}, + {0x23, no_check}, + {0x24, no_check}, + {0x33, no_check}, + {0x34, no_check}, + {0x35, no_check}, + {0x36, no_check}, + {0x37, no_check}, + {0x38, no_check}, + {0x39, no_check}, + {0x3A, no_check}, + {0x3B, no_check}, + {0x3C, no_check}, + {0x3D, no_check}, + {0x3E, no_check}, + {0x40, check_destination_addr0}, + {0x41, check_destination_addr1}, + {0x42, check_destination_addr_mode}, + {0x43, no_check}, + {0x44, no_check}, + {0x50, no_check}, + {0x51, no_check}, + {0x52, no_check}, + {0x53, no_check}, + {0x54, no_check}, + {0x55, no_check}, + {0x56, no_check}, + {0x57, no_check}, + {0x58, no_check}, + {0x70, no_check}, + {0x71, no_check}, + {0x78, no_check}, + {0x79, no_check}, + {0x7A, no_check}, + {0x7B, no_check}, + {0x7C, no_check}, + {0x7D, check_for_vertex_count} +}; + +static hz_init_t init_table2[] = { + {0xf2, check_for_header2_err}, + {0xf0, check_for_header1_err}, + {0xee, check_for_fire}, + {0xcc, check_for_dummy}, + {0x00, check_texture_addr0}, + {0x01, check_texture_addr0}, + {0x02, check_texture_addr0}, + {0x03, check_texture_addr0}, + {0x04, check_texture_addr0}, + {0x05, check_texture_addr0}, + {0x06, check_texture_addr0}, + {0x07, check_texture_addr0}, + {0x08, check_texture_addr0}, + {0x09, check_texture_addr0}, + {0x20, check_texture_addr1}, + {0x21, check_texture_addr1}, + {0x22, check_texture_addr1}, + {0x23, check_texture_addr4}, + {0x2B, check_texture_addr3}, + {0x2C, check_texture_addr3}, + {0x2D, check_texture_addr3}, + {0x2E, check_texture_addr3}, + {0x2F, check_texture_addr3}, + {0x30, check_texture_addr3}, + {0x31, check_texture_addr3}, + {0x32, check_texture_addr3}, + {0x33, check_texture_addr3}, + {0x34, check_texture_addr3}, + {0x4B, check_texture_addr5}, + {0x4C, check_texture_addr6}, + {0x51, check_texture_addr7}, + {0x52, check_texture_addr8}, + {0x77, check_texture_addr2}, + {0x78, no_check}, + {0x79, no_check}, + {0x7A, no_check}, + {0x7B, check_texture_addr_mode}, + {0x7C, no_check}, + {0x7D, no_check}, + {0x7E, no_check}, + {0x7F, no_check}, + {0x80, no_check}, + {0x81, no_check}, + {0x82, no_check}, + {0x83, no_check}, + {0x85, no_check}, + {0x86, no_check}, + {0x87, no_check}, + {0x88, no_check}, + {0x89, no_check}, + {0x8A, no_check}, + {0x90, no_check}, + {0x91, no_check}, + {0x92, no_check}, + {0x93, no_check} +}; + +static hz_init_t init_table3[] = { + {0xf2, check_for_header2_err}, + {0xf0, check_for_header1_err}, + {0xcc, check_for_dummy}, + {0x00, check_number_texunits} +}; + +static hazard_t table1[256]; +static hazard_t table2[256]; +static hazard_t table3[256]; + +static __inline__ int +eat_words(const uint32_t **buf, const uint32_t *buf_end, unsigned num_words) +{ + if ((buf_end - *buf) >= num_words) { + *buf += num_words; + return 0; + } + DRM_ERROR("Illegal termination of DMA command buffer\n"); + return 1; +} + +/* + * Partially stolen from drm_memory.h + */ + +static __inline__ drm_local_map_t *via_drm_lookup_agp_map(drm_via_state_t *seq, + unsigned long offset, + unsigned long size, + struct drm_device *dev) +{ + struct drm_map_list *r_list; + drm_local_map_t *map = seq->map_cache; + + if (map && map->offset <= offset + && (offset + size) <= (map->offset + map->size)) { + return map; + } + + list_for_each_entry(r_list, &dev->maplist, head) { + map = r_list->map; + if (!map) + continue; + if (map->offset <= offset + && (offset + size) <= (map->offset + map->size) + && !(map->flags & _DRM_RESTRICTED) + && (map->type == _DRM_AGP)) { + seq->map_cache = map; + return map; + } + } + return NULL; +} + +/* + * Require that all AGP texture levels reside in the same AGP map which should + * be mappable by the client. This is not a big restriction. + * FIXME: To actually enforce this security policy strictly, drm_rmmap + * would have to wait for dma quiescent before removing an AGP map. + * The via_drm_lookup_agp_map call in reality seems to take + * very little CPU time. + */ + +static __inline__ int finish_current_sequence(drm_via_state_t * cur_seq) +{ + switch (cur_seq->unfinished) { + case z_address: + DRM_DEBUG("Z Buffer start address is 0x%x\n", cur_seq->z_addr); + break; + case dest_address: + DRM_DEBUG("Destination start address is 0x%x\n", + cur_seq->d_addr); + break; + case tex_address: + if (cur_seq->agp_texture) { + unsigned start = + cur_seq->tex_level_lo[cur_seq->texture]; + unsigned end = cur_seq->tex_level_hi[cur_seq->texture]; + unsigned long lo = ~0, hi = 0, tmp; + uint32_t *addr, *pitch, *height, tex; + unsigned i; + int npot; + + if (end > 9) + end = 9; + if (start > 9) + start = 9; + + addr = + &(cur_seq->t_addr[tex = cur_seq->texture][start]); + pitch = &(cur_seq->pitch[tex][start]); + height = &(cur_seq->height[tex][start]); + npot = cur_seq->tex_npot[tex]; + for (i = start; i <= end; ++i) { + tmp = *addr++; + if (tmp < lo) + lo = tmp; + if (i == 0 && npot) + tmp += (*height++ * *pitch++); + else + tmp += (*height++ << *pitch++); + if (tmp > hi) + hi = tmp; + } + + if (!via_drm_lookup_agp_map + (cur_seq, lo, hi - lo, cur_seq->dev)) { + DRM_ERROR + ("AGP texture is not in allowed map\n"); + return 2; + } + } + break; + default: + break; + } + cur_seq->unfinished = no_sequence; + return 0; +} + +static __inline__ int +investigate_hazard(uint32_t cmd, hazard_t hz, drm_via_state_t *cur_seq) +{ + register uint32_t tmp, *tmp_addr; + + if (cur_seq->unfinished && (cur_seq->unfinished != seqs[hz])) { + int ret; + if ((ret = finish_current_sequence(cur_seq))) + return ret; + } + + switch (hz) { + case check_for_header2: + if (cmd == HALCYON_HEADER2) + return 1; + return 0; + case check_for_header1: + if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + return 1; + return 0; + case check_for_header2_err: + if (cmd == HALCYON_HEADER2) + return 1; + DRM_ERROR("Illegal DMA HALCYON_HEADER2 command\n"); + break; + case check_for_header1_err: + if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + return 1; + DRM_ERROR("Illegal DMA HALCYON_HEADER1 command\n"); + break; + case check_for_fire: + if ((cmd & HALCYON_FIREMASK) == HALCYON_FIRECMD) + return 1; + DRM_ERROR("Illegal DMA HALCYON_FIRECMD command\n"); + break; + case check_for_dummy: + if (HC_DUMMY == cmd) + return 0; + DRM_ERROR("Illegal DMA HC_DUMMY command\n"); + break; + case check_for_dd: + if (0xdddddddd == cmd) + return 0; + DRM_ERROR("Illegal DMA 0xdddddddd command\n"); + break; + case check_z_buffer_addr0: + cur_seq->unfinished = z_address; + cur_seq->z_addr = (cur_seq->z_addr & 0xFF000000) | + (cmd & 0x00FFFFFF); + return 0; + case check_z_buffer_addr1: + cur_seq->unfinished = z_address; + cur_seq->z_addr = (cur_seq->z_addr & 0x00FFFFFF) | + ((cmd & 0xFF) << 24); + return 0; + case check_z_buffer_addr_mode: + cur_seq->unfinished = z_address; + if ((cmd & 0x0000C000) == 0) + return 0; + DRM_ERROR("Attempt to place Z buffer in system memory\n"); + return 2; + case check_destination_addr0: + cur_seq->unfinished = dest_address; + cur_seq->d_addr = (cur_seq->d_addr & 0xFF000000) | + (cmd & 0x00FFFFFF); + return 0; + case check_destination_addr1: + cur_seq->unfinished = dest_address; + cur_seq->d_addr = (cur_seq->d_addr & 0x00FFFFFF) | + ((cmd & 0xFF) << 24); + return 0; + case check_destination_addr_mode: + cur_seq->unfinished = dest_address; + if ((cmd & 0x0000C000) == 0) + return 0; + DRM_ERROR + ("Attempt to place 3D drawing buffer in system memory\n"); + return 2; + case check_texture_addr0: + cur_seq->unfinished = tex_address; + tmp = (cmd >> 24); + tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp]; + *tmp_addr = (*tmp_addr & 0xFF000000) | (cmd & 0x00FFFFFF); + return 0; + case check_texture_addr1: + cur_seq->unfinished = tex_address; + tmp = ((cmd >> 24) - 0x20); + tmp += tmp << 1; + tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp]; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24); + tmp_addr++; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF00) << 16); + tmp_addr++; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF0000) << 8); + return 0; + case check_texture_addr2: + cur_seq->unfinished = tex_address; + cur_seq->tex_level_lo[tmp = cur_seq->texture] = cmd & 0x3F; + cur_seq->tex_level_hi[tmp] = (cmd & 0xFC0) >> 6; + return 0; + case check_texture_addr3: + cur_seq->unfinished = tex_address; + tmp = ((cmd >> 24) - HC_SubA_HTXnL0Pit); + if (tmp == 0 && + (cmd & HC_HTXnEnPit_MASK)) { + cur_seq->pitch[cur_seq->texture][tmp] = + (cmd & HC_HTXnLnPit_MASK); + cur_seq->tex_npot[cur_seq->texture] = 1; + } else { + cur_seq->pitch[cur_seq->texture][tmp] = + (cmd & HC_HTXnLnPitE_MASK) >> HC_HTXnLnPitE_SHIFT; + cur_seq->tex_npot[cur_seq->texture] = 0; + if (cmd & 0x000FFFFF) { + DRM_ERROR + ("Unimplemented texture level 0 pitch mode.\n"); + return 2; + } + } + return 0; + case check_texture_addr4: + cur_seq->unfinished = tex_address; + tmp_addr = &cur_seq->t_addr[cur_seq->texture][9]; + *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24); + return 0; + case check_texture_addr5: + case check_texture_addr6: + cur_seq->unfinished = tex_address; + /* + * Texture width. We don't care since we have the pitch. + */ + return 0; + case check_texture_addr7: + cur_seq->unfinished = tex_address; + tmp_addr = &(cur_seq->height[cur_seq->texture][0]); + tmp_addr[5] = 1 << ((cmd & 0x00F00000) >> 20); + tmp_addr[4] = 1 << ((cmd & 0x000F0000) >> 16); + tmp_addr[3] = 1 << ((cmd & 0x0000F000) >> 12); + tmp_addr[2] = 1 << ((cmd & 0x00000F00) >> 8); + tmp_addr[1] = 1 << ((cmd & 0x000000F0) >> 4); + tmp_addr[0] = 1 << (cmd & 0x0000000F); + return 0; + case check_texture_addr8: + cur_seq->unfinished = tex_address; + tmp_addr = &(cur_seq->height[cur_seq->texture][0]); + tmp_addr[9] = 1 << ((cmd & 0x0000F000) >> 12); + tmp_addr[8] = 1 << ((cmd & 0x00000F00) >> 8); + tmp_addr[7] = 1 << ((cmd & 0x000000F0) >> 4); + tmp_addr[6] = 1 << (cmd & 0x0000000F); + return 0; + case check_texture_addr_mode: + cur_seq->unfinished = tex_address; + if (2 == (tmp = cmd & 0x00000003)) { + DRM_ERROR + ("Attempt to fetch texture from system memory.\n"); + return 2; + } + cur_seq->agp_texture = (tmp == 3); + cur_seq->tex_palette_size[cur_seq->texture] = + (cmd >> 16) & 0x000000007; + return 0; + case check_for_vertex_count: + cur_seq->vertex_count = cmd & 0x0000FFFF; + return 0; + case check_number_texunits: + cur_seq->multitex = (cmd >> 3) & 1; + return 0; + default: + DRM_ERROR("Illegal DMA data: 0x%x\n", cmd); + return 2; + } + return 2; +} + +static __inline__ int +via_check_prim_list(uint32_t const **buffer, const uint32_t * buf_end, + drm_via_state_t *cur_seq) +{ + drm_via_private_t *dev_priv = + (drm_via_private_t *) cur_seq->dev->dev_private; + uint32_t a_fire, bcmd, dw_count; + int ret = 0; + int have_fire; + const uint32_t *buf = *buffer; + + while (buf < buf_end) { + have_fire = 0; + if ((buf_end - buf) < 2) { + DRM_ERROR + ("Unexpected termination of primitive list.\n"); + ret = 1; + break; + } + if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdB) + break; + bcmd = *buf++; + if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdA) { + DRM_ERROR("Expected Vertex List A command, got 0x%x\n", + *buf); + ret = 1; + break; + } + a_fire = + *buf++ | HC_HPLEND_MASK | HC_HPMValidN_MASK | + HC_HE3Fire_MASK; + + /* + * How many dwords per vertex ? + */ + + if (cur_seq->agp && ((bcmd & (0xF << 11)) == 0)) { + DRM_ERROR("Illegal B command vertex data for AGP.\n"); + ret = 1; + break; + } + + dw_count = 0; + if (bcmd & (1 << 7)) + dw_count += (cur_seq->multitex) ? 2 : 1; + if (bcmd & (1 << 8)) + dw_count += (cur_seq->multitex) ? 2 : 1; + if (bcmd & (1 << 9)) + dw_count++; + if (bcmd & (1 << 10)) + dw_count++; + if (bcmd & (1 << 11)) + dw_count++; + if (bcmd & (1 << 12)) + dw_count++; + if (bcmd & (1 << 13)) + dw_count++; + if (bcmd & (1 << 14)) + dw_count++; + + while (buf < buf_end) { + if (*buf == a_fire) { + if (dev_priv->num_fire_offsets >= + VIA_FIRE_BUF_SIZE) { + DRM_ERROR("Fire offset buffer full.\n"); + ret = 1; + break; + } + dev_priv->fire_offsets[dev_priv-> + num_fire_offsets++] = + buf; + have_fire = 1; + buf++; + if (buf < buf_end && *buf == a_fire) + buf++; + break; + } + if ((*buf == HALCYON_HEADER2) || + ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) { + DRM_ERROR("Missing Vertex Fire command, " + "Stray Vertex Fire command or verifier " + "lost sync.\n"); + ret = 1; + break; + } + if ((ret = eat_words(&buf, buf_end, dw_count))) + break; + } + if (buf >= buf_end && !have_fire) { + DRM_ERROR("Missing Vertex Fire command or verifier " + "lost sync.\n"); + ret = 1; + break; + } + if (cur_seq->agp && ((buf - cur_seq->buf_start) & 0x01)) { + DRM_ERROR("AGP Primitive list end misaligned.\n"); + ret = 1; + break; + } + } + *buffer = buf; + return ret; +} + +static __inline__ verifier_state_t +via_check_header2(uint32_t const **buffer, const uint32_t *buf_end, + drm_via_state_t *hc_state) +{ + uint32_t cmd; + int hz_mode; + hazard_t hz; + const uint32_t *buf = *buffer; + const hazard_t *hz_table; + + if ((buf_end - buf) < 2) { + DRM_ERROR + ("Illegal termination of DMA HALCYON_HEADER2 sequence.\n"); + return state_error; + } + buf++; + cmd = (*buf++ & 0xFFFF0000) >> 16; + + switch (cmd) { + case HC_ParaType_CmdVdata: + if (via_check_prim_list(&buf, buf_end, hc_state)) + return state_error; + *buffer = buf; + return state_command; + case HC_ParaType_NotTex: + hz_table = table1; + break; + case HC_ParaType_Tex: + hc_state->texture = 0; + hz_table = table2; + break; + case (HC_ParaType_Tex | (HC_SubType_Tex1 << 8)): + hc_state->texture = 1; + hz_table = table2; + break; + case (HC_ParaType_Tex | (HC_SubType_TexGeneral << 8)): + hz_table = table3; + break; + case HC_ParaType_Auto: + if (eat_words(&buf, buf_end, 2)) + return state_error; + *buffer = buf; + return state_command; + case (HC_ParaType_Palette | (HC_SubType_Stipple << 8)): + if (eat_words(&buf, buf_end, 32)) + return state_error; + *buffer = buf; + return state_command; + case (HC_ParaType_Palette | (HC_SubType_TexPalette0 << 8)): + case (HC_ParaType_Palette | (HC_SubType_TexPalette1 << 8)): + DRM_ERROR("Texture palettes are rejected because of " + "lack of info how to determine their size.\n"); + return state_error; + case (HC_ParaType_Palette | (HC_SubType_FogTable << 8)): + DRM_ERROR("Fog factor palettes are rejected because of " + "lack of info how to determine their size.\n"); + return state_error; + default: + + /* + * There are some unimplemented HC_ParaTypes here, that + * need to be implemented if the Mesa driver is extended. + */ + + DRM_ERROR("Invalid or unimplemented HALCYON_HEADER2 " + "DMA subcommand: 0x%x. Previous dword: 0x%x\n", + cmd, *(buf - 2)); + *buffer = buf; + return state_error; + } + + while (buf < buf_end) { + cmd = *buf++; + if ((hz = hz_table[cmd >> 24])) { + if ((hz_mode = investigate_hazard(cmd, hz, hc_state))) { + if (hz_mode == 1) { + buf--; + break; + } + return state_error; + } + } else if (hc_state->unfinished && + finish_current_sequence(hc_state)) { + return state_error; + } + } + if (hc_state->unfinished && finish_current_sequence(hc_state)) + return state_error; + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_parse_header2(drm_via_private_t *dev_priv, uint32_t const **buffer, + const uint32_t *buf_end, int *fire_count) +{ + uint32_t cmd; + const uint32_t *buf = *buffer; + const uint32_t *next_fire; + int burst = 0; + + next_fire = dev_priv->fire_offsets[*fire_count]; + buf++; + cmd = (*buf & 0xFFFF0000) >> 16; + via_write(dev_priv, HC_REG_TRANS_SET + HC_REG_BASE, *buf++); + switch (cmd) { + case HC_ParaType_CmdVdata: + while ((buf < buf_end) && + (*fire_count < dev_priv->num_fire_offsets) && + (*buf & HC_ACMD_MASK) == HC_ACMD_HCmdB) { + while (buf <= next_fire) { + via_write(dev_priv, HC_REG_TRANS_SPACE + HC_REG_BASE + + (burst & 63), *buf++); + burst += 4; + } + if ((buf < buf_end) + && ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) + buf++; + + if (++(*fire_count) < dev_priv->num_fire_offsets) + next_fire = dev_priv->fire_offsets[*fire_count]; + } + break; + default: + while (buf < buf_end) { + + if (*buf == HC_HEADER2 || + (*buf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + break; + + via_write(dev_priv, HC_REG_TRANS_SPACE + HC_REG_BASE + + (burst & 63), *buf++); + burst += 4; + } + } + *buffer = buf; + return state_command; +} + +static __inline__ int verify_mmio_address(uint32_t address) +{ + if ((address > 0x3FF) && (address < 0xC00)) { + DRM_ERROR("Invalid VIDEO DMA command. " + "Attempt to access 3D- or command burst area.\n"); + return 1; + } else if ((address > 0xCFF) && (address < 0x1300)) { + DRM_ERROR("Invalid VIDEO DMA command. " + "Attempt to access PCI DMA area.\n"); + return 1; + } else if (address > 0x13FF) { + DRM_ERROR("Invalid VIDEO DMA command. " + "Attempt to access VGA registers.\n"); + return 1; + } + return 0; +} + +static __inline__ int +verify_video_tail(uint32_t const **buffer, const uint32_t * buf_end, + uint32_t dwords) +{ + const uint32_t *buf = *buffer; + + if (buf_end - buf < dwords) { + DRM_ERROR("Illegal termination of video command.\n"); + return 1; + } + while (dwords--) { + if (*buf++) { + DRM_ERROR("Illegal video command tail.\n"); + return 1; + } + } + *buffer = buf; + return 0; +} + +static __inline__ verifier_state_t +via_check_header1(uint32_t const **buffer, const uint32_t * buf_end) +{ + uint32_t cmd; + const uint32_t *buf = *buffer; + verifier_state_t ret = state_command; + + while (buf < buf_end) { + cmd = *buf; + if ((cmd > ((0x3FF >> 2) | HALCYON_HEADER1)) && + (cmd < ((0xC00 >> 2) | HALCYON_HEADER1))) { + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) + break; + DRM_ERROR("Invalid HALCYON_HEADER1 command. " + "Attempt to access 3D- or command burst area.\n"); + ret = state_error; + break; + } else if (cmd > ((0xCFF >> 2) | HALCYON_HEADER1)) { + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) + break; + DRM_ERROR("Invalid HALCYON_HEADER1 command. " + "Attempt to access VGA registers.\n"); + ret = state_error; + break; + } else { + buf += 2; + } + } + *buffer = buf; + return ret; +} + +static __inline__ verifier_state_t +via_parse_header1(drm_via_private_t *dev_priv, uint32_t const **buffer, + const uint32_t *buf_end) +{ + register uint32_t cmd; + const uint32_t *buf = *buffer; + + while (buf < buf_end) { + cmd = *buf; + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) + break; + via_write(dev_priv, (cmd & ~HALCYON_HEADER1MASK) << 2, *++buf); + buf++; + } + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_check_vheader5(uint32_t const **buffer, const uint32_t *buf_end) +{ + uint32_t data; + const uint32_t *buf = *buffer; + + if (buf_end - buf < 4) { + DRM_ERROR("Illegal termination of video header5 command\n"); + return state_error; + } + + data = *buf++ & ~VIA_VIDEOMASK; + if (verify_mmio_address(data)) + return state_error; + + data = *buf++; + if (*buf++ != 0x00F50000) { + DRM_ERROR("Illegal header5 header data\n"); + return state_error; + } + if (*buf++ != 0x00000000) { + DRM_ERROR("Illegal header5 header data\n"); + return state_error; + } + if (eat_words(&buf, buf_end, data)) + return state_error; + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) + return state_error; + *buffer = buf; + return state_command; + +} + +static __inline__ verifier_state_t +via_parse_vheader5(drm_via_private_t *dev_priv, uint32_t const **buffer, + const uint32_t *buf_end) +{ + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + addr = *buf++ & ~VIA_VIDEOMASK; + i = count = *buf; + buf += 3; + while (i--) + via_write(dev_priv, addr, *buf++); + if (count & 3) + buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_check_vheader6(uint32_t const **buffer, const uint32_t * buf_end) +{ + uint32_t data; + const uint32_t *buf = *buffer; + uint32_t i; + + if (buf_end - buf < 4) { + DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; + } + buf++; + data = *buf++; + if (*buf++ != 0x00F60000) { + DRM_ERROR("Illegal header6 header data\n"); + return state_error; + } + if (*buf++ != 0x00000000) { + DRM_ERROR("Illegal header6 header data\n"); + return state_error; + } + if ((buf_end - buf) < (data << 1)) { + DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; + } + for (i = 0; i < data; ++i) { + if (verify_mmio_address(*buf++)) + return state_error; + buf++; + } + data <<= 1; + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) + return state_error; + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t +via_parse_vheader6(drm_via_private_t *dev_priv, uint32_t const **buffer, + const uint32_t *buf_end) +{ + + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + i = count = *++buf; + buf += 3; + while (i--) { + addr = *buf++; + via_write(dev_priv, addr, *buf++); + } + count <<= 1; + if (count & 3) + buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + +static int +via_verify_command_stream(const uint32_t * buf, unsigned int size, + struct drm_device * dev, int agp) +{ + + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_state_t *hc_state = &dev_priv->hc_state; + drm_via_state_t saved_state = *hc_state; + uint32_t cmd; + const uint32_t *buf_end = buf + (size >> 2); + verifier_state_t state = state_command; + int cme_video; + int supported_3d; + + cme_video = (dev_priv->chipset == VIA_PRO_GROUP_A || + dev_priv->chipset == VIA_DX9_0); + + supported_3d = dev_priv->chipset != VIA_DX9_0; + + hc_state->dev = dev; + hc_state->unfinished = no_sequence; + hc_state->map_cache = NULL; + hc_state->agp = agp; + hc_state->buf_start = buf; + dev_priv->num_fire_offsets = 0; + + while (buf < buf_end) { + + switch (state) { + case state_header2: + state = via_check_header2(&buf, buf_end, hc_state); + break; + case state_header1: + state = via_check_header1(&buf, buf_end); + break; + case state_vheader5: + state = via_check_vheader5(&buf, buf_end); + break; + case state_vheader6: + state = via_check_vheader6(&buf, buf_end); + break; + case state_command: + cmd = *buf; + if ((cmd == HALCYON_HEADER2) && supported_3d) + state = state_header2; + else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + state = state_header1; + else if (cme_video + && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if (cme_video + && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; + else if ((cmd == HALCYON_HEADER2) && !supported_3d) { + DRM_ERROR("Accelerated 3D is not supported on this chipset yet.\n"); + state = state_error; + } else { + DRM_ERROR + ("Invalid / Unimplemented DMA HEADER command. 0x%x\n", + cmd); + state = state_error; + } + break; + case state_error: + default: + *hc_state = saved_state; + return -EINVAL; + } + } + if (state == state_error) { + *hc_state = saved_state; + return -EINVAL; + } + return 0; +} + +static int +via_parse_command_stream(struct drm_device *dev, const uint32_t *buf, + unsigned int size) +{ + + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + uint32_t cmd; + const uint32_t *buf_end = buf + (size >> 2); + verifier_state_t state = state_command; + int fire_count = 0; + + while (buf < buf_end) { + + switch (state) { + case state_header2: + state = + via_parse_header2(dev_priv, &buf, buf_end, + &fire_count); + break; + case state_header1: + state = via_parse_header1(dev_priv, &buf, buf_end); + break; + case state_vheader5: + state = via_parse_vheader5(dev_priv, &buf, buf_end); + break; + case state_vheader6: + state = via_parse_vheader6(dev_priv, &buf, buf_end); + break; + case state_command: + cmd = *buf; + if (cmd == HALCYON_HEADER2) + state = state_header2; + else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + state = state_header1; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; + else { + DRM_ERROR + ("Invalid / Unimplemented DMA HEADER command. 0x%x\n", + cmd); + state = state_error; + } + break; + case state_error: + default: + return -EINVAL; + } + } + if (state == state_error) + return -EINVAL; + return 0; +} + +static void +setup_hazard_table(hz_init_t init_table[], hazard_t table[], int size) +{ + int i; + + for (i = 0; i < 256; ++i) + table[i] = forbidden_command; + + for (i = 0; i < size; ++i) + table[init_table[i].code] = init_table[i].hz; +} + +static void via_init_command_verifier(void) +{ + setup_hazard_table(init_table1, table1, ARRAY_SIZE(init_table1)); + setup_hazard_table(init_table2, table2, ARRAY_SIZE(init_table2)); + setup_hazard_table(init_table3, table3, ARRAY_SIZE(init_table3)); +} /* * Unmap a DMA mapping. */ diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index 6b26decfadb8..640a7701f606 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -46,7 +46,33 @@ #define DRIVER_MINOR 11 #define DRIVER_PATCHLEVEL 1 -#include "via_verifier.h" +typedef enum { + no_sequence = 0, + z_address, + dest_address, + tex_address +} drm_via_sequence_t; + +typedef struct { + unsigned texture; + uint32_t z_addr; + uint32_t d_addr; + uint32_t t_addr[2][10]; + uint32_t pitch[2][10]; + uint32_t height[2][10]; + uint32_t tex_level_lo[2]; + uint32_t tex_level_hi[2]; + uint32_t tex_palette_size[2]; + uint32_t tex_npot[2]; + drm_via_sequence_t unfinished; + int agp_texture; + int multitex; + struct drm_device *dev; + drm_local_map_t *map_cache; + uint32_t vertex_count; + int agp; + const uint32_t *buf_start; +} drm_via_state_t; #define VIA_PCI_BUF_SIZE 60000 #define VIA_FIRE_BUF_SIZE 1024 @@ -234,7 +260,6 @@ extern int via_init_context(struct drm_device *dev, int context); extern int via_do_cleanup_map(struct drm_device *dev); extern int via_dma_cleanup(struct drm_device *dev); -extern void via_init_command_verifier(void); extern int via_driver_dma_quiescent(struct drm_device *dev); #endif diff --git a/drivers/gpu/drm/via/via_verifier.c b/drivers/gpu/drm/via/via_verifier.c deleted file mode 100644 index 3d6e3a70f318..000000000000 --- a/drivers/gpu/drm/via/via_verifier.c +++ /dev/null @@ -1,1110 +0,0 @@ -/* - * Copyright 2004 The Unichrome Project. All Rights Reserved. - * Copyright 2005 Thomas Hellstrom. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Thomas Hellstrom 2004, 2005. - * This code was written using docs obtained under NDA from VIA Inc. - * - * Don't run this code directly on an AGP buffer. Due to cache problems it will - * be very slow. - */ - -#include -#include -#include - -#include "via_3d_reg.h" -#include "via_drv.h" -#include "via_verifier.h" - -typedef enum { - state_command, - state_header2, - state_header1, - state_vheader5, - state_vheader6, - state_error -} verifier_state_t; - -typedef enum { - no_check = 0, - check_for_header2, - check_for_header1, - check_for_header2_err, - check_for_header1_err, - check_for_fire, - check_z_buffer_addr0, - check_z_buffer_addr1, - check_z_buffer_addr_mode, - check_destination_addr0, - check_destination_addr1, - check_destination_addr_mode, - check_for_dummy, - check_for_dd, - check_texture_addr0, - check_texture_addr1, - check_texture_addr2, - check_texture_addr3, - check_texture_addr4, - check_texture_addr5, - check_texture_addr6, - check_texture_addr7, - check_texture_addr8, - check_texture_addr_mode, - check_for_vertex_count, - check_number_texunits, - forbidden_command -} hazard_t; - -/* - * Associates each hazard above with a possible multi-command - * sequence. For example an address that is split over multiple - * commands and that needs to be checked at the first command - * that does not include any part of the address. - */ - -static drm_via_sequence_t seqs[] = { - no_sequence, - no_sequence, - no_sequence, - no_sequence, - no_sequence, - no_sequence, - z_address, - z_address, - z_address, - dest_address, - dest_address, - dest_address, - no_sequence, - no_sequence, - tex_address, - tex_address, - tex_address, - tex_address, - tex_address, - tex_address, - tex_address, - tex_address, - tex_address, - tex_address, - no_sequence -}; - -typedef struct { - unsigned int code; - hazard_t hz; -} hz_init_t; - -static hz_init_t init_table1[] = { - {0xf2, check_for_header2_err}, - {0xf0, check_for_header1_err}, - {0xee, check_for_fire}, - {0xcc, check_for_dummy}, - {0xdd, check_for_dd}, - {0x00, no_check}, - {0x10, check_z_buffer_addr0}, - {0x11, check_z_buffer_addr1}, - {0x12, check_z_buffer_addr_mode}, - {0x13, no_check}, - {0x14, no_check}, - {0x15, no_check}, - {0x23, no_check}, - {0x24, no_check}, - {0x33, no_check}, - {0x34, no_check}, - {0x35, no_check}, - {0x36, no_check}, - {0x37, no_check}, - {0x38, no_check}, - {0x39, no_check}, - {0x3A, no_check}, - {0x3B, no_check}, - {0x3C, no_check}, - {0x3D, no_check}, - {0x3E, no_check}, - {0x40, check_destination_addr0}, - {0x41, check_destination_addr1}, - {0x42, check_destination_addr_mode}, - {0x43, no_check}, - {0x44, no_check}, - {0x50, no_check}, - {0x51, no_check}, - {0x52, no_check}, - {0x53, no_check}, - {0x54, no_check}, - {0x55, no_check}, - {0x56, no_check}, - {0x57, no_check}, - {0x58, no_check}, - {0x70, no_check}, - {0x71, no_check}, - {0x78, no_check}, - {0x79, no_check}, - {0x7A, no_check}, - {0x7B, no_check}, - {0x7C, no_check}, - {0x7D, check_for_vertex_count} -}; - -static hz_init_t init_table2[] = { - {0xf2, check_for_header2_err}, - {0xf0, check_for_header1_err}, - {0xee, check_for_fire}, - {0xcc, check_for_dummy}, - {0x00, check_texture_addr0}, - {0x01, check_texture_addr0}, - {0x02, check_texture_addr0}, - {0x03, check_texture_addr0}, - {0x04, check_texture_addr0}, - {0x05, check_texture_addr0}, - {0x06, check_texture_addr0}, - {0x07, check_texture_addr0}, - {0x08, check_texture_addr0}, - {0x09, check_texture_addr0}, - {0x20, check_texture_addr1}, - {0x21, check_texture_addr1}, - {0x22, check_texture_addr1}, - {0x23, check_texture_addr4}, - {0x2B, check_texture_addr3}, - {0x2C, check_texture_addr3}, - {0x2D, check_texture_addr3}, - {0x2E, check_texture_addr3}, - {0x2F, check_texture_addr3}, - {0x30, check_texture_addr3}, - {0x31, check_texture_addr3}, - {0x32, check_texture_addr3}, - {0x33, check_texture_addr3}, - {0x34, check_texture_addr3}, - {0x4B, check_texture_addr5}, - {0x4C, check_texture_addr6}, - {0x51, check_texture_addr7}, - {0x52, check_texture_addr8}, - {0x77, check_texture_addr2}, - {0x78, no_check}, - {0x79, no_check}, - {0x7A, no_check}, - {0x7B, check_texture_addr_mode}, - {0x7C, no_check}, - {0x7D, no_check}, - {0x7E, no_check}, - {0x7F, no_check}, - {0x80, no_check}, - {0x81, no_check}, - {0x82, no_check}, - {0x83, no_check}, - {0x85, no_check}, - {0x86, no_check}, - {0x87, no_check}, - {0x88, no_check}, - {0x89, no_check}, - {0x8A, no_check}, - {0x90, no_check}, - {0x91, no_check}, - {0x92, no_check}, - {0x93, no_check} -}; - -static hz_init_t init_table3[] = { - {0xf2, check_for_header2_err}, - {0xf0, check_for_header1_err}, - {0xcc, check_for_dummy}, - {0x00, check_number_texunits} -}; - -static hazard_t table1[256]; -static hazard_t table2[256]; -static hazard_t table3[256]; - -static __inline__ int -eat_words(const uint32_t **buf, const uint32_t *buf_end, unsigned num_words) -{ - if ((buf_end - *buf) >= num_words) { - *buf += num_words; - return 0; - } - DRM_ERROR("Illegal termination of DMA command buffer\n"); - return 1; -} - -/* - * Partially stolen from drm_memory.h - */ - -static __inline__ drm_local_map_t *via_drm_lookup_agp_map(drm_via_state_t *seq, - unsigned long offset, - unsigned long size, - struct drm_device *dev) -{ - struct drm_map_list *r_list; - drm_local_map_t *map = seq->map_cache; - - if (map && map->offset <= offset - && (offset + size) <= (map->offset + map->size)) { - return map; - } - - list_for_each_entry(r_list, &dev->maplist, head) { - map = r_list->map; - if (!map) - continue; - if (map->offset <= offset - && (offset + size) <= (map->offset + map->size) - && !(map->flags & _DRM_RESTRICTED) - && (map->type == _DRM_AGP)) { - seq->map_cache = map; - return map; - } - } - return NULL; -} - -/* - * Require that all AGP texture levels reside in the same AGP map which should - * be mappable by the client. This is not a big restriction. - * FIXME: To actually enforce this security policy strictly, drm_rmmap - * would have to wait for dma quiescent before removing an AGP map. - * The via_drm_lookup_agp_map call in reality seems to take - * very little CPU time. - */ - -static __inline__ int finish_current_sequence(drm_via_state_t * cur_seq) -{ - switch (cur_seq->unfinished) { - case z_address: - DRM_DEBUG("Z Buffer start address is 0x%x\n", cur_seq->z_addr); - break; - case dest_address: - DRM_DEBUG("Destination start address is 0x%x\n", - cur_seq->d_addr); - break; - case tex_address: - if (cur_seq->agp_texture) { - unsigned start = - cur_seq->tex_level_lo[cur_seq->texture]; - unsigned end = cur_seq->tex_level_hi[cur_seq->texture]; - unsigned long lo = ~0, hi = 0, tmp; - uint32_t *addr, *pitch, *height, tex; - unsigned i; - int npot; - - if (end > 9) - end = 9; - if (start > 9) - start = 9; - - addr = - &(cur_seq->t_addr[tex = cur_seq->texture][start]); - pitch = &(cur_seq->pitch[tex][start]); - height = &(cur_seq->height[tex][start]); - npot = cur_seq->tex_npot[tex]; - for (i = start; i <= end; ++i) { - tmp = *addr++; - if (tmp < lo) - lo = tmp; - if (i == 0 && npot) - tmp += (*height++ * *pitch++); - else - tmp += (*height++ << *pitch++); - if (tmp > hi) - hi = tmp; - } - - if (!via_drm_lookup_agp_map - (cur_seq, lo, hi - lo, cur_seq->dev)) { - DRM_ERROR - ("AGP texture is not in allowed map\n"); - return 2; - } - } - break; - default: - break; - } - cur_seq->unfinished = no_sequence; - return 0; -} - -static __inline__ int -investigate_hazard(uint32_t cmd, hazard_t hz, drm_via_state_t *cur_seq) -{ - register uint32_t tmp, *tmp_addr; - - if (cur_seq->unfinished && (cur_seq->unfinished != seqs[hz])) { - int ret; - if ((ret = finish_current_sequence(cur_seq))) - return ret; - } - - switch (hz) { - case check_for_header2: - if (cmd == HALCYON_HEADER2) - return 1; - return 0; - case check_for_header1: - if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) - return 1; - return 0; - case check_for_header2_err: - if (cmd == HALCYON_HEADER2) - return 1; - DRM_ERROR("Illegal DMA HALCYON_HEADER2 command\n"); - break; - case check_for_header1_err: - if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) - return 1; - DRM_ERROR("Illegal DMA HALCYON_HEADER1 command\n"); - break; - case check_for_fire: - if ((cmd & HALCYON_FIREMASK) == HALCYON_FIRECMD) - return 1; - DRM_ERROR("Illegal DMA HALCYON_FIRECMD command\n"); - break; - case check_for_dummy: - if (HC_DUMMY == cmd) - return 0; - DRM_ERROR("Illegal DMA HC_DUMMY command\n"); - break; - case check_for_dd: - if (0xdddddddd == cmd) - return 0; - DRM_ERROR("Illegal DMA 0xdddddddd command\n"); - break; - case check_z_buffer_addr0: - cur_seq->unfinished = z_address; - cur_seq->z_addr = (cur_seq->z_addr & 0xFF000000) | - (cmd & 0x00FFFFFF); - return 0; - case check_z_buffer_addr1: - cur_seq->unfinished = z_address; - cur_seq->z_addr = (cur_seq->z_addr & 0x00FFFFFF) | - ((cmd & 0xFF) << 24); - return 0; - case check_z_buffer_addr_mode: - cur_seq->unfinished = z_address; - if ((cmd & 0x0000C000) == 0) - return 0; - DRM_ERROR("Attempt to place Z buffer in system memory\n"); - return 2; - case check_destination_addr0: - cur_seq->unfinished = dest_address; - cur_seq->d_addr = (cur_seq->d_addr & 0xFF000000) | - (cmd & 0x00FFFFFF); - return 0; - case check_destination_addr1: - cur_seq->unfinished = dest_address; - cur_seq->d_addr = (cur_seq->d_addr & 0x00FFFFFF) | - ((cmd & 0xFF) << 24); - return 0; - case check_destination_addr_mode: - cur_seq->unfinished = dest_address; - if ((cmd & 0x0000C000) == 0) - return 0; - DRM_ERROR - ("Attempt to place 3D drawing buffer in system memory\n"); - return 2; - case check_texture_addr0: - cur_seq->unfinished = tex_address; - tmp = (cmd >> 24); - tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp]; - *tmp_addr = (*tmp_addr & 0xFF000000) | (cmd & 0x00FFFFFF); - return 0; - case check_texture_addr1: - cur_seq->unfinished = tex_address; - tmp = ((cmd >> 24) - 0x20); - tmp += tmp << 1; - tmp_addr = &cur_seq->t_addr[cur_seq->texture][tmp]; - *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24); - tmp_addr++; - *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF00) << 16); - tmp_addr++; - *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF0000) << 8); - return 0; - case check_texture_addr2: - cur_seq->unfinished = tex_address; - cur_seq->tex_level_lo[tmp = cur_seq->texture] = cmd & 0x3F; - cur_seq->tex_level_hi[tmp] = (cmd & 0xFC0) >> 6; - return 0; - case check_texture_addr3: - cur_seq->unfinished = tex_address; - tmp = ((cmd >> 24) - HC_SubA_HTXnL0Pit); - if (tmp == 0 && - (cmd & HC_HTXnEnPit_MASK)) { - cur_seq->pitch[cur_seq->texture][tmp] = - (cmd & HC_HTXnLnPit_MASK); - cur_seq->tex_npot[cur_seq->texture] = 1; - } else { - cur_seq->pitch[cur_seq->texture][tmp] = - (cmd & HC_HTXnLnPitE_MASK) >> HC_HTXnLnPitE_SHIFT; - cur_seq->tex_npot[cur_seq->texture] = 0; - if (cmd & 0x000FFFFF) { - DRM_ERROR - ("Unimplemented texture level 0 pitch mode.\n"); - return 2; - } - } - return 0; - case check_texture_addr4: - cur_seq->unfinished = tex_address; - tmp_addr = &cur_seq->t_addr[cur_seq->texture][9]; - *tmp_addr = (*tmp_addr & 0x00FFFFFF) | ((cmd & 0xFF) << 24); - return 0; - case check_texture_addr5: - case check_texture_addr6: - cur_seq->unfinished = tex_address; - /* - * Texture width. We don't care since we have the pitch. - */ - return 0; - case check_texture_addr7: - cur_seq->unfinished = tex_address; - tmp_addr = &(cur_seq->height[cur_seq->texture][0]); - tmp_addr[5] = 1 << ((cmd & 0x00F00000) >> 20); - tmp_addr[4] = 1 << ((cmd & 0x000F0000) >> 16); - tmp_addr[3] = 1 << ((cmd & 0x0000F000) >> 12); - tmp_addr[2] = 1 << ((cmd & 0x00000F00) >> 8); - tmp_addr[1] = 1 << ((cmd & 0x000000F0) >> 4); - tmp_addr[0] = 1 << (cmd & 0x0000000F); - return 0; - case check_texture_addr8: - cur_seq->unfinished = tex_address; - tmp_addr = &(cur_seq->height[cur_seq->texture][0]); - tmp_addr[9] = 1 << ((cmd & 0x0000F000) >> 12); - tmp_addr[8] = 1 << ((cmd & 0x00000F00) >> 8); - tmp_addr[7] = 1 << ((cmd & 0x000000F0) >> 4); - tmp_addr[6] = 1 << (cmd & 0x0000000F); - return 0; - case check_texture_addr_mode: - cur_seq->unfinished = tex_address; - if (2 == (tmp = cmd & 0x00000003)) { - DRM_ERROR - ("Attempt to fetch texture from system memory.\n"); - return 2; - } - cur_seq->agp_texture = (tmp == 3); - cur_seq->tex_palette_size[cur_seq->texture] = - (cmd >> 16) & 0x000000007; - return 0; - case check_for_vertex_count: - cur_seq->vertex_count = cmd & 0x0000FFFF; - return 0; - case check_number_texunits: - cur_seq->multitex = (cmd >> 3) & 1; - return 0; - default: - DRM_ERROR("Illegal DMA data: 0x%x\n", cmd); - return 2; - } - return 2; -} - -static __inline__ int -via_check_prim_list(uint32_t const **buffer, const uint32_t * buf_end, - drm_via_state_t *cur_seq) -{ - drm_via_private_t *dev_priv = - (drm_via_private_t *) cur_seq->dev->dev_private; - uint32_t a_fire, bcmd, dw_count; - int ret = 0; - int have_fire; - const uint32_t *buf = *buffer; - - while (buf < buf_end) { - have_fire = 0; - if ((buf_end - buf) < 2) { - DRM_ERROR - ("Unexpected termination of primitive list.\n"); - ret = 1; - break; - } - if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdB) - break; - bcmd = *buf++; - if ((*buf & HC_ACMD_MASK) != HC_ACMD_HCmdA) { - DRM_ERROR("Expected Vertex List A command, got 0x%x\n", - *buf); - ret = 1; - break; - } - a_fire = - *buf++ | HC_HPLEND_MASK | HC_HPMValidN_MASK | - HC_HE3Fire_MASK; - - /* - * How many dwords per vertex ? - */ - - if (cur_seq->agp && ((bcmd & (0xF << 11)) == 0)) { - DRM_ERROR("Illegal B command vertex data for AGP.\n"); - ret = 1; - break; - } - - dw_count = 0; - if (bcmd & (1 << 7)) - dw_count += (cur_seq->multitex) ? 2 : 1; - if (bcmd & (1 << 8)) - dw_count += (cur_seq->multitex) ? 2 : 1; - if (bcmd & (1 << 9)) - dw_count++; - if (bcmd & (1 << 10)) - dw_count++; - if (bcmd & (1 << 11)) - dw_count++; - if (bcmd & (1 << 12)) - dw_count++; - if (bcmd & (1 << 13)) - dw_count++; - if (bcmd & (1 << 14)) - dw_count++; - - while (buf < buf_end) { - if (*buf == a_fire) { - if (dev_priv->num_fire_offsets >= - VIA_FIRE_BUF_SIZE) { - DRM_ERROR("Fire offset buffer full.\n"); - ret = 1; - break; - } - dev_priv->fire_offsets[dev_priv-> - num_fire_offsets++] = - buf; - have_fire = 1; - buf++; - if (buf < buf_end && *buf == a_fire) - buf++; - break; - } - if ((*buf == HALCYON_HEADER2) || - ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) { - DRM_ERROR("Missing Vertex Fire command, " - "Stray Vertex Fire command or verifier " - "lost sync.\n"); - ret = 1; - break; - } - if ((ret = eat_words(&buf, buf_end, dw_count))) - break; - } - if (buf >= buf_end && !have_fire) { - DRM_ERROR("Missing Vertex Fire command or verifier " - "lost sync.\n"); - ret = 1; - break; - } - if (cur_seq->agp && ((buf - cur_seq->buf_start) & 0x01)) { - DRM_ERROR("AGP Primitive list end misaligned.\n"); - ret = 1; - break; - } - } - *buffer = buf; - return ret; -} - -static __inline__ verifier_state_t -via_check_header2(uint32_t const **buffer, const uint32_t *buf_end, - drm_via_state_t *hc_state) -{ - uint32_t cmd; - int hz_mode; - hazard_t hz; - const uint32_t *buf = *buffer; - const hazard_t *hz_table; - - if ((buf_end - buf) < 2) { - DRM_ERROR - ("Illegal termination of DMA HALCYON_HEADER2 sequence.\n"); - return state_error; - } - buf++; - cmd = (*buf++ & 0xFFFF0000) >> 16; - - switch (cmd) { - case HC_ParaType_CmdVdata: - if (via_check_prim_list(&buf, buf_end, hc_state)) - return state_error; - *buffer = buf; - return state_command; - case HC_ParaType_NotTex: - hz_table = table1; - break; - case HC_ParaType_Tex: - hc_state->texture = 0; - hz_table = table2; - break; - case (HC_ParaType_Tex | (HC_SubType_Tex1 << 8)): - hc_state->texture = 1; - hz_table = table2; - break; - case (HC_ParaType_Tex | (HC_SubType_TexGeneral << 8)): - hz_table = table3; - break; - case HC_ParaType_Auto: - if (eat_words(&buf, buf_end, 2)) - return state_error; - *buffer = buf; - return state_command; - case (HC_ParaType_Palette | (HC_SubType_Stipple << 8)): - if (eat_words(&buf, buf_end, 32)) - return state_error; - *buffer = buf; - return state_command; - case (HC_ParaType_Palette | (HC_SubType_TexPalette0 << 8)): - case (HC_ParaType_Palette | (HC_SubType_TexPalette1 << 8)): - DRM_ERROR("Texture palettes are rejected because of " - "lack of info how to determine their size.\n"); - return state_error; - case (HC_ParaType_Palette | (HC_SubType_FogTable << 8)): - DRM_ERROR("Fog factor palettes are rejected because of " - "lack of info how to determine their size.\n"); - return state_error; - default: - - /* - * There are some unimplemented HC_ParaTypes here, that - * need to be implemented if the Mesa driver is extended. - */ - - DRM_ERROR("Invalid or unimplemented HALCYON_HEADER2 " - "DMA subcommand: 0x%x. Previous dword: 0x%x\n", - cmd, *(buf - 2)); - *buffer = buf; - return state_error; - } - - while (buf < buf_end) { - cmd = *buf++; - if ((hz = hz_table[cmd >> 24])) { - if ((hz_mode = investigate_hazard(cmd, hz, hc_state))) { - if (hz_mode == 1) { - buf--; - break; - } - return state_error; - } - } else if (hc_state->unfinished && - finish_current_sequence(hc_state)) { - return state_error; - } - } - if (hc_state->unfinished && finish_current_sequence(hc_state)) - return state_error; - *buffer = buf; - return state_command; -} - -static __inline__ verifier_state_t -via_parse_header2(drm_via_private_t *dev_priv, uint32_t const **buffer, - const uint32_t *buf_end, int *fire_count) -{ - uint32_t cmd; - const uint32_t *buf = *buffer; - const uint32_t *next_fire; - int burst = 0; - - next_fire = dev_priv->fire_offsets[*fire_count]; - buf++; - cmd = (*buf & 0xFFFF0000) >> 16; - via_write(dev_priv, HC_REG_TRANS_SET + HC_REG_BASE, *buf++); - switch (cmd) { - case HC_ParaType_CmdVdata: - while ((buf < buf_end) && - (*fire_count < dev_priv->num_fire_offsets) && - (*buf & HC_ACMD_MASK) == HC_ACMD_HCmdB) { - while (buf <= next_fire) { - via_write(dev_priv, HC_REG_TRANS_SPACE + HC_REG_BASE + - (burst & 63), *buf++); - burst += 4; - } - if ((buf < buf_end) - && ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) - buf++; - - if (++(*fire_count) < dev_priv->num_fire_offsets) - next_fire = dev_priv->fire_offsets[*fire_count]; - } - break; - default: - while (buf < buf_end) { - - if (*buf == HC_HEADER2 || - (*buf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 || - (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5 || - (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) - break; - - via_write(dev_priv, HC_REG_TRANS_SPACE + HC_REG_BASE + - (burst & 63), *buf++); - burst += 4; - } - } - *buffer = buf; - return state_command; -} - -static __inline__ int verify_mmio_address(uint32_t address) -{ - if ((address > 0x3FF) && (address < 0xC00)) { - DRM_ERROR("Invalid VIDEO DMA command. " - "Attempt to access 3D- or command burst area.\n"); - return 1; - } else if ((address > 0xCFF) && (address < 0x1300)) { - DRM_ERROR("Invalid VIDEO DMA command. " - "Attempt to access PCI DMA area.\n"); - return 1; - } else if (address > 0x13FF) { - DRM_ERROR("Invalid VIDEO DMA command. " - "Attempt to access VGA registers.\n"); - return 1; - } - return 0; -} - -static __inline__ int -verify_video_tail(uint32_t const **buffer, const uint32_t * buf_end, - uint32_t dwords) -{ - const uint32_t *buf = *buffer; - - if (buf_end - buf < dwords) { - DRM_ERROR("Illegal termination of video command.\n"); - return 1; - } - while (dwords--) { - if (*buf++) { - DRM_ERROR("Illegal video command tail.\n"); - return 1; - } - } - *buffer = buf; - return 0; -} - -static __inline__ verifier_state_t -via_check_header1(uint32_t const **buffer, const uint32_t * buf_end) -{ - uint32_t cmd; - const uint32_t *buf = *buffer; - verifier_state_t ret = state_command; - - while (buf < buf_end) { - cmd = *buf; - if ((cmd > ((0x3FF >> 2) | HALCYON_HEADER1)) && - (cmd < ((0xC00 >> 2) | HALCYON_HEADER1))) { - if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) - break; - DRM_ERROR("Invalid HALCYON_HEADER1 command. " - "Attempt to access 3D- or command burst area.\n"); - ret = state_error; - break; - } else if (cmd > ((0xCFF >> 2) | HALCYON_HEADER1)) { - if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) - break; - DRM_ERROR("Invalid HALCYON_HEADER1 command. " - "Attempt to access VGA registers.\n"); - ret = state_error; - break; - } else { - buf += 2; - } - } - *buffer = buf; - return ret; -} - -static __inline__ verifier_state_t -via_parse_header1(drm_via_private_t *dev_priv, uint32_t const **buffer, - const uint32_t *buf_end) -{ - register uint32_t cmd; - const uint32_t *buf = *buffer; - - while (buf < buf_end) { - cmd = *buf; - if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) - break; - via_write(dev_priv, (cmd & ~HALCYON_HEADER1MASK) << 2, *++buf); - buf++; - } - *buffer = buf; - return state_command; -} - -static __inline__ verifier_state_t -via_check_vheader5(uint32_t const **buffer, const uint32_t *buf_end) -{ - uint32_t data; - const uint32_t *buf = *buffer; - - if (buf_end - buf < 4) { - DRM_ERROR("Illegal termination of video header5 command\n"); - return state_error; - } - - data = *buf++ & ~VIA_VIDEOMASK; - if (verify_mmio_address(data)) - return state_error; - - data = *buf++; - if (*buf++ != 0x00F50000) { - DRM_ERROR("Illegal header5 header data\n"); - return state_error; - } - if (*buf++ != 0x00000000) { - DRM_ERROR("Illegal header5 header data\n"); - return state_error; - } - if (eat_words(&buf, buf_end, data)) - return state_error; - if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) - return state_error; - *buffer = buf; - return state_command; - -} - -static __inline__ verifier_state_t -via_parse_vheader5(drm_via_private_t *dev_priv, uint32_t const **buffer, - const uint32_t *buf_end) -{ - uint32_t addr, count, i; - const uint32_t *buf = *buffer; - - addr = *buf++ & ~VIA_VIDEOMASK; - i = count = *buf; - buf += 3; - while (i--) - via_write(dev_priv, addr, *buf++); - if (count & 3) - buf += 4 - (count & 3); - *buffer = buf; - return state_command; -} - -static __inline__ verifier_state_t -via_check_vheader6(uint32_t const **buffer, const uint32_t * buf_end) -{ - uint32_t data; - const uint32_t *buf = *buffer; - uint32_t i; - - if (buf_end - buf < 4) { - DRM_ERROR("Illegal termination of video header6 command\n"); - return state_error; - } - buf++; - data = *buf++; - if (*buf++ != 0x00F60000) { - DRM_ERROR("Illegal header6 header data\n"); - return state_error; - } - if (*buf++ != 0x00000000) { - DRM_ERROR("Illegal header6 header data\n"); - return state_error; - } - if ((buf_end - buf) < (data << 1)) { - DRM_ERROR("Illegal termination of video header6 command\n"); - return state_error; - } - for (i = 0; i < data; ++i) { - if (verify_mmio_address(*buf++)) - return state_error; - buf++; - } - data <<= 1; - if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) - return state_error; - *buffer = buf; - return state_command; -} - -static __inline__ verifier_state_t -via_parse_vheader6(drm_via_private_t *dev_priv, uint32_t const **buffer, - const uint32_t *buf_end) -{ - - uint32_t addr, count, i; - const uint32_t *buf = *buffer; - - i = count = *++buf; - buf += 3; - while (i--) { - addr = *buf++; - via_write(dev_priv, addr, *buf++); - } - count <<= 1; - if (count & 3) - buf += 4 - (count & 3); - *buffer = buf; - return state_command; -} - -int -via_verify_command_stream(const uint32_t * buf, unsigned int size, - struct drm_device * dev, int agp) -{ - - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_state_t *hc_state = &dev_priv->hc_state; - drm_via_state_t saved_state = *hc_state; - uint32_t cmd; - const uint32_t *buf_end = buf + (size >> 2); - verifier_state_t state = state_command; - int cme_video; - int supported_3d; - - cme_video = (dev_priv->chipset == VIA_PRO_GROUP_A || - dev_priv->chipset == VIA_DX9_0); - - supported_3d = dev_priv->chipset != VIA_DX9_0; - - hc_state->dev = dev; - hc_state->unfinished = no_sequence; - hc_state->map_cache = NULL; - hc_state->agp = agp; - hc_state->buf_start = buf; - dev_priv->num_fire_offsets = 0; - - while (buf < buf_end) { - - switch (state) { - case state_header2: - state = via_check_header2(&buf, buf_end, hc_state); - break; - case state_header1: - state = via_check_header1(&buf, buf_end); - break; - case state_vheader5: - state = via_check_vheader5(&buf, buf_end); - break; - case state_vheader6: - state = via_check_vheader6(&buf, buf_end); - break; - case state_command: - cmd = *buf; - if ((cmd == HALCYON_HEADER2) && supported_3d) - state = state_header2; - else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) - state = state_header1; - else if (cme_video - && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) - state = state_vheader5; - else if (cme_video - && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) - state = state_vheader6; - else if ((cmd == HALCYON_HEADER2) && !supported_3d) { - DRM_ERROR("Accelerated 3D is not supported on this chipset yet.\n"); - state = state_error; - } else { - DRM_ERROR - ("Invalid / Unimplemented DMA HEADER command. 0x%x\n", - cmd); - state = state_error; - } - break; - case state_error: - default: - *hc_state = saved_state; - return -EINVAL; - } - } - if (state == state_error) { - *hc_state = saved_state; - return -EINVAL; - } - return 0; -} - -int -via_parse_command_stream(struct drm_device *dev, const uint32_t *buf, - unsigned int size) -{ - - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - uint32_t cmd; - const uint32_t *buf_end = buf + (size >> 2); - verifier_state_t state = state_command; - int fire_count = 0; - - while (buf < buf_end) { - - switch (state) { - case state_header2: - state = - via_parse_header2(dev_priv, &buf, buf_end, - &fire_count); - break; - case state_header1: - state = via_parse_header1(dev_priv, &buf, buf_end); - break; - case state_vheader5: - state = via_parse_vheader5(dev_priv, &buf, buf_end); - break; - case state_vheader6: - state = via_parse_vheader6(dev_priv, &buf, buf_end); - break; - case state_command: - cmd = *buf; - if (cmd == HALCYON_HEADER2) - state = state_header2; - else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) - state = state_header1; - else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) - state = state_vheader5; - else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) - state = state_vheader6; - else { - DRM_ERROR - ("Invalid / Unimplemented DMA HEADER command. 0x%x\n", - cmd); - state = state_error; - } - break; - case state_error: - default: - return -EINVAL; - } - } - if (state == state_error) - return -EINVAL; - return 0; -} - -static void -setup_hazard_table(hz_init_t init_table[], hazard_t table[], int size) -{ - int i; - - for (i = 0; i < 256; ++i) - table[i] = forbidden_command; - - for (i = 0; i < size; ++i) - table[init_table[i].code] = init_table[i].hz; -} - -void via_init_command_verifier(void) -{ - setup_hazard_table(init_table1, table1, ARRAY_SIZE(init_table1)); - setup_hazard_table(init_table2, table2, ARRAY_SIZE(init_table2)); - setup_hazard_table(init_table3, table3, ARRAY_SIZE(init_table3)); -} diff --git a/drivers/gpu/drm/via/via_verifier.h b/drivers/gpu/drm/via/via_verifier.h deleted file mode 100644 index 26b6d361ab95..000000000000 --- a/drivers/gpu/drm/via/via_verifier.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2004 The Unichrome Project. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE UNICHROME PROJECT, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Thomas Hellström 2004. - */ - -#ifndef _VIA_VERIFIER_H_ -#define _VIA_VERIFIER_H_ - -typedef enum { - no_sequence = 0, - z_address, - dest_address, - tex_address -} drm_via_sequence_t; - -typedef struct { - unsigned texture; - uint32_t z_addr; - uint32_t d_addr; - uint32_t t_addr[2][10]; - uint32_t pitch[2][10]; - uint32_t height[2][10]; - uint32_t tex_level_lo[2]; - uint32_t tex_level_hi[2]; - uint32_t tex_palette_size[2]; - uint32_t tex_npot[2]; - drm_via_sequence_t unfinished; - int agp_texture; - int multitex; - struct drm_device *dev; - drm_local_map_t *map_cache; - uint32_t vertex_count; - int agp; - const uint32_t *buf_start; -} drm_via_state_t; - -extern int via_verify_command_stream(const uint32_t *buf, unsigned int size, - struct drm_device *dev, int agp); -extern int via_parse_command_stream(struct drm_device *dev, const uint32_t *buf, - unsigned int size); - -#endif -- cgit v1.2.3-59-g8ed1b From 685bf9f58dd9624af93d9e20e7d5353b06966a7e Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:58 +0200 Subject: drm/via: Embed via_drv.h in via_dri1 With this change the driver is now a signle file driver. The only remaning heder file describes the HW and can be shared with the new openchrome driver. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-10-sam@ravnborg.org --- drivers/gpu/drm/via/via_dri1.c | 229 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/via/via_drv.h | 265 ----------------------------------------- 2 files changed, 228 insertions(+), 266 deletions(-) delete mode 100644 drivers/gpu/drm/via/via_drv.h (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c index f9285cc3e1c7..d695d9291ece 100644 --- a/drivers/gpu/drm/via/via_dri1.c +++ b/drivers/gpu/drm/via/via_dri1.c @@ -36,13 +36,240 @@ #include #include +#include +#include +#include #include +#include #include #include -#include "via_drv.h" #include "via_3d_reg.h" +#define DRIVER_AUTHOR "Various" + +#define DRIVER_NAME "via" +#define DRIVER_DESC "VIA Unichrome / Pro" +#define DRIVER_DATE "20070202" + +#define DRIVER_MAJOR 2 +#define DRIVER_MINOR 11 +#define DRIVER_PATCHLEVEL 1 + +typedef enum { + no_sequence = 0, + z_address, + dest_address, + tex_address +} drm_via_sequence_t; + +typedef struct { + unsigned texture; + uint32_t z_addr; + uint32_t d_addr; + uint32_t t_addr[2][10]; + uint32_t pitch[2][10]; + uint32_t height[2][10]; + uint32_t tex_level_lo[2]; + uint32_t tex_level_hi[2]; + uint32_t tex_palette_size[2]; + uint32_t tex_npot[2]; + drm_via_sequence_t unfinished; + int agp_texture; + int multitex; + struct drm_device *dev; + drm_local_map_t *map_cache; + uint32_t vertex_count; + int agp; + const uint32_t *buf_start; +} drm_via_state_t; + +#define VIA_PCI_BUF_SIZE 60000 +#define VIA_FIRE_BUF_SIZE 1024 +#define VIA_NUM_IRQS 4 + + +#define VIA_NUM_BLIT_ENGINES 2 +#define VIA_NUM_BLIT_SLOTS 8 + +struct _drm_via_descriptor; + +typedef struct _drm_via_sg_info { + struct page **pages; + unsigned long num_pages; + struct _drm_via_descriptor **desc_pages; + int num_desc_pages; + int num_desc; + enum dma_data_direction direction; + unsigned char *bounce_buffer; + dma_addr_t chain_start; + uint32_t free_on_sequence; + unsigned int descriptors_per_page; + int aborted; + enum { + dr_via_device_mapped, + dr_via_desc_pages_alloc, + dr_via_pages_locked, + dr_via_pages_alloc, + dr_via_sg_init + } state; +} drm_via_sg_info_t; + +typedef struct _drm_via_blitq { + struct drm_device *dev; + uint32_t cur_blit_handle; + uint32_t done_blit_handle; + unsigned serviced; + unsigned head; + unsigned cur; + unsigned num_free; + unsigned num_outstanding; + unsigned long end; + int aborting; + int is_active; + drm_via_sg_info_t *blits[VIA_NUM_BLIT_SLOTS]; + spinlock_t blit_lock; + wait_queue_head_t blit_queue[VIA_NUM_BLIT_SLOTS]; + wait_queue_head_t busy_queue; + struct work_struct wq; + struct timer_list poll_timer; +} drm_via_blitq_t; + +typedef struct drm_via_ring_buffer { + drm_local_map_t map; + char *virtual_start; +} drm_via_ring_buffer_t; + +typedef uint32_t maskarray_t[5]; + +typedef struct drm_via_irq { + atomic_t irq_received; + uint32_t pending_mask; + uint32_t enable_mask; + wait_queue_head_t irq_queue; +} drm_via_irq_t; + +typedef struct drm_via_private { + drm_via_sarea_t *sarea_priv; + drm_local_map_t *sarea; + drm_local_map_t *fb; + drm_local_map_t *mmio; + unsigned long agpAddr; + wait_queue_head_t decoder_queue[VIA_NR_XVMC_LOCKS]; + char *dma_ptr; + unsigned int dma_low; + unsigned int dma_high; + unsigned int dma_offset; + uint32_t dma_wrap; + volatile uint32_t *last_pause_ptr; + volatile uint32_t *hw_addr_ptr; + drm_via_ring_buffer_t ring; + ktime_t last_vblank; + int last_vblank_valid; + ktime_t nsec_per_vblank; + atomic_t vbl_received; + drm_via_state_t hc_state; + char pci_buf[VIA_PCI_BUF_SIZE]; + const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; + uint32_t num_fire_offsets; + int chipset; + drm_via_irq_t via_irqs[VIA_NUM_IRQS]; + unsigned num_irqs; + maskarray_t *irq_masks; + uint32_t irq_enable_mask; + uint32_t irq_pending_mask; + int *irq_map; + unsigned int idle_fault; + int vram_initialized; + struct drm_mm vram_mm; + int agp_initialized; + struct drm_mm agp_mm; + /** Mapping of userspace keys to mm objects */ + struct idr object_idr; + unsigned long vram_offset; + unsigned long agp_offset; + drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES]; + uint32_t dma_diff; +} drm_via_private_t; + +struct via_file_private { + struct list_head obj_list; +}; + +enum via_family { + VIA_OTHER = 0, /* Baseline */ + VIA_PRO_GROUP_A, /* Another video engine and DMA commands */ + VIA_DX9_0 /* Same video as pro_group_a, but 3D is unsupported */ +}; + +/* VIA MMIO register access */ +static inline u32 via_read(struct drm_via_private *dev_priv, u32 reg) +{ + return readl((void __iomem *)(dev_priv->mmio->handle + reg)); +} + +static inline void via_write(struct drm_via_private *dev_priv, u32 reg, + u32 val) +{ + writel(val, (void __iomem *)(dev_priv->mmio->handle + reg)); +} + +static inline void via_write8(struct drm_via_private *dev_priv, u32 reg, + u32 val) +{ + writeb(val, (void __iomem *)(dev_priv->mmio->handle + reg)); +} + +static inline void via_write8_mask(struct drm_via_private *dev_priv, + u32 reg, u32 mask, u32 val) +{ + u32 tmp; + + tmp = readb((void __iomem *)(dev_priv->mmio->handle + reg)); + tmp = (tmp & ~mask) | (val & mask); + writeb(tmp, (void __iomem *)(dev_priv->mmio->handle + reg)); +} + +/* + * Poll in a loop waiting for 'contidition' to be true. + * Note: A direct replacement with wait_event_interruptible_timeout() + * will not work unless driver is updated to emit wake_up() + * in relevant places that can impact the 'condition' + * + * Returns: + * ret keeps current value if 'condition' becomes true + * ret = -BUSY if timeout happens + * ret = -EINTR if a signal interrupted the waiting period + */ +#define VIA_WAIT_ON( ret, queue, timeout, condition ) \ +do { \ + DECLARE_WAITQUEUE(entry, current); \ + unsigned long end = jiffies + (timeout); \ + add_wait_queue(&(queue), &entry); \ + \ + for (;;) { \ + __set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (time_after_eq(jiffies, end)) { \ + ret = -EBUSY; \ + break; \ + } \ + schedule_timeout((HZ/100 > 1) ? HZ/100 : 1); \ + if (signal_pending(current)) { \ + ret = -EINTR; \ + break; \ + } \ + } \ + __set_current_state(TASK_RUNNING); \ + remove_wait_queue(&(queue), &entry); \ +} while (0) + +int via_do_cleanup_map(struct drm_device *dev); + +int via_dma_cleanup(struct drm_device *dev); +int via_driver_dma_quiescent(struct drm_device *dev); + #define CMDBUF_ALIGNMENT_SIZE (0x100) #define CMDBUF_ALIGNMENT_MASK (0x0ff) diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h deleted file mode 100644 index 640a7701f606..000000000000 --- a/drivers/gpu/drm/via/via_drv.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ -#ifndef _VIA_DRV_H_ -#define _VIA_DRV_H_ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define DRIVER_AUTHOR "Various" - -#define DRIVER_NAME "via" -#define DRIVER_DESC "VIA Unichrome / Pro" -#define DRIVER_DATE "20070202" - -#define DRIVER_MAJOR 2 -#define DRIVER_MINOR 11 -#define DRIVER_PATCHLEVEL 1 - -typedef enum { - no_sequence = 0, - z_address, - dest_address, - tex_address -} drm_via_sequence_t; - -typedef struct { - unsigned texture; - uint32_t z_addr; - uint32_t d_addr; - uint32_t t_addr[2][10]; - uint32_t pitch[2][10]; - uint32_t height[2][10]; - uint32_t tex_level_lo[2]; - uint32_t tex_level_hi[2]; - uint32_t tex_palette_size[2]; - uint32_t tex_npot[2]; - drm_via_sequence_t unfinished; - int agp_texture; - int multitex; - struct drm_device *dev; - drm_local_map_t *map_cache; - uint32_t vertex_count; - int agp; - const uint32_t *buf_start; -} drm_via_state_t; - -#define VIA_PCI_BUF_SIZE 60000 -#define VIA_FIRE_BUF_SIZE 1024 -#define VIA_NUM_IRQS 4 - - -#define VIA_NUM_BLIT_ENGINES 2 -#define VIA_NUM_BLIT_SLOTS 8 - -struct _drm_via_descriptor; - -typedef struct _drm_via_sg_info { - struct page **pages; - unsigned long num_pages; - struct _drm_via_descriptor **desc_pages; - int num_desc_pages; - int num_desc; - enum dma_data_direction direction; - unsigned char *bounce_buffer; - dma_addr_t chain_start; - uint32_t free_on_sequence; - unsigned int descriptors_per_page; - int aborted; - enum { - dr_via_device_mapped, - dr_via_desc_pages_alloc, - dr_via_pages_locked, - dr_via_pages_alloc, - dr_via_sg_init - } state; -} drm_via_sg_info_t; - -typedef struct _drm_via_blitq { - struct drm_device *dev; - uint32_t cur_blit_handle; - uint32_t done_blit_handle; - unsigned serviced; - unsigned head; - unsigned cur; - unsigned num_free; - unsigned num_outstanding; - unsigned long end; - int aborting; - int is_active; - drm_via_sg_info_t *blits[VIA_NUM_BLIT_SLOTS]; - spinlock_t blit_lock; - wait_queue_head_t blit_queue[VIA_NUM_BLIT_SLOTS]; - wait_queue_head_t busy_queue; - struct work_struct wq; - struct timer_list poll_timer; -} drm_via_blitq_t; - -typedef struct drm_via_ring_buffer { - drm_local_map_t map; - char *virtual_start; -} drm_via_ring_buffer_t; - -typedef uint32_t maskarray_t[5]; - -typedef struct drm_via_irq { - atomic_t irq_received; - uint32_t pending_mask; - uint32_t enable_mask; - wait_queue_head_t irq_queue; -} drm_via_irq_t; - -typedef struct drm_via_private { - drm_via_sarea_t *sarea_priv; - drm_local_map_t *sarea; - drm_local_map_t *fb; - drm_local_map_t *mmio; - unsigned long agpAddr; - wait_queue_head_t decoder_queue[VIA_NR_XVMC_LOCKS]; - char *dma_ptr; - unsigned int dma_low; - unsigned int dma_high; - unsigned int dma_offset; - uint32_t dma_wrap; - volatile uint32_t *last_pause_ptr; - volatile uint32_t *hw_addr_ptr; - drm_via_ring_buffer_t ring; - ktime_t last_vblank; - int last_vblank_valid; - ktime_t nsec_per_vblank; - atomic_t vbl_received; - drm_via_state_t hc_state; - char pci_buf[VIA_PCI_BUF_SIZE]; - const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; - uint32_t num_fire_offsets; - int chipset; - drm_via_irq_t via_irqs[VIA_NUM_IRQS]; - unsigned num_irqs; - maskarray_t *irq_masks; - uint32_t irq_enable_mask; - uint32_t irq_pending_mask; - int *irq_map; - unsigned int idle_fault; - int vram_initialized; - struct drm_mm vram_mm; - int agp_initialized; - struct drm_mm agp_mm; - /** Mapping of userspace keys to mm objects */ - struct idr object_idr; - unsigned long vram_offset; - unsigned long agp_offset; - drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES]; - uint32_t dma_diff; -} drm_via_private_t; - -struct via_file_private { - struct list_head obj_list; -}; - -enum via_family { - VIA_OTHER = 0, /* Baseline */ - VIA_PRO_GROUP_A, /* Another video engine and DMA commands */ - VIA_DX9_0 /* Same video as pro_group_a, but 3D is unsupported */ -}; - -/* VIA MMIO register access */ -static inline u32 via_read(struct drm_via_private *dev_priv, u32 reg) -{ - return readl((void __iomem *)(dev_priv->mmio->handle + reg)); -} - -static inline void via_write(struct drm_via_private *dev_priv, u32 reg, - u32 val) -{ - writel(val, (void __iomem *)(dev_priv->mmio->handle + reg)); -} - -static inline void via_write8(struct drm_via_private *dev_priv, u32 reg, - u32 val) -{ - writeb(val, (void __iomem *)(dev_priv->mmio->handle + reg)); -} - -static inline void via_write8_mask(struct drm_via_private *dev_priv, - u32 reg, u32 mask, u32 val) -{ - u32 tmp; - - tmp = readb((void __iomem *)(dev_priv->mmio->handle + reg)); - tmp = (tmp & ~mask) | (val & mask); - writeb(tmp, (void __iomem *)(dev_priv->mmio->handle + reg)); -} - -/* - * Poll in a loop waiting for 'contidition' to be true. - * Note: A direct replacement with wait_event_interruptible_timeout() - * will not work unless driver is updated to emit wake_up() - * in relevant places that can impact the 'condition' - * - * Returns: - * ret keeps current value if 'condition' becomes true - * ret = -BUSY if timeout happens - * ret = -EINTR if a signal interrupted the waiting period - */ -#define VIA_WAIT_ON( ret, queue, timeout, condition ) \ -do { \ - DECLARE_WAITQUEUE(entry, current); \ - unsigned long end = jiffies + (timeout); \ - add_wait_queue(&(queue), &entry); \ - \ - for (;;) { \ - __set_current_state(TASK_INTERRUPTIBLE); \ - if (condition) \ - break; \ - if (time_after_eq(jiffies, end)) { \ - ret = -EBUSY; \ - break; \ - } \ - schedule_timeout((HZ/100 > 1) ? HZ/100 : 1); \ - if (signal_pending(current)) { \ - ret = -EINTR; \ - break; \ - } \ - } \ - __set_current_state(TASK_RUNNING); \ - remove_wait_queue(&(queue), &entry); \ -} while (0) - -extern int via_init_context(struct drm_device *dev, int context); - -extern int via_do_cleanup_map(struct drm_device *dev); - -extern int via_dma_cleanup(struct drm_device *dev); -extern int via_driver_dma_quiescent(struct drm_device *dev); - -#endif -- cgit v1.2.3-59-g8ed1b From e0a49c786766ebb804592e536dab2eb97da5533f Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:01:59 +0200 Subject: drm/via: Update to the latest via_3d_reg header Updated the 3d_reg header file to match what is used by the openchrome driver. This verifies that the two drivers can use the same header file. The file is a verbatim copy from the openchrome repo - a few style issues will be fixed in following commits. Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-11-sam@ravnborg.org --- drivers/gpu/drm/via/via_3d_reg.h | 395 ++++++++++++++++++++++++++++++--------- 1 file changed, 304 insertions(+), 91 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/via_3d_reg.h b/drivers/gpu/drm/via/via_3d_reg.h index 462375d543b9..fc74647f512a 100644 --- a/drivers/gpu/drm/via/via_3d_reg.h +++ b/drivers/gpu/drm/via/via_3d_reg.h @@ -1,6 +1,6 @@ /* - * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. - * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. + * Copyright 1998-2011 VIA Technologies, Inc. All Rights Reserved. + * Copyright 2001-2011 S3 Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -16,7 +16,7 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. @@ -50,6 +50,7 @@ #define HC_ParaType_Palette 0x0003 #define HC_ParaType_PreCR 0x0010 #define HC_ParaType_Auto 0x00fe +#define INV_ParaType_Dummy 0x00300000 /* Transmission Space */ @@ -176,7 +177,7 @@ /* Command * Command A */ -#define HC_HCmdHeader_MASK 0xfe000000 /*0xffe00000 */ +#define HC_HCmdHeader_MASK 0xfe000000 /*0xffe00000 */ #define HC_HE3Fire_MASK 0x00100000 #define HC_HPMType_MASK 0x000f0000 #define HC_HEFlag_MASK 0x0000e000 @@ -236,6 +237,8 @@ /* Enable Setting */ #define HC_SubA_HEnable 0x0000 +#define HC_HenForce1P_MASK 0x00800000 /* [Force 1 Pipe] */ +#define HC_HenZDCheck_MASK 0x00400000 /* [Z dirty bit settings] */ #define HC_HenTXEnvMap_MASK 0x00200000 #define HC_HenVertexCNT_MASK 0x00100000 #define HC_HenCPUDAZ_MASK 0x00080000 @@ -682,6 +685,12 @@ #define HC_FogDenst_MASK 0x001fff00 #define HC_FogEndL_MASK 0x000000ff +/* Texture subtype definitions + */ +#define HC_SubType_Samp0 0x00000020 +#define HC_SubType_Samp1 0x00000021 + + /* Texture subtype definitions */ #define HC_SubType_Tex0 0x00000000 @@ -762,7 +771,13 @@ #define HC_SubA_HTXnBumpM10 0x0092 #define HC_SubA_HTXnBumpM11 0x0093 #define HC_SubA_HTXnLScale 0x0094 -#define HC_SubA_HTXSMD 0x0000 + +#define HC_SubA_HTXSMD 0x0000 +#define HC_SubA_HTXYUV2RGB1 0x0001 +#define HC_SubA_HTXYUV2RGB2 0x0002 +#define HC_SubA_HTXYUV2RGB3 0x0003 +#define HTXYUV2RGB4BT601 (1<<23) +#define HTXYUV2RGB4BT709 (1<<22) /* HC_SubA_HTXnL012BasH 0x0020 */ #define HC_HTXnL0BasH_MASK 0x000000ff @@ -965,6 +980,7 @@ #define HC_HTXnFM_Lum 0x00100000 #define HC_HTXnFM_Alpha 0x00180000 #define HC_HTXnFM_DX 0x00280000 +#define HC_HTXnFM_YUV 0x00300000 #define HC_HTXnFM_ARGB16 0x00880000 #define HC_HTXnFM_ARGB32 0x00980000 #define HC_HTXnFM_ABGR16 0x00a80000 @@ -995,6 +1011,12 @@ #define HC_HTXnFM_DX1 (HC_HTXnFM_DX | 0x00010000) #define HC_HTXnFM_DX23 (HC_HTXnFM_DX | 0x00020000) #define HC_HTXnFM_DX45 (HC_HTXnFM_DX | 0x00030000) +/* YUV package mode */ +#define HC_HTXnFM_YUY2 (HC_HTXnFM_YUV | 0x00000000) +/* YUV planner mode */ +#define HC_HTXnFM_YV12 (HC_HTXnFM_YUV | 0x00040000) +/* YUV planner mode */ +#define HC_HTXnFM_IYUV (HC_HTXnFM_YUV | 0x00040000) #define HC_HTXnFM_RGB555 (HC_HTXnFM_ARGB16 | 0x00000000) #define HC_HTXnFM_RGB565 (HC_HTXnFM_ARGB16 | 0x00010000) #define HC_HTXnFM_ARGB1555 (HC_HTXnFM_ARGB16 | 0x00020000) @@ -1023,6 +1045,13 @@ #define HC_HTXnLoc_Local 0x00000000 #define HC_HTXnLoc_Sys 0x00000002 #define HC_HTXnLoc_AGP 0x00000003 + +/* Video Texture */ +#define HC_HTXnYUV2RGBMode_RGB 0x00000000 +#define HC_HTXnYUV2RGBMode_SDTV 0x00000001 +#define HC_HTXnYUV2RGBMode_HDTV 0x00000002 +#define HC_HTXnYUV2RGBMode_TABLE 0x00000003 + /* HC_SubA_HTXnTRAH 0x007f */ #define HC_HTXnTRAH_MASK 0x00ff0000 @@ -1330,9 +1359,9 @@ */ #define HC_HFthRTXA_MASK 0x000000ff -/****************************************************************************** +/**************************************************************************** ** Define the Halcyon Internal register access constants. For simulator only. -******************************************************************************/ +****************************************************************************/ #define HC_SIMA_HAGPBstL 0x0000 #define HC_SIMA_HAGPBendL 0x0001 #define HC_SIMA_HAGPCMNT 0x0002 @@ -1477,80 +1506,154 @@ #define HC_SIMA_TX0TX1_OFF 0x0050 /*---- start of texture 1 setting ---- */ -#define HC_SIMA_HTX1L0BasL (HC_SIMA_HTX0L0BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L1BasL (HC_SIMA_HTX0L1BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L2BasL (HC_SIMA_HTX0L2BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L3BasL (HC_SIMA_HTX0L3BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L4BasL (HC_SIMA_HTX0L4BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L5BasL (HC_SIMA_HTX0L5BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6BasL (HC_SIMA_HTX0L6BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L7BasL (HC_SIMA_HTX0L7BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L8BasL (HC_SIMA_HTX0L8BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L9BasL (HC_SIMA_HTX0L9BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LaBasL (HC_SIMA_HTX0LaBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LbBasL (HC_SIMA_HTX0LbBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LcBasL (HC_SIMA_HTX0LcBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LdBasL (HC_SIMA_HTX0LdBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LeBasL (HC_SIMA_HTX0LeBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LfBasL (HC_SIMA_HTX0LfBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L10BasL (HC_SIMA_HTX0L10BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L11BasL (HC_SIMA_HTX0L11BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L012BasH (HC_SIMA_HTX0L012BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L345BasH (HC_SIMA_HTX0L345BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L678BasH (HC_SIMA_HTX0L678BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L9abBasH (HC_SIMA_HTX0L9abBasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LcdeBasH (HC_SIMA_HTX0LcdeBasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1Lf1011BasH (HC_SIMA_HTX0Lf1011BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0Pit (HC_SIMA_HTX0L0Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L1Pit (HC_SIMA_HTX0L1Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L2Pit (HC_SIMA_HTX0L2Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L3Pit (HC_SIMA_HTX0L3Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L4Pit (HC_SIMA_HTX0L4Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L5Pit (HC_SIMA_HTX0L5Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6Pit (HC_SIMA_HTX0L6Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L7Pit (HC_SIMA_HTX0L7Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L8Pit (HC_SIMA_HTX0L8Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L9Pit (HC_SIMA_HTX0L9Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LaPit (HC_SIMA_HTX0LaPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LbPit (HC_SIMA_HTX0LbPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LcPit (HC_SIMA_HTX0LcPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LdPit (HC_SIMA_HTX0LdPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LePit (HC_SIMA_HTX0LePit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LfPit (HC_SIMA_HTX0LfPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L10Pit (HC_SIMA_HTX0L10Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L11Pit (HC_SIMA_HTX0L11Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0_5WE (HC_SIMA_HTX0L0_5WE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6_bWE (HC_SIMA_HTX0L6_bWE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1Lc_11WE (HC_SIMA_HTX0Lc_11WE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0_5HE (HC_SIMA_HTX0L0_5HE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6_bHE (HC_SIMA_HTX0L6_bHE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1Lc_11HE (HC_SIMA_HTX0Lc_11HE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0OS (HC_SIMA_HTX0L0OS + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TB (HC_SIMA_HTX0TB + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1MPMD (HC_SIMA_HTX0MPMD + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1CLODu (HC_SIMA_HTX0CLODu + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1FM (HC_SIMA_HTX0FM + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TRCH (HC_SIMA_HTX0TRCH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TRCL (HC_SIMA_HTX0TRCL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBC (HC_SIMA_HTX0TBC + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TRAH (HC_SIMA_HTX0TRAH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LTC (HC_SIMA_HTX0LTC + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LTA (HC_SIMA_HTX0LTA + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLCsat (HC_SIMA_HTX0TBLCsat + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLCop (HC_SIMA_HTX0TBLCop + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLMPfog (HC_SIMA_HTX0TBLMPfog + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLAsat (HC_SIMA_HTX0TBLAsat + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCa (HC_SIMA_HTX0TBLRCa + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCb (HC_SIMA_HTX0TBLRCb + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCc (HC_SIMA_HTX0TBLRCc + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCbias (HC_SIMA_HTX0TBLRCbias + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRAa (HC_SIMA_HTX0TBLRAa + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRFog (HC_SIMA_HTX0TBLRFog + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM00 (HC_SIMA_HTX0BumpM00 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM01 (HC_SIMA_HTX0BumpM01 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM10 (HC_SIMA_HTX0BumpM10 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM11 (HC_SIMA_HTX0BumpM11 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LScale (HC_SIMA_HTX0LScale + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0BasL \ + (HC_SIMA_HTX0L0BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L1BasL \ + (HC_SIMA_HTX0L1BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L2BasL \ + (HC_SIMA_HTX0L2BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L3BasL \ + (HC_SIMA_HTX0L3BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L4BasL (\ + HC_SIMA_HTX0L4BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L5BasL \ + (HC_SIMA_HTX0L5BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6BasL \ + (HC_SIMA_HTX0L6BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L7BasL \ + (HC_SIMA_HTX0L7BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L8BasL \ + (HC_SIMA_HTX0L8BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9BasL \ + (HC_SIMA_HTX0L9BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LaBasL \ + (HC_SIMA_HTX0LaBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LbBasL \ + (HC_SIMA_HTX0LbBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcBasL \ + (HC_SIMA_HTX0LcBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LdBasL \ + (HC_SIMA_HTX0LdBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LeBasL \ + (HC_SIMA_HTX0LeBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LfBasL \ + (HC_SIMA_HTX0LfBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L10BasL \ + (HC_SIMA_HTX0L10BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L11BasL \ + (HC_SIMA_HTX0L11BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L012BasH \ + (HC_SIMA_HTX0L012BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L345BasH \ + (HC_SIMA_HTX0L345BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L678BasH \ + (HC_SIMA_HTX0L678BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9abBasH \ + (HC_SIMA_HTX0L9abBasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcdeBasH \ + (HC_SIMA_HTX0LcdeBasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lf1011BasH \ + (HC_SIMA_HTX0Lf1011BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0Pit \ + (HC_SIMA_HTX0L0Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L1Pit \ + (HC_SIMA_HTX0L1Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L2Pit \ + (HC_SIMA_HTX0L2Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L3Pit \ + (HC_SIMA_HTX0L3Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L4Pit \ + (HC_SIMA_HTX0L4Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L5Pit \ + (HC_SIMA_HTX0L5Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6Pit \ + (HC_SIMA_HTX0L6Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L7Pit \ + (HC_SIMA_HTX0L7Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L8Pit \ + (HC_SIMA_HTX0L8Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9Pit \ + (HC_SIMA_HTX0L9Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LaPit \ + (HC_SIMA_HTX0LaPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LbPit \ + (HC_SIMA_HTX0LbPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcPit \ + (HC_SIMA_HTX0LcPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LdPit \ + (HC_SIMA_HTX0LdPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LePit \ + (HC_SIMA_HTX0LePit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LfPit \ + (HC_SIMA_HTX0LfPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L10Pit \ + (HC_SIMA_HTX0L10Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L11Pit \ + (HC_SIMA_HTX0L11Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0_5WE \ + (HC_SIMA_HTX0L0_5WE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6_bWE \ + (HC_SIMA_HTX0L6_bWE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lc_11WE \ + (HC_SIMA_HTX0Lc_11WE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0_5HE \ + (HC_SIMA_HTX0L0_5HE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6_bHE \ + (HC_SIMA_HTX0L6_bHE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lc_11HE \ + (HC_SIMA_HTX0Lc_11HE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0OS \ + (HC_SIMA_HTX0L0OS + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TB \ + (HC_SIMA_HTX0TB + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1MPMD \ + (HC_SIMA_HTX0MPMD + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1CLODu \ + (HC_SIMA_HTX0CLODu + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1FM \ + (HC_SIMA_HTX0FM + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRCH \ + (HC_SIMA_HTX0TRCH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRCL \ + (HC_SIMA_HTX0TRCL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBC \ + (HC_SIMA_HTX0TBC + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRAH \ + (HC_SIMA_HTX0TRAH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LTC \ + (HC_SIMA_HTX0LTC + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LTA \ + (HC_SIMA_HTX0LTA + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLCsat \ + (HC_SIMA_HTX0TBLCsat + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLCop \ + (HC_SIMA_HTX0TBLCop + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLMPfog \ + (HC_SIMA_HTX0TBLMPfog + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLAsat \ + (HC_SIMA_HTX0TBLAsat + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCa \ + (HC_SIMA_HTX0TBLRCa + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCb \ + (HC_SIMA_HTX0TBLRCb + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCc \ + (HC_SIMA_HTX0TBLRCc + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCbias \ + (HC_SIMA_HTX0TBLRCbias + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRAa \ + (HC_SIMA_HTX0TBLRAa + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRFog \ + (HC_SIMA_HTX0TBLRFog + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM00 \ + (HC_SIMA_HTX0BumpM00 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM01 \ + (HC_SIMA_HTX0BumpM01 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM10 \ + (HC_SIMA_HTX0BumpM10 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM11 \ + (HC_SIMA_HTX0BumpM11 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LScale \ + (HC_SIMA_HTX0LScale + HC_SIMA_TX0TX1_OFF) /*---- end of texture 1 setting ---- 0xaf */ #define HC_SIMA_HTXSMD 0x00b0 @@ -1580,9 +1683,9 @@ #define HC_SIMA_HRErr 0x0445 #define HC_SIMA_FIFOstatus 0x0446 -/****************************************************************************** +/**************************************************************************** ** Define the AGP command header. -******************************************************************************/ +****************************************************************************/ #define HC_ACMD_MASK 0xfe000000 #define HC_ACMD_SUB_MASK 0x0c000000 #define HC_ACMD_HCmdA 0xee000000 @@ -1605,18 +1708,18 @@ #define HC_ACMD_H4COUNT_MASK 0x01fffe00 #define HC_ACMD_H4COUNT_SHIFT 9 -/******************************************************************************** +/***************************************************************************** ** Define Header -********************************************************************************/ -#define HC_HEADER2 0xF210F110 +*****************************************************************************/ +#define HC_HEADER2 0xF210F110 -/******************************************************************************** +/***************************************************************************** ** Define Dummy Value -********************************************************************************/ -#define HC_DUMMY 0xCCCCCCCC -/******************************************************************************** +*****************************************************************************/ +#define HC_DUMMY 0xCCCCCCCC +/***************************************************************************** ** Define for DMA use -********************************************************************************/ +*****************************************************************************/ #define HALCYON_HEADER2 0XF210F110 #define HALCYON_FIRECMD 0XEE100000 #define HALCYON_FIREMASK 0XFFF00000 @@ -1643,8 +1746,118 @@ #define HC_HAGPBpID_STOP 0x00000002 #define HC_HAGPBpH_MASK 0x00ffffff + #define VIA_VIDEO_HEADER5 0xFE040000 #define VIA_VIDEO_HEADER6 0xFE050000 #define VIA_VIDEO_HEADER7 0xFE060000 #define VIA_VIDEOMASK 0xFFFF0000 + +/***************************************************************************** +** Define for H5 DMA use +*****************************************************************************/ +#define H5_HC_DUMMY 0xCC000000 + +/* Command Header Type */ +#define INV_DUMMY_MASK 0xFF000000 +#define INV_AGPHeader0 0xFE000000 +#define INV_AGPHeader1 0xFE010000 +#define INV_AGPHeader2 0xFE020000 +#define INV_AGPHeader3 0xFE030000 +#define INV_AGPHeader4 0xFE040000 +#define INV_AGPHeader5 0xFE050000 +#define INV_AGPHeader6 0xFE060000 +#define INV_AGPHeader7 0xFE070000 +#define INV_AGPHeader9 0xFE090000 +#define INV_AGPHeaderA 0xFE0A0000 +#define INV_AGPHeader40 0xFE400000 +#define INV_AGPHeader41 0xFE410000 +#define INV_AGPHeader43 0xFE430000 +#define INV_AGPHeader45 0xFE450000 +#define INV_AGPHeader47 0xFE470000 +#define INV_AGPHeader4A 0xFE4A0000 +#define INV_AGPHeader82 0xFE820000 +#define INV_AGPHeader83 0xFE830000 +#define INV_AGPHeader_MASK 0xFFFF0000 +#define INV_AGPHeader2A 0xFE2A0000 +#define INV_AGPHeader25 0xFE250000 +#define INV_AGPHeader20 0xFE200000 +#define INV_AGPHeader23 0xFE230000 +#define INV_AGPHeaderE2 0xFEE20000 +#define INV_AGPHeaderE3 0xFEE30000 + +/*Transmission IO Space*/ +#define INV_REG_CR_TRANS 0x041C +#define INV_REG_CR_BEGIN 0x0420 +#define INV_REG_CR_END 0x0438 + +#define INV_REG_3D_TRANS 0x043C +#define INV_REG_3D_BEGIN 0x0440 +#define INV_REG_3D_END 0x06FC + +#define INV_ParaType_CmdVdata 0x0000 + +/* H5 Enable Setting + */ +#define INV_HC_SubA_HEnable1 0x00 + +#define INV_HC_HenAT4ALLRT_MASK 0x00100000 +#define INV_HC_HenATMRT3_MASK 0x00080000 +#define INV_HC_HenATMRT2_MASK 0x00040000 +#define INV_HC_HenATMRT1_MASK 0x00020000 +#define INV_HC_HenATMRT0_MASK 0x00010000 +#define INV_HC_HenSCMRT3_MASK 0x00008000 +#define INV_HC_HenSCMRT2_MASK 0x00004000 +#define INV_HC_HenSCMRT1_MASK 0x00002000 +#define INV_HC_HenSCMRT0_MASK 0x00001000 +#define INV_HC_HenFOGMRT3_MASK 0x00000800 +#define INV_HC_HenFOGMRT2_MASK 0x00000400 +#define INV_HC_HenFOGMRT1_MASK 0x00000200 +#define INV_HC_HenFOGMRT0_MASK 0x00000100 +#define INV_HC_HenABLMRT3_MASK 0x00000080 +#define INV_HC_HenABLMRT2_MASK 0x00000040 +#define INV_HC_HenABLMRT1_MASK 0x00000020 +#define INV_HC_HenABLMRT0_MASK 0x00000010 +#define INV_HC_HenDTMRT3_MASK 0x00000008 +#define INV_HC_HenDTMRT2_MASK 0x00000004 +#define INV_HC_HenDTMRT1_MASK 0x00000002 +#define INV_HC_HenDTMRT0_MASK 0x00000001 + +#define INV_HC_SubA_HEnable2 0x01 + +#define INV_HC_HenLUL2DR_MASK 0x00800000 +#define INV_HC_HenLDIAMOND_MASK 0x00400000 +#define INV_HC_HenPSPRITE_MASK 0x00200000 +#define INV_HC_HenC2S_MASK 0x00100000 +#define INV_HC_HenFOGPP_MASK 0x00080000 +#define INV_HC_HenSCPP_MASK 0x00040000 +#define INV_HC_HenCPP_MASK 0x00020000 +#define INV_HC_HenCZ_MASK 0x00002000 +#define INV_HC_HenVC_MASK 0x00001000 +#define INV_HC_HenCL_MASK 0x00000800 +#define INV_HC_HenPS_MASK 0x00000400 +#define INV_HC_HenWCZ_MASK 0x00000200 +#define INV_HC_HenTXCH_MASK 0x00000100 +#define INV_HC_HenBFCULL_MASK 0x00000080 +#define INV_HC_HenCW_MASK 0x00000040 +#define INV_HC_HenAA_MASK 0x00000020 +#define INV_HC_HenST_MASK 0x00000010 +#define INV_HC_HenZT_MASK 0x00000008 +#define INV_HC_HenZW_MASK 0x00000004 +#define INV_HC_HenSP_MASK 0x00000002 +#define INV_HC_HenLP_MASK 0x00000001 + +/* H5 Miscellaneous Settings + */ +#define INV_HC_SubA_HCClipTL 0x0080 +#define INV_HC_SubA_HCClipBL 0x0081 +#define INV_HC_SubA_HSClipTL 0x0082 +#define INV_HC_SubA_HSClipBL 0x0083 +#define INV_HC_SubA_HSolidCL 0x0086 +#define INV_HC_SubA_HSolidCH 0x0087 +#define INV_HC_SubA_HGBClipGL 0x0088 +#define INV_HC_SubA_HGBClipGR 0x0089 + + +#define INV_HC_ParaType_Vetex 0x00040000 + #endif -- cgit v1.2.3-59-g8ed1b From e1df7580bb2947a2fcff80d4342eb4ededbf4873 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:02:00 +0200 Subject: drm/via: Use SPDX tag for MIT license in via_3d_reg header The license for the via_3d_reg header is MIT - so use the shorter SPDX tag to identify the license. Suggested-by: Thomas Zimmermann Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-12-sam@ravnborg.org --- drivers/gpu/drm/via/via_3d_reg.h | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/via_3d_reg.h b/drivers/gpu/drm/via/via_3d_reg.h index fc74647f512a..8796fc9fd44b 100644 --- a/drivers/gpu/drm/via/via_3d_reg.h +++ b/drivers/gpu/drm/via/via_3d_reg.h @@ -1,25 +1,7 @@ +/* SPDX-License-Identifier: MIT */ /* * Copyright 1998-2011 VIA Technologies, Inc. All Rights Reserved. * Copyright 2001-2011 S3 Graphics, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sub license, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. */ #ifndef VIA_3D_REG_H -- cgit v1.2.3-59-g8ed1b From 3579ca95759e40f3868ad5a75b4c34710858ad2b Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:02:01 +0200 Subject: drm/via: Make macros readable in the via_3d_reg header The macros for texture 1 setting all used continuation on a new line, resulting in a highly ureadable definition. Merge the lines so they are each on a single line. As a nice side-effect this fixes a number of checkpatch warnings: "WARNING: please, no spaces at the start of a line". Suggested-by: Thomas Zimmermann Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-13-sam@ravnborg.org --- drivers/gpu/drm/via/via_3d_reg.h | 222 +++++++++++++-------------------------- 1 file changed, 74 insertions(+), 148 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/via_3d_reg.h b/drivers/gpu/drm/via/via_3d_reg.h index 8796fc9fd44b..4a884384e5b8 100644 --- a/drivers/gpu/drm/via/via_3d_reg.h +++ b/drivers/gpu/drm/via/via_3d_reg.h @@ -1488,154 +1488,80 @@ #define HC_SIMA_TX0TX1_OFF 0x0050 /*---- start of texture 1 setting ---- */ -#define HC_SIMA_HTX1L0BasL \ - (HC_SIMA_HTX0L0BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L1BasL \ - (HC_SIMA_HTX0L1BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L2BasL \ - (HC_SIMA_HTX0L2BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L3BasL \ - (HC_SIMA_HTX0L3BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L4BasL (\ - HC_SIMA_HTX0L4BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L5BasL \ - (HC_SIMA_HTX0L5BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6BasL \ - (HC_SIMA_HTX0L6BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L7BasL \ - (HC_SIMA_HTX0L7BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L8BasL \ - (HC_SIMA_HTX0L8BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L9BasL \ - (HC_SIMA_HTX0L9BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LaBasL \ - (HC_SIMA_HTX0LaBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LbBasL \ - (HC_SIMA_HTX0LbBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LcBasL \ - (HC_SIMA_HTX0LcBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LdBasL \ - (HC_SIMA_HTX0LdBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LeBasL \ - (HC_SIMA_HTX0LeBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LfBasL \ - (HC_SIMA_HTX0LfBasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L10BasL \ - (HC_SIMA_HTX0L10BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L11BasL \ - (HC_SIMA_HTX0L11BasL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L012BasH \ - (HC_SIMA_HTX0L012BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L345BasH \ - (HC_SIMA_HTX0L345BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L678BasH \ - (HC_SIMA_HTX0L678BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L9abBasH \ - (HC_SIMA_HTX0L9abBasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LcdeBasH \ - (HC_SIMA_HTX0LcdeBasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1Lf1011BasH \ - (HC_SIMA_HTX0Lf1011BasH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0Pit \ - (HC_SIMA_HTX0L0Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L1Pit \ - (HC_SIMA_HTX0L1Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L2Pit \ - (HC_SIMA_HTX0L2Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L3Pit \ - (HC_SIMA_HTX0L3Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L4Pit \ - (HC_SIMA_HTX0L4Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L5Pit \ - (HC_SIMA_HTX0L5Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6Pit \ - (HC_SIMA_HTX0L6Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L7Pit \ - (HC_SIMA_HTX0L7Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L8Pit \ - (HC_SIMA_HTX0L8Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L9Pit \ - (HC_SIMA_HTX0L9Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LaPit \ - (HC_SIMA_HTX0LaPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LbPit \ - (HC_SIMA_HTX0LbPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LcPit \ - (HC_SIMA_HTX0LcPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LdPit \ - (HC_SIMA_HTX0LdPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LePit \ - (HC_SIMA_HTX0LePit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LfPit \ - (HC_SIMA_HTX0LfPit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L10Pit \ - (HC_SIMA_HTX0L10Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L11Pit \ - (HC_SIMA_HTX0L11Pit + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0_5WE \ - (HC_SIMA_HTX0L0_5WE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6_bWE \ - (HC_SIMA_HTX0L6_bWE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1Lc_11WE \ - (HC_SIMA_HTX0Lc_11WE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0_5HE \ - (HC_SIMA_HTX0L0_5HE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L6_bHE \ - (HC_SIMA_HTX0L6_bHE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1Lc_11HE \ - (HC_SIMA_HTX0Lc_11HE + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1L0OS \ - (HC_SIMA_HTX0L0OS + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TB \ - (HC_SIMA_HTX0TB + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1MPMD \ - (HC_SIMA_HTX0MPMD + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1CLODu \ - (HC_SIMA_HTX0CLODu + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1FM \ - (HC_SIMA_HTX0FM + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TRCH \ - (HC_SIMA_HTX0TRCH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TRCL \ - (HC_SIMA_HTX0TRCL + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBC \ - (HC_SIMA_HTX0TBC + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TRAH \ - (HC_SIMA_HTX0TRAH + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LTC \ - (HC_SIMA_HTX0LTC + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LTA \ - (HC_SIMA_HTX0LTA + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLCsat \ - (HC_SIMA_HTX0TBLCsat + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLCop \ - (HC_SIMA_HTX0TBLCop + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLMPfog \ - (HC_SIMA_HTX0TBLMPfog + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLAsat \ - (HC_SIMA_HTX0TBLAsat + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCa \ - (HC_SIMA_HTX0TBLRCa + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCb \ - (HC_SIMA_HTX0TBLRCb + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCc \ - (HC_SIMA_HTX0TBLRCc + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRCbias \ - (HC_SIMA_HTX0TBLRCbias + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRAa \ - (HC_SIMA_HTX0TBLRAa + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1TBLRFog \ - (HC_SIMA_HTX0TBLRFog + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM00 \ - (HC_SIMA_HTX0BumpM00 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM01 \ - (HC_SIMA_HTX0BumpM01 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM10 \ - (HC_SIMA_HTX0BumpM10 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1BumpM11 \ - (HC_SIMA_HTX0BumpM11 + HC_SIMA_TX0TX1_OFF) -#define HC_SIMA_HTX1LScale \ - (HC_SIMA_HTX0LScale + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0BasL (HC_SIMA_HTX0L0BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L1BasL (HC_SIMA_HTX0L1BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L2BasL (HC_SIMA_HTX0L2BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L3BasL (HC_SIMA_HTX0L3BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L4BasL (HC_SIMA_HTX0L4BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L5BasL (HC_SIMA_HTX0L5BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6BasL (HC_SIMA_HTX0L6BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L7BasL (HC_SIMA_HTX0L7BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L8BasL (HC_SIMA_HTX0L8BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9BasL (HC_SIMA_HTX0L9BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LaBasL (HC_SIMA_HTX0LaBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LbBasL (HC_SIMA_HTX0LbBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcBasL (HC_SIMA_HTX0LcBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LdBasL (HC_SIMA_HTX0LdBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LeBasL (HC_SIMA_HTX0LeBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LfBasL (HC_SIMA_HTX0LfBasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L10BasL (HC_SIMA_HTX0L10BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L11BasL (HC_SIMA_HTX0L11BasL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L012BasH (HC_SIMA_HTX0L012BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L345BasH (HC_SIMA_HTX0L345BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L678BasH (HC_SIMA_HTX0L678BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9abBasH (HC_SIMA_HTX0L9abBasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcdeBasH (HC_SIMA_HTX0LcdeBasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lf1011BasH (HC_SIMA_HTX0Lf1011BasH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0Pit (HC_SIMA_HTX0L0Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L1Pit (HC_SIMA_HTX0L1Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L2Pit (HC_SIMA_HTX0L2Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L3Pit (HC_SIMA_HTX0L3Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L4Pit (HC_SIMA_HTX0L4Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L5Pit (HC_SIMA_HTX0L5Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6Pit (HC_SIMA_HTX0L6Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L7Pit (HC_SIMA_HTX0L7Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L8Pit (HC_SIMA_HTX0L8Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L9Pit (HC_SIMA_HTX0L9Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LaPit (HC_SIMA_HTX0LaPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LbPit (HC_SIMA_HTX0LbPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LcPit (HC_SIMA_HTX0LcPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LdPit (HC_SIMA_HTX0LdPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LePit (HC_SIMA_HTX0LePit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LfPit (HC_SIMA_HTX0LfPit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L10Pit (HC_SIMA_HTX0L10Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L11Pit (HC_SIMA_HTX0L11Pit + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0_5WE (HC_SIMA_HTX0L0_5WE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6_bWE (HC_SIMA_HTX0L6_bWE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lc_11WE (HC_SIMA_HTX0Lc_11WE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0_5HE (HC_SIMA_HTX0L0_5HE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L6_bHE (HC_SIMA_HTX0L6_bHE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1Lc_11HE (HC_SIMA_HTX0Lc_11HE + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1L0OS (HC_SIMA_HTX0L0OS + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TB (HC_SIMA_HTX0TB + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1MPMD (HC_SIMA_HTX0MPMD + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1CLODu (HC_SIMA_HTX0CLODu + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1FM (HC_SIMA_HTX0FM + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRCH (HC_SIMA_HTX0TRCH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRCL (HC_SIMA_HTX0TRCL + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBC (HC_SIMA_HTX0TBC + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TRAH (HC_SIMA_HTX0TRAH + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LTC (HC_SIMA_HTX0LTC + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LTA (HC_SIMA_HTX0LTA + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLCsat (HC_SIMA_HTX0TBLCsat + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLCop (HC_SIMA_HTX0TBLCop + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLMPfog (HC_SIMA_HTX0TBLMPfog + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLAsat (HC_SIMA_HTX0TBLAsat + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCa (HC_SIMA_HTX0TBLRCa + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCb (HC_SIMA_HTX0TBLRCb + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCc (HC_SIMA_HTX0TBLRCc + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRCbias (HC_SIMA_HTX0TBLRCbias + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRAa (HC_SIMA_HTX0TBLRAa + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1TBLRFog (HC_SIMA_HTX0TBLRFog + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM00 (HC_SIMA_HTX0BumpM00 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM01 (HC_SIMA_HTX0BumpM01 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM10 (HC_SIMA_HTX0BumpM10 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1BumpM11 (HC_SIMA_HTX0BumpM11 + HC_SIMA_TX0TX1_OFF) +#define HC_SIMA_HTX1LScale (HC_SIMA_HTX0LScale + HC_SIMA_TX0TX1_OFF) /*---- end of texture 1 setting ---- 0xaf */ #define HC_SIMA_HTXSMD 0x00b0 -- cgit v1.2.3-59-g8ed1b From 5f9cfd8243ee6f69e0d82519bae463e81411f439 Mon Sep 17 00:00:00 2001 From: Sam Ravnborg Date: Wed, 13 Jul 2022 19:02:02 +0200 Subject: drm/via: Fix style issues in via_3d_reg header - Fix comments using wrong style - Drop repeated word This fixes all checkpatch complains and makes the file a little bit easier to read - as the eye is not distracted by the style violations. Suggested-by: Thomas Zimmermann Signed-off-by: Sam Ravnborg Acked-by: Thomas Zimmermann Cc: Kevin Brace Link: https://patchwork.freedesktop.org/patch/msgid/20220713170202.1798216-14-sam@ravnborg.org --- drivers/gpu/drm/via/via_3d_reg.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/via/via_3d_reg.h b/drivers/gpu/drm/via/via_3d_reg.h index 4a884384e5b8..eb848508b12b 100644 --- a/drivers/gpu/drm/via/via_3d_reg.h +++ b/drivers/gpu/drm/via/via_3d_reg.h @@ -156,7 +156,7 @@ #define HC_HSPXOS_SHIFT 12 #define HC_HSPYOS_MASK 0x00000fff -/* Command +/* * Command A */ #define HC_HCmdHeader_MASK 0xfe000000 /*0xffe00000 */ @@ -1342,8 +1342,8 @@ #define HC_HFthRTXA_MASK 0x000000ff /**************************************************************************** -** Define the Halcyon Internal register access constants. For simulator only. -****************************************************************************/ + * Define the Halcyon Internal register access constants. For simulator only. + ***************************************************************************/ #define HC_SIMA_HAGPBstL 0x0000 #define HC_SIMA_HAGPBendL 0x0001 #define HC_SIMA_HAGPCMNT 0x0002 @@ -1592,8 +1592,8 @@ #define HC_SIMA_FIFOstatus 0x0446 /**************************************************************************** -** Define the AGP command header. -****************************************************************************/ + * Define the AGP command header. + ***************************************************************************/ #define HC_ACMD_MASK 0xfe000000 #define HC_ACMD_SUB_MASK 0x0c000000 #define HC_ACMD_HCmdA 0xee000000 @@ -1617,17 +1617,17 @@ #define HC_ACMD_H4COUNT_SHIFT 9 /***************************************************************************** -** Define Header -*****************************************************************************/ + * Define Header + ****************************************************************************/ #define HC_HEADER2 0xF210F110 /***************************************************************************** -** Define Dummy Value -*****************************************************************************/ + * Define Dummy Value + ****************************************************************************/ #define HC_DUMMY 0xCCCCCCCC /***************************************************************************** -** Define for DMA use -*****************************************************************************/ + * Define for DMA use + ****************************************************************************/ #define HALCYON_HEADER2 0XF210F110 #define HALCYON_FIRECMD 0XEE100000 #define HALCYON_FIREMASK 0XFFF00000 @@ -1661,8 +1661,8 @@ #define VIA_VIDEOMASK 0xFFFF0000 /***************************************************************************** -** Define for H5 DMA use -*****************************************************************************/ + * Define for H5 DMA use + ****************************************************************************/ #define H5_HC_DUMMY 0xCC000000 /* Command Header Type */ -- cgit v1.2.3-59-g8ed1b From 6fc20a8592f569395aadc9fd97336facdee84f0d Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Sat, 18 Jun 2022 19:43:38 +0200 Subject: drm/ssd130x: Use new regmap bulk write support to drop custom bus Data writes for the ssd130x 4-wire SPI protocol need special handling, due the Data/Command control (D/C) pin having to be toggled prior to the write. The regmap API only allowed drivers to provide .reg_{read,write} callbacks to do per register read/write, but didn't provide a way for drivers to do the same for bulk read/writes. For this reason, a custom regmap bus was used by the driver just to define a bulk write callback that implements the D/C pin toggling. But the regmap API has been extended to support defining bulk read/write handlers, so the custom regmap bus is not needed anymore and could just be dropped. Signed-off-by: Javier Martinez Canillas Acked-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220618174338.2253021-1-javierm@redhat.com --- drivers/gpu/drm/solomon/ssd130x-spi.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/solomon/ssd130x-spi.c b/drivers/gpu/drm/solomon/ssd130x-spi.c index 07802907e39a..19ab4942cb33 100644 --- a/drivers/gpu/drm/solomon/ssd130x-spi.c +++ b/drivers/gpu/drm/solomon/ssd130x-spi.c @@ -18,11 +18,6 @@ struct ssd130x_spi_transport { struct gpio_desc *dc; }; -static const struct regmap_config ssd130x_spi_regmap_config = { - .reg_bits = 8, - .val_bits = 8, -}; - /* * The regmap bus .write handler, it is just a wrapper around spi_write() * but toggling the Data/Command control pin (D/C#). Since for 4-wire SPI @@ -56,17 +51,12 @@ static int ssd130x_spi_read(void *context, const void *reg, size_t reg_size, return -EOPNOTSUPP; } -/* - * A custom bus is needed due the special write that toggles a D/C# pin, - * another option could be to just have a .reg_write() callback but that - * will prevent to do data writes in bulk. - * - * Once the regmap API is extended to support defining a bulk write handler - * in the struct regmap_config, this can be simplified and the bus dropped. - */ -static struct regmap_bus regmap_ssd130x_spi_bus = { +static const struct regmap_config ssd130x_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, .write = ssd130x_spi_write, .read = ssd130x_spi_read, + .can_multi_write = true, }; static int ssd130x_spi_probe(struct spi_device *spi) @@ -90,8 +80,7 @@ static int ssd130x_spi_probe(struct spi_device *spi) t->spi = spi; t->dc = dc; - regmap = devm_regmap_init(dev, ®map_ssd130x_spi_bus, t, - &ssd130x_spi_regmap_config); + regmap = devm_regmap_init(dev, NULL, t, &ssd130x_spi_regmap_config); if (IS_ERR(regmap)) return PTR_ERR(regmap); -- cgit v1.2.3-59-g8ed1b From fbc1fdaa8338ec4ebd862d918a0ce3e12033e8a3 Mon Sep 17 00:00:00 2001 From: Pin-Yen Lin Date: Thu, 14 Jul 2022 17:39:20 +0800 Subject: drm/bridge: it6505: Power on downstream device in .atomic_enable Send DPCD DP_SET_POWER_D0 command to the monitor in .atomic_enable callback. Without this command, some monitors won't show up again after changing the resolution. Fixes: 46ca7da7f1e8 ("drm/bridge: it6505: Send DPCD SET_POWER to downstream") Signed-off-by: Pin-Yen Lin Reviewed-by: Allen Chen Fixes: 46ca7da7f1e8 ("drm/bridge: it6505: Send DPCD SET_POWER to downstream") Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220714173715.v2.1.I85af54e9ceda74ec69f661852825845f983fc343@changeid --- drivers/gpu/drm/bridge/ite-it6505.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 4b673c4792d7..e5626035f311 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -2945,6 +2945,9 @@ static void it6505_bridge_atomic_enable(struct drm_bridge *bridge, if (ret) dev_err(dev, "Failed to setup AVI infoframe: %d", ret); + it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link, + DP_SET_POWER_D0); + it6505_update_video_parameter(it6505, mode); ret = it6505_send_video_infoframe(it6505, &frame); -- cgit v1.2.3-59-g8ed1b From 899e90cf20a9d0eaec8cbc8e1ca1d05278afb6d7 Mon Sep 17 00:00:00 2001 From: allen chen Date: Fri, 15 Jul 2022 10:49:05 +0800 Subject: drm/bridge: it6505: Modified power sequence Change power sequence to meet it6505 data sheet requirement when boot on. Signed-off-by: Pin-Yen Lin Signed-off-by: Allen Chen Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220715024910.12578-2-allen.chen@ite.com.tw --- drivers/gpu/drm/bridge/ite-it6505.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index e5626035f311..8f5bb09ae252 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3047,7 +3047,7 @@ static int it6505_init_pdata(struct it6505 *it6505) return PTR_ERR(pdata->ovdd); } - pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(pdata->gpiod_reset)) { dev_err(dev, "gpiod_reset gpio not found"); return PTR_ERR(pdata->gpiod_reset); -- cgit v1.2.3-59-g8ed1b From 86088f88a25c76baac304b6f887e5da2c30c4e07 Mon Sep 17 00:00:00 2001 From: allen chen Date: Fri, 15 Jul 2022 10:49:06 +0800 Subject: drm/bridge: it6505: Add i2c api power on check Use i2c bus to read/write when it6505 power off will occur i2c error. Add this check will prevent i2c error when it6505 power off. Signed-off-by: Pin-Yen Lin Signed-off-by: Allen Chen Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220715024910.12578-3-allen.chen@ite.com.tw --- drivers/gpu/drm/bridge/ite-it6505.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 8f5bb09ae252..a30a31b21542 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -506,6 +506,9 @@ static int it6505_read(struct it6505 *it6505, unsigned int reg_addr) int err; struct device *dev = &it6505->client->dev; + if (!it6505->powered) + return -ENODEV; + err = regmap_read(it6505->regmap, reg_addr, &value); if (err < 0) { dev_err(dev, "read failed reg[0x%x] err: %d", reg_addr, err); @@ -521,6 +524,9 @@ static int it6505_write(struct it6505 *it6505, unsigned int reg_addr, int err; struct device *dev = &it6505->client->dev; + if (!it6505->powered) + return -ENODEV; + err = regmap_write(it6505->regmap, reg_addr, reg_val); if (err < 0) { @@ -538,6 +544,9 @@ static int it6505_set_bits(struct it6505 *it6505, unsigned int reg, int err; struct device *dev = &it6505->client->dev; + if (!it6505->powered) + return -ENODEV; + err = regmap_update_bits(it6505->regmap, reg, mask, value); if (err < 0) { dev_err(dev, "write reg[0x%x] = 0x%x mask = 0x%x failed err %d", @@ -2559,13 +2568,12 @@ static int it6505_poweron(struct it6505 *it6505) usleep_range(10000, 20000); } + it6505->powered = true; it6505_reset_logic(it6505); it6505_int_mask_enable(it6505); it6505_init(it6505); it6505_lane_off(it6505); - it6505->powered = true; - return 0; } -- cgit v1.2.3-59-g8ed1b From 1a217205bd2643272e32ec38926250aa6c29a854 Mon Sep 17 00:00:00 2001 From: allen chen Date: Fri, 15 Jul 2022 10:49:07 +0800 Subject: drm/bridge: it6505: Modified video clock calculation and video debug message Speed up video clock calculation and remove redundant video debug message. Signed-off-by: Pin-Yen Lin Signed-off-by: Allen Chen Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220715024910.12578-4-allen.chen@ite.com.tw --- drivers/gpu/drm/bridge/ite-it6505.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index a30a31b21542..f9251ec49bf0 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -691,7 +691,7 @@ static void it6505_calc_video_info(struct it6505 *it6505) DRM_DEV_DEBUG_DRIVER(dev, "hactive_start:%d, vactive_start:%d", hdes, vdes); - for (i = 0; i < 10; i++) { + for (i = 0; i < 3; i++) { it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER, ENABLE_PCLK_COUNTER); usleep_range(10000, 15000); @@ -708,7 +708,7 @@ static void it6505_calc_video_info(struct it6505 *it6505) return; } - sum /= 10; + sum /= 3; pclk = 13500 * 2048 / sum; it6505->video_info.clock = pclk; it6505->video_info.hdisplay = hdew; @@ -2350,8 +2350,6 @@ static void it6505_irq_hpd(struct it6505 *it6505) if (!it6505_get_video_status(it6505)) it6505_video_reset(it6505); - - it6505_calc_video_info(it6505); } else { memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); -- cgit v1.2.3-59-g8ed1b From c19491894d4baf2155387e48aa326e1b52d7b986 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 17 Jun 2022 14:28:07 +0300 Subject: drm/i915/tgl+: Fix HDMI transcoder clock vs. DDI BUF disabling order Starting with TGL the disabling order of HDMI transcoder clock vs. DDI BUF has swapped, fix this. There hasn't been any issues seen related to this, but let's follow the spec. Reported-by: Sandeep K Lakkakula Signed-off-by: Imre Deak Reviewed-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20220617112807.1586621-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index f76eb6e345ec..ec131973f374 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2691,10 +2691,14 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, dig_port->set_infoframes(encoder, false, old_crtc_state, old_conn_state); - intel_ddi_disable_pipe_clock(old_crtc_state); + if (DISPLAY_VER(dev_priv) < 12) + intel_ddi_disable_pipe_clock(old_crtc_state); intel_disable_ddi_buf(encoder, old_crtc_state); + if (DISPLAY_VER(dev_priv) >= 12) + intel_ddi_disable_pipe_clock(old_crtc_state); + intel_display_power_put(dev_priv, dig_port->ddi_io_power_domain, fetch_and_zero(&dig_port->ddi_io_wakeref)); -- cgit v1.2.3-59-g8ed1b From ad444123a4a1d2944bd72fc877af9aabef01b889 Mon Sep 17 00:00:00 2001 From: José Roberto de Souza Date: Thu, 14 Jul 2022 08:07:54 -0700 Subject: Revert "drm/i915/display: Ensure PSR gets disabled if no encoders in new state" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patches fixes a issue but not in the right way as for_each_oldnew_intel_crtc_in_state() will interate over all CRTCs not only the crtc passed as parameter, also this two for_each loops are not necessary and only make code harder to understand. Proper fix will be discussed in the next patch. This reverts commit 75f664903d8672897333b86bb450335ec6486ad5. Cc: Jouni Högander Cc: Stanislav Lisovskiy Signed-off-by: José Roberto de Souza Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/20220714150755.154985-1-jose.souza@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 53 +++++++++++++------------------- 1 file changed, 22 insertions(+), 31 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 90599dd1cb1b..e6a870641cd2 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1863,45 +1863,36 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc) { struct drm_i915_private *i915 = to_i915(state->base.dev); - struct intel_crtc_state *new_crtc_state, *old_crtc_state; - int i; + const struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct intel_encoder *encoder; if (!HAS_PSR(i915)) return; - for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, - new_crtc_state, i) { - struct intel_encoder *encoder; - u32 old_new_encoder_mask = old_crtc_state->uapi.encoder_mask | - new_crtc_state->uapi.encoder_mask; - - for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, - old_new_encoder_mask) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct intel_psr *psr = &intel_dp->psr; - bool needs_to_disable = false; + for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, + crtc_state->uapi.encoder_mask) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_psr *psr = &intel_dp->psr; + bool needs_to_disable = false; - mutex_lock(&psr->lock); + mutex_lock(&psr->lock); - /* - * Reasons to disable: - * - PSR disabled in new state - * - All planes will go inactive - * - Changing between PSR versions - * - Encoder isn't present in new mask - */ - needs_to_disable |= intel_crtc_needs_modeset(new_crtc_state); - needs_to_disable |= !new_crtc_state->has_psr; - needs_to_disable |= !new_crtc_state->active_planes; - needs_to_disable |= new_crtc_state->has_psr2 != psr->psr2_enabled; - needs_to_disable |= !(new_crtc_state->uapi.encoder_mask & - drm_encoder_mask(&(encoder)->base)); + /* + * Reasons to disable: + * - PSR disabled in new state + * - All planes will go inactive + * - Changing between PSR versions + */ + needs_to_disable |= intel_crtc_needs_modeset(crtc_state); + needs_to_disable |= !crtc_state->has_psr; + needs_to_disable |= !crtc_state->active_planes; + needs_to_disable |= crtc_state->has_psr2 != psr->psr2_enabled; - if (psr->enabled && needs_to_disable) - intel_psr_disable_locked(intel_dp); + if (psr->enabled && needs_to_disable) + intel_psr_disable_locked(intel_dp); - mutex_unlock(&psr->lock); - } + mutex_unlock(&psr->lock); } } -- cgit v1.2.3-59-g8ed1b From 031a2fea0482965175ad20adea44bdfe6ed817aa Mon Sep 17 00:00:00 2001 From: José Roberto de Souza Date: Thu, 14 Jul 2022 08:07:55 -0700 Subject: drm/i915/psr: Disable PSR before disable pipe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The issue here was on for_each_intel_encoder_mask_with_psr() over the new_crtc_state encoder mask, so if the CRTC was being disabled mask would be zero and it would not have any chance to disable PSR. So here doing for_each_intel_encoder_mask_with_psr() over the old_crtc_state encoder mask and then using the new_crtc_state to check if PSR needs to be disabled. Cc: Jouni Högander Cc: Stanislav Lisovskiy Signed-off-by: José Roberto de Souza Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/20220714150755.154985-2-jose.souza@intel.com --- drivers/gpu/drm/i915/display/intel_psr.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index e6a870641cd2..98c3c8015a5c 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -1863,7 +1863,9 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, struct intel_crtc *crtc) { struct drm_i915_private *i915 = to_i915(state->base.dev); - const struct intel_crtc_state *crtc_state = + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + const struct intel_crtc_state *new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder; @@ -1871,7 +1873,7 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, return; for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, - crtc_state->uapi.encoder_mask) { + old_crtc_state->uapi.encoder_mask) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_psr *psr = &intel_dp->psr; bool needs_to_disable = false; @@ -1884,10 +1886,10 @@ void intel_psr_pre_plane_update(struct intel_atomic_state *state, * - All planes will go inactive * - Changing between PSR versions */ - needs_to_disable |= intel_crtc_needs_modeset(crtc_state); - needs_to_disable |= !crtc_state->has_psr; - needs_to_disable |= !crtc_state->active_planes; - needs_to_disable |= crtc_state->has_psr2 != psr->psr2_enabled; + needs_to_disable |= intel_crtc_needs_modeset(new_crtc_state); + needs_to_disable |= !new_crtc_state->has_psr; + needs_to_disable |= !new_crtc_state->active_planes; + needs_to_disable |= new_crtc_state->has_psr2 != psr->psr2_enabled; if (psr->enabled && needs_to_disable) intel_psr_disable_locked(intel_dp); -- cgit v1.2.3-59-g8ed1b From 2b066860a4f855babd2a22f98364ff6546d85f88 Mon Sep 17 00:00:00 2001 From: shaomin Deng Date: Mon, 18 Jul 2022 10:55:36 -0400 Subject: drm/mgag200:remove rebundant word "or" in comments there is a repeated word "or" in comments, so remove it. Signed-off-by: shaomin Deng Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220718145536.4866-1-dengshaomin@cdjrlc.com --- drivers/gpu/drm/mgag200/mgag200_reg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h index 99a9ab7d9119..1019ffd6c260 100644 --- a/drivers/gpu/drm/mgag200/mgag200_reg.h +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -96,7 +96,7 @@ #define MGAREG_SRCORG 0x2cb4 #define MGAREG_DSTORG 0x2cb8 -/* add or or this to one of the previous "power registers" to start +/* add or this to one of the previous "power registers" to start the drawing engine */ #define MGAREG_EXEC 0x0100 -- cgit v1.2.3-59-g8ed1b From 71eed8a9ba8609e9c97171525c28846b9bde9a4f Mon Sep 17 00:00:00 2001 From: shaomin Deng Date: Mon, 18 Jul 2022 11:11:54 -0400 Subject: drm:delete the repeated word "the" in comments remove the rebundant word "the" from comments. Signed-off-by: shaomin Deng Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220718151154.9628-1-dengshaomin@cdjrlc.com --- drivers/gpu/drm/display/drm_dp_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index e5bab236b3ae..32b295003f49 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -1597,7 +1597,7 @@ static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg) /* * Calculate the length of the i2c transfer in usec, assuming - * the i2c bus speed is as specified. Gives the the "worst" + * the i2c bus speed is as specified. Gives the "worst" * case estimate, ie. successful while as long as possible. * Doesn't account the "MOT" bit, and instead assumes each * message includes a START, ADDRESS and STOP. Neither does it -- cgit v1.2.3-59-g8ed1b From 3475b50873e4a68302822d91ab5cb031c1a92d50 Mon Sep 17 00:00:00 2001 From: shaomin Deng Date: Mon, 18 Jul 2022 11:04:18 -0400 Subject: drm:remove rebundant word "in" in comments there is a repeated word "in", so remove it. Signed-off-by: shaomin Deng Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20220718150418.7321-1-dengshaomin@cdjrlc.com --- drivers/gpu/drm/drm_framebuffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 9899bf1485b2..185b04762e2c 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -935,7 +935,7 @@ EXPORT_SYMBOL(drm_framebuffer_unregister_private); * the id and get back -EINVAL. Obviously no concern at driver unload time. * * Also, the framebuffer will not be removed from the lookup idr - for - * user-created framebuffers this will happen in in the rmfb ioctl. For + * user-created framebuffers this will happen in the rmfb ioctl. For * driver-private objects (e.g. for fbdev) drivers need to explicitly call * drm_framebuffer_unregister_private. */ -- cgit v1.2.3-59-g8ed1b From 9899834ae5f26649da09e0cae72d8d9eb8a07aab Mon Sep 17 00:00:00 2001 From: Swati Sharma Date: Wed, 6 Jul 2022 15:52:40 +0530 Subject: drm/i915/display: Add debug print for scaler filter Add debug print statement to print scaler filter property value. Since property can be set as either default or integer scaler; its good if we can get debug print for the same in dmesg log. Cc: Juha-Pekka Heikkila Signed-off-by: Swati Sharma Reviewed-by: Uma Shankar Signed-off-by: Uma Shankar Link: https://patchwork.freedesktop.org/patch/msgid/20220706102240.21414-1-swati2.sharma@intel.com --- drivers/gpu/drm/i915/display/intel_crtc_state_dump.c | 9 +++++---- drivers/gpu/drm/i915/display/intel_display_debugfs.c | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c index 4ca6e9493ff2..e9212f69c360 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c +++ b/drivers/gpu/drm/i915/display/intel_crtc_state_dump.c @@ -134,8 +134,8 @@ static void intel_dump_plane_state(const struct intel_plane_state *plane_state) plane->base.base.id, plane->base.name, fb->base.id, fb->width, fb->height, &fb->format->format, fb->modifier, str_yes_no(plane_state->uapi.visible)); - drm_dbg_kms(&i915->drm, "\trotation: 0x%x, scaler: %d\n", - plane_state->hw.rotation, plane_state->scaler_id); + drm_dbg_kms(&i915->drm, "\trotation: 0x%x, scaler: %d, scaling_filter: %d\n", + plane_state->hw.rotation, plane_state->scaler_id, plane_state->hw.scaling_filter); if (plane_state->uapi.visible) drm_dbg_kms(&i915->drm, "\tsrc: " DRM_RECT_FP_FMT " dst: " DRM_RECT_FMT "\n", @@ -262,10 +262,11 @@ void intel_crtc_state_dump(const struct intel_crtc_state *pipe_config, if (DISPLAY_VER(i915) >= 9) drm_dbg_kms(&i915->drm, - "num_scalers: %d, scaler_users: 0x%x, scaler_id: %d\n", + "num_scalers: %d, scaler_users: 0x%x, scaler_id: %d, scaling_filter: %d\n", crtc->num_scalers, pipe_config->scaler_state.scaler_users, - pipe_config->scaler_state.scaler_id); + pipe_config->scaler_state.scaler_id, + pipe_config->hw.scaling_filter); if (HAS_GMCH(i915)) drm_dbg_kms(&i915->drm, diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 452d773fd4e3..a770c1bb2665 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -720,10 +720,11 @@ static void intel_scaler_info(struct seq_file *m, struct intel_crtc *crtc) /* Not all platformas have a scaler */ if (num_scalers) { - seq_printf(m, "\tnum_scalers=%d, scaler_users=%x scaler_id=%d", + seq_printf(m, "\tnum_scalers=%d, scaler_users=%x scaler_id=%d scaling_filter=%d", num_scalers, crtc_state->scaler_state.scaler_users, - crtc_state->scaler_state.scaler_id); + crtc_state->scaler_state.scaler_id, + crtc_state->hw.scaling_filter); for (i = 0; i < num_scalers; i++) { const struct intel_scaler *sc = -- cgit v1.2.3-59-g8ed1b From 6f481afe220d36ad7790526a2164d7fd3d5b0487 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:29 +0200 Subject: drm/panel/panel-sitronix-st7701: Make DSI mode flags common to ST7701 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ST7701 and ST7701S are TFT matrix drivers with integrated multi protocol decoder capable of DSI/DPI/SPI input and 480x360...864 line TFT matrix output. Currently the only supported input is DSI. The protocol decoder is separate from the TFT matrix driver and is always capable of handling all of DSI non-burst mode with sync pulses or sync events as well as DSI burst mode. Move the DSI mode configuration from TFT matrix driver properties to common ST7701 code, because this is common to all TFT matrices. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Reviewed-by: Linus Walleij Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-1-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 5192d9aa572a..21a43daf5a6a 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -89,7 +89,6 @@ struct st7701_panel_desc { const struct drm_display_mode *mode; unsigned int lanes; - unsigned long flags; enum mipi_dsi_pixel_format format; const char *const *supply_names; unsigned int num_supplies; @@ -318,7 +317,6 @@ static const char * const ts8550b_supply_names[] = { static const struct st7701_panel_desc ts8550b_desc = { .mode = &ts8550b_mode, .lanes = 2, - .flags = MIPI_DSI_MODE_VIDEO, .format = MIPI_DSI_FMT_RGB888, .supply_names = ts8550b_supply_names, .num_supplies = ARRAY_SIZE(ts8550b_supply_names), @@ -336,7 +334,7 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi) return -ENOMEM; desc = of_device_get_match_data(&dsi->dev); - dsi->mode_flags = desc->flags; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO; dsi->format = desc->format; dsi->lanes = desc->lanes; -- cgit v1.2.3-59-g8ed1b From a6c225be3da7edec46f96781ae07a873b2fbaa17 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:30 +0200 Subject: drm/panel/panel-sitronix-st7701: Enable DSI burst mode, LPM, non-continuous clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ST7701(S) is capable of DSI burst mode, which is more energy efficient than the non-burst modes. Make use of it. The ST7701(S) is capable of DSI non-continuous clock, since it sources the TFT matrix driver clock from internal clock source. The DSI non-continuous clock further reduce power utilization. The ST7701(S) uses DSI LPM for command transmissions, make sure this is configured correctly in the DSI mode flags. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-2-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 21a43daf5a6a..15cd16744406 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -334,7 +334,8 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi) return -ENOMEM; desc = of_device_get_match_data(&dsi->dev); - dsi->mode_flags = MIPI_DSI_MODE_VIDEO; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS; dsi->format = desc->format; dsi->lanes = desc->lanes; -- cgit v1.2.3-59-g8ed1b From 7fa8e07128ed660c97c4a8fe615967e654b84f32 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:31 +0200 Subject: drm/panel/panel-sitronix-st7701: Make voltage supplies common to ST7701 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ST7701 and ST7701S all have two voltage supplies, one for internal logic and one for the TFT matrix driver. The supplies are not property of the TFT matrix driver, so move them to common ST7701 code. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-3-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 29 +++++++-------------------- 1 file changed, 7 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 15cd16744406..8b050f2879dc 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -90,8 +90,6 @@ struct st7701_panel_desc { const struct drm_display_mode *mode; unsigned int lanes; enum mipi_dsi_pixel_format format; - const char *const *supply_names; - unsigned int num_supplies; unsigned int panel_sleep_delay; }; @@ -100,7 +98,7 @@ struct st7701 { struct mipi_dsi_device *dsi; const struct st7701_panel_desc *desc; - struct regulator_bulk_data *supplies; + struct regulator_bulk_data supplies[2]; struct gpio_desc *reset; unsigned int sleep_delay; }; @@ -200,7 +198,7 @@ static int st7701_prepare(struct drm_panel *panel) gpiod_set_value(st7701->reset, 0); - ret = regulator_bulk_enable(st7701->desc->num_supplies, + ret = regulator_bulk_enable(ARRAY_SIZE(st7701->supplies), st7701->supplies); if (ret < 0) return ret; @@ -253,7 +251,7 @@ static int st7701_unprepare(struct drm_panel *panel) */ msleep(st7701->sleep_delay); - regulator_bulk_disable(st7701->desc->num_supplies, st7701->supplies); + regulator_bulk_disable(ARRAY_SIZE(st7701->supplies), st7701->supplies); return 0; } @@ -309,17 +307,10 @@ static const struct drm_display_mode ts8550b_mode = { .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, }; -static const char * const ts8550b_supply_names[] = { - "VCC", - "IOVCC", -}; - static const struct st7701_panel_desc ts8550b_desc = { .mode = &ts8550b_mode, .lanes = 2, .format = MIPI_DSI_FMT_RGB888, - .supply_names = ts8550b_supply_names, - .num_supplies = ARRAY_SIZE(ts8550b_supply_names), .panel_sleep_delay = 80, /* panel need extra 80ms for sleep out cmd */ }; @@ -327,7 +318,7 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi) { const struct st7701_panel_desc *desc; struct st7701 *st7701; - int ret, i; + int ret; st7701 = devm_kzalloc(&dsi->dev, sizeof(*st7701), GFP_KERNEL); if (!st7701) @@ -339,16 +330,10 @@ static int st7701_dsi_probe(struct mipi_dsi_device *dsi) dsi->format = desc->format; dsi->lanes = desc->lanes; - st7701->supplies = devm_kcalloc(&dsi->dev, desc->num_supplies, - sizeof(*st7701->supplies), - GFP_KERNEL); - if (!st7701->supplies) - return -ENOMEM; - - for (i = 0; i < desc->num_supplies; i++) - st7701->supplies[i].supply = desc->supply_names[i]; + st7701->supplies[0].supply = "VCC"; + st7701->supplies[1].supply = "IOVCC"; - ret = devm_regulator_bulk_get(&dsi->dev, desc->num_supplies, + ret = devm_regulator_bulk_get(&dsi->dev, ARRAY_SIZE(st7701->supplies), st7701->supplies); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From 779c84fea3dbd658d3c4b72937732193cb01bf15 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:32 +0200 Subject: drm/panel/panel-sitronix-st7701: Make gamma correction TFT specific MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gamma correction values are specific to the TFT which is attached to the ST7701 TFT matrix driver, move the gamma correction values from what incorrectly looks like common init sequence into TFT matrix specific settings. While doing so, add macros which defined fields within the gamma register file and a macro which mimics FIELD_PREP except works with constant expressions. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-4-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 96 +++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 8b050f2879dc..c5783aa7e51a 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -51,6 +51,23 @@ #define DSI_CMD2BKX_SEL_NONE 0x00 /* Command2, BK0 bytes */ +#define DSI_CMD2_BK0_GAMCTRL_AJ_MASK GENMASK(7, 6) +#define DSI_CMD2_BK0_GAMCTRL_VC0_MASK GENMASK(3, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC4_MASK GENMASK(5, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC8_MASK GENMASK(5, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC16_MASK GENMASK(4, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC24_MASK GENMASK(4, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC52_MASK GENMASK(3, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC80_MASK GENMASK(5, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC108_MASK GENMASK(3, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC147_MASK GENMASK(3, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC175_MASK GENMASK(5, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC203_MASK GENMASK(3, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC231_MASK GENMASK(4, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC239_MASK GENMASK(4, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC247_MASK GENMASK(5, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC251_MASK GENMASK(5, 0) +#define DSI_CMD2_BK0_GAMCTRL_VC255_MASK GENMASK(4, 0) #define DSI_LINESET_LINE 0x69 #define DSI_LINESET_LDE_EN BIT(7) #define DSI_LINESET_LINEDELTA GENMASK(1, 0) @@ -86,11 +103,18 @@ #define DSI_MIPISET1_EOT_EN BIT(3) #define DSI_CMD2_BK1_MIPISET1_SET (BIT(7) | DSI_MIPISET1_EOT_EN) +#define CFIELD_PREP(_mask, _val) \ + (((typeof(_mask))(_val) << (__builtin_ffsll(_mask) - 1)) & (_mask)) + struct st7701_panel_desc { const struct drm_display_mode *mode; unsigned int lanes; enum mipi_dsi_pixel_format format; unsigned int panel_sleep_delay; + + /* TFT matrix driver configuration, panel specific. */ + const u8 pv_gamma[16]; /* Positive voltage gamma control */ + const u8 nv_gamma[16]; /* Negative voltage gamma control */ }; struct st7701 { @@ -122,7 +146,8 @@ static inline int st7701_dsi_write(struct st7701 *st7701, const void *seq, static void st7701_init_sequence(struct st7701 *st7701) { - const struct drm_display_mode *mode = st7701->desc->mode; + const struct st7701_panel_desc *desc = st7701->desc; + const struct drm_display_mode *mode = desc->mode; ST7701_DSI(st7701, MIPI_DCS_SOFT_RESET, 0x00); @@ -136,12 +161,10 @@ static void st7701_init_sequence(struct st7701 *st7701) /* Command2, BK0 */ ST7701_DSI(st7701, DSI_CMD2BKX_SEL, 0x77, 0x01, 0x00, 0x00, DSI_CMD2BK0_SEL); - ST7701_DSI(st7701, DSI_CMD2_BK0_PVGAMCTRL, 0x00, 0x0E, 0x15, 0x0F, - 0x11, 0x08, 0x08, 0x08, 0x08, 0x23, 0x04, 0x13, 0x12, - 0x2B, 0x34, 0x1F); - ST7701_DSI(st7701, DSI_CMD2_BK0_NVGAMCTRL, 0x00, 0x0E, 0x95, 0x0F, - 0x13, 0x07, 0x09, 0x08, 0x08, 0x22, 0x04, 0x10, 0x0E, - 0x2C, 0x34, 0x1F); + mipi_dsi_dcs_write(st7701->dsi, DSI_CMD2_BK0_PVGAMCTRL, + desc->pv_gamma, ARRAY_SIZE(desc->pv_gamma)); + mipi_dsi_dcs_write(st7701->dsi, DSI_CMD2_BK0_NVGAMCTRL, + desc->nv_gamma, ARRAY_SIZE(desc->nv_gamma)); ST7701_DSI(st7701, DSI_CMD2_BK0_LNESET, DSI_CMD2_BK0_LNESET_B0, DSI_CMD2_BK0_LNESET_B1); ST7701_DSI(st7701, DSI_CMD2_BK0_PORCTRL, @@ -312,6 +335,65 @@ static const struct st7701_panel_desc ts8550b_desc = { .lanes = 2, .format = MIPI_DSI_FMT_RGB888, .panel_sleep_delay = 80, /* panel need extra 80ms for sleep out cmd */ + + .pv_gamma = { + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC0_MASK, 0), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC4_MASK, 0xe), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC8_MASK, 0x15), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC16_MASK, 0xf), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC24_MASK, 0x11), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC52_MASK, 0x8), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC80_MASK, 0x8), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC108_MASK, 0x8), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC147_MASK, 0x8), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC175_MASK, 0x23), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC203_MASK, 0x4), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC231_MASK, 0x13), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC239_MASK, 0x12), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC247_MASK, 0x2b), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC251_MASK, 0x34), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x1f) + }, + .nv_gamma = { + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC0_MASK, 0), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC4_MASK, 0xe), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0x2) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC8_MASK, 0x15), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC16_MASK, 0xf), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC24_MASK, 0x13), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC52_MASK, 0x7), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC80_MASK, 0x9), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC108_MASK, 0x8), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC147_MASK, 0x8), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC175_MASK, 0x22), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC203_MASK, 0x4), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC231_MASK, 0x10), + + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC239_MASK, 0xe), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC247_MASK, 0x2c), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC251_MASK, 0x34), + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | + CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x1f) + }, }; static int st7701_dsi_probe(struct mipi_dsi_device *dsi) -- cgit v1.2.3-59-g8ed1b From 1ba85119afb5e45c699bf074dcdc894bfbf1c614 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:33 +0200 Subject: drm/panel/panel-sitronix-st7701: Infer vertical line count from TFT mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vertical line count is a property of the TFT matrix. Currently the driver hard-codes content of this register to specific value which is only compatible with one TFT matrix, likely the TS8550B one. Calculate the vertical line count from the mode instead. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-5-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index c5783aa7e51a..14b0ba7a1b24 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -68,11 +69,9 @@ #define DSI_CMD2_BK0_GAMCTRL_VC247_MASK GENMASK(5, 0) #define DSI_CMD2_BK0_GAMCTRL_VC251_MASK GENMASK(5, 0) #define DSI_CMD2_BK0_GAMCTRL_VC255_MASK GENMASK(4, 0) -#define DSI_LINESET_LINE 0x69 -#define DSI_LINESET_LDE_EN BIT(7) -#define DSI_LINESET_LINEDELTA GENMASK(1, 0) -#define DSI_CMD2_BK0_LNESET_B1 DSI_LINESET_LINEDELTA -#define DSI_CMD2_BK0_LNESET_B0 (DSI_LINESET_LDE_EN | DSI_LINESET_LINE) +#define DSI_CMD2_BK0_LNESET_LINE_MASK GENMASK(6, 0) +#define DSI_CMD2_BK0_LNESET_LDE_EN BIT(7) +#define DSI_CMD2_BK0_LNESET_LINEDELTA GENMASK(1, 0) #define DSI_INVSEL_DEFAULT GENMASK(5, 4) #define DSI_INVSEL_NLINV GENMASK(2, 0) #define DSI_INVSEL_RTNI GENMASK(2, 1) @@ -148,6 +147,8 @@ static void st7701_init_sequence(struct st7701 *st7701) { const struct st7701_panel_desc *desc = st7701->desc; const struct drm_display_mode *mode = desc->mode; + const u8 linecount8 = mode->vdisplay / 8; + const u8 linecountrem2 = (mode->vdisplay % 8) / 2; ST7701_DSI(st7701, MIPI_DCS_SOFT_RESET, 0x00); @@ -165,8 +166,21 @@ static void st7701_init_sequence(struct st7701 *st7701) desc->pv_gamma, ARRAY_SIZE(desc->pv_gamma)); mipi_dsi_dcs_write(st7701->dsi, DSI_CMD2_BK0_NVGAMCTRL, desc->nv_gamma, ARRAY_SIZE(desc->nv_gamma)); + /* + * Vertical line count configuration: + * Line[6:0]: select number of vertical lines of the TFT matrix in + * multiples of 8 lines + * LDE_EN: enable sub-8-line granularity line count + * Line_delta[1:0]: add 0/2/4/6 extra lines to line count selected + * using Line[6:0] + * + * Total number of vertical lines: + * LN = ((Line[6:0] + 1) * 8) + (LDE_EN ? Line_delta[1:0] * 2 : 0) + */ ST7701_DSI(st7701, DSI_CMD2_BK0_LNESET, - DSI_CMD2_BK0_LNESET_B0, DSI_CMD2_BK0_LNESET_B1); + FIELD_PREP(DSI_CMD2_BK0_LNESET_LINE_MASK, linecount8 - 1) | + (linecountrem2 ? DSI_CMD2_BK0_LNESET_LDE_EN : 0), + FIELD_PREP(DSI_CMD2_BK0_LNESET_LINEDELTA, linecountrem2)); ST7701_DSI(st7701, DSI_CMD2_BK0_PORCTRL, DSI_CMD2_BK0_PORCTRL_B0(mode), DSI_CMD2_BK0_PORCTRL_B1(mode)); -- cgit v1.2.3-59-g8ed1b From 82f9cee25598a1cb06529df8e7e1f86f61e786e9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:34 +0200 Subject: drm/panel/panel-sitronix-st7701: Adjust porch control bitfield name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define DSI_CMD2_BK0_PORCTRL_VBP_MASK and DSI_CMD2_BK0_PORCTRL_VFP_MASK and move the vertical back and front porch calculation from macros into the st7701_init_sequence() function, so it is clear what this does. No functional change. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-6-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index 14b0ba7a1b24..ce33b362dc13 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -72,13 +72,13 @@ #define DSI_CMD2_BK0_LNESET_LINE_MASK GENMASK(6, 0) #define DSI_CMD2_BK0_LNESET_LDE_EN BIT(7) #define DSI_CMD2_BK0_LNESET_LINEDELTA GENMASK(1, 0) +#define DSI_CMD2_BK0_PORCTRL_VBP_MASK GENMASK(7, 0) +#define DSI_CMD2_BK0_PORCTRL_VFP_MASK GENMASK(7, 0) #define DSI_INVSEL_DEFAULT GENMASK(5, 4) #define DSI_INVSEL_NLINV GENMASK(2, 0) #define DSI_INVSEL_RTNI GENMASK(2, 1) #define DSI_CMD2_BK0_INVSEL_B1 DSI_INVSEL_RTNI #define DSI_CMD2_BK0_INVSEL_B0 (DSI_INVSEL_DEFAULT | DSI_INVSEL_NLINV) -#define DSI_CMD2_BK0_PORCTRL_B0(m) ((m)->vtotal - (m)->vsync_end) -#define DSI_CMD2_BK0_PORCTRL_B1(m) ((m)->vsync_start - (m)->vdisplay) /* Command2, BK1 bytes */ #define DSI_CMD2_BK1_VRHA_SET 0x45 @@ -182,8 +182,10 @@ static void st7701_init_sequence(struct st7701 *st7701) (linecountrem2 ? DSI_CMD2_BK0_LNESET_LDE_EN : 0), FIELD_PREP(DSI_CMD2_BK0_LNESET_LINEDELTA, linecountrem2)); ST7701_DSI(st7701, DSI_CMD2_BK0_PORCTRL, - DSI_CMD2_BK0_PORCTRL_B0(mode), - DSI_CMD2_BK0_PORCTRL_B1(mode)); + FIELD_PREP(DSI_CMD2_BK0_PORCTRL_VBP_MASK, + mode->vtotal - mode->vsync_end), + FIELD_PREP(DSI_CMD2_BK0_PORCTRL_VFP_MASK, + mode->vsync_start - mode->vdisplay)); ST7701_DSI(st7701, DSI_CMD2_BK0_INVSEL, DSI_CMD2_BK0_INVSEL_B0, DSI_CMD2_BK0_INVSEL_B1); -- cgit v1.2.3-59-g8ed1b From de2b4917843cd433d0f1c279123f75801dcd0c1e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:35 +0200 Subject: drm/panel/panel-sitronix-st7701: Infer horizontal pixel count from TFT mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The horizontal pixel count is a property of the TFT matrix. Currently the driver hard-codes content of this register to specific value which is only compatible with one TFT matrix, likely the TS8550B one. Calculate the horizontal pixel count from the mode instead. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-7-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index ce33b362dc13..ae545eba5cb0 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -74,11 +74,9 @@ #define DSI_CMD2_BK0_LNESET_LINEDELTA GENMASK(1, 0) #define DSI_CMD2_BK0_PORCTRL_VBP_MASK GENMASK(7, 0) #define DSI_CMD2_BK0_PORCTRL_VFP_MASK GENMASK(7, 0) -#define DSI_INVSEL_DEFAULT GENMASK(5, 4) -#define DSI_INVSEL_NLINV GENMASK(2, 0) -#define DSI_INVSEL_RTNI GENMASK(2, 1) -#define DSI_CMD2_BK0_INVSEL_B1 DSI_INVSEL_RTNI -#define DSI_CMD2_BK0_INVSEL_B0 (DSI_INVSEL_DEFAULT | DSI_INVSEL_NLINV) +#define DSI_CMD2_BK0_INVSEL_ONES_MASK GENMASK(5, 4) +#define DSI_CMD2_BK0_INVSEL_NLINV_MASK GENMASK(2, 0) +#define DSI_CMD2_BK0_INVSEL_RTNI_MASK GENMASK(4, 0) /* Command2, BK1 bytes */ #define DSI_CMD2_BK1_VRHA_SET 0x45 @@ -114,6 +112,7 @@ struct st7701_panel_desc { /* TFT matrix driver configuration, panel specific. */ const u8 pv_gamma[16]; /* Positive voltage gamma control */ const u8 nv_gamma[16]; /* Negative voltage gamma control */ + const u8 nlinv; /* Inversion selection */ }; struct st7701 { @@ -186,8 +185,17 @@ static void st7701_init_sequence(struct st7701 *st7701) mode->vtotal - mode->vsync_end), FIELD_PREP(DSI_CMD2_BK0_PORCTRL_VFP_MASK, mode->vsync_start - mode->vdisplay)); + /* + * Horizontal pixel count configuration: + * PCLK = 512 + (RTNI[4:0] * 16) + * The PCLK is number of pixel clock per line, which matches + * mode htotal. The minimum is 512 PCLK. + */ ST7701_DSI(st7701, DSI_CMD2_BK0_INVSEL, - DSI_CMD2_BK0_INVSEL_B0, DSI_CMD2_BK0_INVSEL_B1); + DSI_CMD2_BK0_INVSEL_ONES_MASK | + FIELD_PREP(DSI_CMD2_BK0_INVSEL_NLINV_MASK, desc->nlinv), + FIELD_PREP(DSI_CMD2_BK0_INVSEL_RTNI_MASK, + DIV_ROUND_UP(mode->htotal, 16))); /* Command2, BK1 */ ST7701_DSI(st7701, DSI_CMD2BKX_SEL, @@ -410,6 +418,7 @@ static const struct st7701_panel_desc ts8550b_desc = { CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_AJ_MASK, 0) | CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x1f) }, + .nlinv = 7, }; static int st7701_dsi_probe(struct mipi_dsi_device *dsi) -- cgit v1.2.3-59-g8ed1b From 83b7a8e7e88e7a201facc9c33833330485f5ec12 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:36 +0200 Subject: drm/panel/panel-sitronix-st7701: Parametrize voltage and timing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of hard-coding TFT matrix voltage and timing settings, which can even lead to permanent TFT matrix damage, parametrize them in TFT matrix descriptor. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-8-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 155 +++++++++++++++++++++----- 1 file changed, 127 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index ae545eba5cb0..e8491ac3593c 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -79,30 +79,34 @@ #define DSI_CMD2_BK0_INVSEL_RTNI_MASK GENMASK(4, 0) /* Command2, BK1 bytes */ -#define DSI_CMD2_BK1_VRHA_SET 0x45 -#define DSI_CMD2_BK1_VCOM_SET 0x13 -#define DSI_CMD2_BK1_VGHSS_SET GENMASK(2, 0) +#define DSI_CMD2_BK1_VRHA_MASK GENMASK(7, 0) +#define DSI_CMD2_BK1_VCOM_MASK GENMASK(7, 0) +#define DSI_CMD2_BK1_VGHSS_MASK GENMASK(3, 0) #define DSI_CMD2_BK1_TESTCMD_VAL BIT(7) -#define DSI_VGLS_DEFAULT BIT(6) -#define DSI_VGLS_SEL GENMASK(2, 0) -#define DSI_CMD2_BK1_VGLS_SET (DSI_VGLS_DEFAULT | DSI_VGLS_SEL) -#define DSI_PWCTLR1_AP BIT(7) /* Gamma OP bias, max */ -#define DSI_PWCTLR1_APIS BIT(2) /* Source OP input bias, min */ -#define DSI_PWCTLR1_APOS BIT(0) /* Source OP output bias, min */ -#define DSI_CMD2_BK1_PWCTLR1_SET (DSI_PWCTLR1_AP | DSI_PWCTLR1_APIS | \ - DSI_PWCTLR1_APOS) -#define DSI_PWCTLR2_AVDD BIT(5) /* AVDD 6.6v */ -#define DSI_PWCTLR2_AVCL 0x0 /* AVCL -4.4v */ -#define DSI_CMD2_BK1_PWCTLR2_SET (DSI_PWCTLR2_AVDD | DSI_PWCTLR2_AVCL) -#define DSI_SPD1_T2D BIT(3) -#define DSI_CMD2_BK1_SPD1_SET (GENMASK(6, 4) | DSI_SPD1_T2D) -#define DSI_CMD2_BK1_SPD2_SET DSI_CMD2_BK1_SPD1_SET -#define DSI_MIPISET1_EOT_EN BIT(3) -#define DSI_CMD2_BK1_MIPISET1_SET (BIT(7) | DSI_MIPISET1_EOT_EN) +#define DSI_CMD2_BK1_VGLS_ONES BIT(6) +#define DSI_CMD2_BK1_VGLS_MASK GENMASK(3, 0) +#define DSI_CMD2_BK1_PWRCTRL1_AP_MASK GENMASK(7, 6) +#define DSI_CMD2_BK1_PWRCTRL1_APIS_MASK GENMASK(3, 2) +#define DSI_CMD2_BK1_PWRCTRL1_APOS_MASK GENMASK(1, 0) +#define DSI_CMD2_BK1_PWRCTRL2_AVDD_MASK GENMASK(5, 4) +#define DSI_CMD2_BK1_PWRCTRL2_AVCL_MASK GENMASK(1, 0) +#define DSI_CMD2_BK1_SPD1_ONES_MASK GENMASK(6, 4) +#define DSI_CMD2_BK1_SPD1_T2D_MASK GENMASK(3, 0) +#define DSI_CMD2_BK1_SPD2_ONES_MASK GENMASK(6, 4) +#define DSI_CMD2_BK1_SPD2_T3D_MASK GENMASK(3, 0) +#define DSI_CMD2_BK1_MIPISET1_ONES BIT(7) +#define DSI_CMD2_BK1_MIPISET1_EOT_EN BIT(3) #define CFIELD_PREP(_mask, _val) \ (((typeof(_mask))(_val) << (__builtin_ffsll(_mask) - 1)) & (_mask)) +enum op_bias { + OP_BIAS_OFF = 0, + OP_BIAS_MIN, + OP_BIAS_MIDDLE, + OP_BIAS_MAX +}; + struct st7701_panel_desc { const struct drm_display_mode *mode; unsigned int lanes; @@ -113,6 +117,18 @@ struct st7701_panel_desc { const u8 pv_gamma[16]; /* Positive voltage gamma control */ const u8 nv_gamma[16]; /* Negative voltage gamma control */ const u8 nlinv; /* Inversion selection */ + const u32 vop_uv; /* Vop in uV */ + const u32 vcom_uv; /* Vcom in uV */ + const u16 vgh_mv; /* Vgh in mV */ + const s16 vgl_mv; /* Vgl in mV */ + const u16 avdd_mv; /* Avdd in mV */ + const s16 avcl_mv; /* Avcl in mV */ + const enum op_bias gamma_op_bias; + const enum op_bias input_op_bias; + const enum op_bias output_op_bias; + const u16 t2d_ns; /* T2D in ns */ + const u16 t3d_ns; /* T3D in ns */ + const bool eot_en; }; struct st7701 { @@ -142,6 +158,31 @@ static inline int st7701_dsi_write(struct st7701 *st7701, const void *seq, st7701_dsi_write(st7701, d, ARRAY_SIZE(d)); \ } +static u8 st7701_vgls_map(struct st7701 *st7701) +{ + const struct st7701_panel_desc *desc = st7701->desc; + struct { + s32 vgl; + u8 val; + } map[16] = { + { -7060, 0x0 }, { -7470, 0x1 }, + { -7910, 0x2 }, { -8140, 0x3 }, + { -8650, 0x4 }, { -8920, 0x5 }, + { -9210, 0x6 }, { -9510, 0x7 }, + { -9830, 0x8 }, { -10170, 0x9 }, + { -10530, 0xa }, { -10910, 0xb }, + { -11310, 0xc }, { -11730, 0xd }, + { -12200, 0xe }, { -12690, 0xf } + }; + int i; + + for (i = 0; i < ARRAY_SIZE(map); i++) + if (desc->vgl_mv == map[i].vgl) + return map[i].val; + + return 0; +} + static void st7701_init_sequence(struct st7701 *st7701) { const struct st7701_panel_desc *desc = st7701->desc; @@ -200,16 +241,62 @@ static void st7701_init_sequence(struct st7701 *st7701) /* Command2, BK1 */ ST7701_DSI(st7701, DSI_CMD2BKX_SEL, 0x77, 0x01, 0x00, 0x00, DSI_CMD2BK1_SEL); - ST7701_DSI(st7701, DSI_CMD2_BK1_VRHS, DSI_CMD2_BK1_VRHA_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_VCOM, DSI_CMD2_BK1_VCOM_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_VGHSS, DSI_CMD2_BK1_VGHSS_SET); + + /* Vop = 3.5375V + (VRHA[7:0] * 0.0125V) */ + ST7701_DSI(st7701, DSI_CMD2_BK1_VRHS, + FIELD_PREP(DSI_CMD2_BK1_VRHA_MASK, + DIV_ROUND_CLOSEST(desc->vop_uv - 3537500, 12500))); + + /* Vcom = 0.1V + (VCOM[7:0] * 0.0125V) */ + ST7701_DSI(st7701, DSI_CMD2_BK1_VCOM, + FIELD_PREP(DSI_CMD2_BK1_VCOM_MASK, + DIV_ROUND_CLOSEST(desc->vcom_uv - 100000, 12500))); + + /* Vgh = 11.5V + (VGHSS[7:0] * 0.5V) */ + ST7701_DSI(st7701, DSI_CMD2_BK1_VGHSS, + FIELD_PREP(DSI_CMD2_BK1_VGHSS_MASK, + DIV_ROUND_CLOSEST(clamp(desc->vgh_mv, + (u16)11500, + (u16)17000) - 11500, + 500))); + ST7701_DSI(st7701, DSI_CMD2_BK1_TESTCMD, DSI_CMD2_BK1_TESTCMD_VAL); - ST7701_DSI(st7701, DSI_CMD2_BK1_VGLS, DSI_CMD2_BK1_VGLS_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_PWCTLR1, DSI_CMD2_BK1_PWCTLR1_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_PWCTLR2, DSI_CMD2_BK1_PWCTLR2_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_SPD1, DSI_CMD2_BK1_SPD1_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_SPD2, DSI_CMD2_BK1_SPD2_SET); - ST7701_DSI(st7701, DSI_CMD2_BK1_MIPISET1, DSI_CMD2_BK1_MIPISET1_SET); + + /* Vgl is non-linear */ + ST7701_DSI(st7701, DSI_CMD2_BK1_VGLS, + DSI_CMD2_BK1_VGLS_ONES | + FIELD_PREP(DSI_CMD2_BK1_VGLS_MASK, st7701_vgls_map(st7701))); + + ST7701_DSI(st7701, DSI_CMD2_BK1_PWCTLR1, + FIELD_PREP(DSI_CMD2_BK1_PWRCTRL1_AP_MASK, + desc->gamma_op_bias) | + FIELD_PREP(DSI_CMD2_BK1_PWRCTRL1_APIS_MASK, + desc->input_op_bias) | + FIELD_PREP(DSI_CMD2_BK1_PWRCTRL1_APOS_MASK, + desc->output_op_bias)); + + /* Avdd = 6.2V + (AVDD[1:0] * 0.2V) , Avcl = -4.4V - (AVCL[1:0] * 0.2V) */ + ST7701_DSI(st7701, DSI_CMD2_BK1_PWCTLR2, + FIELD_PREP(DSI_CMD2_BK1_PWRCTRL2_AVDD_MASK, + DIV_ROUND_CLOSEST(desc->avdd_mv - 6200, 200)) | + FIELD_PREP(DSI_CMD2_BK1_PWRCTRL2_AVCL_MASK, + DIV_ROUND_CLOSEST(-4400 + desc->avcl_mv, 200))); + + /* T2D = 0.2us * T2D[3:0] */ + ST7701_DSI(st7701, DSI_CMD2_BK1_SPD1, + DSI_CMD2_BK1_SPD1_ONES_MASK | + FIELD_PREP(DSI_CMD2_BK1_SPD1_T2D_MASK, + DIV_ROUND_CLOSEST(desc->t2d_ns, 200))); + + /* T3D = 4us + (0.8us * T3D[3:0]) */ + ST7701_DSI(st7701, DSI_CMD2_BK1_SPD2, + DSI_CMD2_BK1_SPD2_ONES_MASK | + FIELD_PREP(DSI_CMD2_BK1_SPD2_T3D_MASK, + DIV_ROUND_CLOSEST(desc->t3d_ns - 4000, 800))); + + ST7701_DSI(st7701, DSI_CMD2_BK1_MIPISET1, + DSI_CMD2_BK1_MIPISET1_ONES | + (desc->eot_en ? DSI_CMD2_BK1_MIPISET1_EOT_EN : 0)); /** * ST7701_SPEC_V1.2 is unable to provide enough information above this @@ -419,6 +506,18 @@ static const struct st7701_panel_desc ts8550b_desc = { CFIELD_PREP(DSI_CMD2_BK0_GAMCTRL_VC255_MASK, 0x1f) }, .nlinv = 7, + .vop_uv = 4400000, + .vcom_uv = 337500, + .vgh_mv = 15000, + .vgl_mv = -9510, + .avdd_mv = 6600, + .avcl_mv = -4400, + .gamma_op_bias = OP_BIAS_MAX, + .input_op_bias = OP_BIAS_MIN, + .output_op_bias = OP_BIAS_MIN, + .t2d_ns = 1600, + .t3d_ns = 10400, + .eot_en = true, }; static int st7701_dsi_probe(struct mipi_dsi_device *dsi) -- cgit v1.2.3-59-g8ed1b From 42542c7904cf2e6fb795dc7ffd1903ab7d6e53fb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 10 Jul 2022 21:44:37 +0200 Subject: drm/panel/panel-sitronix-st7701: Split GIP and init sequences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ST7701 initialization sequence is well parametrized, split the GIP programming sequence, which is fully custom completely undocumented TFT matrix specific magic register programming sequence into separate callback so other TFT matrix definitions can add their own GIP sequence. Signed-off-by: Marek Vasut Cc: Guido Günther Cc: Jagan Teki Cc: Laurent Pinchart Cc: Linus Walleij Cc: Sam Ravnborg Cc: Thierry Reding Signed-off-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/20220710194437.289042-9-marex@denx.de --- drivers/gpu/drm/panel/panel-sitronix-st7701.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7701.c b/drivers/gpu/drm/panel/panel-sitronix-st7701.c index e8491ac3593c..65ee383e62fd 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7701.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7701.c @@ -107,6 +107,8 @@ enum op_bias { OP_BIAS_MAX }; +struct st7701; + struct st7701_panel_desc { const struct drm_display_mode *mode; unsigned int lanes; @@ -129,6 +131,9 @@ struct st7701_panel_desc { const u16 t2d_ns; /* T2D in ns */ const u16 t3d_ns; /* T3D in ns */ const bool eot_en; + + /* GIP sequence, fully custom and undocumented. */ + void (*gip_sequence)(struct st7701 *st7701); }; struct st7701 { @@ -297,7 +302,10 @@ static void st7701_init_sequence(struct st7701 *st7701) ST7701_DSI(st7701, DSI_CMD2_BK1_MIPISET1, DSI_CMD2_BK1_MIPISET1_ONES | (desc->eot_en ? DSI_CMD2_BK1_MIPISET1_EOT_EN : 0)); +} +static void ts8550b_gip_sequence(struct st7701 *st7701) +{ /** * ST7701_SPEC_V1.2 is unable to provide enough information above this * specific command sequence, so grab the same from vendor BSP driver. @@ -319,10 +327,6 @@ static void st7701_init_sequence(struct st7701 *st7701) ST7701_DSI(st7701, 0xEC, 0x00, 0x00); ST7701_DSI(st7701, 0xED, 0xFF, 0xF1, 0x04, 0x56, 0x72, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF3, 0x27, 0x65, 0x40, 0x1F, 0xFF); - - /* disable Command2 */ - ST7701_DSI(st7701, DSI_CMD2BKX_SEL, - 0x77, 0x01, 0x00, 0x00, DSI_CMD2BKX_SEL_NONE); } static int st7701_prepare(struct drm_panel *panel) @@ -343,6 +347,13 @@ static int st7701_prepare(struct drm_panel *panel) st7701_init_sequence(st7701); + if (st7701->desc->gip_sequence) + st7701->desc->gip_sequence(st7701); + + /* Disable Command2 */ + ST7701_DSI(st7701, DSI_CMD2BKX_SEL, + 0x77, 0x01, 0x00, 0x00, DSI_CMD2BKX_SEL_NONE); + return 0; } @@ -518,6 +529,7 @@ static const struct st7701_panel_desc ts8550b_desc = { .t2d_ns = 1600, .t3d_ns = 10400, .eot_en = true, + .gip_sequence = ts8550b_gip_sequence, }; static int st7701_dsi_probe(struct mipi_dsi_device *dsi) -- cgit v1.2.3-59-g8ed1b From 5dca122f8388695b1048a48a69d3cf9210036d9a Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 18 Jul 2022 18:07:07 -0700 Subject: drm/i915/gt: Create gt/gtN/.defaults/ for per gt sysfs defaults Create a gt/gtN/.defaults/ directory (similar to engine//.defaults/) to expose default parameter values for each gt in sysfs. This allows userspace to restore default parameter values after they have changed. The empty 'struct gt_defaults' will be populated by subsequent patches. v2: Changed 'struct intel_rps_defaults rps_defaults' to 'struct gt_defaults defaults' (Andi) Cc: Tvrtko Ursulin Cc: Andi Shyti Signed-off-by: Ashutosh Dixit Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/be7c30d0ae58be9d8d5b8242ba00a1b2825e63ad.1658192398.git.ashutosh.dixit@intel.com --- drivers/gpu/drm/i915/gt/intel_gt_sysfs.c | 5 +++++ drivers/gpu/drm/i915/gt/intel_gt_types.h | 7 +++++++ 2 files changed, 12 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c b/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c index 9e4ebf53379b..6447c0376dbd 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c @@ -101,6 +101,10 @@ void intel_gt_sysfs_register(struct intel_gt *gt) gt->i915->sysfs_gt, "gt%d", gt->info.id)) goto exit_fail; + gt->sysfs_defaults = kobject_create_and_add(".defaults", >->sysfs_gt); + if (!gt->sysfs_defaults) + goto exit_fail; + intel_gt_sysfs_pm_init(gt, >->sysfs_gt); return; @@ -113,5 +117,6 @@ exit_fail: void intel_gt_sysfs_unregister(struct intel_gt *gt) { + kobject_put(gt->sysfs_defaults); kobject_put(>->sysfs_gt); } diff --git a/drivers/gpu/drm/i915/gt/intel_gt_types.h b/drivers/gpu/drm/i915/gt/intel_gt_types.h index df708802889d..92839b9adb12 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_types.h +++ b/drivers/gpu/drm/i915/gt/intel_gt_types.h @@ -75,6 +75,9 @@ enum intel_submission_method { INTEL_SUBMISSION_GUC, }; +struct gt_defaults { +}; + struct intel_gt { struct drm_i915_private *i915; struct intel_uncore *uncore; @@ -235,6 +238,10 @@ struct intel_gt { /* gt/gtN sysfs */ struct kobject sysfs_gt; + + /* sysfs defaults per gt */ + struct gt_defaults defaults; + struct kobject *sysfs_defaults; }; enum intel_gt_scratch_field { -- cgit v1.2.3-59-g8ed1b From fdff0a852ec8b8ddba6fc193f187d5d85717ab2e Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Mon, 18 Jul 2022 18:07:08 -0700 Subject: drm/i915/gt: Expose per-gt RPS defaults in sysfs Add the following sysfs files to gt/gtN/.defaults/: * rps_min_freq_mhz * rps_max_freq_mhz v2: Correct gt/gtN/.defaults/* file names in commit message v3: Remove rps_boost_freq_mhz since it is not consumed by userspace Cc: Tvrtko Ursulin Cc: Andi Shyti Signed-off-by: Ashutosh Dixit Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/cf6e483bf79f871c2c8c74af6005bf6a83a3a1ce.1658192398.git.ashutosh.dixit@intel.com --- drivers/gpu/drm/i915/gt/intel_gt_sysfs.c | 5 ----- drivers/gpu/drm/i915/gt/intel_gt_sysfs.h | 6 +++++ drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c | 34 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/gt/intel_gt_types.h | 2 ++ drivers/gpu/drm/i915/gt/intel_rps.c | 2 ++ drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c | 12 ++++++---- 6 files changed, 52 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c b/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c index 6447c0376dbd..d651ccd0ab20 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_sysfs.c @@ -22,11 +22,6 @@ bool is_object_gt(struct kobject *kobj) return !strncmp(kobj->name, "gt", 2); } -static struct intel_gt *kobj_to_gt(struct kobject *kobj) -{ - return container_of(kobj, struct intel_gt, sysfs_gt); -} - struct intel_gt *intel_gt_sysfs_get_drvdata(struct device *dev, const char *name) { diff --git a/drivers/gpu/drm/i915/gt/intel_gt_sysfs.h b/drivers/gpu/drm/i915/gt/intel_gt_sysfs.h index a99aa7e8b01a..6232923a420d 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_sysfs.h +++ b/drivers/gpu/drm/i915/gt/intel_gt_sysfs.h @@ -10,6 +10,7 @@ #include #include "i915_gem.h" /* GEM_BUG_ON() */ +#include "intel_gt_types.h" struct intel_gt; @@ -22,6 +23,11 @@ intel_gt_create_kobj(struct intel_gt *gt, struct kobject *dir, const char *name); +static inline struct intel_gt *kobj_to_gt(struct kobject *kobj) +{ + return container_of(kobj, struct intel_gt, sysfs_gt); +} + void intel_gt_sysfs_register(struct intel_gt *gt); void intel_gt_sysfs_unregister(struct intel_gt *gt); struct intel_gt *intel_gt_sysfs_get_drvdata(struct device *dev, diff --git a/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c index 73a8b46e0234..e066cc33d9f2 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c @@ -727,6 +727,34 @@ static const struct attribute *media_perf_power_attrs[] = { NULL }; +static ssize_t +default_min_freq_mhz_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct intel_gt *gt = kobj_to_gt(kobj->parent); + + return sysfs_emit(buf, "%u\n", gt->defaults.min_freq); +} + +static struct kobj_attribute default_min_freq_mhz = +__ATTR(rps_min_freq_mhz, 0444, default_min_freq_mhz_show, NULL); + +static ssize_t +default_max_freq_mhz_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct intel_gt *gt = kobj_to_gt(kobj->parent); + + return sysfs_emit(buf, "%u\n", gt->defaults.max_freq); +} + +static struct kobj_attribute default_max_freq_mhz = +__ATTR(rps_max_freq_mhz, 0444, default_max_freq_mhz_show, NULL); + +static const struct attribute * const rps_defaults_attrs[] = { + &default_min_freq_mhz.attr, + &default_max_freq_mhz.attr, + NULL +}; + static int intel_sysfs_rps_init(struct intel_gt *gt, struct kobject *kobj, const struct attribute * const *attrs) { @@ -776,4 +804,10 @@ void intel_gt_sysfs_pm_init(struct intel_gt *gt, struct kobject *kobj) "failed to create gt%u media_perf_power_attrs sysfs (%pe)\n", gt->info.id, ERR_PTR(ret)); } + + ret = sysfs_create_files(gt->sysfs_defaults, rps_defaults_attrs); + if (ret) + drm_warn(>->i915->drm, + "failed to add gt%u rps defaults (%pe)\n", + gt->info.id, ERR_PTR(ret)); } diff --git a/drivers/gpu/drm/i915/gt/intel_gt_types.h b/drivers/gpu/drm/i915/gt/intel_gt_types.h index 92839b9adb12..80f52532290e 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_types.h +++ b/drivers/gpu/drm/i915/gt/intel_gt_types.h @@ -76,6 +76,8 @@ enum intel_submission_method { }; struct gt_defaults { + u32 min_freq; + u32 max_freq; }; struct intel_gt { diff --git a/drivers/gpu/drm/i915/gt/intel_rps.c b/drivers/gpu/drm/i915/gt/intel_rps.c index fb3f57ee450b..c7d381ad90cf 100644 --- a/drivers/gpu/drm/i915/gt/intel_rps.c +++ b/drivers/gpu/drm/i915/gt/intel_rps.c @@ -1979,7 +1979,9 @@ void intel_rps_init(struct intel_rps *rps) /* Derive initial user preferences/limits from the hardware limits */ rps->max_freq_softlimit = rps->max_freq; + rps_to_gt(rps)->defaults.max_freq = rps->max_freq_softlimit; rps->min_freq_softlimit = rps->min_freq; + rps_to_gt(rps)->defaults.min_freq = rps->min_freq_softlimit; /* After setting max-softlimit, find the overclock max freq */ if (GRAPHICS_VER(i915) == 6 || IS_IVYBRIDGE(i915) || IS_HASWELL(i915)) { diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c index ec9c4ca0f615..e1fa1f32f29e 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c @@ -575,20 +575,24 @@ static int slpc_set_softlimits(struct intel_guc_slpc *slpc) * unless they have deviated from defaults, in which case, * we retain the values and set min/max accordingly. */ - if (!slpc->max_freq_softlimit) + if (!slpc->max_freq_softlimit) { slpc->max_freq_softlimit = slpc->rp0_freq; - else if (slpc->max_freq_softlimit != slpc->rp0_freq) + slpc_to_gt(slpc)->defaults.max_freq = slpc->max_freq_softlimit; + } else if (slpc->max_freq_softlimit != slpc->rp0_freq) { ret = intel_guc_slpc_set_max_freq(slpc, slpc->max_freq_softlimit); + } if (unlikely(ret)) return ret; - if (!slpc->min_freq_softlimit) + if (!slpc->min_freq_softlimit) { slpc->min_freq_softlimit = slpc->min_freq; - else if (slpc->min_freq_softlimit != slpc->min_freq) + slpc_to_gt(slpc)->defaults.min_freq = slpc->min_freq_softlimit; + } else if (slpc->min_freq_softlimit != slpc->min_freq) { return intel_guc_slpc_set_min_freq(slpc, slpc->min_freq_softlimit); + } return 0; } -- cgit v1.2.3-59-g8ed1b From 64b88afbd92fbf434759d1896a7cf705e1c00e79 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:18 +0300 Subject: drm/virtio: Correct drm_gem_shmem_get_sg_table() error handling Previous commit fixed checking of the ERR_PTR value returned by drm_gem_shmem_get_sg_table(), but it missed to zero out the shmem->pages, which will crash virtio_gpu_cleanup_object(). Add the missing zeroing of the shmem->pages. Fixes: c24968734abf ("drm/virtio: Fix NULL vs IS_ERR checking in virtio_gpu_object_shmem_init") Reviewed-by: Emil Velikov Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-2-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_object.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 1cc8f3fc8e4b..87b19b3b96e0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -170,6 +170,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev, shmem->pages = drm_gem_shmem_get_sg_table(&bo->base); if (IS_ERR(shmem->pages)) { drm_gem_shmem_unpin(&bo->base); + shmem->pages = NULL; return PTR_ERR(shmem->pages); } -- cgit v1.2.3-59-g8ed1b From e473216b42aa1fd9fc6b94b608b42c210c655908 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:19 +0300 Subject: drm/virtio: Check whether transferred 2D BO is shmem Transferred 2D BO always must be a shmem BO. Add check for that to prevent NULL dereference if userspace passes a VRAM BO. Cc: stable@vger.kernel.org Reviewed-by: Emil Velikov Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-3-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_vq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index b7529b2b9883..1262fd0b3bef 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -597,7 +597,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); - if (use_dma_api) + if (virtio_gpu_is_shmem(bo) && use_dma_api) dma_sync_sgtable_for_device(vgdev->vdev->dev.parent, shmem->pages, DMA_TO_DEVICE); -- cgit v1.2.3-59-g8ed1b From fdf0ff4d12cbcd76b53f27c96ce51ddca400884a Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:20 +0300 Subject: drm/virtio: Unlock reservations on virtio_gpu_object_shmem_init() error Unlock reservations in the error code path of virtio_gpu_object_create() to silence debug warning splat produced by ww_mutex_destroy(&obj->lock) when GEM is released with the held lock. Cc: stable@vger.kernel.org Fixes: 30172efbfb84 ("drm/virtio: blob prep: refactor getting pages and attaching backing") Reviewed-by: Emil Velikov Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-4-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_object.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 87b19b3b96e0..75a159df0af6 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -249,6 +249,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents); if (ret != 0) { + if (fence) + virtio_gpu_array_unlock_resv(objs); virtio_gpu_array_put_free(objs); virtio_gpu_free_object(&shmem_obj->base); return ret; -- cgit v1.2.3-59-g8ed1b From 0f877398d30e1df657a31a62f7c7de1869b072b5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:21 +0300 Subject: drm/virtio: Unlock reservations on dma_resv_reserve_fences() error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlock reservations on dma_resv_reserve_fences() error to fix recursive locking of the reservations when this error happens. Cc: stable@vger.kernel.org Fixes: c8d4c18bfbc4 ("dma-buf/drivers: make reserving a shared slot mandatory v4") Reviewed-by: Thomas Hellström Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-5-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_gem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 580a78809836..7db48d17ee3a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -228,8 +228,10 @@ int virtio_gpu_array_lock_resv(struct virtio_gpu_object_array *objs) for (i = 0; i < objs->nents; ++i) { ret = dma_resv_reserve_fences(objs->objs[i]->resv, 1); - if (ret) + if (ret) { + virtio_gpu_array_unlock_resv(objs); return ret; + } } return ret; } -- cgit v1.2.3-59-g8ed1b From 4656b3a26a9e9fe5f04bfd2ab55b066266ba7f4d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:22 +0300 Subject: drm/virtio: Use appropriate atomic state in virtio_gpu_plane_cleanup_fb() Make virtio_gpu_plane_cleanup_fb() to clean the state which DRM core wants to clean up and not the current plane's state. Normally the older atomic state is cleaned up, but the newer state could also be cleaned up in case of aborted commits. Cc: stable@vger.kernel.org Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-6-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_plane.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 6d3cc9e238a4..7148f3813d8b 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -266,14 +266,14 @@ static int virtio_gpu_plane_prepare_fb(struct drm_plane *plane, } static void virtio_gpu_plane_cleanup_fb(struct drm_plane *plane, - struct drm_plane_state *old_state) + struct drm_plane_state *state) { struct virtio_gpu_framebuffer *vgfb; - if (!plane->state->fb) + if (!state->fb) return; - vgfb = to_virtio_gpu_framebuffer(plane->state->fb); + vgfb = to_virtio_gpu_framebuffer(state->fb); if (vgfb->fence) { dma_fence_put(&vgfb->fence->f); vgfb->fence = NULL; -- cgit v1.2.3-59-g8ed1b From e7fef092330321ff311e8c06338ce1b4b608ba05 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:23 +0300 Subject: drm/virtio: Simplify error handling of virtio_gpu_object_create() Change the order of SHMEM initialization and reservation locking to make code cleaner and to prepare for transitioning of the common GEM SHMEM code to use the GEM's reservation lock instead of the shmem.page_lock. There is no need to lock reservation during allocation of the SHMEM pages because the lock is needed only to avoid racing with the async host-side allocation. Hence we can safely move the SHMEM initialization out of the reservation lock. Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-7-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_object.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 75a159df0af6..62b4d075cfac 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -235,6 +235,10 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, bo->dumb = params->dumb; + ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents); + if (ret != 0) + goto err_put_id; + if (fence) { ret = -ENOMEM; objs = virtio_gpu_array_alloc(1); @@ -247,15 +251,6 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, goto err_put_objs; } - ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents); - if (ret != 0) { - if (fence) - virtio_gpu_array_unlock_resv(objs); - virtio_gpu_array_put_free(objs); - virtio_gpu_free_object(&shmem_obj->base); - return ret; - } - if (params->blob) { if (params->blob_mem == VIRTGPU_BLOB_MEM_GUEST) bo->guest_blob = true; -- cgit v1.2.3-59-g8ed1b From b5c9ed70d1a94c59dad7b1ecfc928863c0fe6ac0 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:24 +0300 Subject: drm/virtio: Improve DMA API usage for shmem BOs DRM API requires the DRM's driver to be backed with the device that can be used for generic DMA operations. The VirtIO-GPU device can't perform DMA operations if it uses PCI transport because PCI device driver creates a virtual VirtIO-GPU device that isn't associated with the PCI. Use PCI's GPU device for the DRM's device instead of the VirtIO-GPU device and drop DMA-related hacks from the VirtIO-GPU driver. Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-8-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_drv.c | 51 +++++++----------------------- drivers/gpu/drm/virtio/virtgpu_drv.h | 5 +-- drivers/gpu/drm/virtio/virtgpu_kms.c | 7 ++--- drivers/gpu/drm/virtio/virtgpu_object.c | 55 +++++++-------------------------- drivers/gpu/drm/virtio/virtgpu_vq.c | 13 +++----- 5 files changed, 32 insertions(+), 99 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 5f25a8d15464..0141b7df97ec 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -46,12 +46,11 @@ static int virtio_gpu_modeset = -1; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, virtio_gpu_modeset, int, 0400); -static int virtio_gpu_pci_quirk(struct drm_device *dev, struct virtio_device *vdev) +static int virtio_gpu_pci_quirk(struct drm_device *dev) { - struct pci_dev *pdev = to_pci_dev(vdev->dev.parent); + struct pci_dev *pdev = to_pci_dev(dev->dev); const char *pname = dev_name(&pdev->dev); bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA; - char unique[20]; int ret; DRM_INFO("pci: %s detected at %s\n", @@ -63,39 +62,7 @@ static int virtio_gpu_pci_quirk(struct drm_device *dev, struct virtio_device *vd return ret; } - /* - * Normally the drm_dev_set_unique() call is done by core DRM. - * The following comment covers, why virtio cannot rely on it. - * - * Unlike the other virtual GPU drivers, virtio abstracts the - * underlying bus type by using struct virtio_device. - * - * Hence the dev_is_pci() check, used in core DRM, will fail - * and the unique returned will be the virtio_device "virtio0", - * while a "pci:..." one is required. - * - * A few other ideas were considered: - * - Extend the dev_is_pci() check [in drm_set_busid] to - * consider virtio. - * Seems like a bigger hack than what we have already. - * - * - Point drm_device::dev to the parent of the virtio_device - * Semantic changes: - * * Using the wrong device for i2c, framebuffer_alloc and - * prime import. - * Visual changes: - * * Helpers such as DRM_DEV_ERROR, dev_info, drm_printer, - * will print the wrong information. - * - * We could address the latter issues, by introducing - * drm_device::bus_dev, ... which would be used solely for this. - * - * So for the moment keep things as-is, with a bulky comment - * for the next person who feels like removing this - * drm_dev_set_unique() quirk. - */ - snprintf(unique, sizeof(unique), "pci:%s", pname); - return drm_dev_set_unique(dev, unique); + return 0; } static int virtio_gpu_probe(struct virtio_device *vdev) @@ -109,18 +76,24 @@ static int virtio_gpu_probe(struct virtio_device *vdev) if (virtio_gpu_modeset == 0) return -EINVAL; - dev = drm_dev_alloc(&driver, &vdev->dev); + /* + * The virtio-gpu device is a virtual device that doesn't have DMA + * ops assigned to it, nor DMA mask set and etc. Its parent device + * is actual GPU device we want to use it for the DRM's device in + * order to benefit from using generic DRM APIs. + */ + dev = drm_dev_alloc(&driver, vdev->dev.parent); if (IS_ERR(dev)) return PTR_ERR(dev); vdev->priv = dev; if (!strcmp(vdev->dev.parent->bus->name, "pci")) { - ret = virtio_gpu_pci_quirk(dev, vdev); + ret = virtio_gpu_pci_quirk(dev); if (ret) goto err_free; } - ret = virtio_gpu_init(dev); + ret = virtio_gpu_init(vdev, dev); if (ret) goto err_free; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index f80664cf98d0..9b98470593b0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -101,8 +101,6 @@ struct virtio_gpu_object { struct virtio_gpu_object_shmem { struct virtio_gpu_object base; - struct sg_table *pages; - uint32_t mapped; }; struct virtio_gpu_object_vram { @@ -215,7 +213,6 @@ struct virtio_gpu_drv_cap_cache { }; struct virtio_gpu_device { - struct device *dev; struct drm_device *ddev; struct virtio_device *vdev; @@ -283,7 +280,7 @@ extern struct drm_ioctl_desc virtio_gpu_ioctls[DRM_VIRTIO_NUM_IOCTLS]; void virtio_gpu_create_context(struct drm_device *dev, struct drm_file *file); /* virtgpu_kms.c */ -int virtio_gpu_init(struct drm_device *dev); +int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev); void virtio_gpu_deinit(struct drm_device *dev); void virtio_gpu_release(struct drm_device *dev); int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 3313b92db531..0d1e3eb61bee 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -110,7 +110,7 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, vgdev->num_capsets = num_capsets; } -int virtio_gpu_init(struct drm_device *dev) +int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) { static vq_callback_t *callbacks[] = { virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack @@ -123,7 +123,7 @@ int virtio_gpu_init(struct drm_device *dev) u32 num_scanouts, num_capsets; int ret = 0; - if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1)) + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) return -ENODEV; vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); @@ -132,8 +132,7 @@ int virtio_gpu_init(struct drm_device *dev) vgdev->ddev = dev; dev->dev_private = vgdev; - vgdev->vdev = dev_to_virtio(dev->dev); - vgdev->dev = dev->dev; + vgdev->vdev = vdev; spin_lock_init(&vgdev->display_info_lock); spin_lock_init(&vgdev->resource_export_lock); diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 62b4d075cfac..8d7728181de0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -67,21 +67,6 @@ void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle); if (virtio_gpu_is_shmem(bo)) { - struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); - - if (shmem->pages) { - if (shmem->mapped) { - dma_unmap_sgtable(vgdev->vdev->dev.parent, - shmem->pages, DMA_TO_DEVICE, 0); - shmem->mapped = 0; - } - - sg_free_table(shmem->pages); - kfree(shmem->pages); - shmem->pages = NULL; - drm_gem_shmem_unpin(&bo->base); - } - drm_gem_shmem_free(&bo->base); } else if (virtio_gpu_is_vram(bo)) { struct virtio_gpu_object_vram *vram = to_virtio_gpu_vram(bo); @@ -153,36 +138,18 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev, unsigned int *nents) { bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); - struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); struct scatterlist *sg; - int si, ret; + struct sg_table *pages; + int si; - ret = drm_gem_shmem_pin(&bo->base); - if (ret < 0) - return -EINVAL; - - /* - * virtio_gpu uses drm_gem_shmem_get_sg_table instead of - * drm_gem_shmem_get_pages_sgt because virtio has it's own set of - * dma-ops. This is discouraged for other drivers, but should be fine - * since virtio_gpu doesn't support dma-buf import from other devices. - */ - shmem->pages = drm_gem_shmem_get_sg_table(&bo->base); - if (IS_ERR(shmem->pages)) { - drm_gem_shmem_unpin(&bo->base); - shmem->pages = NULL; - return PTR_ERR(shmem->pages); - } + pages = drm_gem_shmem_get_pages_sgt(&bo->base); + if (IS_ERR(pages)) + return PTR_ERR(pages); - if (use_dma_api) { - ret = dma_map_sgtable(vgdev->vdev->dev.parent, - shmem->pages, DMA_TO_DEVICE, 0); - if (ret) - return ret; - *nents = shmem->mapped = shmem->pages->nents; - } else { - *nents = shmem->pages->orig_nents; - } + if (use_dma_api) + *nents = pages->nents; + else + *nents = pages->orig_nents; *ents = kvmalloc_array(*nents, sizeof(struct virtio_gpu_mem_entry), @@ -193,13 +160,13 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev, } if (use_dma_api) { - for_each_sgtable_dma_sg(shmem->pages, sg, si) { + for_each_sgtable_dma_sg(pages, sg, si) { (*ents)[si].addr = cpu_to_le64(sg_dma_address(sg)); (*ents)[si].length = cpu_to_le32(sg_dma_len(sg)); (*ents)[si].padding = 0; } } else { - for_each_sgtable_sg(shmem->pages, sg, si) { + for_each_sgtable_sg(pages, sg, si) { (*ents)[si].addr = cpu_to_le64(sg_phys(sg)); (*ents)[si].length = cpu_to_le32(sg->length); (*ents)[si].padding = 0; diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 1262fd0b3bef..ee84256946ab 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -595,11 +595,10 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, struct virtio_gpu_transfer_to_host_2d *cmd_p; struct virtio_gpu_vbuffer *vbuf; bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); - struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); if (virtio_gpu_is_shmem(bo) && use_dma_api) - dma_sync_sgtable_for_device(vgdev->vdev->dev.parent, - shmem->pages, DMA_TO_DEVICE); + dma_sync_sgtable_for_device(&vgdev->vdev->dev, + bo->base.sgt, DMA_TO_DEVICE); cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); memset(cmd_p, 0, sizeof(*cmd_p)); @@ -1019,11 +1018,9 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf; bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev); - if (virtio_gpu_is_shmem(bo) && use_dma_api) { - struct virtio_gpu_object_shmem *shmem = to_virtio_gpu_shmem(bo); - dma_sync_sgtable_for_device(vgdev->vdev->dev.parent, - shmem->pages, DMA_TO_DEVICE); - } + if (virtio_gpu_is_shmem(bo) && use_dma_api) + dma_sync_sgtable_for_device(&vgdev->vdev->dev, + bo->base.sgt, DMA_TO_DEVICE); cmd_p = virtio_gpu_alloc_cmd(vgdev, &vbuf, sizeof(*cmd_p)); memset(cmd_p, 0, sizeof(*cmd_p)); -- cgit v1.2.3-59-g8ed1b From a9d2e8b5f1e5caa3cb038d7bb7bab530bcce5af1 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:25 +0300 Subject: drm/virtio: Use dev_is_pci() Use common dev_is_pci() helper to replace the strcmp("pci") used by driver. Suggested-by: Robin Murphy Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-9-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 0141b7df97ec..0035affc3e59 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -87,7 +87,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) return PTR_ERR(dev); vdev->priv = dev; - if (!strcmp(vdev->dev.parent->bus->name, "pci")) { + if (dev_is_pci(vdev->dev.parent)) { ret = virtio_gpu_pci_quirk(dev); if (ret) goto err_free; -- cgit v1.2.3-59-g8ed1b From 4c703f5d6f776eaa6a98611c9b5dfe800fbeb0c8 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Thu, 30 Jun 2022 23:07:26 +0300 Subject: drm/virtio: Return proper error codes instead of -1 Don't return -1 in error cases, return proper error code. The returned error codes propagate to error messages and to userspace and it's always good to have a meaningful error number for debugging purposes. Signed-off-by: Dmitry Osipenko Link: http://patchwork.freedesktop.org/patch/msgid/20220630200726.1884320-10-dmitry.osipenko@collabora.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_vq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index ee84256946ab..9ff8660b50ad 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -322,7 +322,7 @@ static int virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev, if (fence && vbuf->objs) virtio_gpu_array_unlock_resv(vbuf->objs); free_vbuf(vgdev, vbuf); - return -1; + return -ENODEV; } if (vgdev->has_indirect) @@ -386,7 +386,7 @@ static int virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev, if (!sgt) { if (fence && vbuf->objs) virtio_gpu_array_unlock_resv(vbuf->objs); - return -1; + return -ENOMEM; } elemcnt += sg_ents; @@ -720,7 +720,7 @@ static int virtio_get_edid_block(void *data, u8 *buf, size_t start = block * EDID_LENGTH; if (start + len > le32_to_cpu(resp->size)) - return -1; + return -EINVAL; memcpy(buf, resp->edid + start, len); return 0; } -- cgit v1.2.3-59-g8ed1b From 7847628862a808ff3802df96f54e5eab3ff448b6 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 14 Jul 2022 15:00:27 +0200 Subject: drm/virtio: plane: use drm managed resources Use drm managed resource allocation (drmm_universal_plane_alloc()) in order to cleanup/simplify drm plane .destroy callback. Signed-off-by: Danilo Krummrich Link: http://patchwork.freedesktop.org/patch/msgid/20220714130028.2127858-2-dakr@redhat.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_plane.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 7148f3813d8b..6a62f327dc09 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -67,16 +67,10 @@ uint32_t virtio_gpu_translate_format(uint32_t drm_fourcc) return format; } -static void virtio_gpu_plane_destroy(struct drm_plane *plane) -{ - drm_plane_cleanup(plane); - kfree(plane); -} - static const struct drm_plane_funcs virtio_gpu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = virtio_gpu_plane_destroy, + .destroy = drm_plane_cleanup, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, @@ -379,11 +373,7 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, const struct drm_plane_helper_funcs *funcs; struct drm_plane *plane; const uint32_t *formats; - int ret, nformats; - - plane = kzalloc(sizeof(*plane), GFP_KERNEL); - if (!plane) - return ERR_PTR(-ENOMEM); + int nformats; if (type == DRM_PLANE_TYPE_CURSOR) { formats = virtio_gpu_cursor_formats; @@ -394,17 +384,13 @@ struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, nformats = ARRAY_SIZE(virtio_gpu_formats); funcs = &virtio_gpu_primary_helper_funcs; } - ret = drm_universal_plane_init(dev, plane, 1 << index, - &virtio_gpu_plane_funcs, - formats, nformats, - NULL, type, NULL); - if (ret) - goto err_plane_init; + + plane = drmm_universal_plane_alloc(dev, struct drm_plane, dev, + 1 << index, &virtio_gpu_plane_funcs, + formats, nformats, NULL, type, NULL); + if (IS_ERR(plane)) + return plane; drm_plane_helper_add(plane, funcs); return plane; - -err_plane_init: - kfree(plane); - return ERR_PTR(ret); } -- cgit v1.2.3-59-g8ed1b From 90caf42527a40d09e0eed9fcbca08d757f4fd493 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 14 Jul 2022 15:00:28 +0200 Subject: drm/virtio: kms: use drm managed resources Allocate driver structures with drm managed resource allocators in order to cleanup/simplify the drm driver .release callback. Signed-off-by: Danilo Krummrich Link: http://patchwork.freedesktop.org/patch/msgid/20220714130028.2127858-3-dakr@redhat.com Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/virtio/virtgpu_kms.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 0d1e3eb61bee..27b7f14dae89 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -28,6 +28,7 @@ #include #include +#include #include "virtgpu_drv.h" @@ -66,10 +67,11 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, { int i, ret; bool invalid_capset_id = false; + struct drm_device *drm = vgdev->ddev; - vgdev->capsets = kcalloc(num_capsets, - sizeof(struct virtio_gpu_drv_capset), - GFP_KERNEL); + vgdev->capsets = drmm_kcalloc(drm, num_capsets, + sizeof(struct virtio_gpu_drv_capset), + GFP_KERNEL); if (!vgdev->capsets) { DRM_ERROR("failed to allocate cap sets\n"); return; @@ -94,7 +96,7 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, if (ret == 0 || invalid_capset_id) { spin_lock(&vgdev->display_info_lock); - kfree(vgdev->capsets); + drmm_kfree(drm, vgdev->capsets); vgdev->capsets = NULL; spin_unlock(&vgdev->display_info_lock); return; @@ -126,7 +128,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) return -ENODEV; - vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); + vgdev = drmm_kzalloc(dev, sizeof(struct virtio_gpu_device), GFP_KERNEL); if (!vgdev) return -ENOMEM; @@ -256,7 +258,6 @@ err_vbufs: vgdev->vdev->config->del_vqs(vgdev->vdev); err_vqs: dev->dev_private = NULL; - kfree(vgdev); return ret; } @@ -295,9 +296,6 @@ void virtio_gpu_release(struct drm_device *dev) if (vgdev->has_host_visible) drm_mm_takedown(&vgdev->host_visible_mm); - - kfree(vgdev->capsets); - kfree(vgdev); } int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) -- cgit v1.2.3-59-g8ed1b From 8f48dbd6deaf5a14c924c1de08bf7e36526bf3e7 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Jun 2022 17:26:08 +0200 Subject: drm/bochs: Fix some error handling paths in bochs_pci_probe() The remove() function calls bochs_hw_fini() but this function is not called in the error handling of the probe. This call releases the resources allocated by bochs_hw_init() used in bochs_load(). Update the probe and bochs_load() to call bochs_hw_fini() if an error occurs after a successful bochs_hw_init() call. Signed-off-by: Christophe JAILLET Link: http://patchwork.freedesktop.org/patch/msgid/0e676e4d56ab5b10fcf22860081414445611dfa7.1655565953.git.christophe.jaillet@wanadoo.fr Signed-off-by: Gerd Hoffmann --- drivers/gpu/drm/tiny/bochs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 82364a0a7b18..08de13774862 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -583,13 +583,17 @@ static int bochs_load(struct drm_device *dev) ret = drmm_vram_helper_init(dev, bochs->fb_base, bochs->fb_size); if (ret) - return ret; + goto err_hw_fini; ret = bochs_kms_init(bochs); if (ret) - return ret; + goto err_hw_fini; return 0; + +err_hw_fini: + bochs_hw_fini(dev); + return ret; } DEFINE_DRM_GEM_FOPS(bochs_fops); @@ -664,11 +668,13 @@ static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent ret = drm_dev_register(dev, 0); if (ret) - goto err_free_dev; + goto err_hw_fini; drm_fbdev_generic_setup(dev, 32); return ret; +err_hw_fini: + bochs_hw_fini(dev); err_free_dev: drm_dev_put(dev); return ret; -- cgit v1.2.3-59-g8ed1b From 1d43a5120ab49f22ba6c5901ad3994e254510303 Mon Sep 17 00:00:00 2001 From: Liang He Date: Tue, 19 Jul 2022 14:54:46 +0800 Subject: drm/bridge: anx7625: Fix refcount bug in anx7625_parse_dt() In anx7625_parse_dt(), 'pdata->mipi_host_node' will be assigned a new reference with of_graph_get_remote_node() which will increase the refcount of the object, correspondingly, we should call of_node_put() for the old reference stored in the 'pdata->mipi_host_node'. Fixes: 8bdfc5dae4e3 ("drm/bridge: anx7625: Add anx7625 MIPI DSI/DPI to DP") Signed-off-by: Liang He Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220719065447.1080817-1-windhl@126.com --- drivers/gpu/drm/bridge/analogix/anx7625.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index d1f1d525aeb6..79fc7a50b497 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1642,6 +1642,7 @@ static int anx7625_parse_dt(struct device *dev, anx7625_get_swing_setting(dev, pdata); pdata->is_dpi = 0; /* default dsi mode */ + of_node_put(pdata->mipi_host_node); pdata->mipi_host_node = of_graph_get_remote_node(np, 0, 0); if (!pdata->mipi_host_node) { DRM_DEV_ERROR(dev, "fail to get internal panel.\n"); -- cgit v1.2.3-59-g8ed1b From 14e7157afb055248ed34901fcd6fbf54201cfea1 Mon Sep 17 00:00:00 2001 From: Liang He Date: Tue, 19 Jul 2022 14:54:47 +0800 Subject: drm/bridge: tc358767: Add of_node_put() when breaking out of loop In tc_probe_bridge_endpoint(), we should call of_node_put() when breaking out of the for_each_endpoint_of_node() which will automatically increase and decrease the refcount. Fixes: 71f7d9c03118 ("drm/bridge: tc358767: Detect bridge mode from connected endpoints in DT") Signed-off-by: Liang He Reviewed-by: Robert Foss Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220719065447.1080817-2-windhl@126.com --- drivers/gpu/drm/bridge/tc358767.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 02bd757a8987..1dc107f13645 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2010,9 +2010,10 @@ static int tc_probe_bridge_endpoint(struct tc_data *tc) for_each_endpoint_of_node(dev->of_node, node) { of_graph_parse_endpoint(node, &endpoint); - if (endpoint.port > 2) + if (endpoint.port > 2) { + of_node_put(node); return -EINVAL; - + } mode |= BIT(endpoint.port); } -- cgit v1.2.3-59-g8ed1b From 4d9db10576ff51afa8cf7727fbad55ada299359b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:21:08 +0200 Subject: drm/format-helper: Fix endianness in drm_fb_*_to_*() conversion helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DRM formats are defined to be little-endian, unless the DRM_FORMAT_BIG_ENDIAN flag is set. Hence when converting from one format to another, multi-byte pixel values loaded from memory must be converted from little-endian to host-endian. Conversely, multi-byte pixel values written to memory must be converted from host-endian to little-endian. Currently only drm_fb_xrgb8888_to_rgb332_line() includes endianness handling. Fix this by adding endianness handling to all conversion functions that process multi-byte pixel values. Note that the conversion to RGB565 is special, as there are two versions: with and without byteswapping of the RGB565 pixel data. Signed-off-by: Geert Uytterhoeven Signed-off-by: Noralf Trønnes Link: https://patchwork.freedesktop.org/patch/msgid/33f390d3bae2c5a45c0050097dc95f2e17644f2f.1657300532.git.geert@linux-m68k.org --- drivers/gpu/drm/drm_format_helper.c | 80 +++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 34 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index a3ccd8bc966f..c6182b5de78b 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -279,14 +279,16 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb332); static void drm_fb_xrgb8888_to_rgb565_line(void *dbuf, const void *sbuf, unsigned int pixels) { u16 *dbuf16 = dbuf; - const u32 *sbuf32 = sbuf; + const __le32 *sbuf32 = sbuf; unsigned int x; u16 val16; + u32 pix; for (x = 0; x < pixels; x++) { - val16 = ((sbuf32[x] & 0x00F80000) >> 8) | - ((sbuf32[x] & 0x0000FC00) >> 5) | - ((sbuf32[x] & 0x000000F8) >> 3); + pix = le32_to_cpu(sbuf32[x]); + val16 = ((pix & 0x00F80000) >> 8) | + ((pix & 0x0000FC00) >> 5) | + ((pix & 0x000000F8) >> 3); dbuf16[x] = val16; } } @@ -295,14 +297,16 @@ static void drm_fb_xrgb8888_to_rgb565_swab_line(void *dbuf, const void *sbuf, unsigned int pixels) { u16 *dbuf16 = dbuf; - const u32 *sbuf32 = sbuf; + const __le32 *sbuf32 = sbuf; unsigned int x; u16 val16; + u32 pix; for (x = 0; x < pixels; x++) { - val16 = ((sbuf32[x] & 0x00F80000) >> 8) | - ((sbuf32[x] & 0x0000FC00) >> 5) | - ((sbuf32[x] & 0x000000F8) >> 3); + pix = le32_to_cpu(sbuf32[x]); + val16 = ((pix & 0x00F80000) >> 8) | + ((pix & 0x0000FC00) >> 5) | + ((pix & 0x000000F8) >> 3); dbuf16[x] = swab16(val16); } } @@ -360,13 +364,15 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb565_toio); static void drm_fb_xrgb8888_to_rgb888_line(void *dbuf, const void *sbuf, unsigned int pixels) { u8 *dbuf8 = dbuf; - const u32 *sbuf32 = sbuf; + const __le32 *sbuf32 = sbuf; unsigned int x; + u32 pix; for (x = 0; x < pixels; x++) { - *dbuf8++ = (sbuf32[x] & 0x000000FF) >> 0; - *dbuf8++ = (sbuf32[x] & 0x0000FF00) >> 8; - *dbuf8++ = (sbuf32[x] & 0x00FF0000) >> 16; + pix = le32_to_cpu(sbuf32[x]); + *dbuf8++ = (pix & 0x000000FF) >> 0; + *dbuf8++ = (pix & 0x0000FF00) >> 8; + *dbuf8++ = (pix & 0x00FF0000) >> 16; } } @@ -410,17 +416,19 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_rgb888_toio); static void drm_fb_rgb565_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u32 *dbuf32 = dbuf; - const u16 *sbuf16 = sbuf; + __le32 *dbuf32 = dbuf; + const __le16 *sbuf16 = sbuf; unsigned int x; - for (x = 0; x < pixels; x++, ++sbuf16, ++dbuf32) { - u32 val32 = ((*sbuf16 & 0xf800) << 8) | - ((*sbuf16 & 0x07e0) << 5) | - ((*sbuf16 & 0x001f) << 3); - *dbuf32 = 0xff000000 | val32 | - ((val32 >> 3) & 0x00070007) | - ((val32 >> 2) & 0x00000300); + for (x = 0; x < pixels; x++) { + u16 val16 = le16_to_cpu(sbuf16[x]); + u32 val32 = ((val16 & 0xf800) << 8) | + ((val16 & 0x07e0) << 5) | + ((val16 & 0x001f) << 3); + val32 = 0xff000000 | val32 | + ((val32 >> 3) & 0x00070007) | + ((val32 >> 2) & 0x00000300); + dbuf32[x] = cpu_to_le32(val32); } } @@ -434,7 +442,7 @@ static void drm_fb_rgb565_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_p static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u32 *dbuf32 = dbuf; + __le32 *dbuf32 = dbuf; const u8 *sbuf8 = sbuf; unsigned int x; @@ -442,7 +450,8 @@ static void drm_fb_rgb888_to_xrgb8888_line(void *dbuf, const void *sbuf, unsigne u8 r = *sbuf8++; u8 g = *sbuf8++; u8 b = *sbuf8++; - *dbuf32++ = 0xff000000 | (r << 16) | (g << 8) | b; + u32 pix = 0xff000000 | (r << 16) | (g << 8) | b; + dbuf32[x] = cpu_to_le32(pix); } } @@ -456,16 +465,19 @@ static void drm_fb_rgb888_to_xrgb8888_toio(void __iomem *dst, unsigned int dst_p static void drm_fb_xrgb8888_to_xrgb2101010_line(void *dbuf, const void *sbuf, unsigned int pixels) { - u32 *dbuf32 = dbuf; - const u32 *sbuf32 = sbuf; + __le32 *dbuf32 = dbuf; + const __le32 *sbuf32 = sbuf; unsigned int x; u32 val32; + u32 pix; for (x = 0; x < pixels; x++) { - val32 = ((sbuf32[x] & 0x000000FF) << 2) | - ((sbuf32[x] & 0x0000FF00) << 4) | - ((sbuf32[x] & 0x00FF0000) << 6); - *dbuf32++ = val32 | ((val32 >> 8) & 0x00300C03); + pix = le32_to_cpu(sbuf32[x]); + val32 = ((pix & 0x000000FF) << 2) | + ((pix & 0x0000FF00) << 4) | + ((pix & 0x00FF0000) << 6); + pix = val32 | ((val32 >> 8) & 0x00300C03); + *dbuf32++ = cpu_to_le32(pix); } } @@ -494,17 +506,17 @@ EXPORT_SYMBOL(drm_fb_xrgb8888_to_xrgb2101010_toio); static void drm_fb_xrgb8888_to_gray8_line(void *dbuf, const void *sbuf, unsigned int pixels) { u8 *dbuf8 = dbuf; - const u32 *sbuf32 = sbuf; + const __le32 *sbuf32 = sbuf; unsigned int x; for (x = 0; x < pixels; x++) { - u8 r = (*sbuf32 & 0x00ff0000) >> 16; - u8 g = (*sbuf32 & 0x0000ff00) >> 8; - u8 b = *sbuf32 & 0x000000ff; + u32 pix = le32_to_cpu(sbuf32[x]); + u8 r = (pix & 0x00ff0000) >> 16; + u8 g = (pix & 0x0000ff00) >> 8; + u8 b = pix & 0x000000ff; /* ITU BT.601: Y = 0.299 R + 0.587 G + 0.114 B */ *dbuf8++ = (3 * r + 6 * g + b) / 10; - sbuf32++; } } -- cgit v1.2.3-59-g8ed1b From fa2a87e4c5cea2beba9deabcbaf54d1979fff419 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 8 Jul 2022 20:21:09 +0200 Subject: drm/gud: Fix endianness in gud_xrgb8888_to_color() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DRM formats are defined to be little-endian, unless the DRM_FORMAT_BIG_ENDIAN flag is set. Hence when converting from one format to another, multi-byte pixel values loaded from memory must be converted from little-endian to host-endian. Conversely, multi-byte pixel values written to memory must be converted from host-endian to little-endian. Currently only drm_fb_xrgb8888_to_rgb332_line() includes endianness handling. Fix gud_xrgb8888_to_color() on big-endian platforms by adding the missing endianness handling. Signed-off-by: Geert Uytterhoeven Signed-off-by: Noralf Trønnes Link: https://patchwork.freedesktop.org/patch/msgid/b47589ed5d8ca44e0956684412e3f16f3227f887.1657300532.git.geert@linux-m68k.org --- drivers/gpu/drm/gud/gud_pipe.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index 4873f9799f41..d42592f6daab 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -105,7 +105,8 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma unsigned int bits_per_pixel = 8 / block_width; u8 r, g, b, pix, *block = dst; /* Assign to silence compiler warning */ unsigned int x, y, width; - u32 *pix32; + __le32 *sbuf32; + u32 pix32; size_t len; /* Start on a byte boundary */ @@ -114,8 +115,8 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma len = drm_format_info_min_pitch(format, 0, width) * drm_rect_height(rect); for (y = rect->y1; y < rect->y2; y++) { - pix32 = src + (y * fb->pitches[0]); - pix32 += rect->x1; + sbuf32 = src + (y * fb->pitches[0]); + sbuf32 += rect->x1; for (x = 0; x < width; x++) { unsigned int pixpos = x % block_width; /* within byte from the left */ @@ -126,9 +127,10 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma *block = 0; } - r = *pix32 >> 16; - g = *pix32 >> 8; - b = *pix32++; + pix32 = le32_to_cpu(*sbuf32++); + r = pix32 >> 16; + g = pix32 >> 8; + b = pix32; switch (format->format) { case GUD_DRM_FORMAT_XRGB1111: -- cgit v1.2.3-59-g8ed1b From 6d602e031103fb78dbe50dbf57a5f29737494c6f Mon Sep 17 00:00:00 2001 From: Christian König Date: Wed, 13 Jul 2022 18:14:52 +0200 Subject: drm/sched: move calling drm_sched_entity_select_rq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already discussed that the call to drm_sched_entity_select_rq() needs to move to drm_sched_job_arm() to be able to set a new scheduler list between _init() and _arm(). This was just not applied for some reason. Signed-off-by: Christian König Reviewed-by: Andrey Grodzovsky Link: https://patchwork.freedesktop.org/patch/msgid/20220714103902.7084-2-christian.koenig@amd.com --- drivers/gpu/drm/scheduler/sched_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 68317d3a7a27..e0ab14e0fb6b 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -592,7 +592,6 @@ int drm_sched_job_init(struct drm_sched_job *job, struct drm_sched_entity *entity, void *owner) { - drm_sched_entity_select_rq(entity); if (!entity->rq) return -ENOENT; @@ -628,7 +627,7 @@ void drm_sched_job_arm(struct drm_sched_job *job) struct drm_sched_entity *entity = job->entity; BUG_ON(!entity); - + drm_sched_entity_select_rq(entity); sched = entity->rq->sched; job->sched = sched; -- cgit v1.2.3-59-g8ed1b From 2dbeef82d14fda3b15d40c94522bdf83f9c33ae8 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 11 Jul 2022 12:21:16 +0300 Subject: drm/bridge: ti-sn65dsi86: fetch bpc using drm_atomic_state Rather than reading the pdata->connector directly, fetch the connector using drm_atomic_state. This allows us to make pdata->connector optional (and thus supporting DRM_BRIDGE_ATTACH_NO_CONNECTOR). Reviewed-by: Sam Ravnborg Reviewed-by: Laurent Pinchart Signed-off-by: Dmitry Baryshkov Tested-by: Steev Klimaszewski Reviewed-by: Douglas Anderson Tested-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220711092117.360797-2-dmitry.baryshkov@linaro.org --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index d6dd4d99a229..b362a7bf4d97 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -779,9 +779,9 @@ static void ti_sn_bridge_set_dsi_rate(struct ti_sn65dsi86 *pdata) regmap_write(pdata->regmap, SN_DSIA_CLK_FREQ_REG, val); } -static unsigned int ti_sn_bridge_get_bpp(struct ti_sn65dsi86 *pdata) +static unsigned int ti_sn_bridge_get_bpp(struct drm_connector *connector) { - if (pdata->connector->display_info.bpc <= 6) + if (connector->display_info.bpc <= 6) return 18; else return 24; @@ -796,7 +796,7 @@ static const unsigned int ti_sn_bridge_dp_rate_lut[] = { 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400 }; -static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn65dsi86 *pdata) +static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn65dsi86 *pdata, unsigned int bpp) { unsigned int bit_rate_khz, dp_rate_mhz; unsigned int i; @@ -804,7 +804,7 @@ static int ti_sn_bridge_calc_min_dp_rate_idx(struct ti_sn65dsi86 *pdata) &pdata->bridge.encoder->crtc->state->adjusted_mode; /* Calculate minimum bit rate based on our pixel clock. */ - bit_rate_khz = mode->clock * ti_sn_bridge_get_bpp(pdata); + bit_rate_khz = mode->clock * bpp; /* Calculate minimum DP data rate, taking 80% as per DP spec */ dp_rate_mhz = DIV_ROUND_UP(bit_rate_khz * DP_CLK_FUDGE_NUM, @@ -1016,12 +1016,21 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge, struct drm_bridge_state *old_bridge_state) { struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); + struct drm_connector *connector; const char *last_err_str = "No supported DP rate"; unsigned int valid_rates; int dp_rate_idx; unsigned int val; int ret = -EINVAL; int max_dp_lanes; + unsigned int bpp; + + connector = drm_atomic_get_new_connector_for_encoder(old_bridge_state->base.state, + bridge->encoder); + if (!connector) { + dev_err_ratelimited(pdata->dev, "Could not get the connector\n"); + return; + } max_dp_lanes = ti_sn_get_max_lanes(pdata); pdata->dp_lanes = min(pdata->dp_lanes, max_dp_lanes); @@ -1047,8 +1056,9 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge, drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET, DP_ALTERNATE_SCRAMBLER_RESET_ENABLE); + bpp = ti_sn_bridge_get_bpp(connector); /* Set the DP output format (18 bpp or 24 bpp) */ - val = (ti_sn_bridge_get_bpp(pdata) == 18) ? BPP_18_RGB : 0; + val = bpp == 18 ? BPP_18_RGB : 0; regmap_update_bits(pdata->regmap, SN_DATA_FORMAT_REG, BPP_18_RGB, val); /* DP lane config */ @@ -1059,7 +1069,7 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge, valid_rates = ti_sn_bridge_read_valid_rates(pdata); /* Train until we run out of rates */ - for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata); + for (dp_rate_idx = ti_sn_bridge_calc_min_dp_rate_idx(pdata, bpp); dp_rate_idx < ARRAY_SIZE(ti_sn_bridge_dp_rate_lut); dp_rate_idx++) { if (!(valid_rates & BIT(dp_rate_idx))) -- cgit v1.2.3-59-g8ed1b From 6e2dc7ac71411eaf6363d63c333519e1d353d6b0 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Mon, 11 Jul 2022 12:21:17 +0300 Subject: drm/bridge: ti-sn65dsi86: support DRM_BRIDGE_ATTACH_NO_CONNECTOR Now as the driver does not depend on pdata->connector, add support for attaching the bridge with DRM_BRIDGE_ATTACH_NO_CONNECTOR. Reviewed-by: Sam Ravnborg Reviewed-by: Laurent Pinchart Signed-off-by: Dmitry Baryshkov Tested-by: Steev Klimaszewski Reviewed-by: Douglas Anderson Tested-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220711092117.360797-3-dmitry.baryshkov@linaro.org --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index b362a7bf4d97..369bf72717f6 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -698,11 +698,6 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge); int ret; - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) { - DRM_ERROR("Fix bridge driver to make connector optional!"); - return -EINVAL; - } - pdata->aux.drm_dev = bridge->dev; ret = drm_dp_aux_register(&pdata->aux); if (ret < 0) { @@ -710,15 +705,18 @@ static int ti_sn_bridge_attach(struct drm_bridge *bridge, return ret; } - /* We never want the next bridge to *also* create a connector: */ - flags |= DRM_BRIDGE_ATTACH_NO_CONNECTOR; - - /* Attach the next bridge */ + /* + * Attach the next bridge. + * We never want the next bridge to *also* create a connector. + */ ret = drm_bridge_attach(bridge->encoder, pdata->next_bridge, - &pdata->bridge, flags); + &pdata->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret < 0) goto err_initted_aux; + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + pdata->connector = drm_bridge_connector_init(pdata->bridge.dev, pdata->bridge.encoder); if (IS_ERR(pdata->connector)) { -- cgit v1.2.3-59-g8ed1b From 774ce1510e6ccb9c0752d4aa7a9ff3624b3db3f3 Mon Sep 17 00:00:00 2001 From: Daniele Ceraolo Spurio Date: Mon, 18 Jul 2022 16:07:32 -0700 Subject: drm/i915/guc: support v69 in parallel to v70 This patch re-introduces support for GuC v69 in parallel to v70. As this is a quick fix, v69 has been re-introduced as the single "fallback" guc version in case v70 is not available on disk and only for platforms that are out of force_probe and require the GuC by default. All v69 specific code has been labeled as such for easy identification, and the same was done for all v70 functions for which there is a separate v69 version, to avoid accidentally calling the wrong version via the unlabeled name. When the fallback mode kicks in, a drm_notice message is printed in dmesg to inform the user of the required update. The existing logging of the fetch function has also been updated so that we no longer complain immediately if we can't find a fw and we only throw an error if the fetch of both the base and fallback blobs fails. The plan is to follow this up with a more complex rework to allow for multiple different GuC versions to be supported at the same time. v2: reduce the fallback to platform that require it, switch to firmware_request_nowarn(), improve logs. Fixes: 2584b3549f4c ("drm/i915/guc: Update to GuC version 70.1.1") Link: https://lists.freedesktop.org/archives/intel-gfx/2022-July/301640.html Signed-off-by: Daniele Ceraolo Spurio Cc: John Harrison Cc: Matthew Brost Cc: Matt Roper Cc: Dave Airlie Cc: Michal Wajdeczko Acked-by: Rodrigo Vivi Reviewed-by: John Harrison Link: https://patchwork.freedesktop.org/patch/msgid/20220718230732.1409641-1-daniele.ceraolospurio@intel.com --- drivers/gpu/drm/i915/gt/intel_context_types.h | 11 +- drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h | 3 + drivers/gpu/drm/i915/gt/uc/intel_guc.h | 5 + drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h | 45 +++ drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c | 352 ++++++++++++++++++---- drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 56 +++- drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h | 7 + 7 files changed, 417 insertions(+), 62 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h b/drivers/gpu/drm/i915/gt/intel_context_types.h index d2d75d9c0c8d..04eacae1aca5 100644 --- a/drivers/gpu/drm/i915/gt/intel_context_types.h +++ b/drivers/gpu/drm/i915/gt/intel_context_types.h @@ -275,10 +275,17 @@ struct intel_context { u8 child_index; /** @guc: GuC specific members for parallel submission */ struct { - /** @wqi_head: head pointer in work queue */ + /** @wqi_head: cached head pointer in work queue */ u16 wqi_head; - /** @wqi_tail: tail pointer in work queue */ + /** @wqi_tail: cached tail pointer in work queue */ u16 wqi_tail; + /** @wq_head: pointer to the actual head in work queue */ + u32 *wq_head; + /** @wq_tail: pointer to the actual head in work queue */ + u32 *wq_tail; + /** @wq_status: pointer to the status in work queue */ + u32 *wq_status; + /** * @parent_page: page in context state (ce->state) used * by parent for work queue, process descriptor diff --git a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h index 4ef9990ed7f8..29ef8afc8c2e 100644 --- a/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h +++ b/drivers/gpu/drm/i915/gt/uc/abi/guc_actions_abi.h @@ -122,6 +122,9 @@ enum intel_guc_action { INTEL_GUC_ACTION_SCHED_CONTEXT_MODE_DONE = 0x1002, INTEL_GUC_ACTION_SCHED_ENGINE_MODE_SET = 0x1003, INTEL_GUC_ACTION_SCHED_ENGINE_MODE_DONE = 0x1004, + INTEL_GUC_ACTION_V69_SET_CONTEXT_PRIORITY = 0x1005, + INTEL_GUC_ACTION_V69_SET_CONTEXT_EXECUTION_QUANTUM = 0x1006, + INTEL_GUC_ACTION_V69_SET_CONTEXT_PREEMPTION_TIMEOUT = 0x1007, INTEL_GUC_ACTION_CONTEXT_RESET_NOTIFICATION = 0x1008, INTEL_GUC_ACTION_ENGINE_FAILURE_NOTIFICATION = 0x1009, INTEL_GUC_ACTION_HOST2GUC_UPDATE_CONTEXT_POLICIES = 0x100B, diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index d0d99f178f2d..a7acffbf15d1 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -170,6 +170,11 @@ struct intel_guc { /** @ads_engine_usage_size: size of engine usage in the ADS */ u32 ads_engine_usage_size; + /** @lrc_desc_pool_v69: object allocated to hold the GuC LRC descriptor pool */ + struct i915_vma *lrc_desc_pool_v69; + /** @lrc_desc_pool_vaddr_v69: contents of the GuC LRC descriptor pool */ + void *lrc_desc_pool_vaddr_v69; + /** * @context_lookup: used to resolve intel_context from guc_id, if a * context is present in this structure it is registered with the GuC diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h index b3c9a9327f76..323b055e5db9 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_fwif.h @@ -204,6 +204,20 @@ struct guc_wq_item { u32 fence_id; } __packed; +struct guc_process_desc_v69 { + u32 stage_id; + u64 db_base_addr; + u32 head; + u32 tail; + u32 error_offset; + u64 wq_base_addr; + u32 wq_size_bytes; + u32 wq_status; + u32 engine_presence; + u32 priority; + u32 reserved[36]; +} __packed; + struct guc_sched_wq_desc { u32 head; u32 tail; @@ -228,6 +242,37 @@ struct guc_ctxt_registration_info { }; #define CONTEXT_REGISTRATION_FLAG_KMD BIT(0) +/* Preempt to idle on quantum expiry */ +#define CONTEXT_POLICY_FLAG_PREEMPT_TO_IDLE_V69 BIT(0) + +/* + * GuC Context registration descriptor. + * FIXME: This is only required to exist during context registration. + * The current 1:1 between guc_lrc_desc and LRCs for the lifetime of the LRC + * is not required. + */ +struct guc_lrc_desc_v69 { + u32 hw_context_desc; + u32 slpm_perf_mode_hint; /* SPLC v1 only */ + u32 slpm_freq_hint; + u32 engine_submit_mask; /* In logical space */ + u8 engine_class; + u8 reserved0[3]; + u32 priority; + u32 process_desc; + u32 wq_addr; + u32 wq_size; + u32 context_flags; /* CONTEXT_REGISTRATION_* */ + /* Time for one workload to execute. (in micro seconds) */ + u32 execution_quantum; + /* Time to wait for a preemption request to complete before issuing a + * reset. (in micro seconds). + */ + u32 preemption_timeout; + u32 policy_flags; /* CONTEXT_POLICY_* */ + u32 reserved1[19]; +} __packed; + /* 32-bit KLV structure as used by policy updates and others */ struct guc_klv_generic_dw_t { u32 kl; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c index 40f726c61e95..76916aed897a 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c @@ -414,12 +414,15 @@ struct sync_semaphore { }; struct parent_scratch { - struct guc_sched_wq_desc wq_desc; + union guc_descs { + struct guc_sched_wq_desc wq_desc; + struct guc_process_desc_v69 pdesc; + } descs; struct sync_semaphore go; struct sync_semaphore join[MAX_ENGINE_INSTANCE + 1]; - u8 unused[WQ_OFFSET - sizeof(struct guc_sched_wq_desc) - + u8 unused[WQ_OFFSET - sizeof(union guc_descs) - sizeof(struct sync_semaphore) * (MAX_ENGINE_INSTANCE + 2)]; u32 wq[WQ_SIZE / sizeof(u32)]; @@ -456,17 +459,23 @@ __get_parent_scratch(struct intel_context *ce) LRC_STATE_OFFSET) / sizeof(u32))); } +static struct guc_process_desc_v69 * +__get_process_desc_v69(struct intel_context *ce) +{ + struct parent_scratch *ps = __get_parent_scratch(ce); + + return &ps->descs.pdesc; +} + static struct guc_sched_wq_desc * -__get_wq_desc(struct intel_context *ce) +__get_wq_desc_v70(struct intel_context *ce) { struct parent_scratch *ps = __get_parent_scratch(ce); - return &ps->wq_desc; + return &ps->descs.wq_desc; } -static u32 *get_wq_pointer(struct guc_sched_wq_desc *wq_desc, - struct intel_context *ce, - u32 wqi_size) +static u32 *get_wq_pointer(struct intel_context *ce, u32 wqi_size) { /* * Check for space in work queue. Caching a value of head pointer in @@ -476,7 +485,7 @@ static u32 *get_wq_pointer(struct guc_sched_wq_desc *wq_desc, #define AVAILABLE_SPACE \ CIRC_SPACE(ce->parallel.guc.wqi_tail, ce->parallel.guc.wqi_head, WQ_SIZE) if (wqi_size > AVAILABLE_SPACE) { - ce->parallel.guc.wqi_head = READ_ONCE(wq_desc->head); + ce->parallel.guc.wqi_head = READ_ONCE(*ce->parallel.guc.wq_head); if (wqi_size > AVAILABLE_SPACE) return NULL; @@ -495,11 +504,55 @@ static inline struct intel_context *__get_context(struct intel_guc *guc, u32 id) return ce; } +static struct guc_lrc_desc_v69 *__get_lrc_desc_v69(struct intel_guc *guc, u32 index) +{ + struct guc_lrc_desc_v69 *base = guc->lrc_desc_pool_vaddr_v69; + + if (!base) + return NULL; + + GEM_BUG_ON(index >= GUC_MAX_CONTEXT_ID); + + return &base[index]; +} + +static int guc_lrc_desc_pool_create_v69(struct intel_guc *guc) +{ + u32 size; + int ret; + + size = PAGE_ALIGN(sizeof(struct guc_lrc_desc_v69) * + GUC_MAX_CONTEXT_ID); + ret = intel_guc_allocate_and_map_vma(guc, size, &guc->lrc_desc_pool_v69, + (void **)&guc->lrc_desc_pool_vaddr_v69); + if (ret) + return ret; + + return 0; +} + +static void guc_lrc_desc_pool_destroy_v69(struct intel_guc *guc) +{ + if (!guc->lrc_desc_pool_vaddr_v69) + return; + + guc->lrc_desc_pool_vaddr_v69 = NULL; + i915_vma_unpin_and_release(&guc->lrc_desc_pool_v69, I915_VMA_RELEASE_MAP); +} + static inline bool guc_submission_initialized(struct intel_guc *guc) { return guc->submission_initialized; } +static inline void _reset_lrc_desc_v69(struct intel_guc *guc, u32 id) +{ + struct guc_lrc_desc_v69 *desc = __get_lrc_desc_v69(guc, id); + + if (desc) + memset(desc, 0, sizeof(*desc)); +} + static inline bool ctx_id_mapped(struct intel_guc *guc, u32 id) { return __get_context(guc, id); @@ -526,6 +579,8 @@ static inline void clr_ctx_id_mapping(struct intel_guc *guc, u32 id) if (unlikely(!guc_submission_initialized(guc))) return; + _reset_lrc_desc_v69(guc, id); + /* * xarray API doesn't have xa_erase_irqsave wrapper, so calling * the lower level functions directly. @@ -611,7 +666,7 @@ int intel_guc_wait_for_idle(struct intel_guc *guc, long timeout) true, timeout); } -static int guc_context_policy_init(struct intel_context *ce, bool loop); +static int guc_context_policy_init_v70(struct intel_context *ce, bool loop); static int try_context_registration(struct intel_context *ce, bool loop); static int __guc_add_request(struct intel_guc *guc, struct i915_request *rq) @@ -639,7 +694,7 @@ static int __guc_add_request(struct intel_guc *guc, struct i915_request *rq) GEM_BUG_ON(context_guc_id_invalid(ce)); if (context_policy_required(ce)) { - err = guc_context_policy_init(ce, false); + err = guc_context_policy_init_v70(ce, false); if (err) return err; } @@ -737,9 +792,7 @@ static u32 wq_space_until_wrap(struct intel_context *ce) return (WQ_SIZE - ce->parallel.guc.wqi_tail); } -static void write_wqi(struct guc_sched_wq_desc *wq_desc, - struct intel_context *ce, - u32 wqi_size) +static void write_wqi(struct intel_context *ce, u32 wqi_size) { BUILD_BUG_ON(!is_power_of_2(WQ_SIZE)); @@ -750,13 +803,12 @@ static void write_wqi(struct guc_sched_wq_desc *wq_desc, ce->parallel.guc.wqi_tail = (ce->parallel.guc.wqi_tail + wqi_size) & (WQ_SIZE - 1); - WRITE_ONCE(wq_desc->tail, ce->parallel.guc.wqi_tail); + WRITE_ONCE(*ce->parallel.guc.wq_tail, ce->parallel.guc.wqi_tail); } static int guc_wq_noop_append(struct intel_context *ce) { - struct guc_sched_wq_desc *wq_desc = __get_wq_desc(ce); - u32 *wqi = get_wq_pointer(wq_desc, ce, wq_space_until_wrap(ce)); + u32 *wqi = get_wq_pointer(ce, wq_space_until_wrap(ce)); u32 len_dw = wq_space_until_wrap(ce) / sizeof(u32) - 1; if (!wqi) @@ -775,7 +827,6 @@ static int __guc_wq_item_append(struct i915_request *rq) { struct intel_context *ce = request_to_scheduling_context(rq); struct intel_context *child; - struct guc_sched_wq_desc *wq_desc = __get_wq_desc(ce); unsigned int wqi_size = (ce->parallel.number_children + 4) * sizeof(u32); u32 *wqi; @@ -795,7 +846,7 @@ static int __guc_wq_item_append(struct i915_request *rq) return ret; } - wqi = get_wq_pointer(wq_desc, ce, wqi_size); + wqi = get_wq_pointer(ce, wqi_size); if (!wqi) return -EBUSY; @@ -810,7 +861,7 @@ static int __guc_wq_item_append(struct i915_request *rq) for_each_child(ce, child) *wqi++ = child->ring->tail / sizeof(u64); - write_wqi(wq_desc, ce, wqi_size); + write_wqi(ce, wqi_size); return 0; } @@ -1812,20 +1863,34 @@ static void reset_fail_worker_func(struct work_struct *w); int intel_guc_submission_init(struct intel_guc *guc) { struct intel_gt *gt = guc_to_gt(guc); + int ret; if (guc->submission_initialized) return 0; + if (guc->fw.major_ver_found < 70) { + ret = guc_lrc_desc_pool_create_v69(guc); + if (ret) + return ret; + } + guc->submission_state.guc_ids_bitmap = bitmap_zalloc(NUMBER_MULTI_LRC_GUC_ID(guc), GFP_KERNEL); - if (!guc->submission_state.guc_ids_bitmap) - return -ENOMEM; + if (!guc->submission_state.guc_ids_bitmap) { + ret = -ENOMEM; + goto destroy_pool; + } guc->timestamp.ping_delay = (POLL_TIME_CLKS / gt->clock_frequency + 1) * HZ; guc->timestamp.shift = gpm_timestamp_shift(gt); guc->submission_initialized = true; return 0; + +destroy_pool: + guc_lrc_desc_pool_destroy_v69(guc); + + return ret; } void intel_guc_submission_fini(struct intel_guc *guc) @@ -1834,6 +1899,7 @@ void intel_guc_submission_fini(struct intel_guc *guc) return; guc_flush_destroyed_contexts(guc); + guc_lrc_desc_pool_destroy_v69(guc); i915_sched_engine_put(guc->sched_engine); bitmap_free(guc->submission_state.guc_ids_bitmap); guc->submission_initialized = false; @@ -2091,10 +2157,34 @@ static void unpin_guc_id(struct intel_guc *guc, struct intel_context *ce) spin_unlock_irqrestore(&guc->submission_state.lock, flags); } -static int __guc_action_register_multi_lrc(struct intel_guc *guc, - struct intel_context *ce, - struct guc_ctxt_registration_info *info, - bool loop) +static int __guc_action_register_multi_lrc_v69(struct intel_guc *guc, + struct intel_context *ce, + u32 guc_id, + u32 offset, + bool loop) +{ + struct intel_context *child; + u32 action[4 + MAX_ENGINE_INSTANCE]; + int len = 0; + + GEM_BUG_ON(ce->parallel.number_children > MAX_ENGINE_INSTANCE); + + action[len++] = INTEL_GUC_ACTION_REGISTER_CONTEXT_MULTI_LRC; + action[len++] = guc_id; + action[len++] = ce->parallel.number_children + 1; + action[len++] = offset; + for_each_child(ce, child) { + offset += sizeof(struct guc_lrc_desc_v69); + action[len++] = offset; + } + + return guc_submission_send_busy_loop(guc, action, len, 0, loop); +} + +static int __guc_action_register_multi_lrc_v70(struct intel_guc *guc, + struct intel_context *ce, + struct guc_ctxt_registration_info *info, + bool loop) { struct intel_context *child; u32 action[13 + (MAX_ENGINE_INSTANCE * 2)]; @@ -2134,9 +2224,24 @@ static int __guc_action_register_multi_lrc(struct intel_guc *guc, return guc_submission_send_busy_loop(guc, action, len, 0, loop); } -static int __guc_action_register_context(struct intel_guc *guc, - struct guc_ctxt_registration_info *info, - bool loop) +static int __guc_action_register_context_v69(struct intel_guc *guc, + u32 guc_id, + u32 offset, + bool loop) +{ + u32 action[] = { + INTEL_GUC_ACTION_REGISTER_CONTEXT, + guc_id, + offset, + }; + + return guc_submission_send_busy_loop(guc, action, ARRAY_SIZE(action), + 0, loop); +} + +static int __guc_action_register_context_v70(struct intel_guc *guc, + struct guc_ctxt_registration_info *info, + bool loop) { u32 action[] = { INTEL_GUC_ACTION_REGISTER_CONTEXT, @@ -2157,24 +2262,52 @@ static int __guc_action_register_context(struct intel_guc *guc, 0, loop); } -static void prepare_context_registration_info(struct intel_context *ce, - struct guc_ctxt_registration_info *info); +static void prepare_context_registration_info_v69(struct intel_context *ce); +static void prepare_context_registration_info_v70(struct intel_context *ce, + struct guc_ctxt_registration_info *info); -static int register_context(struct intel_context *ce, bool loop) +static int +register_context_v69(struct intel_guc *guc, struct intel_context *ce, bool loop) +{ + u32 offset = intel_guc_ggtt_offset(guc, guc->lrc_desc_pool_v69) + + ce->guc_id.id * sizeof(struct guc_lrc_desc_v69); + + prepare_context_registration_info_v69(ce); + + if (intel_context_is_parent(ce)) + return __guc_action_register_multi_lrc_v69(guc, ce, ce->guc_id.id, + offset, loop); + else + return __guc_action_register_context_v69(guc, ce->guc_id.id, + offset, loop); +} + +static int +register_context_v70(struct intel_guc *guc, struct intel_context *ce, bool loop) { struct guc_ctxt_registration_info info; + + prepare_context_registration_info_v70(ce, &info); + + if (intel_context_is_parent(ce)) + return __guc_action_register_multi_lrc_v70(guc, ce, &info, loop); + else + return __guc_action_register_context_v70(guc, &info, loop); +} + +static int register_context(struct intel_context *ce, bool loop) +{ struct intel_guc *guc = ce_to_guc(ce); int ret; GEM_BUG_ON(intel_context_is_child(ce)); trace_intel_context_register(ce); - prepare_context_registration_info(ce, &info); - - if (intel_context_is_parent(ce)) - ret = __guc_action_register_multi_lrc(guc, ce, &info, loop); + if (guc->fw.major_ver_found >= 70) + ret = register_context_v70(guc, ce, loop); else - ret = __guc_action_register_context(guc, &info, loop); + ret = register_context_v69(guc, ce, loop); + if (likely(!ret)) { unsigned long flags; @@ -2182,7 +2315,8 @@ static int register_context(struct intel_context *ce, bool loop) set_context_registered(ce); spin_unlock_irqrestore(&ce->guc_state.lock, flags); - guc_context_policy_init(ce, loop); + if (guc->fw.major_ver_found >= 70) + guc_context_policy_init_v70(ce, loop); } return ret; @@ -2279,7 +2413,7 @@ static int __guc_context_set_context_policies(struct intel_guc *guc, 0, loop); } -static int guc_context_policy_init(struct intel_context *ce, bool loop) +static int guc_context_policy_init_v70(struct intel_context *ce, bool loop) { struct intel_engine_cs *engine = ce->engine; struct intel_guc *guc = &engine->gt->uc.guc; @@ -2338,6 +2472,19 @@ static int guc_context_policy_init(struct intel_context *ce, bool loop) return ret; } +static void guc_context_policy_init_v69(struct intel_engine_cs *engine, + struct guc_lrc_desc_v69 *desc) +{ + desc->policy_flags = 0; + + if (engine->flags & I915_ENGINE_WANT_FORCED_PREEMPTION) + desc->policy_flags |= CONTEXT_POLICY_FLAG_PREEMPT_TO_IDLE_V69; + + /* NB: For both of these, zero means disabled. */ + desc->execution_quantum = engine->props.timeslice_duration_ms * 1000; + desc->preemption_timeout = engine->props.preempt_timeout_ms * 1000; +} + static u32 map_guc_prio_to_lrc_desc_prio(u8 prio) { /* @@ -2358,8 +2505,75 @@ static u32 map_guc_prio_to_lrc_desc_prio(u8 prio) } } -static void prepare_context_registration_info(struct intel_context *ce, - struct guc_ctxt_registration_info *info) +static void prepare_context_registration_info_v69(struct intel_context *ce) +{ + struct intel_engine_cs *engine = ce->engine; + struct intel_guc *guc = &engine->gt->uc.guc; + u32 ctx_id = ce->guc_id.id; + struct guc_lrc_desc_v69 *desc; + struct intel_context *child; + + GEM_BUG_ON(!engine->mask); + + /* + * Ensure LRC + CT vmas are is same region as write barrier is done + * based on CT vma region. + */ + GEM_BUG_ON(i915_gem_object_is_lmem(guc->ct.vma->obj) != + i915_gem_object_is_lmem(ce->ring->vma->obj)); + + desc = __get_lrc_desc_v69(guc, ctx_id); + desc->engine_class = engine_class_to_guc_class(engine->class); + desc->engine_submit_mask = engine->logical_mask; + desc->hw_context_desc = ce->lrc.lrca; + desc->priority = ce->guc_state.prio; + desc->context_flags = CONTEXT_REGISTRATION_FLAG_KMD; + guc_context_policy_init_v69(engine, desc); + + /* + * If context is a parent, we need to register a process descriptor + * describing a work queue and register all child contexts. + */ + if (intel_context_is_parent(ce)) { + struct guc_process_desc_v69 *pdesc; + + ce->parallel.guc.wqi_tail = 0; + ce->parallel.guc.wqi_head = 0; + + desc->process_desc = i915_ggtt_offset(ce->state) + + __get_parent_scratch_offset(ce); + desc->wq_addr = i915_ggtt_offset(ce->state) + + __get_wq_offset(ce); + desc->wq_size = WQ_SIZE; + + pdesc = __get_process_desc_v69(ce); + memset(pdesc, 0, sizeof(*(pdesc))); + pdesc->stage_id = ce->guc_id.id; + pdesc->wq_base_addr = desc->wq_addr; + pdesc->wq_size_bytes = desc->wq_size; + pdesc->wq_status = WQ_STATUS_ACTIVE; + + ce->parallel.guc.wq_head = &pdesc->head; + ce->parallel.guc.wq_tail = &pdesc->tail; + ce->parallel.guc.wq_status = &pdesc->wq_status; + + for_each_child(ce, child) { + desc = __get_lrc_desc_v69(guc, child->guc_id.id); + + desc->engine_class = + engine_class_to_guc_class(engine->class); + desc->hw_context_desc = child->lrc.lrca; + desc->priority = ce->guc_state.prio; + desc->context_flags = CONTEXT_REGISTRATION_FLAG_KMD; + guc_context_policy_init_v69(engine, desc); + } + + clear_children_join_go_memory(ce); + } +} + +static void prepare_context_registration_info_v70(struct intel_context *ce, + struct guc_ctxt_registration_info *info) { struct intel_engine_cs *engine = ce->engine; struct intel_guc *guc = &engine->gt->uc.guc; @@ -2409,10 +2623,14 @@ static void prepare_context_registration_info(struct intel_context *ce, info->wq_base_hi = upper_32_bits(wq_base_offset); info->wq_size = WQ_SIZE; - wq_desc = __get_wq_desc(ce); + wq_desc = __get_wq_desc_v70(ce); memset(wq_desc, 0, sizeof(*wq_desc)); wq_desc->wq_status = WQ_STATUS_ACTIVE; + ce->parallel.guc.wq_head = &wq_desc->head; + ce->parallel.guc.wq_tail = &wq_desc->tail; + ce->parallel.guc.wq_status = &wq_desc->wq_status; + clear_children_join_go_memory(ce); } } @@ -2727,11 +2945,21 @@ static void __guc_context_set_preemption_timeout(struct intel_guc *guc, u16 guc_id, u32 preemption_timeout) { - struct context_policy policy; + if (guc->fw.major_ver_found >= 70) { + struct context_policy policy; - __guc_context_policy_start_klv(&policy, guc_id); - __guc_context_policy_add_preemption_timeout(&policy, preemption_timeout); - __guc_context_set_context_policies(guc, &policy, true); + __guc_context_policy_start_klv(&policy, guc_id); + __guc_context_policy_add_preemption_timeout(&policy, preemption_timeout); + __guc_context_set_context_policies(guc, &policy, true); + } else { + u32 action[] = { + INTEL_GUC_ACTION_V69_SET_CONTEXT_PREEMPTION_TIMEOUT, + guc_id, + preemption_timeout + }; + + intel_guc_send_busy_loop(guc, action, ARRAY_SIZE(action), 0, true); + } } static void @@ -2982,11 +3210,21 @@ static int guc_context_alloc(struct intel_context *ce) static void __guc_context_set_prio(struct intel_guc *guc, struct intel_context *ce) { - struct context_policy policy; + if (guc->fw.major_ver_found >= 70) { + struct context_policy policy; - __guc_context_policy_start_klv(&policy, ce->guc_id.id); - __guc_context_policy_add_priority(&policy, ce->guc_state.prio); - __guc_context_set_context_policies(guc, &policy, true); + __guc_context_policy_start_klv(&policy, ce->guc_id.id); + __guc_context_policy_add_priority(&policy, ce->guc_state.prio); + __guc_context_set_context_policies(guc, &policy, true); + } else { + u32 action[] = { + INTEL_GUC_ACTION_V69_SET_CONTEXT_PRIORITY, + ce->guc_id.id, + ce->guc_state.prio, + }; + + guc_submission_send_busy_loop(guc, action, ARRAY_SIZE(action), 0, true); + } } static void guc_context_set_prio(struct intel_guc *guc, @@ -4496,17 +4734,19 @@ void intel_guc_submission_print_context_info(struct intel_guc *guc, guc_log_context_priority(p, ce); if (intel_context_is_parent(ce)) { - struct guc_sched_wq_desc *wq_desc = __get_wq_desc(ce); struct intel_context *child; drm_printf(p, "\t\tNumber children: %u\n", ce->parallel.number_children); - drm_printf(p, "\t\tWQI Head: %u\n", - READ_ONCE(wq_desc->head)); - drm_printf(p, "\t\tWQI Tail: %u\n", - READ_ONCE(wq_desc->tail)); - drm_printf(p, "\t\tWQI Status: %u\n\n", - READ_ONCE(wq_desc->wq_status)); + + if (ce->parallel.guc.wq_status) { + drm_printf(p, "\t\tWQI Head: %u\n", + READ_ONCE(*ce->parallel.guc.wq_head)); + drm_printf(p, "\t\tWQI Tail: %u\n", + READ_ONCE(*ce->parallel.guc.wq_tail)); + drm_printf(p, "\t\tWQI Status: %u\n\n", + READ_ONCE(*ce->parallel.guc.wq_status)); + } if (ce->engine->emit_bb_start == emit_bb_start_parent_no_preempt_mid_batch) { diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c index 27363091e1af..56a0d80f88ba 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c @@ -70,6 +70,10 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, fw_def(BROXTON, 0, guc_def(bxt, 70, 1, 1)) \ fw_def(SKYLAKE, 0, guc_def(skl, 70, 1, 1)) +#define INTEL_GUC_FIRMWARE_DEFS_FALLBACK(fw_def, guc_def) \ + fw_def(ALDERLAKE_P, 0, guc_def(adlp, 69, 0, 3)) \ + fw_def(ALDERLAKE_S, 0, guc_def(tgl, 69, 0, 3)) + #define INTEL_HUC_FIRMWARE_DEFS(fw_def, huc_def) \ fw_def(ALDERLAKE_P, 0, huc_def(tgl, 7, 9, 3)) \ fw_def(ALDERLAKE_S, 0, huc_def(tgl, 7, 9, 3)) \ @@ -105,6 +109,7 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, MODULE_FIRMWARE(uc_); INTEL_GUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH) +INTEL_GUC_FIRMWARE_DEFS_FALLBACK(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH) INTEL_HUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_HUC_FW_PATH) /* The below structs and macros are used to iterate across the list of blobs */ @@ -149,6 +154,9 @@ __uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw) static const struct uc_fw_platform_requirement blobs_guc[] = { INTEL_GUC_FIRMWARE_DEFS(MAKE_FW_LIST, GUC_FW_BLOB) }; + static const struct uc_fw_platform_requirement blobs_guc_fallback[] = { + INTEL_GUC_FIRMWARE_DEFS_FALLBACK(MAKE_FW_LIST, GUC_FW_BLOB) + }; static const struct uc_fw_platform_requirement blobs_huc[] = { INTEL_HUC_FIRMWARE_DEFS(MAKE_FW_LIST, HUC_FW_BLOB) }; @@ -179,12 +187,29 @@ __uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw) if (p == fw_blobs[i].p && rev >= fw_blobs[i].rev) { const struct uc_fw_blob *blob = &fw_blobs[i].blob; uc_fw->path = blob->path; + uc_fw->wanted_path = blob->path; uc_fw->major_ver_wanted = blob->major; uc_fw->minor_ver_wanted = blob->minor; break; } } + if (uc_fw->type == INTEL_UC_FW_TYPE_GUC) { + const struct uc_fw_platform_requirement *blobs = blobs_guc_fallback; + u32 count = ARRAY_SIZE(blobs_guc_fallback); + + for (i = 0; i < count && p <= blobs[i].p; i++) { + if (p == blobs[i].p && rev >= blobs[i].rev) { + const struct uc_fw_blob *blob = &blobs[i].blob; + + uc_fw->fallback.path = blob->path; + uc_fw->fallback.major_ver = blob->major; + uc_fw->fallback.minor_ver = blob->minor; + break; + } + } + } + /* make sure the list is ordered as expected */ if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST)) { for (i = 1; i < fw_count; i++) { @@ -412,7 +437,24 @@ int intel_uc_fw_fetch(struct intel_uc_fw *uc_fw) __force_fw_fetch_failures(uc_fw, -EINVAL); __force_fw_fetch_failures(uc_fw, -ESTALE); - err = request_firmware(&fw, uc_fw->path, dev); + err = firmware_request_nowarn(&fw, uc_fw->path, dev); + if (err && !intel_uc_fw_is_overridden(uc_fw) && uc_fw->fallback.path) { + err = firmware_request_nowarn(&fw, uc_fw->fallback.path, dev); + if (!err) { + drm_notice(&i915->drm, + "%s firmware %s is recommended, but only %s was found\n", + intel_uc_fw_type_repr(uc_fw->type), + uc_fw->wanted_path, + uc_fw->fallback.path); + drm_info(&i915->drm, + "Consider updating your linux-firmware pkg or downloading from %s\n", + INTEL_UC_FIRMWARE_URL); + + uc_fw->path = uc_fw->fallback.path; + uc_fw->major_ver_wanted = uc_fw->fallback.major_ver; + uc_fw->minor_ver_wanted = uc_fw->fallback.minor_ver; + } + } if (err) goto fail; @@ -460,8 +502,8 @@ fail: INTEL_UC_FIRMWARE_MISSING : INTEL_UC_FIRMWARE_ERROR); - drm_notice(&i915->drm, "%s firmware %s: fetch failed with error %d\n", - intel_uc_fw_type_repr(uc_fw->type), uc_fw->path, err); + i915_probe_error(i915, "%s firmware %s: fetch failed with error %d\n", + intel_uc_fw_type_repr(uc_fw->type), uc_fw->path, err); drm_info(&i915->drm, "%s firmware(s) can be downloaded from %s\n", intel_uc_fw_type_repr(uc_fw->type), INTEL_UC_FIRMWARE_URL); @@ -822,7 +864,13 @@ size_t intel_uc_fw_copy_rsa(struct intel_uc_fw *uc_fw, void *dst, u32 max_len) void intel_uc_fw_dump(const struct intel_uc_fw *uc_fw, struct drm_printer *p) { drm_printf(p, "%s firmware: %s\n", - intel_uc_fw_type_repr(uc_fw->type), uc_fw->path); + intel_uc_fw_type_repr(uc_fw->type), uc_fw->wanted_path); + if (uc_fw->fallback.path) { + drm_printf(p, "%s firmware fallback: %s\n", + intel_uc_fw_type_repr(uc_fw->type), uc_fw->fallback.path); + drm_printf(p, "fallback selected: %s\n", + str_yes_no(uc_fw->path == uc_fw->fallback.path)); + } drm_printf(p, "\tstatus: %s\n", intel_uc_fw_status_repr(uc_fw->status)); drm_printf(p, "\tversion: wanted %u.%u, found %u.%u\n", diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h index 4f169035f504..7aa2644400b9 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.h @@ -74,6 +74,7 @@ struct intel_uc_fw { const enum intel_uc_fw_status status; enum intel_uc_fw_status __status; /* no accidental overwrites */ }; + const char *wanted_path; const char *path; bool user_overridden; size_t size; @@ -98,6 +99,12 @@ struct intel_uc_fw { u16 major_ver_found; u16 minor_ver_found; + struct { + const char *path; + u16 major_ver; + u16 minor_ver; + } fallback; + u32 rsa_size; u32 ucode_size; -- cgit v1.2.3-59-g8ed1b From f6ff4570e567b687b851a2cf52c7e300c399d925 Mon Sep 17 00:00:00 2001 From: "Nícolas F. R. A. Prado" Date: Tue, 19 Jul 2022 16:38:54 -0400 Subject: drm/panel-edp: Add panel entry for R140NWF5 RH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add panel identification entry for the IVO R140NWF5 RH (product ID: 0x057d) panel. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220719203857.1488831-2-nfraprado@collabora.com --- drivers/gpu/drm/panel/panel-edp.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 3626469c4cc2..675d793d925e 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1854,6 +1854,12 @@ static const struct panel_delay delay_100_500_e200 = { .enable = 200, }; +static const struct panel_delay delay_200_500_e200 = { + .hpd_absent = 200, + .unprepare = 500, + .enable = 200, +}; + #define EDP_PANEL_ENTRY(vend_chr_0, vend_chr_1, vend_chr_2, product_id, _delay, _name) \ { \ .name = _name, \ @@ -1882,6 +1888,8 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"), + EDP_PANEL_ENTRY('I', 'V', 'O', 0x057d, &delay_200_500_e200, "R140NWF5 RH"), + EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"), EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"), -- cgit v1.2.3-59-g8ed1b From b68735e8ef58be6df7a6f511c60186e6051d9fb6 Mon Sep 17 00:00:00 2001 From: "Nícolas F. R. A. Prado" Date: Wed, 20 Jul 2022 15:11:58 -0400 Subject: drm/panel-edp: Add panel entry for B120XAN01.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add panel identification entry for the AUO B120XAN01.0 (product ID: 0x1062) panel. Signed-off-by: Nícolas F. R. A. Prado Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220720191158.1590833-1-nfraprado@collabora.com --- drivers/gpu/drm/panel/panel-edp.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 675d793d925e..e6645d6e9b59 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1876,6 +1876,7 @@ static const struct panel_delay delay_200_500_e200 = { * Sort first by vendor, then by product ID. */ static const struct edp_panel_entry edp_panels[] = { + EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"), -- cgit v1.2.3-59-g8ed1b From a4a4307003307220321f946892829a5364433809 Mon Sep 17 00:00:00 2001 From: John Harrison Date: Thu, 14 Jul 2022 17:40:28 -0700 Subject: drm/i915/guc: Don't use pr_err when not necessary A bunch of code was copy/pasted using pr_err as the default way to report errors. However, drm_err is significantly more useful in identifying where the error came from. So update the code to use that instead. Signed-off-by: John Harrison Reviewed-by: Ashutosh Dixit Link: https://patchwork.freedesktop.org/patch/msgid/20220715004028.2126239-1-John.C.Harrison@Intel.com --- drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c | 10 +++--- drivers/gpu/drm/i915/gt/uc/selftest_guc.c | 37 +++++++++++----------- .../gpu/drm/i915/gt/uc/selftest_guc_multi_lrc.c | 10 +++--- 3 files changed, 28 insertions(+), 29 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c index 56a0d80f88ba..d5fca1f68eff 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c @@ -220,11 +220,11 @@ __uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw) fw_blobs[i].rev < fw_blobs[i - 1].rev) continue; - pr_err("invalid FW blob order: %s r%u comes before %s r%u\n", - intel_platform_name(fw_blobs[i - 1].p), - fw_blobs[i - 1].rev, - intel_platform_name(fw_blobs[i].p), - fw_blobs[i].rev); + drm_err(&i915->drm, "Invalid FW blob order: %s r%u comes before %s r%u\n", + intel_platform_name(fw_blobs[i - 1].p), + fw_blobs[i - 1].rev, + intel_platform_name(fw_blobs[i].p), + fw_blobs[i].rev); uc_fw->path = NULL; } diff --git a/drivers/gpu/drm/i915/gt/uc/selftest_guc.c b/drivers/gpu/drm/i915/gt/uc/selftest_guc.c index 1df71d0796ae..20e0c39259fb 100644 --- a/drivers/gpu/drm/i915/gt/uc/selftest_guc.c +++ b/drivers/gpu/drm/i915/gt/uc/selftest_guc.c @@ -62,7 +62,7 @@ static int intel_guc_scrub_ctbs(void *arg) ce = intel_context_create(engine); if (IS_ERR(ce)) { ret = PTR_ERR(ce); - pr_err("Failed to create context, %d: %d\n", i, ret); + drm_err(>->i915->drm, "Failed to create context, %d: %d\n", i, ret); goto err; } @@ -83,7 +83,7 @@ static int intel_guc_scrub_ctbs(void *arg) if (IS_ERR(rq)) { ret = PTR_ERR(rq); - pr_err("Failed to create request, %d: %d\n", i, ret); + drm_err(>->i915->drm, "Failed to create request, %d: %d\n", i, ret); goto err; } @@ -93,7 +93,7 @@ static int intel_guc_scrub_ctbs(void *arg) for (i = 0; i < 3; ++i) { ret = i915_request_wait(last[i], 0, HZ); if (ret < 0) { - pr_err("Last request failed to complete: %d\n", ret); + drm_err(>->i915->drm, "Last request failed to complete: %d\n", ret); goto err; } i915_request_put(last[i]); @@ -110,7 +110,7 @@ static int intel_guc_scrub_ctbs(void *arg) /* GT will not idle if G2H are lost */ ret = intel_gt_wait_for_idle(gt, HZ); if (ret < 0) { - pr_err("GT failed to idle: %d\n", ret); + drm_err(>->i915->drm, "GT failed to idle: %d\n", ret); goto err; } @@ -150,7 +150,7 @@ static int intel_guc_steal_guc_ids(void *arg) ce = kcalloc(GUC_MAX_CONTEXT_ID, sizeof(*ce), GFP_KERNEL); if (!ce) { - pr_err("Context array allocation failed\n"); + drm_err(>->i915->drm, "Context array allocation failed\n"); return -ENOMEM; } @@ -164,24 +164,24 @@ static int intel_guc_steal_guc_ids(void *arg) if (IS_ERR(ce[context_index])) { ret = PTR_ERR(ce[context_index]); ce[context_index] = NULL; - pr_err("Failed to create context: %d\n", ret); + drm_err(>->i915->drm, "Failed to create context: %d\n", ret); goto err_wakeref; } ret = igt_spinner_init(&spin, engine->gt); if (ret) { - pr_err("Failed to create spinner: %d\n", ret); + drm_err(>->i915->drm, "Failed to create spinner: %d\n", ret); goto err_contexts; } spin_rq = igt_spinner_create_request(&spin, ce[context_index], MI_ARB_CHECK); if (IS_ERR(spin_rq)) { ret = PTR_ERR(spin_rq); - pr_err("Failed to create spinner request: %d\n", ret); + drm_err(>->i915->drm, "Failed to create spinner request: %d\n", ret); goto err_contexts; } ret = request_add_spin(spin_rq, &spin); if (ret) { - pr_err("Failed to add Spinner request: %d\n", ret); + drm_err(>->i915->drm, "Failed to add Spinner request: %d\n", ret); goto err_spin_rq; } @@ -191,7 +191,7 @@ static int intel_guc_steal_guc_ids(void *arg) if (IS_ERR(ce[context_index])) { ret = PTR_ERR(ce[context_index--]); ce[context_index] = NULL; - pr_err("Failed to create context: %d\n", ret); + drm_err(>->i915->drm, "Failed to create context: %d\n", ret); goto err_spin_rq; } @@ -200,8 +200,8 @@ static int intel_guc_steal_guc_ids(void *arg) ret = PTR_ERR(rq); rq = NULL; if (ret != -EAGAIN) { - pr_err("Failed to create request, %d: %d\n", - context_index, ret); + drm_err(>->i915->drm, "Failed to create request, %d: %d\n", + context_index, ret); goto err_spin_rq; } } else { @@ -215,7 +215,7 @@ static int intel_guc_steal_guc_ids(void *arg) igt_spinner_end(&spin); ret = intel_selftest_wait_for_rq(spin_rq); if (ret) { - pr_err("Spin request failed to complete: %d\n", ret); + drm_err(>->i915->drm, "Spin request failed to complete: %d\n", ret); i915_request_put(last); goto err_spin_rq; } @@ -227,7 +227,7 @@ static int intel_guc_steal_guc_ids(void *arg) ret = i915_request_wait(last, 0, HZ * 30); i915_request_put(last); if (ret < 0) { - pr_err("Last request failed to complete: %d\n", ret); + drm_err(>->i915->drm, "Last request failed to complete: %d\n", ret); goto err_spin_rq; } @@ -235,7 +235,7 @@ static int intel_guc_steal_guc_ids(void *arg) rq = nop_user_request(ce[context_index], NULL); if (IS_ERR(rq)) { ret = PTR_ERR(rq); - pr_err("Failed to steal guc_id, %d: %d\n", context_index, ret); + drm_err(>->i915->drm, "Failed to steal guc_id, %d: %d\n", context_index, ret); goto err_spin_rq; } @@ -243,21 +243,20 @@ static int intel_guc_steal_guc_ids(void *arg) ret = i915_request_wait(rq, 0, HZ); i915_request_put(rq); if (ret < 0) { - pr_err("Request with stolen guc_id failed to complete: %d\n", - ret); + drm_err(>->i915->drm, "Request with stolen guc_id failed to complete: %d\n", ret); goto err_spin_rq; } /* Wait for idle */ ret = intel_gt_wait_for_idle(gt, HZ * 30); if (ret < 0) { - pr_err("GT failed to idle: %d\n", ret); + drm_err(>->i915->drm, "GT failed to idle: %d\n", ret); goto err_spin_rq; } /* Verify a guc_id was stolen */ if (guc->number_guc_id_stolen == number_guc_id_stolen) { - pr_err("No guc_id was stolen"); + drm_err(>->i915->drm, "No guc_id was stolen"); ret = -EINVAL; } else { ret = 0; diff --git a/drivers/gpu/drm/i915/gt/uc/selftest_guc_multi_lrc.c b/drivers/gpu/drm/i915/gt/uc/selftest_guc_multi_lrc.c index 812220a43df8..d17982c36d25 100644 --- a/drivers/gpu/drm/i915/gt/uc/selftest_guc_multi_lrc.c +++ b/drivers/gpu/drm/i915/gt/uc/selftest_guc_multi_lrc.c @@ -115,30 +115,30 @@ static int __intel_guc_multi_lrc_basic(struct intel_gt *gt, unsigned int class) parent = multi_lrc_create_parent(gt, class, 0); if (IS_ERR(parent)) { - pr_err("Failed creating contexts: %ld", PTR_ERR(parent)); + drm_err(>->i915->drm, "Failed creating contexts: %ld", PTR_ERR(parent)); return PTR_ERR(parent); } else if (!parent) { - pr_debug("Not enough engines in class: %d", class); + drm_dbg(>->i915->drm, "Not enough engines in class: %d", class); return 0; } rq = multi_lrc_nop_request(parent); if (IS_ERR(rq)) { ret = PTR_ERR(rq); - pr_err("Failed creating requests: %d", ret); + drm_err(>->i915->drm, "Failed creating requests: %d", ret); goto out; } ret = intel_selftest_wait_for_rq(rq); if (ret) - pr_err("Failed waiting on request: %d", ret); + drm_err(>->i915->drm, "Failed waiting on request: %d", ret); i915_request_put(rq); if (ret >= 0) { ret = intel_gt_wait_for_idle(gt, HZ * 5); if (ret < 0) - pr_err("GT failed to idle: %d\n", ret); + drm_err(>->i915->drm, "GT failed to idle: %d\n", ret); } out: -- cgit v1.2.3-59-g8ed1b From 2be1959ecefd9f655e6262144a1b970bc2f34054 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 17 Jul 2022 02:44:39 +0800 Subject: drm/i915/gt: Remove unneeded semicolon The semicolon after the `}' is unneeded. Signed-off-by: Jason Wang Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi [Removed line mention when pushing] Link: https://patchwork.freedesktop.org/patch/msgid/20220716184439.72056-1-wangborong@cdjrlc.com --- drivers/gpu/drm/i915/gt/intel_migrate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/gt/intel_migrate.c b/drivers/gpu/drm/i915/gt/intel_migrate.c index 2c35324b5f68..a69b244f14d0 100644 --- a/drivers/gpu/drm/i915/gt/intel_migrate.c +++ b/drivers/gpu/drm/i915/gt/intel_migrate.c @@ -645,7 +645,7 @@ static int scatter_list_length(struct scatterlist *sg) while (sg && sg_dma_len(sg)) { len += sg_dma_len(sg); sg = sg_next(sg); - }; + } return len; } -- cgit v1.2.3-59-g8ed1b From b25c377a5b26f107ea831af05919fc3782aabebc Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sat, 16 Jul 2022 12:05:20 +0800 Subject: drm/i915/selftests: Fix comment typo Fix the double `wait' typo in comment. Signed-off-by: Jason Wang Reviewed-by: Andrzej Hajda Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20220716040520.31676-1-wangborong@cdjrlc.com --- drivers/gpu/drm/i915/selftests/i915_request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/i915/selftests/i915_request.c b/drivers/gpu/drm/i915/selftests/i915_request.c index c56a0c2cd2f7..ec05f578a698 100644 --- a/drivers/gpu/drm/i915/selftests/i915_request.c +++ b/drivers/gpu/drm/i915/selftests/i915_request.c @@ -971,7 +971,7 @@ static struct i915_vma *empty_batch(struct drm_i915_private *i915) if (err) goto err; - /* Force the wait wait now to avoid including it in the benchmark */ + /* Force the wait now to avoid including it in the benchmark */ err = i915_vma_sync(vma); if (err) goto err_pin; -- cgit v1.2.3-59-g8ed1b From 2b8428a10fd3763819eabf1acef6b5b2c7875520 Mon Sep 17 00:00:00 2001 From: Liu Zixian Date: Mon, 18 Jul 2022 09:53:57 +0800 Subject: drm: correct comments On failure, these functions return error pointer, not NULL. Signed-off-by: Liu Zixian Signed-off-by: Maxime Ripard Link: https://patchwork.freedesktop.org/patch/msgid/20220718015357.1722-1-liuzixian4@huawei.com --- drivers/gpu/drm/drm_gem_shmem_helper.c | 2 +- include/drm/drm_gem_shmem_helper.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index 8ad0e02991ca..05ca028dd152 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -662,7 +662,7 @@ EXPORT_SYMBOL(drm_gem_shmem_print_info); * drm_gem_shmem_get_pages_sgt() instead. * * Returns: - * A pointer to the scatter/gather table of pinned pages or NULL on failure. + * A pointer to the scatter/gather table of pinned pages or error pointer on failure. */ struct sg_table *drm_gem_shmem_get_sg_table(struct drm_gem_shmem_object *shmem) { diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index d0a57853c188..a2201b2488c5 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -210,7 +210,7 @@ static inline void drm_gem_shmem_object_unpin(struct drm_gem_object *obj) * use it as their &drm_gem_object_funcs.get_sg_table handler. * * Returns: - * A pointer to the scatter/gather table of pinned pages or NULL on failure. + * A pointer to the scatter/gather table of pinned pages or error pointer on failure. */ static inline struct sg_table *drm_gem_shmem_object_get_sg_table(struct drm_gem_object *obj) { -- cgit v1.2.3-59-g8ed1b From fc94224c2e0ae8d83ac511a3ef4962178505469d Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 21 Jul 2022 17:22:58 +0800 Subject: drm/bridge: parade-ps8640: Fix regulator supply order The datasheet says that VDD12 must be enabled and at full voltage before VDD33 is enabled. Reorder the bulk regulator supply names so that VDD12 is enabled before VDD33. Any enable ramp delays should be handled by setting proper constraints on the regulators. Fixes: bc1aee7fc8f0 ("drm/bridge: Add I2C based driver for ps8640 bridge") Signed-off-by: Chen-Yu Tsai Reviewed-by: Neil Armstrong Signed-off-by: Robert Foss Link: https://patchwork.freedesktop.org/patch/msgid/20220721092258.3397461-1-wenst@chromium.org --- drivers/gpu/drm/bridge/parade-ps8640.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 31e88cb39f8a..49107a6cdac1 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -631,8 +631,8 @@ static int ps8640_probe(struct i2c_client *client) if (!ps_bridge) return -ENOMEM; - ps_bridge->supplies[0].supply = "vdd33"; - ps_bridge->supplies[1].supply = "vdd12"; + ps_bridge->supplies[0].supply = "vdd12"; + ps_bridge->supplies[1].supply = "vdd33"; ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies), ps_bridge->supplies); if (ret) -- cgit v1.2.3-59-g8ed1b From b62cc8fa824807c00b77a76e78c7417b6bfdf418 Mon Sep 17 00:00:00 2001 From: pengfuyuan Date: Fri, 27 May 2022 11:39:03 +0800 Subject: drm/arm: Fix spelling typo in comments Fix spelling typo in comments. Reported-by: k2ci Signed-off-by: pengfuyuan Acked-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220426121639.39160-1-pengfuyuan@kylinos.cn --- drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c | 2 +- drivers/gpu/drm/arm/malidp_regs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c index e672b9cffee3..3276a3e82c62 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c @@ -1271,7 +1271,7 @@ int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe, return 0; } -/* Since standalong disabled components must be disabled separately and in the +/* Since standalone disabled components must be disabled separately and in the * last, So a complete disable operation may needs to call pipeline_disable * twice (two phase disabling). * Phase 1: disable the common components, flush it. diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h index 514c50dcb74d..3bc16db70ddb 100644 --- a/drivers/gpu/drm/arm/malidp_regs.h +++ b/drivers/gpu/drm/arm/malidp_regs.h @@ -145,7 +145,7 @@ #define MALIDP_SE_COEFFTAB_DATA_MASK 0x3fff #define MALIDP_SE_SET_COEFFTAB_DATA(x) \ ((x) & MALIDP_SE_COEFFTAB_DATA_MASK) -/* Enhance coeffents reigster offset */ +/* Enhance coefficients register offset */ #define MALIDP_SE_IMAGE_ENH 0x3C /* ENH_LIMITS offset 0x0 */ #define MALIDP_SE_ENH_LOW_LEVEL 24 -- cgit v1.2.3-59-g8ed1b From 4b760f76dd6f57cb0d608ba0c4009b7eeeddd864 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 15 Jun 2022 17:09:15 +0100 Subject: drm/arm/hdlcd: Take over EFI framebuffer properly The Arm Juno board EDK2 port has provided an EFI GOP display via HDLCD0 for some time now, which works nicely as an early framebuffer. However, once the HDLCD driver probes and takes over the hardware, it should take over the logical framebuffer as well, otherwise the now-defunct GOP device hangs about and virtual console output inevitably disappears into the wrong place most of the time. We'll do this after binding the HDMI encoder, since that's the most likely thing to fail, and the EFI console is still better than nothing when that happens. However, the two HDLCD controllers on Juno are independent, and many users will still be using older firmware without any display support, so we'll only bother if we find that the HDLCD we're probing is already enabled. And if it is, then we'll also stop it, since otherwise the display can end up shifted if it's still scanning out while the rest of the registers are subsequently reconfigured. Signed-off-by: Robin Murphy Reviewed-by: Javier Martinez Canillas Signed-off-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/31acd57f4aa8a4d02877026fa3a8c8d035e15a0d.1655309004.git.robin.murphy@arm.com --- drivers/gpu/drm/arm/hdlcd_drv.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index e89ae0ec60eb..60a27886248c 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -314,6 +315,15 @@ static int hdlcd_drm_bind(struct device *dev) goto err_vblank; } + /* + * If EFI left us running, take over from simple framebuffer + * drivers. Read HDLCD_REG_COMMAND to see if we are enabled. + */ + if (hdlcd_read(hdlcd, HDLCD_REG_COMMAND)) { + hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0); + drm_aperture_remove_framebuffers(false, &hdlcd_driver); + } + drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); -- cgit v1.2.3-59-g8ed1b From f818eac130d236d41c2f18fd475ebd709e5a56e7 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 15 Jun 2022 17:11:09 +0100 Subject: drm/arm/hdlcd: Simplify IRQ install/uninstall Since we no longer need to conform to the structure of the various DRM IRQ callbacks, we can streamline the code by consolidating the piecemeal functions and passing around our private data structure directly. We're also a platform device so should never see IRQ_NOTCONNECTED either. Furthermore we can also get rid of all the unnecesary read-modify-write operations, since on install we know we cleared the whole interrupt mask before enabling the debug IRQs, and thus on uninstall we're always clearing everything as well. Signed-off-by: Robin Murphy Acked-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/65cf7818b23c1a8629dc851f1d058ecb8a14849e.1655309413.git.robin.murphy@arm.com --- drivers/gpu/drm/arm/hdlcd_drv.c | 60 +++++++++++------------------------------ 1 file changed, 15 insertions(+), 45 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 60a27886248c..350ca4e4eaa6 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -41,8 +41,7 @@ static irqreturn_t hdlcd_irq(int irq, void *arg) { - struct drm_device *drm = arg; - struct hdlcd_drm_private *hdlcd = drm->dev_private; + struct hdlcd_drm_private *hdlcd = arg; unsigned long irq_status; irq_status = hdlcd_read(hdlcd, HDLCD_REG_INT_STATUS); @@ -70,61 +69,32 @@ static irqreturn_t hdlcd_irq(int irq, void *arg) return IRQ_HANDLED; } -static void hdlcd_irq_preinstall(struct drm_device *drm) +static int hdlcd_irq_install(struct hdlcd_drm_private *hdlcd) { - struct hdlcd_drm_private *hdlcd = drm->dev_private; + int ret; + /* Ensure interrupts are disabled */ hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, 0); hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, ~0); -} - -static void hdlcd_irq_postinstall(struct drm_device *drm) -{ -#ifdef CONFIG_DEBUG_FS - struct hdlcd_drm_private *hdlcd = drm->dev_private; - unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK); - - /* enable debug interrupts */ - irq_mask |= HDLCD_DEBUG_INT_MASK; - - hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask); -#endif -} -static int hdlcd_irq_install(struct drm_device *drm, int irq) -{ - int ret; - - if (irq == IRQ_NOTCONNECTED) - return -ENOTCONN; - - hdlcd_irq_preinstall(drm); - - ret = request_irq(irq, hdlcd_irq, 0, drm->driver->name, drm); + ret = request_irq(hdlcd->irq, hdlcd_irq, 0, "hdlcd", hdlcd); if (ret) return ret; - hdlcd_irq_postinstall(drm); +#ifdef CONFIG_DEBUG_FS + /* enable debug interrupts */ + hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, HDLCD_DEBUG_INT_MASK); +#endif return 0; } -static void hdlcd_irq_uninstall(struct drm_device *drm) +static void hdlcd_irq_uninstall(struct hdlcd_drm_private *hdlcd) { - struct hdlcd_drm_private *hdlcd = drm->dev_private; /* disable all the interrupts that we might have enabled */ - unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK); - -#ifdef CONFIG_DEBUG_FS - /* disable debug interrupts */ - irq_mask &= ~HDLCD_DEBUG_INT_MASK; -#endif - - /* disable vsync interrupts */ - irq_mask &= ~HDLCD_INTERRUPT_VSYNC; - hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask); + hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, 0); - free_irq(hdlcd->irq, drm); + free_irq(hdlcd->irq, hdlcd); } static int hdlcd_load(struct drm_device *drm, unsigned long flags) @@ -184,7 +154,7 @@ static int hdlcd_load(struct drm_device *drm, unsigned long flags) goto irq_fail; hdlcd->irq = ret; - ret = hdlcd_irq_install(drm, hdlcd->irq); + ret = hdlcd_irq_install(hdlcd); if (ret < 0) { DRM_ERROR("failed to install IRQ handler\n"); goto irq_fail; @@ -345,7 +315,7 @@ err_pm_active: err_unload: of_node_put(hdlcd->crtc.port); hdlcd->crtc.port = NULL; - hdlcd_irq_uninstall(drm); + hdlcd_irq_uninstall(hdlcd); of_reserved_mem_device_release(drm->dev); err_free: drm_mode_config_cleanup(drm); @@ -367,7 +337,7 @@ static void hdlcd_drm_unbind(struct device *dev) hdlcd->crtc.port = NULL; pm_runtime_get_sync(dev); drm_atomic_helper_shutdown(drm); - hdlcd_irq_uninstall(drm); + hdlcd_irq_uninstall(hdlcd); pm_runtime_put(dev); if (pm_runtime_enabled(dev)) pm_runtime_disable(dev); -- cgit v1.2.3-59-g8ed1b From 000a2f0498f29b0831496bb1fdab3354b00704a2 Mon Sep 17 00:00:00 2001 From: Carsten Haitzler Date: Mon, 18 Jul 2022 17:00:39 +0100 Subject: drm/komeda: Add legacy FB support so VT's work as expected The komeda driver doesn't come up with a visible text (FB) mode VT by default as it was missing legacy FB support. It's useful to have a working text VT on a system for debug and general usability, so enable it. You can always toggle CONFIG_FRAMEBUFFER_CONSOLE. Signed-off-by: Carsten Haitzler Reviewed-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220606114714.175499-1-carsten.haitzler@foss.arm.com --- drivers/gpu/drm/arm/display/komeda/komeda_drv.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c index ba16895690f1..9fce4239d4ad 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include "komeda_dev.h" @@ -72,6 +73,7 @@ static int komeda_bind(struct device *dev) } dev_set_drvdata(dev, mdrv); + drm_fbdev_generic_setup(&mdrv->kms->base, 32); return 0; -- cgit v1.2.3-59-g8ed1b From a8ff2cd916e62b740718a908fbaa1e9ec8a147f1 Mon Sep 17 00:00:00 2001 From: Carsten Haitzler Date: Mon, 6 Jun 2022 12:47:13 +0100 Subject: drm/komeda - At init write GCU control block to handle already on DPU If something has already set up the DPU before the komeda driver comes up, it will fail to init because it was just writing to the SRST bit in the GCU control register and ignoring others. This resulted in TBU bringup stalling and init failing. By writing completely we also set the mode back to 0 (inactive) too and thus TBU bringup works. Signed-off-by: Carsten Haitzler Acked-by: Liviu Dudau Signed-off-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220606114714.175499-2-carsten.haitzler@foss.arm.com --- drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c index daa1faccd3e7..6c56f5662bc7 100644 --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c @@ -310,8 +310,7 @@ static int d71_reset(struct d71_dev *d71) u32 __iomem *gcu = d71->gcu_addr; int ret; - malidp_write32_mask(gcu, BLK_CONTROL, - GCU_CONTROL_SRST, GCU_CONTROL_SRST); + malidp_write32(gcu, BLK_CONTROL, GCU_CONTROL_SRST); ret = dp_wait_cond(!(malidp_read32(gcu, BLK_CONTROL) & GCU_CONTROL_SRST), 100, 1000, 10000); -- cgit v1.2.3-59-g8ed1b From eaa225b6b52233d45457fd33730e1528c604d92d Mon Sep 17 00:00:00 2001 From: Liviu Dudau Date: Fri, 8 Jul 2022 16:39:21 +0100 Subject: drm/komeda: Fix handling of atomic commits in the atomic_commit_tail hook Komeda driver relies on the generic DRM atomic helper functions to handle commits. It only implements an atomic_commit_tail hook for the mode_config_helper_funcs and even that one is pretty close to the generic implementation with the exception of additional dma_fence signalling. What the generic helper framework doesn't do is waiting for the actual hardware to signal that the commit parameters have been written into the appropriate registers. As we signal CRTC events only on the irq handlers, we need to flush the configuration and wait for the hardware to respond. Add the Komeda specific implementation for atomic_commit_hw_done() that flushes and waits for flip done before calling drm_atomic_helper_commit_hw_done(). The fix was prompted by a patch from Carsten Haitzler where he was trying to solve the same issue but in a different way that I think can lead to wrong event signaling to userspace. Reported-by: Carsten Haitzler Tested-by: Carsten Haitzler Reviewed-by: Carsten Haitzler Signed-off-by: Liviu Dudau Link: https://patchwork.freedesktop.org/patch/msgid/20220722122139.288486-1-liviu.dudau@arm.com --- drivers/gpu/drm/arm/display/komeda/komeda_crtc.c | 4 ++-- drivers/gpu/drm/arm/display/komeda/komeda_kms.c | 21 ++++++++++++++++++++- drivers/gpu/drm/arm/display/komeda/komeda_kms.h | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index 59172acb9738..292f533d8cf0 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -235,7 +235,7 @@ void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, crtc->state->event = NULL; drm_crtc_send_vblank_event(crtc, event); } else { - DRM_WARN("CRTC[%d]: FLIP happen but no pending commit.\n", + DRM_WARN("CRTC[%d]: FLIP happened but no pending commit.\n", drm_crtc_index(&kcrtc->base)); } spin_unlock_irqrestore(&crtc->dev->event_lock, flags); @@ -286,7 +286,7 @@ komeda_crtc_atomic_enable(struct drm_crtc *crtc, komeda_crtc_do_flush(crtc, old); } -static void +void komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc, struct completion *input_flip_done) { diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 93b7f09b96ca..327051bba5b6 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -69,6 +69,25 @@ static const struct drm_driver komeda_kms_driver = { .minor = 1, }; +static void komeda_kms_atomic_commit_hw_done(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + struct komeda_kms_dev *kms = to_kdev(dev); + int i; + + for (i = 0; i < kms->n_crtcs; i++) { + struct komeda_crtc *kcrtc = &kms->crtcs[i]; + + if (kcrtc->base.state->active) { + struct completion *flip_done = NULL; + if (kcrtc->base.state->event) + flip_done = kcrtc->base.state->event->base.completion; + komeda_crtc_flush_and_wait_for_flip_done(kcrtc, flip_done); + } + } + drm_atomic_helper_commit_hw_done(state); +} + static void komeda_kms_commit_tail(struct drm_atomic_state *old_state) { struct drm_device *dev = old_state->dev; @@ -81,7 +100,7 @@ static void komeda_kms_commit_tail(struct drm_atomic_state *old_state) drm_atomic_helper_commit_modeset_enables(dev, old_state); - drm_atomic_helper_commit_hw_done(old_state); + komeda_kms_atomic_commit_hw_done(old_state); drm_atomic_helper_wait_for_flip_done(dev, old_state); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h index 7889e380ab23..7339339ef6b8 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h @@ -183,6 +183,8 @@ void komeda_kms_cleanup_private_objs(struct komeda_kms_dev *kms); void komeda_crtc_handle_event(struct komeda_crtc *kcrtc, struct komeda_events *evts); +void komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc, + struct completion *input_flip_done); struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev); void komeda_kms_detach(struct komeda_kms_dev *kms); -- cgit v1.2.3-59-g8ed1b From bbb6f93f51a19787f455768015e9a77a4d83c4c8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 22 Jul 2022 09:47:55 +0200 Subject: drm/bridge: ti-sn65dsi86: Use dev_err_probe() to avoid polluting the log If devm_drm_of_get_bridge() can't find the connected bridge, it returns an ERR_PTR(-EPROBE_DEFER) to indicate that the probe should be deferred. But this path also prints an error message, which pollutes the kernel log since is printed on every probe deferral, i.e: $ dmesg | grep "failed to create panel bridge" | wc -l 38 Signed-off-by: Javier Martinez Canillas Reviewed-by: Neil Armstrong Reviewed-by: Andrzej Hajda Reviewed-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20220722074755.660258-1-javierm@redhat.com --- drivers/gpu/drm/bridge/ti-sn65dsi86.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 369bf72717f6..90bbabde1595 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1206,10 +1206,9 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev, int ret; pdata->next_bridge = devm_drm_of_get_bridge(pdata->dev, np, 1, 0); - if (IS_ERR(pdata->next_bridge)) { - DRM_ERROR("failed to create panel bridge\n"); - return PTR_ERR(pdata->next_bridge); - } + if (IS_ERR(pdata->next_bridge)) + return dev_err_probe(pdata->dev, PTR_ERR(pdata->next_bridge), + "failed to create panel bridge\n"); ti_sn_bridge_parse_lanes(pdata, np); -- cgit v1.2.3-59-g8ed1b From 97216fefed6d9782c7208cca33a5465212f533f3 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 20 Jul 2022 10:30:52 +0200 Subject: drm/vmwgfx: Remove trailing whitespace Fix coding style. No functional changes. Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Reviewed-by: Zack Rusin Link: https://patchwork.freedesktop.org/patch/msgid/20220720083058.15371-2-tzimmermann@suse.de --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index ff2f735bbe7a..c720a277489b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2257,7 +2257,7 @@ out_fini: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); mutex_unlock(&dev->mode_config.mutex); - + return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index c89ad3a2d141..b29e650d77f4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -1383,6 +1383,6 @@ out_revert: vmw_validation_revert(&val_ctx); out_unref: vmw_validation_unref_lists(&val_ctx); - + return ret; } -- cgit v1.2.3-59-g8ed1b From 382fc1f681324bb38bedfe763107a60256c4ddc8 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 20 Jul 2022 10:30:53 +0200 Subject: drm/atomic-helper: Move DRM_PLANE_HELPER_NO_SCALING to atomic helpers The macro DRM_PLANE_HELPER_NO_SCALING is only useful with the interfaces in drm_atomic_helper.h, but defined in drm_plane_helper.h. So half of DRM includes the latter header file for using this macro. Move the macro and remove the include statements. Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20220720083058.15371-3-tzimmermann@suse.de --- drivers/gpu/drm/arm/hdlcd_crtc.c | 1 - drivers/gpu/drm/ast/ast_mode.c | 1 - drivers/gpu/drm/drm_simple_kms_helper.c | 1 - drivers/gpu/drm/i915/display/i9xx_plane.c | 1 - drivers/gpu/drm/i915/display/intel_cursor.c | 1 - drivers/gpu/drm/i915/display/intel_sprite.c | 1 - drivers/gpu/drm/i915/display/skl_universal_plane.c | 1 - drivers/gpu/drm/imx/ipuv3-plane.c | 1 - drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 1 - drivers/gpu/drm/kmb/kmb_plane.c | 1 - drivers/gpu/drm/logicvc/logicvc_layer.c | 1 - drivers/gpu/drm/mediatek/mtk_drm_plane.c | 1 - drivers/gpu/drm/meson/meson_plane.c | 1 - drivers/gpu/drm/mxsfb/lcdif_kms.c | 1 - drivers/gpu/drm/mxsfb/mxsfb_kms.c | 1 - drivers/gpu/drm/nouveau/dispnv50/base507c.c | 1 - drivers/gpu/drm/nouveau/dispnv50/curs507a.c | 1 - drivers/gpu/drm/nouveau/dispnv50/ovly507e.c | 1 - drivers/gpu/drm/nouveau/dispnv50/wndwc37e.c | 1 - drivers/gpu/drm/rcar-du/rcar_du_plane.c | 1 - drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 1 - drivers/gpu/drm/sprd/sprd_dpu.c | 1 - drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 1 - drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 1 - drivers/gpu/drm/tests/drm_plane_helper_test.c | 1 - drivers/gpu/drm/virtio/virtgpu_plane.c | 1 - drivers/gpu/drm/vkms/vkms_plane.c | 1 - drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 1 - drivers/gpu/drm/xlnx/zynqmp_disp.c | 1 - include/drm/drm_atomic_helper.h | 9 +++++++++ include/drm/drm_plane_helper.h | 9 --------- 31 files changed, 9 insertions(+), 38 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/arm/hdlcd_crtc.c b/drivers/gpu/drm/arm/hdlcd_crtc.c index afc9cd856501..3313d75a8860 100644 --- a/drivers/gpu/drm/arm/hdlcd_crtc.c +++ b/drivers/gpu/drm/arm/hdlcd_crtc.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 214b10178454..7fd2e20f743d 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 36633590ebf3..64800f158e69 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c index 592e5adfed8b..6d22e6edef2a 100644 --- a/drivers/gpu/drm/i915/display/i9xx_plane.c +++ b/drivers/gpu/drm/i915/display/i9xx_plane.c @@ -7,7 +7,6 @@ #include #include #include -#include #include "intel_atomic.h" #include "intel_atomic_plane.h" diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index c2797ad2d313..e49927965a6b 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include "intel_atomic.h" diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c index 2713faad0625..a10c29226289 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite.c +++ b/drivers/gpu/drm/i915/display/intel_sprite.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include "i915_drv.h" diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index c11e15a93164..87c202ec7d1e 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -7,7 +7,6 @@ #include #include #include -#include #include "i915_drv.h" #include "intel_atomic_plane.h" diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index ea5f594955df..26a0e2e2a366 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -14,7 +14,6 @@ #include #include #include -#include #include