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