diff --git a/drivers/gpu/drm/clients/drm_splash.c b/drivers/gpu/drm/clients/drm_splash.c
index 0bf9d969dd8f..d3ed53cde0f5 100644
--- a/drivers/gpu/drm/clients/drm_splash.c
+++ b/drivers/gpu/drm/clients/drm_splash.c
@@ -61,6 +61,12 @@ struct drm_splash_scanout {
u32 format;
unsigned int width;
unsigned int height;
+ /*
+ * What the connector needs turned to be seen upright (panel orientation
+ * and video=...,rotate=), and the part of it the plane does not do in
+ * hardware, which is then done here.
+ */
+ unsigned int conn_rot, sw_rot;
struct drm_client_buffer *buffer;
bool bg_drawn;
bool img_drawn;@@ -109,14 +115,20 @@ struct drm_splash {
enum drm_splash_source src;
/*
- * Where the image goes, in screen pixels: a coordinate of -1 centres
- * it on that axis, and the offset is added afterwards. Only the device
- * tree asks for anything but the centre.
+ * Where the image goes, in pixels as the screen is seen: a coordinate
+ * of -1 centres it on that axis, and the offset is added afterwards.
+ * Only the device tree asks for anything but the centre; a BGRT image
+ * goes at the table offsets, on the firmware's screen.
*/
s32 pos_x, pos_y;
s32 off_x, off_y;
- /* One of DRM_MODE_ROTATE_*: turns the image, not the screen */
+ /*
+ * One of DRM_MODE_ROTATE_*: turns the image as it is seen, whichever
+ * way the panel is mounted.
+ */
unsigned int rotation;
+ /* What the BGRT bits ask for: the turn of the firmware's own picture */
+ unsigned int bgrt_rot;
/* RGB888, from the image source or the kernel's default */
u32 bg_color;
};@@ -529,6 +541,12 @@ static int drm_splash_fill_solid_color(struct drm_client_buffer *buffer,
}
#if IS_ENABLED(CONFIG_DRM_CLIENT_SPLASH_BMP_SUPPORT)
+/* Two DRM_MODE_ROTATE_* turns, one after the other */
+static unsigned int drm_splash_rotate_add(unsigned int a, unsigned int b)
+{
+ return BIT((ilog2(a) + ilog2(b)) & 3);
+}
+
/*
* Place the image along one axis of the screen. @pos is the coordinate asked
* for, or -1 to centre on that axis, and @off is added afterwards. Both may@@ -659,8 +677,11 @@ static int drm_splash_bmp_to_scanout(struct drm_splash *splash,
bool bmp_invert_y;
u32 bitmap_offset;
size_t row_len, pixels_len, end;
+ bool bgrt = splash->src == DRM_SPLASH_SRC_BGRT;
unsigned int x_pad, y_pad, img_w, img_h;
+ unsigned int frame, rot, vw, vh, x, y, w, h;
const u8 *image_data;
+ bool quarter;
struct iosys_map map;
struct drm_rect r;
u32 *img;
@@ -724,36 +745,41 @@ static int drm_splash_bmp_to_scanout(struct drm_splash *splash,
}
bmp_pitch = row_len & ~(size_t)3;
- /* The room the image takes up on screen, once turned */
+ /*
+ * Work as the screen is seen, then move the result into the buffer,
+ * which is turned from what is seen by whatever the connector needs
+ * and the plane does not do. A BGRT image is seen as the firmware drew
+ * it: on the unturned screen, but for the table's own orientation
+ * bits, where its offsets are given and where a logo for a panel
+ * mounted turned comes already turned unless the bits say otherwise.
+ */
+ frame = bgrt ? BIT((ilog2(splash->bgrt_rot) + ilog2(scanout->sw_rot) -
+ ilog2(scanout->conn_rot)) & 3) : scanout->sw_rot;
+ rot = drm_splash_rotate_add(splash->rotation, frame);
+ quarter = frame & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
+ vw = quarter ? scanout->height : scanout->width;
+ vh = quarter ? scanout->width : scanout->height;
+
+ /* The room the image takes up as seen, once turned */
if (splash->rotation & (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270)) {
- img_w = bmp_height;
- img_h = bmp_width;
+ w = bmp_height;
+ h = bmp_width;
} else {
- img_w = bmp_width;
- img_h = bmp_height;
+ w = bmp_width;
+ h = bmp_height;
}
- if (img_w > scanout->width || img_h > scanout->height) {
+ if (w > vw || h > vh) {
drm_err(client->dev, "splash: BMP image is too big for the screen\n");
return -EINVAL;
}
- if (splash->src == DRM_SPLASH_SRC_BGRT) {
- /*
- * The BGRT offsets place the upright image on the upright
- * screen, which for a panel mounted turned is not the frame
- * the buffer is scanned out in. Work in that frame, then move
- * the image's rectangle into the buffer's.
- */
- bool quarter = splash->rotation &
- (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270);
- unsigned int lw = quarter ? scanout->height : scanout->width;
- unsigned int lh = quarter ? scanout->width : scanout->height;
+ /* A BGRT image goes where the table says, on the firmware's screen */
+ if (bgrt) {
unsigned int xo = efi_bgrt_image_offset_x();
unsigned int yo = efi_bgrt_image_offset_y();
- if (xo > lw || bmp_width > lw - xo ||
- yo > lh || bmp_height > lh - yo) {
+ if (xo > vw - w || yo > vh - h) {
drm_err(client->dev, "splash: BGRT image would overflow\n");
return -EINVAL;
}@@ -769,36 +795,40 @@ static int drm_splash_bmp_to_scanout(struct drm_splash *splash,
* edge of the display, looking quite bad.
*/
- if (xo != (lw - bmp_width) / 2) {
+ if (xo != (vw - w) / 2) {
drm_err(client->dev, "splash: BGRT sanity check failed\n");
return -EINVAL;
}
#endif
- switch (splash->rotation) {
- case DRM_MODE_ROTATE_90:
- x_pad = yo;
- y_pad = lw - xo - bmp_width;
- break;
- case DRM_MODE_ROTATE_180:
- x_pad = lw - xo - bmp_width;
- y_pad = lh - yo - bmp_height;
- break;
- case DRM_MODE_ROTATE_270:
- x_pad = lh - yo - bmp_height;
- y_pad = xo;
- break;
- default:
- x_pad = xo;
- y_pad = yo;
- break;
- }
+ x = xo;
+ y = yo;
} else {
- x_pad = drm_splash_place_axis(splash->pos_x, splash->off_x,
- scanout->width, img_w);
- y_pad = drm_splash_place_axis(splash->pos_y, splash->off_y,
- scanout->height, img_h);
+ x = drm_splash_place_axis(splash->pos_x, splash->off_x, vw, w);
+ y = drm_splash_place_axis(splash->pos_y, splash->off_y, vh, h);
+ }
+
+ /* Into the buffer: turn the rectangle as the buffer is turned */
+ switch (frame) {
+ case DRM_MODE_ROTATE_90:
+ x_pad = y;
+ y_pad = vw - x - w;
+ break;
+ case DRM_MODE_ROTATE_180:
+ x_pad = vw - x - w;
+ y_pad = vh - y - h;
+ break;
+ case DRM_MODE_ROTATE_270:
+ x_pad = vh - y - h;
+ y_pad = x;
+ break;
+ default:
+ x_pad = x;
+ y_pad = y;
+ break;
}
+ img_w = quarter ? h : w;
+ img_h = quarter ? w : h;
image_data = data + bitmap_offset;
@@ -807,7 +837,7 @@ static int drm_splash_bmp_to_scanout(struct drm_splash *splash,
if (!img)
return -ENOMEM;
drm_splash_bmp_decode(img, image_data, bmp_pitch, bmp_width, bmp_height,
- bmp_invert_y, splash->rotation);
+ bmp_invert_y, rot);
ret = drm_client_buffer_vmap_local(buffer, &map);
if (ret) {@@ -868,7 +898,7 @@ static enum drm_splash_source drm_splash_pick_source(struct drm_splash *splash)
}
if (drm_splash_bgrt_available(splash)) {
- splash->rotation = drm_splash_bgrt_rotation();
+ splash->bgrt_rot = drm_splash_bgrt_rotation();
return DRM_SPLASH_SRC_BGRT;
}
@@ -987,10 +1017,31 @@ static void drm_splash_modeset_off(struct drm_device *dev,
modeset->fb = NULL;
}
+/*
+ * What the connector needs turned to be seen upright, and the part of it
+ * left for the splash to do. As fbdev: only an atomic commit turns the
+ * plane, and a reflection, which is not drawn here, means no turn at all.
+ */
+static void drm_splash_rotation(struct drm_client_dev *client,
+ struct drm_mode_set *modeset,
+ unsigned int *conn_rot, unsigned int *sw_rot)
+{
+ unsigned int rot = DRM_MODE_ROTATE_0;
+ bool hw = drm_client_rotation(modeset, &rot) &&
+ drm_drv_uses_atomic_modeset(client->dev);
+
+ if (!hw && (rot & ~DRM_MODE_ROTATE_MASK))
+ rot = DRM_MODE_ROTATE_0;
+
+ *conn_rot = rot & DRM_MODE_ROTATE_MASK;
+ *sw_rot = hw ? DRM_MODE_ROTATE_0 : *conn_rot;
+}
+
static int drm_splash_init_client(struct drm_splash *splash)
{
struct drm_client_dev *client = &splash->client;
struct drm_mode_set *modeset;
+ unsigned int conn_rot, sw_rot;
unsigned int fb_count = 0;
unsigned int i, k;
int err = -ENODEV;@@ -1046,6 +1097,16 @@ static int drm_splash_init_client(struct drm_splash *splash)
tiled->height = max(tiled->height, modeset->y +
modeset->mode->vdisplay);
tiled->modesets |= BIT(idx);
+
+ /*
+ * One buffer cannot suit tiles that need different
+ * turns: as fbdev, turn none of them in software
+ */
+ drm_splash_rotation(client, modeset, &conn_rot, &sw_rot);
+ if (conn_rot != tiled->conn_rot || sw_rot != tiled->sw_rot) {
+ tiled->conn_rot = DRM_MODE_ROTATE_0;
+ tiled->sw_rot = DRM_MODE_ROTATE_0;
+ }
continue;
}
@@ -1080,6 +1141,11 @@ static int drm_splash_init_client(struct drm_splash *splash)
scanout->format = format;
scanout->width = modeset->x + modeset->mode->hdisplay;
scanout->height = modeset->y + modeset->mode->vdisplay;
+ drm_splash_rotation(client, modeset, &scanout->conn_rot,
+ &scanout->sw_rot);
+ drm_dbg(client->dev, "splash: scanout %u: connector turned %u, here %u\n",
+ splash->n_scanout - 1, 90 * ilog2(scanout->conn_rot),
+ 90 * ilog2(scanout->sw_rot));
}
/* Now that the sizes are known, allocate one buffer per scanout */
--
2.39.5