Den 25.02.2021 10.58, skrev Peter Stuge:
Hi Noralf,
Noralf Trønnes wrote:
The driver supports a one bit monochrome transfer format: R1. This is not implemented in the gadget driver. It is added in preparation for future monochrome e-ink displays.
I forgot, but I have a two-tone (black/red) e-ink display here, and I also have a 3-bpp RGB TFT display.
Should we add maybe R2 and R3? (or R3/R8 for number of colours?)
I'm particularly considering the 3-bpp RGB panel for GUD use now, and while it will surely work with say a 16-bit RGB mode many bits will be wasted in the process.
What are your thoughts? Would you take a patch for that now, later, never?
I've been anticipating the need for more formats, but I didn't want to add them without having a user. Otherwise I could end up adding stuff that would never be used. If you can test, there's no problem adding support for more formats now.
The R1 name is derived from DRM_FORMAT_R8 which is a 8 bit monochrome (or one color channel) format.
Linux has these one byte color pixel formats currently defined:
/* color index */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */
/* 8 bpp Red */ #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */
/* 8 bpp RGB */ #define DRM_FORMAT_RGB332 fourcc_code('R', 'G', 'B', '8') /* [7:0] R:G:B 3:3:2 */ #define DRM_FORMAT_BGR233 fourcc_code('B', 'G', 'R', '8') /* [7:0] B:G:R 2:3:3 */
And these two color formats:
/* 16 bpp RG */ #define DRM_FORMAT_RG88 fourcc_code('R', 'G', '8', '8') /* [15:0] R:G 8:8 little endian */ #define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */
/* 32 bpp RG */ #define DRM_FORMAT_RG1616 fourcc_code('R', 'G', '3', '2') /* [31:0] R:G 16:16 little endian */ #define DRM_FORMAT_GR1616 fourcc_code('G', 'R', '3', '2') /* [31:0] G:R 16:16 little endian */
Building on that I would define a 2 bpp RG format like this in the driver:
static const struct drm_format_info gud_drm_format_rg11 = { .format = GUD_DRM_FORMAT_RG11, .num_planes = 1, .char_per_block = { 1, 0, 0 }, .block_w = { 4, 0, 0 }, /* 4 pixels per block/byte */ .block_h = { 1, 0, 0 }, .hsub = 1, .vsub = 1, };
And a 3 bpp RGB format like this:
static const struct drm_format_info gud_drm_format_rgb111 = { .format = GUD_DRM_FORMAT_RGB111, .num_planes = 1, .char_per_block = { 1, 0, 0 }, .block_w = { 2, 0, 0 }, /* 2 pixels per block/byte */ .block_h = { 1, 0, 0 }, .hsub = 1, .vsub = 1, };
The MIPI DBI standard defines 2 ways to transmit 2x 3-bpp pixels in one byte (X=pad bit): - Option 1: X X R1 G1 B1 R2 G2 B2 - Option 2: X R1 G1 B1 X R2 G2 B2
So maybe we should have GUD_DRM_FORMAT_RGB111_OPTION1 and GUD_DRM_FORMAT_RGB111_OPTION2? Or just use option 2 and let the display fix it up if needed?
What format does your 3 bpp display use?
And then something like this for the conversion function:
static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *format, u32 *src, struct drm_framebuffer *fb, struct drm_rect *rect) { unsigned int block_width = drm_format_info_block_width(format, 0); unsigned int x, y, width, height; u8 r, g, b, *block = dst; /* Assign to silence compiler warning */ size_t len;
WARN_ON_ONCE(format->char_per_block[0] != 1);
/* Start on a byte boundary */ rect->x1 = ALIGN_DOWN(rect->x1, block_width); width = drm_rect_width(rect); height = drm_rect_height(rect); len = drm_format_info_min_pitch(format, 0, width) * height;
for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { if (!(x % block_width)) { block = dst++; *block = 0; }
/* r,g,b are bytes so no need to mask out anything explicitly */ r = *src >> 16; g = *src >> 8; b = *src++;
switch (format->format) { case GUD_DRM_FORMAT_RG11: *block <<= 2; *block |= ((r >> 7) << 1) | (g >> 7); break; case GUD_DRM_FORMAT_RGB111_OPTION1: *block <<= 3; *block |= ((r >> 7) << 2) | ((g >> 7) << 1) | (b >> 7); break; case GUD_DRM_FORMAT_RGB111_OPTION2: *block <<= 4; *block |= ((r >> 7) << 2) | ((g >> 7) << 1) | (b >> 7); break; default: WARN_ON_ONCE(1); return len; }; } }
return len; }
Noralf.