[PATCH 01/11] drm: Only return supported formats from drm_driver_legacy_fb_format

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch extends drm_driver_legacy_fb_format() to only return
formats which are supported by the current drm-device. The motivation
for this change is to fix a regression introduced by commit
c91acda3a380 ("drm/gem: Check for valid formats") which stops the Xorg
modesetting driver from working on the Beagleboard Black (it uses the
tilcdc kernel driver).

When the Xorg modesetting driver starts up, it tries to determine the
default bpp for the device. It does this by allocating a dumb 32bpp
frame buffer (using DRM_IOCTL_MODE_CREATE_DUMB) and then calling
drmModeAddFB() with that frame buffer asking for a 24-bit depth and 32
bpp. As the modesetting driver uses drmModeAddFB() which doesn't
supply a format, the kernel's drm_driver_legacy_fb_format() is called,
in drm_mode_addfb(), to provide a format matching the requested depth
and bpp. If the drmModeAddFB() call fails, the modesetting driver
forces both depth and bpp to 24. If drmModeAddFB() succeeds, depth is
assumed to be 24 and bpp 32. The dummy frame buffer is then
removed (using drmModeRmFB()).

If the modesetting driver finds that both the default bpp and depth
are 24, it forces the use of a 32bpp shadow buffer and a 24bpp front
buffer. Following this, the driver reads the user-specified color
depth option and tries to create a framebuffer of that depth, but if
the use of a shadow buffer has been forced, the bpp and depth of it
overrides the user-supplied option.

The Xorg modesetting driver on top of the tilcdc kernel driver used to
work on the Beagleboard Black if a 16 bit color depth was
configured. The hardware in the Beagleboard Black supports the RG16,
BG24, and XB24 formats. When drm_mode_legacy_fb_format() was called to
request a format for a 24-bit depth and 32 bpp, it would return the
unsupported RG24 format which drmModeAddFB() would happily accept (as
there was no check for a valid format). As a shadow buffer wasn't
forced, the modesetting driver would try the user specified 16 bit
color depth and drm_mode_legacy_fb_format() would return RG16 which is
supported by the hardware. Color depths of 24 bits were not supported,
as the unsupported RG24 would be detected when drmModeSetCrtc() was
called.

Following commit c91acda3a380 ("drm/gem: Check for valid formats"),
which adds a check for a valid (supported by the hardware) format to
the code path for the kernel part of drmModeAddFB(), the modesetting
driver fails to configure and add a frame buffer. This is because the
call to create a 24-bit depth and 32 bpp framebuffer during detection
of the default bpp will now fail and a 24-bit depth and 24 bpp front
buffer will be forced. As drm_mode_legacy_fb_format() will return RG24
which isn't supported, the creation of that framebuffer will also
fail.

To fix the regression, this patch extends
drm_driver_legacy_fb_format() to list all formats with a particular
bpp and color depth known to the kernel, and have it probe the current
drm-device for a supported format. This fixes the regression and, as a
bonus, a color depth of 24 bits on the Beagleboard Black is now
working.

This patch has, in addition to the Beagleboard Black, also been tested
with the nouveau and modesetting drivers on a NVIDIA NV96, and with
the Intel and modesetting drivers on an Intel HD Graphics 4000
chipset.

Signed-off-by: Frej Drejhammar <frej.drejhammar@xxxxxxxxx>
Fixes: c91acda3a380 ("drm/gem: Check for valid formats")
Cc: Maarten Lankhorst <maarten.lankhorst@xxxxxxxxxxxxxxx>
Cc: Maxime Ripard <mripard@xxxxxxxxxx>
Cc: Thomas Zimmermann <tzimmermann@xxxxxxx>
Cc: David Airlie <airlied@xxxxxxxxx>
Cc: Daniel Vetter <daniel@xxxxxxxx>
Cc: "Maíra Canal" <mcanal@xxxxxxxxxx>
Cc: "Ville Syrjälä" <ville.syrjala@xxxxxxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
---

This is an evolved version of the changes proposed in "drm: Don't
return unsupported formats in drm_mode_legacy_fb_format" [1] following
the suggestions of Thomas Zimmermann.

[1] https://lore.kernel.org/all/20240310152803.3315-1-frej.drejhammar@xxxxxxxxx/
---
 drivers/gpu/drm/drm_fourcc.c | 83 ++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c
index 193cf8ed7912..285388bed990 100644
--- a/drivers/gpu/drm/drm_fourcc.c
+++ b/drivers/gpu/drm/drm_fourcc.c
@@ -29,6 +29,7 @@
 
 #include <drm/drm_device.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_plane.h>
 
 /**
  * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
@@ -105,6 +106,87 @@ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
 }
 EXPORT_SYMBOL(drm_mode_legacy_fb_format);
 
+/*
+ * Internal helper to find a supported format among a list of
+ * potentially supported formats.
+ *
+ * Traverses the variadic arguments until a format supported by dev or
+ * an DRM_FORMAT_INVALID argument is found. If a supported format is
+ * found it is returned, otherwise DRM_FORMAT_INVALID is returned.
+ */
+static uint32_t drm_pick_supported_format(struct drm_device *dev, ...)
+{
+	va_list va;
+	uint32_t fmt = DRM_FORMAT_INVALID;
+	uint32_t to_try;
+
+	va_start(va, dev);
+
+	for (to_try = va_arg(va, uint32_t);
+	     to_try != DRM_FORMAT_INVALID;
+	     to_try = va_arg(va, uint32_t)) {
+		if (drm_any_plane_has_format(dev, to_try, 0)) {
+			fmt = to_try;
+			break;
+		}
+	}
+
+	va_end(va);
+
+	return fmt;
+}
+
+/*
+ * Internal helper to find a format which has the same depth and bpp
+ * as the input format and is supported by the drm device.
+ */
+static uint32_t drm_supported_format(struct drm_device *dev, uint32_t fmt)
+{
+	switch (fmt) {
+	case DRM_FORMAT_XRGB1555:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_RGBX5551,
+						 DRM_FORMAT_BGRX5551,
+						 DRM_FORMAT_INVALID);
+	case DRM_FORMAT_RGB565:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_BGR565,
+						 DRM_FORMAT_INVALID);
+	case DRM_FORMAT_RGB888:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_BGR888,
+						 DRM_FORMAT_INVALID);
+	case DRM_FORMAT_XRGB8888:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_XBGR8888,
+						 DRM_FORMAT_RGBX8888,
+						 DRM_FORMAT_BGRX8888,
+						 DRM_FORMAT_INVALID);
+	case DRM_FORMAT_XRGB2101010:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_XBGR2101010,
+						 DRM_FORMAT_RGBX1010102,
+						 DRM_FORMAT_BGRX1010102,
+						 DRM_FORMAT_INVALID);
+	case DRM_FORMAT_ARGB8888:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_ABGR8888,
+						 DRM_FORMAT_RGBA8888,
+						 DRM_FORMAT_BGRA8888,
+						 DRM_FORMAT_INVALID);
+	default:
+		return drm_pick_supported_format(dev,
+						 fmt,
+						 DRM_FORMAT_INVALID);
+	}
+}
+
 /**
  * drm_driver_legacy_fb_format - compute drm fourcc code from legacy description
  * @dev: DRM device
@@ -121,6 +203,7 @@ uint32_t drm_driver_legacy_fb_format(struct drm_device *dev,
 {
 	uint32_t fmt = drm_mode_legacy_fb_format(bpp, depth);
 
+	fmt = drm_supported_format(dev, fmt);
 	if (dev->mode_config.quirk_addfb_prefer_host_byte_order) {
 		if (fmt == DRM_FORMAT_XRGB8888)
 			fmt = DRM_FORMAT_HOST_XRGB8888;
-- 
2.44.0





[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux