From 662884a4c8b7549684b0b81924d0660ffe2c472c Mon Sep 17 00:00:00 2001 From: Fabian Vogt Date: Wed, 25 Jul 2018 17:52:08 +0200 Subject: [PATCH] Initial support for 24bit fbdev This is necessary to provide output for cirrus. While there is some DRM support available, it's too limited for kmscon's drm2d and even then only supports 24bit out of the box. --- src/uterm_fbdev_internal.h | 1 + src/uterm_fbdev_render.c | 61 ++++++++++++++++++++++++++++++++++++++ src/uterm_fbdev_video.c | 22 ++++++++++---- src/uterm_video.h | 1 + 4 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/uterm_fbdev_internal.h b/src/uterm_fbdev_internal.h index f89a199..e263e4a 100644 --- a/src/uterm_fbdev_internal.h +++ b/src/uterm_fbdev_internal.h @@ -55,6 +55,7 @@ struct fbdev_display { unsigned int stride; bool xrgb32; + bool rgb24; bool rgb16; unsigned int Bpp; unsigned int off_r; diff --git a/src/uterm_fbdev_render.c b/src/uterm_fbdev_render.c index a06bf08..2dda19a 100644 --- a/src/uterm_fbdev_render.c +++ b/src/uterm_fbdev_render.c @@ -100,6 +100,21 @@ static uint_fast32_t xrgb32_to_device(struct uterm_display *disp, return res; } +static void write_24bit(uint8_t *dst, uint_fast32_t value) +{ + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + dst[0] = value; + dst[1] = value >> 8; + dst[2] = value >> 16; + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + dst[0] = value >> 16; + dst[1] = value >> 8; + dst[2] = value; + #else + #error "Unknown endianness" + #endif +} + int uterm_fbdev_display_blit(struct uterm_display *disp, const struct uterm_video_buffer *buf, unsigned int x, unsigned int y) @@ -151,6 +166,16 @@ int uterm_fbdev_display_blit(struct uterm_display *disp, dst += fbdev->stride; src += buf->stride; } + } else if (fbdev->Bpp == 3) { + while (height--) { + for (i = 0; i < width; ++i) { + val = ((uint32_t*)src)[i]; + uint_fast32_t full = xrgb32_to_device(disp, val); + write_24bit(&dst[i * 3], full); + } + dst += fbdev->stride; + src += buf->stride; + } } else if (fbdev->Bpp == 4) { while (height--) { for (i = 0; i < width; ++i) { @@ -272,6 +297,35 @@ int uterm_fbdev_display_fake_blendv(struct uterm_display *disp, dst += fbdev->stride; src += req->buf->stride; } + } else if (fbdev->Bpp == 3) { + while (height--) { + for (i = 0; i < width; ++i) { + if (src[i] == 0) { + r = req->br; + g = req->bg; + b = req->bb; + } else if (src[i] == 255) { + r = req->fr; + g = req->fg; + b = req->fb; + } else { + r = req->fr * src[i] + + req->br * (255 - src[i]); + r /= 256; + g = req->fg * src[i] + + req->bg * (255 - src[i]); + g /= 256; + b = req->fb * src[i] + + req->bb * (255 - src[i]); + b /= 256; + } + val = (r << 16) | (g << 8) | b; + uint_fast32_t full = xrgb32_to_device(disp, val); + write_24bit(&dst[i * 3], full); + } + dst += fbdev->stride; + src += req->buf->stride; + } } else if (fbdev->Bpp == 4) { while (height--) { for (i = 0; i < width; ++i) { @@ -358,6 +412,13 @@ int uterm_fbdev_display_fill(struct uterm_display *disp, dst += fbdev->stride; } } + } else if (fbdev->Bpp == 3) { + while (height--) { + for (i = 0; i < width * 3; i += 3) { + write_24bit(&dst[i], full_val); + } + dst += fbdev->stride; + } } else if (fbdev->Bpp == 4) { while (height--) { for (i = 0; i < width; ++i) diff --git a/src/uterm_fbdev_video.c b/src/uterm_fbdev_video.c index 06bddca..c977b8f 100644 --- a/src/uterm_fbdev_video.c +++ b/src/uterm_fbdev_video.c @@ -132,9 +132,7 @@ static int display_activate_force(struct uterm_display *disp, struct uterm_mode *mode, bool force) { - /* TODO: Add support for 24-bpp. However, we need to check how 3-bytes - * integers are assembled in big/little/mixed endian systems. */ - static const char depths[] = { 32, 16, 0 }; + static const char depths[] = { 32, 24, 16, 0 }; struct fbdev_display *dfb = disp->data; struct uterm_mode *m; struct fbdev_mode *mfb; @@ -217,14 +215,19 @@ static int display_activate_force(struct uterm_display *disp, if (finfo->visual != FB_VISUAL_TRUECOLOR || vinfo->bits_per_pixel != 32) { for (i = 0; depths[i]; ++i) { - vinfo->bits_per_pixel = depths[i]; - vinfo->activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; + /* Try to set a new mode and if it's successful... */ + struct fb_var_screeninfo vinfo_new = *vinfo; + vinfo_new.bits_per_pixel = depths[i]; + vinfo_new.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; ret = ioctl(dfb->fd, FBIOPUT_VSCREENINFO, - vinfo); + &vinfo_new); if (ret < 0) continue; + /* ... keep it. */ + *vinfo = vinfo_new; + ret = refresh_info(disp); if (ret) goto err_close; @@ -235,6 +238,7 @@ static int display_activate_force(struct uterm_display *disp, } if (vinfo->bits_per_pixel != 32 && + vinfo->bits_per_pixel != 24 && vinfo->bits_per_pixel != 16) { log_error("device %s does not support 16/32 bpp but: %u", dfb->node, vinfo->bits_per_pixel); @@ -333,6 +337,10 @@ static int display_activate_force(struct uterm_display *disp, dfb->off_r == 11 && dfb->off_g == 5 && dfb->off_b == 0 && dfb->Bpp == 2) dfb->rgb16 = true; + else if (dfb->len_r == 8 && dfb->len_g == 8 && dfb->len_b == 8 && + dfb->off_r == 16 && dfb->off_g == 8 && dfb->off_b == 0 && + dfb->Bpp == 3) + dfb->rgb24 = true; /* TODO: make dithering configurable */ disp->flags |= DISPLAY_DITHERING; @@ -455,6 +463,8 @@ static int display_get_buffers(struct uterm_display *disp, f = UTERM_FORMAT_XRGB32; else if (dfb->rgb16) f = UTERM_FORMAT_RGB16; + else if (dfb->rgb24) + f = UTERM_FORMAT_RGB24; if (!(formats & f)) return -EOPNOTSUPP; diff --git a/src/uterm_video.h b/src/uterm_video.h index 9f516c5..fe3cb78 100644 --- a/src/uterm_video.h +++ b/src/uterm_video.h @@ -100,6 +100,7 @@ enum uterm_video_format { UTERM_FORMAT_GREY = 0x01, UTERM_FORMAT_XRGB32 = 0x02, UTERM_FORMAT_RGB16 = 0x04, + UTERM_FORMAT_RGB24 = 0x08, }; struct uterm_video_buffer {