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.
This commit is contained in:
Fabian Vogt 2018-07-25 17:52:08 +02:00
parent 3f8b688221
commit 662884a4c8
4 changed files with 79 additions and 6 deletions

View File

@ -55,6 +55,7 @@ struct fbdev_display {
unsigned int stride;
bool xrgb32;
bool rgb24;
bool rgb16;
unsigned int Bpp;
unsigned int off_r;

View File

@ -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)

View File

@ -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;

View File

@ -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 {