diff mbox series

[07/11] drm/fbdevdrm: Add DRM <-> fbdev pixel-format conversion

Message ID 20190326091744.11542-8-tzimmermann@suse.de (mailing list archive)
State New, archived
Headers show
Series DRM driver for fbdev devices | expand

Commit Message

Thomas Zimmermann March 26, 2019, 9:17 a.m. UTC
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/fbdevdrm/Makefile          |   1 +
 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c | 441 +++++++++++++++++++++
 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h |  26 ++
 3 files changed, 468 insertions(+)
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
 create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h

Comments

Ville Syrjälä March 26, 2019, 4:29 p.m. UTC | #1
On Tue, Mar 26, 2019 at 10:17:40AM +0100, Thomas Zimmermann wrote:
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  drivers/gpu/drm/fbdevdrm/Makefile          |   1 +
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c | 441 +++++++++++++++++++++
>  drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h |  26 ++
>  3 files changed, 468 insertions(+)
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
>  create mode 100644 drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
> 
> diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile
> index b8fab9d52faa..aef60d0f4888 100644
> --- a/drivers/gpu/drm/fbdevdrm/Makefile
> +++ b/drivers/gpu/drm/fbdevdrm/Makefile
> @@ -2,6 +2,7 @@ ccflags-y = -Iinclude/drm
>  fbdevdrm-y := fbdevdrm_bo.o \
>  	      fbdevdrm_device.o \
>  	      fbdevdrm_drv.o \
> +	      fbdevdrm_format.o \
>  	      fbdevdrm_modeset.o \
>  	      fbdevdrm_ttm.o
>  
> diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
> new file mode 100644
> index 000000000000..208f7c60e525
> --- /dev/null
> +++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
> @@ -0,0 +1,441 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * One purpose of this driver is to allow for easy conversion of framebuffer
> + * drivers to DRM. As a special exception to the GNU GPL, you are allowed to
> + * relicense this file under the terms of a license of your choice if you're
> + * porting a framebuffer driver. In order to do so, update the SPDX license
> + * identifier to the new license and remove this exception.
> + *
> + * If you add code to this file, please ensure that it's compatible with the
> + * stated exception.
> + */
> +
> +#include "fbdevdrm_format.h"
> +#include <asm/byteorder.h>
> +#include <linux/fb.h>
> +
> +#if defined __BIG_ENDIAN
> +#define HOST_FUNC(_func) \
> +	_func ## _be
> +#elif defined __LITTLE_ENDIAN
> +#define HOST_FUNC(_func) \
> +	_func ## _le
> +#else
> +#error "Unsupported endianess"
> +#endif
> +
> +static bool is_c8(const struct fb_info* fb_info)
> +{
> +	return fb_info->var.bits_per_pixel == 8;
> +}
> +
> +static bool is_rgb565_be(const struct fb_info* fb_info)
> +{
> +	return (fb_info->var.bits_per_pixel == 16) &&
> +	       (fb_info->var.red.offset == 0) &&
> +	       (fb_info->var.red.length == 5) &&
> +	       (fb_info->var.green.offset == 5) &&
> +	       (fb_info->var.green.length == 6) &&
> +	       (fb_info->var.blue.offset == 11) &&
> +	       (fb_info->var.blue.length == 5);
> +}

You can't distinguish LE vs. BE like this.

Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
Thomas Zimmermann March 27, 2019, 8:28 a.m. UTC | #2
Hi

Am 26.03.19 um 17:29 schrieb Ville Syrjälä:
>> +
>> +static bool is_c8(const struct fb_info* fb_info)
>> +{
>> +	return fb_info->var.bits_per_pixel == 8;
>> +}
>> +
>> +static bool is_rgb565_be(const struct fb_info* fb_info)
>> +{
>> +	return (fb_info->var.bits_per_pixel == 16) &&
>> +	       (fb_info->var.red.offset == 0) &&
>> +	       (fb_info->var.red.length == 5) &&
>> +	       (fb_info->var.green.offset == 5) &&
>> +	       (fb_info->var.green.length == 6) &&
>> +	       (fb_info->var.blue.offset == 11) &&
>> +	       (fb_info->var.blue.length == 5);
>> +}
> 
> You can't distinguish LE vs. BE like this.
> 
> Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
> 

The test function means 'is the framebuffer in RGB565 format if the host
uses big-endian access'. Further below in the file, there are macros
that reverse the order of fields for little-endian hosts.

However, mapping all this to DRM formats is confusing.

According to the documentation, DRM_FORMAT_ is always in little-endian
format. But does that mean that the device uses little endian or that
the host uses little endian? If the device and the host disagree on
endianess, which takes precedence?

In the end, I tried different combinations of tests and DRM formats, and
checked the resulting output on the screen.

Best regards
Thomas
Ville Syrjälä March 27, 2019, 10 a.m. UTC | #3
On Wed, Mar 27, 2019 at 09:28:49AM +0100, Thomas Zimmermann wrote:
> Hi
> 
> Am 26.03.19 um 17:29 schrieb Ville Syrjälä:
> >> +
> >> +static bool is_c8(const struct fb_info* fb_info)
> >> +{
> >> +	return fb_info->var.bits_per_pixel == 8;
> >> +}
> >> +
> >> +static bool is_rgb565_be(const struct fb_info* fb_info)
> >> +{
> >> +	return (fb_info->var.bits_per_pixel == 16) &&
> >> +	       (fb_info->var.red.offset == 0) &&
> >> +	       (fb_info->var.red.length == 5) &&
> >> +	       (fb_info->var.green.offset == 5) &&
> >> +	       (fb_info->var.green.length == 6) &&
> >> +	       (fb_info->var.blue.offset == 11) &&
> >> +	       (fb_info->var.blue.length == 5);
> >> +}
> > 
> > You can't distinguish LE vs. BE like this.
> > 
> > Maybe FBINFO_FOREIGN_ENDIAN is trustworthy?
> > 
> 
> The test function means 'is the framebuffer in RGB565 format if the host
> uses big-endian access'. Further below in the file, there are macros
> that reverse the order of fields for little-endian hosts.

And that does not work. Wrong endianness swaps bytes, not components.

> 
> However, mapping all this to DRM formats is confusing.
> 
> According to the documentation, DRM_FORMAT_ is always in little-endian
> format. But does that mean that the device uses little endian or that
> the host uses little endian? If the device and the host disagree on
> endianess, which takes precedence?

It just means "the pixel value listed is stored in memory 
least significant byte first".

> 
> In the end, I tried different combinations of tests and DRM formats, and
> checked the resulting output on the screen.

This was just pointed out to me recently:
https://github.com/afrantzis/pixel-format-guide

It seems to interpret drm formats correctly.

Though I wouldn't mind if someone improved the drm_fourcc.h docs since
everyone except me seems to get confused by the current wording.
diff mbox series

Patch

diff --git a/drivers/gpu/drm/fbdevdrm/Makefile b/drivers/gpu/drm/fbdevdrm/Makefile
index b8fab9d52faa..aef60d0f4888 100644
--- a/drivers/gpu/drm/fbdevdrm/Makefile
+++ b/drivers/gpu/drm/fbdevdrm/Makefile
@@ -2,6 +2,7 @@  ccflags-y = -Iinclude/drm
 fbdevdrm-y := fbdevdrm_bo.o \
 	      fbdevdrm_device.o \
 	      fbdevdrm_drv.o \
+	      fbdevdrm_format.o \
 	      fbdevdrm_modeset.o \
 	      fbdevdrm_ttm.o
 
diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
new file mode 100644
index 000000000000..208f7c60e525
--- /dev/null
+++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.c
@@ -0,0 +1,441 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * One purpose of this driver is to allow for easy conversion of framebuffer
+ * drivers to DRM. As a special exception to the GNU GPL, you are allowed to
+ * relicense this file under the terms of a license of your choice if you're
+ * porting a framebuffer driver. In order to do so, update the SPDX license
+ * identifier to the new license and remove this exception.
+ *
+ * If you add code to this file, please ensure that it's compatible with the
+ * stated exception.
+ */
+
+#include "fbdevdrm_format.h"
+#include <asm/byteorder.h>
+#include <linux/fb.h>
+
+#if defined __BIG_ENDIAN
+#define HOST_FUNC(_func) \
+	_func ## _be
+#elif defined __LITTLE_ENDIAN
+#define HOST_FUNC(_func) \
+	_func ## _le
+#else
+#error "Unsupported endianess"
+#endif
+
+static bool is_c8(const struct fb_info* fb_info)
+{
+	return fb_info->var.bits_per_pixel == 8;
+}
+
+static bool is_rgb565_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 16) &&
+	       (fb_info->var.red.offset == 0) &&
+	       (fb_info->var.red.length == 5) &&
+	       (fb_info->var.green.offset == 5) &&
+	       (fb_info->var.green.length == 6) &&
+	       (fb_info->var.blue.offset == 11) &&
+	       (fb_info->var.blue.length == 5);
+}
+
+static bool is_bgr565_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 16) &&
+	       (fb_info->var.red.offset == 11) &&
+	       (fb_info->var.red.length == 5) &&
+	       (fb_info->var.green.offset == 5) &&
+	       (fb_info->var.green.length == 6) &&
+	       (fb_info->var.blue.offset == 0) &&
+	       (fb_info->var.blue.length == 5);
+}
+
+static bool is_rgb888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 24) &&
+	       (fb_info->var.red.offset == 0) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 8) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 16) &&
+	       (fb_info->var.blue.length == 8);
+}
+
+static bool is_bgr888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 24) &&
+	       (fb_info->var.red.offset == 16) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 8) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 0) &&
+	       (fb_info->var.blue.length == 8);
+}
+
+static bool is_xrgb8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 8) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 16) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 24) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.length == 0);
+}
+
+static bool is_xbgr8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 24) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 16) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 8) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.length == 0);
+}
+
+static bool is_rgbx8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 0) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 8) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 16) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.length == 0);
+}
+
+static bool is_bgrx8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 16) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 8) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 0) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.length == 0);
+}
+
+static bool is_argb8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 8) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 16) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 24) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.offset == 0) &&
+	       (fb_info->var.transp.length == 8);
+}
+
+static bool is_abgr8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 24) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 16) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 8) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.offset == 0) &&
+	       (fb_info->var.transp.length == 8);
+}
+
+static bool is_rgba8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 0) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 8) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 16) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.offset == 24) &&
+	       (fb_info->var.transp.length == 8);
+}
+
+static bool is_bgra8888_be(const struct fb_info* fb_info)
+{
+	return (fb_info->var.bits_per_pixel == 32) &&
+	       (fb_info->var.red.offset == 16) &&
+	       (fb_info->var.red.length == 8) &&
+	       (fb_info->var.green.offset == 8) &&
+	       (fb_info->var.green.length == 8) &&
+	       (fb_info->var.blue.offset == 0) &&
+	       (fb_info->var.blue.length == 8) &&
+	       (fb_info->var.transp.offset == 24) &&
+	       (fb_info->var.transp.length == 8);
+}
+
+#define is_rgb565_le is_bgr565_be
+#define is_bgr565_le is_rgb565_be
+#define is_rgb888_le is_bgr888_be
+#define is_bgr888_le is_rgb888_be
+#define is_xrgb8888_le is_bgrx8888_be
+#define is_xbgr8888_le is_rgbx8888_be
+#define is_rgbx8888_le is_xbgr8888_be
+#define is_bgrx8888_le is_xrgb8888_be
+#define is_argb8888_le is_bgra8888_be
+#define is_abgr8888_le is_rgba8888_be
+#define is_rgba8888_le is_abgr8888_be
+#define is_bgra8888_le is_argb8888_be
+
+struct format_map {
+	bool (*is_format)(const struct fb_info*);
+	uint32_t format;
+};
+
+uint32_t fbdevdrm_format_of_fb_info(const struct fb_info *fb_info)
+{
+	static const struct format_map map[] = {
+		{ is_c8,                  DRM_FORMAT_C8 },      /* 256-color palette */
+		{ HOST_FUNC(is_rgb565),   DRM_FORMAT_RGB565 },
+		{ HOST_FUNC(is_bgr565),   DRM_FORMAT_BGR565 },
+		{ HOST_FUNC(is_rgb888),   DRM_FORMAT_RGB888 },
+		{ HOST_FUNC(is_bgr888),   DRM_FORMAT_BGR888 },
+		{ HOST_FUNC(is_xrgb8888), DRM_FORMAT_XRGB8888 },
+		{ HOST_FUNC(is_xbgr8888), DRM_FORMAT_XBGR8888 },
+		{ HOST_FUNC(is_rgbx8888), DRM_FORMAT_RGBX8888 },
+		{ HOST_FUNC(is_bgrx8888), DRM_FORMAT_BGRX8888 },
+		{ HOST_FUNC(is_argb8888), DRM_FORMAT_ARGB8888 },
+		{ HOST_FUNC(is_abgr8888), DRM_FORMAT_ABGR8888 },
+		{ HOST_FUNC(is_rgba8888), DRM_FORMAT_RGBA8888 },
+		{ HOST_FUNC(is_bgra8888), DRM_FORMAT_BGRA8888 }
+	};
+
+	size_t i;
+
+	if (fb_info->fix.type != FB_TYPE_PACKED_PIXELS)
+		goto err; /* multi-plane formats are not supported */
+	if (fb_info->var.bits_per_pixel < 8)
+		goto err; /* at least 8-bit color required */
+	if (fb_info->var.grayscale == 1)
+		goto err; /* grayscale output is not supported */
+
+	for (i = 0; i < ARRAY_SIZE(map); ++i) {
+		if (map[i].is_format(fb_info))
+			return map[i].format;
+	}
+
+err:
+	return DRM_FORMAT_INVALID;
+}
+
+static void set_fb_bitfield(struct fb_bitfield *bits, __u32 offset,
+			    __u32 length)
+{
+	bits->offset = offset;
+	bits->length = length;
+	bits->msb_right = 0;
+}
+
+static void set_c8(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 8;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    0, 8);
+	set_fb_bitfield(&fb_var->green,  0, 8);
+	set_fb_bitfield(&fb_var->blue,   0, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_rgb565_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 16;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    0, 5);
+	set_fb_bitfield(&fb_var->green,  5, 6);
+	set_fb_bitfield(&fb_var->blue,  11, 5);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_bgr565_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 16;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,   11, 5);
+	set_fb_bitfield(&fb_var->green,  5, 6);
+	set_fb_bitfield(&fb_var->blue,   0, 5);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_rgb888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 24;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    0, 8);
+	set_fb_bitfield(&fb_var->green,  8, 8);
+	set_fb_bitfield(&fb_var->blue,  16, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_bgr888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 24;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,   16, 8);
+	set_fb_bitfield(&fb_var->green,  8, 8);
+	set_fb_bitfield(&fb_var->blue,   0, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_xrgb8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    8, 8);
+	set_fb_bitfield(&fb_var->green, 16, 8);
+	set_fb_bitfield(&fb_var->blue,  24, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_xbgr8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,   24, 8);
+	set_fb_bitfield(&fb_var->green, 16, 8);
+	set_fb_bitfield(&fb_var->blue,   8, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_rgbx8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,     0, 8);
+	set_fb_bitfield(&fb_var->green,   8, 8);
+	set_fb_bitfield(&fb_var->blue,   16, 8);
+	set_fb_bitfield(&fb_var->transp, 24, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_bgrx8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    16, 8);
+	set_fb_bitfield(&fb_var->green,   8, 8);
+	set_fb_bitfield(&fb_var->blue,    0, 8);
+	set_fb_bitfield(&fb_var->transp, 24, 0);
+	fb_var->nonstd = 0;
+}
+
+static void set_argb8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    8, 8);
+	set_fb_bitfield(&fb_var->green, 16, 8);
+	set_fb_bitfield(&fb_var->blue,  24, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 8);
+	fb_var->nonstd = 0;
+}
+
+static void set_abgr8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,   24, 8);
+	set_fb_bitfield(&fb_var->green, 16, 8);
+	set_fb_bitfield(&fb_var->blue,   8, 8);
+	set_fb_bitfield(&fb_var->transp, 0, 8);
+	fb_var->nonstd = 0;
+}
+
+static void set_rgba8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,     0, 8);
+	set_fb_bitfield(&fb_var->green,   8, 8);
+	set_fb_bitfield(&fb_var->blue,   16, 8);
+	set_fb_bitfield(&fb_var->transp, 24, 8);
+	fb_var->nonstd = 0;
+}
+
+static void set_bgra8888_be(struct fb_var_screeninfo *fb_var)
+{
+	fb_var->bits_per_pixel = 32;
+	fb_var->grayscale = 0;
+	set_fb_bitfield(&fb_var->red,    16, 8);
+	set_fb_bitfield(&fb_var->green,   8, 8);
+	set_fb_bitfield(&fb_var->blue,    0, 8);
+	set_fb_bitfield(&fb_var->transp, 24, 8);
+	fb_var->nonstd = 0;
+}
+
+#define set_rgb565_le set_bgr565_be
+#define set_bgr565_le set_rgb565_be
+#define set_rgb888_le set_bgr888_be
+#define set_bgr888_le set_rgb888_be
+#define set_xrgb8888_le set_bgrx8888_be
+#define set_xbgr8888_le set_rgbx8888_be
+#define set_rgbx8888_le set_xbgr8888_be
+#define set_bgrx8888_le set_xrgb8888_be
+#define set_argb8888_le set_bgra8888_be
+#define set_abgr8888_le set_rgba8888_be
+#define set_rgba8888_le set_abgr8888_be
+#define set_bgra8888_le set_argb8888_be
+
+int fbdevdrm_update_fb_var_screeninfo_from_format(
+	struct fb_var_screeninfo *fb_var, uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_C8:
+		set_c8(fb_var);
+		break;
+	case DRM_FORMAT_RGB565:
+		HOST_FUNC(set_rgb565)(fb_var);
+		break;
+	case DRM_FORMAT_BGR565:
+		HOST_FUNC(set_bgr565)(fb_var);
+		break;
+	case DRM_FORMAT_RGB888:
+		HOST_FUNC(set_rgb888)(fb_var);
+		break;
+	case DRM_FORMAT_BGR888:
+		HOST_FUNC(set_bgr888)(fb_var);
+		break;
+	case DRM_FORMAT_XRGB8888:
+		HOST_FUNC(set_xrgb8888)(fb_var);
+		break;
+	case DRM_FORMAT_XBGR8888:
+		HOST_FUNC(set_xbgr8888)(fb_var);
+		break;
+	case DRM_FORMAT_RGBX8888:
+		HOST_FUNC(set_rgbx8888)(fb_var);
+		break;
+	case DRM_FORMAT_BGRX8888:
+		HOST_FUNC(set_bgrx8888)(fb_var);
+		break;
+	case DRM_FORMAT_ARGB8888:
+		HOST_FUNC(set_argb8888)(fb_var);
+		break;
+	case DRM_FORMAT_ABGR8888:
+		HOST_FUNC(set_abgr8888)(fb_var);
+		break;
+	case DRM_FORMAT_RGBA8888:
+		HOST_FUNC(set_rgba8888)(fb_var);
+		break;
+	case DRM_FORMAT_BGRA8888:
+		HOST_FUNC(set_bgra8888)(fb_var);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
diff --git a/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
new file mode 100644
index 000000000000..e837978ac8fc
--- /dev/null
+++ b/drivers/gpu/drm/fbdevdrm/fbdevdrm_format.h
@@ -0,0 +1,26 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * One purpose of this driver is to allow for easy conversion of framebuffer
+ * drivers to DRM. As a special exception to the GNU GPL, you are allowed to
+ * relicense this file under the terms of a license of your choice if you're
+ * porting a framebuffer driver. In order to do so, update the SPDX license
+ * identifier to the new license and remove this exception.
+ *
+ * If you add code to this file, please ensure that it's compatible with the
+ * stated exception.
+ */
+
+#ifndef FBDEVDRM_FORMAT_H
+#define FBDEVDRM_FORMAT_H
+
+#include <drm/drm_fourcc.h>
+
+struct fb_info;
+struct fb_var_screeninfo;
+
+uint32_t fbdevdrm_format_of_fb_info(const struct fb_info *fb_info);
+
+int fbdevdrm_update_fb_var_screeninfo_from_format(
+	struct fb_var_screeninfo *fb_var, uint32_t format);
+
+#endif