From: Gabriel Valcazar <gabriel.valcazar@digi.com>
Date: Thu, 17 Sep 2020 13:16:04 +0200
Subject: [PATCH] libweston: g2d-renderer: try re-adjusting fb if the
 FBIOPAN_DISPLAY ioctl fails

By default, the g2d renderer works with 3 buffers, and uses the FBIOPAN_DISPLAY
ioctl to have the kernel switch between them everytime the output has to be
shown. Because of this, when the renderer is initialized, it will increase the
framebuffer's yres_virtual parameter so it is 3 times the size of yres
(vertical resolution).

However, when the system goes through a suspend/resume iteration, the
framebuffer's yres_virtual parameter will change back to its original value,
causing the FBIOPAN_DISPLAY ioctl to fail when using the 2nd and 3rd buffers,
which is 2 out of 3 times whenever something changes in the output. This has
three direct effects:

* Constant "FBIOPAN_DISPLAY failed" messages in the weston log
* Choppy framerate (due to the renderer only being able to show 1/3 of the
  total output frames)
* A 2/3 chance that the desktop will not show after resuming from suspend until
  there's movement on the screen

To avoid this, whenever a FBIOPAN_DISPLAY ioctl fails, check if the
yres_virtual attribute read from the kernel is different from the one saved in
the renderer and, if so, update it once again and retry the ioctl. This adds
some additional overhead in case of a FBIOPAN_DISPLAY failure, but these have
only been observed in the specific suspend/resume scenario, not in an average
use case.

The only drawback to this workaround is that there's a 1/3 chance that the
display will go blank for a very small period of time when resuming from
suspend. This is due to the framebuffer being reconfigured and, although
undesireable, is much less bothersome than the original issue.

https://jira.digi.com/browse/DEL-7236

Signed-off-by: Gabriel Valcazar <gabriel.valcazar@digi.com>
---
 libweston/renderer-g2d/g2d-renderer.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/libweston/renderer-g2d/g2d-renderer.c b/libweston/renderer-g2d/g2d-renderer.c
index 456190a3..cfc2e8b6 100644
--- a/libweston/renderer-g2d/g2d-renderer.c
+++ b/libweston/renderer-g2d/g2d-renderer.c
@@ -528,12 +528,34 @@ static void
 g2d_flip_surface(struct weston_output *output)
 {
 	struct g2d_output_state *go = get_output_state(output);
+	struct fb_var_screeninfo aux_varinfo;
 	go->fb_info.varinfo.yoffset  = go->activebuffer * go->fb_info.y_resolution;

 	if(ioctl(go->fb_info.fb_fd, FBIOPAN_DISPLAY, &(go->fb_info.varinfo)) < 0)
 	{
-		weston_log("FBIOPAN_DISPLAY Failed\n");
+		/* Check if yres_virtual has changed (it happens on suspend/resume) */
+		if (ioctl(go->fb_info.fb_fd, FBIOGET_VSCREENINFO, &aux_varinfo) < 0) {
+			weston_log("FBIOGET_VSCREENINFO Failed\n");
+			goto out;
+		}
+
+		/* If yres_virtual has changed, adjust it and try flipping the surface again */
+		if (aux_varinfo.yres_virtual != go->fb_info.varinfo.yres_virtual) {
+			aux_varinfo.yres_virtual = aux_varinfo.yres * go->nNumBuffers;
+			if (ioctl(go->fb_info.fb_fd, FBIOPUT_VSCREENINFO, &aux_varinfo) < 0) {
+				weston_log("FBIOPUT_VSCREENINFO Failed\n");
+				goto out;
+			}
+
+			if(ioctl(go->fb_info.fb_fd, FBIOPAN_DISPLAY, &(go->fb_info.varinfo)) < 0) {
+				weston_log("FBIOPAN_DISPLAY Failed\n");
+			}
+		} else {
+			weston_log("FBIOPAN_DISPLAY Failed\n");
+		}
 	}
+
+out:
 	go->activebuffer = (go->activebuffer + 1)  % go->nNumBuffers;
 }
