[PATCH] fbconsole: implement panel orientation support
Sascha Hauer
s.hauer at pengutronix.de
Mon Sep 30 23:47:20 PDT 2024
This adds a fbconsolex.orientation property to rotate the console
by 90, 180 or 270 degrees in case the panel is not natively oriented.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
drivers/video/fbconsole.c | 328 ++++++++++++++++++++++++++++++++------
1 file changed, 278 insertions(+), 50 deletions(-)
diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c
index eec524318f..24592d00ed 100644
--- a/drivers/video/fbconsole.c
+++ b/drivers/video/fbconsole.c
@@ -18,6 +18,20 @@ enum state_t {
CSI_CNT,
};
+enum fbconsole_rotation {
+ FBCONSOLE_ROTATE_0,
+ FBCONSOLE_ROTATE_90,
+ FBCONSOLE_ROTATE_180,
+ FBCONSOLE_ROTATE_270,
+};
+
+static const char * const rotation_names[] = {
+ [FBCONSOLE_ROTATE_0] = "0",
+ [FBCONSOLE_ROTATE_90] = "90",
+ [FBCONSOLE_ROTATE_180] = "180",
+ [FBCONSOLE_ROTATE_270] = "270",
+};
+
struct fbc_priv {
struct console_device cdev;
struct fb_info *fb;
@@ -38,6 +52,7 @@ struct fbc_priv {
unsigned int cols, rows;
unsigned int x, y; /* cursor position */
+ unsigned int rotation;
enum state_t state;
int color;
@@ -124,6 +139,10 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
int line_length;
u32 color, bgcolor;
struct rgb *rgb;
+ int xstep;
+ int ystep;
+ int startx;
+ int starty;
buf = gui_screen_render_buffer(priv->sc);
@@ -144,13 +163,44 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
rgb = &colors[bgcolor];
bgcolor = gu_rgb_to_pixel(priv->fb, rgb->r, rgb->g, rgb->b, 0x0);
+ adr = buf;
+
+ switch (priv->rotation) {
+ case FBCONSOLE_ROTATE_0:
+ xstep = bpp;
+ ystep = line_length;
+ startx = x * priv->font->width;
+ starty = y * priv->font->height;
+ break;
+ case FBCONSOLE_ROTATE_90:
+ xstep = line_length;
+ ystep = -bpp;
+ startx = (priv->rows - y) * priv->font->height - 1;
+ starty = x * priv->font->width;
+ break;
+ case FBCONSOLE_ROTATE_180:
+ xstep = -bpp;
+ ystep = -line_length;
+ startx = (priv->cols - x) * priv->font->width - 1;
+ starty = (priv->rows - y) * priv->font->height - 1;
+ break;
+ case FBCONSOLE_ROTATE_270:
+ xstep = -line_length;
+ ystep = bpp;
+ startx = priv->font->height * y;
+ starty = (priv->cols - x) * priv->font->width - 1;
+ break;
+ default:
+ return;
+ }
+
+ adr += (priv->margin.left + startx) * bpp;
+ adr += (priv->margin.top + starty) * line_length;
+
for (i = 0; i < priv->font->height; i++) {
uint8_t mask = 0x80;
int j;
- adr = buf + line_length * (priv->margin.top + y * priv->font->height + i) +
- (priv->margin.left + x * priv->font->width) * bpp;
-
for (j = 0; j < priv->font->width; j++) {
if (!mask) {
inbuf++;
@@ -158,32 +208,107 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c)
}
if (*inbuf & mask)
- gu_set_pixel(priv->fb, adr, color);
+ gu_set_pixel(priv->fb, adr + j * xstep, color);
else
- gu_set_pixel(priv->fb, adr, bgcolor);
+ gu_set_pixel(priv->fb, adr + j * xstep, bgcolor);
- adr += priv->fb->bits_per_pixel >> 3;
mask >>= 1;
}
+ adr += ystep;
+
inbuf++;
mask = 0x80;
}
}
+static void fb_blit_area(struct fbc_priv *priv, int x, int y)
+{
+ int startx, starty, width, height, fw, fh;
+ int mx, my;
+
+ mx = priv->margin.left;
+ my = priv->margin.top;
+
+ fw = priv->font->width;
+ fh = priv->font->height;
+
+ switch (priv->rotation) {
+ case FBCONSOLE_ROTATE_0:
+ startx = mx + x * fw;
+ starty = my + y * fh;
+ width = fw;
+ height = fh;
+ break;
+ case FBCONSOLE_ROTATE_90:
+ startx = mx + (priv->rows - y - 1) * fh;
+ starty = my + x * fw;
+ width = fh;
+ height = fw;
+ break;
+ case FBCONSOLE_ROTATE_180:
+ startx = mx + (priv->cols - x - 1) * fw;
+ starty = my + (priv->rows - y - 1) * fh;
+ width = fw;
+ height = fh;
+ break;
+ case FBCONSOLE_ROTATE_270:
+ startx = mx + y * fh;
+ starty = my + (priv->cols - x - 1) * fw;
+ width = fh;
+ height = fw;
+ break;
+ default:
+ return;
+ }
+
+ gu_screen_blit_area(priv->sc, startx, starty, width, height);
+}
+
static void video_invertchar(struct fbc_priv *priv, int x, int y)
{
+ int startx, starty, width, height, fw, fh;
void *buf;
buf = gui_screen_render_buffer(priv->sc);
+ buf += priv->margin.top * priv->fb->line_length;
+ buf += priv->margin.left * (priv->fb->bits_per_pixel >> 3);
+
+ fw = priv->font->width;
+ fh = priv->font->height;
+
+ switch (priv->rotation) {
+ case FBCONSOLE_ROTATE_0:
+ startx = x * fw;
+ starty = y * fh;
+ width = fw;
+ height = fh;
+ break;
+ case FBCONSOLE_ROTATE_90:
+ startx = (priv->rows - y - 1) * fh;
+ starty = x * fw;
+ width = fh;
+ height = fw;
+ break;
+ case FBCONSOLE_ROTATE_180:
+ startx = (priv->cols - x - 1) * fw;
+ starty = (priv->rows - y - 1) * fh;
+ width = fw;
+ height = fh;
+ break;
+ case FBCONSOLE_ROTATE_270:
+ startx = y * fh;
+ starty = (priv->cols - x - 1) * fw;
+ width = fh;
+ height = fw;
+ break;
+ default:
+ return;
+ }
- gu_invert_area(priv->fb, buf, priv->margin.left + x * priv->font->width,
- priv->margin.top + y * priv->font->height,
- priv->font->width, priv->font->height);
- gu_screen_blit_area(priv->sc, priv->margin.left + x * priv->font->width,
- priv->margin.top + y * priv->font->height,
- priv->font->width, priv->font->height);
+ gu_invert_area(priv->fb, buf, startx, starty, width, height);
+ fb_blit_area(priv, x, y);
}
static void show_cursor(struct fbc_priv *priv, int x, int y)
@@ -192,6 +317,118 @@ static void show_cursor(struct fbc_priv *priv, int x, int y)
video_invertchar(priv, x, y);
}
+static void fb_scroll_up_0(struct fbc_priv *priv, void *adr, int width, int height)
+{
+ u32 line_length = priv->fb->line_length;
+ int line_height = line_length * priv->font->height;
+
+ if (!priv->margin.left && !priv->margin.right) {
+ memcpy(adr, adr + line_height, line_height * (priv->rows - 1));
+ memset(adr + line_height * (priv->rows - 1), 0, line_height);
+ } else {
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ int y;
+
+ for (y = 0; y < height - priv->font->height; y++) {
+ memcpy(adr, adr + line_height, width * bpp);
+ adr += line_length;
+ }
+
+ for (y = height - priv->font->height; y < height; y++) {
+ memset(adr, 0, width * bpp);
+ adr += line_length;
+ }
+ }
+}
+
+static void fb_scroll_up_180(struct fbc_priv *priv, void *adr, int width, int height)
+{
+ u32 line_length = priv->fb->line_length;
+ int line_height = line_length * priv->font->height;
+
+ if (!priv->margin.left && !priv->margin.right) {
+ memmove(adr + line_height, adr, line_height * (priv->rows - 1));
+ memset(adr, 0, line_height);
+ } else {
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ int y;
+
+ adr += (height - 1) * line_length;
+
+ for (y = height; y > priv->font->height; y--) {
+ memcpy(adr, adr - line_height, width * bpp);
+ adr -= line_length;
+ }
+
+ for (y = 0; y < priv->font->height; y++) {
+ memset(adr, 0, width * bpp);
+ adr -= line_length;
+ }
+ }
+}
+
+static void fb_scroll_up_90(struct fbc_priv *priv, void *adr, int width, int height)
+{
+ u32 line_length = priv->fb->line_length;
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ int y;
+
+ for (y = 0; y < priv->cols * priv->font->width; y++) {
+ memmove(adr + priv->font->height * bpp,
+ adr,
+ (priv->rows - 1) * priv->font->height * bpp);
+ memset(adr, 0, priv->font->height * bpp);
+ adr += line_length;
+ }
+}
+
+static void fb_scroll_up_270(struct fbc_priv *priv, void *adr, int width, int height)
+{
+ u32 line_length = priv->fb->line_length;
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ int y;
+
+ for (y = 0; y < priv->cols * priv->font->width; y++) {
+ memmove(adr,
+ adr + priv->font->height * bpp,
+ (priv->rows - 1) * priv->font->height * bpp);
+ memset(adr + priv->font->height * bpp * (priv->rows - 1),
+ 0x0,
+ priv->font->height * bpp);
+ adr += line_length;
+ }
+}
+
+static void fb_scroll_up(struct fbc_priv *priv)
+{
+ int width = priv->fb->xres - priv->margin.left - priv->margin.right;
+ int height = priv->fb->yres - priv->margin.top - priv->margin.bottom;
+ int bpp = priv->fb->bits_per_pixel >> 3;
+ void *adr;
+
+ adr = gui_screen_render_buffer(priv->sc);
+ adr += priv->margin.top * priv->fb->line_length;
+ adr += priv->margin.left * bpp;
+
+ switch (priv->rotation) {
+ case FBCONSOLE_ROTATE_0:
+ fb_scroll_up_0(priv, adr, width, height);
+ break;
+ case FBCONSOLE_ROTATE_90:
+ fb_scroll_up_90(priv, adr, width, height);
+ break;
+ case FBCONSOLE_ROTATE_180:
+ fb_scroll_up_180(priv, adr, width, height);
+ break;
+ case FBCONSOLE_ROTATE_270:
+ fb_scroll_up_270(priv, adr, width, height);
+ break;
+ }
+
+ gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top,
+ width, height);
+}
+
static void printchar(struct fbc_priv *priv, int c)
{
video_invertchar(priv, priv->x, priv->y);
@@ -222,11 +459,7 @@ static void printchar(struct fbc_priv *priv, int c)
default:
drawchar(priv, priv->x, priv->y, c);
-
- gu_screen_blit_area(priv->sc,
- priv->margin.left + priv->x * priv->font->width,
- priv->margin.top + priv->y * priv->font->height,
- priv->font->width, priv->font->height);
+ fb_blit_area(priv, priv->x, priv->y);
priv->x++;
if (priv->x >= priv->cols) {
@@ -236,37 +469,7 @@ static void printchar(struct fbc_priv *priv, int c)
}
if (priv->y >= priv->rows) {
- void *buf;
- void *adr;
- u32 line_length = priv->fb->line_length;
- int line_height = line_length * priv->font->height;
- int width = priv->fb->xres - priv->margin.left - priv->margin.right;
- int height = priv->rows * priv->font->height;
-
- buf = gui_screen_render_buffer(priv->sc);
- adr = buf + priv->margin.top * line_length;
-
- if (!priv->margin.left && !priv->margin.right) {
- memcpy(adr, adr + line_height, line_height * (priv->rows - 1));
- memset(adr + line_height * (priv->rows - 1), 0, line_height);
- } else {
- int bpp = priv->fb->bits_per_pixel >> 3;
- int y;
-
- adr += priv->margin.left * bpp;
-
- for (y = 0; y < height - priv->font->height; y++) {
- memcpy(adr, adr + line_height, width * bpp);
- adr += line_length;
- }
- for (y = height - priv->font->height; y < height; y++) {
- memset(adr, 0, width * bpp);
- adr += line_length;
- }
- }
-
- gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top,
- width, height);
+ fb_scroll_up(priv);
priv->y = priv->rows - 1;
}
@@ -472,8 +675,18 @@ static int setup_font(struct fbc_priv *priv)
priv->font = font;
- priv->rows = height / priv->font->height;
- priv->cols = width / priv->font->width;
+ switch (priv->rotation) {
+ case FBCONSOLE_ROTATE_0:
+ case FBCONSOLE_ROTATE_180:
+ priv->rows = height / priv->font->height;
+ priv->cols = width / priv->font->width;
+ break;
+ case FBCONSOLE_ROTATE_90:
+ case FBCONSOLE_ROTATE_270:
+ priv->rows = width / priv->font->height;
+ priv->cols = height / priv->font->width;
+ break;
+ }
return 0;
}
@@ -562,6 +775,18 @@ static int set_margin(struct param_d *p, void *vpriv)
return 0;
}
+static int set_rotation(struct param_d *p, void *vpriv)
+{
+ struct fbc_priv *priv = vpriv;
+
+ cls(priv);
+ priv->x = 0;
+ priv->y = 0;
+ setup_font(priv);
+
+ return 0;
+}
+
int register_fbconsole(struct fb_info *fb)
{
struct fbc_priv *priv;
@@ -621,6 +846,9 @@ int register_fbconsole(struct fb_info *fb)
NULL, &priv->margin.bottom, "%u", priv);
dev_add_param_uint32(&cdev->class_dev, "margin.right", set_margin,
NULL, &priv->margin.right, "%u", priv);
+ dev_add_param_enum(&cdev->class_dev, "rotation", set_rotation, NULL,
+ &priv->rotation, rotation_names,
+ ARRAY_SIZE(rotation_names), priv);
pr_info("registered as %s%d\n", cdev->class_dev.name, cdev->class_dev.id);
--
2.39.5
More information about the barebox
mailing list