Portrait orientations are implemented by packing the framebuffer
column-major into the same physical RAM layout, so they need the
transposed mode that ssd16xx_connector_get_modes() reports, and a
framebuffer to match.

Setting the rotation property to 90 or 270 on its own provides neither:
the client keeps the mode and framebuffer it had while the driver switches
to portrait packing, and the result is displayed as garbage.  Nothing
reports an error, and data_size is identical either way so no size check
catches it.

Reject the combination in the CRTC atomic check, so userspace gets -EINVAL
and can re-probe and do a full modeset instead.  Rotation set through the
DT "rotation" property is unaffected, since probe() swaps the mode
dimensions before the connector is registered.

Tested on a Mayqueen PIXPAPER 4.26m (SSD1677, 800x480): rotation 90 at
runtime now fails with -EINVAL instead of corrupting the display, 180 still
applies, and DT rotation = <90> still comes up as 480x800.

Signed-off-by: LiangCheng Wang <[email protected]>
---
 drivers/gpu/drm/tiny/ssd16xx.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/drivers/gpu/drm/tiny/ssd16xx.c b/drivers/gpu/drm/tiny/ssd16xx.c
index 
d311720bbfbe0dd288dbd588be02c79b5eab3eab..1224a3d4ce2cbe70406dfa6e550fc85a2ea62682
 100644
--- a/drivers/gpu/drm/tiny/ssd16xx.c
+++ b/drivers/gpu/drm/tiny/ssd16xx.c
@@ -1730,6 +1730,40 @@ static enum drm_mode_status 
ssd16xx_crtc_mode_valid(struct drm_crtc *crtc,
 static int ssd16xx_crtc_atomic_check(struct drm_crtc *crtc,
                                     struct drm_atomic_commit *state)
 {
+       struct ssd16xx_device *device = crtc_to_ssd16xx_device(crtc);
+       struct drm_crtc_state *new_crtc_state =
+               drm_atomic_get_new_crtc_state(state, crtc);
+       struct drm_connector_state *new_conn_state;
+       unsigned int orientation;
+       bool want_portrait, mode_is_portrait;
+
+       if (!new_crtc_state->enable)
+               return 0;
+
+       new_conn_state = drm_atomic_get_new_connector_state(state,
+                                                           &device->connector);
+       if (!new_conn_state)
+               return 0;
+
+       /*
+        * Portrait packing needs the transposed mode that
+        * ssd16xx_connector_get_modes() reports; without it the client keeps a
+        * framebuffer that would be packed with the wrong geometry.
+        */
+       orientation = to_ssd16xx_connector_state(new_conn_state)->orientation;
+       want_portrait = (orientation == 90 || orientation == 270);
+       mode_is_portrait = new_crtc_state->mode.hdisplay <
+                          new_crtc_state->mode.vdisplay;
+
+       if (want_portrait != mode_is_portrait) {
+               drm_dbg(&device->drm,
+                       "atomic_check: %u° orientation needs a %s mode, but 
%ux%u is set\n",
+                       orientation, want_portrait ? "portrait" : "landscape",
+                       new_crtc_state->mode.hdisplay,
+                       new_crtc_state->mode.vdisplay);
+               return -EINVAL;
+       }
+
        return 0;
 }
 

-- 
2.34.1

Reply via email to