[PATCH 2/3] drm/tiny: ssd16xx: support panels whose RAM X order is reversed
From: LiangCheng Wang
Date: Thu Jul 30 2026 - 23:52:07 EST
Some panels have their first column wired to the controller's last source
output, so the controller scans RAM X in the opposite direction and the
image comes out horizontally mirrored. SSD1677 offers no source-direction
control that I could find - command 0x01 only affects the gate side - so it
has to be handled while packing the framebuffer.
Add an x_mirror flag to struct ssd16xx_device_config and honour it in both
branches of ssd16xx_convert_fb_to_1bpp(): the scanline order in the
landscape path, and the inner loop in the portrait one, which is what maps
to RAM X there. The DRM_FORMAT_R1 memcpy fast path cannot mirror while
copying, so it is skipped when the flag is set. Enable it for the
pixpaper-426m entry.
Tested on a Mayqueen PIXPAPER 4.26m (SSD1677, 800x480): a pattern with
four differently sized corner blocks renders correctly at rotation 0, and
a DT rotation of 90 gives a clean clockwise portrait. Doing only the
landscape path leaves the portrait output transposed.
Signed-off-by: LiangCheng Wang <zaq14760@xxxxxxxxx>
---
drivers/gpu/drm/tiny/ssd16xx.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/tiny/ssd16xx.c b/drivers/gpu/drm/tiny/ssd16xx.c
index 02a28c0e07085e35d5210b6e20c214b163ede88f..d311720bbfbe0dd288dbd588be02c79b5eab3eab 100644
--- a/drivers/gpu/drm/tiny/ssd16xx.c
+++ b/drivers/gpu/drm/tiny/ssd16xx.c
@@ -380,6 +380,9 @@ struct ssd16xx_device_config {
const u8 *booster_soft_start_data;
u8 booster_soft_start_len;
+ /* Controller scans this panel's RAM X in the reverse direction. */
+ bool x_mirror;
+
/* Panel-specific display mode (resolution and physical dimensions) */
const struct drm_display_mode *mode;
};
@@ -584,6 +587,7 @@ static const struct ssd16xx_device_config ssd16xx_device_configs[] = {
.default_refresh_mode_init = SSD16XX_REFRESH_FULL,
.red_supported = false,
.default_color_mode = SSD16XX_COLOR_MODE_BW,
+ .x_mirror = true,
.booster_soft_start_data = pixpaper426m_booster_soft_start,
.booster_soft_start_len = ARRAY_SIZE(pixpaper426m_booster_soft_start),
.mode = &pixpaper426m_mode,
@@ -1199,10 +1203,11 @@ static void ssd16xx_convert_fb_to_3color(u8 *bw_dst, u8 *red_dst,
static void ssd16xx_convert_fb_to_1bpp(u8 *dst, struct iosys_map *src,
struct drm_framebuffer *fb,
struct drm_rect *rect,
- unsigned int orientation)
+ unsigned int orientation,
+ bool x_mirror)
{
u32 format = fb->format->format;
- int x, y;
+ int x, y, i;
u8 byte = 0;
unsigned int bit_pos = 0;
unsigned int dst_idx = 0;
@@ -1224,7 +1229,8 @@ static void ssd16xx_convert_fb_to_1bpp(u8 *dst, struct iosys_map *src,
* if not, the generic pixel-by-pixel loop below handles non-aligned
* rects safely.
*/
- if (format == DRM_FORMAT_R1 && orientation == 0 && rect->x1 % 8 == 0) {
+ if (format == DRM_FORMAT_R1 && orientation == 0 && rect->x1 % 8 == 0 &&
+ !x_mirror) {
unsigned int src_pitch = fb->pitches[0];
unsigned int width_bytes = drm_rect_width(rect) / 8;
@@ -1246,7 +1252,8 @@ static void ssd16xx_convert_fb_to_1bpp(u8 *dst, struct iosys_map *src,
* The data entry mode and cursor position control scan direction.
*/
for (x = rect->x2 - 1; x >= (int)rect->x1; x--) {
- for (y = rect->y1; y < rect->y2; y++) {
+ for (i = 0; i < drm_rect_height(rect); i++) {
+ y = x_mirror ? rect->y2 - 1 - i : rect->y1 + i;
if (ssd16xx_pixel_is_white(src, fb, x, y))
byte |= (1 << (7 - bit_pos));
if (++bit_pos == 8) {
@@ -1272,7 +1279,8 @@ static void ssd16xx_convert_fb_to_1bpp(u8 *dst, struct iosys_map *src,
* The data entry mode and cursor position control scan direction.
*/
for (y = rect->y1; y < rect->y2; y++) {
- for (x = rect->x1; x < rect->x2; x++) {
+ for (i = 0; i < drm_rect_width(rect); i++) {
+ x = x_mirror ? rect->x2 - 1 - i : rect->x1 + i;
if (ssd16xx_pixel_is_white(src, fb, x, y))
byte |= (1 << (7 - bit_pos));
if (++bit_pos == 8) {
@@ -1345,7 +1353,9 @@ static int ssd16xx_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect,
if (red_buffer)
ssd16xx_convert_fb_to_3color(mono_buffer, red_buffer, &map, fb, rect);
else
- ssd16xx_convert_fb_to_1bpp(mono_buffer, &map, fb, rect, device->orientation);
+ ssd16xx_convert_fb_to_1bpp(mono_buffer, &map, fb, rect,
+ device->orientation,
+ device->device_cfg->x_mirror);
drm_dbg(&device->drm,
"fb_dirty: mono[0..3]=0x%02x 0x%02x 0x%02x 0x%02x (data_size=%u)\n",
--
2.34.1