diff options
| author | Mark Brown <broonie@kernel.org> | 2026-10-01 15:00:20 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-10-01 15:00:20 +0100 |
| commit | 66fdeba7225471245f4b89b5ebd9954d7350e1be (patch) | |
| tree | 100f0e75ada27a179d5728c5ac9ba81ab14de52e /drivers/gpu | |
| parent | d7ad64ac81f59eb769ee20eacbd8c3f514e44eac (diff) | |
| parent | 7c4bda20eb0f150315d19f1b51058820e75d3f3d (diff) | |
| download | linux-next-66fdeba7225471245f4b89b5ebd9954d7350e1be.tar.gz linux-next-66fdeba7225471245f4b89b5ebd9954d7350e1be.zip | |
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/misc/kernel.git
Diffstat (limited to 'drivers/gpu')
109 files changed, 2129 insertions, 1328 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 65d46dfa7266..61111d50b1c6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -46,7 +46,6 @@ config DRM_KMS_HELPER tristate depends on DRM select DRM_PANIC_HELPER if DRM_PANIC - select FB_CORE if DRM_FBDEV_EMULATION help CRTC helpers for KMS drivers. @@ -256,7 +255,7 @@ config DRM_TTM_HELPER tristate depends on DRM select DRM_TTM - select DRM_KMS_HELPER if DRM_FBDEV_EMULATION + select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION select FB_CORE if DRM_FBDEV_EMULATION select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION help @@ -265,7 +264,7 @@ config DRM_TTM_HELPER config DRM_GEM_DMA_HELPER tristate depends on DRM - select DRM_KMS_HELPER if DRM_FBDEV_EMULATION + select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION select FB_CORE if DRM_FBDEV_EMULATION select FB_DMAMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION help @@ -274,7 +273,7 @@ config DRM_GEM_DMA_HELPER config DRM_GEM_SHMEM_HELPER tristate depends on DRM && MMU - select DRM_KMS_HELPER if DRM_FBDEV_EMULATION + select DRM_CLIENT_LIB if DRM_FBDEV_EMULATION select FB_CORE if DRM_FBDEV_EMULATION select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION help diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f0c340aa2ec6..2c6bc093a923 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -68,6 +68,7 @@ drm-y := \ drm_rect.o \ drm_syncobj.o \ drm_sysfs.o \ + drm_timeout.o \ drm_trace_points.o \ drm_vblank.o \ drm_vblank_work.o \ @@ -79,7 +80,6 @@ drm-$(CONFIG_DRM_CLIENT) += \ drm_client_modeset.o \ drm_client_sysrq.o drm-$(CONFIG_COMPAT) += drm_ioc32.o -drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o drm-$(CONFIG_PCI) += drm_pci.o drm-$(CONFIG_DEBUG_FS) += \ @@ -95,6 +95,7 @@ drm-$(CONFIG_DRM_DRAW) += drm_draw.o drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o obj-$(CONFIG_DRM) += drm.o +obj-$(CONFIG_DRM_PANEL) += drm_panel.o obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o obj-$(CONFIG_DRM_PANEL_BACKLIGHT_QUIRKS) += drm_panel_backlight_quirks.o @@ -155,10 +156,8 @@ drm_kms_helper-y := \ drm_self_refresh_helper.o \ drm_simple_kms_helper.o \ drm_vblank_helper.o -drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o drm_kms_helper-$(CONFIG_DRM_PANIC_HELPER) += drm_panic_helper.o drm_kms_helper-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_helper_qr.o -drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o # diff --git a/drivers/gpu/drm/adp/adp-mipi.c b/drivers/gpu/drm/adp/adp-mipi.c index 232bb9539423..956533870781 100644 --- a/drivers/gpu/drm/adp/adp-mipi.c +++ b/drivers/gpu/drm/adp/adp-mipi.c @@ -8,6 +8,7 @@ #include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> #define DSI_GEN_HDR 0x6c #define DSI_GEN_PLD_DATA 0x70 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 59ea338b554c..126bfd9bb89e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -38,11 +38,11 @@ #include <linux/pci.h> #include <linux/pm_runtime.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_modeset_helper.h> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 56eaf05899a8..7fa321c41f63 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -25,7 +25,6 @@ * Alex Deucher * Jerome Glisse */ -#include <linux/ktime.h> #include <linux/module.h> #include <linux/overflow.h> #include <linux/pagemap.h> @@ -40,6 +39,7 @@ #include <drm/drm_gem_ttm_helper.h> #include <drm/ttm/ttm_tt.h> #include <drm/drm_syncobj.h> +#include <drm/drm_timeout.h> #include "amdgpu.h" #include "amdgpu_display.h" @@ -652,23 +652,11 @@ int amdgpu_gem_mmap_ioctl(struct drm_device *dev, void *data, */ unsigned long amdgpu_gem_timeout(uint64_t timeout_ns) { - unsigned long timeout_jiffies; - ktime_t timeout; - - /* clamp timeout if it's to large */ - if (((int64_t)timeout_ns) < 0) + /* Map anything that doesn't fit in a s64 to an infinite wait */ + if (timeout_ns > S64_MAX) return MAX_SCHEDULE_TIMEOUT; - timeout = ktime_sub(ns_to_ktime(timeout_ns), ktime_get()); - if (ktime_to_ns(timeout) < 0) - return 0; - - timeout_jiffies = nsecs_to_jiffies(ktime_to_ns(timeout)); - /* clamp timeout to avoid unsigned-> signed overflow */ - if (timeout_jiffies > MAX_SCHEDULE_TIMEOUT) - return MAX_SCHEDULE_TIMEOUT - 1; - - return timeout_jiffies; + return drm_timeout_abs_to_jiffies(timeout_ns); } int amdgpu_gem_wait_idle_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index f2f179421c4d..51a7a4091376 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -28,8 +28,8 @@ #include "amdgpu.h" #include <drm/amdgpu_drm.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include "amdgpu_uvd.h" #include "amdgpu_vce.h" #include "atom.h" diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 2b0686bcc7fd..cd5cbb851075 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -98,7 +98,6 @@ #include <drm/drm_edid.h> #include <drm/drm_eld.h> #include <drm/drm_mode.h> -#include <drm/drm_utils.h> #include <drm/drm_vblank.h> #include <drm/drm_colorop.h> #include <drm/drm_gem_atomic_helper.h> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c index 45e1732a5b04..8dc57903de9e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c @@ -41,7 +41,7 @@ #include <linux/backlight.h> #include <linux/power_supply.h> #include <drm/drm_edid.h> -#include <drm/drm_utils.h> +#include <drm/drm_panel_quirks.h> #include <acpi/video.h> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c index 2a63a1c9b79d..25f168a8ea31 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -62,7 +62,6 @@ #include <drm/drm_fixed.h> #include <drm/drm_mode.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_utils.h> #include <drm/display/drm_dp_mst_helper.h> #include <drm/display/drm_hdmi_helper.h> #include <drm/drm_privacy_screen_consumer.h> diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c index 3d81b413efae..0fd069fc3386 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_crtc.c @@ -15,6 +15,7 @@ #include <drm/drm_print.h> #include <drm/drm_vblank.h> #include <drm/drm_bridge.h> +#include <drm/drm_panel.h> #include "komeda_dev.h" #include "komeda_kms.h" diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index 8bbae94804f8..f35afec2125f 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -9,9 +9,9 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 837db36cdf48..c7da6b02d7b6 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -87,7 +87,6 @@ config DRM_FSL_LDB depends on OF depends on ARCH_MXC || COMPILE_TEST select DRM_KMS_HELPER - select DRM_PANEL_BRIDGE help Support for i.MX8MP DPI-to-LVDS on-SoC encoder. @@ -151,6 +150,7 @@ config DRM_LONTIUM_LT8912B config DRM_LONTIUM_LT9211 tristate "Lontium LT9211 DSI/LVDS/DPI bridge" depends on OF + select DRM_PANEL select DRM_PANEL_BRIDGE select DRM_KMS_HELPER select DRM_MIPI_DSI @@ -259,7 +259,7 @@ config DRM_NXP_PTN3460 tristate "NXP PTN3460 DP/LVDS bridge" depends on OF select DRM_KMS_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE help NXP PTN3460 eDP-LVDS bridge chip driver. @@ -276,7 +276,7 @@ config DRM_OF_DISPLAY_MODE_BRIDGE config DRM_PARADE_PS8622 tristate "Parade eDP/LVDS bridge" depends on OF - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_KMS_HELPER select BACKLIGHT_CLASS_DEVICE help @@ -302,7 +302,6 @@ config DRM_SAMSUNG_DSIM depends on OF && HAS_IOMEM select DRM_KMS_HELPER select DRM_MIPI_DSI - select DRM_PANEL_BRIDGE select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY help @@ -350,7 +349,6 @@ config DRM_SOLOMON_SSD2825 depends on SPI_MASTER && OF select DRM_MIPI_DSI select DRM_KMS_HELPER - select DRM_PANEL help Say Y here if you want support for the Solomon SSD2825 RGB/DSI SPI bridge driver. @@ -388,7 +386,7 @@ config DRM_TOSHIBA_TC358764 depends on OF select DRM_MIPI_DSI select DRM_KMS_HELPER - select DRM_PANEL + select DRM_PANEL_BRIDGE help Toshiba TC358764 DSI/LVDS bridge driver. @@ -400,7 +398,6 @@ config DRM_TOSHIBA_TC358767 select DRM_KMS_HELPER select REGMAP_I2C select DRM_MIPI_DSI - select DRM_PANEL help Toshiba TC358767 eDP bridge chip driver. @@ -409,7 +406,6 @@ config DRM_TOSHIBA_TC358768 depends on OF select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL select DRM_MIPI_DSI select VIDEOMODE_HELPERS help @@ -422,16 +418,17 @@ config DRM_TOSHIBA_TC358775 select DRM_DISPLAY_HELPER select DRM_KMS_HELPER select REGMAP_I2C - select DRM_PANEL + select DRM_PANEL_BRIDGE select DRM_MIPI_DSI help Toshiba TC358775 DSI/LVDS bridge chip driver. config DRM_TI_DLPC3433 tristate "TI DLPC3433 Display controller" - depends on DRM && DRM_PANEL + depends on DRM depends on OF select DRM_MIPI_DSI + select DRM_PANEL_BRIDGE help TI DLPC3433 is a MIPI DSI based display controller bridge for processing high resolution DMD based projectors. @@ -494,6 +491,7 @@ config DRM_WAVESHARE_BRIDGE tristate "Waveshare DSI bridge" depends on OF depends on BACKLIGHT_CLASS_DEVICE + select DRM_PANEL select DRM_PANEL_BRIDGE select DRM_KMS_HELPER select DRM_MIPI_DSI diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index 57bb2daa5aaf..5672d83cb4a1 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -6,6 +6,7 @@ config DRM_ANALOGIX_ANX6345 select DRM_DISPLAY_DP_HELPER select DRM_DISPLAY_HELPER select DRM_KMS_HELPER + select DRM_PANEL select REGMAP_I2C help ANX6345 is an ultra-low power Full-HD DisplayPort/eDP diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 18e76d1e78e1..6df6963740d2 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -29,7 +29,6 @@ #include <drm/drm_device.h> #include <drm/drm_edid.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -1567,15 +1566,6 @@ int analogix_dp_bind(struct analogix_dp_device *dp, struct drm_device *drm_dev) if (ret) goto err_unregister_aux; - if (dp->plat_data->panel) { - dp->plat_data->next_bridge = devm_drm_panel_bridge_add(dp->dev, - dp->plat_data->panel); - if (IS_ERR(dp->plat_data->next_bridge)) { - ret = PTR_ERR(dp->plat_data->next_bridge); - goto err_unregister_aux; - } - } - ret = drm_bridge_attach(dp->encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) { DRM_ERROR("failed to create bridge (%d)\n", ret); @@ -1647,6 +1637,11 @@ struct drm_dp_aux *analogix_dp_get_aux(struct analogix_dp_device *dp) } EXPORT_SYMBOL_GPL(analogix_dp_get_aux); +static void analogix_dp_put_bridge(void *data) +{ + drm_bridge_put(data); +} + static int analogix_dp_aux_done_probing(struct drm_dp_aux *aux) { struct analogix_dp_device *dp = to_dp(aux); @@ -1655,14 +1650,25 @@ static int analogix_dp_aux_done_probing(struct drm_dp_aux *aux) int ret; /* - * If drm_of_find_panel_or_bridge() returns -ENODEV, there may be no valid panel - * or bridge nodes. The driver should go on for the driver-free bridge or the DP - * mode applications. + * If of_drm_get_bridge_by_endpoint() returns -ENODEV, there may be no + * valid panel or bridge nodes. The driver should go on for the + * driver-free bridge or the DP mode applications. */ - ret = drm_of_find_panel_or_bridge(dp->dev->of_node, port, 0, - &plat_data->panel, &plat_data->next_bridge); - if (ret && ret != -ENODEV) - return ret; + plat_data->next_bridge = of_drm_get_bridge_by_endpoint(dp->dev->of_node, + port, 0); + if (IS_ERR(plat_data->next_bridge)) { + if (PTR_ERR(plat_data->next_bridge) == -ENODEV) + plat_data->next_bridge = NULL; + else + return PTR_ERR(plat_data->next_bridge); + } + + if (plat_data->next_bridge) { + ret = devm_add_action_or_reset(dp->dev, analogix_dp_put_bridge, + plat_data->next_bridge); + if (ret) + return ret; + } return component_add(dp->dev, plat_data->ops); } diff --git a/drivers/gpu/drm/bridge/aux-bridge.c b/drivers/gpu/drm/bridge/aux-bridge.c index 169939d23310..90f76bb529cc 100644 --- a/drivers/gpu/drm/bridge/aux-bridge.c +++ b/drivers/gpu/drm/bridge/aux-bridge.c @@ -12,6 +12,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/bridge/aux-bridge.h> +#include <drm/drm_panel.h> static DEFINE_IDA(drm_aux_bridge_ida); diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index c3e466ded84a..1e28ea579b06 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -7,6 +7,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> +#include <drm/drm_panel.h> #include <drm/drm_probe_helper.h> #include <video/mipi_display.h> #include <video/videomode.h> diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index 26cc72948f31..7ec84c81cd78 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -15,7 +15,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #define LDB_CTRL_CH0_ENABLE BIT(0) #define LDB_CTRL_CH0_DI_SELECT BIT(1) @@ -86,7 +85,6 @@ static const struct fsl_ldb_devdata fsl_ldb_devdata[] = { struct fsl_ldb { struct device *dev; struct drm_bridge bridge; - struct drm_bridge *panel_bridge; struct clk *clk; struct regmap *regmap; const struct fsl_ldb_devdata *devdata; @@ -119,7 +117,7 @@ static int fsl_ldb_attach(struct drm_bridge *bridge, { struct fsl_ldb *fsl_ldb = to_fsl_ldb(bridge); - return drm_bridge_attach(encoder, fsl_ldb->panel_bridge, + return drm_bridge_attach(encoder, fsl_ldb->bridge.next_bridge, bridge, flags); } @@ -298,7 +296,6 @@ static int fsl_ldb_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *panel_node; struct device_node *remote1, *remote2; - struct drm_panel *panel; struct fsl_ldb *fsl_ldb; int dual_link; @@ -339,19 +336,13 @@ static int fsl_ldb_probe(struct platform_device *pdev) fsl_ldb_is_dual(fsl_ldb) ? "dual-link mode" : fsl_ldb->ch0_enabled ? "channel 0" : "channel 1"); - panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); - if (IS_ERR(panel)) - return PTR_ERR(panel); - if (of_property_present(dev->of_node, "nxp,enable-termination-resistor")) fsl_ldb->use_termination_resistor = true; - fsl_ldb->panel_bridge = devm_drm_panel_bridge_add(dev, panel); - drm_panel_put(panel); - if (IS_ERR(fsl_ldb->panel_bridge)) - return PTR_ERR(fsl_ldb->panel_bridge); - + fsl_ldb->bridge.next_bridge = of_drm_find_and_get_bridge(panel_node); + of_node_put(panel_node); + if (!fsl_ldb->bridge.next_bridge) + return -ENODEV; if (fsl_ldb_is_dual(fsl_ldb)) { struct device_node *port1, *port2; diff --git a/drivers/gpu/drm/bridge/imx/imx93-pdfc.c b/drivers/gpu/drm/bridge/imx/imx93-pdfc.c index 2d6ca95ed554..2e5019ed951a 100644 --- a/drivers/gpu/drm/bridge/imx/imx93-pdfc.c +++ b/drivers/gpu/drm/bridge/imx/imx93-pdfc.c @@ -16,6 +16,7 @@ #include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> +#include <drm/drm_panel.h> #define IMX93_DISPLAY_MUX_REG 0x60 #define PARALLEL_DISP_FORMAT GENMASK(10, 8) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c deleted file mode 100644 index 02388a3de626..000000000000 --- a/drivers/gpu/drm/bridge/panel.c +++ /dev/null @@ -1,563 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> - * Copyright (C) 2017 Broadcom - */ - -#include <linux/debugfs.h> -#include <linux/export.h> - -#include <drm/drm_atomic_helper.h> -#include <drm/drm_bridge.h> -#include <drm/drm_connector.h> -#include <drm/drm_encoder.h> -#include <drm/drm_managed.h> -#include <drm/drm_modeset_helper_vtables.h> -#include <drm/drm_of.h> -#include <drm/drm_panel.h> -#include <drm/drm_print.h> -#include <drm/drm_probe_helper.h> - -struct panel_bridge { - struct drm_bridge bridge; - struct drm_connector connector; - struct drm_panel *panel; - u32 connector_type; -}; - -static inline struct panel_bridge * -drm_bridge_to_panel_bridge(struct drm_bridge *bridge) -{ - return container_of(bridge, struct panel_bridge, bridge); -} - -static inline struct panel_bridge * -drm_connector_to_panel_bridge(struct drm_connector *connector) -{ - return container_of(connector, struct panel_bridge, connector); -} - -static int panel_bridge_connector_get_modes(struct drm_connector *connector) -{ - struct panel_bridge *panel_bridge = - drm_connector_to_panel_bridge(connector); - - return drm_panel_get_modes(panel_bridge->panel, connector); -} - -static const struct drm_connector_helper_funcs -panel_bridge_connector_helper_funcs = { - .get_modes = panel_bridge_connector_get_modes, -}; - -static const struct drm_connector_funcs panel_bridge_connector_funcs = { - .reset = drm_atomic_helper_connector_reset, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = drm_connector_cleanup, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, -}; - -static int panel_bridge_attach(struct drm_bridge *bridge, - struct drm_encoder *encoder, - enum drm_bridge_attach_flags flags) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_connector *connector = &panel_bridge->connector; - int ret; - - if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) - return 0; - - drm_connector_helper_add(connector, - &panel_bridge_connector_helper_funcs); - - ret = drm_connector_init(bridge->dev, connector, - &panel_bridge_connector_funcs, - panel_bridge->connector_type); - if (ret) { - DRM_ERROR("Failed to initialize connector\n"); - return ret; - } - - drm_panel_bridge_set_orientation(connector, bridge); - - drm_connector_attach_encoder(&panel_bridge->connector, - encoder); - - if (bridge->dev->registered) { - if (connector->funcs->reset) - connector->funcs->reset(connector); - drm_connector_register(connector); - } - - return 0; -} - -static void panel_bridge_detach(struct drm_bridge *bridge) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_connector *connector = &panel_bridge->connector; - - /* - * Cleanup the connector if we know it was initialized. - * - * FIXME: This wouldn't be needed if the panel_bridge structure was - * allocated with drmm_kzalloc(). This might be tricky since the - * drm_device pointer can only be retrieved when the bridge is attached. - */ - if (connector->dev) - drm_connector_cleanup(connector); -} - -static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge, - struct drm_atomic_commit *atomic_state) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_encoder *encoder = bridge->encoder; - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - - crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder); - if (!crtc) - return; - - old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); - if (old_crtc_state && old_crtc_state->self_refresh_active) - return; - - drm_panel_prepare(panel_bridge->panel); -} - -static void panel_bridge_atomic_enable(struct drm_bridge *bridge, - struct drm_atomic_commit *atomic_state) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_encoder *encoder = bridge->encoder; - struct drm_crtc *crtc; - struct drm_crtc_state *old_crtc_state; - - crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder); - if (!crtc) - return; - - old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); - if (old_crtc_state && old_crtc_state->self_refresh_active) - return; - - drm_panel_enable(panel_bridge->panel); -} - -static void panel_bridge_atomic_disable(struct drm_bridge *bridge, - struct drm_atomic_commit *atomic_state) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_encoder *encoder = bridge->encoder; - struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; - - crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder); - if (!crtc) - return; - - new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); - if (new_crtc_state && new_crtc_state->self_refresh_active) - return; - - drm_panel_disable(panel_bridge->panel); -} - -static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge, - struct drm_atomic_commit *atomic_state) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_encoder *encoder = bridge->encoder; - struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; - - crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder); - if (!crtc) - return; - - new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); - if (new_crtc_state && new_crtc_state->self_refresh_active) - return; - - drm_panel_unprepare(panel_bridge->panel); -} - -static int panel_bridge_get_modes(struct drm_bridge *bridge, - struct drm_connector *connector) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - - return drm_panel_get_modes(panel_bridge->panel, connector); -} - -static void panel_bridge_debugfs_init(struct drm_bridge *bridge, - struct dentry *root) -{ - struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); - struct drm_panel *panel = panel_bridge->panel; - - root = debugfs_create_dir("panel", root); - if (panel->funcs->debugfs_init) - panel->funcs->debugfs_init(panel, root); -} - -static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { - .attach = panel_bridge_attach, - .detach = panel_bridge_detach, - .atomic_pre_enable = panel_bridge_atomic_pre_enable, - .atomic_enable = panel_bridge_atomic_enable, - .atomic_disable = panel_bridge_atomic_disable, - .atomic_post_disable = panel_bridge_atomic_post_disable, - .get_modes = panel_bridge_get_modes, - .atomic_create_state = drm_atomic_helper_bridge_create_state, - .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, - .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, - .debugfs_init = panel_bridge_debugfs_init, -}; - -/** - * drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge. - * - * @bridge: The drm_bridge to be checked. - * - * Returns true if the bridge is a panel bridge, or false otherwise. - */ -bool drm_bridge_is_panel(const struct drm_bridge *bridge) -{ - return bridge->funcs == &panel_bridge_bridge_funcs; -} -EXPORT_SYMBOL(drm_bridge_is_panel); - -/** - * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that - * just calls the appropriate functions from &drm_panel. - * - * @panel: The drm_panel being wrapped. Must be non-NULL. - * - * For drivers converting from directly using drm_panel: The expected - * usage pattern is that during either encoder module probe or DSI - * host attach, a drm_panel will be looked up through - * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to - * wrap that panel in the new bridge, and the result can then be - * passed to drm_bridge_attach(). The drm_panel_prepare() and related - * functions can be dropped from the encoder driver (they're now - * called by the KMS helpers before calling into the encoder), along - * with connector creation. When done with the bridge (after - * drm_mode_config_cleanup() if the bridge has already been attached), then - * drm_panel_bridge_remove() to free it. - * - * The connector type is set to @panel->connector_type, which must be set to a - * known type. Calling this function with a panel whose connector type is - * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL). - * - * See devm_drm_panel_bridge_add() for an automatically managed version of this - * function. - */ -struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel) -{ - if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) - return ERR_PTR(-EINVAL); - - return drm_panel_bridge_add_typed(panel, panel->connector_type); -} -EXPORT_SYMBOL(drm_panel_bridge_add); - -/** - * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with - * an explicit connector type. - * @panel: The drm_panel being wrapped. Must be non-NULL. - * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) - * - * This is just like drm_panel_bridge_add(), but forces the connector type to - * @connector_type instead of infering it from the panel. - * - * This function is deprecated and should not be used in new drivers. Use - * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they - * don't report a connector type. - */ -struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, - u32 connector_type) -{ - struct panel_bridge *panel_bridge; - - if (!panel) - return ERR_PTR(-EINVAL); - - panel_bridge = devm_drm_bridge_alloc(panel->dev, struct panel_bridge, bridge, - &panel_bridge_bridge_funcs); - if (IS_ERR(panel_bridge)) - return (void *)panel_bridge; - - panel_bridge->connector_type = connector_type; - panel_bridge->panel = drm_panel_get(panel); - - panel_bridge->bridge.of_node = panel->dev->of_node; - panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; - panel_bridge->bridge.type = connector_type; - panel_bridge->bridge.pre_enable_prev_first = panel->prepare_prev_first; - - drm_bridge_add(&panel_bridge->bridge); - - return &panel_bridge->bridge; -} -EXPORT_SYMBOL(drm_panel_bridge_add_typed); - -/** - * drm_panel_bridge_remove - Unregisters and frees a drm_bridge - * created by drm_panel_bridge_add(). - * - * @bridge: The drm_bridge being freed. - */ -void drm_panel_bridge_remove(struct drm_bridge *bridge) -{ - struct panel_bridge *panel_bridge; - struct drm_panel *panel; - - if (!bridge) - return; - - if (!drm_bridge_is_panel(bridge)) { - drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__); - return; - } - - panel_bridge = drm_bridge_to_panel_bridge(bridge); - panel = panel_bridge->panel; - - drm_bridge_remove(bridge); - /* TODO remove this after reworking panel_bridge lifetime */ - devm_drm_put_bridge(panel->dev, bridge); - drm_panel_put(panel); -} -EXPORT_SYMBOL(drm_panel_bridge_remove); - -/** - * drm_panel_bridge_set_orientation - Set the connector's panel orientation - * from the bridge that can be transformed to panel bridge. - * - * @connector: The connector to be set panel orientation. - * @bridge: The drm_bridge to be transformed to panel bridge. - * - * Returns 0 on success, negative errno on failure. - */ -int drm_panel_bridge_set_orientation(struct drm_connector *connector, - struct drm_bridge *bridge) -{ - struct panel_bridge *panel_bridge; - - panel_bridge = drm_bridge_to_panel_bridge(bridge); - - return drm_connector_set_orientation_from_panel(connector, - panel_bridge->panel); -} -EXPORT_SYMBOL(drm_panel_bridge_set_orientation); - -static void devm_drm_panel_bridge_release(struct device *dev, void *res) -{ - struct drm_bridge *bridge = *(struct drm_bridge **)res; - struct panel_bridge *panel_bridge; - struct drm_panel *panel; - - if (!bridge) - return; - - panel_bridge = drm_bridge_to_panel_bridge(bridge); - panel = panel_bridge->panel; - drm_bridge_remove(bridge); - drm_panel_put(panel); -} - -/** - * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector - * that just calls the appropriate functions from &drm_panel. - * @dev: device to tie the bridge lifetime to - * @panel: The drm_panel being wrapped. Must be non-NULL. - * - * This is the managed version of drm_panel_bridge_add() which automatically - * calls drm_panel_bridge_remove() when @dev is unbound. - */ -struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, - struct drm_panel *panel) -{ - if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) - return ERR_PTR(-EINVAL); - - return devm_drm_panel_bridge_add_typed(dev, panel, - panel->connector_type); -} -EXPORT_SYMBOL(devm_drm_panel_bridge_add); - -/** - * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and - * &drm_connector with an explicit connector type. - * @dev: device to tie the bridge lifetime to - * @panel: The drm_panel being wrapped. Must be non-NULL. - * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) - * - * This is just like devm_drm_panel_bridge_add(), but forces the connector type - * to @connector_type instead of infering it from the panel. - * - * This function is deprecated and should not be used in new drivers. Use - * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if - * they don't report a connector type. - */ -struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, - struct drm_panel *panel, - u32 connector_type) -{ - struct drm_bridge **ptr, *bridge; - - ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - bridge = drm_panel_bridge_add_typed(panel, connector_type); - if (IS_ERR(bridge)) { - devres_free(ptr); - return bridge; - } - - *ptr = bridge; - devres_add(dev, ptr); - - return bridge; -} -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. - * - * drm_panel_bridge creates the connector. - * This function gives external access to the connector. - * - * Returns: Pointer to drm_connector - */ -struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge) -{ - struct panel_bridge *panel_bridge; - - panel_bridge = drm_bridge_to_panel_bridge(bridge); - - return &panel_bridge->connector; -} -EXPORT_SYMBOL(drm_panel_bridge_connector); - -#ifdef CONFIG_OF -/** - * devm_drm_of_get_bridge - Return next bridge in the chain - * @dev: 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 pointer to the bridge if successful, or an error pointer - * otherwise. - */ -struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, - 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 = devm_drm_panel_bridge_add(dev, panel); - drm_panel_put(panel); - } - - 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); - drm_panel_put(panel); - } - - return bridge; -} -EXPORT_SYMBOL(drmm_of_get_bridge); - -#endif diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index a60fb7430956..480e568dd2c9 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -25,7 +25,6 @@ #include <video/mipi_display.h> #include <drm/bridge/samsung-dsim.h> -#include <drm/drm_panel.h> #include <drm/drm_print.h> /* returns true iff both arguments logically differs */ @@ -1898,7 +1897,6 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, struct device *dev = dsi->dev; struct device_node *np = dev->of_node; struct device_node *remote; - struct drm_panel *panel; int ret = 0; /* @@ -1913,7 +1911,7 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, of_node_name_eq(remote, "ports")) continue; - goto of_find_panel_or_bridge; + goto of_find_bridge; } /* @@ -1927,25 +1925,13 @@ static int samsung_dsim_host_attach(struct mipi_dsi_host *host, remote = of_graph_get_remote_node(np, 1, 0); -of_find_panel_or_bridge: +of_find_bridge: if (!remote) return -ENODEV; - panel = of_drm_find_panel(remote); - if (!IS_ERR(panel)) { - next_bridge = devm_drm_panel_bridge_add(dev, panel); - drm_panel_put(panel); - if (IS_ERR(next_bridge)) { - ret = PTR_ERR(next_bridge); - next_bridge = NULL; // Inhibit the cleanup action on an ERR_PTR - } else { - drm_bridge_get(next_bridge); - } - } else { - next_bridge = of_drm_find_and_get_bridge(remote); - if (!next_bridge) - ret = -EINVAL; - } + next_bridge = of_drm_find_and_get_bridge(remote); + if (!next_bridge) + ret = -EINVAL; of_node_put(remote); diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c index b0142d6d40dc..71877699dfab 100644 --- a/drivers/gpu/drm/bridge/ssd2825.c +++ b/drivers/gpu/drm/bridge/ssd2825.c @@ -17,7 +17,6 @@ #include <drm/drm_drv.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <video/mipi_display.h> #define SSD2825_DEVICE_ID_REG 0xb0 @@ -114,7 +113,6 @@ static const struct regulator_bulk_data ssd2825_supplies[] = { struct ssd2825_dsi_output { struct mipi_dsi_device *dev; - struct drm_panel *panel; struct drm_bridge *bridge; }; @@ -277,7 +275,6 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d { struct ssd2825_priv *priv = dsi_host_to_ssd2825(host); struct drm_bridge *bridge; - struct drm_panel *panel; struct device_node *ep; int ret; @@ -295,20 +292,14 @@ static int ssd2825_dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_d return -EOPNOTSUPP; } - ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, &panel, &bridge); - if (ret) - return ret; + bridge = of_drm_get_bridge_by_endpoint(host->dev->of_node, 1, 0); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); - if (panel) { - bridge = drm_panel_bridge_add_typed(panel, DRM_MODE_CONNECTOR_DSI); - drm_panel_put(panel); - if (IS_ERR(bridge)) - return PTR_ERR(bridge); - } + bridge->type = DRM_MODE_CONNECTOR_DSI; priv->output.dev = dev; priv->output.bridge = bridge; - priv->output.panel = panel; priv->dsi_lanes = dev->lanes; @@ -333,8 +324,7 @@ static int ssd2825_dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_d struct ssd2825_priv *priv = dsi_host_to_ssd2825(host); drm_bridge_remove(&priv->bridge); - if (priv->output.panel) - drm_panel_bridge_remove(priv->output.bridge); + drm_bridge_put(priv->output.bridge); return 0; } @@ -579,9 +569,6 @@ static void ssd2825_bridge_atomic_pre_enable(struct drm_bridge *bridge, /* Initial DSI configuration register set */ ssd2825_write_reg(priv, SSD2825_CONFIGURATION_REG, config); ssd2825_write_reg(priv, SSD2825_VC_CTRL_REG, 0); - - if (priv->output.panel) - drm_panel_enable(priv->output.panel); } static void ssd2825_bridge_atomic_enable(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index ac83af7902cd..51f4baa62a53 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -35,7 +35,6 @@ #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -370,7 +369,6 @@ struct tc_data { struct drm_dp_aux aux; struct drm_bridge bridge; - struct drm_bridge *panel_bridge; struct drm_connector connector; struct mipi_dsi_device *dsi; @@ -1742,8 +1740,8 @@ static int tc_connector_get_modes(struct drm_connector *connector) return 0; } - if (tc->panel_bridge) { - num_modes = drm_bridge_get_modes(tc->panel_bridge, connector); + if (tc->bridge.next_bridge) { + num_modes = drm_bridge_get_modes(tc->bridge.next_bridge, connector); if (num_modes > 0) return num_modes; } @@ -1788,7 +1786,7 @@ tc_connector_detect(struct drm_connector *connector, bool force) if (tc->hpd_pin >= 0) return tc_bridge_detect(&tc->bridge, connector); - if (tc->panel_bridge) + if (tc->bridge.next_bridge) return connector_status_connected; else return connector_status_unknown; @@ -1809,10 +1807,10 @@ static int tc_dpi_bridge_attach(struct drm_bridge *bridge, { struct tc_data *tc = bridge_to_tc(bridge); - if (!tc->panel_bridge) + if (!tc->bridge.next_bridge) return 0; - return drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge, + return drm_bridge_attach(tc->bridge.encoder, tc->bridge.next_bridge, &tc->bridge, flags); } @@ -1825,9 +1823,9 @@ static int tc_edp_bridge_attach(struct drm_bridge *bridge, struct drm_device *drm = bridge->dev; int ret; - if (tc->panel_bridge) { + if (tc->bridge.next_bridge) { /* If a connector is required then this driver shall create it */ - ret = drm_bridge_attach(tc->bridge.encoder, tc->panel_bridge, + ret = drm_bridge_attach(tc->bridge.encoder, tc->bridge.next_bridge, &tc->bridge, flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) return ret; @@ -2318,53 +2316,31 @@ static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc) { struct device *dev = tc->dev; struct drm_bridge *bridge; - struct drm_panel *panel; - int ret; /* port@1 is the DPI input/output port */ - ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge); - if (ret && ret != -ENODEV) - return dev_err_probe(dev, ret, - "Could not find DPI panel or bridge\n"); - - if (panel) { - bridge = devm_drm_panel_bridge_add(dev, panel); - drm_panel_put(panel); - if (IS_ERR(bridge)) - return PTR_ERR(bridge); - } - - if (bridge) { - tc->panel_bridge = bridge; - tc->bridge.type = DRM_MODE_CONNECTOR_DPI; + bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, 0); + if (IS_ERR(bridge)) + return dev_err_probe(dev, PTR_ERR(bridge), + "Could not find DPI bridge\n"); - return 0; - } + tc->bridge.next_bridge = bridge; + tc->bridge.type = DRM_MODE_CONNECTOR_DPI; - return ret; + return 0; } static int tc_probe_edp_bridge_endpoint(struct tc_data *tc) { struct device *dev = tc->dev; - struct drm_panel *panel; - int ret; + struct drm_bridge *bridge; /* port@2 is the output port */ - ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &panel, NULL); - if (ret && ret != -ENODEV) - return dev_err_probe(dev, ret, - "Could not find DSI panel or bridge\n"); - - if (panel) { - struct drm_bridge *panel_bridge; - - panel_bridge = devm_drm_panel_bridge_add(dev, panel); - drm_panel_put(panel); - if (IS_ERR(panel_bridge)) - return PTR_ERR(panel_bridge); + bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 2, 0); + if (IS_ERR(bridge) && PTR_ERR(bridge) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(bridge), "Could not find DSI bridge\n"); - tc->panel_bridge = panel_bridge; + if (!IS_ERR(bridge)) { + tc->bridge.next_bridge = bridge; tc->bridge.type = DRM_MODE_CONNECTOR_eDP; } else { tc->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 19b43efcf93f..18f73ad2589e 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -22,7 +22,6 @@ #include <drm/drm_drv.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <video/mipi_display.h> #include <video/videomode.h> @@ -137,7 +136,6 @@ static const char * const tc358768_supplies[] = { struct tc358768_dsi_output { struct mipi_dsi_device *dev; - struct drm_panel *panel; struct drm_bridge *bridge; }; @@ -443,7 +441,6 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, { struct tc358768_priv *priv = dsi_host_to_tc358768(host); struct drm_bridge *bridge; - struct drm_panel *panel; struct device_node *ep; int ret; @@ -471,24 +468,15 @@ static int tc358768_dsi_host_attach(struct mipi_dsi_host *host, return -ENOTSUPP; } - ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, &panel, - &bridge); - if (ret) - return ret; + bridge = of_drm_get_bridge_by_endpoint(host->dev->of_node, 1, 0); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); - if (panel) { - bridge = drm_panel_bridge_add_typed(panel, - DRM_MODE_CONNECTOR_DSI); - drm_panel_put(panel); - if (IS_ERR(bridge)) - return PTR_ERR(bridge); - - bridge->pre_enable_prev_first = true; - } + bridge->type = DRM_MODE_CONNECTOR_DSI; + bridge->pre_enable_prev_first = true; priv->output.dev = dev; priv->output.bridge = bridge; - priv->output.panel = panel; priv->dsi_lanes = dev->lanes; priv->dsi_bpp = mipi_dsi_pixel_format_to_bpp(dev->format); @@ -518,8 +506,7 @@ static int tc358768_dsi_host_detach(struct mipi_dsi_host *host, struct tc358768_priv *priv = dsi_host_to_tc358768(host); drm_bridge_remove(&priv->bridge); - if (priv->output.panel) - drm_panel_bridge_remove(priv->output.bridge); + drm_bridge_put(priv->output.bridge); return 0; } diff --git a/drivers/gpu/drm/bridge/ti-tdp158.c b/drivers/gpu/drm/bridge/ti-tdp158.c index 01fc643bd78f..8e3e99cbe41a 100644 --- a/drivers/gpu/drm/bridge/ti-tdp158.c +++ b/drivers/gpu/drm/bridge/ti-tdp158.c @@ -8,6 +8,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> +#include <drm/drm_panel.h> struct tdp158 { struct drm_bridge bridge; diff --git a/drivers/gpu/drm/bridge/waveshare-dsi.c b/drivers/gpu/drm/bridge/waveshare-dsi.c index 420f3b870a74..2c40bfef2b45 100644 --- a/drivers/gpu/drm/bridge/waveshare-dsi.c +++ b/drivers/gpu/drm/bridge/waveshare-dsi.c @@ -16,11 +16,9 @@ #include <drm/drm_bridge.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> struct ws_bridge { struct drm_bridge bridge; - struct drm_bridge *next_bridge; struct backlight_device *backlight; struct device *dev; struct regmap *reg_map; @@ -89,7 +87,7 @@ static int ws_bridge_bridge_attach(struct drm_bridge *bridge, { struct ws_bridge *ws = bridge_to_ws_bridge(bridge); - return drm_bridge_attach(encoder, ws->next_bridge, + return drm_bridge_attach(encoder, ws->bridge.next_bridge, &ws->bridge, flags); } @@ -150,7 +148,6 @@ static struct backlight_device *ws_bridge_create_backlight(struct ws_bridge *ws) static int ws_bridge_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; - struct drm_panel *panel; struct ws_bridge *ws; int ret; @@ -164,14 +161,10 @@ static int ws_bridge_probe(struct i2c_client *i2c) if (IS_ERR(ws->reg_map)) return dev_err_probe(dev, PTR_ERR(ws->reg_map), "Failed to allocate regmap\n"); - ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, &panel, NULL); - if (ret) - return dev_err_probe(dev, ret, "Failed to find remote panel\n"); - - ws->next_bridge = devm_drm_panel_bridge_add(dev, panel); - drm_panel_put(panel); - if (IS_ERR(ws->next_bridge)) - return PTR_ERR(ws->next_bridge); + ws->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, -1); + if (IS_ERR(ws->bridge.next_bridge)) + return dev_err_probe(dev, PTR_ERR(ws->bridge.next_bridge), + "Failed to find remote bridge\n"); ws->backlight = ws_bridge_create_backlight(ws); if (IS_ERR(ws->backlight)) { diff --git a/drivers/gpu/drm/clients/Makefile b/drivers/gpu/drm/clients/Makefile index c16addbc327f..1304ff358509 100644 --- a/drivers/gpu/drm/clients/Makefile +++ b/drivers/gpu/drm/clients/Makefile @@ -4,5 +4,6 @@ subdir-ccflags-y += -I$(src)/.. drm_client_lib-y := drm_client_setup.o drm_client_lib-$(CONFIG_DRM_CLIENT_LOG) += drm_log.o -drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o +drm_client_lib-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_client.o \ + drm_fbdev_helper.o obj-$(CONFIG_DRM_CLIENT_LIB) += drm_client_lib.o diff --git a/drivers/gpu/drm/clients/drm_fbdev_client.c b/drivers/gpu/drm/clients/drm_fbdev_client.c index 7574bd36f45e..eec463d19943 100644 --- a/drivers/gpu/drm/clients/drm_fbdev_client.c +++ b/drivers/gpu/drm/clients/drm_fbdev_client.c @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_client.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/clients/drm_fbdev_helper.c index d4664ed468b2..fff3cf6a0aed 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/clients/drm_fbdev_helper.c @@ -32,9 +32,9 @@ #include <linux/console.h> #include <linux/export.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -43,39 +43,9 @@ #include <drm/drm_vblank.h> #include "drm_internal.h" +#include "drm_crtc_helper_internal.h" #include "drm_crtc_internal.h" -static bool drm_fbdev_emulation = true; -module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); -MODULE_PARM_DESC(fbdev_emulation, - "Enable legacy fbdev emulation [default=true]"); - -static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC; -module_param(drm_fbdev_overalloc, int, 0444); -MODULE_PARM_DESC(drm_fbdev_overalloc, - "Overallocation of the fbdev buffer (%) [default=" - __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]"); - -/* - * In order to keep user-space compatibility, we want in certain use-cases - * to keep leaking the fbdev physical address to the user-space program - * handling the fbdev buffer. - * - * This is a bad habit, essentially kept to support closed-source OpenGL - * drivers that should really be moved into open-source upstream projects - * instead of using legacy physical addresses in user space to communicate - * with other out-of-tree kernel modules. - * - * This module_param *should* be removed as soon as possible and be - * considered as a broken and legacy behaviour from a modern fbdev device. - */ -static bool drm_leak_fbdev_smem; -#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) -module_param_unsafe(drm_leak_fbdev_smem, bool, 0600); -MODULE_PARM_DESC(drm_leak_fbdev_smem, - "Allow unsafe leaking fbdev physical smem address [default=false]"); -#endif - /** * DOC: fbdev helpers * diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 707d7143e475..81f3e26f6fdf 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -18,6 +18,7 @@ #include <drm/drm_edid.h> #include <drm/drm_managed.h> #include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include <drm/display/drm_hdcp_helper.h> diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 7ce9e212770a..b7fce0bf8acc 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -2161,6 +2161,9 @@ ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux, struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux); + if (!port->ddps) + return -EIO; + return drm_dp_send_dpcd_read(port->mgr, port, offset, size, buffer); } @@ -2184,6 +2187,9 @@ ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux, struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux); + if (!port->ddps) + return -EIO; + return drm_dp_send_dpcd_write(port->mgr, port, offset, size, buffer); } diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 2f4208b34234..1b4ee746acb9 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1745,23 +1745,6 @@ struct drm_bridge *of_drm_get_bridge_by_endpoint(const struct device_node *np, EXPORT_SYMBOL_GPL(of_drm_get_bridge_by_endpoint); #endif -/** - * devm_drm_put_bridge - Release a bridge reference obtained via devm - * @dev: device that got the bridge via devm - * @bridge: pointer to a struct drm_bridge obtained via devm - * - * Same as drm_bridge_put() for bridge pointers obtained via devm functions - * such as devm_drm_bridge_alloc(). - * - * This function is a temporary workaround and MUST NOT be used. Manual - * handling of bridge lifetime is inherently unsafe. - */ -void devm_drm_put_bridge(struct device *dev, struct drm_bridge *bridge) -{ - devm_release_action(dev, drm_bridge_put_void, bridge); -} -EXPORT_SYMBOL(devm_drm_put_bridge); - static void drm_bridge_debugfs_show_bridge(struct drm_printer *p, struct drm_bridge *bridge, unsigned int idx, diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index d94c86bfed86..893716a9e489 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -28,10 +28,10 @@ #include <drm/drm_file.h> #include <drm/drm_managed.h> #include <drm/drm_panel.h> +#include <drm/drm_panel_quirks.h> #include <drm/drm_print.h> #include <drm/drm_privacy_screen_consumer.h> #include <drm/drm_sysfs.h> -#include <drm/drm_utils.h> #include <linux/export.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/drm_crtc_helper_internal.h b/drivers/gpu/drm/drm_crtc_helper_internal.h index bae73936acf9..adcc9a2e24ee 100644 --- a/drivers/gpu/drm/drm_crtc_helper_internal.h +++ b/drivers/gpu/drm/drm_crtc_helper_internal.h @@ -29,6 +29,8 @@ #ifndef __DRM_CRTC_HELPER_INTERNAL_H__ #define __DRM_CRTC_HELPER_INTERNAL_H__ +#include <linux/types.h> + enum drm_mode_status; struct drm_connector; struct drm_crtc; @@ -36,6 +38,13 @@ struct drm_display_mode; struct drm_encoder; struct drm_modeset_acquire_ctx; +/* drm_kms_helper_common.c */ +#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) +extern bool drm_fbdev_emulation; +extern int drm_fbdev_overalloc; +extern bool drm_leak_fbdev_smem; +#endif + /* drm_probe_helper.c */ enum drm_mode_status drm_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c index 9412d9fdd74b..452f84400350 100644 --- a/drivers/gpu/drm/drm_fbdev_dma.c +++ b/drivers/gpu/drm/drm_fbdev_dma.c @@ -4,10 +4,10 @@ #include <linux/fb.h> #include <linux/vmalloc.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_drv.h> #include <drm/drm_fbdev_dma.h> #include <drm/drm_fb_dma_helper.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/drm_fbdev_shmem.c b/drivers/gpu/drm/drm_fbdev_shmem.c index 458c899b5d4f..8eb05fe9bc98 100644 --- a/drivers/gpu/drm/drm_fbdev_shmem.c +++ b/drivers/gpu/drm/drm_fbdev_shmem.c @@ -3,9 +3,9 @@ #include <linux/export.h> #include <linux/fb.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_drv.h> #include <drm/drm_fbdev_shmem.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_gem_shmem_helper.h> diff --git a/drivers/gpu/drm/drm_fbdev_ttm.c b/drivers/gpu/drm/drm_fbdev_ttm.c index 160bc35d8738..8eb59c2ac5ea 100644 --- a/drivers/gpu/drm/drm_fbdev_ttm.c +++ b/drivers/gpu/drm/drm_fbdev_ttm.c @@ -4,9 +4,9 @@ #include <linux/moduleparam.h> #include <linux/vmalloc.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index b5d5b469b444..1e55ecac4698 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -25,10 +25,49 @@ * */ +#include <linux/export.h> #include <linux/module.h> +#include <linux/moduleparam.h> +#include "drm_crtc_helper_internal.h" #include "drm_panic_internal.h" +#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) +bool drm_fbdev_emulation = true; +EXPORT_SYMBOL(drm_fbdev_emulation); +module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); +MODULE_PARM_DESC(fbdev_emulation, "Enable legacy fbdev emulation [default=true]"); + +int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC; +EXPORT_SYMBOL(drm_fbdev_overalloc); +module_param(drm_fbdev_overalloc, int, 0444); +MODULE_PARM_DESC(drm_fbdev_overalloc, + "Overallocation of the fbdev buffer (%) [default=" + __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]"); + +/* + * In order to keep user-space compatibility, we want in certain use-cases + * to keep leaking the fbdev physical address to the user-space program + * handling the fbdev buffer. + * + * This is a bad habit, essentially kept to support closed-source OpenGL + * drivers that should really be moved into open-source upstream projects + * instead of using legacy physical addresses in user space to communicate + * with other out-of-tree kernel modules. + * + * This module_param *should* be removed as soon as possible and be + * considered as a broken and legacy behaviour from a modern fbdev device. + */ +bool drm_leak_fbdev_smem; +EXPORT_SYMBOL(drm_leak_fbdev_smem); +#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM) +module_param_unsafe(drm_leak_fbdev_smem, bool, 0600); +MODULE_PARM_DESC(drm_leak_fbdev_smem, + "Allow unsafe leaking fbdev physical smem address [default=false]"); +#endif + +#endif + static int __init drm_kms_helper_init(void) { return drm_panic_helper_init(); diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 2b53124ded68..411f258d9f91 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -264,69 +264,6 @@ int drm_of_get_panel_orientation(const struct device_node *np, } EXPORT_SYMBOL_GPL(drm_of_get_panel_orientation); -/** - * drm_of_find_panel_or_bridge - return connected panel or bridge device - * @np: device tree node containing encoder output ports - * @port: port in the device tree node - * @endpoint: endpoint in the device tree node - * @panel: pointer to hold returned drm_panel, must not be NULL. On success - * the caller must call drm_panel_put() when done with the panel - * @bridge: pointer to hold returned drm_bridge - * - * Given a DT node's port and endpoint number, find the connected node and - * return either the associated struct drm_panel or drm_bridge device. - * - * This function is deprecated and should not be used in new drivers. Use - * of_drm_get_bridge_by_endpoint() instead when not looking for a panel, or - * devm_drm_of_get_bridge() otherwise. - * - * Returns zero if successful, or one of the standard error codes if it fails. - */ -int drm_of_find_panel_or_bridge(const struct device_node *np, - int port, int endpoint, - struct drm_panel **panel, - struct drm_bridge **bridge) -{ - if (WARN_ON(!panel)) - return -EINVAL; - - *panel = NULL; - if (bridge) - *bridge = NULL; - - /* - * of_graph_get_remote_node() produces a noisy error message if port - * node isn't found and the absence of the port is a legit case here, - * so at first we silently check whether a graph is present in the - * device-tree node. - */ - if (!of_graph_is_present(np)) - return -ENODEV; - - struct device_node *remote __free(device_node) = - of_graph_get_remote_node(np, port, endpoint); - if (!remote) - return -ENODEV; - - *panel = of_drm_find_panel(remote); - if (!IS_ERR(*panel)) - return 0; - - *panel = NULL; - - if (bridge) { - /* No panel found yet, check for a bridge next. */ - *bridge = of_drm_find_bridge(remote); - if (*bridge) - return 0; - - *bridge = NULL; - } - - return -EPROBE_DEFER; -} -EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); - enum drm_of_lvds_pixels { DRM_OF_LVDS_EVEN = BIT(0), DRM_OF_LVDS_ODD = BIT(1), diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index f8f6082e637f..c00529bfb706 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -1,36 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2013, NVIDIA Corporation. 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 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. + * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> + * Copyright (C) 2017 Broadcom */ #include <linux/backlight.h> +#include <linux/debugfs.h> #include <linux/err.h> #include <linux/export.h> #include <linux/module.h> #include <linux/of.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_connector.h> #include <drm/drm_crtc.h> +#include <drm/drm_encoder.h> +#include <drm/drm_managed.h> +#include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> static DEFINE_MUTEX(panel_lock); static LIST_HEAD(panel_list); @@ -46,6 +38,526 @@ static LIST_HEAD(panel_list); * take look at drm_panel_bridge_add() and devm_drm_panel_bridge_add(). */ +static inline struct drm_panel * +drm_bridge_to_panel(const struct drm_bridge *bridge) +{ + return container_of(bridge, struct drm_panel, bridge); +} + +static inline struct drm_panel * +drm_connector_to_panel(const struct drm_connector *connector) +{ + return container_of(connector, struct drm_panel, connector); +} + +struct panel_bridge { + struct drm_bridge bridge; + struct drm_connector connector; + struct drm_panel *panel; + u32 connector_type; +}; + +static int panel_bridge_connector_get_modes(struct drm_connector *connector) +{ + struct drm_panel *panel = drm_connector_to_panel(connector); + + return drm_panel_get_modes(panel, connector); +} + +static const struct drm_connector_helper_funcs +panel_bridge_connector_helper_funcs = { + .get_modes = panel_bridge_connector_get_modes, +}; + +static const struct drm_connector_funcs panel_bridge_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int panel_bridge_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + struct drm_connector *connector = &panel->connector; + int ret; + + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) + return 0; + + drm_connector_helper_add(connector, + &panel_bridge_connector_helper_funcs); + + ret = drm_connector_init(bridge->dev, connector, + &panel_bridge_connector_funcs, + panel->connector_type); + if (ret) { + DRM_ERROR("Failed to initialize connector\n"); + return ret; + } + + drm_panel_bridge_set_orientation(connector, bridge); + + drm_connector_attach_encoder(connector, encoder); + + if (bridge->dev->registered) { + if (connector->funcs->reset) + connector->funcs->reset(connector); + drm_connector_register(connector); + } + + return 0; +} + +static void panel_bridge_detach(struct drm_bridge *bridge) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + struct drm_connector *connector = &panel->connector; + + /* Cleanup the connector if we know it was initialized */ + if (connector->dev) + drm_connector_cleanup(connector); +} + +static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_commit *atomic_state) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + struct drm_encoder *encoder = bridge->encoder; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + + crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder); + if (!crtc) + return; + + old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); + if (old_crtc_state && old_crtc_state->self_refresh_active) + return; + + drm_panel_prepare(panel); +} + +static void panel_bridge_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_commit *atomic_state) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + struct drm_encoder *encoder = bridge->encoder; + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state; + + crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder); + if (!crtc) + return; + + old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); + if (old_crtc_state && old_crtc_state->self_refresh_active) + return; + + drm_panel_enable(panel); +} + +static void panel_bridge_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_commit *atomic_state) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + struct drm_encoder *encoder = bridge->encoder; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + + crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder); + if (!crtc) + return; + + new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); + if (new_crtc_state && new_crtc_state->self_refresh_active) + return; + + drm_panel_disable(panel); +} + +static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge, + struct drm_atomic_commit *atomic_state) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + struct drm_encoder *encoder = bridge->encoder; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + + crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder); + if (!crtc) + return; + + new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); + if (new_crtc_state && new_crtc_state->self_refresh_active) + return; + + drm_panel_unprepare(panel); +} + +static int panel_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + + return drm_panel_get_modes(panel, connector); +} + +static void panel_bridge_debugfs_init(struct drm_bridge *bridge, + struct dentry *root) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + + root = debugfs_create_dir("panel", root); + if (panel->funcs->debugfs_init) + panel->funcs->debugfs_init(panel, root); +} + +static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { + .attach = panel_bridge_attach, + .detach = panel_bridge_detach, + .atomic_pre_enable = panel_bridge_atomic_pre_enable, + .atomic_enable = panel_bridge_atomic_enable, + .atomic_disable = panel_bridge_atomic_disable, + .atomic_post_disable = panel_bridge_atomic_post_disable, + .get_modes = panel_bridge_get_modes, + .atomic_create_state = drm_atomic_helper_bridge_create_state, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, + .debugfs_init = panel_bridge_debugfs_init, +}; + +/** + * drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge. + * + * @bridge: The drm_bridge to be checked. + * + * Returns true if the bridge is a panel bridge, or false otherwise. + */ +bool drm_bridge_is_panel(const struct drm_bridge *bridge) +{ + return bridge->funcs == &panel_bridge_bridge_funcs; +} +EXPORT_SYMBOL(drm_bridge_is_panel); + +/** + * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that + * just calls the appropriate functions from &drm_panel. + * + * @panel: The drm_panel being wrapped. Must be non-NULL. + * + * This function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and update your driver to stop using it. + * + * For drivers converting from directly using drm_panel: The expected + * usage pattern is that during either encoder module probe or DSI + * host attach, a drm_panel will be looked up through + * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to + * wrap that panel in the new bridge, and the result can then be + * passed to drm_bridge_attach(). The drm_panel_prepare() and related + * functions can be dropped from the encoder driver (they're now + * called by the KMS helpers before calling into the encoder), along + * with connector creation. When done with the bridge (after + * drm_mode_config_cleanup() if the bridge has already been attached), then + * drm_panel_bridge_remove() to free it. + * + * The connector type is set to @panel->connector_type, which must be set to a + * known type. Calling this function with a panel whose connector type is + * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL). + * + * See devm_drm_panel_bridge_add() for an automatically managed version of this + * function. + */ +struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel) +{ + if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) + return ERR_PTR(-EINVAL); + + return drm_panel_bridge_add_typed(panel, panel->connector_type); +} +EXPORT_SYMBOL(drm_panel_bridge_add); + +/** + * drm_panel_bridge_add_typed - Pretend to create a &drm_bridge and &drm_connector with + * an explicit connector type. + * @panel: The drm_panel being wrapped. Must be non-NULL. + * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) + * + * This is just like drm_panel_bridge_add(), but forces the connector type to + * @connector_type instead of infering it from the panel. + * + * This function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and update your driver to stop using it. + */ +struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, + u32 connector_type) +{ + if (!panel) + return ERR_PTR(-EINVAL); + + return drm_bridge_get(&panel->bridge); +} +EXPORT_SYMBOL(drm_panel_bridge_add_typed); + +/** + * drm_panel_bridge_remove - Unregisters and frees a drm_bridge + * created by drm_panel_bridge_add(). + * + * @bridge: The drm_bridge being freed. + * + * This function is deprecated. A drm_bridge is embedded into every panel + * and removed when the panel is removed. Don't use it in new drivers and + * update your driver to stop using it. + */ +void drm_panel_bridge_remove(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + if (!drm_bridge_is_panel(bridge)) { + drm_warn(bridge->dev, "%s: called on non-panel bridge!\n", __func__); + return; + } + + drm_bridge_put(bridge); +} +EXPORT_SYMBOL(drm_panel_bridge_remove); + +/** + * drm_panel_bridge_set_orientation - Set the connector's panel orientation + * from the bridge that can be transformed to panel bridge. + * + * @connector: The connector to be set panel orientation. + * @bridge: The drm_bridge whose orientation should be set. + * + * Returns 0 on success, negative errno on failure. + */ +int drm_panel_bridge_set_orientation(struct drm_connector *connector, + struct drm_bridge *bridge) +{ + struct drm_panel *panel = drm_bridge_to_panel(bridge); + + return drm_connector_set_orientation_from_panel(connector, panel); +} +EXPORT_SYMBOL(drm_panel_bridge_set_orientation); + +static void devm_drm_panel_bridge_release(struct device *dev, void *res) +{ + struct drm_bridge *bridge = *(struct drm_bridge **)res; + + if (!bridge) + return; + + drm_bridge_put(bridge); +} + +/** + * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector + * that just calls the appropriate functions from &drm_panel. + * @dev: device to tie the bridge lifetime to + * @panel: The drm_panel being wrapped. Must be non-NULL. + * + * This function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and update your driver to stop using it. + * + * This is the managed version of drm_panel_bridge_add() which automatically + * calls drm_panel_bridge_remove() when @dev is unbound. + */ +struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, + struct drm_panel *panel) +{ + if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) + return ERR_PTR(-EINVAL); + + return devm_drm_panel_bridge_add_typed(dev, panel, + panel->connector_type); +} +EXPORT_SYMBOL(devm_drm_panel_bridge_add); + +/** + * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and + * &drm_connector with an explicit connector type. + * @dev: device to tie the bridge lifetime to + * @panel: The drm_panel being wrapped. Must be non-NULL. + * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) + * + * This is just like devm_drm_panel_bridge_add(), but forces the connector type + * to @connector_type instead of infering it from the panel. + * + * This function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and update your driver to stop using it. + */ +struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, + struct drm_panel *panel, + u32 connector_type) +{ + struct drm_bridge **ptr, *bridge; + + ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + bridge = drm_panel_bridge_add_typed(panel, connector_type); + if (IS_ERR(bridge)) { + devres_free(ptr); + return bridge; + } + + *ptr = bridge; + devres_add(dev, ptr); + + return &panel->bridge; +} +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 function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and update your driver to stop using it. + * + * 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 (for + * legacy drivers not using DRM_BRIDGE_ATTACH_NO_CONNECTOR) + * @bridge: The drm_bridge. + * + * This function gives external access to the connector. + * + * Returns: Pointer to drm_connector + */ +struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge) +{ + struct drm_panel *panel; + + panel = drm_bridge_to_panel(bridge); + + return &panel->connector; +} +EXPORT_SYMBOL(drm_panel_bridge_connector); + +#ifdef CONFIG_OF +/** + * devm_drm_of_get_bridge - Return next bridge in the chain + * @dev: 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 + * + * This function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and convert your driver to + * of_drm_get_bridge_by_endpoint(). + * + * 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 pointer to the bridge if successful, or an error pointer + * otherwise. + */ +struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, + 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 = devm_drm_panel_bridge_add(dev, panel); + drm_panel_put(panel); + } + + 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 + * + * This function is deprecated. A drm_bridge is embedded into every panel. + * Don't use it in new drivers and convert your driver to + * of_drm_get_bridge_by_endpoint(). + * + * 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); + drm_panel_put(panel); + } + + return bridge; +} +EXPORT_SYMBOL(drmm_of_get_bridge); + +#endif + /** * drm_panel_init - initialize a panel * @panel: DRM panel @@ -86,6 +598,13 @@ void drm_panel_add(struct drm_panel *panel) mutex_lock(&panel_lock); list_add_tail(&panel->list, &panel_list); mutex_unlock(&panel_lock); + + panel->bridge.of_node = panel->dev->of_node; + panel->bridge.ops = DRM_BRIDGE_OP_MODES; + panel->bridge.type = panel->connector_type; + panel->bridge.pre_enable_prev_first = panel->prepare_prev_first; + + drm_bridge_add(&panel->bridge); } EXPORT_SYMBOL(drm_panel_add); @@ -97,6 +616,7 @@ EXPORT_SYMBOL(drm_panel_add); */ void drm_panel_remove(struct drm_panel *panel) { + drm_bridge_remove(&panel->bridge); mutex_lock(&panel_lock); list_del_init(&panel->list); mutex_unlock(&panel_lock); @@ -370,13 +890,6 @@ int drm_panel_get_modes(struct drm_panel *panel, } EXPORT_SYMBOL(drm_panel_get_modes); -static void __drm_panel_free(struct kref *kref) -{ - struct drm_panel *panel = container_of(kref, struct drm_panel, refcount); - - kfree(panel->container); -} - /** * drm_panel_get - Acquire a panel reference * @panel: DRM panel @@ -387,10 +900,8 @@ static void __drm_panel_free(struct kref *kref) */ struct drm_panel *drm_panel_get(struct drm_panel *panel) { - if (!panel) - return panel; - - kref_get(&panel->refcount); + if (panel) + drm_bridge_get(&panel->bridge); return panel; } @@ -406,7 +917,7 @@ EXPORT_SYMBOL(drm_panel_get); void drm_panel_put(struct drm_panel *panel) { if (panel) - kref_put(&panel->refcount, __drm_panel_free); + drm_bridge_put(&panel->bridge); } EXPORT_SYMBOL(drm_panel_put); @@ -429,8 +940,22 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, const struct drm_panel_funcs *funcs, int connector_type) { - void *container; + /* + * Struct embedding and offsets: + * + * |--------------- user container struct ------------| + * : |---------- struct drm_panel ------------| + * : : |----- struct drm_bridge ------| + * A B C + * + * B - A = offset (passed as argument) + * C - B = panel_bridge_offset + * C - A = alloc_bridge_offset + */ + const size_t panel_bridge_offset = offsetof(struct drm_panel, bridge); + const size_t alloc_bridge_offset = offset + panel_bridge_offset; struct drm_panel *panel; + void *container; int err; if (!funcs) { @@ -438,14 +963,16 @@ void *__devm_drm_panel_alloc(struct device *dev, size_t size, size_t offset, return ERR_PTR(-EINVAL); } - container = kzalloc(size, GFP_KERNEL); - if (!container) - return ERR_PTR(-ENOMEM); + container = __devm_drm_bridge_alloc(dev, size, alloc_bridge_offset, + &panel_bridge_bridge_funcs); + if (IS_ERR(container)) + return container; panel = container + offset; - panel->container = container; panel->funcs = funcs; - kref_init(&panel->refcount); + panel->bridge.of_node = dev->of_node; + + drm_panel_get(panel); err = devm_add_action_or_reset(dev, drm_panel_put_void, panel); if (err) @@ -498,6 +1025,69 @@ struct drm_panel *of_drm_find_panel(const struct device_node *np) return ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL(of_drm_find_panel); + +/** + * drm_of_find_panel_or_bridge - return connected panel or bridge device + * @np: device tree node containing encoder output ports + * @port: port in the device tree node + * @endpoint: endpoint in the device tree node + * @panel: pointer to hold returned drm_panel, must not be NULL. On success + * the caller must call drm_panel_put() when done with the panel + * @bridge: pointer to hold returned drm_bridge + * + * Given a DT node's port and endpoint number, find the connected node and + * return either the associated struct drm_panel or drm_bridge device. + * + * This function is deprecated and should not be used in new drivers. Use + * of_drm_get_bridge_by_endpoint() instead when not looking for a panel, or + * devm_drm_of_get_bridge() otherwise. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + if (WARN_ON(!panel)) + return -EINVAL; + + *panel = NULL; + if (bridge) + *bridge = NULL; + + /* + * of_graph_get_remote_node() produces a noisy error message if port + * node isn't found and the absence of the port is a legit case here, + * so at first we silently check whether a graph is present in the + * device-tree node. + */ + if (!of_graph_is_present(np)) + return -ENODEV; + + struct device_node *remote __free(device_node) = + of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + *panel = of_drm_find_panel(remote); + if (!IS_ERR(*panel)) + return 0; + + *panel = NULL; + + if (bridge) { + /* No panel found yet, check for a bridge next. */ + *bridge = of_drm_find_bridge(remote); + if (*bridge) + return 0; + + *bridge = NULL; + } + + return -EPROBE_DEFER; +} +EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); #endif /* diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c b/drivers/gpu/drm/drm_panel_backlight_quirks.c index e417c7533053..dcb91e12bac6 100644 --- a/drivers/gpu/drm/drm_panel_backlight_quirks.c +++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c @@ -5,7 +5,7 @@ #include <linux/export.h> #include <linux/module.h> #include <drm/drm_edid.h> -#include <drm/drm_utils.h> +#include <drm/drm_panel_quirks.h> struct drm_panel_match { enum dmi_field field; diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 3a218fb592ce..813a204cd201 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -12,7 +12,7 @@ #include <linux/export.h> #include <linux/module.h> #include <drm/drm_connector.h> -#include <drm/drm_utils.h> +#include <drm/drm_panel_quirks.h> #ifdef CONFIG_DMI diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index c23a5de27eff..f1d4da168545 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -208,7 +208,7 @@ #include <drm/drm_gem.h> #include <drm/drm_print.h> #include <drm/drm_syncobj.h> -#include <drm/drm_utils.h> +#include <drm/drm_timeout.h> #include "drm_internal.h" @@ -1193,39 +1193,6 @@ err_free_points: return timeout; } -/** - * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value - * - * @timeout_nsec: timeout nsec component in ns, 0 for poll - * - * Calculate the timeout in jiffies from an absolute time in sec/nsec. - */ -signed long drm_timeout_abs_to_jiffies(int64_t timeout_nsec) -{ - ktime_t abs_timeout, now; - u64 timeout_ns, timeout_jiffies64; - - /* make 0 timeout means poll - absolute 0 doesn't seem valid */ - if (timeout_nsec == 0) - return 0; - - abs_timeout = ns_to_ktime(timeout_nsec); - now = ktime_get(); - - if (!ktime_after(abs_timeout, now)) - return 0; - - timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now)); - - timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns); - /* clamp timeout to avoid infinite timeout */ - if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1) - return MAX_SCHEDULE_TIMEOUT - 1; - - return timeout_jiffies64 + 1; -} -EXPORT_SYMBOL(drm_timeout_abs_to_jiffies); - static int drm_syncobj_array_wait(struct drm_device *dev, struct drm_file *file_private, struct drm_syncobj_wait *wait, diff --git a/drivers/gpu/drm/drm_timeout.c b/drivers/gpu/drm/drm_timeout.c new file mode 100644 index 000000000000..1a823607c633 --- /dev/null +++ b/drivers/gpu/drm/drm_timeout.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: MIT +/* + * Timeout conversion helpers for wait ioctls. + * + * Copyright 2017 Red Hat + * Copyright 2016 Advanced Micro Devices, Inc. + */ + +#include <linux/export.h> +#include <linux/jiffies.h> +#include <linux/ktime.h> +#include <linux/math64.h> +#include <linux/sched.h> + +#include <drm/drm_timeout.h> + +/** + * drm_timeout_abs_to_jiffies - calculate jiffies timeout from absolute value + * @timeout_nsec: timeout nsec component in ns, 0 for poll + * + * Calculate the timeout in jiffies from an absolute time in ns. The result is + * clamped to MAX_SCHEDULE_TIMEOUT - 1 to avoid infinite timeouts. + * + * Returns: + * The number of jiffies until the given timeout, or the upper limit. + */ +signed long drm_timeout_abs_to_jiffies(s64 timeout_nsec) +{ + ktime_t abs_timeout, now; + u64 timeout_ns, timeout_jiffies64; + + /* make 0 timeout means poll - absolute 0 doesn't seem valid */ + if (timeout_nsec == 0) + return 0; + + abs_timeout = ns_to_ktime(timeout_nsec); + now = ktime_get(); + + if (!ktime_after(abs_timeout, now)) + return 0; + + timeout_ns = ktime_to_ns(ktime_sub(abs_timeout, now)); + + timeout_jiffies64 = nsecs_to_jiffies64(timeout_ns); + /* clamp timeout to avoid infinite timeout */ + if (timeout_jiffies64 >= MAX_SCHEDULE_TIMEOUT - 1) + return MAX_SCHEDULE_TIMEOUT - 1; + + return timeout_jiffies64 + 1; +} +EXPORT_SYMBOL(drm_timeout_abs_to_jiffies); + +/** + * drm_timeout_rel_to_jiffies - calculate jiffies timeout from relative value + * @timeout_nsec: relative timeout in ns, 0 for poll + * + * Calculate the timeout in jiffies from a relative timeout in ns, for drivers + * whose UAPI expresses a wait as a duration rather than as a deadline. + * + * The result is clamped to MAX_JIFFY_OFFSET. That keeps it positive once it is + * converted to the signed long taken by dma_fence_wait_timeout() and friends, + * which matters on 32-bit, and keeps it distinct from MAX_SCHEDULE_TIMEOUT so + * that a finite wait is never understood as an infinite one. + * + * It's strongly discouraged to use relative timeouts in uAPIs, as they do not + * survive a restarted ioctl. A signal-interrupted ioctl is re-entered with the + * same arguments, so the duration starts counting from zero again. New uAPIs + * should take an absolute deadline and use drm_timeout_abs_to_jiffies(). + * + * Returns: + * 0 if @timeout_nsec is 0. Otherwise the equivalent number of jiffies and + * clamped to MAX_JIFFY_OFFSET. + */ +unsigned long drm_timeout_rel_to_jiffies(u64 timeout_nsec) +{ + u64 secs; + u32 rem; + + /* Make 0 timeout means poll, as for the absolute variant. */ + if (timeout_nsec == 0) + return 0; + + /* + * As nsecs_to_jiffies64() does not guard against overflow, split + * the timeout into whole seconds and nanoseconds. This way + * nsecs_to_jiffies64() is always handed a value below a second. + */ + secs = div_u64_rem(timeout_nsec, NSEC_PER_SEC, &rem); + if (secs >= MAX_JIFFY_OFFSET / HZ) + return MAX_JIFFY_OFFSET; + + return min_t(u64, MAX_JIFFY_OFFSET, + secs_to_jiffies(secs) + nsecs_to_jiffies64(rem) + 1); +} +EXPORT_SYMBOL(drm_timeout_rel_to_jiffies); diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index e1c9e2946b8a..82c238829921 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -26,7 +26,6 @@ #include <drm/drm_crtc.h> #include <drm/drm_encoder.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include <drm/exynos_drm.h> @@ -147,7 +146,6 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np; struct exynos_dp_device *dp; dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), @@ -163,18 +161,6 @@ static int exynos_dp_probe(struct platform_device *pdev) */ platform_set_drvdata(pdev, dp); - /* This is for the backward compatibility. */ - np = of_parse_phandle(dev->of_node, "panel", 0); - if (np) { - dp->plat_data.panel = of_drm_find_panel(np); - - of_node_put(np); - if (IS_ERR(dp->plat_data.panel)) - return PTR_ERR(dp->plat_data.panel); - - goto out; - } - if (of_get_display_timings(dev->of_node)) { dp->plat_data.next_bridge = devm_drm_of_display_mode_bridge(dp->dev, dp->dev->of_node, @@ -189,19 +175,11 @@ static int exynos_dp_probe(struct platform_device *pdev) dp->plat_data.power_off = exynos_dp_poweroff; dp->plat_data.ops = &exynos_dp_ops; -out: dp->adp = analogix_dp_probe(dev, &dp->plat_data); - if (IS_ERR(dp->adp)) { - /* - * The driver core does not invoke remove() for failed probes, - * so release the probe-time panel reference here. - */ - if (dp->plat_data.panel) - drm_panel_put(dp->plat_data.panel); + if (IS_ERR(dp->adp)) return PTR_ERR(dp->adp); - } - if (dp->plat_data.panel || dp->plat_data.next_bridge) + if (dp->plat_data.next_bridge) return component_add(&pdev->dev, &exynos_dp_ops); else return analogix_dp_finish_probe(dp->adp); @@ -209,16 +187,6 @@ out: static void exynos_dp_remove(struct platform_device *pdev) { - struct exynos_dp_device *dp = platform_get_drvdata(pdev); - - /* - * Release the probe-time reference from of_drm_find_panel(). If bind - * ran, the panel_bridge holds a second reference that devm cleanup - * will release when the bridge is destroyed after remove() returns. - */ - if (dp->plat_data.panel) - drm_panel_put(dp->plat_data.panel); - component_del(&pdev->dev, &exynos_dp_ops); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 8641c9e84d4c..f412269d6323 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -10,9 +10,9 @@ #include <linux/fb.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_prime.h> diff --git a/drivers/gpu/drm/gma500/fbdev.c b/drivers/gpu/drm/gma500/fbdev.c index d1e93588234f..428912574f20 100644 --- a/drivers/gpu/drm/gma500/fbdev.c +++ b/drivers/gpu/drm/gma500/fbdev.c @@ -7,9 +7,9 @@ #include <linux/fb.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> #include "gem.h" diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index db0e36dd8722..219ee17de3f6 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -38,10 +38,10 @@ #include <linux/vga_switcheroo.h> #include <drm/clients/drm_client_setup.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/intel/display_parent_interface.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_gem.h> #include <drm/drm_gem_framebuffer_helper.h> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_wait.c b/drivers/gpu/drm/i915/gem/i915_gem_wait.c index 2893df65c359..872d4045813a 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_wait.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_wait.c @@ -7,6 +7,8 @@ #include <linux/dma-fence-chain.h> #include <linux/jiffies.h> +#include <drm/drm_timeout.h> + #include "gt/intel_engine.h" #include "gt/intel_rps.h" @@ -184,25 +186,12 @@ i915_gem_object_wait(struct drm_i915_gem_object *obj, return !timeout ? -ETIME : 0; } -static inline unsigned long nsecs_to_jiffies_timeout(const u64 n) -{ - /* nsecs_to_jiffies64() does not guard against overflow */ - if ((NSEC_PER_SEC % HZ) != 0 && - div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ) - return MAX_JIFFY_OFFSET; - - return min_t(u64, MAX_JIFFY_OFFSET, nsecs_to_jiffies64(n) + 1); -} - static unsigned long to_wait_timeout(s64 timeout_ns) { if (timeout_ns < 0) return MAX_SCHEDULE_TIMEOUT; - if (timeout_ns == 0) - return 0; - - return nsecs_to_jiffies_timeout(timeout_ns); + return drm_timeout_rel_to_jiffies(timeout_ns); } /** diff --git a/drivers/gpu/drm/imagination/pvr_fw.c b/drivers/gpu/drm/imagination/pvr_fw.c index c6c77b152342..c7988278df3f 100644 --- a/drivers/gpu/drm/imagination/pvr_fw.c +++ b/drivers/gpu/drm/imagination/pvr_fw.c @@ -278,37 +278,6 @@ pvr_fw_find_mmu_segment(struct pvr_device *pvr_dev, u32 addr, u32 size, void *fw return -EINVAL; } -static int -pvr_fw_create_fwif_connection_ctl(struct pvr_device *pvr_dev) -{ - struct drm_device *drm_dev = from_pvr_device(pvr_dev); - struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; - - fw_dev->fwif_connection_ctl = - pvr_fw_object_create_and_map_offset(pvr_dev, - fw_dev->fw_heap_info.config_offset + - PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET, - sizeof(*fw_dev->fwif_connection_ctl), - PVR_BO_FW_FLAGS_DEVICE_UNCACHED, - NULL, NULL, - &fw_dev->mem.fwif_connection_ctl_obj); - if (IS_ERR(fw_dev->fwif_connection_ctl)) { - drm_err(drm_dev, - "Unable to allocate FWIF connection control memory\n"); - return PTR_ERR(fw_dev->fwif_connection_ctl); - } - - return 0; -} - -static void -pvr_fw_fini_fwif_connection_ctl(struct pvr_device *pvr_dev) -{ - struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev; - - pvr_fw_object_unmap_and_destroy(fw_dev->mem.fwif_connection_ctl_obj); -} - static void fw_osinit_init(void *cpu_ptr, void *priv) { @@ -459,21 +428,34 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev) struct pvr_fw_mem *fw_mem = &fw_dev->mem; int err; + err = pvr_fw_object_create_offset(pvr_dev, + fw_dev->fw_heap_info.config_offset + + PVR_ROGUE_FWIF_CONNECTION_CTL_OFFSET, + sizeof(struct rogue_fwif_connection_ctl), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, + &fw_mem->fwif_connection_ctl_obj); + if (err) { + drm_err(drm_dev, + "Unable to allocate FWIF connection control memory\n"); + return err; + } + fw_dev->power_sync = pvr_fw_object_create_and_map(pvr_dev, sizeof(*fw_dev->power_sync), PVR_BO_FW_FLAGS_DEVICE_UNCACHED, NULL, NULL, &fw_mem->power_sync_obj); if (IS_ERR(fw_dev->power_sync)) { drm_err(drm_dev, "Unable to allocate FW power_sync structure\n"); - return PTR_ERR(fw_dev->power_sync); + err = PTR_ERR(fw_dev->power_sync); + goto err_release_connection_ctl; } - fw_dev->hwrinfobuf = pvr_fw_object_create_and_map(pvr_dev, sizeof(*fw_dev->hwrinfobuf), - PVR_BO_FW_FLAGS_DEVICE_UNCACHED, - NULL, NULL, &fw_mem->hwrinfobuf_obj); - if (IS_ERR(fw_dev->hwrinfobuf)) { + err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrinfobuf), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + NULL, NULL, &fw_mem->hwrinfobuf_obj); + if (err) { drm_err(drm_dev, "Unable to allocate FW hwrinfobuf structure\n"); - err = PTR_ERR(fw_dev->hwrinfobuf); goto err_release_power_sync; } @@ -536,16 +518,14 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev) goto err_fw_trace_fini; } - fw_dev->fwif_osinit = - pvr_fw_object_create_and_map_offset(pvr_dev, - fw_dev->fw_heap_info.config_offset + - PVR_ROGUE_FWIF_OSINIT_OFFSET, - sizeof(*fw_dev->fwif_osinit), - PVR_BO_FW_FLAGS_DEVICE_UNCACHED, - fw_osinit_init, pvr_dev, &fw_mem->osinit_obj); - if (IS_ERR(fw_dev->fwif_osinit)) { + err = pvr_fw_object_create_offset(pvr_dev, + fw_dev->fw_heap_info.config_offset + + PVR_ROGUE_FWIF_OSINIT_OFFSET, + sizeof(struct rogue_fwif_osinit), + PVR_BO_FW_FLAGS_DEVICE_UNCACHED, + fw_osinit_init, pvr_dev, &fw_mem->osinit_obj); + if (err) { drm_err(drm_dev, "Unable to allocate FW OSINIT structure\n"); - err = PTR_ERR(fw_dev->fwif_osinit); goto err_release_osdata; } @@ -565,7 +545,7 @@ pvr_fw_create_structures(struct pvr_device *pvr_dev) return 0; err_release_osinit: - pvr_fw_object_unmap_and_destroy(fw_mem->osinit_obj); + pvr_fw_object_destroy(fw_mem->osinit_obj); err_release_osdata: pvr_fw_object_unmap_and_destroy(fw_mem->osdata_obj); @@ -589,11 +569,14 @@ err_release_mmucache_sync_obj: pvr_fw_object_destroy(fw_mem->mmucache_sync_obj); err_release_hwrinfobuf: - pvr_fw_object_unmap_and_destroy(fw_mem->hwrinfobuf_obj); + pvr_fw_object_destroy(fw_mem->hwrinfobuf_obj); err_release_power_sync: pvr_fw_object_unmap_and_destroy(fw_mem->power_sync_obj); +err_release_connection_ctl: + pvr_fw_object_destroy(fw_mem->fwif_connection_ctl_obj); + return err; } @@ -611,10 +594,11 @@ pvr_fw_destroy_structures(struct pvr_device *pvr_dev) pvr_fw_object_unmap_and_destroy(fw_mem->sysinit_obj); pvr_fw_object_destroy(fw_mem->mmucache_sync_obj); - pvr_fw_object_unmap_and_destroy(fw_mem->hwrinfobuf_obj); + pvr_fw_object_destroy(fw_mem->hwrinfobuf_obj); pvr_fw_object_unmap_and_destroy(fw_mem->power_sync_obj); pvr_fw_object_unmap_and_destroy(fw_mem->osdata_obj); - pvr_fw_object_unmap_and_destroy(fw_mem->osinit_obj); + pvr_fw_object_destroy(fw_mem->osinit_obj); + pvr_fw_object_destroy(fw_mem->fwif_connection_ctl_obj); } /** @@ -755,10 +739,6 @@ pvr_fw_process(struct pvr_device *pvr_dev) pvr_fw_object_vunmap(fw_mem->code_obj); fw_code_ptr = NULL; - err = pvr_fw_create_fwif_connection_ctl(pvr_dev); - if (err) - goto err_free_kdata; - return 0; err_free_kdata: @@ -842,8 +822,6 @@ pvr_fw_cleanup(struct pvr_device *pvr_dev) { struct pvr_fw_mem *fw_mem = &pvr_dev->fw_dev.mem; - pvr_fw_fini_fwif_connection_ctl(pvr_dev); - kfree(fw_mem->core_data); kfree(fw_mem->core_code); kfree(fw_mem->data); @@ -1403,6 +1381,46 @@ pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, } /** + * pvr_fw_object_create_offset() - Create a FW object and map to firmware + * at the provided offset. + * @pvr_dev: PowerVR device pointer. + * @fw_addr: Base address of desired FW mapping, offset from start of FW heap. + * If a specific FW address is not required, pass %U32_MAX. + * @size: Size of object, in bytes. + * @flags: Options which affect both this operation and future mapping + * operations performed on the returned object. Must be a combination of + * DRM_PVR_BO_* and/or PVR_BO_* flags. + * @init: Initialisation callback. + * @init_priv: Private pointer to pass to initialisation callback. + * @fw_obj_out: Pointer to location to store created object pointer. + * + * %DRM_PVR_BO_DEVICE_PM_FW_PROTECT is implied for all FW objects. Consequently, + * this function will fail if @flags has %DRM_PVR_BO_CPU_ALLOW_USERSPACE_ACCESS + * set. + * + * Returns: + * * 0 on success, or + * * Any error returned by pvr_fw_object_create_and_map(). + */ +int +pvr_fw_object_create_offset(struct pvr_device *pvr_dev, u32 fw_addr, + size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **fw_obj_out) +{ + void *cpu_ptr; + + cpu_ptr = pvr_fw_object_create_and_map_offset(pvr_dev, fw_addr, size, flags, + init, init_priv, fw_obj_out); + if (IS_ERR(cpu_ptr)) + return PTR_ERR(cpu_ptr); + + pvr_fw_object_vunmap(*fw_obj_out); + + return 0; +} + +/** * pvr_fw_object_destroy() - Destroy a pvr_fw_object * @fw_obj: Pointer to object to destroy. */ diff --git a/drivers/gpu/drm/imagination/pvr_fw.h b/drivers/gpu/drm/imagination/pvr_fw.h index 4b25291135b6..626a1fdb6a4d 100644 --- a/drivers/gpu/drm/imagination/pvr_fw.h +++ b/drivers/gpu/drm/imagination/pvr_fw.h @@ -349,30 +349,18 @@ struct pvr_fw_device { /** @fw_mm_base: Base address of address space managed by @fw_mm. */ u64 fw_mm_base; - /** - * @fwif_connection_ctl: Pointer to CPU mapping of FWIF connection - * control structure. - */ - struct rogue_fwif_connection_ctl *fwif_connection_ctl; - /** @fwif_sysinit: Pointer to CPU mapping of FW SYSINIT structure. */ struct rogue_fwif_sysinit *fwif_sysinit; /** @fwif_sysdata: Pointer to CPU mapping of FW SYSDATA structure. */ struct rogue_fwif_sysdata *fwif_sysdata; - /** @fwif_osinit: Pointer to CPU mapping of FW OSINIT structure. */ - struct rogue_fwif_osinit *fwif_osinit; - /** @fwif_osdata: Pointer to CPU mapping of FW OSDATA structure. */ struct rogue_fwif_osdata *fwif_osdata; /** @power_sync: Pointer to CPU mapping of power sync state. */ u32 *power_sync; - /** @hwrinfobuf: Pointer to CPU mapping of FW HWR info buffer. */ - struct rogue_fwif_hwrinfobuf *hwrinfobuf; - /** @fw_trace: Device firmware trace buffer state. */ struct pvr_fw_trace fw_trace; @@ -430,6 +418,12 @@ void *pvr_fw_object_create_and_map(struct pvr_device *pvr_dev, size_t size, u64 void (*init)(void *cpu_ptr, void *priv), void *init_priv, struct pvr_fw_object **pvr_obj_out); +int +pvr_fw_object_create_offset(struct pvr_device *pvr_dev, u32 fw_addr, + size_t size, u64 flags, + void (*init)(void *cpu_ptr, void *priv), + void *init_priv, struct pvr_fw_object **pvr_obj_out); + void * pvr_fw_object_create_and_map_offset(struct pvr_device *pvr_dev, u32 dev_offset, size_t size, u64 flags, void (*init)(void *cpu_ptr, void *priv), diff --git a/drivers/gpu/drm/imx/dc/dc-kms.c b/drivers/gpu/drm/imx/dc/dc-kms.c index a9adcfc68b84..0e2ce6a42548 100644 --- a/drivers/gpu/drm/imx/dc/dc-kms.c +++ b/drivers/gpu/drm/imx/dc/dc-kms.c @@ -15,6 +15,7 @@ #include <drm/drm_encoder.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_mode_config.h> +#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> diff --git a/drivers/gpu/drm/imx/dcss/Kconfig b/drivers/gpu/drm/imx/dcss/Kconfig index e014ed3ae66c..d278bebfc33c 100644 --- a/drivers/gpu/drm/imx/dcss/Kconfig +++ b/drivers/gpu/drm/imx/dcss/Kconfig @@ -6,6 +6,7 @@ config DRM_IMX_DCSS select DRM_DISPLAY_HELPER select DRM_BRIDGE_CONNECTOR select DRM_GEM_DMA_HELPER + select DRM_PANEL select VIDEOMODE_HELPERS depends on DRM && ((ARCH_MXC && ARM64) || COMPILE_TEST) help diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c index c67fe80b8115..8f0a0d4cf61a 100644 --- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c +++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2020 Marian Cichy <M.Cichy@pengutronix.de> #include <drm/clients/drm_client_setup.h> +#include <drm/drm_atomic.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> #include <drm/drm_damage_helper.h> @@ -14,10 +15,11 @@ #include <drm/drm_gem_dma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_of.h> +#include <drm/drm_plane_helper.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> -#include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> +#include <drm/drm_vblank_helper.h> #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/dma-mapping.h> @@ -101,7 +103,9 @@ struct imx_lcdc { struct drm_device drm; - struct drm_simple_display_pipe pipe; + struct drm_plane plane; + struct drm_crtc crtc; + struct drm_encoder encoder; struct drm_connector *connector; void __iomem *base; @@ -134,14 +138,15 @@ static unsigned int imx_lcdc_get_format(unsigned int drm_format) } } -static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state, +static void imx_lcdc_update_hw_registers(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state, + struct drm_plane_state *new_state, bool mode_set) { - struct drm_crtc *crtc = &pipe->crtc; - struct drm_plane_state *new_state = pipe->plane.state; + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev); + const struct drm_display_mode *mode = &new_crtc_state->mode; struct drm_framebuffer *fb = new_state->fb; - struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev); u32 lpcr, lvcr, lhcr; u32 framesize; dma_addr_t addr; @@ -154,24 +159,24 @@ static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe, return; /* Disable PER clock to make register write possible */ - if (old_state && old_state->crtc && old_state->crtc->enabled) + if (old_crtc_state && old_crtc_state->enable) clk_disable_unprepare(lcdc->clk_per); /* Framesize */ - framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, crtc->mode.hdisplay >> 4) | - FIELD_PREP(IMX21LCDC_LSR_YMAX, crtc->mode.vdisplay); + framesize = FIELD_PREP(IMX21LCDC_LSR_XMAX, mode->hdisplay >> 4) | + FIELD_PREP(IMX21LCDC_LSR_YMAX, mode->vdisplay); writel(framesize, lcdc->base + IMX21LCDC_LSR); /* HSYNC */ - lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, crtc->mode.hsync_start - crtc->mode.hdisplay - 1) | - FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, crtc->mode.hsync_end - crtc->mode.hsync_start - 1) | - FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, crtc->mode.htotal - crtc->mode.hsync_end - 3); + lhcr = FIELD_PREP(IMX21LCDC_LHCR_HFPORCH, mode->hsync_start - mode->hdisplay - 1) | + FIELD_PREP(IMX21LCDC_LHCR_HWIDTH, mode->hsync_end - mode->hsync_start - 1) | + FIELD_PREP(IMX21LCDC_LHCR_HBPORCH, mode->htotal - mode->hsync_end - 3); writel(lhcr, lcdc->base + IMX21LCDC_LHCR); /* VSYNC */ - lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, crtc->mode.vsync_start - crtc->mode.vdisplay) | - FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, crtc->mode.vsync_end - crtc->mode.vsync_start) | - FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, crtc->mode.vtotal - crtc->mode.vsync_end); + lvcr = FIELD_PREP(IMX21LCDC_LVCR_VFPORCH, mode->vsync_start - mode->vdisplay) | + FIELD_PREP(IMX21LCDC_LVCR_VWIDTH, mode->vsync_end - mode->vsync_start) | + FIELD_PREP(IMX21LCDC_LVCR_VBPORCH, mode->vtotal - mode->vsync_end); writel(lvcr, lcdc->base + IMX21LCDC_LVCR); lpcr = readl(lcdc->base + IMX21LCDC_LPCR); @@ -183,19 +188,20 @@ static void imx_lcdc_update_hw_registers(struct drm_simple_display_pipe *pipe, writel(new_state->fb->pitches[0] / 4, lcdc->base + IMX21LCDC_LVPWR); /* Enable PER clock */ - if (new_state->crtc->enabled) + if (new_crtc_state->enable) clk_prepare_enable(lcdc->clk_per); } -static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe, - struct drm_crtc_state *crtc_state, - struct drm_plane_state *plane_state) +static void imx_lcdc_crtc_helper_atomic_enable(struct drm_crtc *crtc, + struct drm_atomic_commit *commit) { int ret; int clk_div; int bpp; - struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev); - struct drm_display_mode *mode = &pipe->crtc.mode; + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev); + struct drm_crtc_state *cstate = drm_atomic_get_new_crtc_state(commit, crtc); + struct drm_plane_state *pstate = drm_atomic_get_new_plane_state(commit, &lcdc->plane); + struct drm_display_mode *mode = &cstate->mode; struct drm_display_info *disp_info = &lcdc->connector->display_info; const int hsync_pol = (mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : 1; const int vsync_pol = (mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : 1; @@ -206,7 +212,7 @@ static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe, clk_div = DIV_ROUND_CLOSEST_ULL(clk_get_rate(lcdc->clk_per), mode->clock * 1000); - bpp = imx_lcdc_get_format(plane_state->fb->format->format); + bpp = imx_lcdc_get_format(pstate->fb->format->format); writel(FIELD_PREP(IMX21LCDC_LPCR_PCD, clk_div - 1) | FIELD_PREP(IMX21LCDC_LPCR_LPPOL, hsync_pol) | @@ -230,40 +236,46 @@ static void imx_lcdc_pipe_enable(struct drm_simple_display_pipe *pipe, ret = clk_prepare_enable(lcdc->clk_ipg); if (ret) { - dev_err(pipe->crtc.dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret)); + dev_err(crtc->dev->dev, "Cannot enable ipg clock: %pe\n", ERR_PTR(ret)); return; } ret = clk_prepare_enable(lcdc->clk_ahb); if (ret) { - dev_err(pipe->crtc.dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret)); + dev_err(crtc->dev->dev, "Cannot enable ahb clock: %pe\n", ERR_PTR(ret)); clk_disable_unprepare(lcdc->clk_ipg); return; } - imx_lcdc_update_hw_registers(pipe, NULL, true); + imx_lcdc_update_hw_registers(crtc, NULL, cstate, pstate, true); /* Enable VBLANK Interrupt */ writel(INTR_EOF, lcdc->base + IMX21LCDC_LIER); + + drm_crtc_vblank_on(crtc); } -static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe) +static void imx_lcdc_crtc_helper_atomic_disable(struct drm_crtc *crtc, + struct drm_atomic_commit *commit) { - struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(pipe->crtc.dev); - struct drm_crtc *crtc = &lcdc->pipe.crtc; + struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(commit, crtc); + struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(commit, crtc); + struct imx_lcdc *lcdc = imx_lcdc_from_drmdev(crtc->dev); struct drm_pending_vblank_event *event; + drm_crtc_vblank_off(crtc); + clk_disable_unprepare(lcdc->clk_ahb); clk_disable_unprepare(lcdc->clk_ipg); - if (pipe->crtc.enabled) + if (old_crtc_state->enable) clk_disable_unprepare(lcdc->clk_per); spin_lock_irq(&lcdc->drm.event_lock); - event = crtc->state->event; + event = new_crtc_state->event; if (event) { - crtc->state->event = NULL; + new_crtc_state->event = NULL; drm_crtc_send_vblank_event(crtc, event); } spin_unlock_irq(&lcdc->drm.event_lock); @@ -272,65 +284,132 @@ static void imx_lcdc_pipe_disable(struct drm_simple_display_pipe *pipe) writel(0, lcdc->base + IMX21LCDC_LIER); } -static int imx_lcdc_pipe_check(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state, - struct drm_crtc_state *crtc_state) +static int imx_lcdc_crtc_helper_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_commit *commit) { + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(commit, crtc); + struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(commit, crtc); const struct drm_display_mode *mode = &crtc_state->mode; - const struct drm_display_mode *old_mode = &pipe->crtc.state->mode; + const struct drm_display_mode *old_mode = &old_crtc_state->mode; + int ret; + + if (crtc_state->enable) { + ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state); + if (ret) + return ret; + } - if (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES || - mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES || - mode->hdisplay % 0x10) { /* must be multiple of 16 */ - drm_err(pipe->crtc.dev, "unsupported display mode (%u x %u)\n", + if (crtc_state->enable && + (mode->hdisplay < LCDC_MIN_XRES || mode->hdisplay > LCDC_MAX_XRES || + mode->vdisplay < LCDC_MIN_YRES || mode->vdisplay > LCDC_MAX_YRES || + mode->hdisplay % 0x10)) { /* must be multiple of 16 */ + drm_err(crtc->dev, "unsupported display mode (%u x %u)\n", mode->hdisplay, mode->vdisplay); return -EINVAL; } - crtc_state->mode_changed = - old_mode->hdisplay != mode->hdisplay || - old_mode->vdisplay != mode->vdisplay; + if (old_mode->hdisplay != mode->hdisplay || + old_mode->vdisplay != mode->vdisplay) + crtc_state->mode_changed = true; - return 0; + return drm_atomic_add_affected_planes(commit, crtc); } -static void imx_lcdc_pipe_update(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *old_state) +static void imx_lcdc_plane_helper_atomic_update(struct drm_plane *plane, + struct drm_atomic_commit *commit) { - struct drm_crtc *crtc = &pipe->crtc; - struct drm_pending_vblank_event *event = crtc->state->event; - struct drm_plane_state *new_state = pipe->plane.state; + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(commit, plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(commit, plane); + struct drm_crtc *crtc = new_state->crtc; + struct drm_crtc_state *old_crtc_state = NULL; + struct drm_crtc_state *new_crtc_state; struct drm_framebuffer *fb = new_state->fb; struct drm_framebuffer *old_fb = old_state->fb; struct drm_crtc *old_crtc = old_state->crtc; bool mode_changed = false; + if (!fb || !crtc) + return; + + if (old_crtc) + old_crtc_state = drm_atomic_get_old_crtc_state(commit, old_crtc); + + new_crtc_state = drm_atomic_get_new_crtc_state(commit, crtc); + if (!new_crtc_state) + return; + if (old_fb && old_fb->format != fb->format) mode_changed = true; else if (old_crtc != crtc) mode_changed = true; - imx_lcdc_update_hw_registers(pipe, old_state, mode_changed); + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) + imx_lcdc_update_hw_registers(crtc, old_crtc_state, new_crtc_state, + new_state, mode_changed); +} - if (event) { - crtc->state->event = NULL; +static int imx_lcdc_plane_helper_atomic_check(struct drm_plane *plane, + struct drm_atomic_commit *commit) +{ + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(commit, plane); + struct drm_crtc_state *crtc_state = NULL; - spin_lock_irq(&crtc->dev->event_lock); + if (plane_state->crtc) { + crtc_state = drm_atomic_get_crtc_state(commit, plane_state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + } - if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) - drm_crtc_arm_vblank_event(crtc, event); - else - drm_crtc_send_vblank_event(crtc, event); + return drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, false); +} - spin_unlock_irq(&crtc->dev->event_lock); - } +static const struct drm_plane_helper_funcs imx_lcdc_plane_helper_funcs = { + .prepare_fb = drm_gem_plane_helper_prepare_fb, + .atomic_check = imx_lcdc_plane_helper_atomic_check, + .atomic_update = imx_lcdc_plane_helper_atomic_update, +}; + +static const struct drm_plane_funcs imx_lcdc_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .atomic_create_state = drm_atomic_helper_plane_create_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static const struct drm_crtc_helper_funcs imx_lcdc_crtc_helper_funcs = { + .atomic_check = imx_lcdc_crtc_helper_atomic_check, + .atomic_enable = imx_lcdc_crtc_helper_atomic_enable, + .atomic_disable = imx_lcdc_crtc_helper_atomic_disable, + .atomic_flush = drm_crtc_vblank_atomic_flush, +}; + +static int imx_lcdc_crtc_enable_vblank(struct drm_crtc *crtc) +{ + return 0; } -static const struct drm_simple_display_pipe_funcs imx_lcdc_pipe_funcs = { - .enable = imx_lcdc_pipe_enable, - .disable = imx_lcdc_pipe_disable, - .check = imx_lcdc_pipe_check, - .update = imx_lcdc_pipe_update, +static void imx_lcdc_crtc_disable_vblank(struct drm_crtc *crtc) +{ +} + +static const struct drm_crtc_funcs imx_lcdc_crtc_funcs = { + .atomic_create_state = drm_atomic_helper_crtc_create_state, + .destroy = drm_crtc_cleanup, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .enable_vblank = imx_lcdc_crtc_enable_vblank, + .disable_vblank = imx_lcdc_crtc_disable_vblank, +}; + +static const struct drm_encoder_funcs imx_lcdc_encoder_funcs = { + .destroy = drm_encoder_cleanup, }; static const struct drm_mode_config_funcs imx_lcdc_mode_config_funcs = { @@ -368,7 +447,7 @@ MODULE_DEVICE_TABLE(of, imx_lcdc_of_dev_id); static irqreturn_t imx_lcdc_irq_handler(int irq, void *arg) { struct imx_lcdc *lcdc = arg; - struct drm_crtc *crtc = &lcdc->pipe.crtc; + struct drm_crtc *crtc = &lcdc->crtc; unsigned int status; status = readl(lcdc->base + IMX21LCDC_LISR); @@ -386,6 +465,9 @@ static int imx_lcdc_probe(struct platform_device *pdev) struct imx_lcdc *lcdc; struct drm_device *drm; struct drm_bridge *bridge; + struct drm_plane *plane; + struct drm_crtc *crtc; + struct drm_encoder *encoder; int irq; int ret; struct device *dev = &pdev->dev; @@ -427,23 +509,40 @@ static int imx_lcdc_probe(struct platform_device *pdev) if (ret) return dev_err_probe(dev, ret, "Cannot initialize mode configuration structure\n"); - /* CRTC, Plane, Encoder */ - ret = drm_simple_display_pipe_init(drm, &lcdc->pipe, - &imx_lcdc_pipe_funcs, - imx_lcdc_formats, - ARRAY_SIZE(imx_lcdc_formats), NULL, NULL); + plane = &lcdc->plane; + ret = drm_universal_plane_init(drm, plane, 0, + &imx_lcdc_plane_funcs, + imx_lcdc_formats, + ARRAY_SIZE(imx_lcdc_formats), + NULL, + DRM_PLANE_TYPE_PRIMARY, NULL); + if (ret < 0) + return dev_err_probe(drm->dev, ret, "Cannot initialize primary plane\n"); + drm_plane_helper_add(plane, &imx_lcdc_plane_helper_funcs); + + crtc = &lcdc->crtc; + ret = drm_crtc_init_with_planes(drm, crtc, plane, NULL, + &imx_lcdc_crtc_funcs, NULL); + if (ret < 0) + return dev_err_probe(drm->dev, ret, "Cannot initialize CRTC\n"); + drm_crtc_helper_add(crtc, &imx_lcdc_crtc_helper_funcs); + + encoder = &lcdc->encoder; + ret = drm_encoder_init(drm, encoder, &imx_lcdc_encoder_funcs, + DRM_MODE_ENCODER_NONE, NULL); if (ret < 0) - return dev_err_probe(drm->dev, ret, "Cannot setup simple display pipe\n"); + return dev_err_probe(drm->dev, ret, "Cannot initialize encoder\n"); + encoder->possible_crtcs = drm_crtc_mask(crtc); ret = drm_vblank_init(drm, drm->mode_config.num_crtc); if (ret < 0) return dev_err_probe(drm->dev, ret, "Failed to initialize vblank\n"); - ret = drm_bridge_attach(&lcdc->pipe.encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) return dev_err_probe(drm->dev, ret, "Cannot attach bridge\n"); - lcdc->connector = drm_bridge_connector_init(drm, &lcdc->pipe.encoder); + lcdc->connector = drm_bridge_connector_init(drm, encoder); if (IS_ERR(lcdc->connector)) return dev_err_probe(drm->dev, PTR_ERR(lcdc->connector), "Cannot init bridge connector\n"); @@ -482,7 +581,9 @@ static int imx_lcdc_probe(struct platform_device *pdev) drm->mode_config.funcs = &imx_lcdc_mode_config_funcs; drm->mode_config.helper_private = &imx_lcdc_mode_config_helpers; - drm_mode_config_reset(drm); + ret = drm_mode_config_create_initial_state(drm); + if (ret) + return dev_err_probe(dev, ret, "Cannot create initial state\n"); irq = platform_get_irq(pdev, 0); if (irq < 0) { diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig index 04ecfb0c5dd6..987ed42f6f7a 100644 --- a/drivers/gpu/drm/ingenic/Kconfig +++ b/drivers/gpu/drm/ingenic/Kconfig @@ -7,6 +7,7 @@ config DRM_INGENIC depends on COMMON_CLK select DRM_BRIDGE select DRM_CLIENT_SELECTION + select DRM_PANEL select DRM_PANEL_BRIDGE select DRM_KMS_HELPER select DRM_DISPLAY_HELPER diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c index 0c73860bb501..97e1dc541c2e 100644 --- a/drivers/gpu/drm/lima/lima_gem.c +++ b/drivers/gpu/drm/lima/lima_gem.c @@ -10,7 +10,7 @@ #include <drm/drm_file.h> #include <drm/drm_syncobj.h> -#include <drm/drm_utils.h> +#include <drm/drm_timeout.h> #include <drm/lima_drm.h> diff --git a/drivers/gpu/drm/logicvc/Kconfig b/drivers/gpu/drm/logicvc/Kconfig index 11aae1626199..95cdc5cb8cd5 100644 --- a/drivers/gpu/drm/logicvc/Kconfig +++ b/drivers/gpu/drm/logicvc/Kconfig @@ -5,6 +5,7 @@ config DRM_LOGICVC select DRM_CLIENT_SELECTION select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER + select DRM_PANEL select REGMAP select REGMAP_MMIO help diff --git a/drivers/gpu/drm/mcde/Kconfig b/drivers/gpu/drm/mcde/Kconfig index 3516c8d2a5d9..81e7d3796c06 100644 --- a/drivers/gpu/drm/mcde/Kconfig +++ b/drivers/gpu/drm/mcde/Kconfig @@ -9,7 +9,7 @@ config DRM_MCDE select DRM_CLIENT_SELECTION select DRM_MIPI_DSI select DRM_BRIDGE - select DRM_PANEL_BRIDGE + select DRM_PANEL select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER help diff --git a/drivers/gpu/drm/mcde/mcde_display.c b/drivers/gpu/drm/mcde/mcde_display.c index af1b8c466a02..281f5b3274bd 100644 --- a/drivers/gpu/drm/mcde/mcde_display.c +++ b/drivers/gpu/drm/mcde/mcde_display.c @@ -18,6 +18,7 @@ #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_bridge.h> #include <drm/drm_vblank.h> diff --git a/drivers/gpu/drm/mcde/mcde_dsi.c b/drivers/gpu/drm/mcde/mcde_dsi.c index ce078aa3910f..daac8d67913d 100644 --- a/drivers/gpu/drm/mcde/mcde_dsi.c +++ b/drivers/gpu/drm/mcde/mcde_dsi.c @@ -11,17 +11,15 @@ #include <linux/regulator/consumer.h> #include <video/mipi_display.h> -#include <drm/drm_atomic_helper.h> +#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_mipi_dsi.h> -#include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_print.h> -#include <drm/drm_probe_helper.h> #include "mcde_drm.h" #include "mcde_dsi_regs.h" @@ -39,7 +37,6 @@ struct mcde_dsi { struct device *dev; struct mcde *mcde; struct drm_bridge bridge; - struct drm_panel *panel; struct mipi_dsi_host dsi_host; struct mipi_dsi_device *mdsi; const struct drm_display_mode *mode; @@ -1076,7 +1073,6 @@ static int mcde_dsi_bind(struct device *dev, struct device *master, struct drm_device *drm = data; struct mcde *mcde = to_mcde(drm); struct mcde_dsi *d = dev_get_drvdata(dev); - struct drm_panel *panel = NULL; struct drm_bridge *bridge __free(drm_bridge_put) = NULL; if (!of_get_available_child_count(dev->of_node)) { @@ -1104,40 +1100,21 @@ static int mcde_dsi_bind(struct device *dev, struct device *master, /* Look for a panel as a child to this node */ for_each_available_child_of_node_scoped(dev->of_node, child) { - panel = of_drm_find_panel(child); - if (IS_ERR(panel)) { - dev_err(dev, "failed to find panel try bridge (%ld)\n", - PTR_ERR(panel)); - panel = NULL; - - bridge = of_drm_find_and_get_bridge(child); - if (!bridge) { - dev_err(dev, "failed to find bridge\n"); - return -EINVAL; - } + bridge = of_drm_find_and_get_bridge(child); + if (!bridge) { + dev_err(dev, "failed to find bridge\n"); + return -EINVAL; } - if (panel || bridge) - break; + break; } - if (panel) { - bridge = drm_panel_bridge_add_typed(panel, - DRM_MODE_CONNECTOR_DSI); - drm_panel_put(panel); - if (IS_ERR(bridge)) { - dev_err(dev, "error adding panel bridge\n"); - return PTR_ERR(bridge); - } - drm_bridge_get(bridge); - dev_info(dev, "connected to panel\n"); - d->panel = panel; - } else if (bridge) { + if (!bridge) { + dev_err(dev, "no bridge\n"); + return -ENODEV; + } else if (!drm_bridge_is_panel(bridge)) { /* TODO: AV8100 HDMI encoder goes here for example */ dev_info(dev, "connected to non-panel bridge (unsupported)\n"); return -ENODEV; - } else { - dev_err(dev, "no panel or bridge\n"); - return -ENODEV; } d->bridge.next_bridge = drm_bridge_get(bridge); @@ -1159,8 +1136,6 @@ static void mcde_dsi_unbind(struct device *dev, struct device *master, { struct mcde_dsi *d = dev_get_drvdata(dev); - if (d->panel) - drm_panel_bridge_remove(d->bridge.next_bridge); regmap_update_bits(d->prcmu, PRCM_DSI_SW_RESET, PRCM_DSI_SW_RESET_DSI0_SW_RESETN, 0); } diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 4dcbd9b99d06..6abe30bdff12 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -15,6 +15,7 @@ #include <drm/display/drm_dp_aux_bus.h> #include <drm/display/drm_hdmi_audio_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_panel.h> #include "msm_drv.h" #include "msm_kms.h" diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index 3c9f01ed6271..c726fc628a08 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -5,6 +5,8 @@ #include "dsi.h" +#include <drm/drm_panel.h> + bool msm_dsi_is_cmd_mode(struct msm_dsi *msm_dsi) { unsigned long host_flags = msm_dsi_host_get_mode_flags(msm_dsi->host); @@ -267,4 +269,3 @@ void msm_dsi_snapshot(struct msm_disp_state *disp_state, struct msm_dsi *msm_dsi msm_dsi_host_snapshot(disp_state, msm_dsi->host); msm_dsi_phy_snapshot(disp_state, msm_dsi->phy); } - diff --git a/drivers/gpu/drm/msm/msm_debugfs.c b/drivers/gpu/drm/msm/msm_debugfs.c index f12701e286ec..d05307bfa123 100644 --- a/drivers/gpu/drm/msm/msm_debugfs.c +++ b/drivers/gpu/drm/msm/msm_debugfs.c @@ -9,8 +9,8 @@ #include <linux/debugfs.h> #include <linux/fault-inject.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_debugfs.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_framebuffer.h> diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index dd6d6c507d77..f37cc3f97a21 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -6,9 +6,9 @@ #include <linux/fb.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_drv.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 8b4a500347fb..641691fc92c8 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -12,7 +12,10 @@ #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/io.h> +#include <linux/media-bus-format.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/pm_runtime.h> @@ -207,11 +210,41 @@ static void mxsfb_irq_uninstall(struct drm_device *dev) free_irq(mxsfb->irq, dev); } +static int mxsfb_of_bus_fmt(const struct device_node *ep, u32 *bus_format) +{ + u32 bus_width; + int ret; + + ret = of_property_read_u32(ep, "bus-width", &bus_width); + if (ret == -EINVAL) + return 0; + if (ret) + return ret; + + switch (bus_width) { + case 16: + *bus_format = MEDIA_BUS_FMT_RGB565_1X16; + break; + case 18: + *bus_format = MEDIA_BUS_FMT_RGB666_1X18; + break; + case 24: + *bus_format = MEDIA_BUS_FMT_RGB888_1X24; + break; + default: + return -EINVAL; + } + + return 0; +} + static int mxsfb_load(struct drm_device *drm, const struct mxsfb_devdata *devdata) { struct platform_device *pdev = to_platform_device(drm->dev); + struct device_node *np = pdev->dev.of_node; struct mxsfb_drm_private *mxsfb; + struct device_node *ep; int ret; mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL); @@ -238,6 +271,16 @@ static int mxsfb_load(struct drm_device *drm, if (IS_ERR(mxsfb->clk_disp_axi)) mxsfb->clk_disp_axi = NULL; + ep = of_graph_get_next_endpoint(np, NULL); + if (ep) { + ret = mxsfb_of_bus_fmt(ep, &mxsfb->bus_format); + of_node_put(ep); + if (ret) { + dev_err(drm->dev, "Invalid bus-width endpoint property\n"); + return ret; + } + } + ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); if (ret) return ret; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h index d160d921b25f..bdc47ce5be79 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.h +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -46,6 +46,8 @@ struct mxsfb_drm_private { struct drm_connector *connector; struct drm_bridge *bridge; + u32 bus_format; + bool crc_active; }; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_kms.c b/drivers/gpu/drm/mxsfb/mxsfb_kms.c index 6e82f8f76793..bbc2a2df9f16 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_kms.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_kms.c @@ -386,6 +386,18 @@ static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc, if (!bus_format) bus_format = MEDIA_BUS_FMT_RGB888_1X24; + /* + * Prefer the bus format derived from the OF graph endpoint "bus-width" + * property when available. Otherwise, use the bus format reported by + * the downstream bridge or panel. + * + * This supports mismatched display and interface bus widths, such as + * a 24-bit panel connected through an 18-bit interface or an 18-bit + * panel connected through a 24-bit interface. + */ + if (mxsfb->bus_format) + bus_format = mxsfb->bus_format; + mxsfb_crtc_mode_set_nofb(mxsfb, bridge_state, bus_format); /* Write cur_buf as well to avoid an initial corrupt frame */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index add19d479ebe..e91130f93db6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -40,7 +40,6 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_edid.h> #include <drm/drm_eld.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fixed.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 4c22c09c93d5..e3e685c931f1 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -204,7 +204,6 @@ struct omap_dss_device { struct dss_device *dss; struct drm_bridge *bridge; struct drm_bridge *next_bridge; - struct drm_panel *panel; struct list_head list; diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 6e9bc605ee22..36d37deac802 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -12,7 +12,6 @@ #include <linux/of_graph.h> #include <drm/drm_bridge.h> -#include <drm/drm_panel.h> #include "dss.h" #include "omapdss.h" @@ -20,40 +19,16 @@ int omapdss_device_init_output(struct omap_dss_device *out, struct drm_bridge *local_bridge) { - struct device_node *remote_node; + struct drm_bridge *bridge; int ret; - remote_node = of_graph_get_remote_node(out->dev->of_node, - out->of_port, 0); - if (!remote_node) { + bridge = of_drm_get_bridge_by_endpoint(out->dev->of_node, out->of_port, 0); + if (IS_ERR(bridge)) { dev_dbg(out->dev, "failed to find video sink\n"); return 0; } - out->panel = of_drm_find_panel(remote_node); - if (IS_ERR(out->panel)) - out->panel = NULL; - - if (!out->panel) - out->bridge = of_drm_find_and_get_bridge(remote_node); - - of_node_put(remote_node); - - if (out->panel) { - struct drm_bridge *bridge; - - bridge = drm_panel_bridge_add(out->panel); - drm_panel_put(out->panel); - if (IS_ERR(bridge)) { - dev_err(out->dev, - "unable to create panel bridge (%ld)\n", - PTR_ERR(bridge)); - ret = PTR_ERR(bridge); - goto error; - } - - out->bridge = drm_bridge_get(bridge); - } + out->bridge = bridge; if (local_bridge) { if (!out->bridge) { @@ -65,11 +40,6 @@ int omapdss_device_init_output(struct omap_dss_device *out, out->bridge = drm_bridge_get(local_bridge); } - if (!out->bridge) { - ret = -EPROBE_DEFER; - goto error; - } - return 0; error: @@ -79,10 +49,6 @@ error: void omapdss_device_cleanup_output(struct omap_dss_device *out) { - if (out->bridge && out->panel) - drm_panel_bridge_remove(out->next_bridge ? - out->next_bridge : out->bridge); - drm_bridge_put(out->next_bridge); drm_bridge_put(out->bridge); } diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index 9edc1b3f9f95..1c40c489ed72 100644 --- a/drivers/gpu/drm/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -6,10 +6,10 @@ #include <linux/seq_file.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_debugfs.h> #include <drm/drm_file.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_framebuffer.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 4881777642d2..e1dcc3cb7c55 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -7,9 +7,9 @@ #include <linux/fb.h> #include <drm/clients/drm_client_setup.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_drv.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 747f47347521..53a1fc2913fe 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_PANEL - bool + tristate depends on DRM + select DRM_KMS_HELPER help Panel registration and lookup framework. diff --git a/drivers/gpu/drm/panel/panel-ebbg-ft8719.c b/drivers/gpu/drm/panel/panel-ebbg-ft8719.c index fb9f9f42be4f..1652de70900f 100644 --- a/drivers/gpu/drm/panel/panel-ebbg-ft8719.c +++ b/drivers/gpu/drm/panel/panel-ebbg-ft8719.c @@ -54,8 +54,9 @@ static void ebbg_ft8719_reset(struct ebbg_ft8719 *ctx) usleep_range(15000, 16000); } -static int ebbg_ft8719_on(struct ebbg_ft8719 *ctx) +static int ebbg_ft8719_enable(struct drm_panel *panel) { + struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); struct mipi_dsi_device *dsi = ctx->dsi; struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; @@ -72,19 +73,20 @@ static int ebbg_ft8719_on(struct ebbg_ft8719 *ctx) return dsi_ctx.accum_err; } -static int ebbg_ft8719_off(struct ebbg_ft8719 *ctx) +static int ebbg_ft8719_disable(struct drm_panel *panel) { + struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); struct mipi_dsi_device *dsi = ctx->dsi; struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; - dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); mipi_dsi_usleep_range(&dsi_ctx, 10000, 11000); mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); mipi_dsi_msleep(&dsi_ctx, 90); - return dsi_ctx.accum_err; + return 0; } static int ebbg_ft8719_prepare(struct drm_panel *panel) @@ -98,12 +100,6 @@ static int ebbg_ft8719_prepare(struct drm_panel *panel) ebbg_ft8719_reset(ctx); - ret = ebbg_ft8719_on(ctx); - if (ret < 0) { - gpiod_set_value_cansleep(ctx->reset_gpio, 1); - return ret; - } - return 0; } @@ -111,7 +107,6 @@ static int ebbg_ft8719_unprepare(struct drm_panel *panel) { struct ebbg_ft8719 *ctx = to_ebbg_ft8719(panel); - ebbg_ft8719_off(ctx); gpiod_set_value_cansleep(ctx->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); @@ -153,6 +148,8 @@ static int ebbg_ft8719_get_modes(struct drm_panel *panel, static const struct drm_panel_funcs ebbg_ft8719_panel_funcs = { .prepare = ebbg_ft8719_prepare, + .enable = ebbg_ft8719_enable, + .disable = ebbg_ft8719_disable, .unprepare = ebbg_ft8719_unprepare, .get_modes = ebbg_ft8719_get_modes, }; @@ -198,6 +195,8 @@ static int ebbg_ft8719_probe(struct mipi_dsi_device *dsi) dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_CLOCK_NON_CONTINUOUS; + ctx->panel.prepare_prev_first = true; + ret = drm_panel_of_backlight(&ctx->panel); if (ret) return dev_err_probe(dev, ret, "Failed to get backlight\n"); @@ -244,4 +243,4 @@ module_mipi_dsi_driver(ebbg_ft8719_driver); MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>"); MODULE_DESCRIPTION("DRM driver for EBBG FT8719 video dsi panel"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 29f250e6e64d..5d7d696b7725 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -2084,6 +2084,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x3879, &delay_200_500_e80, "NT116WHM-N21"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x388b, &delay_200_500_e80, "NV116FH1-M31"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x388c, &delay_200_500_e80, "NV116FH1-M30"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x38ae, &delay_200_500_e50, "NV140WU2-M70"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x38ea, &delay_200_500_e50, "NV140FHM-T0A"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1130, &delay_200_500_e50, "N116BGE-EB2"), diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c index d4b383138488..bea5ca70c775 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9882t.c @@ -133,6 +133,207 @@ static const struct drm_dsc_config tianma_il79900a_dsc = { .flatness_min_qp = 3, }; +static int rpi_10inch_init(struct ili9882t *ili) +{ + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; + + mipi_dsi_usleep_range(&ctx, 5000, 5100); + + il79900a_switch_page(&ctx, 0x01); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x00, 0x42); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x01, 0x11); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x02, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x03, 0x00); + + mipi_dsi_dcs_write_seq_multi(&ctx, 0x04, 0x01); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x05, 0x11); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x06, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x07, 0x00); + + mipi_dsi_dcs_write_seq_multi(&ctx, 0x08, 0x80); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x09, 0x01); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0a, 0x71); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0c, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0e, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0d, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0f, 0x00); + + mipi_dsi_dcs_write_seq_multi(&ctx, 0x30, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x31, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x32, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x33, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x34, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x35, 0x01); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x36, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x37, 0x28); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x38, 0x29); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x39, 0x11); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3A, 0x13); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3B, 0x15); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3C, 0x17); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3D, 0x09); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3E, 0x0D); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3F, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x40, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x41, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x42, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x43, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x44, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x45, 0x02); + + mipi_dsi_dcs_write_seq_multi(&ctx, 0x48, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x49, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4A, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4B, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4C, 0x07); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4D, 0x01); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4E, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4F, 0x28); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x50, 0x29); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x51, 0x10); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x52, 0x12); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x53, 0x14); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x54, 0x16); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x55, 0x08); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x56, 0x0C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x57, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x58, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x59, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5A, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5B, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5C, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5D, 0x02); + + mipi_dsi_dcs_write_seq_multi(&ctx, 0x2C, 0xB0); + + il79900a_switch_page(&ctx, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x07, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x06, 0xD2); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x53, 0x1F); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5E, 0x40); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x84, 0x00); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x29, 0x3A); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x2A, 0x3B); + + il79900a_switch_page(&ctx, 0x03); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x20, 0x01); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x21, 0x3C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x22, 0xFA); + + il79900a_switch_page(&ctx, 0x12); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x87, 0x2C); + + il79900a_switch_page(&ctx, 0x05); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x61, 0x8F); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x6D, 0xBB); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x5A, 0x7C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x67, 0x7C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x54, 0x92); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x55, 0x92); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x40, 0xD2); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x46, 0x70); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x30, 0x47); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x7A, 0x89); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xA4, 0x0B); + + il79900a_switch_page(&ctx, 0x06); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xC0, 0x80); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xC1, 0x7); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xCA, 0x58); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xCB, 0x2); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xCE, 0x58); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xCF, 0x2); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x10, 0x0); + + il79900a_switch_page(&ctx, 0x0b); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x76, 0x55); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x78, 0x68); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x77, 0x0D); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x79, 0x60); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x7A, 0x10); + + il79900a_switch_page(&ctx, 0x08); + mipi_dsi_dcs_write_seq_multi(&ctx, + 0xE0, 0x00, 0x1E, 0x3F, + 0x68, 0x7D, 0x72, 0x86, + 0x89, 0x92, 0x8E, 0xA5, + 0xA8, 0xAA, 0xBD, 0xB8, + 0xBE, 0xC5, 0xD6, 0xCD, + 0xC3, 0xDA, 0xEB, 0xED); + mipi_dsi_dcs_write_seq_multi(&ctx, + 0xE1, 0x00, 0x1E, 0x3F, + 0x68, 0x7D, 0x72, 0x86, + 0x89, 0x92, 0x8E, 0xA5, + 0xA8, 0xAA, 0xBD, 0xB8, + 0xBE, 0xC5, 0xD6, 0xCD, + 0xC3, 0xDA, 0xEB, 0xED); + + il79900a_switch_page(&ctx, 0x14); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x85, 0xCC); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x87, 0x91); + + il79900a_switch_page(&ctx, 0x10); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x11, 0x3); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x12, 0x34); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x10, 0x1); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x13, 0x4); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x14, 0xBB); + + il79900a_switch_page(&ctx, 0x0b); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x94, 0x88); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x95, 0x23); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x96, 0x0B); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x97, 0x0B); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x98, 0xC6); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x99, 0xC6); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xA6, 0x47); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xA7, 0x77); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xA8, 0x5); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xA9, 0x5); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xAA, 0xBA); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xAB, 0xBA); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xBD, 0x92); + mipi_dsi_dcs_write_seq_multi(&ctx, 0xBE, 0xE0); + + il79900a_switch_page(&ctx, 0x0e); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0, 0x80); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x14, 0x3); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x12, 0x0); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x13, 0x4); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x11, 0x1); + + il79900a_switch_page(&ctx, 0x02); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x80, 0x11); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x82, 0x11); + il79900a_switch_page(&ctx, 0x12); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x82, 0x2C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x85, 0x2C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x87, 0x2C); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x88, 0xEC); + il79900a_switch_page(&ctx, 0x10); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x4, 0x1); + il79900a_switch_page(&ctx, 0x11); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0C, 0x80); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x3C, 0x80); + + il79900a_switch_page(&ctx, 0x06); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0E, 0x0); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x0E, 0x3F); + mipi_dsi_dcs_write_seq_multi(&ctx, 0x10, 0x0); + + il79900a_switch_page(&ctx, 0x00); + mipi_dsi_dcs_set_tear_on_multi(&ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); + + mipi_dsi_msleep(&ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(&ctx); + + mipi_dsi_msleep(&ctx, 20); + + return ctx.accum_err; +}; + static int starry_ili9882t_init(struct ili9882t *ili) { struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; @@ -670,6 +871,19 @@ static int ili9882t_enable(struct drm_panel *panel) return 0; } +static const struct drm_display_mode rpi_10inch_default_mode = { + .clock = 166666, + .hdisplay = 1200, + .hsync_start = 1200 + 28, + .hsync_end = 1200 + 28 + 10, + .htotal = 1200 + 28 + 10 + 30, + .vdisplay = 1920, + .vsync_start = 1920 + 238, + .vsync_end = 1920 + 238 + 4, + .vtotal = 1920 + 238 + 4 + 28, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + static const struct drm_display_mode starry_ili9882t_default_mode = { .clock = 165280, .hdisplay = 1200, @@ -696,6 +910,20 @@ static const struct drm_display_mode tianma_il79900a_default_mode = { .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, }; +static const struct panel_desc rpi_10inch_desc = { + .modes = &rpi_10inch_default_mode, + .bpc = 8, + .size = { + .width_mm = 135, + .height_mm = 216, + }, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, + .init = rpi_10inch_init, +}; + static const struct panel_desc starry_ili9882t_desc = { .modes = &starry_ili9882t_default_mode, .bpc = 8, @@ -787,7 +1015,7 @@ static int ili9882t_add(struct ili9882t *ili) if (IS_ERR(ili->pp1800)) return PTR_ERR(ili->pp1800); - ili->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + ili->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(ili->enable_gpio)) { dev_err(dev, "cannot get enable-gpios %ld\n", PTR_ERR(ili->enable_gpio)); @@ -808,6 +1036,7 @@ static int ili9882t_add(struct ili9882t *ili) ili->base.funcs = &ili9882t_funcs; ili->base.dev = &ili->dsi->dev; + ili->base.prepare_prev_first = true; return devm_drm_panel_add(dev, &ili->base); } @@ -846,6 +1075,9 @@ static int ili9882t_probe(struct mipi_dsi_device *dsi) } static const struct of_device_id ili9882t_of_match[] = { + { .compatible = "raspberrypi,dsi-10inch", + .data = &rpi_10inch_desc + }, { .compatible = "starry,ili9882t", .data = &starry_ili9882t_desc }, diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index c95964ba25a9..d7dbd7442919 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1593,6 +1593,31 @@ static const struct panel_desc boe_bp101wx1_100 = { .connector_type = DRM_MODE_CONNECTOR_LVDS, }; +static const struct display_timing boe_dv215fhm_r01_timing = { + .pixelclock = { 148500000, 148500000, 148500000 }, + .hactive = { 1920, 1920, 1920 }, + .hfront_porch = { 88, 88, 88 }, + .hback_porch = { 148, 148, 148 }, + .hsync_len = { 44, 44, 44 }, + .vactive = { 1080, 1080, 1080 }, + .vfront_porch = { 4, 4, 4 }, + .vback_porch = { 36, 36, 36 }, + .vsync_len = { 5, 5, 5 }, + .flags = DISPLAY_FLAGS_DE_HIGH, +}; + +static const struct panel_desc boe_dv215fhm_r01 = { + .timings = &boe_dv215fhm_r01_timing, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 476, + .height = 268, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct display_timing boe_ev121wxm_n10_1850_timing = { .pixelclock = { 69922000, 71000000, 72293000 }, .hactive = { 1280, 1280, 1280 }, @@ -5114,6 +5139,32 @@ static const struct panel_desc tpk_f10a_0102 = { }, }; +static const struct drm_display_mode truly_tft320240_229_e_mode = { + .clock = 6410, + .hdisplay = 320, + .hsync_start = 320 + 8, + .hsync_end = 320 + 8 + 39, + .htotal = 320 + 8 + 39 + 4, + .vdisplay = 240, + .vsync_start = 240 + 8, + .vsync_end = 240 + 8 + 8, + .vtotal = 240 + 8 + 8 + 4, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc truly_tft320240_229_e = { + .modes = &truly_tft320240_229_e_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 70, + .height = 53, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_DPI, +}; + static const struct display_timing urt_umsh_8596md_timing = { .pixelclock = { 33260000, 33260000, 33260000 }, .hactive = { 800, 800, 800 }, @@ -5760,6 +5811,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "boe,bp101wx1-100", .data = &boe_bp101wx1_100, }, { + .compatible = "boe,dv215fhm-r01", + .data = &boe_dv215fhm_r01, + }, { .compatible = "boe,ev121wxm-n10-1850", .data = &boe_ev121wxm_n10_1850, }, { @@ -6168,6 +6222,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "tpk,f10a-0102", .data = &tpk_f10a_0102, }, { + .compatible = "truly,tft320240-229-e", + .data = &truly_tft320240_229_e, + }, { .compatible = "urt,umsh-8596md-t", .data = &urt_umsh_8596md_parallel, }, { diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 96a66d673ba6..70c05210ea84 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -18,7 +18,7 @@ #include <drm/drm_ioctl.h> #include <drm/drm_print.h> #include <drm/drm_syncobj.h> -#include <drm/drm_utils.h> +#include <drm/drm_timeout.h> #include "panfrost_device.h" #include "panfrost_drv.h" diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 8cdba0c1a14b..1ca8aedd83f4 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -25,7 +25,6 @@ #include <drm/drm_ioctl.h> #include <drm/drm_print.h> #include <drm/drm_syncobj.h> -#include <drm/drm_utils.h> #include <drm/gpu_scheduler.h> #include <drm/panthor_drm.h> diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig index 82e918820950..20201ec20aa7 100644 --- a/drivers/gpu/drm/pl111/Kconfig +++ b/drivers/gpu/drm/pl111/Kconfig @@ -9,6 +9,7 @@ config DRM_PL111 select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER select DRM_BRIDGE + select DRM_PANEL select DRM_PANEL_BRIDGE help Choose this option for DRM support for the PL111 CLCD controller. diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 65dc1efb26ad..9e4dc4531c90 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -33,11 +33,11 @@ #include <linux/vga_switcheroo.h> #include <linux/vgaarb.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_cache.h> #include <drm/drm_client_event.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_device.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_file.h> #include <drm/drm_framebuffer.h> #include <drm/drm_probe_helper.h> diff --git a/drivers/gpu/drm/radeon/radeon_fbdev.c b/drivers/gpu/drm/radeon/radeon_fbdev.c index 3e243f5e2f44..e40c23d9dff2 100644 --- a/drivers/gpu/drm/radeon/radeon_fbdev.c +++ b/drivers/gpu/drm/radeon/radeon_fbdev.c @@ -29,9 +29,9 @@ #include <linux/pm_runtime.h> #include <linux/vga_switcheroo.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> diff --git a/drivers/gpu/drm/renesas/rz-du/Kconfig b/drivers/gpu/drm/renesas/rz-du/Kconfig index 7f2ef7137ae5..0dbe86bd65d0 100644 --- a/drivers/gpu/drm/renesas/rz-du/Kconfig +++ b/drivers/gpu/drm/renesas/rz-du/Kconfig @@ -26,3 +26,16 @@ config DRM_RZG2L_MIPI_DSI def_tristate DRM_RZG2L_DU depends on DRM_RZG2L_USE_MIPI_DSI select DRM_MIPI_DSI + +config DRM_RZG3L_USE_LVDS + bool "RZ/G3L DU LVDS Encoder Support" + depends on DRM_BRIDGE && OF + depends on DRM_RZG2L_DU || COMPILE_TEST + default DRM_RZG2L_DU + help + Enable support for the RZ/G3L Display Unit embedded LVDS encoder. + +config DRM_RZG3L_LVDS + def_tristate DRM_RZG2L_DU + depends on DRM_RZG3L_USE_LVDS + select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/renesas/rz-du/Makefile b/drivers/gpu/drm/renesas/rz-du/Makefile index 2987900ea6b6..46decb7ac4f1 100644 --- a/drivers/gpu/drm/renesas/rz-du/Makefile +++ b/drivers/gpu/drm/renesas/rz-du/Makefile @@ -8,3 +8,4 @@ rzg2l-du-drm-$(CONFIG_VIDEO_RENESAS_VSP1) += rzg2l_du_vsp.o obj-$(CONFIG_DRM_RZG2L_DU) += rzg2l-du-drm.o obj-$(CONFIG_DRM_RZG2L_MIPI_DSI) += rzg2l_mipi_dsi.o +obj-$(CONFIG_DRM_RZG3L_LVDS) += rzg3l_lvds.o diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c index 3d13f61d3c97..6c2dc86abc7d 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c @@ -55,6 +55,26 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = { .mode_clock_max = 83500, }; +static const struct rzg2l_du_device_info rzg2l_du_r9a08g046_info = { + .channels_mask = BIT(0), + .routes = { + [RZG2L_DU_OUTPUT_DSI0] = { + .possible_outputs = BIT(0), + .port = 0, + }, + [RZG2L_DU_OUTPUT_DPAD0] = { + .possible_outputs = BIT(0), + .port = 1, + }, + [RZG2L_DU_OUTPUT_LVDS0] = { + .possible_outputs = BIT(0), + .port = 2, + }, + }, + .mode_clock_min = 5440, + .mode_clock_max = 87000, +}; + static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = { .channels_mask = BIT(0), .routes = { @@ -81,6 +101,7 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a09g077_info = { static const struct of_device_id rzg2l_du_of_table[] = { { .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info }, { .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info }, + { .compatible = "renesas,r9a08g046-du", .data = &rzg2l_du_r9a08g046_info }, { .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info }, { .compatible = "renesas,r9a09g077-du", .data = &rzg2l_du_r9a09g077_info }, { /* sentinel */ } @@ -92,7 +113,8 @@ const char *rzg2l_du_output_name(enum rzg2l_du_output output) { static const char * const names[] = { [RZG2L_DU_OUTPUT_DSI0] = "DSI0", - [RZG2L_DU_OUTPUT_DPAD0] = "DPAD0" + [RZG2L_DU_OUTPUT_DPAD0] = "DPAD0", + [RZG2L_DU_OUTPUT_LVDS0] = "LVDS0" }; if (output >= ARRAY_SIZE(names)) diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h index baf076d69cda..e9626ef37cde 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h @@ -25,6 +25,7 @@ struct drm_property; enum rzg2l_du_output { RZG2L_DU_OUTPUT_DSI0, RZG2L_DU_OUTPUT_DPAD0, + RZG2L_DU_OUTPUT_LVDS0, RZG2L_DU_OUTPUT_MAX, }; @@ -33,7 +34,7 @@ enum rzg2l_du_output { * @possible_outputs: bitmask of possible outputs * @port: device tree port number corresponding to this output route * - * The DU has 2 possible outputs (DPAD0, DSI0). Output routing data + * The DU has 3 possible outputs (DPAD0, DSI0, LVDS0). Output routing data * specify the valid SoC outputs, which CRTC can drive the output, and the type * of in-SoC encoder for the output. */ diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c index 3d0999e4fcfd..a1b48973aa8e 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c @@ -7,6 +7,7 @@ * Based on rcar_du_encoder.c */ +#include <linux/clk.h> #include <linux/export.h> #include <linux/of.h> @@ -15,6 +16,7 @@ #include <drm/drm_bridge_connector.h> #include <drm/drm_panel.h> +#include "rzg2l_du_crtc.h" #include "rzg2l_du_drv.h" #include "rzg2l_du_encoder.h" @@ -64,8 +66,30 @@ rzg2l_du_encoder_mode_valid(struct drm_encoder *encoder, return MODE_OK; } +static void rzg2l_du_encoder_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct rzg2l_du_encoder *renc = to_rzg2l_encoder(encoder); + struct rzg2l_du_crtc *rcrtc = to_rzg2l_crtc(crtc_state->crtc); + struct clk *clk_parent; + + clk_parent = clk_get_parent(rcrtc->rzg2l_clocks.dclk); + + /* + * Request appropriate duty cycle + * - LVDS path has DUTY H/L=4/3, 4/7 duty cycle. + * - DSI/RGB path has symmetric 50% duty cycle. + */ + if (renc->output == RZG2L_DU_OUTPUT_LVDS0) + clk_set_duty_cycle(clk_parent, 4, 7); + else + clk_set_duty_cycle(clk_parent, 1, 2); +} + static const struct drm_encoder_helper_funcs rzg2l_du_encoder_helper_funcs = { .mode_valid = rzg2l_du_encoder_mode_valid, + .atomic_mode_set = rzg2l_du_encoder_atomic_mode_set, }; int rzg2l_du_encoder_init(struct rzg2l_du_device *rcdu, diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index 0590ade96b91..5b257f5adf06 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -18,6 +18,7 @@ #include <linux/of_graph.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/pwrseq/consumer.h> #include <linux/reset.h> #include <linux/slab.h> #include <linux/units.h> @@ -38,6 +39,7 @@ MODULE_IMPORT_NS("RZV2H_CPG"); #define RZG2L_DCS_BUF_SIZE 128 /* Maximum DCS buffer size in external memory. */ #define RZ_MIPI_DSI_FEATURE_16BPP BIT(0) +#define RZ_MIPI_DSI_FEATURE_PWRRDY BIT(1) struct rzg2l_mipi_dsi; @@ -54,10 +56,14 @@ struct rzg2l_mipi_dsi_hw_info { const u8 *table; const u8 table_size; } cpg_plldsi; + const struct rzg2l_mipi_dsi_timings *dsi_global_timings; + unsigned int num_dsi_global_timings; u32 phy_reg_offset; u32 link_reg_offset; + u32 dphyctrl0_init_val; unsigned long min_dclk; unsigned long max_dclk; + u16 activation_dly; u8 features; }; @@ -83,6 +89,8 @@ struct rzg2l_mipi_dsi { struct clk *vclk; struct clk *lpclk; + struct pwrseq_desc *pwrseq; + enum mipi_dsi_pixel_format format; unsigned int num_data_lanes; unsigned int lanes; @@ -218,6 +226,107 @@ static const struct rzg2l_mipi_dsi_timings rzg2l_mipi_dsi_global_timings[] = { }, }; +static const struct rzg2l_mipi_dsi_timings rzg3l_mipi_dsi_global_timings[] = { + { + .hsfreq_max = 100000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 18, + .tclk_zero = 35, + .tclk_pre = 13, + .tclk_post = 94, + .tclk_trail = 10, + .ths_zero = 16, + .ths_trail = 22, + .ths_exit = 15, + .tlpx = 9, + }, + { + .hsfreq_max = 150000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 16, + .tclk_zero = 35, + .tclk_pre = 13, + .tclk_post = 94, + .tclk_trail = 10, + .ths_zero = 16, + .ths_trail = 15, + .ths_exit = 15, + .tlpx = 9, + }, + { + .hsfreq_max = 250000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 13, + .tclk_zero = 35, + .tclk_pre = 13, + .tclk_post = 58, + .tclk_trail = 8, + .ths_zero = 16, + .ths_trail = 10, + .ths_exit = 15, + .tlpx = 9, + }, + { + .hsfreq_max = 400000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 12, + .tclk_zero = 35, + .tclk_pre = 4, + .tclk_post = 58, + .tclk_trail = 7, + .ths_zero = 16, + .ths_trail = 9, + .ths_exit = 15, + .tlpx = 9, + }, + { + .hsfreq_max = 600000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 11, + .tclk_zero = 35, + .tclk_pre = 4, + .tclk_post = 35, + .tclk_trail = 5, + .ths_zero = 16, + .ths_trail = 6, + .ths_exit = 15, + .tlpx = 9, + }, + { + .hsfreq_max = 1000000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 11, + .tclk_zero = 35, + .tclk_pre = 4, + .tclk_post = 35, + .tclk_trail = 5, + .ths_zero = 16, + .ths_trail = 6, + .ths_exit = 15, + .tlpx = 9, + }, + { + .hsfreq_max = 1500000000, + .t_init = 79801, + .tclk_prepare = 10, + .ths_prepare = 11, + .tclk_zero = 35, + .tclk_pre = 4, + .tclk_post = 35, + .tclk_trail = 4, + .ths_zero = 16, + .ths_trail = 5, + .ths_exit = 15, + .tlpx = 9, + }, +}; + /** * struct rzv2h_mipi_dsi_timings - Timing parameter table structure * @@ -486,16 +595,14 @@ static int rzg2l_mipi_dsi_dphy_init(struct rzg2l_mipi_dsi *dsi, u32 dphytim3; /* All DSI global operation timings are set with recommended setting */ - for (i = 0; i < ARRAY_SIZE(rzg2l_mipi_dsi_global_timings); ++i) { - dphy_timings = &rzg2l_mipi_dsi_global_timings[i]; + for (i = 0; i < dsi->info->num_dsi_global_timings; ++i) { + dphy_timings = &dsi->info->dsi_global_timings[i]; if (hsfreq <= dphy_timings->hsfreq_max) break; } /* Initializing DPHY before accessing LINK */ - dphyctrl0 = DSIDPHYCTRL0_CAL_EN_HSRX_OFS | DSIDPHYCTRL0_CMN_MASTER_EN | - DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 | DSIDPHYCTRL0_EN_BGR; - + dphyctrl0 = dsi->info->dphyctrl0_init_val; rzg2l_mipi_dsi_phy_write(dsi, DSIDPHYCTRL0, dphyctrl0); usleep_range(20, 30); @@ -807,7 +914,7 @@ static int rzg2l_mipi_dsi_startup(struct rzg2l_mipi_dsi *dsi, if (ret < 0) goto err_phy; - fsleep(1000); + fsleep(dsi->info->activation_dly); } return 0; @@ -1394,6 +1501,25 @@ static const struct dev_pm_ops rzg2l_mipi_pm_ops = { * Probe & Remove */ +static int rzg2l_mipi_dsi_pwrrdy_init(struct rzg2l_mipi_dsi *dsi) +{ + if (!(dsi->info->features & RZ_MIPI_DSI_FEATURE_PWRRDY)) + return 0; + + dsi->pwrseq = devm_pwrseq_get(dsi->dev, "dsi-pwrrdy"); + if (IS_ERR(dsi->pwrseq)) { + /* + * This platform requires a sequencer. If we can't get it, we + * must return the error (including -EPROBE_DEFER to wait for + * the provider to appear) + */ + return dev_err_probe(dsi->dev, PTR_ERR(dsi->pwrseq), + "Failed to get required power sequencer\n"); + } + + return pwrseq_enable(dsi->pwrseq); +} + static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) { unsigned int num_data_lanes; @@ -1452,6 +1578,10 @@ static int rzg2l_mipi_dsi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dsi); + ret = rzg2l_mipi_dsi_pwrrdy_init(dsi); + if (ret) + return ret; + pm_runtime_enable(dsi->dev); ret = pm_runtime_resume_and_get(dsi->dev); @@ -1530,12 +1660,32 @@ static const struct rzg2l_mipi_dsi_hw_info rzg2l_mipi_dsi_info = { .dphy_init = rzg2l_mipi_dsi_dphy_init, .dphy_exit = rzg2l_mipi_dsi_dphy_exit, .dphy_conf_clks = rzg2l_dphy_conf_clks, + .dsi_global_timings = rzg2l_mipi_dsi_global_timings, + .num_dsi_global_timings = ARRAY_SIZE(rzg2l_mipi_dsi_global_timings), .link_reg_offset = 0x10000, + .dphyctrl0_init_val = DSIDPHYCTRL0_CAL_EN_HSRX_OFS | DSIDPHYCTRL0_CMN_MASTER_EN | + DSIDPHYCTRL0_RE_VDD_DETVCCQLV18 | DSIDPHYCTRL0_EN_BGR, .min_dclk = 5803, .max_dclk = 148500, + .activation_dly = 1000, +}; + +static const struct rzg2l_mipi_dsi_hw_info rzg3l_mipi_dsi_info = { + .dphy_init = rzg2l_mipi_dsi_dphy_init, + .dphy_exit = rzg2l_mipi_dsi_dphy_exit, + .dphy_conf_clks = rzg2l_dphy_conf_clks, + .dsi_global_timings = rzg3l_mipi_dsi_global_timings, + .num_dsi_global_timings = ARRAY_SIZE(rzg3l_mipi_dsi_global_timings), + .link_reg_offset = 0x10000, + .dphyctrl0_init_val = DSIDPHYCTRL0_CMN_MASTER_EN | DSIDPHYCTRL0_EN_BGR, + .min_dclk = 5440, + .max_dclk = 187500, + .activation_dly = 100, + .features = RZ_MIPI_DSI_FEATURE_16BPP | RZ_MIPI_DSI_FEATURE_PWRRDY, }; static const struct of_device_id rzg2l_mipi_dsi_of_table[] = { + { .compatible = "renesas,r9a08g046-mipi-dsi", .data = &rzg3l_mipi_dsi_info, }, { .compatible = "renesas,r9a09g057-mipi-dsi", .data = &rzv2h_mipi_dsi_info, }, { .compatible = "renesas,rzg2l-mipi-dsi", .data = &rzg2l_mipi_dsi_info, }, { /* sentinel */ } diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c new file mode 100644 index 000000000000..99ad6445d359 --- /dev/null +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G3L LVDS Encoder Driver + * + * Copyright (C) 2026 Renesas Electronics Corporation + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> + +#include "rzg3l_lvds_regs.h" + +enum rzg3l_lvds_mode { + RZG3L_LVDS_MODE_JEIDA = 0, + RZG3L_LVDS_MODE_JEIDA_MIRROR = 1, + RZG3L_LVDS_MODE_MODE2 = 2, + RZG3L_LVDS_MODE_MODE2_MIRROR = 3, + RZG3L_LVDS_MODE_VESA = 4, + RZG3L_LVDS_MODE_VESA_MIRROR = 5, + RZG3L_LVDS_MODE_MODE6 = 6, + RZG3L_LVDS_MODE_MODE6_MIRROR = 7, +}; + +struct rzg3l_lvds { + struct device *dev; + struct reset_control_bulk_data resets[2]; + struct reset_control_bulk_data dsi_resets[2]; + struct regmap *regmap; + struct drm_bridge bridge; +}; + +#define bridge_to_rzg3l_lvds(b) \ + container_of(b, struct rzg3l_lvds, bridge) + +static const struct regmap_config rzg3l_lvds_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LVDS_0_CTL_OFFSET, +}; + +/* ----------------------------------------------------------------------------- + * Bridge + */ + +static void rzg3l_lvds_atomic_enable(struct drm_bridge *bridge, + struct drm_atomic_commit *state) +{ + struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge); + const struct drm_bridge_state *bridge_state; + u32 fmt; + + if (WARN_ON(pm_runtime_get_sync(lvds->dev) < 0)) + return; + + /* Get the LVDS format from the bridge state. */ + bridge_state = drm_atomic_get_new_bridge_state(state, bridge); + if (WARN_ON(!bridge_state)) + return; + + switch (bridge_state->output_bus_cfg.format) { + case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA: + fmt = RZG3L_LVDS_MODE_JEIDA; + break; + case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG: + fmt = RZG3L_LVDS_MODE_VESA; + break; + default: + fmt = RZG3L_LVDS_MODE_VESA; + dev_warn(lvds->dev, "Unsupported bus fmt 0x%04x\n", + bridge_state->output_bus_cfg.format); + break; + } + + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET, + LVDS_0_PHY_CH_EN_BGR, LVDS_0_PHY_CH_EN_BGR); + fsleep(20); + + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET, + LVDS_0_PHY_CH_EN_LDO, LVDS_0_PHY_CH_EN_LDO); + fsleep(10); + + regmap_write(lvds->regmap, LVDS_CMN, LVDS_CMN_RST_PHY0_SEL); + regmap_update_bits(lvds->regmap, LVDS_0_CTL_OFFSET, + LVDS_0_CTL_FMT_SEL0_MSK, + FIELD_PREP(LVDS_0_CTL_FMT_SEL0_MSK, fmt)); + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET, + LVDS_0_PHY_CH_IO_EN0_MSK, LVDS_0_PHY_CH_IO_EN0); + regmap_write(lvds->regmap, LVDS_CMN, + LVDS_CMN_RST_PHY0_SEL | LVDS_CMN_PHY_RESET); + fsleep(100); +} + +static void rzg3l_lvds_atomic_disable(struct drm_bridge *bridge, + struct drm_atomic_commit *state) +{ + struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge); + + { + PM_RUNTIME_ACQUIRE_IF_ENABLED(lvds->dev, pm); + if (!PM_RUNTIME_ACQUIRE_ERR(&pm)) { + regmap_update_bits(lvds->regmap, LVDS_CMN, + LVDS_CMN_PHY_RESET, 0); + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET, + LVDS_0_PHY_CH_IO_EN0_MSK, 0); + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET, + LVDS_0_PHY_CH_EN_LDO, 0); + regmap_update_bits(lvds->regmap, LVDS_0_PHY_OFFSET, + LVDS_0_PHY_CH_EN_BGR, 0); + } + } + + pm_runtime_put_sync(lvds->dev); +} + +static int rzg3l_lvds_attach(struct drm_bridge *bridge, + struct drm_encoder *encoder, + enum drm_bridge_attach_flags flags) +{ + struct rzg3l_lvds *lvds = bridge_to_rzg3l_lvds(bridge); + + return drm_bridge_attach(encoder, lvds->bridge.next_bridge, bridge, flags); +} + +static enum drm_mode_status +rzg3l_lvds_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + if (mode->clock > 87000) + return MODE_CLOCK_HIGH; + + if (mode->clock < 25000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static const struct drm_bridge_funcs rzg3l_lvds_bridge_ops = { + .attach = rzg3l_lvds_attach, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_create_state = drm_atomic_helper_bridge_create_state, + .atomic_enable = rzg3l_lvds_atomic_enable, + .atomic_disable = rzg3l_lvds_atomic_disable, + .mode_valid = rzg3l_lvds_bridge_mode_valid, +}; + +/* ----------------------------------------------------------------------------- + * Power Management + */ + +static int rzg3l_lvds_pm_runtime_suspend(struct device *dev) +{ + struct rzg3l_lvds *lvds = dev_get_drvdata(dev); + + return reset_control_bulk_assert(ARRAY_SIZE(lvds->resets), lvds->resets); +} + +static int rzg3l_lvds_pm_runtime_resume(struct device *dev) +{ + struct rzg3l_lvds *lvds = dev_get_drvdata(dev); + + return reset_control_bulk_deassert(ARRAY_SIZE(lvds->resets), lvds->resets); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(rzg3l_lvds_pm_ops, + rzg3l_lvds_pm_runtime_suspend, + rzg3l_lvds_pm_runtime_resume, NULL); + +/* ----------------------------------------------------------------------------- + * Probe & Remove + */ + +static int rzg3l_lvds_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rzg3l_lvds *lvds; + void __iomem *base; + int ret; + + lvds = devm_drm_bridge_alloc(dev, struct rzg3l_lvds, bridge, + &rzg3l_lvds_bridge_ops); + if (IS_ERR(lvds)) + return PTR_ERR(lvds); + + lvds->dev = dev; + lvds->bridge.of_node = pdev->dev.of_node; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + lvds->regmap = devm_regmap_init_mmio(dev, base, &rzg3l_lvds_regmap_config); + if (IS_ERR(lvds->regmap)) + return dev_err_probe(dev, PTR_ERR(lvds->regmap), + "Failed to init regmap\n"); + + lvds->resets[0].id = "prst"; + lvds->resets[1].id = "lvdrst"; + ret = devm_reset_control_bulk_get_exclusive(dev, ARRAY_SIZE(lvds->resets), + lvds->resets); + if (ret) + return dev_err_probe(dev, ret, "Failed to get prst/core resets\n"); + + platform_set_drvdata(pdev, lvds); + ret = devm_pm_runtime_enable(dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable Runtime PM\n"); + + lvds->bridge.next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0); + if (IS_ERR(lvds->bridge.next_bridge)) + return dev_err_probe(dev, PTR_ERR(lvds->bridge.next_bridge), + "Failed to get next bridge\n"); + + /* + * This module cannot be used at the same time as MIPI-DSI, so assert + * the MIPI_DSI_CMN_RSTB and MIPI_DSI_ARESET_N resets before using this + * module. + */ + lvds->dsi_resets[0].id = "rst"; + lvds->dsi_resets[1].id = "arst"; + ret = devm_reset_control_bulk_get_exclusive(dev, ARRAY_SIZE(lvds->dsi_resets), + lvds->dsi_resets); + if (ret) + return dev_err_probe(dev, ret, "Failed to get rst/arst resets\n"); + + ret = reset_control_bulk_assert(ARRAY_SIZE(lvds->dsi_resets), lvds->dsi_resets); + if (ret < 0) + return ret; + + ret = devm_drm_bridge_add(dev, &lvds->bridge); + if (ret) + return dev_err_probe(dev, ret, "Failed to register drm bridge\n"); + + return ret; +} + +static const struct of_device_id rzg3l_lvds_of_table[] = { + { .compatible = "renesas,r9a08g046-lvds" }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, rzg3l_lvds_of_table); + +static struct platform_driver rzg3l_lvds_platform_driver = { + .probe = rzg3l_lvds_probe, + .driver = { + .name = "rzg3l-lvds", + .pm = pm_ptr(&rzg3l_lvds_pm_ops), + .of_match_table = rzg3l_lvds_of_table, + }, +}; + +module_platform_driver(rzg3l_lvds_platform_driver); + +MODULE_AUTHOR("Biju Das <biju.das.jz@bp.renesas.com>"); +MODULE_AUTHOR("Tommaso Merciai <tommaso.merciai.xr@bp.renesas.com>"); +MODULE_DESCRIPTION("Renesas RZ/G3L LVDS Encoder Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h new file mode 100644 index 000000000000..5b276cca9a5d --- /dev/null +++ b/drivers/gpu/drm/renesas/rz-du/rzg3l_lvds_regs.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * RZ/G3L LVDS Interface Registers Definitions + * + * Copyright (C) 2026 Renesas Electronics Corporation + * + */ + +#ifndef __RZG3L_LVDS_REGS_H__ +#define __RZG3L_LVDS_REGS_H__ + +#define LVDS_CMN 0x00 +#define LVDS_CMN_RST_PHY0_SEL BIT(24) +#define LVDS_CMN_PHY_RESET BIT(0) + +#define LVDS_0_PHY_OFFSET 0x10 +#define LVDS_0_PHY_CH_IO_EN0_MSK 0x1f +#define LVDS_0_PHY_CH_IO_EN0 (LVDS_0_PHY_CH_IO_EN0_MSK << 0) +#define LVDS_0_PHY_CH_EN_BGR BIT(8) +#define LVDS_0_PHY_CH_EN_LDO BIT(9) + +#define LVDS_0_CTL_OFFSET 0x14 +#define LVDS_0_CTL_FMT_SEL0_MSK GENMASK(23, 20) + +#endif /* __RZG3L_LVDS_REGS_H__ */ diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 4e58685f58ff..754d9b977ff1 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -125,6 +125,7 @@ config ROCKCHIP_LVDS depends on PINCTRL && OF select DRM_DISPLAY_HELPER select DRM_BRIDGE_CONNECTOR + select DRM_PANEL help Choose this option to enable support for Rockchip LVDS controllers. Rockchip rk3288 SoC has LVDS TX Controller can be used, and it @@ -137,6 +138,7 @@ config ROCKCHIP_RGB depends on PINCTRL select DRM_DISPLAY_HELPER select DRM_BRIDGE_CONNECTOR + select DRM_PANEL help Choose this option to enable support for Rockchip RGB output. Some Rockchip CRTCs, like rv1108, can directly output parallel diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index efd5a98e80bd..ccdd4b9ecdb8 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -28,7 +28,6 @@ #include <drm/drm_bridge_connector.h> #include <drm/bridge/analogix_dp.h> #include <drm/drm_of.h> -#include <drm/drm_panel.h> #include <drm/drm_print.h> #include <drm/drm_probe_helper.h> @@ -487,16 +486,6 @@ static int rockchip_dp_probe(struct platform_device *pdev) static void rockchip_dp_remove(struct platform_device *pdev) { - struct rockchip_dp_device *dp = platform_get_drvdata(pdev); - - /* - * Release the probe-time reference from of_drm_find_panel(). If bind - * ran, the panel_bridge holds a second reference that devm cleanup - * will release when the bridge is destroyed after remove() returns. - */ - if (dp->plat_data.panel) - drm_panel_put(dp->plat_data.panel); - component_del(&pdev->dev, &rockchip_dp_component_ops); } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c index 9a1dc9f12072..b55113a7a00b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c @@ -8,9 +8,9 @@ #include <linux/iommu.h> #include <linux/vmalloc.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm.h> #include <drm/drm_dumb_buffers.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_gem.h> #include <drm/drm_gem_dma_helper.h> #include <drm/drm_prime.h> diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index e4069fcb0272..7be0387bac3c 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -136,7 +136,6 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, DRM_SCHED_PRIORITY_KERNEL : priority; entity->num_sched_list = num_sched_list; entity->sched_list = num_sched_list > 1 ? sched_list : NULL; - RCU_INIT_POINTER(entity->last_scheduled, NULL); RB_CLEAR_NODE(&entity->rb_tree_node); if (!sched_list[0]->sched_rq) { @@ -233,10 +232,10 @@ int drm_sched_entity_error(struct drm_sched_entity *entity) struct dma_fence *fence; int r; - rcu_read_lock(); - fence = rcu_dereference(entity->last_scheduled); + spin_lock(&entity->lock); + fence = entity->last_scheduled; r = fence ? fence->error : 0; - rcu_read_unlock(); + spin_unlock(&entity->lock); return r; } @@ -319,9 +318,9 @@ void drm_sched_entity_kill(struct drm_sched_entity *entity) /* Make sure this entity is not used by the scheduler at the moment */ wait_for_completion(&entity->entity_idle); - /* The entity is guaranteed to not be used by the scheduler */ - prev = rcu_dereference_check(entity->last_scheduled, true); - dma_fence_get(prev); + spin_lock(&entity->lock); + prev = dma_fence_get(entity->last_scheduled); + spin_unlock(&entity->lock); while ((job = drm_sched_entity_queue_pop(entity))) { struct drm_sched_fence *s_fence = job->s_fence; @@ -413,8 +412,7 @@ void drm_sched_entity_fini(struct drm_sched_entity *entity) entity->dependency = NULL; } - dma_fence_put(rcu_dereference_check(entity->last_scheduled, true)); - RCU_INIT_POINTER(entity->last_scheduled, NULL); + dma_fence_put(entity->last_scheduled); drm_sched_entity_stats_put(entity->stats); } EXPORT_SYMBOL(drm_sched_entity_fini); @@ -536,6 +534,10 @@ drm_sched_job_dependency(struct drm_sched_job *job, struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) { + /* Helper to avoid dropping the reference while the entity lock is held, + * just to have some more robustness. + */ + struct dma_fence *prev_last_scheduled; struct drm_sched_job *sched_job; sched_job = drm_sched_entity_queue_peek(entity); @@ -552,22 +554,15 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity) if (entity->guilty && atomic_read(entity->guilty)) dma_fence_set_error(&sched_job->s_fence->finished, -ECANCELED); - dma_fence_put(rcu_dereference_check(entity->last_scheduled, true)); - rcu_assign_pointer(entity->last_scheduled, - dma_fence_get(&sched_job->s_fence->finished)); - - /* - * If the queue is empty we allow drm_sched_entity_select_rq() to - * locklessly access ->last_scheduled. This only works if we set the - * pointer before we dequeue and if we a write barrier here. - */ - smp_wmb(); - spin_lock(&entity->lock); + prev_last_scheduled = entity->last_scheduled; + entity->last_scheduled = dma_fence_get(&sched_job->s_fence->finished); spsc_queue_pop(&entity->job_queue); drm_sched_rq_pop_entity(entity); spin_unlock(&entity->lock); + dma_fence_put(prev_last_scheduled); + /* Jobs and entities might have different lifecycles. Since we're * removing the job from the entities queue, set the jobs entity pointer * to NULL to prevent any future access of the entity through this job. @@ -591,21 +586,15 @@ void drm_sched_entity_select_rq(struct drm_sched_entity *entity) if (spsc_queue_count(&entity->job_queue)) return; - /* - * Only when the queue is empty are we guaranteed that - * drm_sched_run_job_work() cannot change entity->last_scheduled. To - * enforce ordering we need a read barrier here. See - * drm_sched_entity_pop_job() for the other side. - */ - smp_rmb(); - - fence = rcu_dereference_check(entity->last_scheduled, true); + spin_lock(&entity->lock); + fence = entity->last_scheduled; /* stay on the same engine if the previous job hasn't finished */ - if (fence && !dma_fence_is_signaled(fence)) + if (fence && !dma_fence_is_signaled(fence)) { + spin_unlock(&entity->lock); return; + } - spin_lock(&entity->lock); sched = drm_sched_pick_best(entity->sched_list, entity->num_sched_list); rq = sched ? sched->sched_rq[entity->rq_priority] : NULL; if (rq != entity->rq) { diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index aba5545033ac..b9580ce1ddec 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -77,7 +77,6 @@ #include <drm/drm_gem.h> #include <drm/drm_syncobj.h> #include <drm/gpu_scheduler.h> -#include <drm/spsc_queue.h> #include "sched_internal.h" diff --git a/drivers/gpu/drm/sitronix/st7571.c b/drivers/gpu/drm/sitronix/st7571.c index fc3ba5e8600f..5e60c4685671 100644 --- a/drivers/gpu/drm/sitronix/st7571.c +++ b/drivers/gpu/drm/sitronix/st7571.c @@ -20,7 +20,6 @@ #include <drm/drm_damage_helper.h> #include <drm/drm_drv.h> #include <drm/drm_encoder.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fbdev_shmem.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 324467ae9309..8cbf18754487 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -415,6 +415,20 @@ static int ssd130x_set_page_range(struct ssd130x_device *ssd130x, return 0; } +static int ssd132x_set_col_range(struct ssd130x_device *ssd130x, + u8 col_start, u8 cols) +{ + return ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, + col_start, col_start + cols - 1); +} + +static int ssd132x_set_row_range(struct ssd130x_device *ssd130x, + u8 row_start, u8 rows) +{ + return ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, + row_start, row_start + rows - 1); +} + /* Set page and column start address for page addressing mode */ static int ssd130x_set_page_pos(struct ssd130x_device *ssd130x, u8 page_start, u8 col_start) @@ -896,13 +910,12 @@ static int ssd132x_update_rect(struct ssd130x_device *ssd130x, * the second byte are SEG2 (D1[3:0]) and SEG3 (D1[7:4]) and so on. */ - /* Set column start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, col, col + columns - 1); + /* Set address range for horizontal addressing mode */ + ret = ssd132x_set_col_range(ssd130x, col, columns); if (ret < 0) return ret; - /* Set row start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, row, row + rows - 1); + ret = ssd132x_set_row_range(ssd130x, row, rows); if (ret < 0) return ret; @@ -1052,9 +1065,18 @@ static void ssd132x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array) { unsigned int columns = DIV_ROUND_UP(ssd130x->width, SSD132X_SEGMENT_WIDTH); unsigned int height = ssd130x->height; + int ret; memset(data_array, 0, columns * height); + ret = ssd132x_set_col_range(ssd130x, 0, columns); + if (ret < 0) + return; + + ret = ssd132x_set_row_range(ssd130x, 0, height); + if (ret < 0) + return; + /* Write out update in one go since horizontal addressing mode is used */ ssd130x_write_data(ssd130x, data_array, columns * height); } diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig index 635be0ac00af..1e3205aa2c7e 100644 --- a/drivers/gpu/drm/stm/Kconfig +++ b/drivers/gpu/drm/stm/Kconfig @@ -6,6 +6,7 @@ config DRM_STM select DRM_CLIENT_SELECTION select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER + select DRM_PANEL select DRM_PANEL_BRIDGE select VIDEOMODE_HELPERS select FB_PROVIDE_GET_FB_UNMAPPED_AREA if FB diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 003e80cf7b9a..64c2e64f219b 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -11,9 +11,9 @@ #include <linux/fb.h> #include <linux/vmalloc.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_drv.h> #include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index bc1c93c7554c..b6d09a8d8e5e 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -10,6 +10,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge_connector.h> #include <drm/drm_encoder.h> +#include <drm/drm_panel.h> #include <drm/drm_print.h> #include "drm.h" diff --git a/drivers/gpu/drm/tegra/uapi.c b/drivers/gpu/drm/tegra/uapi.c index c0ac6b45f2d7..bee310b07eb0 100644 --- a/drivers/gpu/drm/tegra/uapi.c +++ b/drivers/gpu/drm/tegra/uapi.c @@ -7,7 +7,7 @@ #include <drm/drm_drv.h> #include <drm/drm_file.h> -#include <drm/drm_utils.h> +#include <drm/drm_timeout.h> #include "drm.h" #include "uapi.h" diff --git a/drivers/gpu/drm/tidss/Kconfig b/drivers/gpu/drm/tidss/Kconfig index 31ad582b7602..52a64fa1ad5d 100644 --- a/drivers/gpu/drm/tidss/Kconfig +++ b/drivers/gpu/drm/tidss/Kconfig @@ -7,6 +7,7 @@ config DRM_TIDSS select DRM_DISPLAY_HELPER select DRM_BRIDGE_CONNECTOR select DRM_GEM_DMA_HELPER + select DRM_PANEL help The TI Keystone family SoCs introduced a new generation of Display SubSystem. There is currently three Keystone family diff --git a/drivers/gpu/drm/tve200/Kconfig b/drivers/gpu/drm/tve200/Kconfig index a9d6fe535d88..afe243df4ebb 100644 --- a/drivers/gpu/drm/tve200/Kconfig +++ b/drivers/gpu/drm/tve200/Kconfig @@ -7,7 +7,7 @@ config DRM_TVE200 depends on OF select DRM_BRIDGE select DRM_CLIENT_SELECTION - select DRM_PANEL_BRIDGE + select DRM_PANEL select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER help diff --git a/drivers/gpu/drm/tve200/tve200_drm.h b/drivers/gpu/drm/tve200/tve200_drm.h index 631a9f5d9aa6..708f8020667a 100644 --- a/drivers/gpu/drm/tve200/tve200_drm.h +++ b/drivers/gpu/drm/tve200/tve200_drm.h @@ -103,7 +103,6 @@ struct tve200_drm_dev_private { struct drm_device *drm; struct drm_connector *connector; - struct drm_panel *panel; struct drm_bridge *bridge; struct drm_plane plane; struct drm_crtc crtc; diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index 63ca4a53fc4c..2bcb9dc86ffd 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -65,7 +65,6 @@ static int tve200_modeset_init(struct drm_device *dev) { struct drm_mode_config *mode_config; struct tve200_drm_dev_private *priv = dev->dev_private; - struct drm_panel *panel; struct drm_bridge *bridge; int ret; @@ -77,19 +76,10 @@ static int tve200_modeset_init(struct drm_device *dev) mode_config->min_height = 240; mode_config->max_height = 576; - ret = drm_of_find_panel_or_bridge(dev->dev->of_node, - 0, 0, &panel, &bridge); - if (ret && ret != -ENODEV) - return ret; - if (panel) { - bridge = drm_panel_bridge_add_typed(panel, - DRM_MODE_CONNECTOR_Unknown); - drm_panel_put(panel); - if (IS_ERR(bridge)) { - ret = PTR_ERR(bridge); - goto out_bridge; - } - } else { + bridge = of_drm_get_bridge_by_endpoint(dev->dev->of_node, 0, 0); + if (IS_ERR(bridge) && PTR_ERR(bridge) != -ENODEV) + return PTR_ERR(bridge); + if (IS_ERR(bridge) || !drm_bridge_is_panel(bridge)) { /* * TODO: when we are using a different bridge than a panel * (such as a dumb VGA connector) we need to devise a different @@ -100,13 +90,14 @@ static int tve200_modeset_init(struct drm_device *dev) goto out_bridge; } + bridge->type = DRM_MODE_CONNECTOR_Unknown; + ret = tve200_display_init(dev); if (ret) { dev_err(dev->dev, "failed to init display\n"); goto out_bridge; } - priv->panel = panel; priv->bridge = bridge; ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0); @@ -121,9 +112,6 @@ static int tve200_modeset_init(struct drm_device *dev) goto out_bridge; } - dev_info(dev->dev, "attached to panel %s\n", - dev_name(panel->dev)); - ret = drm_vblank_init(dev, 1); if (ret) { dev_err(dev->dev, "failed to init vblank\n"); @@ -136,8 +124,6 @@ static int tve200_modeset_init(struct drm_device *dev) goto finish; out_bridge: - if (panel) - drm_panel_bridge_remove(bridge); drm_mode_config_cleanup(dev); finish: return ret; @@ -233,8 +219,6 @@ static int tve200_probe(struct platform_device *pdev) return 0; mode_config_cleanup: - if (priv->panel) - drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); clk_disable: clk_disable_unprepare(priv->pclk); @@ -248,10 +232,9 @@ static void tve200_remove(struct platform_device *pdev) struct drm_device *drm = platform_get_drvdata(pdev); struct tve200_drm_dev_private *priv = drm->dev_private; + drm_bridge_put(priv->bridge); drm_dev_unregister(drm); drm_atomic_helper_shutdown(drm); - if (priv->panel) - drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); clk_disable_unprepare(priv->pclk); drm_dev_put(drm); diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c index a847d2f0ccf5..db95a2fac5d0 100644 --- a/drivers/gpu/drm/v3d/v3d_bo.c +++ b/drivers/gpu/drm/v3d/v3d_bo.c @@ -19,6 +19,7 @@ #include <linux/vmalloc.h> #include <drm/drm_print.h> +#include <drm/drm_timeout.h> #include "v3d_drv.h" #include "uapi/drm/v3d_drm.h" @@ -281,7 +282,7 @@ v3d_wait_bo_ioctl(struct drm_device *dev, void *data, ktime_t start = ktime_get(); u64 delta_ns; unsigned long timeout_jiffies = - nsecs_to_jiffies_timeout(args->timeout_ns); + drm_timeout_rel_to_jiffies(args->timeout_ns); if (args->pad != 0) return -EINVAL; diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index d40610a34db9..7c7bdf6e51a5 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -588,16 +588,6 @@ struct v3d_submit_ext { (Wmax)) #define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000) -static inline unsigned long nsecs_to_jiffies_timeout(const u64 n) -{ - /* nsecs_to_jiffies64() does not guard against overflow */ - if ((NSEC_PER_SEC % HZ) != 0 && - div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ) - return MAX_JIFFY_OFFSET; - - return min_t(u64, MAX_JIFFY_OFFSET, nsecs_to_jiffies64(n) + 1); -} - /* v3d_bo.c */ struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size); void v3d_free_object(struct drm_gem_object *gem_obj); diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c index bb3f7f0ad7c4..7c34299fa531 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_mode.c +++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c @@ -13,11 +13,11 @@ #include <linux/iosys-map.h> #include <linux/export.h> +#include <drm/clients/drm_fbdev_helper.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_blend.h> #include <drm/drm_edid.h> -#include <drm/drm_fb_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 3212b9167620..0ea921bab906 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -32,6 +32,7 @@ #include <drm/drm_exec.h> #include <drm/drm_print.h> #include <drm/drm_syncobj.h> +#include <drm/drm_timeout.h> #include "vc4_drv.h" #include "vc4_regs.h" @@ -982,7 +983,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev, void *data, int ret; struct drm_vc4_wait_bo *args = data; unsigned long timeout_jiffies = - usecs_to_jiffies(div_u64(args->timeout_ns, 1000)); + drm_timeout_rel_to_jiffies(args->timeout_ns); ktime_t start = ktime_get(); u64 delta_ns; diff --git a/drivers/gpu/drm/xe/xe_wait_user_fence.c b/drivers/gpu/drm/xe/xe_wait_user_fence.c index 51eb940ceb4e..e241f3ec3547 100644 --- a/drivers/gpu/drm/xe/xe_wait_user_fence.c +++ b/drivers/gpu/drm/xe/xe_wait_user_fence.c @@ -7,7 +7,7 @@ #include <drm/drm_device.h> #include <drm/drm_file.h> -#include <drm/drm_utils.h> +#include <drm/drm_timeout.h> #include <uapi/drm/xe_drm.h> #include "xe_device.h" |
