Modesetting driver limits max framebuffer resolution (all monitors) to 4096x4096 on Intel Valleyview devices
Description
We have a Device with two DisplayPort connectors driven by a Intel x5-80000 CPU with integrated Valleyview graphic. If you connect 2 x 2160x1440 monitors with extended configuration the xrandr call fails and at best we have the screens mirrored (with Ubuntu Cosmic they remain black after this). After some searching and testing it was found that there seems to be a 4096x4096 limit (the ..x4096 part was not really checked). The xrandr reports a max supported framebuffer of 8192x8192. If disabling the GL acceleration with setting the LIBGL_ALWAYS_SOFTWARE environment variable to true all is working. The Intel Xorg driver is also working with this configuration but has other problems (screen goes black for 2-3 seconds and come back again) which the modesetting driver does not have (The reason for the intel driver problem is most probably the Valleview Mouse Plane workaround in the kernel with negative X coordinates. But this is another issue...).
Analyse
To debug the problem better we tried using MESA debug variables first but with nothing useful coming out of this. Tests were done with 2 x 4k@30 monitors connected (Position of the second monitor was 1920x0 as far as I remember). After this the DRM debug in the kernel was activated and we got following:
[ 1518.335834] [drm:intel_framebuffer_init [i915]] tiled pitch (23040) must be at most 16384
[ 1518.335891] [drm:drm_internal_framebuffer_create [drm]] could not create framebuffer
[ 1518.358821] [drm:intel_framebuffer_init [i915]] tiled pitch (23040) must be at most 16384
After some digging in Kernel we found that this is a hardware limit.
static
u32 intel_fb_pitch_limit(struct drm_i915_private *dev_priv,
uint64_t fb_modifier, uint32_t pixel_format)
{
u32 gen = INTEL_GEN(dev_priv);
if (gen >= 9) {
int cpp = drm_format_plane_cpp(pixel_format, 0);
/* "The stride in bytes must not exceed the of the size of 8K
* pixels and 32K bytes."
*/
return min(8192 * cpp, 32768);
} else if (gen >= 5 && !HAS_GMCH_DISPLAY(dev_priv)) {
return 32*1024;
} else if (gen >= 4) {
if (fb_modifier == I915_FORMAT_MOD_X_TILED)
return 16*1024;
else
return 32*1024;
} else if (gen >= 3) {
if (fb_modifier == I915_FORMAT_MOD_X_TILED)
return 8*1024;
else
return 16*1024;
} else {
/* XXX DSPC is limited to 4k tiled */
return 8*1024;
}
}
So if using tiled framebuffer you limit the max size of your screen configuration to 4096x4096 as it seem. This was somehow unexpected as the intel driver is capable of driving this. After some further digging in the intel xorg driver we found a table with the hardware limits and that the driver will switch to a Linear Framebuffer if hitting the limit. With the modesetting driver this is currently not possible.
Workaround
To work around this problem we added a new option to the modesetting driver which is far from ideal but helps for the moment. See patch below or the attached IGEL-add-linearfb-option-to-modesetting.diff(against Xorg-1.20.4):
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -131,6 +131,7 @@ static const OptionInfoRec Options[] = {
{OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE},
{OPTION_DOUBLE_SHADOW, "DoubleShadow", OPTV_BOOLEAN, {0}, FALSE},
+ {OPTION_USE_LINEAR_FB, "LinearFB", OPTV_BOOLEAN, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
@@ -982,6 +983,10 @@ PreInit(ScrnInfoPtr pScrn, int flags)
ms->drmmode.sw_cursor = TRUE;
}
+ if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_USE_LINEAR_FB, FALSE)) {
+ ms->drmmode.use_linear = TRUE;
+ }
+
ms->cursor_width = 64;
ms->cursor_height = 64;
ret = drmGetCap(ms->fd, DRM_CAP_CURSOR_WIDTH, &value);
--- a/hw/xfree86/drivers/modesetting/driver.h
+++ b/hw/xfree86/drivers/modesetting/driver.h
@@ -51,6 +51,7 @@ typedef enum {
OPTION_PAGEFLIP,
OPTION_ZAPHOD_HEADS,
OPTION_DOUBLE_SHADOW,
+ OPTION_USE_LINEAR_FB,
} modesettingOpts;
typedef struct
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -163,6 +163,13 @@ get_modifiers_set(ScrnInfoPtr scrn, uint
for (j = 0; j < iter->num_modifiers; j++) {
Bool found = FALSE;
+ if (drmmode->use_linear) {
+ if (iter->modifiers[j] == I915_FORMAT_MOD_X_TILED ||
+ iter->modifiers[j] == I915_FORMAT_MOD_Y_TILED ||
+ iter->modifiers[j] == I915_FORMAT_MOD_Y_TILED_CCS)
+ continue;
+ }
+
/* Don't choose multi-plane formats for our screen pixmap.
* These will get used with frontbuffer rendering, which will
* lead to worse-than-tearing with multi-plane formats, as the
@@ -1034,8 +1041,12 @@ drmmode_create_bo(drmmode_ptr drmmode, d
}
#endif
- bo->gbm = gbm_bo_create(drmmode->gbm, width, height, format,
- GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
+ if (drmmode->use_linear)
+ bo->gbm = gbm_bo_create(drmmode->gbm, width, height, format,
+ GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
+ else
+ bo->gbm = gbm_bo_create(drmmode->gbm, width, height, format,
+ GBM_BO_USE_RENDERING | GBM_BO_USE_SCANOUT);
bo->used_modifiers = FALSE;
return bo->gbm != NULL;
}
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -123,6 +123,8 @@ typedef struct {
Bool dri2_enable;
Bool present_enable;
+
+ Bool use_linear;
} drmmode_rec, *drmmode_ptr;
typedef struct {
Summary
The problem is caused by a hardware limit the modesetting driver is not aware of. The patch is merely a humble workaround to be able to use the modesetting driver in such cases at all. A probably better solution would be something like in the intel driver with a hardware limit table. Or something like if creating failed then try the linear or different Tiling cases. Not sure if the solution also could be something complete different but for this I have not enough knowledge of the Xorg internals.
If one needs hardware to test send me a notice.