Skip to contentSkip to conversation

drm: shmobile: Fix white screen after resume when LCDC is stopped

PATCH Superseded [email protected] 1 participant 2 messages Mar 19 – Apr 29

Subsystems (3)

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

PATCH v3 1/1 drm: shmobile: Fix white screen after resume when LCDC is stopped

From: bui duc phuc <[email protected]>

The LCDC on R8A7740 may show a completely white screen after resuming from suspend (s2idle or s2ram).

After resume, both Set A and Set B registers are reset to 0. As the Frame End interrupt is not generated while the controller is stopped (DO=0), the hardware register switching mechanism is not triggered. Consequently, Set A remains at 0x00000000 even though a valid Start Address is written to Set B, resulting in a white screen.

This is a timing-dependent race condition. In some configurations, debug options slow down the resume path enough for a Frame End interrupt to occur, which can mask the issue.

Fix this by priming both register sets with the Start Address while the controller is stopped, ensuring a valid base address is available immediately after resume.

---
Changes:
- v3
Update commit message, add Tested-by and Reviewed-by tags
- v2
Fix incorrect use of lcdc_write_mirror() for LDSA2R in the DO=0 path; use lcdc_write() to update both register sets as intended.

 .../gpu/drm/renesas/shmobile/shmob_drm_plane.c  | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)
drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c +13 -4
index b61fda52d17a..23c8489dba71 100644
@@ -70,6 +70,7 @@ static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
 	struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);
 	struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);
 	struct drm_framebuffer *fb = state->fb;
+	u32 ldcnt2r;
 
 	/* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
 	lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1);
@@ -78,11 +79,19 @@ static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
 	/* Word and long word swap. */
 	lcdc_write(sdev, LDDDSR, sstate->format->ldddsr);
 
-	lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);
-	if (shmob_drm_format_is_yuv(sstate->format))
-		lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);
+	ldcnt2r = lcdc_read(sdev, LDCNT2R);
+
+	if (ldcnt2r & LDCNT2R_DO) {
+		lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);
+		if (shmob_drm_format_is_yuv(sstate->format))
+			lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);
 
-	lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
+		lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
+	} else {
+		lcdc_write(sdev, LDSA1R, sstate->dma[0]);
+		if (shmob_drm_format_is_yuv(sstate->format))
+			lcdc_write(sdev, LDSA2R, sstate->dma[1]);
+	}
 }
 
 static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane,
Signature · 1 line
2.43.0
#1 lore raw

Hi all,

Just a gentle ping on this patch.

It has been reviewed and tested, but I haven't seen it being applied yet. Is there anything I should fix or resend?

Thanks!

Quoted 74 lines · On Thu, Mar 19, 2026 at 3:32 PM <[email protected]> wrote:

From: bui duc phuc <[email protected]>

The LCDC on R8A7740 may show a completely white screen after resuming from suspend (s2idle or s2ram).

After resume, both Set A and Set B registers are reset to 0. As the Frame End interrupt is not generated while the controller is stopped (DO=0), the hardware register switching mechanism is not triggered. Consequently, Set A remains at 0x00000000 even though a valid Start Address is written to Set B, resulting in a white screen.

This is a timing-dependent race condition. In some configurations, debug options slow down the resume path enough for a Frame End interrupt to occur, which can mask the issue.

Fix this by priming both register sets with the Start Address while the controller is stopped, ensuring a valid base address is available immediately after resume.

---
Changes:
- v3
Update commit message, add Tested-by and Reviewed-by tags
- v2
Fix incorrect use of lcdc_write_mirror() for LDSA2R in the DO=0 path; use lcdc_write() to update both register sets as intended.

 .../gpu/drm/renesas/shmobile/shmob_drm_plane.c  | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)
drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c +13 -5
index b61fda52d17a..23c8489dba71 100644
@@ -70,6 +70,7 @@ static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
        struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);
        struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);
        struct drm_framebuffer *fb = state->fb;
+       u32 ldcnt2r;
 
        /* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
        lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1);
@@ -78,11 +79,19 @@ static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
        /* Word and long word swap. */
        lcdc_write(sdev, LDDDSR, sstate->format->ldddsr);
 
-       lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);
-       if (shmob_drm_format_is_yuv(sstate->format))
-               lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);
+       ldcnt2r = lcdc_read(sdev, LDCNT2R);
+
+       if (ldcnt2r & LDCNT2R_DO) {
+               lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);
+               if (shmob_drm_format_is_yuv(sstate->format))
+                       lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);
 
-       lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
+               lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
+       } else {
+               lcdc_write(sdev, LDSA1R, sstate->dma[0]);
+               if (shmob_drm_format_is_yuv(sstate->format))
+                       lcdc_write(sdev, LDSA2R, sstate->dma[1]);
+       }
 }
 
 static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane,
--

2.43.0