[PATCH 2/2] drm/ast: Fix framebuffer color swapping on ppc64 systems

Timothy Pearson tpearson at raptorengineering.com
Sat Oct 18 05:18:32 AEDT 2025


On certain big endian systems, such as the POWER9 operating in big endian
mode, PCI MMIO BAR accesses are transparently endian-swapped by hardware.
On the AST2xx0 series devices, this results in the little endian framebuffer
appearing to the host as a big endian device, resulting in unwanted color
swapping.

Furthermore, per ASpeed technical support, the big endian mode on these devices
is not supported and, per Raptor's internal testing, does not function at a
hardware level.

Detect transparent PCI swapping via CONFIG_PCI_ARCH_ENDIAN_AUTOSWAP, and
expose the framebuffer as a big endian device with software swapping.

Tested on a POWER9 Blackbird system with Debian sid/ppc64.

Signed-off-by: Timothy Pearson <tpearson at raptorengineering.com>
---
 drivers/gpu/drm/ast/ast_mode.c | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 30b011ed0a05..07f04668ef92 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -105,9 +105,11 @@ static void ast_crtc_fill_gamma(struct ast_device *ast,
 		drm_crtc_fill_palette_8(crtc, ast_set_gamma_lut);
 		break;
 	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_HOST_RGB565:
 		/* also uses 8-bit gamma ramp on low-color modes */
 		fallthrough;
 	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_HOST_XRGB8888:
 		drm_crtc_fill_gamma_888(crtc, ast_set_gamma_lut);
 		break;
 	default:
@@ -129,9 +131,11 @@ static void ast_crtc_load_gamma(struct ast_device *ast,
 		drm_crtc_load_palette_8(crtc, lut, ast_set_gamma_lut);
 		break;
 	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_HOST_RGB565:
 		/* also uses 8-bit gamma ramp on low-color modes */
 		fallthrough;
 	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_HOST_XRGB8888:
 		drm_crtc_load_gamma_888(crtc, lut, ast_set_gamma_lut);
 		break;
 	default:
@@ -502,8 +506,13 @@ void __iomem *ast_plane_vaddr(struct ast_plane *ast_plane)
  */
 
 static const uint32_t ast_primary_plane_formats[] = {
+#if defined(__BIG_ENDIAN) && defined(CONFIG_PCI_ARCH_ENDIAN_AUTOSWAP)
+	DRM_FORMAT_HOST_XRGB8888,
+	DRM_FORMAT_HOST_RGB565,
+#else
 	DRM_FORMAT_XRGB8888,
 	DRM_FORMAT_RGB565,
+#endif
 	DRM_FORMAT_C8,
 };
 
@@ -541,12 +550,24 @@ static int ast_primary_plane_helper_atomic_check(struct drm_plane *plane,
 
 static void ast_handle_damage(struct ast_plane *ast_plane, struct iosys_map *src,
 			      struct drm_framebuffer *fb,
-			      const struct drm_rect *clip)
+			      const struct drm_rect *clip,
+			      struct drm_format_conv_state *fmtcnv_state)
 {
 	struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(ast_plane_vaddr(ast_plane));
 
 	iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip));
+#if defined(__BIG_ENDIAN) && defined(CONFIG_PCI_ARCH_ENDIAN_AUTOSWAP)
+	switch (fb->format->format) {
+		case DRM_FORMAT_HOST_XRGB8888:
+		case DRM_FORMAT_HOST_RGB565:
+			drm_fb_swab(&dst, fb->pitches, src, fb, clip, false, fmtcnv_state);
+			break;
+		default:
+			drm_fb_memcpy(&dst, fb->pitches, src, fb, clip);
+	}
+#else
 	drm_fb_memcpy(&dst, fb->pitches, src, fb, clip);
+#endif
 }
 
 static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
@@ -574,7 +595,7 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
 
 	drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
 	drm_atomic_for_each_plane_damage(&iter, &damage) {
-		ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage);
+		ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage, &shadow_plane_state->fmtcnv_state);
 	}
 
 	/*
@@ -766,10 +787,13 @@ static int ast_crtc_helper_atomic_check(struct drm_crtc *crtc,
 		ast_state->std_table = &vbios_stdtable[VGAModeIndex];
 		break;
 	case DRM_FORMAT_RGB565:
+	case DRM_FORMAT_HOST_RGB565:
 		ast_state->std_table = &vbios_stdtable[HiCModeIndex];
 		break;
 	case DRM_FORMAT_RGB888:
+	case DRM_FORMAT_BGR888:
 	case DRM_FORMAT_XRGB8888:
+	case DRM_FORMAT_HOST_XRGB8888:
 		ast_state->std_table = &vbios_stdtable[TrueCModeIndex];
 		break;
 	default:
@@ -978,7 +1002,11 @@ static const struct drm_mode_config_helper_funcs ast_mode_config_helper_funcs =
 static enum drm_mode_status ast_mode_config_mode_valid(struct drm_device *dev,
 						       const struct drm_display_mode *mode)
 {
+#if defined(__BIG_ENDIAN) && defined(CONFIG_PCI_ARCH_ENDIAN_AUTOSWAP)
+	const struct drm_format_info *info = drm_format_info(DRM_FORMAT_HOST_XRGB8888);
+#else
 	const struct drm_format_info *info = drm_format_info(DRM_FORMAT_XRGB8888);
+#endif
 	struct ast_device *ast = to_ast_device(dev);
 	unsigned long max_fb_size = ast_fb_vram_size(ast);
 	u64 pitch;
@@ -1021,6 +1049,7 @@ int ast_mode_config_init(struct ast_device *ast)
 	dev->mode_config.min_width = 0;
 	dev->mode_config.min_height = 0;
 	dev->mode_config.preferred_depth = 24;
+	dev->mode_config.quirk_addfb_prefer_host_byte_order = true;
 
 	if (ast->support_fullhd) {
 		dev->mode_config.max_width = 1920;
-- 
2.51.0


More information about the Linuxppc-dev mailing list