Просмотр исходного кода

Merge branch 'starfive-kirkstone-ddk1.17' into 'starfive-kirkstone'

visionfive2: XFCE image support with DDK1.17

See merge request starfive-tech/meta-starfive!35
Rebecca Chang 11 месяцев назад
Родитель
Сommit
f1986f8042
100 измененных файлов с 21395 добавлено и 19 удалено
  1. 3 0
      conf/layer.conf
  2. 18 0
      conf/machine/starfive-visionfive2.conf
  3. 6 0
      recipes-bsp/common/visionfive2-firmware.inc
  4. 1 1
      recipes-bsp/opensbi/opensbi_%.bbappend
  5. 13 0
      recipes-bsp/u-boot/files/uboot_disable_logo.patch
  6. 5 4
      recipes-bsp/u-boot/u-boot-starfive.bb
  7. 5 2
      recipes-core/images/core-image-minimal-initramfs.bbappend
  8. 29 0
      recipes-core/images/core-image-minimal-xfce.bbappend
  9. 16 12
      recipes-core/images/core-image-minimal.bbappend
  10. 10 0
      recipes-core/images/sd-image.inc
  11. BIN
      recipes-graphics/drivers/files/img-gpu-powervr-bin-1.17.6210866.tar.gz
  12. 80 0
      recipes-graphics/drivers/visionfive2-pvr-graphics_1.17.bb
  13. 2 0
      recipes-graphics/libsdl2/libsdl2_2.0.20.bbappend
  14. 391 0
      recipes-graphics/mesa/mesa-pvr.inc
  15. 4689 0
      recipes-graphics/mesa/mesa-pvr/0001-Add-pvr-dri-driver.patch
  16. 32 0
      recipes-graphics/mesa/mesa-pvr/0002-Force-Mesa-to-use-the-PVR-driver-for-platform-device.patch
  17. 78 0
      recipes-graphics/mesa/mesa-pvr/0003-dri-Add-some-new-DRI-formats-and-fourccs.patch
  18. 101 0
      recipes-graphics/mesa/mesa-pvr/0004-GL_EXT_sparse_texture-entry-points.patch
  19. 104 0
      recipes-graphics/mesa/mesa-pvr/0005-Add-support-for-various-GLES-extensions.patch
  20. 185 0
      recipes-graphics/mesa/mesa-pvr/0006-Add-EGL_IMG_cl_image-extension.patch
  21. 49 0
      recipes-graphics/mesa/mesa-pvr/0007-egl-Be-stricter-when-making-a-context-current-withou.patch
  22. 44 0
      recipes-graphics/mesa/mesa-pvr/0008-egl-optimise-eglMakeCurrent-for-the-case-where-nothi.patch
  23. 81 0
      recipes-graphics/mesa/mesa-pvr/0009-GL_EXT_shader_pixel_local_storage2-entry-points.patch
  24. 82 0
      recipes-graphics/mesa/mesa-pvr/0010-GL_IMG_framebuffer_downsample-entry-points.patch
  25. 50 0
      recipes-graphics/mesa/mesa-pvr/0011-GL_OVR_multiview-entry-points.patch
  26. 65 0
      recipes-graphics/mesa/mesa-pvr/0012-Add-OVR_multiview_multisampled_render_to_texture.patch
  27. 55 0
      recipes-graphics/mesa/mesa-pvr/0013-wayland-drm-install-wayland-drm.xml-to-the-configure.patch
  28. 25 0
      recipes-graphics/mesa/mesa-pvr/0014-Enable-buffer-sharing-in-the-kms_swrast-driver.patch
  29. 43 0
      recipes-graphics/mesa/mesa-pvr/0015-egl-wayland-add-support-for-RGB565-back-buffers.patch
  30. 47 0
      recipes-graphics/mesa/mesa-pvr/0016-egl-dri3-fix-segfault-in-eglCopyBuffers.patch
  31. 76 0
      recipes-graphics/mesa/mesa-pvr/0017-egl-automatically-call-eglReleaseThread-on-thread-te.patch
  32. 1634 0
      recipes-graphics/mesa/mesa-pvr/0018-egl-add-Tizen-platform-support.patch
  33. 228 0
      recipes-graphics/mesa/mesa-pvr/0019-egl-add-support-for-EGL_TIZEN_image_native_surface.patch
  34. 31 0
      recipes-graphics/mesa/mesa-pvr/0020-egl-wayland-post-maximum-damage-when-blitting.patch
  35. 44 0
      recipes-graphics/mesa/mesa-pvr/0021-egl-wayland-flush-the-drawable-before-blitting.patch
  36. 377 0
      recipes-graphics/mesa/mesa-pvr/0022-egl-tizen-create-an-internal-_EGLImage-for-each-tbm-.patch
  37. 52 0
      recipes-graphics/mesa/mesa-pvr/0023-dri-use-a-supported-API-in-driCreateNewContext.patch
  38. 222 0
      recipes-graphics/mesa/mesa-pvr/0024-gbm-add-gbm_bo_blit.patch
  39. 42 0
      recipes-graphics/mesa/mesa-pvr/0025-gbm-don-t-assert-if-DRI-context-creation-fails.patch
  40. 469 0
      recipes-graphics/mesa/mesa-pvr/0026-egl-wayland-add-pbuffer-support.patch
  41. 119 0
      recipes-graphics/mesa/mesa-pvr/0027-egl-tizen-support-DRI-driver-handling-of-swap-preser.patch
  42. 27 0
      recipes-graphics/mesa/mesa-pvr/0028-egl-eglBindAPI-workaround-for-dEQP-bug.patch
  43. 53 0
      recipes-graphics/mesa/mesa-pvr/0029-GL_EXT_multi_draw_indirect-entry-points.patch
  44. 492 0
      recipes-graphics/mesa/mesa-pvr/0030-dri-add-support-for-YUV-DRI-config.patch
  45. 539 0
      recipes-graphics/mesa/mesa-pvr/0031-egl-add-support-for-EXT_yuv_surface.patch
  46. 33 0
      recipes-graphics/mesa/mesa-pvr/0032-dri-add-missing-__DRI_IMAGE_COMPONENTS-define-for-EG.patch
  47. 72 0
      recipes-graphics/mesa/mesa-pvr/0033-egl-wayland-expose-EXT_yuv_surface-support.patch
  48. 355 0
      recipes-graphics/mesa/mesa-pvr/0034-egl-tizen-expose-EXT_yuv_surface-support.patch
  49. 67 0
      recipes-graphics/mesa/mesa-pvr/0035-gbm-add-some-new-GBM-formats.patch
  50. 1488 0
      recipes-graphics/mesa/mesa-pvr/0036-egl-add-null-platform.patch
  51. 507 0
      recipes-graphics/mesa/mesa-pvr/0037-egl-add-config-debug-printout.patch
  52. 214 0
      recipes-graphics/mesa/mesa-pvr/0038-egl-add-support-for-EXT_image_gl_colorspace.patch
  53. 33 0
      recipes-graphics/mesa/mesa-pvr/0039-meson-force-C-2011-for-thread_local.patch
  54. 783 0
      recipes-graphics/mesa/mesa-pvr/0040-dri2-add-support-for-swap-intervals-other-than-1.patch
  55. 212 0
      recipes-graphics/mesa/mesa-pvr/0041-null_platform-add-support-for-explicit-synchronisati.patch
  56. 399 0
      recipes-graphics/mesa/mesa-pvr/0042-egl-null-add-support-for-DRM-image-format-modifiers.patch
  57. 201 0
      recipes-graphics/mesa/mesa-pvr/0043-egl-query-the-supported-ES2-context-version.patch
  58. 142 0
      recipes-graphics/mesa/mesa-pvr/0044-meson-allow-libGL-to-be-built-without-GLX.patch
  59. 36 0
      recipes-graphics/mesa/mesa-pvr/0045-egl-wayland-process-non-resized-window-movement.patch
  60. 537 0
      recipes-graphics/mesa/mesa-pvr/0046-Separate-EXT_framebuffer_object-from-ARB-version.patch
  61. 319 0
      recipes-graphics/mesa/mesa-pvr/0047-egl-null-add-support-for-async-flip-with-front-buffe.patch
  62. 227 0
      recipes-graphics/mesa/mesa-pvr/0048-gbm-add-pbuffer-support.patch
  63. 344 0
      recipes-graphics/mesa/mesa-pvr/0049-egl-null-expose-EXT_yuv_surface-support.patch
  64. 431 0
      recipes-graphics/mesa/mesa-pvr/0050-dri-preserve-the-original-FD-for-driver-use.patch
  65. 54 0
      recipes-graphics/mesa/mesa-pvr/0051-egl-wayland-a-linear-buffer-is-not-needed-with-DRM-f.patch
  66. 300 0
      recipes-graphics/mesa/mesa-pvr/0052-dri3-a-linear-buffer-is-not-needed-with-DRM-format-m.patch
  67. 265 0
      recipes-graphics/mesa/mesa-pvr/0053-egl-drm-add-support-for-DRI_PRIME-GPU-selection.patch
  68. 302 0
      recipes-graphics/mesa/mesa-pvr/0054-egl-null-add-support-for-DRI_PRIME-GPU-selection.patch
  69. 118 0
      recipes-graphics/mesa/mesa-pvr/0055-egl-null-introduce-NULL_DRM_DISPLAY.patch
  70. 34 0
      recipes-graphics/mesa/mesa-pvr/0056-vulkan-wsi-check-the-DRI3-and-Present-XCB-reply-poin.patch
  71. 141 0
      recipes-graphics/mesa/mesa-pvr/0057-vulkan-wsi-Allow-host-visible-memory-to-be-requested.patch
  72. 417 0
      recipes-graphics/mesa/mesa-pvr/0058-vulkan-wsi-make-the-display-FD-available.patch
  73. 1341 0
      recipes-graphics/mesa/mesa-pvr/0059-pvr-wsi-add-PowerVR-Vulkan-WSI-library.patch
  74. 74 0
      recipes-graphics/mesa/mesa-pvr/0060-vulkan-wsi-Disable-use-of-VK_EXT_pci_bus_info.patch
  75. 32 0
      recipes-graphics/mesa/mesa-pvr/0061-vulkan-wsi-default-to-force_bgra8_unorm_first-true.patch
  76. 62 0
      recipes-graphics/mesa/mesa-pvr/0062-vulkan-wsi-enable-additional-formats-for-Wayland.patch
  77. 25 0
      recipes-graphics/mesa/mesa-pvr/0063-vulkan-wsi-enable-additional-formats-for-Display.patch
  78. 180 0
      recipes-graphics/mesa/mesa-pvr/0064-mesa-main-dri-add-YUV420_3PLANE-and-YVU420_3PLANE.patch
  79. 55 0
      recipes-graphics/mesa/mesa-pvr/0065-egl-null-add-support-for-YU12-and-YV12.patch
  80. 57 0
      recipes-graphics/mesa/mesa-pvr/0066-mesa-partially-revert-pbuffer-attribute-removal.patch
  81. 64 0
      recipes-graphics/mesa/mesa-pvr/0067-egl_dri2-set-pbuffer-config-attribs-to-0-for-non-pbu.patch
  82. 143 0
      recipes-graphics/mesa/mesa-pvr/0068-GL_ARB_geometry_shader4-entry-points.patch
  83. 2 0
      recipes-graphics/mesa/mesa-pvr_21.2.1.bb
  84. 8 0
      recipes-graphics/x11-common/files/xserver-nodm-local.conf.in
  85. 12 0
      recipes-graphics/x11-common/files/xserver-nodm-local.service.in
  86. 12 0
      recipes-graphics/x11-common/xserver-nodm-init_%.bbappend
  87. 41 0
      recipes-graphics/xorg-xserver/xserver-xf86-config/xorg.conf
  88. 2 0
      recipes-graphics/xorg-xserver/xserver-xf86-config_%.bbappend
  89. 183 0
      recipes-graphics/xorg-xserver/xserver-xorg.inc
  90. 45 0
      recipes-graphics/xorg-xserver/xserver-xorg/0001-Avoid-duplicate-definitions-of-IOPortBase.patch
  91. 34 0
      recipes-graphics/xorg-xserver/xserver-xorg/0001-Fix-segfault-on-probing-a-non-PCI-platform-device-on.patch
  92. 23 0
      recipes-graphics/xorg-xserver/xserver-xorg/0001-drmmode_display.c-add-missing-mi.h-include.patch
  93. 36 0
      recipes-graphics/xorg-xserver/xserver-xorg/0001-test-xtest-Initialize-array-with-braces.patch
  94. 53 0
      recipes-graphics/xorg-xserver/xserver-xorg/0001-xf86pciBus.c-use-Intel-ddx-only-for-pre-gen4-hardwar.patch
  95. 34 0
      recipes-graphics/xorg-xserver/xserver-xorg/pkgconfig.patch
  96. 50 0
      recipes-graphics/xorg-xserver/xserver-xorg/sdksyms-no-build-path.patch
  97. 5 0
      recipes-graphics/xorg-xserver/xserver-xorg_%.bbappend
  98. 33 0
      recipes-graphics/xorg-xserver/xserver-xorg_1.20.13.bb
  99. 30 0
      recipes-graphics/xorg-xserver/xserver-xorg_21.1.4.bb
  100. 14 0
      recipes-kernel/firmware/initramfs-img-gpu-firmware.bb

+ 3 - 0
conf/layer.conf

@@ -15,3 +15,6 @@ BBFILE_PRIORITY_meta-starfive = "7"
 LAYERVERSION_meta-starfive = "1"
 
 LAYERSERIES_COMPAT_meta-starfive = "kirkstone"
+
+PACKAGE_CLASSES = "package_ipk"
+LICENSE_FLAGS_ACCEPTED += "commercial_ffmpeg"

+ 18 - 0
conf/machine/starfive-visionfive2.conf

@@ -12,10 +12,28 @@ SERIAL_CONSOLE = "115200 ttyS0"
 PREFERRED_PROVIDER_virtual/kernel = "linux-starfive-dev"
 PREFERRED_PROVIDER_virtual/bootloader = "u-boot-starfive"
 
+# For core-image-minimal-xfce
+PREFERRED_PROVIDER_virtual/libgl ?= "mesa-pvr"
+PREFERRED_PROVIDER_virtual/mesa ?= "mesa-pvr"
+PREFERRED_PROVIDER_virtual/libgbm ?= "mesa-pvr"
+PREFERRED_PROVIDER_virtual/egl ?= "mesa-pvr"
+PREFERRED_PROVIDER_virtual/libgles1 ?= "mesa-pvr"
+PREFERRED_PROVIDER_virtual/libgles3 ?= "mesa-pvr"
+PREFERRED_PROVIDER_virtual/libgles2 ?= "visionfive2-pvr-graphics"
+PREFERRED_PROVIDER_virtual/libomxil ?= "libsf-omxil"
+
 PREFERRED_VERSION_glibc ?= "2.35"
 PREFERRED_VERSION_gcc ?= "11.3.0"
 PREFERRED_VERSION_binutils ?= "2.38"
 PREFERRED_VERSION_opensbi ?= "1.2"
+PREFERRED_VERSION_xserver-xorg = "1.20.13"
+
+MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS += " \
+    kernel-modules \
+    vdec-module \
+    venc-module \
+    jpu-module \
+"
 
 RISCV_SBI_PLAT = "generic"
 RISCV_SBI_PAYLOAD = "u-boot.bin"

+ 6 - 0
recipes-bsp/common/visionfive2-firmware.inc

@@ -0,0 +1,6 @@
+# VF2_v3.0.4
+BRANCH = "JH7110_VisionFive2_devel"
+SRCREV = "a8d360763d203874fefe38fad2b041b98d674743"
+FORK = "starfive-tech"
+SRC_URI += "git://github.com/${FORK}/soft_3rdpart.git;protocol=https;lfs=1;branch=${BRANCH}"
+HOMEPAGE ?= "https://github.com/starfive-tech/soft_3rdpart"

+ 1 - 1
recipes-bsp/opensbi/opensbi_%.bbappend

@@ -1,5 +1,5 @@
 SRCREV:starfive-visionfive2 = "c6a092cd80112529cb2e92e180767ff5341b22a3"
-SRC_URI:starfive-visionfive2 = "git://git@192.168.110.45/sbc/opensbi.git;protocol=ssh \
+SRC_URI:starfive-visionfive2 = "git://github.com/starfive-tech/opensbi.git;protocol=https \
                                 file://visionfive2-uboot-fit-image.its \
                                "
 

+ 13 - 0
recipes-bsp/u-boot/files/uboot_disable_logo.patch

@@ -0,0 +1,13 @@
+diff --git a/drivers/video/starfive/sf_vop.c b/drivers/video/starfive/sf_vop.c
+index dbbccdf04e..73ad226339 100644
+--- a/drivers/video/starfive/sf_vop.c
++++ b/drivers/video/starfive/sf_vop.c
+@@ -617,7 +617,7 @@ static int sf_vop_probe(struct udevice *dev)
+ 
+ 	ofnode port, node;
+ 	int ret;
+-
++	return 0;
+ 	sf_vop_power(dev);
+ 	mdelay(50);
+ 

+ 5 - 4
recipes-bsp/u-boot/u-boot-starfive.bb

@@ -5,16 +5,17 @@ inherit uboot-extlinux-config
 
 FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
 
-FORK = "sbc"
-BRANCH = "vf2-devel"
+FORK = "starfive-tech"
+BRANCH = "JH7110_VisionFive2_devel"
+SRCREV = "b6e2b0e85c774a18ae668223a6e5f7d335895243"
 
-SRC_URI = "git://git@192.168.110.45/${FORK}/u-boot.git;protocol=ssh;branch=${BRANCH} \
+SRC_URI = "git://github.com/${FORK}/u-boot.git;protocol=https;branch=${BRANCH} \
            file://vf2_uEnv.txt \
 	   file://vf2_nvme_uEnv.txt \
 	   file://tftp-mmc-boot.txt \
 	   file://visionfive2-fit-image.its \
+	   file://uboot_disable_logo.patch \
           "
-SRCREV = "77781526125544acc873b51959f19bbdb50a4f2e"
 
 DEPENDS:append = " u-boot-tools-native starfive-tool-native"
 

+ 5 - 2
recipes-core/images/core-image-minimal-initramfs.bbappend

@@ -1,5 +1,8 @@
-# Manually adding riscv64-oe-linux as a compatible hosts
+# Install IMG GPU firmware into the initramfs
+PACKAGE_INSTALL += "initramfs-img-gpu-firmware"
+
+# Add riscv64-oe-linux as a compatible hosts
 COMPATIBLE_HOST = '(x86_64.*|i.86.*|arm.*|aarch64.*|riscv64.*)-(linux.*|freebsd.*)'
 
-# Manually avoid circular dependencies
+# Avoid circular dependencies
 EXTRA_IMAGEDEPENDS = ""

+ 29 - 0
recipes-core/images/core-image-minimal-xfce.bbappend

@@ -0,0 +1,29 @@
+require sd-image.inc
+
+DEPENDS += " opensbi starfive-tool-native u-boot-tools-native dtc-native"
+
+IMAGE_INSTALL += " \
+	helloworld \
+	coremark \
+	dhrystone \
+	perf \
+	gdb \
+	gdbserver \
+	util-linux \
+	ethtool \
+	bmap-tools \
+	systemd-analyze \
+	e2fsprogs-resize2fs \
+	mesa-pvr \
+	visionfive2-pvr-graphics \
+	glmark2 \
+	ffmpeg \
+	libsf-codaj12 \
+	libsf-omxil \
+	libsf-wave420l \
+	libsf-wave511 \
+	jpu-module \
+	vdec-module \
+	venc-module \
+	openssh \
+	"

+ 16 - 12
recipes-core/images/core-image-minimal.bbappend

@@ -1,14 +1,18 @@
-DEPENDS += " opensbi starfive-tool-native u-boot-tools-native dtc-native"
-
-IMAGE_INSTALL += "helloworld coremark dhrystone perf gdb gdbserver util-linux ethtool bmap-tools systemd-analyze"
+require sd-image.inc
 
-# Generate SD Card image and eMMC image
-# - Using spl_tool to fix img header to boot from eMMC
-
-do_vf2_sd (){
-	cd ${IMGDEPLOYDIR}
-	bmaptool copy core-image-minimal-starfive-visionfive2.wic.gz ${DEPLOY_DIR_IMAGE}/starfive-visionfive2-core-image-minimal.img
-	spl_tool -i -f ${DEPLOY_DIR_IMAGE}/starfive-visionfive2-core-image-minimal.img
-}
+DEPENDS += " opensbi starfive-tool-native u-boot-tools-native dtc-native"
 
-IMAGE_POSTPROCESS_COMMAND:starfive-visionfive2 += "do_vf2_sd;"
+IMAGE_INSTALL += " \
+	helloworld \
+	coremark \
+	dhrystone \
+	perf \
+	gdb \
+	gdbserver \
+	util-linux \
+	ethtool \
+	bmap-tools \
+	systemd-analyze \
+	openssh \
+	e2fsprogs-resize2fs \
+	"

+ 10 - 0
recipes-core/images/sd-image.inc

@@ -0,0 +1,10 @@
+# Generate SD Card image and eMMC image
+# - Using spl_tool to fix img header to boot from eMMC
+
+do_vf2_sd (){
+        cd ${IMGDEPLOYDIR}
+        bmaptool copy ${PN}-${MACHINE}.wic.gz ${DEPLOY_DIR_IMAGE}/${MACHINE}-${PN}.img
+        spl_tool -i -f ${DEPLOY_DIR_IMAGE}/${MACHINE}-${PN}.img
+}
+
+IMAGE_POSTPROCESS_COMMAND:starfive-visionfive2 += "do_vf2_sd;"

BIN
recipes-graphics/drivers/files/img-gpu-powervr-bin-1.17.6210866.tar.gz


+ 80 - 0
recipes-graphics/drivers/visionfive2-pvr-graphics_1.17.bb

@@ -0,0 +1,80 @@
+LICENSE="CLOSED"
+
+PROVIDES = "virtual/libgles2"
+COMPATIBLE_MACHINE = "starfive-visionfive2"
+
+inherit update-rc.d
+
+S = "${WORKDIR}/git"
+
+IMG_GPU_POWERVR_VERSION = "img-gpu-powervr-bin-1.17.6210866"
+
+PACKAGES += " \
+    ${PN}-firmware \
+    ${PN}-tools \
+"
+
+do_install () {
+    tar xz --no-same-owner -f ${THISDIR}/files/${IMG_GPU_POWERVR_VERSION}.tar.gz -C ${D}
+
+    # Remove .a files from tar file
+    rm ${D}/${IMG_GPU_POWERVR_VERSION}/target/usr/local/lib/*.a
+    rm ${D}/${IMG_GPU_POWERVR_VERSION}/target/usr/local/lib/*/*.a
+
+    # Remove files that has dependency on python
+    rm ${D}/${IMG_GPU_POWERVR_VERSION}/target/usr/local/bin/pvrlogsplit
+    rm ${D}/${IMG_GPU_POWERVR_VERSION}/target/usr/local/bin/hwperfjsonmerge.py
+
+    # Create a copy of GPU firmware at the deploy directory
+    cp -r ${D}/${IMG_GPU_POWERVR_VERSION}/target/lib ${DEPLOY_DIR}
+
+    # provided via separate arch-independent firmware package
+    mv ${D}/${IMG_GPU_POWERVR_VERSION}/target/* ${D}
+    rm -rf ${D}/lib/firmware
+    rmdir ${D}/lib
+
+    # cleanup unused
+    rm -rf ${D}/${IMG_GPU_POWERVR_VERSION}
+}
+
+INITSCRIPT_NAME = "rc.pvr"
+
+FILES_SOLIBSDEV = ""
+FILES:${PN} += " \
+    ${libdir}/*.so \
+    /usr/local/* \
+    /usr/lib/* \
+"
+
+FILES:${PN}-tools = " \
+    ${prefix}/local/bin/ \
+"
+
+FILES:${PN}-firmware = " \
+    ${base_libdir}/firmware/ \
+"
+
+RDEPENDS:${PN} += " \
+    bash \
+    libdrm \
+    glibc \
+    libgcc \
+    libstdc++ \
+    libudev \
+"
+
+RDEPENDS:${PN}-tools += " \
+    python3 \
+"
+
+# remove warning caused by wrong lib directory
+INSANE_SKIP:${PN} += "libdir"
+INSANE_SKIP:${PN}-dbg += "libdir"
+
+INSANE_SKIP:${PN} += "already-stripped dev-so"
+# ignore dependency check for python scripting
+INSANE_SKIP:${PN}-tools += "already-stripped file-rdeps"
+INSANE_SKIP:${PN}-firmware += "arch"
+
+# ignore "multiple shlib providers" error
+EXCLUDE_FROM_SHLIBS = "1"

+ 2 - 0
recipes-graphics/libsdl2/libsdl2_2.0.20.bbappend

@@ -0,0 +1,2 @@
+# To fix  "no accelerated colorspace conversion found" during ffplay, aka software decoding
+PACKAGECONFIG:remove = "opengl"

+ 391 - 0
recipes-graphics/mesa/mesa-pvr.inc

@@ -0,0 +1,391 @@
+SUMMARY = "PVR Mesa fork"
+DESCRIPTION = "Mesa is an open-source implementation of the OpenGL specification - \
+a system for rendering interactive 3D graphics.  \
+A variety of device drivers allows Mesa to be used in many different environments \
+ranging from software emulation to complete hardware acceleration for modern GPUs. \
+Mesa is used as part of the overall Direct Rendering Infrastructure and X.org \
+environment."
+
+HOMEPAGE = "http://mesa3d.org"
+BUGTRACKER = "https://bugs.freedesktop.org"
+SECTION = "x11"
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://docs/license.rst;md5=17a4ea65de7a9ab42437f3131e616a7f"
+
+PE = "2"
+
+SRC_URI = "https://mesa.freedesktop.org/archive/mesa-${PV}.tar.xz \
+        file://0001-Add-pvr-dri-driver.patch \
+        file://0002-Force-Mesa-to-use-the-PVR-driver-for-platform-device.patch \
+        file://0003-dri-Add-some-new-DRI-formats-and-fourccs.patch \
+        file://0004-GL_EXT_sparse_texture-entry-points.patch \
+        file://0005-Add-support-for-various-GLES-extensions.patch \
+        file://0006-Add-EGL_IMG_cl_image-extension.patch \
+        file://0007-egl-Be-stricter-when-making-a-context-current-withou.patch \
+        file://0008-egl-optimise-eglMakeCurrent-for-the-case-where-nothi.patch \
+        file://0009-GL_EXT_shader_pixel_local_storage2-entry-points.patch \
+        file://0010-GL_IMG_framebuffer_downsample-entry-points.patch \
+        file://0011-GL_OVR_multiview-entry-points.patch \
+        file://0012-Add-OVR_multiview_multisampled_render_to_texture.patch \
+        file://0013-wayland-drm-install-wayland-drm.xml-to-the-configure.patch \
+        file://0014-Enable-buffer-sharing-in-the-kms_swrast-driver.patch \
+        file://0015-egl-wayland-add-support-for-RGB565-back-buffers.patch \
+        file://0016-egl-dri3-fix-segfault-in-eglCopyBuffers.patch \
+        file://0017-egl-automatically-call-eglReleaseThread-on-thread-te.patch \
+        file://0018-egl-add-Tizen-platform-support.patch \
+        file://0019-egl-add-support-for-EGL_TIZEN_image_native_surface.patch \
+        file://0020-egl-wayland-post-maximum-damage-when-blitting.patch \
+        file://0021-egl-wayland-flush-the-drawable-before-blitting.patch \
+        file://0022-egl-tizen-create-an-internal-_EGLImage-for-each-tbm-.patch \
+        file://0023-dri-use-a-supported-API-in-driCreateNewContext.patch \
+        file://0024-gbm-add-gbm_bo_blit.patch \
+        file://0025-gbm-don-t-assert-if-DRI-context-creation-fails.patch \
+        file://0026-egl-wayland-add-pbuffer-support.patch \
+        file://0027-egl-tizen-support-DRI-driver-handling-of-swap-preser.patch \
+        file://0028-egl-eglBindAPI-workaround-for-dEQP-bug.patch \
+        file://0029-GL_EXT_multi_draw_indirect-entry-points.patch \
+        file://0030-dri-add-support-for-YUV-DRI-config.patch \
+        file://0031-egl-add-support-for-EXT_yuv_surface.patch \
+        file://0032-dri-add-missing-__DRI_IMAGE_COMPONENTS-define-for-EG.patch \
+        file://0033-egl-wayland-expose-EXT_yuv_surface-support.patch \
+        file://0034-egl-tizen-expose-EXT_yuv_surface-support.patch \
+        file://0035-gbm-add-some-new-GBM-formats.patch \
+        file://0036-egl-add-null-platform.patch \
+        file://0037-egl-add-config-debug-printout.patch \
+        file://0038-egl-add-support-for-EXT_image_gl_colorspace.patch \
+        file://0039-meson-force-C-2011-for-thread_local.patch \
+        file://0040-dri2-add-support-for-swap-intervals-other-than-1.patch \
+        file://0041-null_platform-add-support-for-explicit-synchronisati.patch \
+        file://0042-egl-null-add-support-for-DRM-image-format-modifiers.patch \
+        file://0043-egl-query-the-supported-ES2-context-version.patch \
+        file://0044-meson-allow-libGL-to-be-built-without-GLX.patch \
+        file://0045-egl-wayland-process-non-resized-window-movement.patch \
+        file://0046-Separate-EXT_framebuffer_object-from-ARB-version.patch \
+        file://0047-egl-null-add-support-for-async-flip-with-front-buffe.patch \
+        file://0048-gbm-add-pbuffer-support.patch \
+        file://0049-egl-null-expose-EXT_yuv_surface-support.patch \
+        file://0050-dri-preserve-the-original-FD-for-driver-use.patch \
+        file://0051-egl-wayland-a-linear-buffer-is-not-needed-with-DRM-f.patch \
+        file://0052-dri3-a-linear-buffer-is-not-needed-with-DRM-format-m.patch \
+        file://0053-egl-drm-add-support-for-DRI_PRIME-GPU-selection.patch \
+        file://0054-egl-null-add-support-for-DRI_PRIME-GPU-selection.patch \
+        file://0055-egl-null-introduce-NULL_DRM_DISPLAY.patch \
+        file://0056-vulkan-wsi-check-the-DRI3-and-Present-XCB-reply-poin.patch \
+        file://0057-vulkan-wsi-Allow-host-visible-memory-to-be-requested.patch \
+        file://0058-vulkan-wsi-make-the-display-FD-available.patch \
+        file://0059-pvr-wsi-add-PowerVR-Vulkan-WSI-library.patch \
+        file://0060-vulkan-wsi-Disable-use-of-VK_EXT_pci_bus_info.patch \
+        file://0061-vulkan-wsi-default-to-force_bgra8_unorm_first-true.patch \
+        file://0062-vulkan-wsi-enable-additional-formats-for-Wayland.patch \
+        file://0063-vulkan-wsi-enable-additional-formats-for-Display.patch \
+        file://0064-mesa-main-dri-add-YUV420_3PLANE-and-YVU420_3PLANE.patch \
+        file://0065-egl-null-add-support-for-YU12-and-YV12.patch \
+        file://0066-mesa-partially-revert-pbuffer-attribute-removal.patch \
+        file://0067-egl_dri2-set-pbuffer-config-attribs-to-0-for-non-pbu.patch \
+        file://0068-GL_ARB_geometry_shader4-entry-points.patch \
+           "
+
+S = "${WORKDIR}/mesa-${PV}"
+
+SRC_URI[sha256sum] = "2c65e6710b419b67456a48beefd0be827b32db416772e0e363d5f7d54dc01787"
+
+UPSTREAM_CHECK_GITTAGREGEX = "mesa-(?P<pver>\d+(\.\d+)+)"
+
+#because we cannot rely on the fact that all apps will use pkgconfig,
+#make eglplatform.h independent of MESA_EGL_NO_X11_HEADER
+do_install:append() {
+    if ${@bb.utils.contains('PACKAGECONFIG', 'egl', 'true', 'false', d)}; then
+        sed -i -e 's/^#elif defined(__unix__) && defined(EGL_NO_X11)$/#elif defined(__unix__) \&\& defined(EGL_NO_X11) || ${@bb.utils.contains('PACKAGECONFIG', 'x11', '0', '1', d)}/' ${D}${includedir}/EGL/eglplatform.h
+    fi
+}
+
+DEPENDS = "expat makedepend-native flex-native bison-native libxml2-native zlib chrpath-replacement-native python3-mako-native gettext-native"
+EXTRANATIVEPATH += "chrpath-native"
+# note: different to upstream Mesa, here is not PROVIDES for gles2, which conflicts with the way the visionfive2 binary driver is integrated
+PROVIDES = " \
+    ${@bb.utils.contains('PACKAGECONFIG', 'opengl', 'virtual/libgl', '', d)} \
+    ${@bb.utils.contains('PACKAGECONFIG', 'egl', 'virtual/egl', '', d)} \
+    ${@bb.utils.contains('PACKAGECONFIG', 'gbm', 'virtual/libgbm', '', d)} \
+    virtual/mesa \
+    "
+
+inherit meson pkgconfig python3native gettext features_check
+
+BBCLASSEXTEND = "native nativesdk"
+
+ANY_OF_DISTRO_FEATURES:class-target = "opengl vulkan"
+
+PLATFORMS ??= "${@bb.utils.filter('PACKAGECONFIG', 'x11 wayland', d)}"
+
+# By placing llvm-config in the target sysroot bindir, it will then map values
+# to the target libdir magically. We can safely add to path as there are no other binaries
+# there.
+PATH:prepend = "${STAGING_BINDIR_CROSS}:${STAGING_BINDIR}:"
+MESA_LLVM_RELEASE ?= "${LLVMVERSION}"
+do_configure:prepend () {
+	if [ -e ${STAGING_BINDIR_NATIVE}/llvm-config${MESA_LLVM_RELEASE} ]; then
+		cp ${STAGING_BINDIR_NATIVE}/llvm-config${MESA_LLVM_RELEASE} ${STAGING_BINDIR}
+		cp ${STAGING_BINDIR_NATIVE}/llvm-config ${STAGING_BINDIR}
+	fi
+}
+
+# set the MESA_BUILD_TYPE to either 'release' (default) or 'debug'
+# by default the upstream mesa sources build a debug release
+# here we assume the user will want a release build by default
+MESA_BUILD_TYPE ?= "release"
+def check_buildtype(d):
+    _buildtype = d.getVar('MESA_BUILD_TYPE')
+    if _buildtype not in ['release', 'debug']:
+        bb.fatal("unknown build type (%s), please set MESA_BUILD_TYPE to either 'release' or 'debug'" % _buildtype)
+    if _buildtype == 'debug':
+        return 'debugoptimized'
+    return 'plain'
+MESON_BUILDTYPE = "${@check_buildtype(d)}"
+
+EXTRA_OEMESON = " \
+    -Dshared-glapi=enabled \
+    -Dglx-read-only-text=true \
+    -Dplatforms='${@",".join("${PLATFORMS}".split())}' \
+    -Ddri-drivers=pvr \
+    -Dgles1=disabled \
+    -Dgles2=disabled \
+"
+
+def strip_comma(s):
+    return s.strip(',')
+
+# note: this is the exact config as needed and usable for the VisionFive2 board
+#       upstream Mesa provides much more flexibility which is ignored for sake of simplicity
+PACKAGECONFIG = "gallium x11 wayland opengl egl gbm dri3 "
+
+# "gbm" requires "opengl"
+PACKAGECONFIG[gbm] = "-Dgbm=enabled,-Dgbm=disabled"
+
+X11_DEPS = "xorgproto virtual/libx11 libxext libxxf86vm libxdamage libxfixes xrandr"
+# "x11" requires "opengl"
+PACKAGECONFIG[x11] = ",-Dglx=disabled,${X11_DEPS}"
+PACKAGECONFIG[xvmc] = "-Dgallium-xvmc=enabled,-Dgallium-xvmc=disabled,libxvmc"
+PACKAGECONFIG[wayland] = ",,wayland-native wayland libdrm wayland-protocols"
+
+PACKAGECONFIG[dri3] = "-Ddri3=enabled, -Ddri3=disabled, xorgproto libxshmfence"
+
+# Vulkan drivers need dri3 enabled
+# amd could be enabled as well but requires gallium-llvm with llvm >= 3.9
+VULKAN_DRIVERS = ""
+VULKAN_DRIVERS:append:x86:class-target = ",intel"
+VULKAN_DRIVERS:append:x86-64:class-target = ",intel"
+VULKAN_DRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'freedreno', ',freedreno', '', d)}"
+VULKAN_DRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'broadcom', ',broadcom', '', d)}"
+PACKAGECONFIG[vulkan] = "-Dvulkan-drivers=${@strip_comma('${VULKAN_DRIVERS}')}, -Dvulkan-drivers='',glslang-native vulkan-loader vulkan-headers"
+
+PACKAGECONFIG[opengl] = "-Dopengl=true, -Dopengl=false"
+
+# "gles" requires "opengl"
+PACKAGECONFIG[gles] = "-Dgles1=enabled -Dgles2=enabled, -Dgles1=disabled -Dgles2=disabled"
+
+# "egl" requires "opengl"
+PACKAGECONFIG[egl] = "-Degl=enabled, -Degl=disabled"
+
+# "opencl" requires libclc from meta-clang and spirv-tools from OE-Core
+OPENCL_NATIVE = "${@bb.utils.contains('PACKAGECONFIG', 'freedreno', '-Dopencl-native=true', '', d)}"
+PACKAGECONFIG[opencl] = "-Dgallium-opencl=icd -Dopencl-spirv=true ${OPENCL_NATIVE},-Dgallium-opencl=disabled -Dopencl-spirv=false,libclc spirv-tools"
+
+PACKAGECONFIG[broadcom] = ""
+PACKAGECONFIG[etnaviv] = ""
+PACKAGECONFIG[freedreno] = ""
+PACKAGECONFIG[kmsro] = ""
+PACKAGECONFIG[vc4] = ""
+PACKAGECONFIG[v3d] = ""
+PACKAGECONFIG[zink] = ""
+
+GALLIUMDRIVERS = "swrast"
+# gallium swrast was found to crash Xorg on startup in x32 qemu
+GALLIUMDRIVERS:x86-x32 = ""
+GALLIUMDRIVERS:append:x86:class-target = ",i915,iris,crocus"
+GALLIUMDRIVERS:append:x86-64:class-target = ",i915,iris,crocus"
+
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'etnaviv', ',etnaviv', '', d)}"
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'freedreno', ',freedreno', '', d)}"
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'kmsro', ',kmsro', '', d)}"
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'vc4', ',vc4', '', d)}"
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'v3d', ',v3d', '', d)}"
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'zink', ',zink', '', d)}"
+
+# radeonsi requires LLVM
+GALLIUMDRIVERS_RADEONSI = "${@bb.utils.contains('PACKAGECONFIG', 'r600', ',radeonsi', '', d)}"
+GALLIUMDRIVERS_LLVM = "r300,nouveau${GALLIUMDRIVERS_RADEONSI}"
+GALLIUMDRIVERS_LLVM:append:x86:class-target = ",svga"
+GALLIUMDRIVERS_LLVM:append:x86-64:class-target = ",svga"
+
+PACKAGECONFIG[r600] = ""
+PACKAGECONFIG[virgl] = ""
+
+GALLIUMDRIVERS:append = "${@bb.utils.contains('PACKAGECONFIG', 'gallium-llvm', ',${GALLIUMDRIVERS_LLVM}', '', d)}"
+GALLIUMDRIVERS:append = "${@bb.utils.contains('PACKAGECONFIG', 'r600', ',r600', '', d)}"
+GALLIUMDRIVERS:append = "${@bb.utils.contains('PACKAGECONFIG', 'virgl', ',virgl', '', d)}"
+
+PACKAGECONFIG[gallium] = "-Dgallium-drivers=${@strip_comma('${GALLIUMDRIVERS}')}, -Dgallium-drivers='', libdrm"
+PACKAGECONFIG[gallium-llvm] = "-Dllvm=enabled -Dshared-llvm=enabled, -Dllvm=disabled, llvm${MESA_LLVM_RELEASE} llvm-native \
+                               elfutils"
+PACKAGECONFIG[xa]  = "-Dgallium-xa=enabled, -Dgallium-xa=disabled"
+PACKAGECONFIG[va] = "-Dgallium-va=enabled,-Dgallium-va=disabled,libva-initial"
+
+PACKAGECONFIG[vdpau] = "-Dgallium-vdpau=enabled,-Dgallium-vdpau=disabled,libvdpau"
+
+PACKAGECONFIG[lima] = ""
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'lima', ',lima', '', d)}"
+
+PACKAGECONFIG[panfrost] = ""
+GALLIUMDRIVERS:append ="${@bb.utils.contains('PACKAGECONFIG', 'panfrost', ',panfrost', '', d)}"
+
+PACKAGECONFIG[osmesa] = "-Dosmesa=true,-Dosmesa=false"
+
+PACKAGECONFIG[perfetto] = "-Dperfetto=true,-Dperfetto=false,libperfetto"
+
+PACKAGECONFIG[unwind] = "-Dlibunwind=enabled,-Dlibunwind=disabled,libunwind"
+
+PACKAGECONFIG[lmsensors] = "-Dlmsensors=enabled,-Dlmsensors=disabled,lmsensors"
+
+# llvmpipe is slow if compiled with -fomit-frame-pointer (e.g. -O2)
+FULL_OPTIMIZATION:append = " -fno-omit-frame-pointer"
+
+CFLAGS:append:armv5 = " -DMISSING_64BIT_ATOMICS"
+CFLAGS:append:armv6 = " -DMISSING_64BIT_ATOMICS"
+
+# Remove the mesa dependency on mesa-dev, as mesa is empty
+DEV_PKG_DEPENDENCY = ""
+
+# Khronos documentation says that include/GLES2/gl2ext.h can be used for
+# OpenGL ES 3 specification as well as for OpenGL ES 2.
+# There can be applications including GLES2/gl2ext.h instead of GLES3/gl3ext.h
+# meaning we should probably bring in GLES2/gl2ext.h if someone asks for
+# development package of libgles3.
+RDEPENDS:libgles3-mesa-dev += "libgles2-mesa-dev"
+
+RDEPENDS:libopencl-mesa += "${@bb.utils.contains('PACKAGECONFIG', 'opencl', 'libclc spirv-tools', '', d)}"
+
+PACKAGES =+ "libegl-mesa libegl-mesa-dev \
+             libosmesa libosmesa-dev \
+             libgl-mesa libgl-mesa-dev \
+             libglapi libglapi-dev \
+             libgbm libgbm-dev \
+             libgles1-mesa libgles1-mesa-dev \
+             libgles2-mesa libgles2-mesa-dev \
+             libgles3-mesa libgles3-mesa-dev \
+             libopencl-mesa libopencl-mesa-dev \
+             libxatracker libxatracker-dev \
+             mesa-megadriver mesa-vulkan-drivers \
+             mesa-vdpau-drivers \
+            "
+
+do_install:append () {
+    # Drivers never need libtool .la files
+    rm -f ${D}${libdir}/dri/*.la
+    rm -f ${D}${libdir}/egl/*.la
+    rm -f ${D}${libdir}/gallium-pipe/*.la
+    rm -f ${D}${libdir}/gbm/*.la
+
+    # it was packaged in libdricore9.1.3-1 and preventing upgrades when debian.bbclass was used 
+    chrpath --delete ${D}${libdir}/dri/*_dri.so || true
+
+    # libwayland-egl has been moved to wayland 1.15+
+    rm -f ${D}${libdir}/libwayland-egl*
+    rm -f ${D}${libdir}/pkgconfig/wayland-egl.pc
+}
+
+# For the packages that make up the OpenGL interfaces, inject variables so that
+# they don't get Debian-renamed (which would remove the -mesa suffix), and
+# RPROVIDEs/RCONFLICTs on the generic libgl name.
+python __anonymous() {
+    pkgconfig = (d.getVar('PACKAGECONFIG') or "").split()
+    suffix = ""
+    if "-native" in d.getVar("PN"):
+        suffix = "-native"
+    for p in (("egl", "libegl", "libegl1"),
+              ("opengl", "libgl", "libgl1"),
+              ("gles", "libgles1", "libglesv1-cm1"),
+              ("gles", "libgles2", "libglesv2-2"),
+              ("gles", "libgles3",),
+              ("opencl", "libopencl",)):
+        if not p[0] in pkgconfig:
+            continue
+        mlprefix = d.getVar("MLPREFIX")
+        fullp = mlprefix + p[1] + "-mesa" + suffix
+        mlprefix = d.getVar("MLPREFIX")
+        pkgs = " " + " ".join(mlprefix + x + suffix for x in p[1:])
+        d.setVar("DEBIAN_NOAUTONAME:" + fullp, "1")
+        d.appendVar("RREPLACES:" + fullp, pkgs)
+        d.appendVar("RPROVIDES:" + fullp, pkgs)
+        d.appendVar("RCONFLICTS:" + fullp, pkgs)
+
+        d.appendVar("RRECOMMENDS:" + fullp, " ${MLPREFIX}mesa-megadriver" + suffix)
+
+        # For -dev, the first element is both the Debian and original name
+        fullp = mlprefix + p[1] + "-mesa-dev" + suffix
+        pkgs = " " + mlprefix + p[1] + "-dev" + suffix
+        d.setVar("DEBIAN_NOAUTONAME:" + fullp, "1")
+        d.appendVar("RREPLACES:" + fullp, pkgs)
+        d.appendVar("RPROVIDES:" + fullp, pkgs)
+        d.appendVar("RCONFLICTS:" + fullp, pkgs)
+}
+
+python mesa_populate_packages() {
+    pkgs = ['mesa', 'mesa-dev', 'mesa-dbg']
+    for pkg in pkgs:
+        d.setVar("RPROVIDES:%s" % pkg, pkg.replace("mesa", "mesa-dri", 1))
+        d.setVar("RCONFLICTS:%s" % pkg, pkg.replace("mesa", "mesa-dri", 1))
+        d.setVar("RREPLACES:%s" % pkg, pkg.replace("mesa", "mesa-dri", 1))
+
+    import re
+    dri_drivers_root = oe.path.join(d.getVar('PKGD'), d.getVar('libdir'), "dri")
+    if os.path.isdir(dri_drivers_root):
+        dri_pkgs = sorted(os.listdir(dri_drivers_root))
+        lib_name = d.expand("${MLPREFIX}mesa-megadriver")
+        for p in dri_pkgs:
+            m = re.match(r'^(.*)_dri\.so$', p)
+            if m:
+                pkg_name = " ${MLPREFIX}mesa-driver-%s" % legitimize_package_name(m.group(1))
+                d.appendVar("RPROVIDES:%s" % lib_name, pkg_name)
+                d.appendVar("RCONFLICTS:%s" % lib_name, pkg_name)
+                d.appendVar("RREPLACES:%s" % lib_name, pkg_name)
+
+    pipe_drivers_root = os.path.join(d.getVar('libdir'), "gallium-pipe")
+    do_split_packages(d, pipe_drivers_root, r'^pipe_(.*)\.so$', 'mesa-driver-pipe-%s', 'Mesa %s pipe driver', extra_depends='')
+}
+
+PACKAGESPLITFUNCS:prepend = "mesa_populate_packages "
+
+PACKAGES_DYNAMIC += "^mesa-driver-.*"
+PACKAGES_DYNAMIC:class-native = "^mesa-driver-.*-native"
+
+FILES:mesa-megadriver = "${libdir}/dri/* ${datadir}/drirc.d"
+FILES:mesa-vulkan-drivers = "${libdir}/libvulkan_*.so ${datadir}/vulkan"
+FILES:${PN}-vdpau-drivers = "${libdir}/vdpau/*.so.*"
+FILES:libegl-mesa = "${libdir}/libEGL.so.*"
+FILES:libgbm = "${libdir}/libgbm.so.*"
+FILES:libgles1-mesa = "${libdir}/libGLESv1*.so.*"
+FILES:libgles2-mesa = "${libdir}/libGLESv2.so.*"
+FILES:libgl-mesa = "${libdir}/libGL.so.*"
+FILES:libopencl-mesa = "${libdir}/libMesaOpenCL.so.* ${libdir}/gallium-pipe/*.so ${sysconfdir}/OpenCL/vendors/mesa.icd"
+FILES:libglapi = "${libdir}/libglapi.so.*"
+FILES:libosmesa = "${libdir}/libOSMesa.so.*"
+FILES:libxatracker = "${libdir}/libxatracker.so.*"
+
+FILES:${PN}-dev = "${libdir}/pkgconfig/dri.pc ${includedir}/vulkan ${libdir}/vdpau/*.so ${datadir}/mesa/wayland-drm.xml ${datadir}/pkgconfig/*"
+FILES:libegl-mesa-dev = "${libdir}/libEGL.* ${includedir}/EGL ${includedir}/KHR ${libdir}/pkgconfig/egl.pc"
+FILES:libgbm-dev = "${libdir}/libgbm.* ${libdir}/pkgconfig/gbm.pc ${includedir}/gbm.h"
+FILES:libgl-mesa-dev = "${libdir}/libGL.* ${includedir}/GL ${libdir}/pkgconfig/gl.pc"
+FILES:libglapi-dev = "${libdir}/libglapi.*"
+FILES:libgles1-mesa-dev = "${libdir}/libGLESv1*.* ${includedir}/GLES ${libdir}/pkgconfig/glesv1*.pc"
+FILES:libgles2-mesa-dev = "${libdir}/libGLESv2.* ${includedir}/GLES2 ${libdir}/pkgconfig/glesv2.pc"
+FILES:libgles3-mesa-dev = "${includedir}/GLES3"
+FILES:libopencl-mesa-dev = "${libdir}/libMesaOpenCL.so"
+FILES:libosmesa-dev = "${libdir}/libOSMesa.* ${includedir}/GL/osmesa.h ${libdir}/pkgconfig/osmesa.pc"
+FILES:libxatracker-dev = "${libdir}/libxatracker.so ${libdir}/libxatracker.la \
+                          ${includedir}/xa_tracker.h ${includedir}/xa_composite.h ${includedir}/xa_context.h \
+                          ${libdir}/pkgconfig/xatracker.pc"
+
+# Fix upgrade path from mesa to mesa-megadriver
+RREPLACES:mesa-megadriver = "mesa"
+RCONFLICTS:mesa-megadriver = "mesa"
+RPROVIDES:mesa-megadriver = "mesa"

+ 4689 - 0
recipes-graphics/mesa/mesa-pvr/0001-Add-pvr-dri-driver.patch

@@ -0,0 +1,4689 @@
+From a97794e871281ac4a8184e65f988bb6d37250500 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Tue, 14 Apr 2020 09:47:46 +0100
+Subject: [PATCH] Add pvr dri driver
+
+---
+ meson.build                               |  19 +-
+ meson_options.txt                         |   2 +-
+ src/mesa/drivers/dri/meson.build          |   5 +
+ src/mesa/drivers/dri/pvr/dri_support.h    | 581 +++++++++++++++
+ src/mesa/drivers/dri/pvr/img_drm_fourcc.h | 113 +++
+ src/mesa/drivers/dri/pvr/imgpixfmts.h     | 307 ++++++++
+ src/mesa/drivers/dri/pvr/imgyuv.h         |  58 ++
+ src/mesa/drivers/dri/pvr/mesa_context.c   | 208 ++++++
+ src/mesa/drivers/dri/pvr/meson.build      |  48 ++
+ src/mesa/drivers/dri/pvr/pvrcb.c          | 328 +++++++++
+ src/mesa/drivers/dri/pvr/pvrcompat.c      | 838 ++++++++++++++++++++++
+ src/mesa/drivers/dri/pvr/pvrdri.c         | 613 ++++++++++++++++
+ src/mesa/drivers/dri/pvr/pvrdri.h         | 188 +++++
+ src/mesa/drivers/dri/pvr/pvrdri_support.h | 206 ++++++
+ src/mesa/drivers/dri/pvr/pvrext.c         | 704 ++++++++++++++++++
+ src/mesa/drivers/dri/pvr/pvrmesa.h        |  36 +
+ src/mesa/drivers/dri/pvr/pvrutil.c        | 239 ++++++
+ src/meson.build                           |   1 +
+ 18 files changed, 4485 insertions(+), 9 deletions(-)
+ create mode 100644 src/mesa/drivers/dri/pvr/dri_support.h
+ create mode 100644 src/mesa/drivers/dri/pvr/img_drm_fourcc.h
+ create mode 100644 src/mesa/drivers/dri/pvr/imgpixfmts.h
+ create mode 100644 src/mesa/drivers/dri/pvr/imgyuv.h
+ create mode 100644 src/mesa/drivers/dri/pvr/mesa_context.c
+ create mode 100644 src/mesa/drivers/dri/pvr/meson.build
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrcb.c
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrcompat.c
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrdri.c
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrdri.h
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrdri_support.h
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrext.c
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrmesa.h
+ create mode 100644 src/mesa/drivers/dri/pvr/pvrutil.c
+
+diff --git a/meson.build b/meson.build
+index 155db28..968b4f5 100644
+--- a/meson.build
++++ b/meson.build
+@@ -171,11 +171,11 @@ if dri_drivers.contains('auto')
+   if system_has_kms_drm
+     # TODO: PPC, Sparc
+     if ['x86', 'x86_64'].contains(host_machine.cpu_family())
+-      dri_drivers = ['i915', 'i965', 'r100', 'r200', 'nouveau']
++      dri_drivers = ['i915', 'i965', 'r100', 'r200', 'nouveau', 'pvr']
+     elif ['arm', 'aarch64'].contains(host_machine.cpu_family())
+-      dri_drivers = []
++      dri_drivers = ['pvr']
+     elif ['mips', 'mips64', 'riscv32', 'riscv64'].contains(host_machine.cpu_family())
+-      dri_drivers = ['r100', 'r200', 'nouveau']
++      dri_drivers = ['r100', 'r200', 'nouveau', 'pvr']
+     else
+       error('Unknown architecture @0@. Please pass -Ddri-drivers to set driver options. Patches gladly accepted to fix this.'.format(
+             host_machine.cpu_family()))
+@@ -194,6 +194,7 @@ with_dri_i965 = dri_drivers.contains('i965')
+ with_dri_r100 = dri_drivers.contains('r100')
+ with_dri_r200 = dri_drivers.contains('r200')
+ with_dri_nouveau = dri_drivers.contains('nouveau')
++with_dri_pvr = dri_drivers.contains('pvr')
+ 
+ with_dri = dri_drivers.length() != 0
+ 
+@@ -1516,14 +1517,16 @@ _drm_amdgpu_ver = '2.4.107'
+ _drm_radeon_ver = '2.4.71'
+ _drm_nouveau_ver = '2.4.102'
+ _drm_intel_ver = '2.4.75'
++_drm_pvr_ver = '2.4.60'
+ _drm_ver = '2.4.81'
+ 
+ _libdrm_checks = [
+-  ['intel', with_dri_i915 or with_gallium_i915],
+-  ['amdgpu', (with_amd_vk and not with_platform_windows) or with_gallium_radeonsi],
++  ['intel', with_dri_i915 or with_gallium_i915, true],
++  ['amdgpu', (with_amd_vk and not with_platform_windows) or with_gallium_radeonsi, true],
+   ['radeon', (with_gallium_radeonsi or with_dri_r100 or with_dri_r200 or
+-              with_gallium_r300 or with_gallium_r600)],
+-  ['nouveau', (with_gallium_nouveau or with_dri_nouveau)],
++              with_gallium_r300 or with_gallium_r600), true],
++  ['nouveau', (with_gallium_nouveau or with_dri_nouveau), true],
++  ['pvr', with_dri_pvr, false],
+ ]
+ 
+ # VC4 only needs core libdrm support of this version, not a libdrm_vc4
+@@ -1553,7 +1556,7 @@ endif
+ 
+ # Then get each libdrm module
+ foreach d : _libdrm_checks
+-  if d[1]
++  if d[1] and d[2]
+     set_variable(
+       'dep_libdrm_' + d[0],
+       dependency('libdrm_' + d[0], version : '>=' + _drm_ver)
+diff --git a/meson_options.txt b/meson_options.txt
+index 29c402c..dac7910 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -55,7 +55,7 @@ option(
+   'dri-drivers',
+   type : 'array',
+   value : ['auto'],
+-  choices : ['auto', 'i915', 'i965', 'r100', 'r200', 'nouveau'],
++  choices : ['auto', 'i915', 'i965', 'r100', 'r200', 'nouveau', 'pvr'],
+   description : 'List of dri drivers to build. If this is set to auto all drivers applicable to the target OS/architecture will be built'
+ )
+ option(
+diff --git a/src/mesa/drivers/dri/meson.build b/src/mesa/drivers/dri/meson.build
+index 239ca96..88a973b 100644
+--- a/src/mesa/drivers/dri/meson.build
++++ b/src/mesa/drivers/dri/meson.build
+@@ -48,6 +48,11 @@ if with_dri_nouveau
+   _dri_drivers += libnouveau_vieux
+   _dri_link += 'nouveau_vieux_dri.so'
+ endif
++if with_dri_pvr
++  subdir('pvr')
++  _dri_drivers += libpvr
++  _dri_link += 'pvr_dri.so'
++endif
+ 
+ if _dri_drivers != []
+   libmesa_dri_drivers = shared_library(
+diff --git a/src/mesa/drivers/dri/pvr/dri_support.h b/src/mesa/drivers/dri/pvr/dri_support.h
+new file mode 100644
+index 0000000..6314104
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/dri_support.h
+@@ -0,0 +1,581 @@
++/* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
++/* vi: set ts=8 sw=8 sts=8: */
++/*************************************************************************/ /*!
++@File
++@Title          PVR DRI interface definition
++@Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
++@License        MIT
++
++The contents of this file are subject to the MIT license as set out below.
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
++*/ /**************************************************************************/
++
++#if !defined(__PVRDRIIFCE_H__)
++#define __PVRDRIIFCE_H__
++
++#include <stdint.h>
++#include <stdbool.h>
++
++/* API type. */
++typedef enum
++{
++	PVRDRI_API_NONE = 0,
++	PVRDRI_API_GLES1 = 2,
++	PVRDRI_API_GLES2 = 3,
++	PVRDRI_API_CL = 4,
++	PVRDRI_API_GL_COMPAT = 5,
++	PVRDRI_API_GL_CORE = 6,
++} PVRDRIAPIType;
++
++typedef enum
++{
++	PVRDRI_CONFIG_ATTRIB_INVALID = 0,
++	PVRDRI_CONFIG_ATTRIB_RENDERABLE_TYPE = 1,
++	PVRDRI_CONFIG_ATTRIB_RGB_MODE = 2,
++	PVRDRI_CONFIG_ATTRIB_DOUBLE_BUFFER_MODE = 3,
++	PVRDRI_CONFIG_ATTRIB_RED_BITS = 4,
++	PVRDRI_CONFIG_ATTRIB_GREEN_BITS = 5,
++	PVRDRI_CONFIG_ATTRIB_BLUE_BITS = 6,
++	PVRDRI_CONFIG_ATTRIB_ALPHA_BITS = 7,
++	PVRDRI_CONFIG_ATTRIB_RGB_BITS = 8,
++	PVRDRI_CONFIG_ATTRIB_DEPTH_BITS = 9,
++	PVRDRI_CONFIG_ATTRIB_STENCIL_BITS = 10,
++	PVRDRI_CONFIG_ATTRIB_SAMPLE_BUFFERS = 11,
++	PVRDRI_CONFIG_ATTRIB_SAMPLES = 12,
++	PVRDRI_CONFIG_ATTRIB_BIND_TO_TEXTURE_RGB = 13,
++	PVRDRI_CONFIG_ATTRIB_BIND_TO_TEXTURE_RGBA = 14,
++	PVRDRI_CONFIG_ATTRIB_YUV_ORDER = 15,
++	PVRDRI_CONFIG_ATTRIB_YUV_NUM_OF_PLANES = 16,
++	PVRDRI_CONFIG_ATTRIB_YUV_SUBSAMPLE = 17,
++	PVRDRI_CONFIG_ATTRIB_YUV_DEPTH_RANGE = 18,
++	PVRDRI_CONFIG_ATTRIB_YUV_CSC_STANDARD = 19,
++	PVRDRI_CONFIG_ATTRIB_YUV_PLANE_BPP = 20,
++	PVRDRI_CONFIG_ATTRIB_RED_MASK = 21,
++	PVRDRI_CONFIG_ATTRIB_GREEN_MASK = 22,
++	PVRDRI_CONFIG_ATTRIB_BLUE_MASK = 23,
++	PVRDRI_CONFIG_ATTRIB_ALPHA_MASK = 24,
++	PVRDRI_CONFIG_ATTRIB_SRGB_CAPABLE = 25
++} PVRDRIConfigAttrib;
++
++/* EGL_RENDERABLE_TYPE mask bits */
++#define PVRDRI_API_BIT_GLES		0x0001
++#define PVRDRI_API_BIT_GLES2		0x0004
++#define PVRDRI_API_BIT_GL		0x0008
++#define PVRDRI_API_BIT_GLES3		0x0040
++
++/* Mesa config formats. These need not match their MESA_FORMAT counterparts */
++#define	PVRDRI_MESA_FORMAT_NONE			0
++#define PVRDRI_MESA_FORMAT_B8G8R8A8_UNORM	1
++#define PVRDRI_MESA_FORMAT_B8G8R8X8_UNORM	2
++#define PVRDRI_MESA_FORMAT_B5G6R5_UNORM		3
++#define PVRDRI_MESA_FORMAT_R8G8B8A8_UNORM	4
++#define PVRDRI_MESA_FORMAT_R8G8B8X8_UNORM	5
++#define PVRDRI_MESA_FORMAT_YCBCR		6
++#define PVRDRI_MESA_FORMAT_YUV420_2PLANE	7
++#define PVRDRI_MESA_FORMAT_YVU420_2PLANE	8
++#define PVRDRI_MESA_FORMAT_B8G8R8A8_SRGB	9
++#define PVRDRI_MESA_FORMAT_R8G8B8A8_SRGB	10
++
++typedef struct __DRIimageRec __DRIimage;
++
++typedef struct PVRDRIConfigRec PVRDRIConfig;
++
++/* The PVRDRI_GL defines match their EGL_GL counterparts */
++#define PVRDRI_GL_RENDERBUFFER			0x30B9
++#define PVRDRI_GL_TEXTURE_2D			0x30B1
++#define PVRDRI_GL_TEXTURE_3D			0x30B2
++#define PVRDRI_GL_TEXTURE_CUBE_MAP_POSITIVE_X	0x30B3
++#define PVRDRI_GL_TEXTURE_CUBE_MAP_NEGATIVE_X	0x30B4
++#define PVRDRI_GL_TEXTURE_CUBE_MAP_POSITIVE_Y	0x30B5
++#define PVRDRI_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y	0x30B6
++#define PVRDRI_GL_TEXTURE_CUBE_MAP_POSITIVE_Z	0x30B7
++#define PVRDRI_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z	0x30B8
++
++struct __DRIscreenRec;
++struct __DRIcontextRec;
++struct __DRIdrawableRec;
++
++struct __DRIconfigRec;
++
++struct DRISUPScreen;
++struct DRISUPContext;
++struct DRISUPDrawable;
++struct DRISUPBuffer;
++
++struct PVRDRIContextConfig
++{
++	unsigned int uMajorVersion;
++	unsigned int uMinorVersion;
++	uint32_t uFlags;
++
++	int iResetStrategy;
++	unsigned int uPriority;
++	int iReleaseBehavior;
++};
++
++/*
++ * PVR DRI Support interface V2.
++ * This structure may change over time, as older interfaces become obsolete.
++ * For example, the v0 interface may be removed if superseded by newer
++ * interfaces.
++ */
++struct  PVRDRISupportInterfaceV2
++{
++	struct
++	{
++		struct DRISUPScreen *(*CreateScreen)
++			(struct __DRIscreenRec *psDRIScreen,
++			 int iFD,
++			 bool bUseInvalidate,
++			 void *pvLoaderPrivate,
++			 const struct __DRIconfigRec ***pppsConfigs,
++			 int *piMaxGLES1Version,
++			 int *piMaxGLES2Version);
++
++		void (*DestroyScreen)
++			(struct DRISUPScreen *psDRISUPScreen);
++
++		unsigned int (*CreateContext)
++			(PVRDRIAPIType eAPI,
++			 PVRDRIConfig *psPVRDRIConfig,
++			 struct PVRDRIContextConfig *psCtxConfig,
++			 struct __DRIcontextRec *psDRIContext,
++			 struct DRISUPContext *psDRISUPSharedContext,
++			 struct DRISUPScreen *psDRISUPScreen,
++			 struct DRISUPContext **ppsDRISUPContext);
++
++		void (*DestroyContext)
++			(struct DRISUPContext *psDRISUPContext);
++
++		struct DRISUPDrawable *(*CreateDrawable)
++			(struct __DRIdrawableRec *psDRIDrawable,
++			 struct DRISUPScreen *psDRISUPDrawable,
++			 void *pvLoaderPrivate,
++			 PVRDRIConfig *psPVRDRIConfig);
++
++		void (*DestroyDrawable)
++			(struct DRISUPDrawable *psDRISUPDrawable);
++
++		bool (*MakeCurrent)
++			(struct DRISUPContext *psDRISUPContext,
++			 struct DRISUPDrawable *psDRISUPWrite,
++			 struct DRISUPDrawable *psDRISUPRead);
++
++		bool (*UnbindContext)
++			(struct DRISUPContext *psDRISUPContext);
++
++		struct DRISUPBuffer *(*AllocateBuffer)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 unsigned int uAttachment,
++			 unsigned int uFormat,
++			 int iWidth,
++			 int iHeight,
++			 unsigned int *puName,
++			 unsigned int *puPitch,
++			 unsigned int *puCPP,
++			 unsigned int *puFlags);
++
++		void (*ReleaseBuffer)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 struct DRISUPBuffer *psDRISUPBuffer);
++
++		/* Functions to support the DRI TexBuffer extension */
++		void (*SetTexBuffer2)
++			(struct DRISUPContext *psDRISUPContext,
++			 int iTarget,
++			 int iFormat,
++			 struct DRISUPDrawable *psDRISUPDrawable);
++
++		void (*ReleaseTexBuffer)
++			(struct DRISUPContext *psDRISUPContext,
++			 int iTarget,
++			 struct DRISUPDrawable *psDRISUPDrawable);
++
++		/* Functions to support the DRI Flush extension */
++		void (*Flush)
++			(struct DRISUPDrawable *psDRISUPDrawable);
++
++		void (*Invalidate)
++			(struct DRISUPDrawable *psDRISUPDrawable);
++
++		void (*FlushWithFlags)
++			(struct DRISUPContext *psDRISUPContext,
++			 struct DRISUPDrawable *psDRISUPDrawable,
++			 unsigned int uFlags,
++			 unsigned int uThrottleReason);
++
++		/* Functions to support the DRI Image extension */
++		__DRIimage *(*CreateImageFromName)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 int iName,
++			 int iPitch,
++			 void *pvLoaderPrivate);
++
++		__DRIimage *(*CreateImageFromRenderbuffer)
++			(struct DRISUPContext *psDRISUPContext,
++			int iRenderBuffer,
++			void *pvLoaderPrivate);
++
++		void (*DestroyImage)
++			(__DRIimage *psImage);
++
++		__DRIimage *(*CreateImage)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 unsigned int uUse,
++			 void *pvLoaderPrivate);
++
++		bool (*QueryImage)
++			(__DRIimage *psImage,
++			 int iAttrib,
++			 int *iValue);
++
++		__DRIimage *(*DupImage)
++			(__DRIimage *psImage,
++			 void *pvLoaderPrivate);
++
++		bool (*ValidateImageUsage)
++			(__DRIimage *psImage,
++			 unsigned int uUse);
++
++		__DRIimage *(*CreateImageFromNames)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 int *piNames,
++			 int iNumNames,
++			 int *piStrides,
++			 int *piOffsets,
++			 void *pvLoaderPrivate);
++			 __DRIimage *(*FromPlanar)(__DRIimage *psImage,
++			 int iPlane,
++			 void *pvLoaderPrivate);
++
++		__DRIimage *(*CreateImageFromTexture)
++			(struct DRISUPContext *psDRISUPContext,
++			 int iTarget,
++			 unsigned int uTexture,
++			 int iDepth,
++			 int iLevel,
++			 unsigned int *puError,
++			 void *pvLoaderPrivate);
++
++		__DRIimage *(*CreateImageFromFDs)
++			(struct DRISUPScreen *psDRISUPcreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 int *piFDs,
++			 int iNumFDs,
++			 int *piStrides,
++			 int *piOffsets,
++			 void *pvLoaderPrivate);
++
++		__DRIimage *(*CreateImageFromDMABufs)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 int *piFDs,
++			 int iNumFDs,
++			 int *piStrides,
++			 int *piOffsets,
++			 unsigned int uColorSpace,
++			 unsigned int uSampleRange,
++			 unsigned int uHorizSiting,
++			 unsigned int uVertSiting,
++			 unsigned int *puError,
++			 void *pvLoaderPrivate);
++
++		int (*GetImageCapabilities)
++			(struct DRISUPScreen *psDRISUPScreen);
++
++		void (*BlitImage)
++			(struct DRISUPContext *psDRISUPContext,
++			 __DRIimage *psDst,
++			 __DRIimage *psSrc,
++			 int iDstX0,
++			 int iDstY0,
++			 int iDstWidth,
++			 int iDstHeight,
++			 int iSrcX0, int
++			 iSrcY0,
++			 int iSrcWidth,
++			 int iSrcHeight,
++			 int iFlushFlag);
++
++		void *(*MapImage)
++			(struct DRISUPContext *psDRISUPContext,
++			 __DRIimage *psImage,
++			 int iX0,
++			 int iY0,
++			 int iWidth,
++			 int iHeight,
++			 unsigned int uFlags,
++			 int *piStride,
++			 void **ppvData);
++
++		void (*UnmapImage)
++			(struct DRISUPContext *psDRISUPContext,
++			 __DRIimage *psImage,
++			 void *pvData);
++
++		__DRIimage *(*CreateImageWithModifiers)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 const uint64_t *puModifiers,
++			 const unsigned int uModifierCount,
++			 void *pvLoaderPrivate);
++
++		__DRIimage *(*CreateImageFromDMABufs2)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iWidth,
++			 int iHeight,
++			 int iFourCC,
++			 uint64_t uModifier,
++			 int *piFDs,
++			 int iNumFDs,
++			 int *piStrides,
++			 int *piOffsets,
++			 unsigned int uColorSpace,
++			 unsigned int uSampleRange,
++			 unsigned int uHorizSiting,
++			 unsigned int uVertSiting,
++			 unsigned int *puError,
++			 void *pvLoaderPrivate);
++
++		bool (*QueryDMABufFormats)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iMax,
++			 int *piFormats,
++			 int *piCount);
++
++		bool (*QueryDMABufModifiers)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iFourCC,
++			 int iMax,
++			 uint64_t *puModifiers,
++			 unsigned int *piExternalOnly,
++			 int *piCount);
++
++		bool (*QueryDMABufFormatModifierAttribs)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 uint32_t uFourcc,
++			 uint64_t uModifier,
++			 int iAttrib,
++			 uint64_t *puValue);
++
++		__DRIimage *(*CreateImageFromRenderBuffer2)
++			(struct DRISUPContext *psDRISUPContext,
++			 int iRenderBuffer,
++			 void *pvLoaderPrivate,
++			 unsigned int *puError);
++
++		__DRIimage *(*CreateImageFromBuffer)
++			(struct DRISUPContext *psDRISUPContext,
++			 int iTarget,
++			 void *pvBuffer,
++			 unsigned int *puError,
++			 void *pvLoaderPrivate);
++
++		/* Functions to support the DRI Renderer Query extension */
++		int (*QueryRendererInteger)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iAttribute,
++			 unsigned int *puValue);
++
++		int (*QueryRendererString)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 int iAttribute,
++			 const char **ppszValue);
++
++		/* Functions to support the DRI Fence extension */
++		void *(*CreateFence)
++			(struct DRISUPContext *psDRISUPContext);
++
++		void (*DestroyFence)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 void *pvFence);
++
++		bool (*ClientWaitSync)
++			(struct DRISUPContext *psDRISUPContext,
++			 void *pvFence,
++			 unsigned int uFlags,
++			 uint64_t uTimeout);
++
++		void (*ServerWaitSync)
++			(struct DRISUPContext *psDRISUPContext,
++			 void *pvFence,
++			 unsigned int uFlags);
++
++		unsigned int (*GetFenceCapabilities)
++			(struct DRISUPScreen *psDRISUPScreen);
++
++		void *(*CreateFenceFD)
++			(struct DRISUPContext *psDRISUPContext,
++			 int iFD);
++
++		int (*GetFenceFD)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 void *pvFence);
++
++		unsigned int (*GetNumAPIProcs)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 PVRDRIAPIType eAPI);
++
++		const char *(*GetAPIProcName)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 PVRDRIAPIType eAPI,
++			 unsigned int uIndex);
++
++		void *(*GetAPIProcAddress)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 PVRDRIAPIType eAPI,
++			 unsigned int uIndex);
++
++		void (*SetDamageRegion)
++			(struct DRISUPDrawable *psDRISUPDrawable,
++			 unsigned int uNRects,
++			 int *piRects);
++	} v0;
++	/* The v1 interface is an extension of v0, so v0 is required as well */
++	struct {
++		void *(*GetFenceFromCLEvent)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 intptr_t iCLEvent);
++	} v1;
++	/* The v2 interface is an extension of v1, so v1 is required as well */
++	struct {
++		int (*GetAPIVersion)
++			(struct DRISUPScreen *psDRISUPScreen,
++			 PVRDRIAPIType eAPI);
++	} v2;
++	/*
++	 * The v3 interface has no additional entry points. It indicates that
++	 * OpenCL event based fences are available, provided the DDK is built
++	 * with OpenCL support.
++	 */
++	/* The v4 interface is an extension of v3, so v3 is required as well */
++	struct {
++		bool (*HaveGetFenceFromCLEvent)(void);
++	} v4;
++};
++
++struct PVRDRIImageList {
++	uint32_t uImageMask;
++	__DRIimage *psBack;
++	__DRIimage *psFront;
++	__DRIimage *psPrev;
++};
++
++/*
++ * PVR DRI Support callback interface V2.
++ * This structure may change over time, as older interfaces become obsolete.
++ * For example, the v0 interface may be removed if superseded by newer
++ * interfaces.
++ */
++struct PVRDRICallbacksV2
++{
++	struct {
++		bool (*RegisterSupportInterface)
++			(const void *psInterface,
++			 unsigned int uVersion,
++			 unsigned int uMinVersion);
++
++		int (*GetBuffers)
++			(struct __DRIdrawableRec *psDRIDrawable,
++			 unsigned int uFourCC,
++			 uint32_t *puStamp,
++			 void *pvLoaderPrivate,
++			 uint32_t uBufferMask,
++			 struct PVRDRIImageList *psImageList);
++
++		bool (*CreateConfigs)
++			(struct __DRIconfigRec ***pppsConfigs,
++			 struct __DRIscreenRec *psDRIScreen,
++			 int iPVRDRIMesaFormat,
++			 const uint8_t *puDepthBits,
++			 const uint8_t *puStencilBits,
++			 unsigned int uNumDepthStencilBits,
++			 const unsigned int *puDBModes,
++			 unsigned int uNumDBModes,
++			 const uint8_t *puMSAASamples,
++			 unsigned int uNumMSAAModes,
++			 bool bEnableAccum,
++			 bool bColorDepthMatch,
++			 bool bMutableRenderBuffer,
++			 int iYUVDepthRange,
++			 int iYUVCSCStandard,
++			 uint32_t uMaxPbufferWidth,
++			 uint32_t uMaxPbufferHeight);
++
++		struct __DRIconfigRec **(*ConcatConfigs)
++			(struct __DRIscreenRec *psDRIScreen,
++			 struct __DRIconfigRec **ppsConfigA,
++			 struct __DRIconfigRec **ppsConfigB);
++
++		bool (*ConfigQuery)
++			(const PVRDRIConfig *psConfig,
++			 PVRDRIConfigAttrib eConfigAttrib,
++			 unsigned int *puValueOut);
++
++		__DRIimage *(*LookupEGLImage)
++			(struct __DRIscreenRec *psDRIScreen,
++			 void *pvImage,
++			 void *pvLoaderPrivate);
++
++		unsigned int (*GetCapability)
++			(struct __DRIscreenRec *psDRIScreen,
++			 unsigned int uCapability);
++	} v0;
++	/* The v1 interface is an extension of v0, so v0 is required as well */
++	struct {
++		void (*FlushFrontBuffer)
++			(struct __DRIdrawableRec *psDRIDrawable,
++			 void *pvLoaderPrivate);
++	} v1;
++	/* The v2 interface is an extension of v1, so v1 is required as well */
++	struct {
++		int (*GetDisplayFD)
++			(struct __DRIscreenRec *psDRIScreen,
++			 void *pvLoaderPrivate);
++	} v2;
++	/* The v3 interface is an extension of v2, so v2 is required as well */
++	struct {
++		void *(*DrawableGetReferenceHandle)
++			(struct __DRIdrawableRec *psDRIDrawable);
++
++		void (*DrawableAddReference)
++			(void *pvReferenceHandle);
++
++		void (*DrawableRemoveReference)
++			(void *pvReferenceHandle);
++	} v3;
++};
++
++#endif /* defined(__PVRDRIIFCE_H__) */
+diff --git a/src/mesa/drivers/dri/pvr/img_drm_fourcc.h b/src/mesa/drivers/dri/pvr/img_drm_fourcc.h
+new file mode 100644
+index 0000000..8d570ff
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/img_drm_fourcc.h
+@@ -0,0 +1,113 @@
++/*************************************************************************/ /*!
++@File
++@Title          Wrapper around drm_fourcc.h
++@Description    FourCCs and DRM framebuffer modifiers that are not in the
++                Kernel's and libdrm's drm_fourcc.h can be added here.
++@Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
++@License        MIT
++
++The contents of this file are subject to the MIT license as set out below.
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
++*/ /**************************************************************************/
++
++#ifndef IMG_DRM_FOURCC_H
++#define IMG_DRM_FOURCC_H
++
++#if defined(__KERNEL__)
++#include <drm/drm_fourcc.h>
++#else
++/*
++ * Include types.h to workaround versions of libdrm older than 2.4.68
++ * not including the correct headers.
++ */
++#include <linux/types.h>
++
++#include <drm_fourcc.h>
++#endif
++
++/*
++ * Don't get too inspired by this example :)
++ * ADF doesn't support DRM modifiers, so the memory layout had to be
++ * included in the fourcc name, but the proper way to specify information
++ * additional to pixel formats is to use DRM modifiers.
++ *
++ * See upstream drm_fourcc.h for the proper naming convention.
++ */
++#ifndef DRM_FORMAT_BGRA8888_DIRECT_16x4
++#define DRM_FORMAT_BGRA8888_DIRECT_16x4 fourcc_code('I', 'M', 'G', '0')
++#endif
++
++/*
++ * Upstream doesn't have a floating point format yet, so let's make one
++ * up.
++ * Note: The kernel's core DRM needs to know about this format,
++ * otherwise it won't be supported and should not be exposed by our
++ * kernel modules either.
++ * Refer to the provided kernel patch adding this format.
++ */
++#if !defined(__KERNEL__)
++#define DRM_FORMAT_ABGR16_IMG fourcc_code('I', 'M', 'G', '1')
++#endif
++
++/*
++ * Upstream does not have a packed 10 Bits Per Channel YVU format yet,
++ * so let`s make one up.
++ * Note: at the moment this format is not intended to be used with
++ *       a framebuffer, so the kernels core DRM doesn`t need to know
++ *       about this format. This means that the kernel doesn`t need
++ *       to be patched.
++ */
++#if !defined(__KERNEL__)
++#define DRM_FORMAT_YVU444_PACK10_IMG fourcc_code('I', 'M', 'G', '2')
++#define DRM_FORMAT_YUV422_2PLANE_PACK10_IMG fourcc_code('I', 'M', 'G', '3')
++#define DRM_FORMAT_YUV420_2PLANE_PACK10_IMG fourcc_code('I', 'M', 'G', '4')
++#endif
++
++/*
++ * Value chosen in the middle of 255 pool to minimise the chance of hitting
++ * the same value potentially defined by other vendors in the drm_fourcc.h
++ */
++#define DRM_FORMAT_MOD_VENDOR_PVR 0x92
++
++#ifndef DRM_FORMAT_MOD_VENDOR_NONE
++#define DRM_FORMAT_MOD_VENDOR_NONE 0
++#endif
++
++#ifndef DRM_FORMAT_RESERVED
++#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1)
++#endif
++
++#ifndef fourcc_mod_code
++#define fourcc_mod_code(vendor, val) \
++	((((__u64)DRM_FORMAT_MOD_VENDOR_## vendor) << 56) | (val & 0x00ffffffffffffffULL))
++#endif
++
++#ifndef DRM_FORMAT_MOD_INVALID
++#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED)
++#endif
++
++#ifndef DRM_FORMAT_MOD_LINEAR
++#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0)
++#endif
++
++#define DRM_FORMAT_MOD_PVR_FBCDC_8x8_V7      fourcc_mod_code(PVR, 6)
++#define DRM_FORMAT_MOD_PVR_FBCDC_16x4_V7     fourcc_mod_code(PVR, 12)
++
++#endif /* IMG_DRM_FOURCC_H */
+diff --git a/src/mesa/drivers/dri/pvr/imgpixfmts.h b/src/mesa/drivers/dri/pvr/imgpixfmts.h
+new file mode 100644
+index 0000000..da12a0f
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/imgpixfmts.h
+@@ -0,0 +1,307 @@
++/*************************************************************************/ /*!
++@File           imgpixfmts.h
++@Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
++@License        MIT
++
++The contents of this file are subject to the MIT license as set out below.
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
++*/ /**************************************************************************/
++
++/******************************************************************************
++ **
++ ** WARNING: File is autogenerated by parsesystable.py - DO NOT EDIT.
++ ** Use fmts_systable.txt to add new formats.
++ **
++ *****************************************************************************/
++
++#if !defined(IMGPIXFMTS_H)
++#define IMGPIXFMTS_H
++
++typedef enum _IMG_PIXFMT_
++{
++	IMG_PIXFMT_UNKNOWN = 0,
++	IMG_PIXFMT_RESERVED_1 = 1,
++	IMG_PIXFMT_RESERVED_2 = 2,
++	IMG_PIXFMT_RESERVED_3 = 3,
++	IMG_PIXFMT_RESERVED_4 = 4,
++	IMG_PIXFMT_RESERVED_5 = 5,
++	IMG_PIXFMT_RESERVED_6 = 6,
++	IMG_PIXFMT_RESERVED_7 = 7,
++	IMG_PIXFMT_RESERVED_8 = 8,
++	IMG_PIXFMT_RESERVED_9 = 9,
++	IMG_PIXFMT_RESERVED_10 = 10,
++	IMG_PIXFMT_RESERVED_11 = 11,
++	IMG_PIXFMT_RESERVED_12 = 12,
++	IMG_PIXFMT_RESERVED_13 = 13,
++	IMG_PIXFMT_RESERVED_14 = 14,
++	IMG_PIXFMT_RESERVED_15 = 15,
++	IMG_PIXFMT_RESERVED_16 = 16,
++	IMG_PIXFMT_RESERVED_17 = 17,
++	IMG_PIXFMT_RESERVED_18 = 18,
++	IMG_PIXFMT_RESERVED_19 = 19,
++	IMG_PIXFMT_RESERVED_20 = 20,
++	IMG_PIXFMT_RESERVED_21 = 21,
++	IMG_PIXFMT_RESERVED_22 = 22,
++	IMG_PIXFMT_RESERVED_23 = 23,
++	IMG_PIXFMT_RESERVED_24 = 24,
++	IMG_PIXFMT_R10G10B10A2_UNORM = 25,
++	IMG_PIXFMT_RESERVED_26 = 26,
++	IMG_PIXFMT_RESERVED_27 = 27,
++	IMG_PIXFMT_B10G10R10A2_UNORM = 28,
++	IMG_PIXFMT_RESERVED_29 = 29,
++	IMG_PIXFMT_RESERVED_30 = 30,
++	IMG_PIXFMT_RESERVED_31 = 31,
++	IMG_PIXFMT_R8G8B8A8_UNORM = 32,
++	IMG_PIXFMT_R8G8B8A8_UNORM_SRGB = 33,
++	IMG_PIXFMT_RESERVED_34 = 34,
++	IMG_PIXFMT_RESERVED_35 = 35,
++	IMG_PIXFMT_RESERVED_36 = 36,
++	IMG_PIXFMT_R8G8B8X8_UNORM = 37,
++	IMG_PIXFMT_RESERVED_38 = 38,
++	IMG_PIXFMT_RESERVED_39 = 39,
++	IMG_PIXFMT_RESERVED_40 = 40,
++	IMG_PIXFMT_RESERVED_41 = 41,
++	IMG_PIXFMT_RESERVED_42 = 42,
++	IMG_PIXFMT_RESERVED_43 = 43,
++	IMG_PIXFMT_RESERVED_44 = 44,
++	IMG_PIXFMT_RESERVED_45 = 45,
++	IMG_PIXFMT_RESERVED_46 = 46,
++	IMG_PIXFMT_RESERVED_47 = 47,
++	IMG_PIXFMT_RESERVED_48 = 48,
++	IMG_PIXFMT_RESERVED_49 = 49,
++	IMG_PIXFMT_RESERVED_50 = 50,
++	IMG_PIXFMT_D32_FLOAT = 51,
++	IMG_PIXFMT_RESERVED_52 = 52,
++	IMG_PIXFMT_RESERVED_53 = 53,
++	IMG_PIXFMT_RESERVED_54 = 54,
++	IMG_PIXFMT_RESERVED_55 = 55,
++	IMG_PIXFMT_RESERVED_56 = 56,
++	IMG_PIXFMT_RESERVED_57 = 57,
++	IMG_PIXFMT_D24_UNORM_X8_TYPELESS = 58,
++	IMG_PIXFMT_RESERVED_59 = 59,
++	IMG_PIXFMT_RESERVED_60 = 60,
++	IMG_PIXFMT_RESERVED_61 = 61,
++	IMG_PIXFMT_R8G8_UNORM = 62,
++	IMG_PIXFMT_RESERVED_63 = 63,
++	IMG_PIXFMT_RESERVED_64 = 64,
++	IMG_PIXFMT_RESERVED_65 = 65,
++	IMG_PIXFMT_RESERVED_66 = 66,
++	IMG_PIXFMT_RESERVED_67 = 67,
++	IMG_PIXFMT_RESERVED_68 = 68,
++	IMG_PIXFMT_D16_UNORM = 69,
++	IMG_PIXFMT_RESERVED_70 = 70,
++	IMG_PIXFMT_RESERVED_71 = 71,
++	IMG_PIXFMT_RESERVED_72 = 72,
++	IMG_PIXFMT_RESERVED_73 = 73,
++	IMG_PIXFMT_RESERVED_74 = 74,
++	IMG_PIXFMT_RESERVED_75 = 75,
++	IMG_PIXFMT_R8_UNORM = 76,
++	IMG_PIXFMT_RESERVED_77 = 77,
++	IMG_PIXFMT_RESERVED_78 = 78,
++	IMG_PIXFMT_RESERVED_79 = 79,
++	IMG_PIXFMT_RESERVED_80 = 80,
++	IMG_PIXFMT_S8_UINT = 81,
++	IMG_PIXFMT_RESERVED_82 = 82,
++	IMG_PIXFMT_RESERVED_83 = 83,
++	IMG_PIXFMT_RESERVED_84 = 84,
++	IMG_PIXFMT_B5G6R5_UNORM = 85,
++	IMG_PIXFMT_R5G6B5_UNORM = 86,
++	IMG_PIXFMT_B5G5R5A1_UNORM = 87,
++	IMG_PIXFMT_B5G5R5X1_UNORM = 88,
++	IMG_PIXFMT_B8G8R8A8_UNORM = 89,
++	IMG_PIXFMT_B8G8R8X8_UNORM = 90,
++	IMG_PIXFMT_B8G8R8A8_UINT = 91,
++	IMG_PIXFMT_B8G8R8A8_SNORM = 92,
++	IMG_PIXFMT_B8G8R8A8_SINT = 93,
++	IMG_PIXFMT_B8G8R8A8_UNORM_SRGB = 94,
++	IMG_PIXFMT_RESERVED_95 = 95,
++	IMG_PIXFMT_RESERVED_96 = 96,
++	IMG_PIXFMT_RESERVED_97 = 97,
++	IMG_PIXFMT_RESERVED_98 = 98,
++	IMG_PIXFMT_RESERVED_99 = 99,
++	IMG_PIXFMT_RESERVED_100 = 100,
++	IMG_PIXFMT_RESERVED_101 = 101,
++	IMG_PIXFMT_RESERVED_102 = 102,
++	IMG_PIXFMT_RESERVED_103 = 103,
++	IMG_PIXFMT_RESERVED_104 = 104,
++	IMG_PIXFMT_RESERVED_105 = 105,
++	IMG_PIXFMT_RESERVED_106 = 106,
++	IMG_PIXFMT_RESERVED_107 = 107,
++	IMG_PIXFMT_RESERVED_108 = 108,
++	IMG_PIXFMT_RESERVED_109 = 109,
++	IMG_PIXFMT_RESERVED_110 = 110,
++	IMG_PIXFMT_RESERVED_111 = 111,
++	IMG_PIXFMT_RESERVED_112 = 112,
++	IMG_PIXFMT_RESERVED_113 = 113,
++	IMG_PIXFMT_RESERVED_114 = 114,
++	IMG_PIXFMT_RESERVED_115 = 115,
++	IMG_PIXFMT_RESERVED_116 = 116,
++	IMG_PIXFMT_RESERVED_117 = 117,
++	IMG_PIXFMT_RESERVED_118 = 118,
++	IMG_PIXFMT_RESERVED_119 = 119,
++	IMG_PIXFMT_RESERVED_120 = 120,
++	IMG_PIXFMT_RESERVED_121 = 121,
++	IMG_PIXFMT_RESERVED_122 = 122,
++	IMG_PIXFMT_RESERVED_123 = 123,
++	IMG_PIXFMT_RESERVED_124 = 124,
++	IMG_PIXFMT_RESERVED_125 = 125,
++	IMG_PIXFMT_RESERVED_126 = 126,
++	IMG_PIXFMT_RESERVED_127 = 127,
++	IMG_PIXFMT_RESERVED_128 = 128,
++	IMG_PIXFMT_RESERVED_129 = 129,
++	IMG_PIXFMT_RESERVED_130 = 130,
++	IMG_PIXFMT_RESERVED_131 = 131,
++	IMG_PIXFMT_RESERVED_132 = 132,
++	IMG_PIXFMT_RESERVED_133 = 133,
++	IMG_PIXFMT_RESERVED_134 = 134,
++	IMG_PIXFMT_RESERVED_135 = 135,
++	IMG_PIXFMT_L8_UNORM = 136,
++	IMG_PIXFMT_RESERVED_137 = 137,
++	IMG_PIXFMT_L8A8_UNORM = 138,
++	IMG_PIXFMT_RESERVED_139 = 139,
++	IMG_PIXFMT_RESERVED_140 = 140,
++	IMG_PIXFMT_RESERVED_141 = 141,
++	IMG_PIXFMT_RESERVED_142 = 142,
++	IMG_PIXFMT_RESERVED_143 = 143,
++	IMG_PIXFMT_RESERVED_144 = 144,
++	IMG_PIXFMT_B4G4R4A4_UNORM = 145,
++	IMG_PIXFMT_RESERVED_146 = 146,
++	IMG_PIXFMT_RESERVED_147 = 147,
++	IMG_PIXFMT_RESERVED_148 = 148,
++	IMG_PIXFMT_RESERVED_149 = 149,
++	IMG_PIXFMT_RESERVED_150 = 150,
++	IMG_PIXFMT_RESERVED_151 = 151,
++	IMG_PIXFMT_RESERVED_152 = 152,
++	IMG_PIXFMT_RESERVED_153 = 153,
++	IMG_PIXFMT_RESERVED_154 = 154,
++	IMG_PIXFMT_RESERVED_155 = 155,
++	IMG_PIXFMT_RESERVED_156 = 156,
++	IMG_PIXFMT_RESERVED_157 = 157,
++	IMG_PIXFMT_RESERVED_158 = 158,
++	IMG_PIXFMT_RESERVED_159 = 159,
++	IMG_PIXFMT_R8G8B8_UNORM = 160,
++	IMG_PIXFMT_R8G8B8_UNORM_SRGB = 161,
++	IMG_PIXFMT_RESERVED_162 = 162,
++	IMG_PIXFMT_RESERVED_163 = 163,
++	IMG_PIXFMT_RESERVED_164 = 164,
++	IMG_PIXFMT_RESERVED_165 = 165,
++	IMG_PIXFMT_RESERVED_166 = 166,
++	IMG_PIXFMT_RESERVED_167 = 167,
++	IMG_PIXFMT_RESERVED_168 = 168,
++	IMG_PIXFMT_RESERVED_169 = 169,
++	IMG_PIXFMT_RESERVED_170 = 170,
++	IMG_PIXFMT_UYVY = 171,
++	IMG_PIXFMT_VYUY = 172,
++	IMG_PIXFMT_YUYV = 173,
++	IMG_PIXFMT_YVYU = 174,
++	IMG_PIXFMT_YVU420_2PLANE = 175,
++	IMG_PIXFMT_YUV420_2PLANE = 176,
++	IMG_PIXFMT_YVU420_2PLANE_MACRO_BLOCK = 177,
++	IMG_PIXFMT_YUV420_3PLANE = 178,
++	IMG_PIXFMT_YVU420_3PLANE = 179,
++	IMG_PIXFMT_RESERVED_180 = 180,
++	IMG_PIXFMT_RESERVED_181 = 181,
++	IMG_PIXFMT_RESERVED_182 = 182,
++	IMG_PIXFMT_RESERVED_183 = 183,
++	IMG_PIXFMT_RESERVED_184 = 184,
++	IMG_PIXFMT_RESERVED_185 = 185,
++	IMG_PIXFMT_RESERVED_186 = 186,
++	IMG_PIXFMT_RESERVED_187 = 187,
++	IMG_PIXFMT_RESERVED_188 = 188,
++	IMG_PIXFMT_RESERVED_189 = 189,
++	IMG_PIXFMT_RESERVED_190 = 190,
++	IMG_PIXFMT_RESERVED_191 = 191,
++	IMG_PIXFMT_RESERVED_192 = 192,
++	IMG_PIXFMT_RESERVED_193 = 193,
++	IMG_PIXFMT_RESERVED_194 = 194,
++	IMG_PIXFMT_RESERVED_195 = 195,
++	IMG_PIXFMT_RESERVED_196 = 196,
++	IMG_PIXFMT_RESERVED_197 = 197,
++	IMG_PIXFMT_RESERVED_198 = 198,
++	IMG_PIXFMT_RESERVED_199 = 199,
++	IMG_PIXFMT_RESERVED_200 = 200,
++	IMG_PIXFMT_YVU8_422_2PLANE_PACK8 = 201,
++	IMG_PIXFMT_RESERVED_202 = 202,
++	IMG_PIXFMT_YVU10_444_1PLANE_PACK10 = 203,
++	IMG_PIXFMT_RESERVED_204 = 204,
++	IMG_PIXFMT_RESERVED_205 = 205,
++	IMG_PIXFMT_RESERVED_206 = 206,
++	IMG_PIXFMT_YUV8_422_2PLANE_PACK8 = 207,
++	IMG_PIXFMT_YUV8_444_3PLANE_PACK8 = 208,
++	IMG_PIXFMT_RESERVED_209 = 209,
++	IMG_PIXFMT_RESERVED_210 = 210,
++	IMG_PIXFMT_RESERVED_211 = 211,
++	IMG_PIXFMT_RESERVED_212 = 212,
++	IMG_PIXFMT_RESERVED_213 = 213,
++	IMG_PIXFMT_RESERVED_214 = 214,
++	IMG_PIXFMT_RESERVED_215 = 215,
++	IMG_PIXFMT_RESERVED_216 = 216,
++	IMG_PIXFMT_RESERVED_217 = 217,
++	IMG_PIXFMT_RESERVED_218 = 218,
++	IMG_PIXFMT_RESERVED_219 = 219,
++	IMG_PIXFMT_RESERVED_220 = 220,
++	IMG_PIXFMT_RESERVED_221 = 221,
++	IMG_PIXFMT_RESERVED_222 = 222,
++	IMG_PIXFMT_RESERVED_223 = 223,
++	IMG_PIXFMT_RESERVED_224 = 224,
++	IMG_PIXFMT_RESERVED_225 = 225,
++	IMG_PIXFMT_RESERVED_226 = 226,
++	IMG_PIXFMT_RESERVED_227 = 227,
++	IMG_PIXFMT_RESERVED_228 = 228,
++	IMG_PIXFMT_RESERVED_229 = 229,
++	IMG_PIXFMT_RESERVED_230 = 230,
++	IMG_PIXFMT_RESERVED_231 = 231,
++	IMG_PIXFMT_RESERVED_232 = 232,
++	IMG_PIXFMT_RESERVED_233 = 233,
++	IMG_PIXFMT_RESERVED_234 = 234,
++	IMG_PIXFMT_RESERVED_235 = 235,
++	IMG_PIXFMT_RESERVED_236 = 236,
++	IMG_PIXFMT_RESERVED_237 = 237,
++	IMG_PIXFMT_RESERVED_238 = 238,
++	IMG_PIXFMT_RESERVED_239 = 239,
++	IMG_PIXFMT_RESERVED_240 = 240,
++	IMG_PIXFMT_RESERVED_241 = 241,
++	IMG_PIXFMT_RESERVED_242 = 242,
++	IMG_PIXFMT_RESERVED_243 = 243,
++	IMG_PIXFMT_RESERVED_244 = 244,
++	IMG_PIXFMT_YVU8_420_2PLANE_PACK8_P = 245,
++	IMG_PIXFMT_RESERVED_246 = 246,
++	IMG_PIXFMT_RESERVED_247 = 247,
++	IMG_PIXFMT_RESERVED_248 = 248,
++	IMG_PIXFMT_YUV8_420_2PLANE_PACK8_P = 249,
++	IMG_PIXFMT_RESERVED_250 = 250,
++	IMG_PIXFMT_RESERVED_251 = 251,
++	IMG_PIXFMT_UYVY10_422_1PLANE_PACK10_CUST1 = 252,
++	IMG_PIXFMT_RESERVED_253 = 253,
++	IMG_PIXFMT_RESERVED_254 = 254,
++	IMG_PIXFMT_RESERVED_255 = 255,
++	IMG_PIXFMT_RESERVED_256 = 256,
++	IMG_PIXFMT_RESERVED_257 = 257,
++	IMG_PIXFMT_RESERVED_258 = 258,
++	IMG_PIXFMT_RESERVED_259 = 259,
++	IMG_PIXFMT_RESERVED_260 = 260,
++	IMG_PIXFMT_RESERVED_261 = 261,
++	IMG_PIXFMT_RESERVED_262 = 262,
++	IMG_PIXFMT_RESERVED_263 = 263,
++	IMG_PIXFMT_RESERVED_264 = 264,
++#define IMG_PIXFMT_ENUM_COUNT 265
++} IMG_PIXFMT;
++
++#endif /* IMGPIXFMTS_H */
+diff --git a/src/mesa/drivers/dri/pvr/imgyuv.h b/src/mesa/drivers/dri/pvr/imgyuv.h
+new file mode 100644
+index 0000000..7ae8fd1
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/imgyuv.h
+@@ -0,0 +1,58 @@
++/*************************************************************************/ /*!
++@File
++@Title          YUV defines
++@Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
++@License        MIT
++
++The contents of this file are subject to the MIT license as set out below.
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
++*/ /**************************************************************************/
++
++#if !defined(IMGYUV_H)
++#define IMGYUV_H
++
++typedef enum
++{
++	IMG_COLORSPACE_UNDEFINED = 0,
++	IMG_COLORSPACE_BT601_CONFORMANT_RANGE = 1,
++	IMG_COLORSPACE_BT601_FULL_RANGE = 2,
++	IMG_COLORSPACE_BT709_CONFORMANT_RANGE = 3,
++	IMG_COLORSPACE_BT709_FULL_RANGE = 4,
++	IMG_COLORSPACE_BT2020_CONFORMANT_RANGE = 5,
++	IMG_COLORSPACE_BT2020_FULL_RANGE = 6,
++	IMG_COLORSPACE_BT601_CONFORMANT_RANGE_INVERSE = 7,
++	IMG_COLORSPACE_BT601_FULL_RANGE_INVERSE = 8,
++	IMG_COLORSPACE_BT709_CONFORMANT_RANGE_INVERSE = 9,
++	IMG_COLORSPACE_BT709_FULL_RANGE_INVERSE = 10,
++	IMG_COLORSPACE_BT2020_CONFORMANT_RANGE_INVERSE = 11,
++	IMG_COLORSPACE_BT2020_FULL_RANGE_INVERSE = 12
++} IMG_YUV_COLORSPACE;
++
++typedef enum
++{
++	IMG_CHROMA_INTERP_UNDEFINED = 0,
++	IMG_CHROMA_INTERP_ZERO = 1,
++	IMG_CHROMA_INTERP_QUARTER = 2,
++	IMG_CHROMA_INTERP_HALF = 3,
++	IMG_CHROMA_INTERP_THREEQUARTERS = 4
++} IMG_YUV_CHROMA_INTERP;
++
++
++#endif /* IMGYUV_H */
+diff --git a/src/mesa/drivers/dri/pvr/mesa_context.c b/src/mesa/drivers/dri/pvr/mesa_context.c
+new file mode 100644
+index 0000000..d36bae5
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/mesa_context.c
+@@ -0,0 +1,208 @@
++/**
++ * \file context.c
++ * Mesa context/visual/framebuffer management functions.
++ * \author Brian Paul
++ */
++
++/*
++ * Mesa 3-D graphics library
++ * Version:  7.1
++ *
++ * Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include <stdlib.h>
++#include <assert.h>
++
++#include "main/version.h"
++#include "main/errors.h"
++
++#include "dri_support.h"
++#include "dri_util.h"
++#include "glapi.h"
++#include "dispatch.h"
++#include "pvrmesa.h"
++
++/**
++ * This is the default function we plug into all dispatch table slots This
++ * helps prevents a segfault when someone calls a GL function without first
++ * checking if the extension is supported.
++ */
++static int
++generic_nop(void)
++{
++   _mesa_warning(NULL, "User called no-op dispatch function (an unsupported extension function?)");
++
++   return 0;
++}
++
++/**
++ * Allocate and initialise a new dispatch table.
++ */
++static struct _glapi_table *
++pvrdri_alloc_dispatch_table(void)
++{
++   unsigned int numEntries = _glapi_get_dispatch_table_size();
++   _glapi_proc *table;
++
++   table = malloc(numEntries * sizeof(_glapi_proc));
++   if (table)
++      for (unsigned int i = 0; i < numEntries; i++)
++         table[i] = (_glapi_proc) generic_nop;
++
++   return (struct _glapi_table *) table;
++}
++
++/**
++ * Return a pointer to the pointer to the dispatch table of an API in
++ * PVRDRIScreen.
++ */
++static struct _glapi_table **
++pvrdri_get_dispatch_table_ptr(PVRDRIScreen *psPVRScreen, PVRDRIAPIType eAPI)
++{
++   switch (eAPI) {
++   case PVRDRI_API_GLES1:
++      return &psPVRScreen->psOGLES1Dispatch;
++   case PVRDRI_API_GLES2:
++      return &psPVRScreen->psOGLES2Dispatch;
++   case PVRDRI_API_GL_COMPAT:
++   case PVRDRI_API_GL_CORE:
++      return &psPVRScreen->psOGLDispatch;
++   default:
++      return NULL;
++   }
++}
++
++/**
++ * Return a pointer to the dispatch table of an API.
++ */
++static struct _glapi_table *
++pvrdri_get_dispatch_table(PVRDRIScreen *psPVRScreen, PVRDRIAPIType eAPI)
++{
++   struct _glapi_table **ppsTable =
++      pvrdri_get_dispatch_table_ptr(psPVRScreen, eAPI);
++
++   return ppsTable ? *ppsTable : NULL;
++}
++
++/**
++ * Free all dispatch tables.
++ */
++void
++pvrdri_free_dispatch_tables(PVRDRIScreen *psPVRScreen)
++{
++   if (psPVRScreen->psOGLES1Dispatch != NULL) {
++      free(psPVRScreen->psOGLES1Dispatch);
++      psPVRScreen->psOGLES1Dispatch = NULL;
++   }
++
++   if (psPVRScreen->psOGLES2Dispatch != NULL) {
++      free(psPVRScreen->psOGLES2Dispatch);
++      psPVRScreen->psOGLES2Dispatch = NULL;
++   }
++
++   if (psPVRScreen->psOGLDispatch != NULL) {
++      free(psPVRScreen->psOGLDispatch);
++      psPVRScreen->psOGLDispatch = NULL;
++   }
++}
++
++static void
++pvrdri_add_mesa_dispatch(struct _glapi_table *psTable, PVRDRIAPIType eAPI,
++                         struct DRISUPScreen *psDRISUPScreen,
++                         unsigned int uIdx)
++{
++   const char *asFunc[] = { NULL, NULL };
++   int iOffset;
++   const char *psFunc;
++   _glapi_proc pfFunc;
++
++   pfFunc = DRISUPGetAPIProcAddress(psDRISUPScreen, eAPI, uIdx);
++   if (pfFunc == NULL)
++      return;
++
++   psFunc = DRISUPGetAPIProcName(psDRISUPScreen, eAPI, uIdx);
++   assert(psFunc != NULL);
++
++   asFunc[0] = psFunc;
++   iOffset = _glapi_add_dispatch(asFunc, "");
++   if (iOffset == -1) {
++      _mesa_warning(NULL, "Couldn't add %s to the Mesa dispatch table",
++                    psFunc);
++   } else {
++      SET_by_offset(psTable, iOffset, pfFunc);
++   }
++}
++
++static void
++pvrdri_set_mesa_dispatch(struct _glapi_table *psTable, PVRDRIAPIType eAPI,
++                         struct DRISUPScreen *psDRISUPScreen,
++                         unsigned int uNumFuncs)
++{
++   for (unsigned int i = 0; i < uNumFuncs; i++)
++      pvrdri_add_mesa_dispatch(psTable, eAPI, psDRISUPScreen, i);
++}
++
++bool
++pvrdri_create_dispatch_table(PVRDRIScreen *psPVRScreen, PVRDRIAPIType eAPI)
++{
++   struct DRISUPScreen *psDRISUPScreen = psPVRScreen->psDRISUPScreen;
++   struct _glapi_table **ppsTable;
++   unsigned int uNumFuncs;
++
++   ppsTable = pvrdri_get_dispatch_table_ptr(psPVRScreen, eAPI);
++   if (ppsTable == NULL)
++      return false;
++
++   if (*ppsTable != NULL)
++      return true;
++
++   uNumFuncs = DRISUPGetNumAPIProcs(psDRISUPScreen, eAPI);
++   if (!uNumFuncs)
++      return false;
++
++   *ppsTable = pvrdri_alloc_dispatch_table();
++   if (*ppsTable == NULL)
++      return false;
++
++   pvrdri_set_mesa_dispatch(*ppsTable, eAPI, psDRISUPScreen, uNumFuncs);
++
++   return true;
++}
++
++void
++pvrdri_set_null_dispatch_table(void)
++{
++   _glapi_set_dispatch(NULL);
++}
++
++void
++pvrdri_set_dispatch_table(PVRDRIContext *psPVRContext)
++{
++   struct _glapi_table *psTable;
++
++   psTable = pvrdri_get_dispatch_table(psPVRContext->psPVRScreen,
++                                       psPVRContext->eAPI);
++
++   _glapi_set_dispatch(psTable);
++}
+diff --git a/src/mesa/drivers/dri/pvr/meson.build b/src/mesa/drivers/dri/pvr/meson.build
+new file mode 100644
+index 0000000..59cccf8
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/meson.build
+@@ -0,0 +1,48 @@
++# Copyright (c) Imagination Technologies Ltd.
++#
++# The contents of this file are subject to the MIT license as set out below.
++#
++# Permission is hereby granted, free of charge, to any person obtaining a copy
++# of this software and associated documentation files (the "Software"), to deal
++# in the Software without restriction, including without limitation the rights
++# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++# copies of the Software, and to permit persons to whom the Software is
++# furnished to do so, subject to the following conditions:
++#
++# The above copyright notice and this permission notice shall be included in
++# all copies or substantial portions of the Software.
++#
++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++# THE SOFTWARE.
++
++files_pvr = files(
++  'mesa_context.c',
++  'pvrcb.c',
++  'pvrcompat.c',
++  'pvrdri.c',
++  'pvrext.c',
++  'pvrutil.c',
++)
++
++dep_libpvr = [
++  dep_libdrm,
++]
++
++if with_platform_tizen
++  dep_libpvr += dep_dlog
++endif
++
++libpvr = static_library(
++  'pvr',
++  [files_pvr, main_dispatch_h],
++  include_directories : [inc_include, inc_src, inc_mapi, inc_mesa,
++                         inc_dri_common, inc_util, inc_pvr],
++  c_args : [no_override_init_args],
++  gnu_symbol_visibility : 'hidden',
++  dependencies : [dep_libpvr],
++)
+diff --git a/src/mesa/drivers/dri/pvr/pvrcb.c b/src/mesa/drivers/dri/pvr/pvrcb.c
+new file mode 100644
+index 0000000..ea8911c
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrcb.c
+@@ -0,0 +1,328 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include <assert.h>
++
++#include "utils.h"
++#include "pvrdri.h"
++
++int
++MODSUPGetBuffers(__DRIdrawable *psDRIDrawable, unsigned int uFourCC,
++                 uint32_t *puStamp, void *pvLoaderPrivate,
++                 uint32_t uBufferMask, struct PVRDRIImageList *psImageList)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++   __DRIscreen *psDRIScreen = psDRIDrawable->driScreenPriv;
++   struct __DRIimageList sDRIList;
++   int res;
++
++#if !defined(DRI_IMAGE_HAS_BUFFER_PREV)
++   uBufferMask &= ~PVRDRI_IMAGE_BUFFER_PREV;
++#endif
++
++   if (psPVRDrawable->uFourCC != uFourCC) {
++      psPVRDrawable->uDRIFormat = PVRDRIFourCCToDRIFormat(uFourCC);
++      psPVRDrawable->uFourCC = uFourCC;
++   }
++
++   res = psDRIScreen->image.loader->getBuffers(psDRIDrawable,
++                                               psPVRDrawable->uDRIFormat,
++                                               puStamp,
++                                               pvLoaderPrivate,
++                                               uBufferMask, &sDRIList);
++
++   if (res) {
++      psImageList->uImageMask = sDRIList.image_mask;
++      psImageList->psBack = sDRIList.back;
++      psImageList->psFront = sDRIList.front;
++      psImageList->psPrev = sDRIList.prev;
++   }
++
++   return res;
++}
++
++bool
++MODSUPCreateConfigs(__DRIconfig ***pppsConfigs, __DRIscreen *psDRIScreen,
++                    int iPVRDRIMesaFormat, const uint8_t *puDepthBits,
++                    const uint8_t *puStencilBits,
++                    unsigned int uNumDepthStencilBits,
++                    const unsigned int *puDBModes, unsigned int uNumDBModes,
++                    const uint8_t *puMSAASamples, unsigned int uNumMSAAModes,
++                    bool bEnableAccum, bool bColorDepthMatch,
++                    UNUSED bool bMutableRenderBuffer,
++                    int iYUVDepthRange, int iYUVCSCStandard,
++                    uint32_t uMaxPbufferWidth, uint32_t uMaxPbufferHeight)
++{
++   __DRIconfig **ppsConfigs;
++   mesa_format eFormat = PVRDRIMesaFormatToMesaFormat(iPVRDRIMesaFormat);
++   unsigned int i;
++
++   (void) psDRIScreen;
++
++   switch (eFormat) {
++   case MESA_FORMAT_NONE:
++      __driUtilMessage("%s: Unknown PVR DRI format: %u",
++                       __func__, iPVRDRIMesaFormat);
++      return false;
++   default:
++      break;
++   }
++
++   /*
++    * The double buffered modes array argument for driCreateConfigs has
++    * entries of type GLenum.
++    */
++   static_assert(sizeof(GLenum) == sizeof(unsigned int),
++                 "Size mismatch between GLenum and unsigned int");
++
++   ppsConfigs = driCreateConfigs(eFormat, puDepthBits, puStencilBits,
++                                 uNumDepthStencilBits, (GLenum *) puDBModes,
++                                 uNumDBModes, puMSAASamples, uNumMSAAModes,
++                                 bEnableAccum, bColorDepthMatch,
++                                 iYUVDepthRange, iYUVCSCStandard);
++   if (!ppsConfigs)
++      return false;
++
++   for (i = 0; ppsConfigs[i]; i++) {
++      ppsConfigs[i]->modes.maxPbufferWidth = uMaxPbufferWidth;
++      ppsConfigs[i]->modes.maxPbufferHeight = uMaxPbufferHeight;
++      ppsConfigs[i]->modes.maxPbufferPixels =
++         uMaxPbufferWidth * uMaxPbufferHeight;
++   }
++
++   *pppsConfigs = ppsConfigs;
++
++   return true;
++}
++
++__DRIconfig **
++MODSUPConcatConfigs(__DRIscreen *psDRIScreen,
++                    __DRIconfig **ppsConfigA, __DRIconfig **ppsConfigB)
++{
++   (void) psDRIScreen;
++
++   return driConcatConfigs(ppsConfigA, ppsConfigB);
++}
++
++struct __DRIimageRec *
++MODSUPLookupEGLImage(__DRIscreen *psDRIScreen, void *pvImage,
++                     void *pvLoaderPrivate)
++{
++   return psDRIScreen->dri2.image->lookupEGLImage(psDRIScreen,
++                                                  pvImage,
++                                                  pvLoaderPrivate);
++}
++
++
++unsigned int
++MODSUPGetCapability(__DRIscreen *psDRIScreen, unsigned int uCapability)
++{
++   if (psDRIScreen->image.loader->base.version >= 2 &&
++       psDRIScreen->image.loader->getCapability) {
++      enum dri_loader_cap eCapability =
++         (enum dri_loader_cap) uCapability;
++
++      return psDRIScreen->image.loader->getCapability(
++         psDRIScreen->loaderPrivate,
++         eCapability);
++   }
++
++   return 0;
++}
++
++int
++MODSUPGetDisplayFD(__DRIscreen *psDRIScreen, void *pvLoaderPrivate)
++{
++#if __DRI_IMAGE_LOADER_VERSION >= 5
++   if (psDRIScreen->image.loader->base.version >= 5 &&
++       psDRIScreen->image.loader->getDisplayFD)
++      return psDRIScreen->image.loader->getDisplayFD(pvLoaderPrivate);
++#else
++   (void) psDRIScreen;
++   (void) pvLoaderPrivate;
++#endif
++
++   return -1;
++}
++
++static bool
++PVRDRIConfigQueryUnsigned(const PVRDRIConfig *psConfig,
++                          PVRDRIConfigAttrib eConfigAttrib,
++                          unsigned int *puValueOut)
++{
++   if (!psConfig || !puValueOut)
++      return false;
++
++   switch (eConfigAttrib) {
++   case PVRDRI_CONFIG_ATTRIB_RENDERABLE_TYPE:
++      *puValueOut = psConfig->iSupportedAPIs;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_RGB_MODE:
++      *puValueOut = psConfig->sGLMode.rgbMode;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_DOUBLE_BUFFER_MODE:
++      *puValueOut = psConfig->sGLMode.doubleBufferMode;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_RED_BITS:
++      *puValueOut = psConfig->sGLMode.redBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_GREEN_BITS:
++      *puValueOut = psConfig->sGLMode.greenBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_BLUE_BITS:
++      *puValueOut = psConfig->sGLMode.blueBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_ALPHA_BITS:
++      *puValueOut = psConfig->sGLMode.alphaBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_RGB_BITS:
++      *puValueOut = psConfig->sGLMode.rgbBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_DEPTH_BITS:
++      *puValueOut = psConfig->sGLMode.depthBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_STENCIL_BITS:
++      *puValueOut = psConfig->sGLMode.stencilBits;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_SAMPLE_BUFFERS:
++      *puValueOut = !!psConfig->sGLMode.samples;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_SAMPLES:
++      *puValueOut = psConfig->sGLMode.samples;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_BIND_TO_TEXTURE_RGB:
++      *puValueOut = GL_TRUE;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_BIND_TO_TEXTURE_RGBA:
++      *puValueOut = GL_TRUE;
++      return true;
++#if defined(__DRI_ATTRIB_YUV_BIT)
++   case PVRDRI_CONFIG_ATTRIB_YUV_ORDER:
++      *puValueOut = psConfig->sGLMode.YUVOrder;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_YUV_NUM_OF_PLANES:
++      *puValueOut = psConfig->sGLMode.YUVNumberOfPlanes;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_YUV_SUBSAMPLE:
++      *puValueOut = psConfig->sGLMode.YUVSubsample;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_YUV_DEPTH_RANGE:
++      *puValueOut = psConfig->sGLMode.YUVDepthRange;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_YUV_CSC_STANDARD:
++      *puValueOut = psConfig->sGLMode.YUVCSCStandard;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_YUV_PLANE_BPP:
++      *puValueOut = psConfig->sGLMode.YUVPlaneBPP;
++      return true;
++#endif
++#if !defined(__DRI_ATTRIB_YUV_BIT)
++   case PVRDRI_CONFIG_ATTRIB_YUV_ORDER:
++   case PVRDRI_CONFIG_ATTRIB_YUV_NUM_OF_PLANES:
++   case PVRDRI_CONFIG_ATTRIB_YUV_SUBSAMPLE:
++   case PVRDRI_CONFIG_ATTRIB_YUV_DEPTH_RANGE:
++   case PVRDRI_CONFIG_ATTRIB_YUV_CSC_STANDARD:
++   case PVRDRI_CONFIG_ATTRIB_YUV_PLANE_BPP:
++      return false;
++#endif
++   case PVRDRI_CONFIG_ATTRIB_RED_MASK:
++      *puValueOut = psConfig->sGLMode.redMask;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_GREEN_MASK:
++      *puValueOut = psConfig->sGLMode.greenMask;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_BLUE_MASK:
++      *puValueOut = psConfig->sGLMode.blueMask;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_ALPHA_MASK:
++      *puValueOut = psConfig->sGLMode.alphaMask;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_SRGB_CAPABLE:
++      *puValueOut = psConfig->sGLMode.sRGBCapable;
++      return true;
++   case PVRDRI_CONFIG_ATTRIB_INVALID:
++      errorMessage("%s: Invalid attribute", __func__);
++      assert(0);
++      return false;
++   default:
++      return false;
++   }
++}
++
++bool
++PVRDRIConfigQuery(const PVRDRIConfig *psConfig,
++                  PVRDRIConfigAttrib eConfigAttrib, int *piValueOut)
++{
++   bool bRes;
++   unsigned int uValue;
++
++   bRes = PVRDRIConfigQueryUnsigned(psConfig, eConfigAttrib, &uValue);
++   if (bRes)
++      *piValueOut = (int) uValue;
++
++   return bRes;
++}
++
++bool
++MODSUPConfigQuery(const PVRDRIConfig *psConfig,
++                  PVRDRIConfigAttrib eConfigAttrib, unsigned int *puValueOut)
++{
++   return PVRDRIConfigQueryUnsigned(psConfig, eConfigAttrib, puValueOut);
++}
++
++void
++MODSUPFlushFrontBuffer(struct __DRIdrawableRec *psDRIDrawable,
++                       void *pvLoaderPrivate)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++   __DRIscreen *psDRIScreen = psDRIDrawable->driScreenPriv;
++
++   if (!psPVRDrawable->sConfig.sGLMode.doubleBufferMode)
++      psDRIScreen->image.loader->flushFrontBuffer(psDRIDrawable,
++                                                  pvLoaderPrivate);
++}
++
++void *
++MODSUPDrawableGetReferenceHandle(struct __DRIdrawableRec *psDRIDrawable)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++
++   return psPVRDrawable;
++}
++
++void
++MODSUPDrawableAddReference(void *pvReferenceHandle)
++{
++   PVRDRIDrawable *psPVRDrawable = pvReferenceHandle;
++
++   PVRDRIDrawableAddReference(psPVRDrawable);
++}
++
++void
++MODSUPDrawableRemoveReference(void *pvReferenceHandle)
++{
++   PVRDRIDrawable *psPVRDrawable = pvReferenceHandle;
++
++   PVRDRIDrawableRemoveReference(psPVRDrawable);
++}
+diff --git a/src/mesa/drivers/dri/pvr/pvrcompat.c b/src/mesa/drivers/dri/pvr/pvrcompat.c
+new file mode 100644
+index 0000000..23a57dd
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrcompat.c
+@@ -0,0 +1,838 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include <stddef.h>
++#include <stdlib.h>
++#include <string.h>
++#include <dlfcn.h>
++#include <pthread.h>
++#include <assert.h>
++
++#include <drm_fourcc.h>
++
++#include "pvrdri.h"
++
++#ifndef DRM_FORMAT_MOD_INVALID
++#define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1)
++#endif
++
++#define _MAKESTRING(x) # x
++#define MAKESTRING(x) _MAKESTRING(x)
++
++#define PVRDRI_SUPPORT_LIB "libpvr_dri_support.so"
++
++static void *gpvSupLib;
++static int giSupLibRef;
++
++static struct PVRDRISupportInterfaceV2 gsSupV2;
++
++static pthread_mutex_t gsCompatLock = PTHREAD_MUTEX_INITIALIZER;
++
++/* Lookup a function, and set the pointer to the function address */
++#define LookupFunc(func, ptr)                   \
++   do {                                         \
++      ptr = dlsym(gpvSupLib, MAKESTRING(func)); \
++   } while(0)
++
++/* Call a function via the DRI Support interface structure */
++#define CallFuncV2(field, ...)                  \
++   do {                                         \
++      if (gsSupV2.field)                        \
++         return gsSupV2.field(__VA_ARGS__);     \
++   } while(0)
++
++/* Calculate the start of the PVRDRISupportInterfaceV2 structure */
++#define PVRDRIInterfaceV2Start(field)                   \
++   (offsetof(struct PVRDRISupportInterfaceV2, field))
++
++/* Calculate the end of the PVRDRISupportInterfaceV2 structure */
++#define PVRDRIInterfaceV2End(field)                             \
++   (offsetof(struct PVRDRISupportInterfaceV2, field) +          \
++    sizeof((struct PVRDRISupportInterfaceV2 *)0)->field)
++
++static void
++CompatLock(void)
++{
++   int ret;
++
++   ret = pthread_mutex_lock(&gsCompatLock);
++   if (ret) {
++      errorMessage("%s: Failed to lock mutex (%d)", __func__, ret);
++      abort();
++   }
++}
++
++static void
++CompatUnlock(void)
++{
++   int ret;
++
++   ret = pthread_mutex_unlock(&gsCompatLock);
++   if (ret) {
++      errorMessage("%s: Failed to unlock mutex (%d)", __func__, ret);
++      abort();
++   }
++}
++
++static void *
++LoadLib(const char *path)
++{
++   void *handle;
++
++   /* Clear the error */
++   (void) dlerror();
++
++   handle = dlopen(path, RTLD_NOW);
++   if (handle) {
++      __driUtilMessage("Loaded %s", path);
++   } else {
++      const char *error;
++
++      error = dlerror();
++      if (!error)
++         error = "unknown error";
++
++      errorMessage("%s: Couldn't load %s: %s", __func__, path, error);
++   }
++
++   return handle;
++}
++
++static void
++UnloadLib(void *handle, const char *name)
++{
++   if (!handle)
++      return;
++
++   /* Clear the error */
++   (void) dlerror();
++
++   if (dlclose(handle)) {
++      const char *error;
++
++      error = dlerror();
++      if (!error)
++         error = "unknown error";
++
++      errorMessage("%s: Couldn't unload %s: %s", __func__, name, error);
++   } else {
++      __driUtilMessage("Unloaded %s", name);
++   }
++}
++
++static bool
++LoadSupportLib(void)
++{
++   gpvSupLib = LoadLib(PVRDRI_SUPPORT_LIB);
++
++   return gpvSupLib != NULL;
++}
++
++static void
++UnloadSupportLib(void)
++{
++   UnloadLib(gpvSupLib, PVRDRI_SUPPORT_LIB);
++   gpvSupLib = NULL;
++}
++
++static void
++CompatDeinit(void)
++{
++   UnloadSupportLib();
++   memset(&gsSupV2, 0, sizeof(gsSupV2));
++}
++
++bool
++PVRDRICompatInit(const struct PVRDRICallbacksV2 *psCallbacksV2,
++                 unsigned int uVersionV2, unsigned int uMinVersionV2)
++{
++   bool (*pfRegisterVersionedCallbacksV2)(const void *pvCallbacks,
++                                          unsigned int uVersion,
++                                          unsigned int uMinVersion);
++   bool res;
++
++   CompatLock();
++   res = (giSupLibRef++ != 0);
++   if (res)
++      goto Exit;
++
++   res = LoadSupportLib();
++   if (!res)
++      goto Exit;
++
++   LookupFunc(PVRDRIRegisterVersionedCallbacksV2,
++              pfRegisterVersionedCallbacksV2);
++
++   res = (pfRegisterVersionedCallbacksV2 != NULL);
++   if (!res)
++      goto Exit;
++
++   res = pfRegisterVersionedCallbacksV2(psCallbacksV2,
++                                        uVersionV2, uMinVersionV2);
++
++Exit:
++   if (!res) {
++      CompatDeinit();
++      giSupLibRef--;
++   }
++   CompatUnlock();
++
++   return res;
++}
++
++void
++PVRDRICompatDeinit(void)
++{
++   CompatLock();
++   if (--giSupLibRef == 0)
++      CompatDeinit();
++   CompatUnlock();
++}
++
++bool
++MODSUPRegisterSupportInterfaceV2(const void *pvInterface,
++                                 unsigned int uVersion,
++                                 unsigned int uMinVersion)
++{
++   size_t uStart, uEnd;
++
++   memset(&gsSupV2, 0, sizeof(gsSupV2));
++
++   if (uVersion < uMinVersion)
++      return false;
++
++   /*
++    * Minimum versions we support. To prevent the accumulation of old unused
++    * interfaces in the PVRDRIInterfaceV2 structure, the caller specifies the
++    * minimum version it supports. This will be pointed to be the psInterface
++    * argument. Assuming we support that version, we must copy the structure
++    * passed to us into the correct place in our version of the interface
++    * structure.
++    */
++   switch (uMinVersion) {
++   case 0:
++      uStart = PVRDRIInterfaceV2Start(v0);
++      break;
++   case 1:
++   case 2:
++   case 3:
++   case 4:
++      /* These versions require version 0 */
++      return false;
++   default:
++      return false;
++   }
++
++   /* The "default" case should be associated with the latest version */
++   switch (uVersion) {
++   default:
++   case 4:
++      /* This version is an extension of versions 0 to 3 */
++      if (uMinVersion > 0)
++         return false;
++
++      uEnd = PVRDRIInterfaceV2End(v4);
++      break;
++   case 3:
++      /*
++       * This version is an extension of version 2, with no new
++       * entry points.
++       */
++   case 2:
++      /* This version is an extension of versions 0 and 1 */
++      if (uMinVersion > 0)
++         return false;
++
++      uEnd = PVRDRIInterfaceV2End(v2);
++      break;
++   case 1:
++      /* This version is an extension of version 0 */
++      if (uMinVersion > 0)
++         return false;
++
++      uEnd = PVRDRIInterfaceV2End(v1);
++      break;
++   case 0:
++      uEnd = PVRDRIInterfaceV2End(v0);
++      break;
++   }
++
++   memcpy(((char *) &gsSupV2) + uStart, pvInterface, uEnd - uStart);
++
++   PVRDRIAdjustExtensions(uVersion, uMinVersion);
++
++   return true;
++}
++
++struct DRISUPScreen *
++DRISUPCreateScreen(struct __DRIscreenRec *psDRIScreen, int iFD,
++                   bool bUseInvalidate, void *pvLoaderPrivate,
++                   const struct __DRIconfigRec ***pppsConfigs,
++                   int *piMaxGLES1Version, int *piMaxGLES2Version)
++{
++   CallFuncV2(v0.CreateScreen,
++              psDRIScreen, iFD, bUseInvalidate, pvLoaderPrivate, pppsConfigs,
++              piMaxGLES1Version, piMaxGLES2Version);
++
++   return NULL;
++}
++
++void
++DRISUPDestroyScreen(struct DRISUPScreen *psDRISUPScreen)
++{
++   CallFuncV2(v0.DestroyScreen,
++              psDRISUPScreen);
++}
++
++unsigned int
++DRISUPCreateContext(PVRDRIAPIType eAPI, PVRDRIConfig *psPVRDRIConfig,
++                    struct PVRDRIContextConfig *psCtxConfig,
++                    struct __DRIcontextRec *psDRIContext,
++                    struct DRISUPContext *psDRISUPSharedContext,
++                    struct DRISUPScreen *psDRISUPScreen,
++                    struct DRISUPContext **ppsDRISUPContext)
++{
++   CallFuncV2(v0.CreateContext,
++              eAPI, psPVRDRIConfig, psCtxConfig, psDRIContext,
++              psDRISUPSharedContext, psDRISUPScreen, ppsDRISUPContext);
++
++   return __DRI_CTX_ERROR_BAD_API;
++}
++
++void
++DRISUPDestroyContext(struct DRISUPContext *psDRISUPContext)
++{
++   CallFuncV2(v0.DestroyContext,
++              psDRISUPContext);
++}
++
++struct DRISUPDrawable *
++DRISUPCreateDrawable(struct __DRIdrawableRec *psDRIDrawable,
++                     struct DRISUPScreen *psDRISUPScreen,
++                     void *pvLoaderPrivate, PVRDRIConfig *psPVRDRIConfig)
++{
++   CallFuncV2(v0.CreateDrawable,
++              psDRIDrawable, psDRISUPScreen, pvLoaderPrivate, psPVRDRIConfig);
++
++   return NULL;
++}
++
++void
++DRISUPDestroyDrawable(struct DRISUPDrawable *psDRISUPDrawable)
++{
++   CallFuncV2(v0.DestroyDrawable,
++              psDRISUPDrawable);
++}
++
++bool
++DRISUPMakeCurrent(struct DRISUPContext *psDRISUPContext,
++                  struct DRISUPDrawable *psDRISUPWrite,
++                  struct DRISUPDrawable *psDRISUPRead)
++{
++   CallFuncV2(v0.MakeCurrent,
++              psDRISUPContext, psDRISUPWrite, psDRISUPRead);
++
++   return false;
++}
++
++bool
++DRISUPUnbindContext(struct DRISUPContext *psDRISUPContext)
++{
++   CallFuncV2(v0.UnbindContext,
++              psDRISUPContext);
++
++   return false;
++}
++
++struct DRISUPBuffer *
++DRISUPAllocateBuffer(struct DRISUPScreen *psDRISUPScreen,
++                     unsigned int uAttachment, unsigned int uFormat,
++                     int iWidth, int iHeight, unsigned int *puName,
++                     unsigned int *puPitch, unsigned int *puCPP,
++                     unsigned int *puFlags)
++{
++   CallFuncV2(v0.AllocateBuffer,
++              psDRISUPScreen, uAttachment, uFormat, iWidth, iHeight, puName,
++              puPitch, puCPP, puFlags);
++
++   return NULL;
++}
++
++void
++DRISUPReleaseBuffer(struct DRISUPScreen *psDRISUPScreen,
++                    struct DRISUPBuffer *psDRISUPBuffer)
++{
++   CallFuncV2(v0.ReleaseBuffer,
++              psDRISUPScreen, psDRISUPBuffer);
++}
++
++void
++DRISUPSetTexBuffer2(struct DRISUPContext *psDRISUPContext, int iTarget,
++                    int iFormat, struct DRISUPDrawable *psDRISUPDrawable)
++{
++   CallFuncV2(v0.SetTexBuffer2,
++              psDRISUPContext, iTarget, iFormat, psDRISUPDrawable);
++}
++
++void
++DRISUPReleaseTexBuffer(struct DRISUPContext *psDRISUPContext, int iTarget,
++                       struct DRISUPDrawable *psDRISUPDrawable)
++{
++   CallFuncV2(v0.ReleaseTexBuffer,
++              psDRISUPContext, iTarget, psDRISUPDrawable);
++}
++
++void
++DRISUPFlush(struct DRISUPDrawable *psDRISUPDrawable)
++{
++   CallFuncV2(v0.Flush,
++              psDRISUPDrawable);
++}
++
++void
++DRISUPInvalidate(struct DRISUPDrawable *psDRISUPDrawable)
++{
++   CallFuncV2(v0.Invalidate,
++              psDRISUPDrawable);
++}
++
++void
++DRISUPFlushWithFlags(struct DRISUPContext *psDRISUPContext,
++                     struct DRISUPDrawable *psDRISUPDrawable,
++                     unsigned int uFlags, unsigned int uThrottleReason)
++{
++   CallFuncV2(v0.FlushWithFlags,
++              psDRISUPContext, psDRISUPDrawable, uFlags, uThrottleReason);
++}
++
++__DRIimage *
++DRISUPCreateImageFromName(struct DRISUPScreen *psDRISUPScreen,
++                          int iWidth, int iHeight, int iFourCC, int iName,
++                          int iPitch, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromName,
++              psDRISUPScreen, iWidth, iHeight, iFourCC, iName, iPitch,
++              pvLoaderPrivate);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPCreateImageFromRenderbuffer(struct DRISUPContext *psDRISUPContext,
++                                  int iRenderBuffer, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromRenderbuffer,
++              psDRISUPContext, iRenderBuffer, pvLoaderPrivate);
++
++   return NULL;
++}
++
++void
++DRISUPDestroyImage(__DRIimage *psImage)
++{
++   CallFuncV2(v0.DestroyImage, psImage);
++}
++
++__DRIimage *
++DRISUPCreateImage(struct DRISUPScreen *psDRISUPScreen,
++                  int iWidth, int iHeight, int iFourCC, unsigned int uUse,
++                  void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImage,
++              psDRISUPScreen, iWidth, iHeight, iFourCC, uUse, pvLoaderPrivate);
++
++   return NULL;
++}
++
++bool
++DRISUPQueryImage(__DRIimage *psImage, int iAttrib, int *piValue)
++{
++   CallFuncV2(v0.QueryImage,
++              psImage, iAttrib, piValue);
++
++   return false;
++}
++
++__DRIimage *
++DRISUPDupImage(__DRIimage *psImage, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.DupImage,
++              psImage, pvLoaderPrivate);
++
++   return NULL;
++}
++
++bool
++DRISUPValidateImageUsage(__DRIimage *psImage, unsigned int uUse)
++{
++   CallFuncV2(v0.ValidateImageUsage,
++              psImage, uUse);
++
++   return false;
++}
++
++__DRIimage *
++DRISUPCreateImageFromNames(struct DRISUPScreen *psDRISUPScreen,
++                           int iWidth, int iHeight, int iFourCC,
++                           int *piNames, int iNumNames,
++                           int *piStrides, int *piOffsets,
++                           void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromNames,
++              psDRISUPScreen, iWidth, iHeight, iFourCC, piNames, iNumNames,
++              piStrides, piOffsets, pvLoaderPrivate);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPFromPlanar(__DRIimage *psImage, int iPlane, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.FromPlanar,
++              psImage, iPlane, pvLoaderPrivate);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPCreateImageFromTexture(struct DRISUPContext *psDRISUPContext,
++                             int iTarget, unsigned int uTexture, int iDepth,
++                             int iLevel, unsigned int *puError,
++                             void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromTexture,
++              psDRISUPContext, iTarget, uTexture, iDepth, iLevel, puError,
++              pvLoaderPrivate);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPCreateImageFromFDs(struct DRISUPScreen *psDRISUPcreen,
++                         int iWidth, int iHeight, int iFourCC,
++                         int *piFDs, int iNumFDs, int *piStrides,
++                         int *piOffsets, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromFDs,
++              psDRISUPcreen, iWidth, iHeight, iFourCC, piFDs, iNumFDs,
++              piStrides, piOffsets, pvLoaderPrivate);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPCreateImageFromDmaBufs(struct DRISUPScreen *psDRISUPScreen,
++                             int iWidth, int iHeight, int iFourCC,
++                             int *piFDs, int iNumFDs,
++                             int *piStrides, int *piOffsets,
++                             unsigned int uColorSpace,
++                             unsigned int uSampleRange,
++                             unsigned int uHorizSiting,
++                             unsigned int uVertSiting,
++                             unsigned int *puError,
++                             void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromDMABufs,
++              psDRISUPScreen, iWidth, iHeight, iFourCC, piFDs, iNumFDs,
++              piStrides, piOffsets, uColorSpace, uSampleRange,
++              uHorizSiting, uVertSiting, puError, pvLoaderPrivate);
++
++   return NULL;
++}
++
++int
++DRISUPGetImageCapabilities(struct DRISUPScreen *psDRISUPScreen)
++{
++   CallFuncV2(v0.GetImageCapabilities,
++              psDRISUPScreen);
++
++   return 0;
++}
++
++void
++DRISUPBlitImage(struct DRISUPContext *psDRISUPContext,
++                __DRIimage *psDst, __DRIimage *psSrc, int iDstX0, int iDstY0,
++                int iDstWidth, int iDstHeight, int iSrcX0, int iSrcY0,
++                int iSrcWidth, int iSrcHeight, int iFlushFlag)
++{
++   CallFuncV2(v0.BlitImage,
++              psDRISUPContext, psDst, psSrc, iDstX0, iDstY0,
++              iDstWidth, iDstHeight, iSrcX0, iSrcY0,
++              iSrcWidth, iSrcHeight, iFlushFlag);
++}
++
++void *
++DRISUPMapImage(struct DRISUPContext *psDRISUPContext, __DRIimage* psImage,
++               int iX0, int iY0, int iWidth, int iHeight, unsigned int uFlags,
++               int *piStride, void **ppvData)
++{
++   CallFuncV2(v0.MapImage,
++              psDRISUPContext, psImage, iX0, iY0, iWidth, iHeight, uFlags,
++              piStride, ppvData);
++
++   return NULL;
++}
++
++void
++DRISUPUnmapImage(struct DRISUPContext *psDRISUPContext, __DRIimage *psImage,
++                 void *pvData)
++{
++   CallFuncV2(v0.UnmapImage,
++              psDRISUPContext, psImage, pvData);
++}
++
++__DRIimage *
++DRISUPCreateImageWithModifiers(struct DRISUPScreen *psDRISUPScreen,
++                               int iWidth, int iHeight, int iFourCC,
++                               const uint64_t *puModifiers,
++                               const unsigned int uModifierCount,
++                               void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageWithModifiers,
++              psDRISUPScreen, iWidth, iHeight, iFourCC, puModifiers,
++              uModifierCount, pvLoaderPrivate);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPCreateImageFromDMABufs2(struct DRISUPScreen *psDRISUPScreen,
++                              int iWidth, int iHeight, int iFourCC,
++                              uint64_t uModifier, int *piFDs, int iNumFDs,
++                              int *piStrides, int *piOffsets,
++                              unsigned int uColorSpace,
++                              unsigned int uSampleRange,
++                              unsigned int uHorizSiting,
++                              unsigned int uVertSiting,
++                              unsigned int *puError, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromDMABufs2,
++              psDRISUPScreen, iWidth, iHeight, iFourCC, uModifier,
++              piFDs, iNumFDs, piStrides, piOffsets, uColorSpace, uSampleRange,
++              uHorizSiting, uVertSiting, puError, pvLoaderPrivate);
++
++   return NULL;
++}
++
++bool
++DRISUPQueryDMABufFormats(struct DRISUPScreen *psDRISUPScreen, int iMax,
++                         int *piFormats, int *piCount)
++{
++   CallFuncV2(v0.QueryDMABufFormats,
++              psDRISUPScreen, iMax, piFormats, piCount);
++
++   return false;
++}
++
++bool
++DRISUPQueryDMABufModifiers(struct DRISUPScreen *psDRISUPScreen, int iFourCC,
++                           int iMax, uint64_t *puModifiers,
++                           unsigned int *piExternalOnly, int *piCount)
++{
++   CallFuncV2(v0.QueryDMABufModifiers,
++              psDRISUPScreen, iFourCC, iMax, puModifiers, piExternalOnly,
++              piCount);
++
++   return false;
++}
++
++bool
++DRISUPQueryDMABufFormatModifierAttribs(struct DRISUPScreen *psDRISUPScreen,
++                                       uint32_t iFourCC, uint64_t uModifier,
++                                       int iAttrib, uint64_t *puValue)
++{
++   CallFuncV2(v0.QueryDMABufFormatModifierAttribs,
++              psDRISUPScreen, iFourCC, uModifier, iAttrib, puValue);
++
++   return false;
++}
++
++__DRIimage *
++DRISUPCreateImageFromRenderBuffer2(struct DRISUPContext *psDRISUPContext,
++                                   int iRenderBuffer, void *pvLoaderPrivate,
++                                   unsigned int *puError)
++{
++   CallFuncV2(v0.CreateImageFromRenderBuffer2,
++              psDRISUPContext, iRenderBuffer, pvLoaderPrivate, puError);
++
++   return NULL;
++}
++
++__DRIimage *
++DRISUPCreateImageFromBuffer(struct DRISUPContext *psDRISUPContext,
++                            int iTarget, void *pvBuffer,
++                            unsigned int *puError, void *pvLoaderPrivate)
++{
++   CallFuncV2(v0.CreateImageFromBuffer,
++              psDRISUPContext, iTarget, pvBuffer, puError, pvLoaderPrivate);
++
++   return NULL;
++}
++
++int
++DRISUPQueryRendererInteger(struct DRISUPScreen *psDRISUPScreen,
++                           int iAttribute, unsigned int *puValue)
++{
++   CallFuncV2(v0.QueryRendererInteger,
++              psDRISUPScreen, iAttribute, puValue);
++
++   return -1;
++}
++
++int
++DRISUPQueryRendererString(struct DRISUPScreen *psDRISUPScreen,
++                          int iAttribute, const char **ppszValue)
++{
++   CallFuncV2(v0.QueryRendererString,
++              psDRISUPScreen, iAttribute, ppszValue);
++
++   return -1;
++}
++
++void *
++DRISUPCreateFence(struct DRISUPContext *psDRISUPContext)
++{
++   CallFuncV2(v0.CreateFence,
++              psDRISUPContext);
++
++   return NULL;
++}
++
++void
++DRISUPDestroyFence(struct DRISUPScreen *psDRISUPScreen, void *pvFence)
++{
++   CallFuncV2(v0.DestroyFence,
++              psDRISUPScreen, pvFence);
++}
++
++bool
++DRISUPClientWaitSync(struct DRISUPContext *psDRISUPContext, void *pvFence,
++                     unsigned int uFlags, uint64_t uTimeout)
++{
++   CallFuncV2(v0.ClientWaitSync,
++              psDRISUPContext, pvFence, uFlags, uTimeout);
++
++   return false;
++}
++
++void
++DRISUPServerWaitSync(struct DRISUPContext *psDRISUPContext, void *pvFence,
++                     unsigned int uFlags)
++{
++   CallFuncV2(v0.ServerWaitSync,
++              psDRISUPContext, pvFence, uFlags);
++}
++
++unsigned int
++DRISUPGetFenceCapabilities(struct DRISUPScreen *psDRISUPScreen)
++{
++   CallFuncV2(v0.GetFenceCapabilities,
++              psDRISUPScreen);
++
++   return 0;
++}
++
++void *
++DRISUPCreateFenceFD(struct DRISUPContext *psDRISUPContext, int iFD)
++{
++   CallFuncV2(v0.CreateFenceFD,
++              psDRISUPContext, iFD);
++
++   return NULL;
++}
++
++int
++DRISUPGetFenceFD(struct DRISUPScreen *psDRISUPScreen, void *pvFence)
++{
++   CallFuncV2(v0.GetFenceFD,
++              psDRISUPScreen, pvFence);
++
++   return -1;
++}
++
++void *
++DRISUPGetFenceFromCLEvent(struct DRISUPScreen *psDRISUPScreen,
++                          intptr_t iCLEvent)
++{
++   CallFuncV2(v1.GetFenceFromCLEvent,
++              psDRISUPScreen, iCLEvent);
++
++   return NULL;
++}
++
++int
++DRISUPGetAPIVersion(struct DRISUPScreen *psDRISUPScreen,
++                    PVRDRIAPIType eAPI)
++{
++   CallFuncV2(v2.GetAPIVersion,
++              psDRISUPScreen, eAPI);
++
++   return 0;
++}
++
++unsigned int
++DRISUPGetNumAPIProcs(struct DRISUPScreen *psDRISUPScreen,
++                     PVRDRIAPIType eAPI)
++{
++   CallFuncV2(v0.GetNumAPIProcs,
++              psDRISUPScreen, eAPI);
++
++   return 0;
++}
++
++const char *
++DRISUPGetAPIProcName(struct DRISUPScreen *psDRISUPScreen, PVRDRIAPIType eAPI,
++                     unsigned int uIndex)
++{
++   CallFuncV2(v0.GetAPIProcName,
++              psDRISUPScreen, eAPI, uIndex);
++
++   return NULL;
++}
++
++void *
++DRISUPGetAPIProcAddress(struct DRISUPScreen *psDRISUPScreen,
++                        PVRDRIAPIType eAPI, unsigned int uIndex)
++{
++   CallFuncV2(v0.GetAPIProcAddress,
++              psDRISUPScreen, eAPI, uIndex);
++
++   return NULL;
++}
++
++void
++DRISUPSetDamageRegion(struct DRISUPDrawable *psDRISUPDrawable,
++                      unsigned int uNRects, int *piRects)
++{
++   CallFuncV2(v0.SetDamageRegion,
++              psDRISUPDrawable, uNRects, piRects);
++}
++
++bool
++DRISUPHaveGetFenceFromCLEvent(void)
++{
++   CallFuncV2(v4.HaveGetFenceFromCLEvent);
++
++   return true;
++}
+diff --git a/src/mesa/drivers/dri/pvr/pvrdri.c b/src/mesa/drivers/dri/pvr/pvrdri.c
+new file mode 100644
+index 0000000..ab941f7
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrdri.c
+@@ -0,0 +1,613 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include <string.h>
++#include <assert.h>
++
++#include "util/u_atomic.h"
++#include "utils.h"
++
++#include "pvrdri.h"
++#include "pvrmesa.h"
++
++#define PVR_IMAGE_LOADER_VER_MIN 1
++
++struct PVRBuffer {
++   __DRIbuffer sDRIBuffer;
++   struct DRISUPBuffer *psDRISUPBuffer;
++};
++
++/*************************************************************************//*!
++ Local functions
++ *//**************************************************************************/
++
++static bool
++PVRLoaderIsSupported(__DRIscreen *psDRIScreen)
++{
++   if (psDRIScreen->image.loader) {
++      if (psDRIScreen->image.loader->base.version < PVR_IMAGE_LOADER_VER_MIN) {
++         __driUtilMessage("%s: Image loader extension version %d but need %d",
++                          __func__, psDRIScreen->image.loader->base.version,
++                          PVR_IMAGE_LOADER_VER_MIN);
++         return false;
++      } else if (!psDRIScreen->image.loader->getBuffers) {
++         __driUtilMessage("%s: Image loader extension missing support for getBuffers",
++                          __func__);
++         return false;
++      }
++   } else {
++      __driUtilMessage("%s: Image loader extension required", __func__);
++      return false;
++   }
++
++   return true;
++}
++
++static inline struct DRISUPContext *
++getSharedContextImpl(void *pvSharedContextPrivate)
++{
++   if (pvSharedContextPrivate == NULL)
++      return NULL;
++
++   return ((PVRDRIContext *) pvSharedContextPrivate)->psDRISUPContext;
++}
++
++static void
++PVRDRIScreenAddReference(PVRDRIScreen *psPVRScreen)
++{
++   int iRefCount = p_atomic_inc_return(&psPVRScreen->iRefCount);
++
++   (void) iRefCount;
++   assert(iRefCount > 1);
++}
++
++static void
++PVRDRIScreenRemoveReference(PVRDRIScreen *psPVRScreen)
++{
++   int iRefCount = p_atomic_dec_return(&psPVRScreen->iRefCount);
++
++   assert(iRefCount >= 0);
++
++   if (iRefCount != 0)
++      return;
++
++   pvrdri_free_dispatch_tables(psPVRScreen);
++   DRISUPDestroyScreen(psPVRScreen->psDRISUPScreen);
++   PVRDRICompatDeinit();
++   free(psPVRScreen);
++}
++
++void
++PVRDRIDrawableAddReference(PVRDRIDrawable *psPVRDrawable)
++{
++   int iRefCount = p_atomic_inc_return(&psPVRDrawable->iRefCount);
++
++   (void) iRefCount;
++   assert(iRefCount > 1);
++}
++
++void
++PVRDRIDrawableRemoveReference(PVRDRIDrawable *psPVRDrawable)
++{
++   int iRefCount = p_atomic_dec_return(&psPVRDrawable->iRefCount);
++
++   assert(iRefCount >= 0);
++
++   if (iRefCount != 0)
++      return;
++
++   DRISUPDestroyDrawable(psPVRDrawable->psDRISUPDrawable);
++
++#if defined(DEBUG)
++   p_atomic_dec(&psPVRDrawable->psPVRScreen->iDrawableAlloc);
++#endif
++
++   PVRDRIScreenRemoveReference(psPVRDrawable->psPVRScreen);
++   free(psPVRDrawable);
++}
++
++static void
++PVRScreenPrintExtensions(__DRIscreen *psDRIScreen)
++{
++   /* Don't attempt to print anything if LIBGL_DEBUG isn't in the environment */
++   if (getenv("LIBGL_DEBUG") == NULL)
++      return;
++
++   if (psDRIScreen->extensions) {
++      const __DRIextension *psScreenExtensionVersionInfo =
++         PVRDRIScreenExtensionVersionInfo();
++      int i;
++      int j;
++
++      __driUtilMessage("Supported screen extensions:");
++
++      for (i = 0; psDRIScreen->extensions[i]; i++) {
++         for (j = 0; psScreenExtensionVersionInfo[j].name; j++) {
++            if (strcmp(psDRIScreen->extensions[i]->name,
++                       psScreenExtensionVersionInfo[j].name) == 0) {
++               __driUtilMessage("\t%s (supported version: %u - max version: %u)",
++                                psDRIScreen->extensions[i]->name,
++                                psDRIScreen->extensions[i]->version,
++                                psScreenExtensionVersionInfo[j].version);
++               break;
++            }
++         }
++
++         if (psScreenExtensionVersionInfo[j].name == NULL) {
++            __driUtilMessage("\t%s (supported version: %u - max version: unknown)",
++                             psDRIScreen->extensions[i]->name,
++                             psDRIScreen->extensions[i]->version);
++         }
++      }
++   } else {
++      __driUtilMessage("No screen extensions found");
++   }
++}
++
++/*************************************************************************//*!
++ Mesa driver API functions
++ *//**************************************************************************/
++
++static const __DRIconfig **
++PVRDRIInitScreen(__DRIscreen *psDRIScreen)
++{
++   PVRDRIScreen *psPVRScreen;
++   const __DRIconfig **ppsConfigs;
++   int iMaxGLES1Version, iMaxGLES2Version;
++   const struct PVRDRICallbacksV2 sDRICallbacksV2 = {
++      /* Version 0 callbacks */
++      .v0.RegisterSupportInterface = MODSUPRegisterSupportInterfaceV2,
++      .v0.GetBuffers = MODSUPGetBuffers,
++      .v0.CreateConfigs = MODSUPCreateConfigs,
++      .v0.ConcatConfigs = MODSUPConcatConfigs,
++      .v0.ConfigQuery = MODSUPConfigQuery,
++      .v0.LookupEGLImage = MODSUPLookupEGLImage,
++      .v0.GetCapability = MODSUPGetCapability,
++      /* Version 1 callbacks */
++      .v1.FlushFrontBuffer = MODSUPFlushFrontBuffer,
++      /* Version 2 callbacks */
++      .v2.GetDisplayFD = MODSUPGetDisplayFD,
++      /* Version 3 callbacks */
++      .v3.DrawableGetReferenceHandle = MODSUPDrawableGetReferenceHandle,
++      .v3.DrawableAddReference = MODSUPDrawableAddReference,
++      .v3.DrawableRemoveReference = MODSUPDrawableRemoveReference,
++   };
++
++   if (!PVRLoaderIsSupported(psDRIScreen))
++      return NULL;
++
++   if (!PVRDRICompatInit(&sDRICallbacksV2, 3, 0))
++      return NULL;
++
++   psPVRScreen = calloc(1, sizeof(*psPVRScreen));
++   if (psPVRScreen == NULL) {
++      __driUtilMessage("%s: Couldn't allocate PVRDRIScreen", __func__);
++      goto ErrorCompatDeinit;
++   }
++
++   psDRIScreen->driverPrivate = psPVRScreen;
++   psPVRScreen->psDRIScreen = psDRIScreen;
++   psPVRScreen->iRefCount = 1;
++
++   psPVRScreen->psDRISUPScreen =
++      DRISUPCreateScreen(psDRIScreen, psDRIScreen->fd,
++                         psDRIScreen->dri2.useInvalidate != NULL,
++                         psDRIScreen->loaderPrivate,
++                         &ppsConfigs, &iMaxGLES1Version, &iMaxGLES2Version);
++   if (!psPVRScreen->psDRISUPScreen)
++      goto ErrorScreenFree;
++
++   psDRIScreen->max_gl_es1_version = iMaxGLES1Version;
++   psDRIScreen->max_gl_es2_version = iMaxGLES2Version;
++
++   psDRIScreen->max_gl_compat_version =
++      DRISUPGetAPIVersion(psPVRScreen->psDRISUPScreen, PVRDRI_API_GL_COMPAT);
++   psDRIScreen->max_gl_core_version =
++      DRISUPGetAPIVersion(psPVRScreen->psDRISUPScreen, PVRDRI_API_GL_CORE);
++
++   psDRIScreen->extensions = PVRDRIScreenExtensions();
++
++   PVRScreenPrintExtensions(psDRIScreen);
++
++   return ppsConfigs;
++
++ErrorScreenFree:
++   psDRIScreen->driverPrivate = NULL;
++   free(psPVRScreen);
++
++ErrorCompatDeinit:
++   PVRDRICompatDeinit();
++
++   return NULL;
++}
++
++static void
++PVRDRIDestroyScreen(__DRIscreen *psDRIScreen)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++#if defined(DEBUG)
++   if (psPVRScreen->iBufferAlloc != 0 ||
++       psPVRScreen->iDrawableAlloc != 0 ||
++       psPVRScreen->iContextAlloc != 0) {
++      errorMessage("%s: Outstanding allocations: Contexts: %d Drawables: %d Buffers: %d",
++                   __func__, psPVRScreen->iContextAlloc,
++                   psPVRScreen->iDrawableAlloc, psPVRScreen->iBufferAlloc);
++
++      if (psPVRScreen->iRefCount > 1) {
++         errorMessage("%s: PVRDRIScreen resources will not be freed until its %d references are removed",
++                      __func__, psPVRScreen->iRefCount - 1);
++      }
++   }
++#endif
++
++   PVRDRIScreenRemoveReference(psPVRScreen);
++}
++
++static int
++PVRDRIScreenSupportedAPIs(PVRDRIScreen *psPVRScreen)
++{
++   unsigned int api_mask = psPVRScreen->psDRIScreen->api_mask;
++   int supported = 0;
++
++   if ((api_mask & (1 << __DRI_API_GLES)) != 0)
++      supported |= PVRDRI_API_BIT_GLES;
++
++   if ((api_mask & (1 << __DRI_API_GLES2)) != 0)
++      supported |= PVRDRI_API_BIT_GLES2;
++
++   if ((api_mask & (1 << __DRI_API_GLES3)) != 0)
++      supported |= PVRDRI_API_BIT_GLES3;
++
++   if ((api_mask & (1 << __DRI_API_OPENGL)) != 0)
++      supported |= PVRDRI_API_BIT_GL;
++
++   if ((api_mask & (1 << __DRI_API_OPENGL_CORE)) != 0)
++      supported |= PVRDRI_API_BIT_GL;
++
++   return supported;
++}
++
++static GLboolean
++PVRDRICreateContext(gl_api eMesaAPI, const struct gl_config *psGLMode,
++                    __DRIcontext *psDRIContext,
++                    const struct __DriverContextConfig *psCtxConfig,
++                    unsigned int *puError, void *pvSharedContextPrivate)
++{
++   __DRIscreen *psDRIScreen = psDRIContext->driScreenPriv;
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   PVRDRIContext *psPVRContext;
++   struct DRISUPContext *psDRISUPContext;
++   struct DRISUPContext *psDRISUPSharedContext;
++   struct PVRDRIContextConfig sCtxConfig;
++   PVRDRIAPIType eAPI;
++
++   psDRISUPSharedContext = getSharedContextImpl(pvSharedContextPrivate);
++
++   sCtxConfig.uMajorVersion = psCtxConfig->major_version;
++   sCtxConfig.uMinorVersion = psCtxConfig->minor_version;
++   sCtxConfig.uFlags = psCtxConfig->flags;
++   sCtxConfig.iResetStrategy = __DRI_CTX_RESET_NO_NOTIFICATION;
++   sCtxConfig.uPriority = __DRI_CTX_PRIORITY_MEDIUM;
++   sCtxConfig.iReleaseBehavior = __DRI_CTX_RELEASE_BEHAVIOR_FLUSH;
++
++   psPVRContext = calloc(1, sizeof(*psPVRContext));
++   if (psPVRContext == NULL) {
++      __driUtilMessage("%s: Couldn't allocate PVRDRIContext", __func__);
++      *puError = __DRI_CTX_ERROR_NO_MEMORY;
++      return GL_FALSE;
++   }
++
++   psPVRContext->psDRIContext = psDRIContext;
++   psPVRContext->psPVRScreen = psPVRScreen;
++
++   if (psGLMode)
++      psPVRContext->sConfig.sGLMode = *psGLMode;
++
++   switch (eMesaAPI) {
++   case API_OPENGLES:
++      eAPI = PVRDRI_API_GLES1;
++      break;
++   case API_OPENGLES2:
++      eAPI = PVRDRI_API_GLES2;
++      break;
++   case API_OPENGL_COMPAT:
++      eAPI = PVRDRI_API_GL_COMPAT;
++      break;
++   case API_OPENGL_CORE:
++      eAPI = PVRDRI_API_GL_CORE;
++      break;
++   default:
++      __driUtilMessage("%s: Unsupported API: %d",
++                       __func__, (int) eMesaAPI);
++      *puError = __DRI_CTX_ERROR_BAD_API;
++      goto ErrorContextFree;
++   }
++   psPVRContext->eAPI = eAPI;
++
++   if ((psCtxConfig->attribute_mask &
++        __DRIVER_CONTEXT_ATTRIB_RESET_STRATEGY) != 0) {
++      sCtxConfig.iResetStrategy = psCtxConfig->reset_strategy;
++   }
++
++   if ((psCtxConfig->attribute_mask &
++        __DRIVER_CONTEXT_ATTRIB_RELEASE_BEHAVIOR) != 0) {
++      sCtxConfig.iReleaseBehavior = psCtxConfig->release_behavior;
++   }
++
++   if ((psCtxConfig->attribute_mask &
++        __DRIVER_CONTEXT_ATTRIB_PRIORITY) != 0) {
++      sCtxConfig.uPriority = psCtxConfig->priority;
++   }
++
++   *puError = DRISUPCreateContext(eAPI, &psPVRContext->sConfig, &sCtxConfig,
++                                  psDRIContext, psDRISUPSharedContext,
++                                  psPVRScreen->psDRISUPScreen,
++                                  &psDRISUPContext);
++   if (*puError != __DRI_CTX_ERROR_SUCCESS)
++      goto ErrorContextFree;
++
++   psPVRContext->psDRISUPContext = psDRISUPContext;
++
++   if (!pvrdri_create_dispatch_table(psPVRScreen, eAPI)) {
++      __driUtilMessage("%s: Couldn't create dispatch table", __func__);
++      *puError = __DRI_CTX_ERROR_BAD_API;
++      goto ErrorContextDestroy;
++   }
++#if defined(DEBUG)
++   p_atomic_inc(&psPVRScreen->iContextAlloc);
++#endif
++
++   psDRIContext->driverPrivate = (void *) psPVRContext;
++   PVRDRIScreenAddReference(psPVRScreen);
++
++   *puError = __DRI_CTX_ERROR_SUCCESS;
++
++   return GL_TRUE;
++
++ErrorContextDestroy:
++   DRISUPDestroyContext(psPVRContext->psDRISUPContext);
++
++ErrorContextFree:
++   free(psPVRContext);
++
++   return GL_FALSE;
++}
++
++static void
++PVRDRIDestroyContext(__DRIcontext *psDRIContext)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++   PVRDRIScreen *psPVRScreen = psPVRContext->psPVRScreen;
++
++   DRISUPDestroyContext(psPVRContext->psDRISUPContext);
++
++#if defined(DEBUG)
++   p_atomic_dec(&psPVRScreen->iContextAlloc);
++#endif
++
++   PVRDRIScreenRemoveReference(psPVRScreen);
++   free(psPVRContext);
++}
++
++static GLboolean
++PVRDRICreateBuffer(__DRIscreen *psDRIScreen, __DRIdrawable *psDRIDrawable,
++                   const struct gl_config *psGLMode, GLboolean bIsPixmap)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   PVRDRIDrawable *psPVRDrawable = NULL;
++
++   /* No known callers ever set this to true */
++   if (bIsPixmap)
++      return GL_FALSE;
++
++   if (!psGLMode) {
++      __driUtilMessage("%s: Invalid GL config", __func__);
++      return GL_FALSE;
++   }
++
++   psPVRDrawable = calloc(1, sizeof(*psPVRDrawable));
++   if (!psPVRDrawable) {
++      __driUtilMessage("%s: Couldn't allocate PVR drawable", __func__);
++      goto ErrorDrawableFree;
++   }
++
++   psDRIDrawable->driverPrivate = (void *) psPVRDrawable;
++   psPVRDrawable->iRefCount = 1;
++   psPVRDrawable->psDRIDrawable = psDRIDrawable;
++   psPVRDrawable->psPVRScreen = psPVRScreen;
++   psPVRDrawable->sConfig.sGLMode = *psGLMode;
++   psPVRDrawable->sConfig.iSupportedAPIs =
++      PVRDRIScreenSupportedAPIs(psPVRScreen);
++
++   psPVRDrawable->psDRISUPDrawable =
++      DRISUPCreateDrawable(psDRIDrawable, psPVRScreen->psDRISUPScreen,
++                           psDRIDrawable->loaderPrivate,
++                           &psPVRDrawable->sConfig);
++   if (!psPVRDrawable->psDRISUPDrawable) {
++      __driUtilMessage("%s: Couldn't create DRI Support drawable",
++                       __func__);
++      goto ErrorDrawableFree;
++   }
++
++   /* Initialisation is completed in MakeCurrent */
++#if defined(DEBUG)
++   p_atomic_inc(&psPVRScreen->iDrawableAlloc);
++#endif
++   PVRDRIScreenAddReference(psPVRScreen);
++   return GL_TRUE;
++
++ErrorDrawableFree:
++   DRISUPDestroyDrawable(psPVRDrawable->psDRISUPDrawable);
++   free(psPVRDrawable);
++   psDRIDrawable->driverPrivate = NULL;
++
++   return GL_FALSE;
++}
++
++static void
++PVRDRIDestroyBuffer(__DRIdrawable *psDRIDrawable)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++
++   PVRDRIDrawableRemoveReference(psPVRDrawable);
++}
++
++static GLboolean
++PVRDRIMakeCurrent(__DRIcontext *psDRIContext,
++                  __DRIdrawable *psDRIWrite, __DRIdrawable *psDRIRead)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++   struct DRISUPDrawable *psDRISUPWrite;
++   struct DRISUPDrawable *psDRISUPRead;
++
++   if (psDRIWrite) {
++      PVRDRIDrawable *psPVRWrite = psDRIWrite->driverPrivate;
++
++      psDRISUPWrite = psPVRWrite->psDRISUPDrawable;
++   } else {
++      psDRISUPWrite = NULL;
++   }
++
++   if (psDRIRead) {
++      PVRDRIDrawable *psPVRRead = psDRIRead->driverPrivate;
++
++      psDRISUPRead = psPVRRead->psDRISUPDrawable;
++   } else {
++      psDRISUPRead = NULL;
++   }
++
++   if (!DRISUPMakeCurrent(psPVRContext->psDRISUPContext,
++                          psDRISUPWrite, psDRISUPRead))
++      goto ErrorUnlock;
++
++   pvrdri_set_dispatch_table(psPVRContext);
++
++   return GL_TRUE;
++
++ErrorUnlock:
++   return GL_FALSE;
++}
++
++static GLboolean
++PVRDRIUnbindContext(__DRIcontext *psDRIContext)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   pvrdri_set_null_dispatch_table();
++   DRISUPUnbindContext(psPVRContext->psDRISUPContext);
++
++   return GL_TRUE;
++}
++
++static __DRIbuffer *
++PVRDRIAllocateBuffer(__DRIscreen *psDRIScreen, unsigned int uAttachment,
++                     unsigned int uFormat, int iWidth, int iHeight)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   struct PVRBuffer *psBuffer;
++
++   psBuffer = calloc(1, sizeof(*psBuffer));
++   if (psBuffer == NULL) {
++      __driUtilMessage("%s: Failed to allocate buffer", __func__);
++      return NULL;
++   }
++
++   psBuffer->psDRISUPBuffer =
++      DRISUPAllocateBuffer(psPVRScreen->psDRISUPScreen, uAttachment, uFormat,
++                           iWidth, iHeight, &psBuffer->sDRIBuffer.name,
++                           &psBuffer->sDRIBuffer.pitch,
++                           &psBuffer->sDRIBuffer.cpp,
++                           &psBuffer->sDRIBuffer.flags);
++   if (!psBuffer->psDRISUPBuffer) {
++      __driUtilMessage("%s: Failed to create DRI Support buffer", __func__);
++      goto ErrorFreeDRIBuffer;
++   }
++
++   psBuffer->sDRIBuffer.attachment = uAttachment;
++
++#if defined(DEBUG)
++   p_atomic_inc(&psPVRScreen->iBufferAlloc);
++#endif
++
++   return &psBuffer->sDRIBuffer;
++
++ErrorFreeDRIBuffer:
++   free(psBuffer);
++
++   return NULL;
++}
++
++static void
++PVRDRIReleaseBuffer(__DRIscreen *psDRIScreen, __DRIbuffer *psDRIBuffer)
++{
++   struct PVRBuffer *psPVRBuffer = (struct PVRBuffer *) psDRIBuffer;
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   DRISUPReleaseBuffer(psPVRScreen->psDRISUPScreen,
++                       psPVRBuffer->psDRISUPBuffer);
++
++#if defined(DEBUG)
++   p_atomic_dec(&psPVRScreen->iBufferAlloc);
++#endif
++
++   free(psPVRBuffer);
++}
++
++static const struct __DriverAPIRec pvr_driver_api = {
++   .InitScreen = PVRDRIInitScreen,
++   .DestroyScreen = PVRDRIDestroyScreen,
++   .CreateContext = PVRDRICreateContext,
++   .DestroyContext = PVRDRIDestroyContext,
++   .CreateBuffer = PVRDRICreateBuffer,
++   .DestroyBuffer = PVRDRIDestroyBuffer,
++   .SwapBuffers = NULL,
++   .MakeCurrent = PVRDRIMakeCurrent,
++   .UnbindContext = PVRDRIUnbindContext,
++   .AllocateBuffer = PVRDRIAllocateBuffer,
++   .ReleaseBuffer = PVRDRIReleaseBuffer,
++};
++
++static const struct __DRIDriverVtableExtensionRec pvr_vtable = {
++   .base = {__DRI_DRIVER_VTABLE, 1},
++   .vtable = &pvr_driver_api,
++};
++
++static const __DRIextension *pvr_driver_extensions[] = {
++   &driCoreExtension.base,
++   &driImageDriverExtension.base,
++   &driDRI2Extension.base,
++   &pvr_vtable.base,
++   NULL
++};
++
++const __DRIextension **__driDriverGetExtensions_pvr(void);
++
++PUBLIC const __DRIextension **
++__driDriverGetExtensions_pvr(void)
++{
++   globalDriverAPI = &pvr_driver_api;
++
++   return pvr_driver_extensions;
++}
+diff --git a/src/mesa/drivers/dri/pvr/pvrdri.h b/src/mesa/drivers/dri/pvr/pvrdri.h
+new file mode 100644
+index 0000000..fd465bf
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrdri.h
+@@ -0,0 +1,188 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#if !defined(__PVRDRI_H__)
++#define __PVRDRI_H__
++
++#include <stdbool.h>
++#include <stdint.h>
++#include <inttypes.h>
++
++#include <glapi/glapi.h>
++
++#include "main/mtypes.h"
++#include "util/macros.h"
++#include "dri_util.h"
++#include "pvrdri_support.h"
++
++struct PVRDRIConfigRec {
++   struct gl_config sGLMode;
++   int iSupportedAPIs;
++};
++
++/* PVR screen data */
++typedef struct PVRDRIScreen_TAG {
++   /* DRI screen structure pointer */
++   __DRIscreen *psDRIScreen;
++
++   /* Opaque PVR DRI Support screen structure pointer */
++   struct DRISUPScreen *psDRISUPScreen;
++
++   /* Reference count */
++   int iRefCount;
++
++#if defined(DEBUG)
++   /* Counters of outstanding allocations */
++   int iContextAlloc;
++   int iDrawableAlloc;
++   int iBufferAlloc;
++#endif
++
++   /* PVR OGLES 1 dispatch table */
++   struct _glapi_table *psOGLES1Dispatch;
++   /* PVR OGLES 2/3 dispatch table */
++   struct _glapi_table *psOGLES2Dispatch;
++   /* PVR OGL dispatch table */
++   struct _glapi_table *psOGLDispatch;
++} PVRDRIScreen;
++
++/* PVR context data */
++typedef struct PVRDRIContext_TAG {
++   /* Pointer to DRI context */
++   __DRIcontext *psDRIContext;
++
++   /* Opaque PVR DRI Support context structure pointer */
++   struct DRISUPContext *psDRISUPContext;
++
++   /* Pointer to PVRDRIScreen structure */
++   PVRDRIScreen *psPVRScreen;
++
++   /* GL config */
++   PVRDRIConfig sConfig;
++
++   /* API */
++   PVRDRIAPIType eAPI;
++} PVRDRIContext;
++
++/* PVR drawable data */
++typedef struct PVRDRIDrawable_TAG {
++   PVRDRIScreen *psPVRScreen;
++   __DRIdrawable *psDRIDrawable;
++   int iRefCount;
++   PVRDRIConfig sConfig;
++   struct DRISUPDrawable *psDRISUPDrawable;
++   unsigned int uFourCC;
++   unsigned int uDRIFormat;
++} PVRDRIDrawable;
++
++/*************************************************************************//*!
++ pvrdri.c
++ *//**************************************************************************/
++void PVRDRIDrawableAddReference(PVRDRIDrawable *psPVRDrawable);
++void PVRDRIDrawableRemoveReference(PVRDRIDrawable *psPVRDrawable);
++
++/*************************************************************************//*!
++ pvrutil.c
++ *//**************************************************************************/
++
++void PRINTFLIKE(1, 2) __driUtilMessage(const char *f, ...);
++void PRINTFLIKE(1, 2) errorMessage(const char *f, ...);
++
++mesa_format PVRDRIMesaFormatToMesaFormat(int pvrdri_mesa_format);
++int PVRDRIFormatToFourCC(int dri_format);
++int PVRDRIFourCCToDRIFormat(int iFourCC);
++
++/*************************************************************************//*!
++ pvrext.c
++ *//**************************************************************************/
++
++const __DRIextension **PVRDRIScreenExtensions(void);
++const __DRIextension *PVRDRIScreenExtensionVersionInfo(void);
++
++void PVRDRIAdjustExtensions(unsigned int uVersion, unsigned int uMinVersion);
++
++/*************************************************************************//*!
++ pvrcompat.c
++ *//**************************************************************************/
++
++bool PVRDRICompatInit(const struct PVRDRICallbacksV2 *psCallbacksV2,
++                      unsigned int uVersionV2, unsigned int uMinVersionV2);
++void PVRDRICompatDeinit(void);
++
++bool MODSUPRegisterSupportInterfaceV2(const void *pvInterface,
++                                      unsigned int uVersion,
++                                      unsigned int uMinVersion);
++
++/*************************************************************************//*!
++ pvrcb.c
++ *//**************************************************************************/
++
++int MODSUPGetBuffers(struct __DRIdrawableRec *psDRIDrawable,
++                     unsigned int uFourCC, uint32_t *puStamp,
++                     void *pvLoaderPrivate, uint32_t uBufferMask,
++                     struct PVRDRIImageList *psImageList);
++
++bool MODSUPCreateConfigs(struct __DRIconfigRec ***psConfigs,
++                         struct __DRIscreenRec *psDRIScreen,
++                         int iPVRDRIMesaFormat, const uint8_t *puDepthBits,
++                         const uint8_t *puStencilBits,
++                         unsigned int uNumDepthStencilBits,
++                         const unsigned int *puDBModes,
++                         unsigned int uNumDBModes,
++                         const uint8_t *puMSAASamples,
++                         unsigned int uNumMSAAModes, bool bEnableAccum,
++                         bool bColorDepthMatch, bool bMutableRenderBuffer,
++                         int iYUVDepthRange, int iYUVCSCStandard,
++                         uint32_t uMaxPbufferWidth, uint32_t uMaxPbufferHeight);
++
++struct __DRIconfigRec **MODSUPConcatConfigs(struct __DRIscreenRec *psDRIScreen,
++                                            struct __DRIconfigRec **ppsConfigA,
++                                            struct __DRIconfigRec **ppsConfigB);
++
++__DRIimage *MODSUPLookupEGLImage(struct __DRIscreenRec *psDRIScreen,
++                                 void *pvImage, void *pvLoaderPrivate);
++
++unsigned int MODSUPGetCapability(struct __DRIscreenRec *psDRIScreen,
++                                 unsigned int uCapability);
++
++int MODSUPGetDisplayFD(struct __DRIscreenRec *psDRIScreen,
++                       void *pvLoaderPrivate);
++
++bool PVRDRIConfigQuery(const PVRDRIConfig *psConfig,
++                       PVRDRIConfigAttrib eConfigAttrib, int *piValueOut);
++
++bool MODSUPConfigQuery(const PVRDRIConfig *psConfig,
++                       PVRDRIConfigAttrib eConfigAttrib,
++                       unsigned int *puValueOut);
++
++void MODSUPFlushFrontBuffer(struct __DRIdrawableRec *psDRIDrawable,
++                            void *pvLoaderPrivate);
++
++void *MODSUPDrawableGetReferenceHandle(struct __DRIdrawableRec *psDRIDrawable);
++
++void MODSUPDrawableAddReference(void *pvReferenceHandle);
++
++void MODSUPDrawableRemoveReference(void *pvReferenceHandle);
++
++#endif /* defined(__PVRDRI_H__) */
+diff --git a/src/mesa/drivers/dri/pvr/pvrdri_support.h b/src/mesa/drivers/dri/pvr/pvrdri_support.h
+new file mode 100644
+index 0000000..58ea350
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrdri_support.h
+@@ -0,0 +1,206 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#if !defined(__PVRDRI_SUPPORT_H__)
++#define __PVRDRI_SUPPORT_H__
++
++#include <stdint.h>
++#include <stdbool.h>
++
++#include "dri_support.h"
++
++struct DRISUPScreen *DRISUPCreateScreen(struct __DRIscreenRec *psDRIScreen,
++                                        int iFD, bool bUseInvalidate,
++                                        void *pvLoaderPrivate,
++                                        const struct __DRIconfigRec ***pppsConfigs,
++                                        int *piMaxGLES1Version,
++                                        int *piMaxGLES2Version);
++void DRISUPDestroyScreen(struct DRISUPScreen *psDRISUPScreen);
++
++unsigned int DRISUPCreateContext(PVRDRIAPIType eAPI,
++                                 PVRDRIConfig *psPVRDRIConfig,
++                                 struct PVRDRIContextConfig *psCtxConfig,
++                                 struct __DRIcontextRec *psDRIContext,
++                                 struct DRISUPContext *psDRISUPSharedContext,
++                                 struct DRISUPScreen *psDRISUPScreen,
++                                 struct DRISUPContext **ppsDRISUPContext);
++void DRISUPDestroyContext(struct DRISUPContext *psDRISUPContext);
++
++struct DRISUPDrawable *DRISUPCreateDrawable(struct __DRIdrawableRec *psDRIDrawable,
++                                            struct DRISUPScreen *psDRISUPScreen,
++                                            void *pvLoaderPrivate,
++                                            PVRDRIConfig *psPVRDRIConfig);
++void DRISUPDestroyDrawable(struct DRISUPDrawable *psDRISUPDrawable);
++
++bool DRISUPMakeCurrent(struct DRISUPContext *psDRISUPContext,
++                       struct DRISUPDrawable *psDRISUPWrite,
++                       struct DRISUPDrawable *psDRISUPRead);
++bool DRISUPUnbindContext(struct DRISUPContext *psDRISUPContext);
++
++struct DRISUPBuffer *DRISUPAllocateBuffer(struct DRISUPScreen *psDRISUPScreen,
++                                          unsigned int uAttchment,
++                                          unsigned int uFormat,
++                                          int iWidth, int iHeight,
++                                          unsigned int *puName,
++                                          unsigned int *puPitch,
++                                          unsigned int *puCPP,
++                                          unsigned int *puFlags);
++void DRISUPReleaseBuffer(struct DRISUPScreen *psDRISUPScreen,
++                         struct DRISUPBuffer *psDRISUPBuffer);
++void DRISUPSetTexBuffer2(struct DRISUPContext *psDRISUPContext,
++                         int iTarget, int iFormat,
++                         struct DRISUPDrawable *psDRISUPDrawable);
++void DRISUPReleaseTexBuffer(struct DRISUPContext *psDRISUPContext,
++                            int iTarget,
++                            struct DRISUPDrawable *psDRISUPDrawable);
++
++void DRISUPFlush(struct DRISUPDrawable *psDRISUPDrawable);
++void DRISUPInvalidate(struct DRISUPDrawable *psDRISUPDrawable);
++void DRISUPFlushWithFlags(struct DRISUPContext *psDRISUPContext,
++                          struct DRISUPDrawable *psDRISUPDrawable,
++                          unsigned int uFlags, unsigned int uThrottleReason);
++
++__DRIimage *DRISUPCreateImageFromName(struct DRISUPScreen *psDRISUPScreen,
++                                      int iWidth, int iHeight, int iFourCC,
++                                      int iName, int iPitch,
++                                      void *pvLoaderPrivate);
++__DRIimage *DRISUPCreateImageFromRenderbuffer(struct DRISUPContext *psDRISUPContext,
++                                              int iRenderBuffer,
++                                              void *pvLoaderPrivate);
++void DRISUPDestroyImage(__DRIimage *psImage);
++__DRIimage *DRISUPCreateImage(struct DRISUPScreen *psDRISUPScreen,
++                              int iWidth, int iHeight, int iFourCC,
++                              unsigned int uUse, void *pvLoaderPrivate);
++bool DRISUPQueryImage(__DRIimage *psImage, int iAttrib, int *piValue);
++__DRIimage *DRISUPDupImage(__DRIimage *psImage, void *pvLoaderPrivate);
++bool DRISUPValidateImageUsage(__DRIimage *psImage, unsigned int uUse);
++__DRIimage *DRISUPCreateImageFromNames(struct DRISUPScreen *psDRISUPScreen,
++                                       int iWidth, int iHeight, int iFourCC,
++                                       int *piNames, int iNumNames,
++                                       int *piStrides, int *piOffsets,
++                                       void *pvLoaderPrivate);
++__DRIimage *DRISUPFromPlanar(__DRIimage *psImage, int iPlane,
++                             void *pvLoaderPrivate);
++__DRIimage *DRISUPCreateImageFromTexture(struct DRISUPContext *psDRISUPContext,
++                                         int iTarget, unsigned int uTexture,
++                                         int iDepth, int iLevel,
++                                         unsigned int *puError,
++                                         void *pvLoaderPrivate);
++__DRIimage *DRISUPCreateImageFromFDs(struct DRISUPScreen *psDRISUPcreen,
++                                     int iWidth, int iHeight, int iFourCC,
++                                     int *piFDs, int iNumFDs,
++                                     int *piStrides, int *piOffsets,
++                                     void *pvLoaderPrivate);
++__DRIimage *DRISUPCreateImageFromDmaBufs(struct DRISUPScreen *psDRISUPScreen,
++                                         int iWidth, int iHeight, int iFourCC,
++                                         int *piFDs, int iNumFDs,
++                                         int *piStrides, int *piOffsets,
++                                         unsigned int uColorSpace,
++                                         unsigned int uSampleRange,
++                                         unsigned int uHorizSiting,
++                                         unsigned int uVertSiting,
++                                         unsigned int *puError,
++                                         void *pvLoaderPrivate);
++int DRISUPGetImageCapabilities(struct DRISUPScreen *psDRISUPScreen);
++void DRISUPBlitImage(struct DRISUPContext *psDRISUPContext,
++                     __DRIimage *psDst, __DRIimage *psSrc,
++                     int iDstX0, int iDstY0, int iDstWidth, int iDstHeight,
++                     int iSrcX0, int iSrcY0, int iSrcWidth, int iSrcHeight,
++                     int iFlushFlag);
++void *DRISUPMapImage(struct DRISUPContext *psDRISUPContext,
++                     __DRIimage *psImage,
++                     int iX0, int iY0, int iWidth, int iHeight,
++                     unsigned int uFlags, int *piStride, void **ppvData);
++void DRISUPUnmapImage(struct DRISUPContext *psDRISUPContext,
++                      __DRIimage *psImage, void *pvData);
++__DRIimage *DRISUPCreateImageWithModifiers(struct DRISUPScreen *psDRISUPScreen,
++                                           int iWidth, int iHeight, int iFourCC,
++                                           const uint64_t *puModifiers,
++                                           const unsigned int uModifierCount,
++                                           void *pvLoaderPrivate);
++__DRIimage *DRISUPCreateImageFromDMABufs2(struct DRISUPScreen *psDRISUPScreen,
++                                          int iWidth, int iHeight,
++                                          int iFourCC, uint64_t uModifier,
++                                          int *piFDs, int iNumFDs,
++                                          int *piStrides, int *piOffsets,
++                                          unsigned int uColorSpace,
++                                          unsigned int uSampleRange,
++                                          unsigned int uHorizSiting,
++                                          unsigned int uVertSiting,
++                                          unsigned int *puError,
++                                          void *pvLoaderPrivate);
++
++bool DRISUPQueryDMABufFormats(struct DRISUPScreen *psDRISUPScreen, int iMax,
++                              int *piFormats, int *piCount);
++bool DRISUPQueryDMABufModifiers(struct DRISUPScreen *psDRISUPScreen,
++                                int iFourCC, int iMax, uint64_t *puModifiers,
++                                unsigned int *piExternalOnly, int *piCount);
++bool DRISUPQueryDMABufFormatModifierAttribs(struct DRISUPScreen *psDRISUPScreen,
++                                            uint32_t uFourcc,
++                                            uint64_t uModifier,
++                                            int iAttrib, uint64_t *puValue);
++
++__DRIimage *DRISUPCreateImageFromRenderBuffer2(struct DRISUPContext *psDRISUPContext,
++                                               int iRenderBuffer,
++                                               void *pvLoaderPrivate,
++                                               unsigned int *puError);
++
++__DRIimage *DRISUPCreateImageFromBuffer(struct DRISUPContext *psDRISUPContext,
++                                        int iTarget, void *pvBuffer,
++                                        unsigned int *puError,
++                                        void *pvLoaderPrivate);
++
++int DRISUPQueryRendererInteger(struct DRISUPScreen *psDRISUPScreen,
++                               int iAttribute, unsigned int *puValue);
++int DRISUPQueryRendererString(struct DRISUPScreen *psDRISUPScreen,
++                              int iAttribute, const char **ppszValue);
++
++void *DRISUPCreateFence(struct DRISUPContext *psDRISUPContext);
++void DRISUPDestroyFence(struct DRISUPScreen *psDRISUPScreen, void *pvFence);
++bool DRISUPClientWaitSync(struct DRISUPContext *psDRISUPContext,
++                          void *pvFence, unsigned int uFlags,
++                          uint64_t uTimeout);
++void DRISUPServerWaitSync(struct DRISUPContext *psDRISUPContext,
++                          void *pvFence, unsigned int uFlags);
++unsigned int DRISUPGetFenceCapabilities(struct DRISUPScreen *psDRISUPScreen);
++void *DRISUPCreateFenceFD(struct DRISUPContext *psDRISUPContext, int iFD);
++int DRISUPGetFenceFD(struct DRISUPScreen *psDRISUPScreen, void *pvFence);
++void *DRISUPGetFenceFromCLEvent(struct DRISUPScreen *psDRISUPScreen,
++                                intptr_t iCLEvent);
++int DRISUPGetAPIVersion(struct DRISUPScreen *psDRISUPScreen,
++                        PVRDRIAPIType eAPI);
++
++unsigned int DRISUPGetNumAPIProcs(struct DRISUPScreen *psDRISUPScreen,
++                                  PVRDRIAPIType eAPI);
++const char *DRISUPGetAPIProcName(struct DRISUPScreen *psDRISUPScreen,
++                                 PVRDRIAPIType eAPI, unsigned int uIndex);
++void *DRISUPGetAPIProcAddress(struct DRISUPScreen *psDRISUPScreen,
++                              PVRDRIAPIType eAPI, unsigned int uIndex);
++
++void DRISUPSetDamageRegion(struct DRISUPDrawable *psDRISUPDrawable,
++                           unsigned int uNRects, int *piRects);
++
++bool DRISUPHaveGetFenceFromCLEvent(void);
++
++#endif /* defined(__PVRDRI_SUPPORT_H__) */
+diff --git a/src/mesa/drivers/dri/pvr/pvrext.c b/src/mesa/drivers/dri/pvr/pvrext.c
+new file mode 100644
+index 0000000..4783996
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrext.c
+@@ -0,0 +1,704 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++/*
++ * EXTENSION SUPPORT
++ *
++ * As the driver supports a range of Mesa versions it can be the case that it
++ * needs to support different extensions and extension versions depending on
++ * the version of Mesa that it's built against. As a guide the following rules
++ * should be followed:
++ *
++ * 1) If an extension appears in some supported versions of Mesa but not others
++ *    then it should be protected by the extension define, e.g.:
++ *    #if defined(__DRI_IMAGE)
++ *    <code>
++ *    #endif
++ *
++ *    However, if it appears in all versions then there's no need for it to
++ *    be protected.
++ *
++ * 2) Each driver supported extension should have a define for the maximum
++ *    version supported by the driver. This should be used when initialising
++ *    the corresponding extension structure. The Mesa extension version define
++ *    should *NOT* be used.
++ *
++ * 3) If the driver supports a range of versions for a given extension then
++ *    it should protect the extension code based on the Mesa extension version
++ *    define. For example, if the driver has to support versions 7 to 8 of the
++ *    __DRI_IMAGE extension then any fields, in the __DRIimageExtension
++ *    structure, that appear in version 8 but not 7 should be protected as
++ *    follows:
++ *    #if (__DRI_IMAGE_VERSION >= 8)
++ *    .createImageFromDmaBufs = PVRDRICreateImageFromDmaBufs,
++ *    #endif
++ *
++ *    Obviously any other associated code should also be protected in the same
++ *    way.
++ */
++
++#include "dri_util.h"
++#include "utils.h"
++
++#include "dri_support.h"
++#include "pvrdri.h"
++
++#include "EGL/egl.h"
++#include "EGL/eglext.h"
++#include "EGL/eglmesaext.h"
++
++/* Maximum version numbers for each supported extension */
++#define PVR_DRI_TEX_BUFFER_VERSION      3
++#define PVR_DRI2_FLUSH_VERSION          4
++#define PVR_DRI_IMAGE_VERSION           17
++#define PVR_DRI2_ROBUSTNESS_VERSION     1
++#define PVR_DRI2_FENCE_VERSION          2
++#define PVR_DRI2_RENDERER_QUERY_VERSION 1
++#define PVR_DRI2_BUFFER_DAMAGE_VERSION  1
++
++static void
++PVRDRIExtSetTexBuffer(__DRIcontext *psDRIContext, GLint iTarget,
++                      GLint iFormat, __DRIdrawable *psDRIDrawable)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   DRISUPSetTexBuffer2(psPVRContext->psDRISUPContext,
++                       iTarget, iFormat, psPVRDrawable->psDRISUPDrawable);
++}
++
++static void
++PVRDRIExtReleaseTexBuffer(__DRIcontext *psDRIContext, GLint iTarget,
++                          __DRIdrawable *psDRIDrawable)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   DRISUPReleaseTexBuffer(psPVRContext->psDRISUPContext,
++                          iTarget, psPVRDrawable->psDRISUPDrawable);
++
++}
++
++static __DRItexBufferExtension pvrDRITexBufferExtension = {
++   .base = {
++      .name = __DRI_TEX_BUFFER,
++      .version = PVR_DRI_TEX_BUFFER_VERSION,
++   },
++   .setTexBuffer = NULL,
++   .setTexBuffer2 = PVRDRIExtSetTexBuffer,
++   .releaseTexBuffer = PVRDRIExtReleaseTexBuffer,
++};
++
++static void
++PVRDRI2Flush(__DRIdrawable *psDRIDrawable)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++
++   DRISUPFlush(psPVRDrawable->psDRISUPDrawable);
++}
++
++static void
++PVRDRI2Invalidate(__DRIdrawable *psDRIDrawable)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++
++   DRISUPInvalidate(psPVRDrawable->psDRISUPDrawable);
++}
++
++static void
++PVRDRI2FlushWithFlags(__DRIcontext *psDRIContext,
++                      __DRIdrawable *psDRIDrawable,
++                      unsigned int uFlags,
++                      enum __DRI2throttleReason eThrottleReason)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++   struct DRISUPDrawable *psDRISUPDrawable;
++
++   if ((uFlags & __DRI2_FLUSH_DRAWABLE) != 0) {
++      PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++
++      psDRISUPDrawable = psPVRDrawable->psDRISUPDrawable;
++   } else {
++      psDRISUPDrawable = NULL;
++   }
++
++   DRISUPFlushWithFlags(psPVRContext->psDRISUPContext, psDRISUPDrawable,
++                        uFlags, (unsigned int) eThrottleReason);
++}
++
++static __DRI2flushExtension pvrDRI2FlushExtension = {
++   .base = {
++      .name = __DRI2_FLUSH,
++      .version = PVR_DRI2_FLUSH_VERSION,
++   },
++   .flush = PVRDRI2Flush,
++   .invalidate = PVRDRI2Invalidate,
++   .flush_with_flags = PVRDRI2FlushWithFlags,
++};
++
++static __DRIimage *
++PVRDRICreateImageFromName(__DRIscreen *psDRIScreen, int iWidth, int iHeight,
++                          int iFormat, int iName, int iPitch,
++                          void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   int iFourCC = PVRDRIFormatToFourCC(iFormat);
++
++   return DRISUPCreateImageFromName(psPVRScreen->psDRISUPScreen,
++                                    iWidth, iHeight, iFourCC, iName, iPitch,
++                                    pvLoaderPrivate);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromRenderbuffer(__DRIcontext *psDRIContext,
++                                  int iRenderBuffer, void *pvLoaderPrivate)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPCreateImageFromRenderbuffer(psPVRContext->psDRISUPContext,
++                                            iRenderBuffer, pvLoaderPrivate);
++}
++
++static void
++PVRDRIDestroyImage(__DRIimage *psImage)
++{
++   DRISUPDestroyImage(psImage);
++}
++
++static __DRIimage *
++PVRDRICreateImage(__DRIscreen *psDRIScreen, int iWidth, int iHeight,
++                  int iFormat, unsigned int uUse, void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   int iFourCC = PVRDRIFormatToFourCC(iFormat);
++
++   return DRISUPCreateImage(psPVRScreen->psDRISUPScreen, iWidth, iHeight,
++                            iFourCC, uUse, pvLoaderPrivate);
++}
++
++static GLboolean
++PVRDRIQueryImage(__DRIimage *psImage, int iAttrib, int *piValue)
++{
++   int iFourCC;
++
++   switch (iAttrib) {
++   case __DRI_IMAGE_ATTRIB_FORMAT:
++      if (DRISUPQueryImage(psImage,
++                           __DRI_IMAGE_ATTRIB_FOURCC, &iFourCC)) {
++         *piValue = PVRDRIFourCCToDRIFormat(iFourCC);
++         return GL_TRUE;
++      }
++      return GL_FALSE;
++   default:
++      return DRISUPQueryImage(psImage, iAttrib, piValue);
++   }
++
++}
++
++static __DRIimage *
++PVRDRIDupImage(__DRIimage *psImage, void *pvLoaderPrivate)
++{
++   return DRISUPDupImage(psImage, pvLoaderPrivate);
++}
++
++static GLboolean
++PVRDRIValidateImageUsage(__DRIimage *psImage, unsigned int uUse)
++{
++   return DRISUPValidateImageUsage(psImage, uUse);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromNames(__DRIscreen *psDRIScreen, int iWidth, int iHeight,
++                           int iFourCC, int *piNames, int iNumNames,
++                           int *piStrides, int *piOffsets,
++                           void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPCreateImageFromNames(psPVRScreen->psDRISUPScreen,
++                                     iWidth, iHeight, iFourCC,
++                                     piNames, iNumNames,
++                                     piStrides, piOffsets, pvLoaderPrivate);
++}
++
++static __DRIimage *
++PVRDRIFromPlanar(__DRIimage *psImage, int iPlane, void *pvLoaderPrivate)
++{
++   return DRISUPFromPlanar(psImage, iPlane, pvLoaderPrivate);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromTexture(__DRIcontext *psDRIContext, int iTarget,
++                             unsigned int uTexture, int iDepth, int iLevel,
++                             unsigned int *puError, void *pvLoaderPrivate)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++   int iEGLTarget;
++
++   switch (iTarget) {
++   case GL_TEXTURE_2D:
++      iEGLTarget = PVRDRI_GL_TEXTURE_2D;
++      break;
++   case GL_TEXTURE_3D:
++      iEGLTarget = PVRDRI_GL_TEXTURE_3D;
++      break;
++   case GL_TEXTURE_CUBE_MAP:
++      iEGLTarget = PVRDRI_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
++      break;
++   default:
++      errorMessage("%s: GL Target %d is not supported",
++                   __func__, iTarget);
++      *puError = __DRI_IMAGE_ERROR_BAD_PARAMETER;
++      return NULL;
++   }
++
++   return DRISUPCreateImageFromTexture(psPVRContext->psDRISUPContext,
++                                       iEGLTarget, uTexture, iDepth, iLevel,
++                                       puError, pvLoaderPrivate);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromFds(__DRIscreen *psDRIScreen, int iWidth, int iHeight,
++                         int iFourCC, int *piFDs, int iNumFDs,
++                         int *piStrides, int *piOffsets,
++                         void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPCreateImageFromFDs(psPVRScreen->psDRISUPScreen,
++                                   iWidth, iHeight, iFourCC, piFDs, iNumFDs,
++                                   piStrides, piOffsets, pvLoaderPrivate);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromDmaBufs(__DRIscreen *psDRIScreen,
++                             int iWidth, int iHeight, int iFourCC,
++                             int *piFDs, int iNumFDs,
++                             int *piStrides, int *piOffsets,
++                             enum __DRIYUVColorSpace eColorSpace,
++                             enum __DRISampleRange eSampleRange,
++                             enum __DRIChromaSiting eHorizSiting,
++                             enum __DRIChromaSiting eVertSiting,
++                             unsigned int *puError, void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPCreateImageFromDmaBufs(psPVRScreen->psDRISUPScreen,
++                                       iWidth, iHeight, iFourCC,
++                                       piFDs, iNumFDs, piStrides, piOffsets,
++                                       (unsigned int) eColorSpace,
++                                       (unsigned int) eSampleRange,
++                                       (unsigned int) eHorizSiting,
++                                       (unsigned int) eVertSiting,
++                                       puError, pvLoaderPrivate);
++}
++
++static void
++PVRDRIBlitImage(__DRIcontext *psDRIContext,
++                __DRIimage *psDst, __DRIimage *psSrc,
++                int iDstX0, int iDstY0, int iDstWidth, int iDstHeight,
++                int iSrcX0, int iSrcY0, int iSrcWidth, int iSrcHeight,
++                int iFlushFlag)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPBlitImage(psPVRContext->psDRISUPContext,
++                          psDst, psSrc,
++                          iDstX0, iDstY0, iDstWidth, iDstHeight,
++                          iSrcX0, iSrcY0, iSrcWidth, iSrcHeight,
++                          iFlushFlag);
++}
++
++static int
++PVRDRIGetCapabilities(__DRIscreen *psDRIScreen)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPGetImageCapabilities(psPVRScreen->psDRISUPScreen);
++}
++
++static void *
++PVRDRIMapImage(__DRIcontext *psDRIContext, __DRIimage *psImage,
++               int iX0, int iY0, int iWidth, int iHeight,
++               unsigned int iFlags, int *piStride, void **ppvData)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPMapImage(psPVRContext->psDRISUPContext, psImage,
++                         iX0, iY0, iWidth, iHeight, iFlags, piStride,
++                         ppvData);
++}
++
++static void
++PVRDRIUnmapImage(__DRIcontext *psDRIContext, __DRIimage *psImage,
++                 void *pvData)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPUnmapImage(psPVRContext->psDRISUPContext, psImage, pvData);
++}
++
++static __DRIimage *
++PVRDRICreateImageWithModifiers(__DRIscreen *psDRIScreen,
++                               int iWidth, int iHeight, int iFormat,
++                               const uint64_t *puModifiers,
++                               const unsigned int uModifierCount,
++                               void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   int iFourCC = PVRDRIFormatToFourCC(iFormat);
++
++   return DRISUPCreateImageWithModifiers(psPVRScreen->psDRISUPScreen,
++                                         iWidth, iHeight, iFourCC,
++                                         puModifiers, uModifierCount,
++                                         pvLoaderPrivate);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromDmaBufs2(__DRIscreen *psDRIScreen,
++                              int iWidth, int iHeight,
++                              int iFourCC, uint64_t uModifier,
++                              int *piFDs, int iNumFDs,
++                              int *piStrides, int *piOffsets,
++                              enum __DRIYUVColorSpace eColorSpace,
++                              enum __DRISampleRange eSampleRange,
++                              enum __DRIChromaSiting eHorizSiting,
++                              enum __DRIChromaSiting eVertSiting,
++                              unsigned int *puError, void *pvLoaderPrivate)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPCreateImageFromDMABufs2(psPVRScreen->psDRISUPScreen,
++                                        iWidth, iHeight, iFourCC, uModifier,
++                                        piFDs, iNumFDs, piStrides, piOffsets,
++                                        (unsigned int) eColorSpace,
++                                        (unsigned int) eSampleRange,
++                                        (unsigned int) eHorizSiting,
++                                        (unsigned int) eVertSiting,
++                                        puError, pvLoaderPrivate);
++}
++
++static GLboolean
++PVRDRIQueryDmaBufFormats(__DRIscreen *psDRIScreen, int iMax,
++                         int *piFormats, int *piCount)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPQueryDMABufFormats(psPVRScreen->psDRISUPScreen, iMax,
++                                   piFormats, piCount);
++}
++
++static GLboolean
++PVRDRIQueryDmaBufModifiers(__DRIscreen *psDRIScreen, int iFourCC, int iMax,
++                           uint64_t *puModifiers,
++                           unsigned int *puExternalOnly, int *piCount)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPQueryDMABufModifiers(psPVRScreen->psDRISUPScreen, iFourCC,
++                                     iMax,
++                                     puModifiers, puExternalOnly, piCount);
++}
++
++static GLboolean
++PVRDRIQueryDmaBufFormatModifierAttribs(__DRIscreen *psDRIScreen,
++                                       uint32_t uFourCC, uint64_t uModifier,
++                                       int iAttrib, uint64_t *puValue)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   struct DRISUPScreen *psDRISUPScreen = psPVRScreen->psDRISUPScreen;
++
++   return DRISUPQueryDMABufFormatModifierAttribs(psDRISUPScreen, uFourCC,
++                                                 uModifier, iAttrib, puValue);
++}
++
++static __DRIimage *
++PVRDRICreateImageFromRenderbuffer2(__DRIcontext *psDRIContext,
++                                   int iRenderBuffer, void *pvLoaderPrivate,
++                                   unsigned int *puError)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++   struct DRISUPContext *psDRISUPContext = psPVRContext->psDRISUPContext;
++
++   return DRISUPCreateImageFromRenderBuffer2(psDRISUPContext, iRenderBuffer,
++                                             pvLoaderPrivate, puError);
++}
++
++#if defined(EGL_IMG_cl_image)
++static __DRIimage *
++PVRDRICreateImageFromBuffer(__DRIcontext *psDRIContext, int iTarget,
++                            void *pvBuffer, unsigned int *puError,
++                            void *pvLoaderPrivate)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPCreateImageFromBuffer(psPVRContext->psDRISUPContext, iTarget,
++                                      pvBuffer, puError, pvLoaderPrivate);
++}
++#endif
++
++static __DRIimageExtension pvrDRIImage = {
++   .base = {
++      .name = __DRI_IMAGE,
++      .version = PVR_DRI_IMAGE_VERSION,
++   },
++   .createImageFromName = PVRDRICreateImageFromName,
++   .createImageFromRenderbuffer = PVRDRICreateImageFromRenderbuffer,
++   .destroyImage = PVRDRIDestroyImage,
++   .createImage = PVRDRICreateImage,
++   .queryImage = PVRDRIQueryImage,
++   .dupImage = PVRDRIDupImage,
++   .validateUsage = PVRDRIValidateImageUsage,
++   .createImageFromNames = PVRDRICreateImageFromNames,
++   .fromPlanar = PVRDRIFromPlanar,
++   .createImageFromTexture = PVRDRICreateImageFromTexture,
++   .createImageFromFds = PVRDRICreateImageFromFds,
++   .createImageFromDmaBufs = PVRDRICreateImageFromDmaBufs,
++   .blitImage = PVRDRIBlitImage,
++   .getCapabilities = PVRDRIGetCapabilities,
++   .mapImage = PVRDRIMapImage,
++   .unmapImage = PVRDRIUnmapImage,
++   .createImageWithModifiers = PVRDRICreateImageWithModifiers,
++   .createImageFromDmaBufs2 = PVRDRICreateImageFromDmaBufs2,
++   .queryDmaBufFormats = PVRDRIQueryDmaBufFormats,
++   .queryDmaBufModifiers = PVRDRIQueryDmaBufModifiers,
++   .queryDmaBufFormatModifierAttribs =
++   PVRDRIQueryDmaBufFormatModifierAttribs,
++   .createImageFromRenderbuffer2 = PVRDRICreateImageFromRenderbuffer2,
++#if defined(EGL_IMG_cl_image)
++   .createImageFromBuffer = PVRDRICreateImageFromBuffer,
++#endif
++};
++
++static __DRIrobustnessExtension pvrDRIRobustness = {
++   .base = {
++      .name = __DRI2_ROBUSTNESS,
++      .version = PVR_DRI2_ROBUSTNESS_VERSION,
++   }
++};
++
++static int
++PVRDRIQueryRendererInteger(__DRIscreen *psDRIScreen, int iAttribute,
++                           unsigned int *puValue)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++   int res;
++
++   res = DRISUPQueryRendererInteger(psPVRScreen->psDRISUPScreen,
++                                    iAttribute, puValue);
++   if (res == -1)
++      return driQueryRendererIntegerCommon(psDRIScreen, iAttribute, puValue);
++
++   return res;
++}
++
++static int
++PVRDRIQueryRendererString(__DRIscreen *psDRIScreen, int iAttribute,
++                          const char **ppszValue)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPQueryRendererString(psPVRScreen->psDRISUPScreen, iAttribute,
++                                    ppszValue);
++}
++
++static const __DRI2rendererQueryExtension pvrDRIRendererQueryExtension = {
++   .base = {
++      .name = __DRI2_RENDERER_QUERY,
++      .version = PVR_DRI2_RENDERER_QUERY_VERSION,
++   },
++   .queryInteger = PVRDRIQueryRendererInteger,
++   .queryString = PVRDRIQueryRendererString,
++};
++
++
++static void *
++PVRDRICreateFenceEXT(__DRIcontext *psDRIContext)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPCreateFence(psPVRContext->psDRISUPContext);
++}
++
++static void
++PVRDRIDestroyFenceEXT(__DRIscreen *psDRIScreen, void *pvFence)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPDestroyFence(psPVRScreen->psDRISUPScreen, pvFence);
++}
++
++static GLboolean
++PVRDRIClientWaitSyncEXT(__DRIcontext *psDRIContext, void *pvFence,
++                        unsigned int uFlags, uint64_t uTimeout)
++{
++   struct DRISUPContext *psDRISUPContext;
++
++   if (psDRIContext) {
++      PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++      psDRISUPContext = psPVRContext->psDRISUPContext;
++   } else {
++      psDRISUPContext = NULL;
++   }
++
++   return DRISUPClientWaitSync(psDRISUPContext, pvFence, uFlags, uTimeout);
++}
++
++static void
++PVRDRIServerWaitSyncEXT(__DRIcontext *psDRIContext, void *pvFence,
++                        unsigned int uFlags)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPServerWaitSync(psPVRContext->psDRISUPContext, pvFence, uFlags);
++}
++
++static unsigned int
++PVRDRIGetFenceCapabilitiesEXT(__DRIscreen *psDRIScreen)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPGetFenceCapabilities(psPVRScreen->psDRISUPScreen);
++}
++
++static void *
++PVRDRICreateFenceFdEXT(__DRIcontext *psDRIContext, int iFD)
++{
++   PVRDRIContext *psPVRContext = psDRIContext->driverPrivate;
++
++   return DRISUPCreateFenceFD(psPVRContext->psDRISUPContext, iFD);
++}
++
++static int
++PVRDRIGetFenceFdEXT(__DRIscreen *psDRIScreen, void *pvFence)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPGetFenceFD(psPVRScreen->psDRISUPScreen, pvFence);
++}
++
++static void *
++PVRDRIGetFenceFromClEventEXT(__DRIscreen *psDRIScreen, intptr_t iClEvent)
++{
++   PVRDRIScreen *psPVRScreen = psDRIScreen->driverPrivate;
++
++   return DRISUPGetFenceFromCLEvent(psPVRScreen->psDRISUPScreen, iClEvent);
++}
++
++__DRI2fenceExtension pvrDRIFenceExtension = {
++   .base = {
++      .name = __DRI2_FENCE,
++      .version = PVR_DRI2_FENCE_VERSION,
++   },
++   .create_fence = PVRDRICreateFenceEXT,
++   .get_fence_from_cl_event = PVRDRIGetFenceFromClEventEXT,
++   .destroy_fence = PVRDRIDestroyFenceEXT,
++   .client_wait_sync = PVRDRIClientWaitSyncEXT,
++   .server_wait_sync = PVRDRIServerWaitSyncEXT,
++   .get_capabilities = PVRDRIGetFenceCapabilitiesEXT,
++   .create_fence_fd = PVRDRICreateFenceFdEXT,
++   .get_fence_fd = PVRDRIGetFenceFdEXT,
++};
++
++static void
++PVRDRISetDamageRegion(__DRIdrawable *psDRIDrawable,
++                      unsigned int uNRects, int *piRects)
++{
++   PVRDRIDrawable *psPVRDrawable = psDRIDrawable->driverPrivate;
++
++   DRISUPSetDamageRegion(psPVRDrawable->psDRISUPDrawable, uNRects, piRects);
++}
++
++const __DRI2bufferDamageExtension pvrDRIbufferDamageExtension = {
++   .base = {
++      .name = __DRI2_BUFFER_DAMAGE,
++      .version = PVR_DRI2_BUFFER_DAMAGE_VERSION,
++   },
++   .set_damage_region = PVRDRISetDamageRegion,
++};
++
++/*
++ * Extension lists
++ *
++ * NOTE: When adding a new screen extension asScreenExtensionVersionInfo
++ *       should also be updated accordingly.
++ */
++static const __DRIextension *apsScreenExtensions[] = {
++   &pvrDRITexBufferExtension.base,
++   &pvrDRI2FlushExtension.base,
++   &pvrDRIImage.base,
++   &pvrDRIRobustness.base,
++   &pvrDRIRendererQueryExtension.base,
++   &pvrDRIFenceExtension.base,
++   &pvrDRIbufferDamageExtension.base,
++   &dri2ConfigQueryExtension.base,
++   NULL
++};
++
++static const __DRIextension asScreenExtensionVersionInfo[] = {
++   {.name = __DRI_TEX_BUFFER,.version = __DRI_TEX_BUFFER_VERSION},
++   {.name = __DRI2_FLUSH,.version = __DRI2_FLUSH_VERSION},
++   {.name = __DRI_IMAGE,.version = __DRI_IMAGE_VERSION},
++   {.name = __DRI2_ROBUSTNESS,.version = __DRI2_ROBUSTNESS_VERSION},
++   {.name = __DRI2_RENDERER_QUERY,.version = __DRI2_RENDERER_QUERY_VERSION},
++   {.name = __DRI2_FENCE,.version = __DRI2_FENCE_VERSION},
++   {.name = __DRI2_BUFFER_DAMAGE,.version = __DRI2_BUFFER_DAMAGE_VERSION},
++   {.name = __DRI2_CONFIG_QUERY,.version = __DRI2_CONFIG_QUERY_VERSION},
++   {.name = NULL,.version = 0},
++};
++
++const __DRIextension **
++PVRDRIScreenExtensions(void)
++{
++   return apsScreenExtensions;
++}
++
++const __DRIextension *
++PVRDRIScreenExtensionVersionInfo(void)
++{
++   return asScreenExtensionVersionInfo;
++}
++
++void
++PVRDRIAdjustExtensions(unsigned int uVersion, unsigned int uMinVersion)
++{
++   switch (uVersion) {
++   default:
++   case 4:
++      /* Is the KHR_cl_event2 EGL extension supported? */
++      if (!DRISUPHaveGetFenceFromCLEvent())
++         pvrDRIFenceExtension.get_fence_from_cl_event = NULL;
++
++      break;
++   case 3:
++      break;
++   case 2:
++   case 1:
++   case 0:
++      /* The KHR_cl_event2 EGL extension is not supported */
++      pvrDRIFenceExtension.get_fence_from_cl_event = NULL;
++      break;
++   }
++}
+diff --git a/src/mesa/drivers/dri/pvr/pvrmesa.h b/src/mesa/drivers/dri/pvr/pvrmesa.h
+new file mode 100644
+index 0000000..5e1c9c1
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrmesa.h
+@@ -0,0 +1,36 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#if !defined(__PVRMESA_H__)
++#define __PVRMESA_H__
++
++#include "pvrdri.h"
++
++void pvrdri_free_dispatch_tables(PVRDRIScreen *psPVRScreen);
++bool pvrdri_create_dispatch_table(PVRDRIScreen *psPVRScreen,
++                                  PVRDRIAPIType eAPI);
++void pvrdri_set_null_dispatch_table(void);
++void pvrdri_set_dispatch_table(PVRDRIContext *psPVRContext);
++
++#endif /* !defined(__PVRMESA_H__) */
+diff --git a/src/mesa/drivers/dri/pvr/pvrutil.c b/src/mesa/drivers/dri/pvr/pvrutil.c
+new file mode 100644
+index 0000000..945e18c
+--- /dev/null
++++ b/src/mesa/drivers/dri/pvr/pvrutil.c
+@@ -0,0 +1,239 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <assert.h>
++
++#include "drm-uapi/drm_fourcc.h"
++
++#include "utils.h"
++#include "dri_util.h"
++#include "pvrdri.h"
++
++#define MESSAGE_LENGTH_MAX 1024
++
++/*
++ * Define before including android/log.h and dlog.h as this is used by these
++ * headers.
++ */
++#define LOG_TAG "PVR-MESA"
++
++#if defined(HAVE_ANDROID_PLATFORM)
++#include <android/log.h>
++#define err_printf(f, args...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, f, ##args))
++#define dbg_printf(f, args...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, f, ##args))
++#elif defined(HAVE_TIZEN_PLATFORM)
++#include <dlog.h>
++#define err_printf(f, args...) LOGE(f, ##args)
++#define dbg_printf(f, args...) LOGD(f, ##args)
++#else
++#define err_printf(f, args...) fprintf(stderr, f "\n", ##args)
++#define dbg_printf(f, args...) fprintf(stderr, "LibGL: " f "\n", ##args)
++#endif /* HAVE_ANDROID_PLATFORM */
++
++/* Standard error message */
++void PRINTFLIKE(1, 2)
++errorMessage(const char *f, ...)
++{
++   char message[MESSAGE_LENGTH_MAX];
++   va_list args;
++
++   va_start(args, f);
++   vsnprintf(message, sizeof message, f, args);
++   va_end(args);
++
++   err_printf("%s", message);
++}
++
++void PRINTFLIKE(1, 2)
++__driUtilMessage(const char *f, ...)
++{
++   char message[MESSAGE_LENGTH_MAX];
++   va_list args;
++
++   /*
++    * On Android and Tizen, always print messages; otherwise, only print if
++    * the environment variable LIBGL_DEBUG=verbose.
++    */
++#if !defined(HAVE_ANDROID_PLATFORM) && !defined(HAVE_TIZEN_PLATFORM)
++   char *ev = getenv("LIBGL_DEBUG");
++
++   if (!ev || strcmp(ev, "verbose") != 0)
++      return;
++#endif
++
++   va_start(args, f);
++   vsnprintf(message, sizeof message, f, args);
++   va_end(args);
++
++   dbg_printf("%s", message);
++}
++
++mesa_format
++PVRDRIMesaFormatToMesaFormat(int pvrdri_mesa_format)
++{
++   switch (pvrdri_mesa_format) {
++   case PVRDRI_MESA_FORMAT_NONE:
++      return MESA_FORMAT_NONE;
++   case PVRDRI_MESA_FORMAT_B8G8R8A8_UNORM:
++      return MESA_FORMAT_B8G8R8A8_UNORM;
++   case PVRDRI_MESA_FORMAT_B8G8R8X8_UNORM:
++      return MESA_FORMAT_B8G8R8X8_UNORM;
++   case PVRDRI_MESA_FORMAT_B5G6R5_UNORM:
++      return MESA_FORMAT_B5G6R5_UNORM;
++   case PVRDRI_MESA_FORMAT_R8G8B8A8_UNORM:
++      return MESA_FORMAT_R8G8B8A8_UNORM;
++   case PVRDRI_MESA_FORMAT_R8G8B8X8_UNORM:
++      return MESA_FORMAT_R8G8B8X8_UNORM;
++   case PVRDRI_MESA_FORMAT_YCBCR:
++      return MESA_FORMAT_YCBCR;
++   case PVRDRI_MESA_FORMAT_YUV420_2PLANE:
++      return MESA_FORMAT_YUV420_2PLANE;
++   case PVRDRI_MESA_FORMAT_YVU420_2PLANE:
++      return MESA_FORMAT_YVU420_2PLANE;
++   case PVRDRI_MESA_FORMAT_B8G8R8A8_SRGB:
++      return MESA_FORMAT_B8G8R8A8_SRGB;
++   case PVRDRI_MESA_FORMAT_R8G8B8A8_SRGB:
++      return MESA_FORMAT_R8G8B8A8_SRGB;
++   default:
++      __driUtilMessage("%s: Unknown format: %d", __func__, pvrdri_mesa_format);
++      break;
++   }
++
++   return MESA_FORMAT_NONE;
++}
++
++int
++PVRDRIFormatToFourCC(int dri_format)
++{
++   switch (dri_format) {
++   case __DRI_IMAGE_FORMAT_RGB565:
++      return DRM_FORMAT_RGB565;
++   case __DRI_IMAGE_FORMAT_XRGB8888:
++      return DRM_FORMAT_XRGB8888;
++   case __DRI_IMAGE_FORMAT_ARGB8888:
++      return DRM_FORMAT_ARGB8888;
++   case __DRI_IMAGE_FORMAT_ABGR8888:
++      return DRM_FORMAT_ABGR8888;
++   case __DRI_IMAGE_FORMAT_XBGR8888:
++      return DRM_FORMAT_XBGR8888;
++   case __DRI_IMAGE_FORMAT_R8:
++      return DRM_FORMAT_R8;
++   case __DRI_IMAGE_FORMAT_GR88:
++      return DRM_FORMAT_GR88;
++   case __DRI_IMAGE_FORMAT_NONE:
++      return 0;
++   case __DRI_IMAGE_FORMAT_XRGB2101010:
++      return DRM_FORMAT_XRGB2101010;
++   case __DRI_IMAGE_FORMAT_ARGB2101010:
++      return DRM_FORMAT_ARGB2101010;
++   case __DRI_IMAGE_FORMAT_SARGB8:
++      return __DRI_IMAGE_FOURCC_SARGB8888;
++   case __DRI_IMAGE_FORMAT_ARGB1555:
++      return DRM_FORMAT_ARGB1555;
++   case __DRI_IMAGE_FORMAT_R16:
++      return DRM_FORMAT_R16;
++   case __DRI_IMAGE_FORMAT_GR1616:
++      return DRM_FORMAT_GR1616;
++   case __DRI_IMAGE_FORMAT_YUYV:
++      return DRM_FORMAT_YUYV;
++   case __DRI_IMAGE_FORMAT_XBGR2101010:
++      return DRM_FORMAT_XBGR2101010;
++   case __DRI_IMAGE_FORMAT_ABGR2101010:
++      return DRM_FORMAT_ABGR2101010;
++   case __DRI_IMAGE_FORMAT_SABGR8:
++      return __DRI_IMAGE_FOURCC_SABGR8888;
++   case __DRI_IMAGE_FORMAT_UYVY:
++      return DRM_FORMAT_UYVY;
++   case __DRI_IMAGE_FORMAT_ARGB4444:
++      return DRM_FORMAT_ARGB4444;
++   case __DRI_IMAGE_FORMAT_YVU444_PACK10_IMG:
++      return DRM_FORMAT_YVU444_PACK10_IMG;
++   case __DRI_IMAGE_FORMAT_BGR888:
++      return DRM_FORMAT_BGR888;
++   default:
++      __driUtilMessage("%s: Unknown format: %d", __func__, dri_format);
++      break;
++   }
++
++   return 0;
++}
++
++int
++PVRDRIFourCCToDRIFormat(int iFourCC)
++{
++   switch (iFourCC) {
++   case 0:
++      return __DRI_IMAGE_FORMAT_NONE;
++   case DRM_FORMAT_RGB565:
++      return __DRI_IMAGE_FORMAT_RGB565;
++   case DRM_FORMAT_XRGB8888:
++      return __DRI_IMAGE_FORMAT_XRGB8888;
++   case DRM_FORMAT_ARGB8888:
++      return __DRI_IMAGE_FORMAT_ARGB8888;
++   case DRM_FORMAT_ABGR8888:
++      return __DRI_IMAGE_FORMAT_ABGR8888;
++   case DRM_FORMAT_XBGR8888:
++      return __DRI_IMAGE_FORMAT_XBGR8888;
++   case DRM_FORMAT_R8:
++      return __DRI_IMAGE_FORMAT_R8;
++   case DRM_FORMAT_GR88:
++      return __DRI_IMAGE_FORMAT_GR88;
++   case DRM_FORMAT_XRGB2101010:
++      return __DRI_IMAGE_FORMAT_XRGB2101010;
++   case DRM_FORMAT_ARGB2101010:
++      return __DRI_IMAGE_FORMAT_ARGB2101010;
++   case __DRI_IMAGE_FOURCC_SARGB8888:
++      return __DRI_IMAGE_FORMAT_SARGB8;
++   case DRM_FORMAT_ARGB1555:
++      return __DRI_IMAGE_FORMAT_ARGB1555;
++   case DRM_FORMAT_R16:
++      return __DRI_IMAGE_FORMAT_R16;
++   case DRM_FORMAT_GR1616:
++      return __DRI_IMAGE_FORMAT_GR1616;
++   case DRM_FORMAT_YUYV:
++      return __DRI_IMAGE_FORMAT_YUYV;
++   case DRM_FORMAT_XBGR2101010:
++      return __DRI_IMAGE_FORMAT_XBGR2101010;
++   case DRM_FORMAT_ABGR2101010:
++      return __DRI_IMAGE_FORMAT_ABGR2101010;
++   case __DRI_IMAGE_FOURCC_SABGR8888:
++      return __DRI_IMAGE_FORMAT_SABGR8;
++   case DRM_FORMAT_UYVY:
++      return __DRI_IMAGE_FORMAT_UYVY;
++   case DRM_FORMAT_ARGB4444:
++      return __DRI_IMAGE_FORMAT_ARGB4444;
++   case DRM_FORMAT_YVU444_PACK10_IMG:
++      return __DRI_IMAGE_FORMAT_YVU444_PACK10_IMG;
++   case DRM_FORMAT_BGR888:
++      return __DRI_IMAGE_FORMAT_BGR888;
++   default:
++      __driUtilMessage("%s: Unknown format: %d", __func__, iFourCC);
++      break;
++   }
++
++   return 0;
++}
+diff --git a/src/meson.build b/src/meson.build
+index acc7fe9..c3bab27 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -25,6 +25,7 @@ inc_gallium = include_directories('gallium/include')
+ inc_gallium_aux = include_directories('gallium/auxiliary')
+ inc_amd_common = include_directories('amd/common')
+ inc_amd_common_llvm = include_directories('amd/llvm')
++inc_pvr = include_directories('mesa/main', 'mapi/glapi')
+ inc_tool = include_directories('tool')
+ pps_datasources = []
+ pps_includes = []

+ 32 - 0
recipes-graphics/mesa/mesa-pvr/0002-Force-Mesa-to-use-the-PVR-driver-for-platform-device.patch

@@ -0,0 +1,32 @@
+From 15a2b80c0cd5cff126661d48fe7236c903d281c9 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Wed, 17 Jun 2015 15:37:18 +0100
+Subject: [PATCH] Force Mesa to use the PVR driver for platform devices
+
+For platform devices, Mesa uses the DRM driver name to determine which DRI
+driver to load. This doesn't work in the multi-DRM driver model where there
+are separate DRM drivers for the display and GPU. This is because we normally
+want Mesa to deal with the display driver and the name of this won't match
+that of the DRI driver. For the time being, take the easy approach and just
+force it to use the PVR DRI driver.
+
+---
+ src/loader/loader.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/loader/loader.c b/src/loader/loader.c
+index 08eeb61..c7e561e 100644
+--- a/src/loader/loader.c
++++ b/src/loader/loader.c
+@@ -436,7 +436,11 @@ loader_get_pci_driver(int fd)
+    char *driver = NULL;
+ 
+    if (!loader_get_pci_id_for_fd(fd, &vendor_id, &chip_id))
++#if 1
++      return strdup("pvr");
++#else
+       return NULL;
++#endif
+ 
+    for (i = 0; i < ARRAY_SIZE(driver_map); i++) {
+       if (vendor_id != driver_map[i].vendor_id)

+ 78 - 0
recipes-graphics/mesa/mesa-pvr/0003-dri-Add-some-new-DRI-formats-and-fourccs.patch

@@ -0,0 +1,78 @@
+From 31cc2cd723963bdecb2c5d5e4c09598ee830f0d6 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Thu, 5 Jun 2014 12:07:01 +0100
+Subject: [PATCH] dri: Add some new DRI formats and fourccs
+
+Add ARGB4444 DRI format and fourcc.
+Add YVU444_PACK10_IMG DRI format and fourcc.
+Add BGR888 DRI format and fourcc.
+
+---
+ include/GL/internal/dri_interface.h    | 4 ++++
+ include/drm-uapi/drm_fourcc.h          | 1 +
+ src/egl/drivers/dri2/egl_dri2.c        | 1 +
+ src/mesa/drivers/dri/common/dri_util.c | 5 +++++
+ 4 files changed, 11 insertions(+)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 66dc092..5102894 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1367,6 +1367,9 @@ struct __DRIdri2ExtensionRec {
+ #define __DRI_IMAGE_FORMAT_ABGR16161616F 0x1015
+ #define __DRI_IMAGE_FORMAT_SXRGB8       0x1016
+ #define __DRI_IMAGE_FORMAT_ABGR16161616 0x1017
++#define __DRI_IMAGE_FORMAT_ARGB4444     0x1018
++#define __DRI_IMAGE_FORMAT_YVU444_PACK10_IMG 0x1019
++#define __DRI_IMAGE_FORMAT_BGR888       0x101a
+ 
+ #define __DRI_IMAGE_USE_SHARE		0x0001
+ #define __DRI_IMAGE_USE_SCANOUT		0x0002
+@@ -1397,6 +1400,7 @@ struct __DRIdri2ExtensionRec {
+ #define __DRI_IMAGE_FOURCC_SABGR8888	0x84324258
+ #define __DRI_IMAGE_FOURCC_SXRGB8888	0x85324258
+ #define __DRI_IMAGE_FOURCC_RGBA16161616 0x38344152  /* fourcc_code('R', 'A', '4', '8' ) */
++#define __DRI_IMAGE_FOURCC_SBGR888	0xff324742
+ 
+ /**
+  * Queryable on images created by createImageFromNames.
+diff --git a/include/drm-uapi/drm_fourcc.h b/include/drm-uapi/drm_fourcc.h
+index cd3ce8a..5765759 100644
+--- a/include/drm-uapi/drm_fourcc.h
++++ b/include/drm-uapi/drm_fourcc.h
+@@ -344,6 +344,7 @@ extern "C" {
+ #define DRM_FORMAT_YUV444	fourcc_code('Y', 'U', '2', '4') /* non-subsampled Cb (1) and Cr (2) planes */
+ #define DRM_FORMAT_YVU444	fourcc_code('Y', 'V', '2', '4') /* non-subsampled Cr (1) and Cb (2) planes */
+ 
++#define DRM_FORMAT_YVU444_PACK10_IMG fourcc_code('I', 'M', 'G', '2')
+ 
+ /*
+  * Format Modifiers:
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index 9e5a55a..eee28eb 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -2652,6 +2652,7 @@ dri2_num_fourcc_format_planes(EGLint format)
+    case DRM_FORMAT_Y410:
+    case DRM_FORMAT_Y412:
+    case DRM_FORMAT_Y416:
++   case DRM_FORMAT_YVU444_PACK10_IMG:
+       return 1;
+ 
+    case DRM_FORMAT_NV12:
+diff --git a/src/mesa/drivers/dri/common/dri_util.c b/src/mesa/drivers/dri/common/dri_util.c
+index e2a1124..caed5fa 100644
+--- a/src/mesa/drivers/dri/common/dri_util.c
++++ b/src/mesa/drivers/dri/common/dri_util.c
+@@ -904,6 +904,11 @@ static const struct {
+       .mesa_format     =        MESA_FORMAT_B5G5R5A1_UNORM,
+       .internal_format =        GL_RGB5_A1,
+    },
++   {
++      .image_format    = __DRI_IMAGE_FORMAT_ARGB4444,
++      .mesa_format     =        MESA_FORMAT_B4G4R4A4_UNORM,
++      .internal_format =        GL_RGBA4,
++   },
+    {
+       .image_format    = __DRI_IMAGE_FORMAT_XRGB8888,
+       .mesa_format     =        MESA_FORMAT_B8G8R8X8_UNORM,

+ 101 - 0
recipes-graphics/mesa/mesa-pvr/0004-GL_EXT_sparse_texture-entry-points.patch

@@ -0,0 +1,101 @@
+From 3aac60c44489cbb295442cb8e87396987f84d4b5 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Wed, 12 Aug 2015 09:11:51 +0100
+Subject: [PATCH] GL_EXT_sparse_texture entry points
+
+---
+ src/mapi/glapi/gen/EXT_sparse_texture.xml | 56 +++++++++++++++++++++++
+ src/mapi/glapi/gen/es_EXT.xml             |  3 ++
+ src/mapi/glapi/gen/static_data.py         |  2 +
+ 3 files changed, 61 insertions(+)
+ create mode 100644 src/mapi/glapi/gen/EXT_sparse_texture.xml
+
+diff --git a/src/mapi/glapi/gen/EXT_sparse_texture.xml b/src/mapi/glapi/gen/EXT_sparse_texture.xml
+new file mode 100644
+index 0000000..48d03e7
+--- /dev/null
++++ b/src/mapi/glapi/gen/EXT_sparse_texture.xml
+@@ -0,0 +1,56 @@
++<?xml version="1.0"?>
++<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd">
++
++<OpenGLAPI>
++
++<category name="EXT_sparse_texture" number="240">
++
++    <enum name="TEXTURE_SPARSE_EXT"                         value="0x91A6"/>
++    <enum name="VIRTUAL_PAGE_SIZE_INDEX_EXT"                value="0x91A7"/>
++
++    <enum name="NUM_SPARSE_LEVELS_EXT"                      value="0x91AA"/>
++
++    <enum name="NUM_VIRTUAL_PAGE_SIZES_EXT"                 value="0x91A8"/>
++
++    <enum name="VIRTUAL_PAGE_SIZE_X_EXT"                    value="0x9195"/>
++    <enum name="VIRTUAL_PAGE_SIZE_Y_EXT"                    value="0x9196"/>
++    <enum name="VIRTUAL_PAGE_SIZE_Z_EXT"                    value="0x9197"/>
++
++    <enum name="TEXTURE_2D"                                 value="0x0DE1"/>
++    <enum name="TEXTURE_2D_ARRAY"                           value="0x8C1A"/>
++    <enum name="TEXTURE_CUBE_MAP"                           value="0x8513"/>
++    <enum name="TEXTURE_CUBE_MAP_ARRAY_OES"                 value="0x9009"/>
++    <enum name="TEXTURE_3D"                                 value="0x806F"/>
++
++    <enum name="MAX_SPARSE_TEXTURE_SIZE_EXT"                value="0x9198"/>
++    <enum name="MAX_SPARSE_3D_TEXTURE_SIZE_EXT"             value="0x9199"/>
++    <enum name="MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT"        value="0x919A"/>
++    <enum name="SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT" value="0x91A9"/>
++
++    <function name="TexPageCommitmentEXT" es2="3.1" exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="level" type="GLint"/>
++        <param name="xoffset" type="GLint"/>
++        <param name="yoffset" type="GLint"/>
++        <param name="zoffset" type="GLint"/>
++        <param name="width" type="GLsizei"/>
++        <param name="height" type="GLsizei"/>
++        <param name="depth" type="GLsizei"/>
++        <param name="commit" type="GLboolean"/>
++    </function>
++
++    <function name="TexturePageCommitmentEXT" es2="3.1" exec="dynamic">
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++        <param name="xoffset" type="GLint"/>
++        <param name="yoffset" type="GLint"/>
++        <param name="zoffset" type="GLint"/>
++        <param name="width" type="GLsizei"/>
++        <param name="height" type="GLsizei"/>
++        <param name="depth" type="GLsizei"/>
++        <param name="commit" type="GLboolean"/>
++    </function>
++
++</category>
++
++</OpenGLAPI>
+diff --git a/src/mapi/glapi/gen/es_EXT.xml b/src/mapi/glapi/gen/es_EXT.xml
+index 929b40b..962170b 100644
+--- a/src/mapi/glapi/gen/es_EXT.xml
++++ b/src/mapi/glapi/gen/es_EXT.xml
+@@ -1433,6 +1433,9 @@
+ 
+ </category>
+ 
++<!-- 240. EXT_sparse_texture -->
++<xi:include href="EXT_sparse_texture.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
++
+ <category name="GL_OES_viewport_array" number="267">
+     <function name="ViewportArrayvOES" es2="3.1" alias="ViewportArrayv">
+         <param name="first" type="GLuint"/>
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index 6accff1..d063930 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1689,6 +1689,8 @@ offsets = {
+     "VertexAttribs2hvNV": 1653,
+     "VertexAttribs3hvNV": 1654,
+     "VertexAttribs4hvNV": 1655,
++    "TexPageCommitmentEXT": 1656,
++    "TexturePageCommitmentEXT" : 1657,
+ }
+ 
+ functions = [

+ 104 - 0
recipes-graphics/mesa/mesa-pvr/0005-Add-support-for-various-GLES-extensions.patch

@@ -0,0 +1,104 @@
+From 804b1621754ab265af92c796e04da4d024fb22a5 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 10 Mar 2014 12:27:03 +0000
+Subject: [PATCH] Add support for various GLES extensions
+
+Add support for:
+	EXT_occlusion_query_boolean
+	IMG_multisampled_render_to_texture
+	OES_matrix_palette
+
+---
+ src/mapi/glapi/gen/es_EXT.xml     | 41 ++++++++++++++++++++++++-------
+ src/mapi/glapi/gen/static_data.py |  6 +++++
+ 2 files changed, 38 insertions(+), 9 deletions(-)
+
+diff --git a/src/mapi/glapi/gen/es_EXT.xml b/src/mapi/glapi/gen/es_EXT.xml
+index 962170b..e586b0e 100644
+--- a/src/mapi/glapi/gen/es_EXT.xml
++++ b/src/mapi/glapi/gen/es_EXT.xml
+@@ -285,28 +285,25 @@
+     <enum name="WEIGHT_ARRAY_BUFFER_BINDING_OES"          value="0x889E"/>
+     <enum name="MATRIX_INDEX_ARRAY_BUFFER_BINDING_OES"    value="0x8B9E"/>
+ 
+-    <function name="CurrentPaletteMatrixOES" alias="CurrentPaletteMatrixARB"
+-	      exec="skip">
++    <function name="CurrentPaletteMatrixOES" es1="1.0" exec="dynamic">
+         <param name="matrixpaletteindex" type="GLuint"/>
+     </function>
+ 
+-    <!-- no offset -->
+-    <function name="LoadPaletteFromModelViewMatrixOES" exec="skip">
++    <function name="LoadPaletteFromModelViewMatrixOES" es1="1.0" exec="dynamic">
+     </function>
+ 
+-    <function name="MatrixIndexPointerOES" alias="MatrixIndexPointerARB"
+-	      exec="skip">
++    <function name="MatrixIndexPointerOES" es1="1.0" exec="dynamic">
+         <param name="size" type="GLint"/>
+         <param name="type" type="GLenum"/>
+         <param name="stride" type="GLsizei"/>
+-        <param name="pointer" type="const GLvoid *"/>
++        <param name="pointer" type="GLvoid *"/>
+     </function>
+ 
+-    <function name="WeightPointerOES" alias="WeightPointerARB" exec="skip">
++    <function name="WeightPointerOES" es1="1.0" exec="dynamic">
+         <param name="size" type="GLint"/>
+         <param name="type" type="GLenum"/>
+         <param name="stride" type="GLsizei"/>
+-        <param name="pointer" type="const GLvoid *"/>
++        <param name="pointer" type="GLvoid *"/>
+     </function>
+ </category>
+ 
+@@ -680,6 +677,32 @@
+     </enum>
+ </category>
+ 
++<!-- 74. GL_IMG_multisampled_render_to_texture -->
++<category name="GL_IMG_multisampled_render_to_texture" number="74">
++    <enum name="RENDERBUFFER_SAMPLES_IMG"                   value="0x9133"/>
++    <enum name="FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG"     value="0x9134"/>
++    <enum name="MAX_SAMPLES_IMG"                            value="0x9135"/>
++    <enum name="TEXTURE_SAMPLES_IMG"                        value="0x9136"/>
++
++    <function name="RenderbufferStorageMultisampleIMG" es2="2.0" exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="samples" type="GLsizei"/>
++        <param name="internalformat" type="GLenum"/>
++        <param name="width" type="GLsizei"/>
++        <param name="height" type="GLsizei"/>
++    </function>
++
++    <function name="FramebufferTexture2DMultisampleIMG" es2="2.0"
++              exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment" type="GLenum"/>
++        <param name="textarget" type="GLenum"/>
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++        <param name="samples" type="GLsizei"/>
++    </function>
++</category>
++
+ <!-- 87. GL_OES_EGL_image_external -->
+ <category name="GL_OES_EGL_image_external" number="87">
+     <enum name="TEXTURE_EXTERNAL_OES"                     value="0x8D65"/>
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index d063930..79b8f19 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1691,6 +1691,12 @@ offsets = {
+     "VertexAttribs4hvNV": 1655,
+     "TexPageCommitmentEXT": 1656,
+     "TexturePageCommitmentEXT" : 1657,
++    "CurrentPaletteMatrixOES" : 1658,
++    "LoadPaletteFromModelViewMatrixOES" : 1659,
++    "MatrixIndexPointerOES" : 1660,
++    "WeightPointerOES" : 1661,
++    "RenderbufferStorageMultisampleIMG" : 1662,
++    "FramebufferTexture2DMultisampleIMG" : 1663,
+ }
+ 
+ functions = [

+ 185 - 0
recipes-graphics/mesa/mesa-pvr/0006-Add-EGL_IMG_cl_image-extension.patch

@@ -0,0 +1,185 @@
+From 6d9dc4ffa940f9bde48fc68c85b54ab1b839ea49 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Tue, 11 Mar 2014 11:50:53 +0000
+Subject: [PATCH] Add EGL_IMG_cl_image extension
+
+Add support for the experimental EGL_IMG_cl_image extension to EGL, and
+the DRI2 EGL driver.
+
+---
+ include/EGL/eglmesaext.h            |  5 +++
+ include/GL/internal/dri_interface.h | 13 +++++++
+ src/egl/drivers/dri2/egl_dri2.c     | 60 +++++++++++++++++++++++++----
+ src/egl/main/eglapi.c               |  1 +
+ src/egl/main/egldisplay.h           |  2 +
+ 5 files changed, 73 insertions(+), 8 deletions(-)
+
+diff --git a/include/EGL/eglmesaext.h b/include/EGL/eglmesaext.h
+index f0395a8..5d11f3e 100644
+--- a/include/EGL/eglmesaext.h
++++ b/include/EGL/eglmesaext.h
+@@ -49,6 +49,11 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSREGIONNOK) (EGLDisplay dpy, EG
+ #define EGL_DRM_BUFFER_FORMAT_RGB565_MESA       0x3292
+ #endif /* EGL_MESA_drm_image_formats */
+ 
++#ifndef EGL_IMG_cl_image
++#define EGL_IMG_cl_image 1
++#define EGL_CL_IMAGE_IMG			0x6010
++#endif /* Experimental eglCreateImageKHR target */
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 5102894..b197092 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1831,6 +1831,19 @@ struct __DRIimageExtensionRec {
+                                             const unsigned int modifier_count,
+                                             unsigned int use,
+                                             void *loaderPrivate);
++
++   /**
++    * Support for experimental EGL_CL_IMAGE_IMG.
++    * Like createImageFromTexture, but from a buffer, the contents
++    * of which depend on the target.
++    *
++    * \since 8
++    */
++   __DRIimage *(*createImageFromBuffer)(__DRIcontext *context,
++                                        int target,
++                                        void *buffer,
++                                        unsigned *error,
++                                        void *loaderPrivate);
+ };
+ 
+ 
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index eee28eb..dea5899 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -995,6 +995,10 @@ dri2_setup_screen(_EGLDisplay *disp)
+          disp->Extensions.EXT_image_dma_buf_import_modifiers = EGL_TRUE;
+       }
+ #endif
++      if (dri2_dpy->image->base.version >= 8 &&
++          dri2_dpy->image->createImageFromBuffer) {
++         disp->Extensions.IMG_cl_image = EGL_TRUE;
++      }
+    }
+ 
+    if (dri2_dpy->flush_control)
+@@ -2360,17 +2364,13 @@ dri2_get_sync_values_chromium(_EGLDisplay *disp, _EGLSurface *surf,
+    return dri2_dpy->vtbl->get_sync_values(disp, surf, ust, msc, sbc);
+ }
+ 
+-/**
+- * Set the error code after a call to
+- * dri2_egl_image::dri_image::createImageFromTexture.
+- */
+ static void
+-dri2_create_image_khr_texture_error(int dri_error)
++dri2_create_image_khr_error(int dri_error)
+ {
+    EGLint egl_error = egl_error_from_dri_image_error(dri_error);
+ 
+    if (egl_error != EGL_SUCCESS)
+-      _eglError(egl_error, "dri2_create_image_khr_texture");
++      _eglError(egl_error, "dri2_create_image_khr");
+ }
+ 
+ static _EGLImage *
+@@ -2449,7 +2449,49 @@ dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx,
+                                               attrs.GLTextureLevel,
+                                               &error,
+                                               NULL);
+-   dri2_create_image_khr_texture_error(error);
++   dri2_create_image_khr_error(error);
++
++   if (!dri2_img->dri_image) {
++      free(dri2_img);
++      return EGL_NO_IMAGE_KHR;
++   }
++   return &dri2_img->base;
++}
++
++static _EGLImage *
++dri2_create_image_img_buffer(_EGLDisplay *disp, _EGLContext *ctx,
++				   EGLenum target,
++				   EGLClientBuffer buffer,
++				   const EGLint *attr_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
++   struct dri2_egl_image *dri2_img;
++   unsigned error;
++
++   switch (target) {
++   case EGL_CL_IMAGE_IMG:
++      break;
++   default:
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr");
++      return EGL_NO_IMAGE_KHR;
++   }
++
++   dri2_img = malloc(sizeof *dri2_img);
++   if (!dri2_img) {
++      _eglError(EGL_BAD_ALLOC, "dri2_create_image_khr");
++      return EGL_NO_IMAGE_KHR;
++   }
++
++   _eglInitImage(&dri2_img->base, disp);
++
++   dri2_img->dri_image =
++      dri2_dpy->image->createImageFromBuffer(dri2_ctx->dri_context,
++                                              target,
++                                              buffer,
++                                              &error,
++                                              NULL);
++   dri2_create_image_khr_error(error);
+ 
+    if (!dri2_img->dri_image) {
+       free(dri2_img);
+@@ -2916,7 +2958,7 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
+             &error,
+             NULL);
+    }
+-   dri2_create_image_khr_texture_error(error);
++   dri2_create_image_khr_error(error);
+ 
+    if (!dri_image)
+       return EGL_NO_IMAGE_KHR;
+@@ -3152,6 +3194,8 @@ dri2_create_image_khr(_EGLDisplay *disp, _EGLContext *ctx, EGLenum target,
+    case EGL_WAYLAND_BUFFER_WL:
+       return dri2_create_image_wayland_wl_buffer(disp, ctx, buffer, attr_list);
+ #endif
++   case EGL_CL_IMAGE_IMG:
++      return dri2_create_image_img_buffer(disp, ctx, target, buffer, attr_list);
+    default:
+       _eglError(EGL_BAD_PARAMETER, "dri2_create_image_khr");
+       return EGL_NO_IMAGE_KHR;
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 17e36af..6ffcff8 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -549,6 +549,7 @@ _eglCreateExtensionsString(_EGLDisplay *disp)
+    _EGL_CHECK_EXTENSION(WL_bind_wayland_display);
+    _EGL_CHECK_EXTENSION(WL_create_wayland_buffer_from_image);
+ 
++   _EGL_CHECK_EXTENSION(IMG_cl_image);
+ #undef _EGL_CHECK_EXTENSION
+ }
+ 
+diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
+index 4d2afbc..5b05dcc 100644
+--- a/src/egl/main/egldisplay.h
++++ b/src/egl/main/egldisplay.h
+@@ -150,6 +150,8 @@ struct _egl_extensions
+ 
+    EGLBoolean WL_bind_wayland_display;
+    EGLBoolean WL_create_wayland_buffer_from_image;
++
++   EGLBoolean IMG_cl_image;
+ };
+ 
+ struct _egl_display

+ 49 - 0
recipes-graphics/mesa/mesa-pvr/0007-egl-Be-stricter-when-making-a-context-current-withou.patch

@@ -0,0 +1,49 @@
+From d2badaa4a36fdd2193faf40df67b3595f871ffd7 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Wed, 18 Jun 2014 17:10:28 +0100
+Subject: [PATCH] egl: Be stricter when making a context current without any
+ surfaces
+
+The EGL_KHR_surfaceless_context extension spec states for eglMakeCurrent:
+    "If <ctx> does not support being bound without read and draw
+    surfaces, and both <draw> and <read> are EGL_NO_SURFACE, an
+    EGL_BAD_MATCH error is generated."
+
+Only OpenGLES contexts support this, via the GL_OES_surfaceless_context,
+so if EGL_KHR_surfaceless_context is supported and the context isn't an
+OpenGLES context then set the EGL error to EGL_BAD_MATCH.
+
+NOTE: This patch can't be upstreamed as is because we set the error to
+EGL_BAD_MATCH if we have an OpenGLES 1.x context but the
+GL_OES_surfaceless_context extension spec says:
+    "This extension is written against the OpenGL ES 2.0 Specification
+    but can apply to OpenGL ES 1.1 with the GL_OES_framebuffer_object
+    extension."
+
+All Mesa OpenGLES drivers support GL_OES_framebuffer_object, but we
+don't, so there would never be a reason to check whether or not this
+extension is supported.
+
+In order to upstream this patch the check would need to be changed
+to set the error to EGL_BAD_MATCH if EGL_KHR_surfaceless_context is
+supported and the context API isn't OpenGLES or the OpenGLES version
+is 1.0.
+
+---
+ src/egl/main/eglapi.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 6ffcff8..3cd69b9 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -888,6 +888,9 @@ eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read,
+          RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE);
+       if (draw_surf || read_surf)
+          RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_FALSE);
++      if (disp->Extensions.KHR_surfaceless_context && context &&
++          (context->ClientAPI != EGL_OPENGL_ES_API || context->ClientMajorVersion == 1))
++         RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_FALSE);
+    }
+ 
+    /*    If a native window underlying either draw or read is no longer valid,

+ 44 - 0
recipes-graphics/mesa/mesa-pvr/0008-egl-optimise-eglMakeCurrent-for-the-case-where-nothi.patch

@@ -0,0 +1,44 @@
+From 9e3b9f4dfa273c5d0536c748153f65c0899bf3b8 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Tue, 15 Sep 2015 14:15:31 +0100
+Subject: [PATCH] egl: optimise eglMakeCurrent for the case where nothing has
+ changed
+
+When an application calls eglMakeCurrent with a context, draw surface and
+read surface that match those that are currently bound to the calling
+thread don't perform a flush as this is an expensive operation.
+
+---
+ src/egl/main/eglapi.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 3cd69b9..234449a 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -853,6 +853,7 @@ eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read,
+                EGLContext ctx)
+ {
+    _EGLDisplay *disp = _eglLockDisplay(dpy);
++   _EGLContext *current_context = _eglGetCurrentContext();
+    _EGLContext *context = _eglLookupContext(ctx, disp);
+    _EGLSurface *draw_surf = _eglLookupSurface(draw, disp);
+    _EGLSurface *read_surf = _eglLookupSurface(read, disp);
+@@ -909,7 +910,16 @@ eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read,
+        draw_surf && !draw_surf->ProtectedContent)
+       RETURN_EGL_ERROR(disp, EGL_BAD_ACCESS, EGL_FALSE);
+ 
+-   ret = disp->Driver->MakeCurrent(disp, draw_surf, read_surf, context);
++   /* As an optimisation don't do anything unless something has changed */
++   if (context != current_context ||
++       (current_context &&
++        (draw_surf != current_context->DrawSurface ||
++         read_surf != current_context->ReadSurface)) ||
++       (!current_context && (draw_surf || read_surf))) {
++           ret = disp->Driver->MakeCurrent(disp, draw_surf, read_surf, context);
++   } else {
++	   ret = EGL_TRUE;
++   }
+ 
+    RETURN_EGL_EVAL(disp, ret);
+ }

+ 81 - 0
recipes-graphics/mesa/mesa-pvr/0009-GL_EXT_shader_pixel_local_storage2-entry-points.patch

@@ -0,0 +1,81 @@
+From a0e664462560c48078b696e535bcc0bf9e7431e8 Mon Sep 17 00:00:00 2001
+From: Rufus Hamade <rufus.hamade@imgtec.com>
+Date: Thu, 4 Feb 2016 14:09:26 +0000
+Subject: [PATCH] GL_EXT_shader_pixel_local_storage2 entry points
+
+---
+ .../gen/EXT_shader_pixel_local_storage2.xml   | 35 +++++++++++++++++++
+ src/mapi/glapi/gen/es_EXT.xml                 |  3 ++
+ src/mapi/glapi/gen/static_data.py             |  3 ++
+ 3 files changed, 41 insertions(+)
+ create mode 100644 src/mapi/glapi/gen/EXT_shader_pixel_local_storage2.xml
+
+diff --git a/src/mapi/glapi/gen/EXT_shader_pixel_local_storage2.xml b/src/mapi/glapi/gen/EXT_shader_pixel_local_storage2.xml
+new file mode 100644
+index 0000000..20e186c
+--- /dev/null
++++ b/src/mapi/glapi/gen/EXT_shader_pixel_local_storage2.xml
+@@ -0,0 +1,35 @@
++<?xml version="1.0"?>
++<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd">
++
++<OpenGLAPI>
++
++<category name="EXT_shader_pixel_local_storage2" number="240">
++
++    <enum name="GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT"                          value="0x8F63"/>
++    <enum name="GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT"                               value="0x8F67"/>
++    <enum name="GL_SHADER_PIXEL_LOCAL_STORAGE_EXT"                                        value="0x8F64"/>
++    <enum name="GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT"                       value="0x9650"/>
++    <enum name="GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT"                            value="0x9651"/>
++    <enum name="GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_EXT" value="0x9652"/>
++
++    <function name="ClearPixelLocalStorageuiEXT" es2="3.1" exec="dynamic">
++        <param name="offset" type="GLsizei"/>
++        <param name="n" type="GLsizei"/>
++        <param name="values" type="const GLuint *"/>
++    </function>
++
++    <function name="FramebufferPixelLocalStorageSizeEXT" es2="3.1"
++              exec="dynamic">
++        <param name="target" type="GLuint"/>
++        <param name="size" type="GLsizei"/>
++    </function>
++
++    <function name="GetFramebufferPixelLocalStorageSizeEXT" es2="3.1"
++              exec="dynamic">
++        <param name="target" type="GLuint"/>
++        <return type="GLsizei"/>
++    </function>
++
++</category>
++
++</OpenGLAPI>
+diff --git a/src/mapi/glapi/gen/es_EXT.xml b/src/mapi/glapi/gen/es_EXT.xml
+index e586b0e..a3bfe86 100644
+--- a/src/mapi/glapi/gen/es_EXT.xml
++++ b/src/mapi/glapi/gen/es_EXT.xml
+@@ -1459,6 +1459,9 @@
+ <!-- 240. EXT_sparse_texture -->
+ <xi:include href="EXT_sparse_texture.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ 
++<!-- 253. GL_EXT_shader_pixel_local_storage2 -->
++<xi:include href="EXT_shader_pixel_local_storage2.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
++
+ <category name="GL_OES_viewport_array" number="267">
+     <function name="ViewportArrayvOES" es2="3.1" alias="ViewportArrayv">
+         <param name="first" type="GLuint"/>
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index 79b8f19..99729e2 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1697,6 +1697,9 @@ offsets = {
+     "WeightPointerOES" : 1661,
+     "RenderbufferStorageMultisampleIMG" : 1662,
+     "FramebufferTexture2DMultisampleIMG" : 1663,
++    "ClearPixelLocalStorageuiEXT" : 1664,
++    "FramebufferPixelLocalStorageSizeEXT" : 1665,
++    "GetFramebufferPixelLocalStorageSizeEXT" : 1666,
+ }
+ 
+ functions = [

+ 82 - 0
recipes-graphics/mesa/mesa-pvr/0010-GL_IMG_framebuffer_downsample-entry-points.patch

@@ -0,0 +1,82 @@
+From 16eb24dd94d836480cb3bb1fa7f30b8500b8c12f Mon Sep 17 00:00:00 2001
+From: Rufus Hamade <rufus.hamade@imgtec.com>
+Date: Thu, 4 Feb 2016 14:09:26 +0000
+Subject: [PATCH] GL_IMG_framebuffer_downsample entry points
+
+---
+ .../glapi/gen/IMG_framebuffer_downsample.xml  | 37 +++++++++++++++++++
+ src/mapi/glapi/gen/es_EXT.xml                 |  3 ++
+ src/mapi/glapi/gen/static_data.py             |  2 +
+ 3 files changed, 42 insertions(+)
+ create mode 100644 src/mapi/glapi/gen/IMG_framebuffer_downsample.xml
+
+diff --git a/src/mapi/glapi/gen/IMG_framebuffer_downsample.xml b/src/mapi/glapi/gen/IMG_framebuffer_downsample.xml
+new file mode 100644
+index 0000000..b5ce77d
+--- /dev/null
++++ b/src/mapi/glapi/gen/IMG_framebuffer_downsample.xml
+@@ -0,0 +1,37 @@
++<?xml version="1.0"?>
++<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd">
++
++<OpenGLAPI>
++
++<category name="GL_IMG_framebuffer_downsample" number="255">
++
++    <enum name="FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG"                    value="0x913C"/>
++    <enum name="NUM_DOWNSAMPLE_SCALES_IMG"                                                value="0x913D"/>
++    <enum name="DOWNSAMPLE_SCALES_IMG"                                                    value="0x913E"/>
++    <enum name="FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG"                                 value="0x913F"/>
++
++    <function name="FramebufferTexture2DDownsampleIMG" es1="1.0" es2="2.0"
++              exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment" type="GLenum"/>
++        <param name="textarget" type="GLenum"/>
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++        <param name="xscale" type="GLint"/>
++        <param name="yscale" type="GLint"/>
++    </function>
++
++    <function name="FramebufferTextureLayerDownsampleIMG" es1="1.0" es2="2.0"
++              exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment" type="GLenum"/>
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++        <param name="layer" type="GLint"/>
++        <param name="xscale" type="GLint"/>
++        <param name="yscale" type="GLint"/>
++    </function>
++
++</category>
++
++</OpenGLAPI>
+diff --git a/src/mapi/glapi/gen/es_EXT.xml b/src/mapi/glapi/gen/es_EXT.xml
+index a3bfe86..b343282 100644
+--- a/src/mapi/glapi/gen/es_EXT.xml
++++ b/src/mapi/glapi/gen/es_EXT.xml
+@@ -1462,6 +1462,9 @@
+ <!-- 253. GL_EXT_shader_pixel_local_storage2 -->
+ <xi:include href="EXT_shader_pixel_local_storage2.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ 
++<!-- 255. GL_IMG_framebuffer_downsample -->
++<xi:include href="IMG_framebuffer_downsample.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
++
+ <category name="GL_OES_viewport_array" number="267">
+     <function name="ViewportArrayvOES" es2="3.1" alias="ViewportArrayv">
+         <param name="first" type="GLuint"/>
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index 99729e2..c723cb3 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1700,6 +1700,8 @@ offsets = {
+     "ClearPixelLocalStorageuiEXT" : 1664,
+     "FramebufferPixelLocalStorageSizeEXT" : 1665,
+     "GetFramebufferPixelLocalStorageSizeEXT" : 1666,
++    "FramebufferTexture2DDownsampleIMG" : 1667,
++    "FramebufferTextureLayerDownsampleIMG" : 1668,
+ }
+ 
+ functions = [

+ 50 - 0
recipes-graphics/mesa/mesa-pvr/0011-GL_OVR_multiview-entry-points.patch

@@ -0,0 +1,50 @@
+From d7176528f6dbb0c807fe34ebd8a3c5cee5945d3d Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 11 Jul 2016 12:45:30 +0100
+Subject: [PATCH] GL_OVR_multiview entry points
+
+---
+ src/mapi/glapi/gen/gl_API.xml     | 17 +++++++++++++++++
+ src/mapi/glapi/gen/static_data.py |  1 +
+ 2 files changed, 18 insertions(+)
+
+diff --git a/src/mapi/glapi/gen/gl_API.xml b/src/mapi/glapi/gen/gl_API.xml
+index cf5a0f0..4e35de0 100644
+--- a/src/mapi/glapi/gen/gl_API.xml
++++ b/src/mapi/glapi/gen/gl_API.xml
+@@ -13125,6 +13125,23 @@
+     </function>
+ </category>
+ 
++<category name="GL_OVR_multiview" number="478">
++    <enum name="FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR" value="0x9630" />
++    <enum name="FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR" value="0x9632" />
++    <enum name="MAX_VIEWS_OVR" value="0x9631">
++        <size name="Get" mode="get"/>
++    </enum>
++    <enum name="FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR" value="0x9633" />
++    <function name="FramebufferTextureMultiviewOVR" es2="3.0" exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment"  type="GLenum"/>
++        <param name="texture"  type="GLuint"/>
++        <param name="level"  type="GLint"/>
++        <param name="baseViewIndex"  type="GLint"/>
++        <param name="numViews"  type="GLsizei"/>
++    </function>
++</category>
++
+ <xi:include href="EXT_window_rectangles.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ 
+ <!-- 520. GL_EXT_shader_framebuffer_fetch -->
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index c723cb3..73625c7 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1702,6 +1702,7 @@ offsets = {
+     "GetFramebufferPixelLocalStorageSizeEXT" : 1666,
+     "FramebufferTexture2DDownsampleIMG" : 1667,
+     "FramebufferTextureLayerDownsampleIMG" : 1668,
++    "FramebufferTextureMultiviewOVR" : 1669,
+ }
+ 
+ functions = [

+ 65 - 0
recipes-graphics/mesa/mesa-pvr/0012-Add-OVR_multiview_multisampled_render_to_texture.patch

@@ -0,0 +1,65 @@
+From 11e20cde197c451743534f3ab316a9c16bbeda50 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 11 Jul 2016 13:29:51 +0100
+Subject: [PATCH] Add OVR_multiview_multisampled_render_to_texture
+
+---
+ ...ltiview_multisampled_render_to_texture.xml | 21 +++++++++++++++++++
+ src/mapi/glapi/gen/es_EXT.xml                 |  3 +++
+ src/mapi/glapi/gen/static_data.py             |  1 +
+ 3 files changed, 25 insertions(+)
+ create mode 100644 src/mapi/glapi/gen/OVR_multiview_multisampled_render_to_texture.xml
+
+diff --git a/src/mapi/glapi/gen/OVR_multiview_multisampled_render_to_texture.xml b/src/mapi/glapi/gen/OVR_multiview_multisampled_render_to_texture.xml
+new file mode 100644
+index 0000000..86bebc7
+--- /dev/null
++++ b/src/mapi/glapi/gen/OVR_multiview_multisampled_render_to_texture.xml
+@@ -0,0 +1,21 @@
++<?xml version="1.0"?>
++<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd">
++
++<OpenGLAPI>
++
++<category name="GL_OVR_multiview_multisampled_render_to_texture" number="250">
++
++    <function name="FramebufferTextureMultisampleMultiviewOVR" es2="3.0"
++              exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment"  type="GLenum"/>
++        <param name="texture"  type="GLuint"/>
++        <param name="level"  type="GLint"/>
++        <param name="samples"  type="GLsizei"/>
++        <param name="baseViewIndex"  type="GLint"/>
++        <param name="numViews"  type="GLsizei"/>
++    </function>
++
++</category>
++
++</OpenGLAPI>
+diff --git a/src/mapi/glapi/gen/es_EXT.xml b/src/mapi/glapi/gen/es_EXT.xml
+index b343282..fe8f27e 100644
+--- a/src/mapi/glapi/gen/es_EXT.xml
++++ b/src/mapi/glapi/gen/es_EXT.xml
+@@ -1459,6 +1459,9 @@
+ <!-- 240. EXT_sparse_texture -->
+ <xi:include href="EXT_sparse_texture.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ 
++<!-- 250. GL_OVR_multiview_multisampled_render_to_texture -->
++<xi:include href="OVR_multiview_multisampled_render_to_texture.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
++
+ <!-- 253. GL_EXT_shader_pixel_local_storage2 -->
+ <xi:include href="EXT_shader_pixel_local_storage2.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ 
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index 73625c7..dc6bdc9 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1703,6 +1703,7 @@ offsets = {
+     "FramebufferTexture2DDownsampleIMG" : 1667,
+     "FramebufferTextureLayerDownsampleIMG" : 1668,
+     "FramebufferTextureMultiviewOVR" : 1669,
++    "FramebufferTextureMultisampleMultiviewOVR" : 1670,
+ }
+ 
+ functions = [

+ 55 - 0
recipes-graphics/mesa/mesa-pvr/0013-wayland-drm-install-wayland-drm.xml-to-the-configure.patch

@@ -0,0 +1,55 @@
+From 4e92d56ba91f5bedc51881bddbe47f665eddca48 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Thu, 18 Aug 2016 15:52:28 +0100
+Subject: [PATCH] wayland-drm: install wayland-drm.xml to the configured
+ pkgdatadir
+
+Add a pkg-config file as well so that it can be located without hardcoding the
+path.
+
+---
+ src/egl/wayland/wayland-drm/meson.build       | 15 +++++++++++++++
+ src/egl/wayland/wayland-drm/wayland-drm.pc.in |  7 +++++++
+ 2 files changed, 22 insertions(+)
+ create mode 100644 src/egl/wayland/wayland-drm/wayland-drm.pc.in
+
+diff --git a/src/egl/wayland/wayland-drm/meson.build b/src/egl/wayland/wayland-drm/meson.build
+index b4782a0..f291ef9 100644
+--- a/src/egl/wayland/wayland-drm/meson.build
++++ b/src/egl/wayland/wayland-drm/meson.build
+@@ -49,6 +49,21 @@ libwayland_drm = static_library(
+   build_by_default : false,
+ )
+ 
++install_data('wayland-drm.xml')
++
++pkg.generate(
++  filebase : 'wayland-drm',
++  name : 'Mesa Wayland Protocols',
++  description : 'Mesa Wayland protocol files',
++  version : meson.project_version(),
++  variables : [
++     'datarootdir=${prefix}/' + get_option('datadir'),
++     'pkgdatadir=${pc_sysrootdir}${datarootdir}/' + meson.project_name(),
++  ],
++  install_dir : '@0@/@1@/pkgconfig'.format(get_option('prefix'),
++                get_option('datadir')),
++)
++
+ # linux-dmabuf isn't part of wayland-drm, but this happens to be the only
+ # place which is a) guaranteed to be built when building either or both
+ # of EGL and Vulkan WSI, and b) guaranteed to be included before both,
+diff --git a/src/egl/wayland/wayland-drm/wayland-drm.pc.in b/src/egl/wayland/wayland-drm/wayland-drm.pc.in
+new file mode 100644
+index 0000000..d08ccda
+--- /dev/null
++++ b/src/egl/wayland/wayland-drm/wayland-drm.pc.in
+@@ -0,0 +1,7 @@
++prefix=@prefix@
++datarootdir=@datarootdir@
++pkgdatadir=${pc_sysrootdir}@datadir@/@PACKAGE@
++
++Name: @PACKAGE_NAME@ Wayland Protocols
++Description: @PACKAGE_NAME@ Wayland protocol files
++Version: @PACKAGE_VERSION@
+\ No newline at end of file

+ 25 - 0
recipes-graphics/mesa/mesa-pvr/0014-Enable-buffer-sharing-in-the-kms_swrast-driver.patch

@@ -0,0 +1,25 @@
+From 499acbbea8d82fe7018bd8663bc543fa691d41de Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Wed, 26 Oct 2016 16:24:28 +0100
+Subject: [PATCH] Enable buffer sharing in the kms_swrast driver
+
+Enable buffer sharing, so that a DRI driver can be loaded by a
+Wayland client when kms_swrast is being used by the compositor.
+
+---
+ src/gallium/frontends/dri/dri2.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/gallium/frontends/dri/dri2.c b/src/gallium/frontends/dri/dri2.c
+index ebc18a7..09f4b02 100644
+--- a/src/gallium/frontends/dri/dri2.c
++++ b/src/gallium/frontends/dri/dri2.c
+@@ -2400,7 +2400,7 @@ dri_kms_init_screen(__DRIscreen * sPriv)
+    if (!configs)
+       goto destroy_screen;
+ 
+-   screen->can_share_buffer = false;
++   screen->can_share_buffer = true;
+    screen->auto_fake_front = dri_with_format(sPriv);
+    screen->broken_invalidate = !sPriv->dri2.useInvalidate;
+    screen->lookup_egl_image = dri2_lookup_egl_image;

+ 43 - 0
recipes-graphics/mesa/mesa-pvr/0015-egl-wayland-add-support-for-RGB565-back-buffers.patch

@@ -0,0 +1,43 @@
+From 0faf5745262a68722abce4fe692a341cbdba811b Mon Sep 17 00:00:00 2001
+From: James Glanville <James.Glanville@imgtec.com>
+Date: Tue, 28 Feb 2017 16:08:47 +0000
+Subject: [PATCH] egl/wayland: add support for RGB565 back buffers
+
+---
+ src/egl/drivers/dri2/platform_wayland.c | 13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index cfff0ad..c18edc0 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -633,18 +633,27 @@ back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
+    struct dri2_egl_display *dri2_dpy =
+       dri2_egl_display(dri2_surf->base.Resource.Display);
+    __DRIimage *image;
+-   int name, pitch;
++   int name, pitch, format;
+ 
+    image = dri2_surf->back->dri_image;
+ 
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NAME, &name);
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch);
++   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
+ 
+    buffer->attachment = __DRI_BUFFER_BACK_LEFT;
+    buffer->name = name;
+    buffer->pitch = pitch;
+-   buffer->cpp = 4;
+    buffer->flags = 0;
++
++   switch (format) {
++   case __DRI_IMAGE_FORMAT_RGB565:
++      buffer->cpp = 2;
++      break;
++   default:
++      buffer->cpp = 4;
++      break;
++   }
+ }
+ 
+ static int

+ 47 - 0
recipes-graphics/mesa/mesa-pvr/0016-egl-dri3-fix-segfault-in-eglCopyBuffers.patch

@@ -0,0 +1,47 @@
+From 09d413ab8c88e98534b32067e170bdd92c887cc5 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 13 Apr 2017 10:12:40 +0100
+Subject: [PATCH] egl/dri3: fix segfault in eglCopyBuffers
+
+The loader_dri3_copy_drawable function expected the drawable to have
+a fake front buffer, resulting in a segfault if there wasn't one,
+when dri3_fence_reset was called.
+If there is no fake front buffer, but there are back buffers, use the
+current back buffer.
+
+---
+ src/loader/loader_dri3_helper.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c
+index 6e825f9..ff6d1ff 100644
+--- a/src/loader/loader_dri3_helper.c
++++ b/src/loader/loader_dri3_helper.c
+@@ -863,15 +863,24 @@ loader_dri3_copy_drawable(struct loader_dri3_drawable *draw,
+                           xcb_drawable_t dest,
+                           xcb_drawable_t src)
+ {
++   struct loader_dri3_buffer *buffer;
++
++   if (draw->have_fake_front)
++      buffer = dri3_fake_front_buffer(draw);
++   else if (draw->have_back)
++      buffer = dri3_back_buffer(draw);
++   else
++      return;
++
+    loader_dri3_flush(draw, __DRI2_FLUSH_DRAWABLE, __DRI2_THROTTLE_COPYSUBBUFFER);
+ 
+-   dri3_fence_reset(draw->conn, dri3_fake_front_buffer(draw));
++   dri3_fence_reset(draw->conn, buffer);
+    dri3_copy_area(draw->conn,
+                   src, dest,
+                   dri3_drawable_gc(draw),
+                   0, 0, 0, 0, draw->width, draw->height);
+-   dri3_fence_trigger(draw->conn, dri3_fake_front_buffer(draw));
+-   dri3_fence_await(draw->conn, draw, dri3_fake_front_buffer(draw));
++   dri3_fence_trigger(draw->conn, buffer);
++   dri3_fence_await(draw->conn, draw, buffer);
+ }
+ 
+ void

+ 76 - 0
recipes-graphics/mesa/mesa-pvr/0017-egl-automatically-call-eglReleaseThread-on-thread-te.patch

@@ -0,0 +1,76 @@
+From df5f4dae5d92afb8d9922197efabd32c26e665a7 Mon Sep 17 00:00:00 2001
+From: Iosif Antochi <iosif.antochi@imgtec.com>
+Date: Wed, 14 Jun 2017 14:49:55 +0100
+Subject: [PATCH] egl: automatically call eglReleaseThread on thread
+ termination
+
+EGL thread cleanup conformance tests could run out of memory as the contexts
+were not freed even though the application requested to have them deleted.
+This was caused by the fact that the contexts were still current on their
+threads when delete was called and (in order not to block any potential
+pending renders) they were just marked for delete.
+
+Fix this by calling eglReleaseThread on thread termination. This is safe to
+do even if this was already called by the application since, according to the
+EGL 1.5 spec, eglReleaseThread can be called multiple times without error.
+
+Fixes:
+dEQP-EGL.functional.thread_cleanup.multi_context_*
+dEQP-EGL.functional.robustness.create_context.query_robust_access
+
+---
+ src/egl/main/eglcurrent.c | 27 ++++++++++++++++++++++++++-
+ 1 file changed, 26 insertions(+), 1 deletion(-)
+
+diff --git a/src/egl/main/eglcurrent.c b/src/egl/main/eglcurrent.c
+index 11277d3..c87eac0 100644
+--- a/src/egl/main/eglcurrent.c
++++ b/src/egl/main/eglcurrent.c
+@@ -44,6 +44,7 @@ static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
+ static EGLBoolean _egl_TSDInitialized;
+ static tss_t _egl_TSD;
+ static void _eglDestroyThreadInfo(_EGLThreadInfo *t);
++static void _eglDestroyThreadInfoCallback(_EGLThreadInfo *t);
+ 
+ #ifdef USE_ELF_TLS
+ static __THREAD_INITIAL_EXEC const _EGLThreadInfo *_egl_TLS;
+@@ -86,7 +87,7 @@ static inline EGLBoolean _eglInitTSD()
+ 
+       /* check again after acquiring lock */
+       if (!_egl_TSDInitialized) {
+-         if (tss_create(&_egl_TSD, (void (*)(void *)) _eglDestroyThreadInfo) != thrd_success) {
++         if (tss_create(&_egl_TSD, (void (*)(void *)) _eglDestroyThreadInfoCallback) != thrd_success) {
+             mtx_unlock(&_egl_TSDMutex);
+             return EGL_FALSE;
+          }
+@@ -135,6 +136,30 @@ _eglDestroyThreadInfo(_EGLThreadInfo *t)
+ }
+ 
+ 
++/**
++ * Delete/free a _EGLThreadInfo object.
++ */
++static void
++_eglDestroyThreadInfoCallback(_EGLThreadInfo *t)
++{
++   /* If this callback is called on thread termination then try to also give a
++    * chance to cleanup to the client drivers. If called for module termination
++    * then just release the thread information as calling eglReleaseThread
++    * would result in a deadlock.
++    */
++   if (_egl_TSDInitialized) {
++      /* The callback handler has replaced the TLS entry, which is passed in as
++       * 't', with NULL. Restore it here so that the release thread finds it in
++       * the TLS entry.
++       */
++      _eglSetTSD(t);
++      eglReleaseThread();
++   } else {
++      _eglDestroyThreadInfo(t);
++   }
++}
++
++
+ /**
+  * Make sure TSD is initialized and return current value.
+  */

+ 1634 - 0
recipes-graphics/mesa/mesa-pvr/0018-egl-add-Tizen-platform-support.patch

@@ -0,0 +1,1634 @@
+From 70abef4d4511b1e25ab3b4746644e5324140d9d7 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Fri, 6 Jan 2017 16:35:00 +0000
+Subject: [PATCH] egl: add Tizen platform support
+
+Add a new "tizen" platform to support drm/gbm and Wayland on Tizen.
+The platform can be enabled by passing "-Dplatforms=tizen" to Meson.
+
+---
+ meson.build                           |  25 +-
+ meson_options.txt                     |   2 +-
+ src/egl/drivers/dri2/egl_dri2.c       | 182 ++++-
+ src/egl/drivers/dri2/egl_dri2.h       |  59 +-
+ src/egl/drivers/dri2/platform_tizen.c | 980 ++++++++++++++++++++++++++
+ src/egl/main/eglapi.c                 |   7 +
+ src/egl/main/egldisplay.c             |  32 +-
+ src/egl/main/egldisplay.h             |   7 +
+ src/egl/main/egllog.c                 |  27 +-
+ src/egl/meson.build                   |   7 +-
+ 10 files changed, 1313 insertions(+), 15 deletions(-)
+ create mode 100644 src/egl/drivers/dri2/platform_tizen.c
+
+diff --git a/meson.build b/meson.build
+index 968b4f5..3d9d345 100644
+--- a/meson.build
++++ b/meson.build
+@@ -358,6 +358,11 @@ with_platform_x11 = _platforms.contains('x11')
+ with_platform_wayland = _platforms.contains('wayland')
+ with_platform_haiku = _platforms.contains('haiku')
+ with_platform_windows = _platforms.contains('windows')
++with_platform_tizen = _platforms.contains('tizen')
++
++if with_platform_tizen and _platforms.length() != 1
++  error('tizen cannot be enabled at the same time as other platforms')
++endif
+ 
+ with_glx = get_option('glx')
+ if with_glx == 'auto'
+@@ -910,7 +915,7 @@ else
+   pre_args += '-DEGL_NO_X11'
+   gl_pkgconfig_c_flags += '-DEGL_NO_X11'
+ endif
+-if with_gbm and not with_platform_android
++if with_gbm and not with_platform_android and not with_platform_tizen
+   pre_args += '-DHAVE_DRM_PLATFORM'
+ endif
+ 
+@@ -944,6 +949,15 @@ endif
+ if with_platform_haiku
+   pre_args += '-DHAVE_HAIKU_PLATFORM'
+ endif
++if with_platform_tizen
++  dep_dlog = dependency('dlog')
++  dep_tizen = [
++    dep_dlog,
++    dependency('libtbm'),
++    dependency('tpl-egl'),
++  ]
++  pre_args += '-DHAVE_TIZEN_PLATFORM'
++endif
+ 
+ prog_python = import('python').find_installation('python3')
+ has_mako = run_command(
+@@ -1568,7 +1582,8 @@ with_gallium_drisw_kms = false
+ dep_libdrm = dependency(
+   'libdrm', version : '>=' + _drm_ver,
+   # GNU/Hurd includes egl_dri2, without drm.
+-  required : (with_dri2 and host_machine.system() != 'gnu') or with_dri3
++  required : (with_dri2 and host_machine.system() != 'gnu') or with_dri3 or
++             with_platform_tizen
+ )
+ if dep_libdrm.found()
+   pre_args += '-DHAVE_LIBDRM'
+@@ -2187,8 +2202,10 @@ if with_egl
+   lines += 'EGL drivers:     ' + ' '.join(egl_drivers)
+ endif
+ if with_egl or with_any_vk
+-  _platforms += 'surfaceless'
+-  if with_gbm and not with_platform_android
++  if not with_platform_tizen
++    _platforms += 'surfaceless'
++  endif
++  if with_gbm and not with_platform_android and not with_platform_tizen
+     _platforms += 'drm'
+   endif
+   lines += 'EGL/Vulkan/VL platforms:   ' + ' '.join(_platforms)
+diff --git a/meson_options.txt b/meson_options.txt
+index dac7910..8230db9 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -23,7 +23,7 @@ option(
+   type : 'array',
+   value : ['auto'],
+   choices : [
+-    'auto', 'x11', 'wayland', 'haiku', 'android', 'windows',
++    'auto', 'x11', 'wayland', 'haiku', 'android', 'windows', 'tizen',
+   ],
+   description : 'window systems to support. If this is set to `auto`, all platforms applicable will be enabled.'
+ )
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index dea5899..6d34395 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1179,6 +1179,11 @@ dri2_initialize(_EGLDisplay *disp)
+    case _EGL_PLATFORM_ANDROID:
+       ret = dri2_initialize_android(disp);
+       break;
++#ifdef HAVE_TIZEN_PLATFORM
++   case _EGL_PLATFORM_TIZEN:
++      ret = dri2_initialize_tizen(disp);
++      break;
++#endif
+    default:
+       unreachable("Callers ensure we cannot get here.");
+       return EGL_FALSE;
+@@ -1253,6 +1258,12 @@ dri2_display_destroy(_EGLDisplay *disp)
+    case _EGL_PLATFORM_WAYLAND:
+       dri2_teardown_wayland(dri2_dpy);
+       break;
++#ifdef HAVE_TIZEN_PLATFORM
++   case _EGL_PLATFORM_TIZEN:
++      if (dri2_dpy->tpl_dpy)
++         tpl_object_unreference((tpl_object_t *) dri2_dpy->tpl_dpy);
++      break;
++#endif
+    default:
+       /* TODO: add teardown for other platforms */
+       break;
+@@ -2293,8 +2304,96 @@ dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx,
+    return dri2_create_image_from_dri(disp, dri_image);
+ }
+ 
+-#ifdef HAVE_WAYLAND_PLATFORM
++#ifdef HAVE_TIZEN_PLATFORM
++int
++dri2_fourcc_from_tbm_format(tbm_format format)
++{
++   switch (format) {
++   case TBM_FORMAT_ARGB8888:
++      return DRM_FORMAT_ARGB8888;
++   case TBM_FORMAT_XRGB8888:
++      return DRM_FORMAT_XRGB8888;
++   case TBM_FORMAT_RGB565:
++      return DRM_FORMAT_RGB565;
++   default:
++      _eglLog(_EGL_DEBUG, "%s: unsupported tbm format %#x", __func__, format);
++      return 0;
++   }
++}
+ 
++static _EGLImage *
++dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx,
++                                          EGLClientBuffer _buffer,
++                                          const EGLint *attr_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   __DRIimage *dri_image;
++   _EGLImageAttribs attrs;
++   tbm_surface_h tbm_surf;
++   tbm_bo tbm_buf;
++   tbm_surface_info_s info;
++   int fourcc, fd, pitch, offset;
++
++   tbm_surf = tpl_display_get_buffer_from_native_pixmap(dri2_dpy->tpl_dpy,
++                                                        (tpl_handle_t) _buffer);
++   if (!tbm_surf) {
++      _eglError(EGL_BAD_PARAMETER, "tpl_display_get_buffer_from_native_pixmap");
++      return NULL;
++   }
++
++   if (!_eglParseImageAttribList(&attrs, disp, attr_list)) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer_tizen");
++      return NULL;
++   }
++
++   if (tbm_surface_get_info(tbm_surf, &info)) {
++      _eglError(EGL_BAD_PARAMETER, "tbm_surface_get_info");
++      return NULL;
++   }
++
++   if (info.num_planes > 1) {
++      _eglError(EGL_BAD_PARAMETER,
++                "dri2_create_image_wayland_wl_buffer_tizen (multi-plane format)");
++      return NULL;
++   }
++
++   if (attrs.PlaneWL < 0 || attrs.PlaneWL >= info.num_planes) {
++      _eglError(EGL_BAD_PARAMETER,
++                "dri2_create_image_wayland_wl_buffer_tizen (plane out of bounds)");
++      return NULL;
++   }
++
++   tbm_buf = tbm_surface_internal_get_bo(tbm_surf, attrs.PlaneWL);
++   if (!tbm_buf) {
++      _eglError(EGL_BAD_PARAMETER, "tbm_surface_internal_get_bo");
++      return NULL;
++   }
++
++   fourcc = dri2_fourcc_from_tbm_format(info.format);
++   pitch = info.planes[attrs.PlaneWL].stride;
++   offset = info.planes[attrs.PlaneWL].offset;
++   fd = tbm_bo_export_fd(tbm_buf);
++
++   dri_image = dri2_dpy->image->createImageFromFds(dri2_dpy->dri_screen,
++                                                   info.width,
++                                                   info.height,
++                                                   fourcc,
++                                                   &fd,
++                                                   1,
++                                                   &pitch,
++                                                   &offset,
++                                                   tbm_surf);
++   close(fd);
++   if (dri_image == NULL) {
++      _eglError(EGL_BAD_PARAMETER, "createImageFromFds");
++      return NULL;
++   }
++
++   return dri2_create_image_from_dri(disp, dri_image);
++}
++#endif
++
++#ifdef HAVE_WAYLAND_PLATFORM
+ /* This structure describes how a wl_buffer maps to one or more
+  * __DRIimages.  A wl_drm_buffer stores the wl_drm format code and the
+  * offsets and strides of the planes in the buffer.  This table maps a
+@@ -3193,6 +3292,10 @@ dri2_create_image_khr(_EGLDisplay *disp, _EGLContext *ctx, EGLenum target,
+ #ifdef HAVE_WAYLAND_PLATFORM
+    case EGL_WAYLAND_BUFFER_WL:
+       return dri2_create_image_wayland_wl_buffer(disp, ctx, buffer, attr_list);
++#endif
++#ifdef HAVE_TIZEN_PLATFORM
++   case EGL_WAYLAND_BUFFER_WL:
++      return dri2_create_image_wayland_wl_buffer_tizen(disp, ctx, buffer, attr_list);
+ #endif
+    case EGL_CL_IMAGE_IMG:
+       return dri2_create_image_img_buffer(disp, ctx, target, buffer, attr_list);
+@@ -3214,6 +3317,78 @@ dri2_destroy_image_khr(_EGLDisplay *disp, _EGLImage *image)
+    return EGL_TRUE;
+ }
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++
++static EGLint get_texture_format_from_gbm_format(tbm_format format)
++{
++   switch (format) {
++   case TBM_FORMAT_ARGB8888:
++      return EGL_TEXTURE_RGBA;
++   default:
++      _eglLog(_EGL_DEBUG, "%s: unsupported tbm format %#x", __func__, format);
++      return 0;
++   }
++}
++
++static EGLBoolean
++dri2_bind_wayland_display_wl_tizen(_EGLDisplay *disp, struct wl_display *wl_dpy)
++{
++   (void) disp;
++   (void) wl_dpy;
++
++   return EGL_TRUE;
++}
++
++static EGLBoolean
++dri2_unbind_wayland_display_wl_tizen(_EGLDisplay *disp,
++                                     struct wl_display *wl_dpy)
++{
++   (void) disp;
++   (void) wl_dpy;
++
++   return EGL_TRUE;
++}
++
++static EGLBoolean
++dri2_query_wayland_buffer_wl_tizen(_EGLDisplay *disp,
++                                   struct wl_resource *buffer_resource,
++                                   EGLint attribute, EGLint *value)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   tpl_handle_t pixmap = (tpl_handle_t) buffer_resource;
++   tbm_surface_h tbm_surf;
++   tbm_format format;
++   EGLint tex_format;
++
++   tbm_surf = tpl_display_get_buffer_from_native_pixmap(dri2_dpy->tpl_dpy,
++                                                        pixmap);
++   if (!tbm_surf)
++      return EGL_FALSE;
++
++   switch (attribute) {
++   case EGL_TEXTURE_FORMAT:
++      format = tbm_surface_get_format(tbm_surf);
++      tex_format = get_texture_format_from_gbm_format(format);
++      if (!tex_format)
++         return EGL_FALSE;
++
++      *value = tex_format;
++      break;
++   case EGL_WIDTH:
++      *value = tbm_surface_get_width(tbm_surf);
++      break;
++   case EGL_HEIGHT:
++      *value = tbm_surface_get_height(tbm_surf);
++      break;
++   default:
++      return EGL_FALSE;
++   }
++
++   return EGL_TRUE;
++}
++
++#endif
++
+ #ifdef HAVE_WAYLAND_PLATFORM
+ 
+ static void
+@@ -3755,6 +3930,11 @@ const _EGLDriver _eglDriver = {
+    .BindWaylandDisplayWL = dri2_bind_wayland_display_wl,
+    .UnbindWaylandDisplayWL = dri2_unbind_wayland_display_wl,
+    .QueryWaylandBufferWL = dri2_query_wayland_buffer_wl,
++#endif
++#ifdef HAVE_TIZEN_PLATFORM
++   .BindWaylandDisplayWL = dri2_bind_wayland_display_wl_tizen,
++   .UnbindWaylandDisplayWL = dri2_unbind_wayland_display_wl_tizen,
++   .QueryWaylandBufferWL = dri2_query_wayland_buffer_wl_tizen,
+ #endif
+    .GetSyncValuesCHROMIUM = dri2_get_sync_values_chromium,
+    .CreateSyncKHR = dri2_create_sync,
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index 6a7eede..a8bfe45 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -62,9 +62,9 @@ struct zwp_linux_dmabuf_v1;
+ #include <gbm_driint.h>
+ #endif
+ 
+-#ifdef HAVE_ANDROID_PLATFORM
+ #define LOG_TAG "EGL-DRI2"
+ 
++#ifdef HAVE_ANDROID_PLATFORM
+ #include <hardware/gralloc.h>
+ 
+ #if ANDROID_API_LEVEL >= 26
+@@ -75,6 +75,12 @@ struct zwp_linux_dmabuf_v1;
+ 
+ #endif /* HAVE_ANDROID_PLATFORM */
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++#include <pthread.h>
++#include <tpl.h>
++#include <tbm_surface_internal.h>
++#endif
++
+ #include "eglconfig.h"
+ #include "eglcontext.h"
+ #include "egldevice.h"
+@@ -259,6 +265,10 @@ struct dri2_egl_display
+ 
+    bool                      is_render_node;
+    bool                      is_different_gpu;
++
++#ifdef HAVE_TIZEN_PLATFORM
++   tpl_display_t            *tpl_dpy;
++#endif
+ };
+ 
+ struct dri2_egl_context
+@@ -276,6 +286,18 @@ enum wayland_buffer_type {
+ };
+ #endif
+ 
++#define DRI2_SURFACE_NUM_COLOR_BUFFERS 4
++
++#ifdef HAVE_TIZEN_PLATFORM
++struct tpl_swap_queue_elem
++{
++   tbm_surface_h       tbm_surf;
++   EGLint             *rects;
++   EGLint              n_rects_max;
++   EGLint              n_rects;
++};
++#endif
++
+ struct dri2_egl_surface
+ {
+    _EGLSurface          base;
+@@ -308,15 +330,33 @@ struct dri2_egl_surface
+    struct gbm_dri_surface *gbm_surf;
+ #endif
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++   tpl_surface_t         *tpl_surf;
++   bool                   reset;
++   /*
++    * Protects swap_queue_idx_head, swap_queue_idx_tail and
++    * color_buffers.locked.
++    */
++   pthread_mutex_t        mutex;
++   pthread_cond_t         swap_queue_cond;
++   struct tpl_swap_queue_elem swap_queue[DRI2_SURFACE_NUM_COLOR_BUFFERS];
++   int                    swap_queue_idx_head;
++   int                    swap_queue_idx_tail;
++   pthread_t              swap_queue_processor;
++#endif
++
+    /* EGL-owned buffers */
+    __DRIbuffer           *local_buffers[__DRI_BUFFER_COUNT];
+ 
+-#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM)
++#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM) || \
++    defined(HAVE_TIZEN_PLATFORM)
+    struct {
++#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_TIZEN_PLATFORM)
++      __DRIimage         *dri_image;
++#endif
+ #ifdef HAVE_WAYLAND_PLATFORM
+       struct wl_buffer   *wl_buffer;
+       bool                wl_release;
+-      __DRIimage         *dri_image;
+       /* for is_different_gpu case. NULL else */
+       __DRIimage         *linear_copy;
+       /* for swrast */
+@@ -325,6 +365,9 @@ struct dri2_egl_surface
+ #endif
+ #ifdef HAVE_DRM_PLATFORM
+       struct gbm_bo       *bo;
++#endif
++#ifdef HAVE_TIZEN_PLATFORM
++      tbm_surface_h       tbm_surf;
+ #endif
+       bool                locked;
+       int                 age;
+@@ -525,6 +568,11 @@ dri2_initialize_android(_EGLDisplay *disp)
+ EGLBoolean
+ dri2_initialize_surfaceless(_EGLDisplay *disp);
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++EGLBoolean
++dri2_initialize_tizen(_EGLDisplay *disp);
++#endif
++
+ EGLBoolean
+ dri2_initialize_device(_EGLDisplay *disp);
+ static inline void
+@@ -559,6 +607,11 @@ dri2_set_WL_bind_wayland_display(_EGLDisplay *disp)
+ #endif
+ }
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++int
++dri2_fourcc_from_tbm_format(tbm_format format);
++#endif
++
+ void
+ dri2_display_destroy(_EGLDisplay *disp);
+ 
+diff --git a/src/egl/drivers/dri2/platform_tizen.c b/src/egl/drivers/dri2/platform_tizen.c
+new file mode 100644
+index 0000000..a08bc8c
+--- /dev/null
++++ b/src/egl/drivers/dri2/platform_tizen.c
+@@ -0,0 +1,980 @@
++/*
++ * Mesa 3-D graphics library
++ *
++ * Copyright (c) Imagination Technologies Ltd.
++ * Copyright © 2011 Intel Corporation
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included
++ * in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include <dlfcn.h>
++#include <pthread.h>
++#include <stdio.h>
++#include <string.h>
++#include <tpl.h>
++#include <xf86drm.h>
++
++#include "egl_dri2.h"
++#include "loader.h"
++
++#define TIZEN_DRM_RENDER_MINOR_START 128
++#define TIZEN_DRM_RENDER_MINOR_MAX 191
++
++#define TIZEN_SWAP_N_RECTS_MIN 32
++
++struct rgba_shifts_and_sizes {
++   int shifts[4];
++   unsigned int sizes[4];
++};
++
++static void *swap_queue_processor_worker(void *data)
++{
++   struct dri2_egl_surface *dri2_surf = data;
++
++   pthread_mutex_lock(&dri2_surf->mutex);
++   while (1) {
++      struct tpl_swap_queue_elem *queue_elem;
++      int i;
++      tpl_result_t res;
++
++      if (dri2_surf->swap_queue_idx_head == dri2_surf->swap_queue_idx_tail) {
++         /* Wait for new swaps to be added to the queue */
++         pthread_cond_wait(&dri2_surf->swap_queue_cond, &dri2_surf->mutex);
++         continue;
++      }
++
++      queue_elem = &dri2_surf->swap_queue[dri2_surf->swap_queue_idx_head];
++      pthread_mutex_unlock(&dri2_surf->mutex);
++
++      res = tpl_surface_enqueue_buffer_with_damage(dri2_surf->tpl_surf,
++                                                   queue_elem->tbm_surf,
++                                                   queue_elem->n_rects,
++                                                   queue_elem->rects);
++      if (res != TPL_ERROR_NONE)
++         return NULL;
++
++      pthread_mutex_lock(&dri2_surf->mutex);
++      for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++         if (dri2_surf->color_buffers[i].tbm_surf == queue_elem->tbm_surf) {
++            dri2_surf->color_buffers[i].locked = false;
++            break;
++         }
++      }
++
++      dri2_surf->swap_queue_idx_head++;
++      dri2_surf->swap_queue_idx_head %= ARRAY_SIZE(dri2_surf->swap_queue);
++
++      /*
++       * Notify get_back_bo that a buffer has become available, reset_surface_cb
++       * that it may be able to reset the surface or dri2_tizen_destroy_surface
++       * that it may be able to proceed with surface destruction.
++       */
++      pthread_cond_signal(&dri2_surf->swap_queue_cond);
++   }
++   pthread_mutex_unlock(&dri2_surf->mutex);
++
++   return NULL;
++}
++
++static __DRIimage *
++create_image_from_native(struct dri2_egl_surface *dri2_surf,
++                         tbm_surface_h tbm_surf,
++                         int *width, int *height,
++                         void *loaderPrivate)
++{
++   _EGLSurface *surf = &dri2_surf->base;
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(surf->Resource.Display);
++   tbm_bo tbm_buf;
++   tbm_surface_info_s info;
++   int fd, fourcc, offset, pitch;
++   __DRIimage *dri_image;
++
++   tbm_buf = tbm_surface_internal_get_bo(tbm_surf, 0);
++   if (!tbm_buf) {
++      _eglLog(_EGL_DEBUG, "%s: failed to get bo for tbm surface", __func__);
++      return NULL;
++   }
++
++   if (tbm_surface_get_info(tbm_surf, &info)) {
++      _eglLog(_EGL_DEBUG, "%s: failed to get tbm surface info", __func__);
++      return NULL;
++   }
++
++   fd = tbm_bo_export_fd(tbm_buf);
++   fourcc = dri2_fourcc_from_tbm_format(info.format);
++   offset = info.planes[0].offset;
++   pitch = info.planes[0].stride;
++
++   dri_image = dri2_dpy->image->createImageFromFds(dri2_dpy->dri_screen,
++                                                   info.width,
++                                                   info.height,
++                                                   fourcc,
++                                                   &fd,
++                                                   1,
++                                                   &pitch,
++                                                   &offset,
++                                                   loaderPrivate);
++   close(fd);
++
++   if (!dri_image) {
++      _eglLog(_EGL_DEBUG, "%s: failed to create dri image from tbm bo", __func__);
++      return NULL;
++   }
++
++   if (width)
++      *width = info.width;
++
++   if (height)
++      *height = info.height;
++
++   return dri_image;
++}
++
++static int
++get_back_bo(struct dri2_egl_surface *dri2_surf, bool allow_update)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   tbm_surface_h tbm_surf;
++   tpl_bool_t tpl_surf_valid;
++   int i;
++
++   if (dri2_surf->base.Type == EGL_PIXMAP_BIT) {
++      _eglLog(_EGL_DEBUG, "%s: can't get back bo for pixmap surface", __func__);
++      return -1;
++   }
++
++   if (dri2_surf->back && !allow_update)
++      return 0;
++
++   /*
++    * If the tpl surface is no longer valid, e.g. it has been resized, then
++    * we should get a new back buffer.
++    */
++   tpl_surf_valid = tpl_surface_validate(dri2_surf->tpl_surf);
++   if (dri2_surf->back && tpl_surf_valid == TPL_TRUE)
++      return 0;
++
++   tbm_surf = tpl_surface_dequeue_buffer(dri2_surf->tpl_surf);
++   if (!tbm_surf) {
++      _eglLog(_EGL_DEBUG, "%s: surface buffer dequeue failed", __func__);
++      return -1;
++   }
++
++   pthread_mutex_lock(&dri2_surf->mutex);
++   /*
++    * If the tpl surface is no longer valid, e.g. it has been resized, then the
++    * call to tpl_surface should've resulted in a call to reset_surface_cb,
++    * which sets the dri2_surf reset flag. In this case we need to reset the
++    * color_buffer state.
++    *
++    * Note: we can't rely on the return value of tpl_surface_validate
++    * since the surface may have become invalid between this call and
++    * tpl_surface_dequeue_buffer.
++    */
++   if (dri2_surf->reset) {
++      /* Wait for any outstanding swaps to complete */
++      while (dri2_surf->swap_queue_idx_head != dri2_surf->swap_queue_idx_tail)
++         pthread_cond_wait(&dri2_surf->swap_queue_cond, &dri2_surf->mutex);
++
++      /* Cancel the old back buffer */
++      if (dri2_surf->back) {
++         tbm_surface_internal_unref(dri2_surf->back->tbm_surf);
++         dri2_surf->back->locked = false;
++         dri2_surf->back = NULL;
++      }
++
++      /* Reset the color buffers */
++      for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++         assert(!dri2_surf->color_buffers[i].locked);
++
++         if (dri2_surf->color_buffers[i].dri_image) {
++            dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
++            dri2_surf->color_buffers[i].dri_image = NULL;
++         }
++
++         dri2_surf->color_buffers[i].tbm_surf = NULL;
++         dri2_surf->color_buffers[i].age = 0;
++      }
++
++      dri2_surf->back = NULL;
++      dri2_surf->current = NULL;
++      dri2_surf->reset = false;
++   }
++
++   assert(!dri2_surf->back);
++
++   for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++      if (dri2_surf->color_buffers[i].tbm_surf == tbm_surf) {
++         dri2_surf->back = &dri2_surf->color_buffers[i];
++         assert(!dri2_surf->back->locked);
++         break;
++      }
++   }
++
++   while (!dri2_surf->back) {
++      int age;
++
++      /*
++       * Search for a free color buffer. Free the oldest buffer if one
++       * cannot be found.
++       */
++      for (i = 0, age = -1; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++         if (dri2_surf->color_buffers[i].locked)
++            continue;
++         if (!dri2_surf->color_buffers[i].dri_image) {
++            dri2_surf->back = &dri2_surf->color_buffers[i];
++            break;
++         }
++
++         if (dri2_surf->color_buffers[i].age > age) {
++            dri2_surf->back = &dri2_surf->color_buffers[i];
++            age = dri2_surf->color_buffers[i].age;
++         }
++      }
++
++      if (!dri2_surf->back) {
++         /*
++          * There aren't any available back buffers so wait for
++          * swap_queue_processor_worker to make one available.
++          */
++         pthread_cond_wait(&dri2_surf->swap_queue_cond, &dri2_surf->mutex);
++         continue;
++      }
++
++      if (dri2_surf->back->dri_image)
++         dri2_dpy->image->destroyImage(dri2_surf->back->dri_image);
++
++      dri2_surf->back->dri_image =
++         create_image_from_native(dri2_surf, tbm_surf,
++                                  &dri2_surf->base.Width,
++                                  &dri2_surf->base.Height,
++                                  dri2_surf);
++      if (!dri2_surf->back->dri_image) {
++         dri2_surf->back = NULL;
++         pthread_mutex_unlock(&dri2_surf->mutex);
++         return -1;
++      }
++   }
++
++   dri2_surf->back->tbm_surf = tbm_surf;
++   dri2_surf->back->locked = true;
++
++   if (dri2_surf->base.SwapBehavior == EGL_BUFFER_PRESERVED &&
++       dri2_surf->current) {
++      _EGLContext *ctx = _eglGetCurrentContext();
++      struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
++
++      if (dri2_ctx)
++      {
++         dri2_dpy->image->blitImage(dri2_ctx->dri_context,
++                                    dri2_surf->back->dri_image,
++                                    dri2_surf->current->dri_image,
++                                    0, 0, dri2_surf->base.Width,
++                                    dri2_surf->base.Height,
++                                    0, 0, dri2_surf->base.Width,
++                                    dri2_surf->base.Height,
++                                    __BLIT_FLAG_FLUSH);
++      }
++   }
++
++   pthread_mutex_unlock(&dri2_surf->mutex);
++
++   return 0;
++}
++
++static int
++get_front_bo(struct dri2_egl_surface *dri2_surf)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++
++   if (dri2_surf->front)
++      return 0;
++
++   if (dri2_surf->base.Type == EGL_PIXMAP_BIT) {
++      tbm_surface_h tbm_surf;
++
++      tbm_surf = tpl_surface_dequeue_buffer(dri2_surf->tpl_surf);
++      if (!tbm_surf)
++         return -1;
++
++      dri2_surf->front = create_image_from_native(dri2_surf, tbm_surf,
++                                                  &dri2_surf->base.Width,
++                                                  &dri2_surf->base.Height,
++                                                  dri2_surf);
++   } else {
++      dri2_surf->front = dri2_dpy->image->createImage(dri2_dpy->dri_screen,
++                                                      dri2_surf->base.Width,
++                                                      dri2_surf->base.Height,
++                                                      dri2_surf->visual,
++                                                      0,
++                                                      dri2_surf);
++   }
++
++   return dri2_surf->front ? 0 : -1;
++}
++
++static _EGLSurface *
++create_surface(_EGLDisplay *dpy, _EGLConfig *config, EGLint type,
++               const EGLint *attrib_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
++   struct dri2_egl_config *dri2_config = dri2_egl_config(config);
++   struct dri2_egl_surface *dri2_surf;
++   const __DRIconfig *dri_config;
++
++   dri2_surf = calloc(1, sizeof(*dri2_surf));
++   if (!dri2_surf) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to create surface");
++      return NULL;
++   }
++
++   if (!dri2_init_surface(&dri2_surf->base, dpy, type, config, attrib_list,
++                          false, NULL))
++      goto err_free_surface;
++
++   dri_config = dri2_get_dri_config(dri2_config, type,
++                                    dri2_surf->base.GLColorspace);
++   if (!dri_config) {
++      _eglError(EGL_BAD_MATCH, "Unsupported surfacetype/colorspace configuration");
++      goto err_free_surface;
++   }
++
++   dri2_surf->dri_drawable =
++      dri2_dpy->image_driver->createNewDrawable(dri2_dpy->dri_screen,
++                                                dri_config, dri2_surf);
++   if (!dri2_surf->dri_drawable) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to create drawable");
++      goto err_free_surface;
++   }
++
++   pthread_mutex_init(&dri2_surf->mutex, NULL);
++   pthread_cond_init(&dri2_surf->swap_queue_cond, NULL);
++
++   return &dri2_surf->base;
++
++err_free_surface:
++   free(dri2_surf);
++   return NULL;
++}
++
++static void
++destroy_surface(_EGLSurface *surf)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(surf->Resource.Display);
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
++
++   pthread_cond_destroy(&dri2_surf->swap_queue_cond);
++   pthread_mutex_destroy(&dri2_surf->mutex);
++   dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable);
++   free(dri2_surf);
++}
++
++static void
++reset_surface_cb(void *data)
++{
++   struct dri2_egl_surface *dri2_surf = data;
++
++   dri2_surf->reset = true;
++}
++
++static _EGLSurface *
++dri2_tizen_create_window_surface(_EGLDisplay *dpy, _EGLConfig *config,
++                                 void *native_window, const EGLint *attrib_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
++   struct dri2_egl_surface *dri2_surf;
++   _EGLSurface *surf;
++   int width, height;
++   tbm_format format;
++   tpl_result_t res;
++   int err;
++
++   surf = create_surface(dpy, config, EGL_WINDOW_BIT, attrib_list);
++   if (!surf)
++      return NULL;
++
++   res = tpl_display_get_native_window_info(dri2_dpy->tpl_dpy, native_window,
++                                            &width, &height, &format,
++                                            config->RedSize +
++                                            config->GreenSize +
++                                            config->BlueSize +
++                                            config->AlphaSize,
++                                            config->AlphaSize);
++   if (res != TPL_ERROR_NONE) {
++      _eglError(EGL_BAD_NATIVE_WINDOW, "DRI2: failed to get window info");
++      goto err_destroy_surface;
++   }
++
++   dri2_surf = dri2_egl_surface(surf);
++   dri2_surf->base.Width = width;
++   dri2_surf->base.Height = height;
++
++   dri2_surf->tpl_surf = tpl_surface_create(dri2_dpy->tpl_dpy, native_window,
++                                            TPL_SURFACE_TYPE_WINDOW, format);
++   if (!dri2_surf->tpl_surf) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to create TPL window surface");
++      goto err_destroy_surface;
++   }
++
++   res = tpl_surface_set_reset_cb(dri2_surf->tpl_surf, dri2_surf,
++                                  reset_surface_cb);
++   if (res != TPL_ERROR_NONE) {
++      _eglError(EGL_BAD_NATIVE_WINDOW,
++                "DRI2: failed to register TPL window surface reset callback");
++      goto err_tpl_surface_destroy;
++   }
++
++   err = pthread_create(&dri2_surf->swap_queue_processor, NULL,
++                        swap_queue_processor_worker, dri2_surf);
++   if (err) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to create TPL window thread");
++      goto err_tpl_surface_destroy;
++   }
++
++   return surf;
++
++err_tpl_surface_destroy:
++   tpl_object_unreference((tpl_object_t *) dri2_surf->tpl_surf);
++err_destroy_surface:
++   destroy_surface(surf);
++   return NULL;
++}
++
++static _EGLSurface *
++dri2_tizen_create_pixmap_surface(_EGLDisplay *dpy, _EGLConfig *config,
++                                 void *native_pixmap, const EGLint *attrib_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
++   struct dri2_egl_surface *dri2_surf;
++   _EGLSurface *surf;
++   int width, height;
++   tbm_format format;
++   tpl_result_t res;
++
++   surf = create_surface(dpy, config, EGL_PIXMAP_BIT, attrib_list);
++   if (!surf)
++      return NULL;
++
++   res = tpl_display_get_native_pixmap_info(dri2_dpy->tpl_dpy, native_pixmap,
++                                            &width, &height, &format);
++   if (res != TPL_ERROR_NONE) {
++      _eglError(EGL_BAD_NATIVE_PIXMAP, "DRI2: failed to get pixmap info");
++      goto err_destroy_surface;
++   }
++
++   dri2_surf = dri2_egl_surface(surf);
++   dri2_surf->base.Width = width;
++   dri2_surf->base.Height = height;
++
++   dri2_surf->tpl_surf = tpl_surface_create(dri2_dpy->tpl_dpy, native_pixmap,
++                                            TPL_SURFACE_TYPE_PIXMAP, format);
++   if (!dri2_surf->tpl_surf) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to create TPL pixmap surface");
++      goto err_destroy_surface;
++   }
++
++   return surf;
++
++err_destroy_surface:
++   destroy_surface(surf);
++   return NULL;
++}
++
++static _EGLSurface *
++dri2_tizen_create_pbuffer_surface(_EGLDisplay *dpy, _EGLConfig *config,
++                                  const EGLint *attrib_list)
++{
++   struct dri2_egl_surface *dri2_surf;
++   _EGLSurface *surf;
++
++   surf = create_surface(dpy, config, EGL_PBUFFER_BIT, attrib_list);
++   if (!surf)
++      return NULL;
++
++   dri2_surf = dri2_egl_surface(surf);
++
++   if (config->RedSize == 5)
++      dri2_surf->visual = __DRI_IMAGE_FORMAT_RGB565;
++   else if (config->AlphaSize)
++      dri2_surf->visual = __DRI_IMAGE_FORMAT_ARGB8888;
++   else
++      dri2_surf->visual = __DRI_IMAGE_FORMAT_XRGB8888;
++
++   return surf;
++}
++
++static EGLBoolean
++dri2_tizen_destroy_surface(_EGLDisplay *dpy, _EGLSurface *surf)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
++   int i;
++
++   if (dri2_surf->swap_queue_processor) {
++      /* Wait for any outstanding swaps to complete */
++      pthread_mutex_lock(&dri2_surf->mutex);
++      while (dri2_surf->swap_queue_idx_head != dri2_surf->swap_queue_idx_tail)
++         pthread_cond_wait(&dri2_surf->swap_queue_cond, &dri2_surf->mutex);
++      pthread_mutex_unlock(&dri2_surf->mutex);
++
++      pthread_cancel(dri2_surf->swap_queue_processor);
++      pthread_join(dri2_surf->swap_queue_processor, NULL);
++   }
++
++   for (i = 0; i < ARRAY_SIZE(dri2_surf->swap_queue); i++)
++      free(dri2_surf->swap_queue[i].rects);
++
++   if (dri2_surf->front)
++      dri2_dpy->image->destroyImage(dri2_surf->front);
++
++   if (dri2_surf->back) {
++      tbm_surface_internal_unref(dri2_surf->back->tbm_surf);
++      dri2_surf->back->locked = false;
++   }
++
++   for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++      if (dri2_surf->color_buffers[i].dri_image)
++         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
++      assert(!dri2_surf->color_buffers[i].locked);
++   }
++
++   if (dri2_surf->tpl_surf)
++      tpl_object_unreference((tpl_object_t *) dri2_surf->tpl_surf);
++
++   destroy_surface(surf);
++
++   return EGL_TRUE;
++}
++
++static EGLBoolean
++dri2_tizen_swap_interval(_EGLDisplay *dpy, _EGLSurface *surf, EGLint interval)
++{
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
++   tpl_result_t res;
++
++   if (interval > surf->Config->MaxSwapInterval)
++      interval = surf->Config->MaxSwapInterval;
++   else if (interval < surf->Config->MinSwapInterval)
++      interval = surf->Config->MinSwapInterval;
++
++   if (interval == surf->SwapInterval)
++      return EGL_TRUE;
++
++   res = tpl_surface_set_post_interval(dri2_surf->tpl_surf, interval);
++   if (res == TPL_ERROR_NONE) {
++      surf->SwapInterval = interval;
++      return EGL_TRUE;
++   }
++
++   return EGL_FALSE;
++}
++
++static EGLBoolean
++dri2_tizen_swap_buffers_with_damage(_EGLDisplay *dpy, _EGLSurface *draw,
++                                    const EGLint *rects, EGLint n_rects)
++{
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
++   struct tpl_swap_queue_elem *queue_elem;
++   int i;
++
++   if (dri2_surf->base.Type != EGL_WINDOW_BIT)
++      return EGL_TRUE;
++
++   for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++      if (draw->SwapBehavior == EGL_BUFFER_PRESERVED)
++         dri2_surf->color_buffers[i].age = 1;
++      else if (dri2_surf->color_buffers[i].age > 0)
++         dri2_surf->color_buffers[i].age++;
++   }
++
++   /*
++    * Make sure we have a back buffer in case we're swapping without ever
++    * rendering.
++    */
++   if (get_back_bo(dri2_surf, false) < 0) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to get back buffer");
++      return EGL_FALSE;
++   }
++
++   dri2_flush_drawable_for_swapbuffers(dpy, draw);
++
++   pthread_mutex_lock(&dri2_surf->mutex);
++   queue_elem =
++      &dri2_surf->swap_queue[dri2_surf->swap_queue_idx_tail];
++
++   queue_elem->tbm_surf = dri2_surf->back->tbm_surf;
++
++   /* Allocate space for damage rectangles in swap queue element if necessary */
++   if (queue_elem->n_rects_max < n_rects) {
++      free(queue_elem->rects);
++
++      if (!queue_elem->n_rects_max)
++         queue_elem->n_rects_max = TIZEN_SWAP_N_RECTS_MIN;
++
++      while (queue_elem->n_rects_max <= n_rects)
++         queue_elem->n_rects_max *= 2;
++
++      queue_elem->rects =
++         malloc(sizeof(*queue_elem->rects) * queue_elem->n_rects_max);
++      if (!queue_elem->rects) {
++         _eglError(EGL_BAD_ALLOC,
++                   "DRI2: failed to allocate space for damage rects");
++         queue_elem->n_rects_max = 0;
++         pthread_mutex_unlock(&dri2_surf->mutex);
++         return EGL_FALSE;
++      }
++   }
++
++   /* Copy damage rectangles into swap queue element */
++   STATIC_ASSERT(sizeof(*queue_elem->rects) == sizeof(*rects));
++   memcpy(&queue_elem->rects[0], &rects[0], sizeof(*rects) * n_rects);
++   queue_elem->n_rects = n_rects;
++
++   dri2_surf->swap_queue_idx_tail++;
++   dri2_surf->swap_queue_idx_tail %= ARRAY_SIZE(dri2_surf->color_buffers);
++
++   /* Notify swap_queue_processor_worker that there's new work */
++   pthread_cond_signal(&dri2_surf->swap_queue_cond);
++   pthread_mutex_unlock(&dri2_surf->mutex);
++
++   dri2_surf->back->age = 1;
++   dri2_surf->current = dri2_surf->back;
++   dri2_surf->back = NULL;
++
++   return EGL_TRUE;
++}
++
++static EGLBoolean
++dri2_tizen_swap_buffers(_EGLDisplay *dpy, _EGLSurface *draw)
++{
++   return dri2_tizen_swap_buffers_with_damage(dpy, draw, NULL, 0);
++}
++
++static EGLint
++dri2_tizen_query_buffer_age(_EGLDisplay *dpy, _EGLSurface *surface)
++{
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
++
++   if (get_back_bo(dri2_surf, false) < 0) {
++      _eglError(EGL_BAD_ALLOC, "DRI2: failed to get back buffer");
++      return -1;
++   }
++
++   return dri2_surf->back->age;
++}
++
++
++static EGLBoolean
++dri2_tizen_query_surface(_EGLDisplay *dpy, _EGLSurface *surf,
++                         EGLint attribute, EGLint *value)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
++
++   switch (attribute) {
++   case EGL_WIDTH:
++   case EGL_HEIGHT:
++      if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
++         tpl_handle_t window;
++         int width, height;
++         tbm_format format;
++         tpl_result_t res;
++
++         window = tpl_surface_get_native_handle(dri2_surf->tpl_surf);
++
++         res = tpl_display_get_native_window_info(dri2_dpy->tpl_dpy, window,
++                                                  &width, &height, &format,
++                                                  surf->Config->RedSize +
++                                                  surf->Config->GreenSize +
++                                                  surf->Config->BlueSize +
++                                                  surf->Config->AlphaSize,
++                                                  surf->Config->AlphaSize);
++         if (res == TPL_ERROR_NONE) {
++            surf->Width = width;
++            surf->Height = height;
++         }
++      }
++      break;
++   default:
++      break;
++   }
++
++   return _eglQuerySurface(dpy, surf, attribute, value);
++}
++
++
++static struct dri2_egl_display_vtbl dri2_tizen_display_vtbl = {
++   .create_window_surface = dri2_tizen_create_window_surface,
++   .create_pixmap_surface = dri2_tizen_create_pixmap_surface,
++   .create_pbuffer_surface = dri2_tizen_create_pbuffer_surface,
++   .destroy_surface = dri2_tizen_destroy_surface,
++   .create_image = dri2_create_image_khr,
++   .swap_interval = dri2_tizen_swap_interval,
++   .swap_buffers = dri2_tizen_swap_buffers,
++   .swap_buffers_with_damage = dri2_tizen_swap_buffers_with_damage,
++   .query_buffer_age = dri2_tizen_query_buffer_age,
++   .query_surface = dri2_tizen_query_surface,
++   .get_dri_drawable = dri2_surface_get_dri_drawable,
++};
++
++static int
++dri2_tizen_get_buffers(__DRIdrawable *driDrawable,
++                       unsigned int format,
++                       uint32_t *stamp,
++                       void *loaderPrivate,
++                       uint32_t buffer_mask,
++                       struct __DRIimageList *buffers)
++{
++   struct dri2_egl_surface *dri2_surf = loaderPrivate;
++
++   buffers->image_mask = 0;
++   buffers->front = NULL;
++   buffers->back = NULL;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT)
++      if (get_front_bo(dri2_surf) < 0)
++         return 0;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK)
++      if (get_back_bo(dri2_surf, true) < 0)
++         return 0;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
++      buffers->front = dri2_surf->front;
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
++   }
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
++      buffers->back = dri2_surf->back->dri_image;
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
++   }
++
++   return 1;
++}
++
++static void
++dri2_tizen_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
++{
++}
++
++static const __DRIimageLoaderExtension tizen_image_loader_extension = {
++   .base             = { __DRI_IMAGE_LOADER, 1 },
++   .getBuffers       = dri2_tizen_get_buffers,
++   .flushFrontBuffer = dri2_tizen_flush_front_buffer,
++};
++
++static const __DRIextension *image_loader_extensions[] = {
++   &tizen_image_loader_extension.base,
++   &image_lookup_extension.base,
++   NULL,
++};
++
++static EGLBoolean
++dri2_tizen_add_configs(_EGLDisplay *dpy)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(dpy);
++   int count = 0;
++
++   for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) {
++      static const struct rgba_shifts_and_sizes pbuffer_sasa[] = {
++         {
++            /* ARGB8888 */
++            { 16, 8, 0, 24 },
++            { 8, 8, 8, 8 },
++         },
++         {
++            /* RGB888 */
++            { 16, 8, 0, -1 },
++            { 8, 8, 8, 0 },
++         },
++         {
++            /* RGB565 */
++            { 11, 5, 0, -1 },
++            { 5, 6, 5, 0 },
++         },
++      };
++      struct dri2_egl_config *dri2_cfg;
++      int shifts[4];
++      unsigned int sizes[4];
++      unsigned int caveat = 0;
++      int surface_type = 0;
++      tpl_bool_t is_slow;
++      EGLint attr_list[] = {
++         EGL_NATIVE_VISUAL_ID, 0,
++         EGL_CONFIG_CAVEAT, EGL_NONE,
++         EGL_NONE,
++      };
++      tpl_result_t res;
++
++      dri2_get_shifts_and_sizes(dri2_dpy->core, dri2_dpy->driver_configs[i],
++                                shifts, sizes);
++
++      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
++                                      __DRI_ATTRIB_BUFFER_SIZE, &depth);
++
++      dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
++                                      __DRI_ATTRIB_CONFIG_CAVEAT, &caveat);
++
++      res = tpl_display_query_config(dri2_dpy->tpl_dpy, TPL_SURFACE_TYPE_WINDOW,
++                                     sizes[0], sizes[1], sizes[2], sizes[3],
++                                     depth, &attr_list[1], &is_slow);
++      if (res != TPL_ERROR_NONE)
++         continue;
++      surface_type |= EGL_WINDOW_BIT;
++
++      if (is_slow)
++         caveat |= __DRI_ATTRIB_SLOW_BIT;
++
++      res = tpl_display_query_config(dri2_dpy->tpl_dpy, TPL_SURFACE_TYPE_PIXMAP,
++                                     sizes[0], sizes[1], sizes[2], sizes[3],
++                                     depth, NULL, &is_slow);
++      if (res == TPL_ERROR_NONE) {
++         surface_type |= EGL_PIXMAP_BIT;
++
++         if (is_slow)
++            caveat |= __DRI_ATTRIB_SLOW_BIT;
++      }
++
++      for (unsigned j = 0; j < ARRAY_SIZE(pbuffer_sasa); j++) {
++         const struct rgba_shifts_and_sizes *pbuffer_sas = &pbuffer_sasa[j];
++
++         if (shifts[0] == pbuffer_sas->shifts[0] &&
++             shifts[1] == pbuffer_sas->shifts[1] &&
++             shifts[2] == pbuffer_sas->shifts[2] &&
++             shifts[3] == pbuffer_sas->shifts[3] &&
++             sizes[0] == pbuffer_sas->sizes[0] &&
++             sizes[1] == pbuffer_sas->sizes[1] &&
++             sizes[2] == pbuffer_sas->sizes[2] &&
++             sizes[3] == pbuffer_sas->sizes[3]) {
++            surface_type |= EGL_PBUFFER_BIT;
++            break;
++         }
++      }
++
++      if (dri2_dpy->image->base.version >= 9 && dri2_dpy->image->blitImage)
++         surface_type |= EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
++
++      if (caveat & __DRI_ATTRIB_NON_CONFORMANT_CONFIG)
++         attr_list[3] = EGL_NON_CONFORMANT_CONFIG;
++      else if (caveat & __DRI_ATTRIB_SLOW_BIT)
++         attr_list[3] = EGL_SLOW_CONFIG;
++
++      dri2_cfg = dri2_add_config(dpy, dri2_dpy->driver_configs[i], count + 1,
++                                 surface_type, &attr_list[0], NULL, NULL);
++      if (dri2_cfg)
++         count++;
++   }
++
++   return count != 0;
++}
++
++EGLBoolean
++dri2_initialize_tizen(_EGLDisplay *dpy)
++{
++   struct dri2_egl_display *dri2_dpy;
++   int i;
++
++   dri2_dpy = calloc(1, sizeof(*dri2_dpy));
++   if (!dri2_dpy)
++      return _eglError(EGL_BAD_ALLOC, "DRI2: failed to create display");
++
++   dpy->DriverData = (void *) dri2_dpy;
++   dri2_dpy->fd = -1;
++
++   dri2_dpy->tpl_dpy =
++      tpl_display_create(TPL_BACKEND_UNKNOWN, dpy->PlatformDisplay);
++   if (!dri2_dpy->tpl_dpy) {
++      _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to create tpl display");
++      goto cleanup;
++   }
++
++   for (i = TIZEN_DRM_RENDER_MINOR_START; i <= TIZEN_DRM_RENDER_MINOR_MAX; i++) {
++      char *render_path;
++
++      if (asprintf(&render_path, DRM_RENDER_DEV_NAME, DRM_DIR_NAME, i) < 0)
++         continue;
++
++      dri2_dpy->fd = loader_open_device(render_path);
++      free(render_path);
++      if (dri2_dpy->fd < 0)
++         continue;
++
++      dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
++      if (dri2_dpy->driver_name) {
++         if (dri2_load_driver_dri3(dpy)) {
++            _EGLDevice *dev = _eglAddDevice(dri2_dpy->fd, false);
++            if (!dev) {
++               dlclose(dri2_dpy->driver);
++               _eglLog(_EGL_WARNING, "DRI2: failed to find EGLDevice");
++            } else {
++               dri2_dpy->own_device = 1;
++               dpy->Device = dev;
++               break;
++	    }
++         }
++
++         free(dri2_dpy->driver_name);
++         dri2_dpy->driver_name = NULL;
++      }
++
++      close(dri2_dpy->fd);
++      dri2_dpy->fd = -1;
++   }
++
++   if (dri2_dpy->fd < 0) {
++      _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to load driver");
++      goto cleanup;
++   }
++
++   dri2_dpy->loader_extensions = &image_loader_extensions[0];
++
++   if (!dri2_create_screen(dpy)) {
++      _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to create screen");
++      goto cleanup;
++   }
++
++   if (!dri2_setup_extensions(dpy))
++      goto cleanup;
++
++   dri2_setup_screen(dpy);
++
++   if (!dri2_tizen_add_configs(dpy)) {
++      _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to add configs");
++      goto cleanup;
++   }
++
++   dpy->Extensions.EXT_buffer_age = EGL_TRUE;
++   dpy->Extensions.EXT_swap_buffers_with_damage = EGL_TRUE;
++   dpy->Extensions.KHR_image_base = EGL_TRUE;
++   dpy->Extensions.WL_bind_wayland_display = EGL_TRUE;
++
++   /*
++    * Fill vtbl last to prevent accidentally calling virtual function during
++    * initialization.
++    */
++   dri2_dpy->vtbl = &dri2_tizen_display_vtbl;
++
++   return EGL_TRUE;
++
++cleanup:
++   dri2_display_destroy(dpy);
++   return EGL_FALSE;
++}
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 234449a..43c7b91 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -416,6 +416,13 @@ _eglGetPlatformDisplayCommon(EGLenum platform, void *native_display,
+    case EGL_PLATFORM_DEVICE_EXT:
+       disp = _eglGetDeviceDisplay(native_display, attrib_list);
+       break;
++#ifdef HAVE_TIZEN_PLATFORM
++   case EGL_PLATFORM_GBM_MESA:
++   case EGL_PLATFORM_WAYLAND_EXT:
++      disp = _eglGetTizenDisplay((struct gbm_device*) native_display,
++                                attrib_list);
++      break;
++#endif
+    default:
+       RETURN_EGL_ERROR(NULL, EGL_BAD_PARAMETER, NULL);
+    }
+diff --git a/src/egl/main/egldisplay.c b/src/egl/main/egldisplay.c
+index 765618f..486ac3b 100644
+--- a/src/egl/main/egldisplay.c
++++ b/src/egl/main/egldisplay.c
+@@ -77,6 +77,7 @@ static const struct {
+    { _EGL_PLATFORM_HAIKU, "haiku" },
+    { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
+    { _EGL_PLATFORM_DEVICE, "device" },
++   { _EGL_PLATFORM_TIZEN, "tizen" },
+ };
+ 
+ 
+@@ -107,6 +108,17 @@ _eglGetNativePlatformFromEnv(void)
+       }
+    }
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++   switch (plat) {
++   case _EGL_PLATFORM_DRM:
++   case _EGL_PLATFORM_WAYLAND:
++      plat = _EGL_PLATFORM_TIZEN;
++      break;
++   default:
++      break;
++   }
++#endif
++
+    if (plat == _EGL_INVALID_PLATFORM)
+       _eglLog(_EGL_WARNING, "invalid EGL_PLATFORM given");
+ 
+@@ -120,6 +132,7 @@ _eglGetNativePlatformFromEnv(void)
+ static _EGLPlatformType
+ _eglNativePlatformDetectNativeDisplay(void *nativeDisplay)
+ {
++#ifndef HAVE_TIZEN_PLATFORM
+    if (nativeDisplay == EGL_DEFAULT_DISPLAY)
+       return _EGL_INVALID_PLATFORM;
+ 
+@@ -141,7 +154,7 @@ _eglNativePlatformDetectNativeDisplay(void *nativeDisplay)
+          return _EGL_PLATFORM_DRM;
+ #endif
+    }
+-
++#endif
+    return _EGL_INVALID_PLATFORM;
+ }
+ 
+@@ -595,6 +608,23 @@ _eglGetAndroidDisplay(void *native_display,
+ }
+ #endif /* HAVE_ANDROID_PLATFORM */
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++_EGLDisplay*
++_eglGetTizenDisplay(void *native_display,
++                    const EGLint *attrib_list)
++{
++   /* EGL_MESA_platform_gbm and EGL_EXT_platform_wayland recognizes no
++    * attributes. */
++   if (attrib_list != NULL && attrib_list[0] != EGL_NONE) {
++      _eglError(EGL_BAD_ATTRIBUTE, "eglGetPlatformDisplay");
++      return NULL;
++   }
++
++   return _eglFindDisplay(_EGL_PLATFORM_TIZEN, native_display,
++                          attrib_list);
++}
++#endif /* HAVE_TIZEN_PLATFORM */
++
+ _EGLDisplay*
+ _eglGetDeviceDisplay(void *native_display,
+                      const EGLAttrib *attrib_list)
+diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
+index 5b05dcc..cbb0983 100644
+--- a/src/egl/main/egldisplay.h
++++ b/src/egl/main/egldisplay.h
+@@ -52,6 +52,7 @@ enum _egl_platform_type {
+    _EGL_PLATFORM_HAIKU,
+    _EGL_PLATFORM_SURFACELESS,
+    _EGL_PLATFORM_DEVICE,
++   _EGL_PLATFORM_TIZEN,
+ 
+    _EGL_NUM_PLATFORMS,
+    _EGL_INVALID_PLATFORM = -1
+@@ -329,6 +330,12 @@ _eglGetAndroidDisplay(void *native_display,
+                          const EGLAttrib *attrib_list);
+ #endif
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++_EGLDisplay*
++_eglGetTizenDisplay(void *native_display,
++                    const EGLint *attrib_list);
++#endif
++
+ _EGLDisplay*
+ _eglGetDeviceDisplay(void *native_display,
+                      const EGLAttrib *attrib_list);
+diff --git a/src/egl/main/egllog.c b/src/egl/main/egllog.c
+index 6a91952..973b760 100644
+--- a/src/egl/main/egllog.c
++++ b/src/egl/main/egllog.c
+@@ -46,15 +46,17 @@
+ 
+ #include "egllog.h"
+ 
+-#ifdef HAVE_ANDROID_PLATFORM
+ #define LOG_TAG "EGL-MAIN"
++
++#ifdef HAVE_ANDROID_PLATFORM
+ #if ANDROID_API_LEVEL >= 26
+ #include <log/log.h>
+ #else
+ #include <cutils/log.h>
+ #endif /* use log/log.h start from android 8 major version */
+-
+-#endif /* HAVE_ANDROID_PLATFORM */
++#elif defined(HAVE_TIZEN_PLATFORM)
++#include <dlog.h>
++#endif /* HAVE_TIZEN_PLATFORM */
+ 
+ #define MAXSTRING 1000
+ #define FALLBACK_LOG_LEVEL _EGL_WARNING
+@@ -93,9 +95,26 @@ _eglDefaultLogger(EGLint level, const char *msg)
+       [_EGL_DEBUG] = ANDROID_LOG_DEBUG,
+    };
+    LOG_PRI(egl2alog[level], LOG_TAG, "%s", msg);
++#elif defined(HAVE_TIZEN_PLATFORM)
++   switch (level) {
++   case _EGL_DEBUG:
++      LOGD("%s", msg);
++      break;
++   case _EGL_INFO:
++      LOGI("%s", msg);
++      break;
++   case _EGL_WARNING:
++      LOGW("%s", msg);
++      break;
++   case _EGL_FATAL:
++      LOGF("%s", msg);
++      break;
++   default:
++      break;
++   }
+ #else
+    fprintf(stderr, "libEGL %s: %s\n", level_strings[level], msg);
+-#endif /* HAVE_ANDROID_PLATFORM */
++#endif /* HAVE_TIZEN_PLATFORM */
+ }
+ 
+ 
+diff --git a/src/egl/meson.build b/src/egl/meson.build
+index ab8f4e1..5749ec8 100644
+--- a/src/egl/meson.build
++++ b/src/egl/meson.build
+@@ -106,7 +106,7 @@ if with_dri2
+     endif
+     deps_for_egl += [dep_x11_xcb, dep_xcb_dri2, dep_xcb_xfixes]
+   endif
+-  if with_gbm and not with_platform_android
++  if with_gbm and not with_platform_android and not with_platform_tizen
+     files_egl += files('drivers/dri2/platform_drm.c')
+     link_for_egl += libgbm
+     incs_for_egl += [inc_gbm, include_directories('../gbm/main')]
+@@ -141,6 +141,11 @@ elif with_platform_haiku
+   link_for_egl += libgl
+   deps_for_egl += cpp.find_library('be')
+ endif
++if with_platform_tizen
++  files_egl += files('drivers/dri2/platform_tizen.c')
++  incs_for_egl += [inc_loader]
++  deps_for_egl += [dep_tizen, dep_libdrm]
++endif
+ 
+ if cc.has_function('mincore')
+   c_args_for_egl += '-DHAVE_MINCORE'

+ 228 - 0
recipes-graphics/mesa/mesa-pvr/0019-egl-add-support-for-EGL_TIZEN_image_native_surface.patch

@@ -0,0 +1,228 @@
+From 917eca873d3c493ce552811cbe4860334e73ddc1 Mon Sep 17 00:00:00 2001
+From: Eric Engestrom <eric.engestrom@imgtec.com>
+Date: Mon, 6 Feb 2017 15:54:00 +0000
+Subject: [PATCH] egl: add support for EGL_TIZEN_image_native_surface
+
+---
+ src/egl/drivers/dri2/egl_dri2.c       | 127 ++++++++++++++++++++------
+ src/egl/drivers/dri2/platform_tizen.c |   1 +
+ src/egl/main/eglapi.c                 |   2 +
+ src/egl/main/egldisplay.h             |   2 +
+ 4 files changed, 102 insertions(+), 30 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index 6d34395..eaa0bae 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -58,6 +58,10 @@
+ #include "X11/Xlibint.h"
+ #endif
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++#include <tpl.h>
++#endif
++
+ #include "egldefines.h"
+ #include "egl_dri2.h"
+ #include "GL/mesa_glinterop.h"
+@@ -2311,28 +2315,115 @@ dri2_fourcc_from_tbm_format(tbm_format format)
+    switch (format) {
+    case TBM_FORMAT_ARGB8888:
+       return DRM_FORMAT_ARGB8888;
++   case TBM_FORMAT_ABGR8888:
++      return DRM_FORMAT_ABGR8888;
+    case TBM_FORMAT_XRGB8888:
+       return DRM_FORMAT_XRGB8888;
++   case TBM_FORMAT_XBGR8888:
++      return DRM_FORMAT_XBGR8888;
++   case TBM_FORMAT_ARGB4444:
++      return DRM_FORMAT_ARGB4444;
++   case TBM_FORMAT_ARGB1555:
++      return DRM_FORMAT_ARGB1555;
+    case TBM_FORMAT_RGB565:
+       return DRM_FORMAT_RGB565;
++   case TBM_FORMAT_YUV420:
++      return DRM_FORMAT_YUV420;
++   case TBM_FORMAT_YVU420:
++      return DRM_FORMAT_YVU420;
++   case TBM_FORMAT_NV12:
++      return DRM_FORMAT_NV12;
++   case TBM_FORMAT_NV21:
++      return DRM_FORMAT_NV21;
+    default:
+       _eglLog(_EGL_DEBUG, "%s: unsupported tbm format %#x", __func__, format);
+       return 0;
+    }
+ }
+ 
++static _EGLImage *
++dri2_create_image_tbm_surface(_EGLDisplay *disp, _EGLContext *ctx,
++                              tbm_surface_h tbm_surf)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   __DRIimage *dri_image;
++   tbm_surface_info_s info;
++   int fd[TBM_SURF_PLANE_MAX], pitch[TBM_SURF_PLANE_MAX], offset[TBM_SURF_PLANE_MAX];
++   int fourcc;
++   int i;
++
++   if (tbm_surface_get_info(tbm_surf, &info)) {
++      _eglError(EGL_BAD_PARAMETER, "tbm_surface_get_info");
++      return NULL;
++   }
++
++   fourcc = dri2_fourcc_from_tbm_format(info.format);
++   if (!fourcc) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_fourcc_from_tbm_format");
++      return NULL;
++   }
++
++   for (i = 0; i < info.num_planes; i++) {
++      tbm_bo tbm_buf;
++
++      tbm_buf = tbm_surface_internal_get_bo(tbm_surf, i);
++      if (!tbm_buf) {
++         _eglError(EGL_BAD_PARAMETER, "tbm_surface_internal_get_bo");
++         goto fail_close;
++      }
++
++      pitch[i] = info.planes[i].stride;
++      offset[i] = info.planes[i].offset;
++      fd[i] = tbm_bo_export_fd(tbm_buf);
++   }
++
++   dri_image = dri2_dpy->image->createImageFromFds(dri2_dpy->dri_screen,
++                                                   info.width,
++                                                   info.height,
++                                                   fourcc,
++                                                   &fd[0],
++                                                   info.num_planes,
++                                                   &pitch[0],
++                                                   &offset[0],
++                                                   tbm_surf);
++   for (i = 0; i < info.num_planes; i++) {
++      close(fd[i]);
++      fd[i] = -1;
++   }
++
++   if (!dri_image) {
++      _eglError(EGL_BAD_PARAMETER, "createImageFromFds");
++      return NULL;
++   }
++
++   return dri2_create_image_from_dri(disp, dri_image);
++
++fail_close:
++   while (i--)
++      close(fd[i]);
++
++   return NULL;
++}
++
++static _EGLImage *
++dri2_create_image_tizen(_EGLDisplay *disp, _EGLContext *ctx,
++                        EGLClientBuffer _buffer,
++                        const EGLint *attr_list)
++{
++   tbm_surface_h tbm_surf = (tbm_surface_h)_buffer;
++
++   return dri2_create_image_tbm_surface(disp, ctx, tbm_surf);
++}
++
+ static _EGLImage *
+ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx,
+                                           EGLClientBuffer _buffer,
+                                           const EGLint *attr_list)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+-   __DRIimage *dri_image;
+    _EGLImageAttribs attrs;
+    tbm_surface_h tbm_surf;
+-   tbm_bo tbm_buf;
+    tbm_surface_info_s info;
+-   int fourcc, fd, pitch, offset;
+ 
+    tbm_surf = tpl_display_get_buffer_from_native_pixmap(dri2_dpy->tpl_dpy,
+                                                         (tpl_handle_t) _buffer);
+@@ -2363,33 +2454,7 @@ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx,
+       return NULL;
+    }
+ 
+-   tbm_buf = tbm_surface_internal_get_bo(tbm_surf, attrs.PlaneWL);
+-   if (!tbm_buf) {
+-      _eglError(EGL_BAD_PARAMETER, "tbm_surface_internal_get_bo");
+-      return NULL;
+-   }
+-
+-   fourcc = dri2_fourcc_from_tbm_format(info.format);
+-   pitch = info.planes[attrs.PlaneWL].stride;
+-   offset = info.planes[attrs.PlaneWL].offset;
+-   fd = tbm_bo_export_fd(tbm_buf);
+-
+-   dri_image = dri2_dpy->image->createImageFromFds(dri2_dpy->dri_screen,
+-                                                   info.width,
+-                                                   info.height,
+-                                                   fourcc,
+-                                                   &fd,
+-                                                   1,
+-                                                   &pitch,
+-                                                   &offset,
+-                                                   tbm_surf);
+-   close(fd);
+-   if (dri_image == NULL) {
+-      _eglError(EGL_BAD_PARAMETER, "createImageFromFds");
+-      return NULL;
+-   }
+-
+-   return dri2_create_image_from_dri(disp, dri_image);
++   return dri2_create_image_tbm_surface(disp, ctx, tbm_surf);
+ }
+ #endif
+ 
+@@ -3296,6 +3361,8 @@ dri2_create_image_khr(_EGLDisplay *disp, _EGLContext *ctx, EGLenum target,
+ #ifdef HAVE_TIZEN_PLATFORM
+    case EGL_WAYLAND_BUFFER_WL:
+       return dri2_create_image_wayland_wl_buffer_tizen(disp, ctx, buffer, attr_list);
++   case EGL_NATIVE_SURFACE_TIZEN:
++      return dri2_create_image_tizen(disp, ctx, buffer, attr_list);
+ #endif
+    case EGL_CL_IMAGE_IMG:
+       return dri2_create_image_img_buffer(disp, ctx, target, buffer, attr_list);
+diff --git a/src/egl/drivers/dri2/platform_tizen.c b/src/egl/drivers/dri2/platform_tizen.c
+index a08bc8c..ad75c11 100644
+--- a/src/egl/drivers/dri2/platform_tizen.c
++++ b/src/egl/drivers/dri2/platform_tizen.c
+@@ -965,6 +965,7 @@ dri2_initialize_tizen(_EGLDisplay *dpy)
+    dpy->Extensions.EXT_swap_buffers_with_damage = EGL_TRUE;
+    dpy->Extensions.KHR_image_base = EGL_TRUE;
+    dpy->Extensions.WL_bind_wayland_display = EGL_TRUE;
++   dpy->Extensions.TIZEN_image_native_surface = EGL_TRUE;
+ 
+    /*
+     * Fill vtbl last to prevent accidentally calling virtual function during
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 43c7b91..e2a7797 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -553,6 +553,8 @@ _eglCreateExtensionsString(_EGLDisplay *disp)
+ 
+    _EGL_CHECK_EXTENSION(NV_post_sub_buffer);
+ 
++   _EGL_CHECK_EXTENSION(TIZEN_image_native_surface);
++
+    _EGL_CHECK_EXTENSION(WL_bind_wayland_display);
+    _EGL_CHECK_EXTENSION(WL_create_wayland_buffer_from_image);
+ 
+diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
+index cbb0983..0a7366b 100644
+--- a/src/egl/main/egldisplay.h
++++ b/src/egl/main/egldisplay.h
+@@ -149,6 +149,8 @@ struct _egl_extensions
+ 
+    EGLBoolean NV_post_sub_buffer;
+ 
++   EGLBoolean TIZEN_image_native_surface;
++
+    EGLBoolean WL_bind_wayland_display;
+    EGLBoolean WL_create_wayland_buffer_from_image;
+ 

+ 31 - 0
recipes-graphics/mesa/mesa-pvr/0020-egl-wayland-post-maximum-damage-when-blitting.patch

@@ -0,0 +1,31 @@
+From 047016425214067e3cee93ace2d64018fa13c2ce Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Wed, 8 Nov 2017 15:15:20 +0000
+Subject: [PATCH] egl/wayland: post maximum damage when blitting
+
+When blitting, as part of the "is_different_gpu" case when swapping
+buffers, the blit is done using the full surface dimensions, ignoring
+the damage region. This results in corruption of the non damaged region
+on screen. Workaround this by posting maximum damage when blitting.
+
+A better fix would be to limit the blit to the damaged region, at least
+when the number of damage rectangles is 1.
+
+---
+ src/egl/drivers/dri2/platform_wayland.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index c18edc0..c2e4287 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -1112,7 +1112,8 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+    /* If the compositor doesn't support damage_buffer, we deliberately
+     * ignore the damage region and post maximum damage, due to
+     * https://bugs.freedesktop.org/78190 */
+-   if (!n_rects || !try_damage_buffer(dri2_surf, rects, n_rects))
++   if (dri2_dpy->is_different_gpu ||
++       !n_rects || !try_damage_buffer(dri2_surf, rects, n_rects))
+       wl_surface_damage(dri2_surf->wl_surface_wrapper,
+                         0, 0, INT32_MAX, INT32_MAX);
+ 

+ 44 - 0
recipes-graphics/mesa/mesa-pvr/0021-egl-wayland-flush-the-drawable-before-blitting.patch

@@ -0,0 +1,44 @@
+From dc5816cc975e908bc213176fc1fccfb634c18c0a Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Wed, 8 Nov 2017 15:26:25 +0000
+Subject: [PATCH] egl/wayland: flush the drawable before blitting
+
+Flush the drawable before blitting in the "is_different_gpu" case when
+swapping buffers, and pass the flush flag to the blitImage call. The
+change brings the code into line with the way the DRI3 GLX code works.
+
+For the PowerVR driver, the drawable parameters that will be used in
+the flush have been obtained previously, including any native fence
+associated with the blit source. The blit will result in a new native
+fence for the source, and make the one in the drawable parameters
+invalid.
+
+---
+ src/egl/drivers/dri2/platform_wayland.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index c2e4287..811d28b 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -1117,6 +1117,8 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+       wl_surface_damage(dri2_surf->wl_surface_wrapper,
+                         0, 0, INT32_MAX, INT32_MAX);
+ 
++   dri2_flush_drawable_for_swapbuffers(disp, draw);
++
+    if (dri2_dpy->is_different_gpu) {
+       _EGLContext *ctx = _eglGetCurrentContext();
+       struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+@@ -1126,10 +1128,9 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+                                  0, 0, dri2_surf->base.Width,
+                                  dri2_surf->base.Height,
+                                  0, 0, dri2_surf->base.Width,
+-                                 dri2_surf->base.Height, 0);
++                                 dri2_surf->base.Height, __BLIT_FLAG_FLUSH);
+    }
+ 
+-   dri2_flush_drawable_for_swapbuffers(disp, draw);
+    dri2_dpy->flush->invalidate(dri2_surf->dri_drawable);
+ 
+    wl_surface_commit(dri2_surf->wl_surface_wrapper);

+ 377 - 0
recipes-graphics/mesa/mesa-pvr/0022-egl-tizen-create-an-internal-_EGLImage-for-each-tbm-.patch

@@ -0,0 +1,377 @@
+From 6d364d64fb28069a0ce9bab65a6f12f873707eda Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Wed, 25 Oct 2017 18:15:00 +0100
+Subject: [PATCH] egl/tizen: create an internal _EGLImage for each tbm surface
+
+Create an internal _EGLImage the first time a tbm surface is seen
+by eglCreateImageKHR (with either the EGL_WAYLAND_BUFFER_WL or
+EGL_NATIVE_SURFACE_TIZEN target) and return a copy of it to the
+caller. This avoids some tbm surfaces being frequently mapped and
+then unmapped from the GPU.
+
+---
+ src/egl/drivers/dri2/egl_dri2.c       | 236 +++++++++++++++++++++++++-
+ src/egl/drivers/dri2/egl_dri2.h       |  12 ++
+ src/egl/drivers/dri2/platform_tizen.c |   6 +
+ 3 files changed, 251 insertions(+), 3 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index eaa0bae..db993f8 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1264,6 +1264,10 @@ dri2_display_destroy(_EGLDisplay *disp)
+       break;
+ #ifdef HAVE_TIZEN_PLATFORM
+    case _EGL_PLATFORM_TIZEN:
++      if (dri2_dpy->image_list_mutex_initialized) {
++         pthread_mutex_destroy(&dri2_dpy->image_list_mutex);
++         dri2_dpy->image_list_mutex_initialized = false;
++      }
+       if (dri2_dpy->tpl_dpy)
+          tpl_object_unreference((tpl_object_t *) dri2_dpy->tpl_dpy);
+       break;
+@@ -1320,6 +1324,60 @@ dri2_egl_surface_free_local_buffers(struct dri2_egl_surface *dri2_surf)
+    }
+ }
+ 
++static void
++dri2_display_release_resources_tizen(_EGLDisplay *disp)
++{
++#ifdef HAVE_TIZEN_PLATFORM
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   _EGLResource *image_elem;
++
++   /* Destroy _EGLImages in the image_list */
++   pthread_mutex_lock(&dri2_dpy->image_list_mutex);
++   image_elem = dri2_dpy->image_list;
++   dri2_dpy->image_list = NULL;
++   pthread_mutex_unlock(&dri2_dpy->image_list_mutex);
++
++   while (image_elem) {
++      _EGLImage *img = (_EGLImage *) image_elem;
++      struct dri2_egl_image *dri2_img = dri2_egl_image(img);
++
++      image_elem = image_elem->Next;
++
++      /*
++       * Delete the tbm surface user data (_EGLImage), unless the tbm surface
++       * was destroyed since we started processing the image_list.
++       */
++      if (dri2_img->tbm_surf)
++         tbm_surface_internal_delete_user_data(dri2_img->tbm_surf,
++                                               (unsigned long) disp);
++
++      /*
++       * dri2_orphan_tbm_surf_egl_image won't be able to transfer the
++       * _EGLImage to the orphan_image_list once we have started processing
++       * the image_list so we must destroy it ourselves.
++       */
++      dri2_dpy->image->destroyImage(dri2_img->dri_image);
++      free(dri2_img);
++   }
++
++   /* Destroy _EGLimages  in the orphan_image_list*/
++   pthread_mutex_lock(&dri2_dpy->image_list_mutex);
++   image_elem = dri2_dpy->orphan_image_list;
++   dri2_dpy->orphan_image_list = NULL;
++   pthread_mutex_unlock(&dri2_dpy->image_list_mutex);
++
++   while (image_elem) {
++      _EGLImage *img = (_EGLImage *) image_elem;
++      struct dri2_egl_image *dri2_img = dri2_egl_image(img);
++
++      image_elem = image_elem->Next;
++
++       dri2_dpy->image->destroyImage(dri2_img->dri_image);
++       free(dri2_img);
++   }
++#endif
++}
++
+ /**
+  * Called via eglTerminate(), drv->Terminate().
+  *
+@@ -1332,6 +1390,8 @@ dri2_terminate(_EGLDisplay *disp)
+    /* Release all non-current Context/Surfaces. */
+    _eglReleaseDisplayResources(disp);
+ 
++   dri2_display_release_resources_tizen(disp);
++
+    dri2_display_release(disp);
+ 
+    return EGL_TRUE;
+@@ -2309,6 +2369,9 @@ dri2_create_image_khr_renderbuffer(_EGLDisplay *disp, _EGLContext *ctx,
+ }
+ 
+ #ifdef HAVE_TIZEN_PLATFORM
++static EGLBoolean
++dri2_destroy_image_khr(_EGLDisplay *disp, _EGLImage *image);
++
+ int
+ dri2_fourcc_from_tbm_format(tbm_format format)
+ {
+@@ -2405,14 +2468,145 @@ fail_close:
+    return NULL;
+ }
+ 
++static void
++dri2_orphan_tbm_surf_egl_image(void *user_data)
++{
++   _EGLImage *img = user_data;
++   struct dri2_egl_image *dri2_img = dri2_egl_image(img);
++   _EGLDisplay *disp = img->Resource.Display;
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   /*
++    * Transfer the passed in _EGLImage from the image_list to the
++    * orphan_image_list so that it can be cleaned up at some later
++    * point. This is necessary as the disp->Mutex needs to be held
++    * in order destroy the _EGLImage and this could potentially cause
++    * a deadlock in the event that the tbm surface is destroyed.
++    */
++   pthread_mutex_lock(&dri2_dpy->image_list_mutex);
++   /*
++    * This may be NULL if called via dri2_terminate or if dri2_terminate is
++    * running in another thread.
++    */
++   if (dri2_dpy->image_list) {
++      _EGLResource *image_elem;
++
++      /* Remove the _EGLImage from the image_list */
++      image_elem = dri2_dpy->image_list;
++      if (image_elem != &img->Resource) {
++         while (image_elem) {
++            if (image_elem->Next == &img->Resource)
++               break;
++            image_elem = image_elem->Next;
++         }
++         image_elem->Next = img->Resource.Next;
++      } else {
++         dri2_dpy->image_list = img->Resource.Next;
++      }
++
++      /* Add the _EGLImage to the orphan_image_list */
++      img->Resource.Next = dri2_dpy->orphan_image_list;
++      dri2_dpy->orphan_image_list = &img->Resource;
++   }
++
++   /*
++    * This function may have been called via tbm_surface_destroy so tbm_surf
++    * may be invalid after this function returns.
++    */
++   dri2_img->tbm_surf = NULL;
++   pthread_mutex_unlock(&dri2_dpy->image_list_mutex);
++}
++
++static _EGLImage *
++dri2_get_tbm_surf_egl_image(_EGLDisplay *disp, _EGLContext *ctx,
++                            tbm_surface_h tbm_surf)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_image *dri2_img;
++   _EGLImage *img;
++   int ret;
++
++   ret = tbm_surface_internal_get_user_data(tbm_surf, (unsigned long) disp,
++                                            (void **) &img);
++   if (ret)
++      return img;
++
++   img = dri2_create_image_tbm_surface(disp, ctx, tbm_surf);
++   if (!img) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_tbm_surface failed");
++      goto fail_exit;
++   }
++   dri2_img = dri2_egl_image(img);
++
++   /* Use 'disp' as the 'key' as the _EGLimage is tied to it */
++   ret = tbm_surface_internal_add_user_data(tbm_surf, (unsigned long) disp,
++                                            dri2_orphan_tbm_surf_egl_image);
++   if (!ret) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_get_tbm_surf_egl_image");
++      goto fail_destroy_image;
++   }
++
++   ret = tbm_surface_internal_set_user_data(tbm_surf, (unsigned long) disp, img);
++   if (!ret) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_get_tbm_surf_egl_image");
++      goto fail_delete_user_data;
++   }
++
++   /*
++    * Store the tbm surface so that the user data (_EGLImage) can be destroyed
++    * in dri2_terminate. We don't take a reference on the surface as this would
++    * prevent it from being destroyed until eglTerminate is called. This isn't
++    * an issue since it will be set to NULL, via dri2_orphan_tbm_surf_egl_image,
++    * once the surface is destroyed (although it's actually safe to call tbm
++    * surface functions with stale/NULL pointers).
++    */
++   dri2_img->tbm_surf = tbm_surf;
++
++   /*
++    * Add to the list of _EGLImages that are associated with tbm surfaces.
++    * This allows the _EGLImage to be destroyed if the application calls
++    * eglTerminate before the tbm surface is destroyed.
++    */
++   pthread_mutex_lock(&dri2_dpy->image_list_mutex);
++   img->Resource.Next = dri2_dpy->image_list;
++   dri2_dpy->image_list = &img->Resource;
++   pthread_mutex_unlock(&dri2_dpy->image_list_mutex);
++
++   return img;
++
++fail_delete_user_data:
++   tbm_surface_internal_delete_user_data(tbm_surf, (unsigned long) disp);
++fail_destroy_image:
++   dri2_destroy_image_khr(disp, img);
++fail_exit:
++   return NULL;
++}
++
+ static _EGLImage *
+ dri2_create_image_tizen(_EGLDisplay *disp, _EGLContext *ctx,
+                         EGLClientBuffer _buffer,
+                         const EGLint *attr_list)
+ {
+-   tbm_surface_h tbm_surf = (tbm_surface_h)_buffer;
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   tbm_surface_h tbm_surf = (tbm_surface_h) _buffer;
++   _EGLImage *img;
++   struct dri2_egl_image *dri2_img;
++   __DRIimage *dri_image;
++
++   img = dri2_get_tbm_surf_egl_image(disp, ctx, tbm_surf);
++   if (!img) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_tizen");
++      return NULL;
++   }
++   dri2_img = dri2_egl_image(img);
++
++   dri_image = dri2_dpy->image->fromPlanar(dri2_img->dri_image, 0, NULL);
++   if (!dri_image) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_tizen");
++      return NULL;
++   }
+ 
+-   return dri2_create_image_tbm_surface(disp, ctx, tbm_surf);
++   return dri2_create_image_from_dri(disp, dri_image);
+ }
+ 
+ static _EGLImage *
+@@ -2424,6 +2618,9 @@ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx,
+    _EGLImageAttribs attrs;
+    tbm_surface_h tbm_surf;
+    tbm_surface_info_s info;
++   _EGLImage *img;
++   struct dri2_egl_image *dri2_img;
++   __DRIimage *dri_image;
+ 
+    tbm_surf = tpl_display_get_buffer_from_native_pixmap(dri2_dpy->tpl_dpy,
+                                                         (tpl_handle_t) _buffer);
+@@ -2454,7 +2651,21 @@ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx,
+       return NULL;
+    }
+ 
+-   return dri2_create_image_tbm_surface(disp, ctx, tbm_surf);
++   img = dri2_get_tbm_surf_egl_image(disp, ctx, tbm_surf);
++   if (!img) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer_tizen");
++      return NULL;
++   }
++   dri2_img = dri2_egl_image(img);
++
++   dri_image =
++      dri2_dpy->image->fromPlanar(dri2_img->dri_image, attrs.PlaneWL, NULL);
++   if (!dri_image) {
++      _eglError(EGL_BAD_PARAMETER, "dri2_create_image_wayland_wl_buffer_tizen");
++      return NULL;
++   }
++
++   return dri2_create_image_from_dri(disp, dri_image);
+ }
+ #endif
+ 
+@@ -3377,10 +3588,29 @@ dri2_destroy_image_khr(_EGLDisplay *disp, _EGLImage *image)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    struct dri2_egl_image *dri2_img = dri2_egl_image(image);
++#ifdef HAVE_TIZEN_PLATFORM
++   _EGLResource *image_elem;
++#endif
+ 
+    dri2_dpy->image->destroyImage(dri2_img->dri_image);
+    free(dri2_img);
+ 
++#ifdef HAVE_TIZEN_PLATFORM
++   /* Take the opportunity to destroy orphaned tbm surface _EGLImages */
++   pthread_mutex_lock(&dri2_dpy->image_list_mutex);
++   image_elem = dri2_dpy->orphan_image_list;
++   dri2_dpy->orphan_image_list = NULL;
++   pthread_mutex_unlock(&dri2_dpy->image_list_mutex);
++
++   while (image_elem) {
++      dri2_img = dri2_egl_image((_EGLImage *) image_elem);
++      image_elem = image_elem->Next;
++
++      dri2_dpy->image->destroyImage(dri2_img->dri_image);
++      free(dri2_img);
++   }
++#endif
++
+    return EGL_TRUE;
+ }
+ 
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index a8bfe45..546bc0a 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -268,6 +268,15 @@ struct dri2_egl_display
+ 
+ #ifdef HAVE_TIZEN_PLATFORM
+    tpl_display_t            *tpl_dpy;
++   /*
++    * The image_list_mutex protects the image_list and orphan_image_list. It
++    * should never be held while calling a libtbm function as this may result
++    * in a deadlock.
++    */
++   pthread_mutex_t           image_list_mutex;
++   bool                      image_list_mutex_initialized;
++   _EGLResource             *image_list;
++   _EGLResource             *orphan_image_list;
+ #endif
+ };
+ 
+@@ -413,6 +422,9 @@ struct dri2_egl_image
+ {
+    _EGLImage   base;
+    __DRIimage *dri_image;
++#ifdef HAVE_TIZEN_PLATFORM
++   tbm_surface_h tbm_surf;
++#endif
+ };
+ 
+ struct dri2_egl_sync {
+diff --git a/src/egl/drivers/dri2/platform_tizen.c b/src/egl/drivers/dri2/platform_tizen.c
+index ad75c11..4946215 100644
+--- a/src/egl/drivers/dri2/platform_tizen.c
++++ b/src/egl/drivers/dri2/platform_tizen.c
+@@ -891,6 +891,7 @@ dri2_initialize_tizen(_EGLDisplay *dpy)
+ {
+    struct dri2_egl_display *dri2_dpy;
+    int i;
++   int err;
+ 
+    dri2_dpy = calloc(1, sizeof(*dri2_dpy));
+    if (!dri2_dpy)
+@@ -906,6 +907,11 @@ dri2_initialize_tizen(_EGLDisplay *dpy)
+       goto cleanup;
+    }
+ 
++   err = pthread_mutex_init(&dri2_dpy->image_list_mutex, NULL);
++   if (err)
++      goto cleanup;
++   dri2_dpy->image_list_mutex_initialized = true;
++
+    for (i = TIZEN_DRM_RENDER_MINOR_START; i <= TIZEN_DRM_RENDER_MINOR_MAX; i++) {
+       char *render_path;
+ 

+ 52 - 0
recipes-graphics/mesa/mesa-pvr/0023-dri-use-a-supported-API-in-driCreateNewContext.patch

@@ -0,0 +1,52 @@
+From bad7401b26d84646293aef242c710af5f2addf06 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 23 Nov 2017 15:50:21 +0000
+Subject: [PATCH] dri: use a supported API in driCreateNewContext
+
+Don't assume the screen supports OpenGL when creating a new context,
+use an API that the screen supports.
+
+---
+ src/mesa/drivers/dri/common/dri_util.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/src/mesa/drivers/dri/common/dri_util.c b/src/mesa/drivers/dri/common/dri_util.c
+index caed5fa..6c45eb0 100644
+--- a/src/mesa/drivers/dri/common/dri_util.c
++++ b/src/mesa/drivers/dri/common/dri_util.c
+@@ -50,6 +50,7 @@
+ #include "main/debug_output.h"
+ #include "main/errors.h"
+ #include "main/macros.h"
++#include "util/bitscan.h"
+ 
+ driOptionDescription __dri2ConfigOptions[] = {
+       DRI_CONF_SECTION_DEBUG
+@@ -332,7 +333,11 @@ driCreateContextAttribs(__DRIscreen *screen, int api,
+ 	mesa_api = API_OPENGLES;
+ 	break;
+     case __DRI_API_GLES2:
++	ctx_config.major_version = 2;
++	mesa_api = API_OPENGLES2;
++	break;
+     case __DRI_API_GLES3:
++	ctx_config.major_version = 3;
+ 	mesa_api = API_OPENGLES2;
+ 	break;
+     case __DRI_API_OPENGL_CORE:
+@@ -515,7 +520,14 @@ static __DRIcontext *
+ driCreateNewContext(__DRIscreen *screen, const __DRIconfig *config,
+                     __DRIcontext *shared, void *data)
+ {
+-    return driCreateNewContextForAPI(screen, __DRI_API_OPENGL,
++    int apifs;
++
++    apifs = ffs(screen->api_mask);
++
++    if (!apifs)
++        return NULL;
++
++    return driCreateNewContextForAPI(screen, apifs - 1,
+                                      config, shared, data);
+ }
+ 

+ 222 - 0
recipes-graphics/mesa/mesa-pvr/0024-gbm-add-gbm_bo_blit.patch

@@ -0,0 +1,222 @@
+From 689befeb2523bc1244efda8de8585e079de47e6f Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Tue, 28 Nov 2017 16:27:38 +0000
+Subject: [PATCH] gbm: add gbm_bo_blit
+
+For the GBM DRI backend, gbm_bo_blit is a wrapper around blitImage in
+the DRI Image extension.
+
+---
+ src/gbm/backends/dri/gbm_dri.c | 33 +++++++++++++++++++++++++++++++++
+ src/gbm/main/gbm.c             | 31 +++++++++++++++++++++++++++++++
+ src/gbm/main/gbm.h             | 21 +++++++++++++++++++++
+ src/gbm/main/gbm_abi_check.c   | 20 +++++++++++++++++++-
+ src/gbm/main/gbm_backend_abi.h | 10 +++++++++-
+ 5 files changed, 113 insertions(+), 2 deletions(-)
+
+diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
+index 96fa921..bd8a80c 100644
+--- a/src/gbm/backends/dri/gbm_dri.c
++++ b/src/gbm/backends/dri/gbm_dri.c
+@@ -1355,6 +1355,37 @@ gbm_dri_surface_destroy(struct gbm_surface *_surf)
+    free(surf);
+ }
+ 
++static int
++gbm_dri_bo_blit(struct gbm_bo *_dst_bo, struct gbm_bo *_src_bo,
++                int dst_x0, int dst_y0, int dst_width, int dst_height,
++                int src_x0, int src_y0, int src_width, int src_height,
++                enum gbm_blit_flags flags)
++{
++   struct gbm_dri_device *dri = gbm_dri_device(_dst_bo->gbm);
++   struct gbm_dri_bo *dst_bo = gbm_dri_bo(_dst_bo);
++   struct gbm_dri_bo *src_bo = gbm_dri_bo(_src_bo);
++
++   if (!dri->image || dri->image->base.version < 9 || !dri->image->blitImage) {
++      errno = ENOSYS;
++      return 0;
++   }
++
++   mtx_lock(&dri->mutex);
++   if (!dri->context)
++      dri->context = dri->dri2->createNewContext(dri->screen, NULL,
++                                                 NULL, NULL);
++   assert(dri->context);
++   mtx_unlock(&dri->mutex);
++
++   /* GBM flags and DRI flags are the same, so just pass them on */
++   dri->image->blitImage(dri->context, dst_bo->image, src_bo->image,
++                         dst_x0, dst_y0, dst_width, dst_height,
++                         src_x0, src_y0, src_width, src_height,
++                         flags);
++
++   return 1;
++}
++
+ static void
+ dri_destroy(struct gbm_device *gbm)
+ {
+@@ -1416,6 +1447,8 @@ dri_device_create(int fd, uint32_t gbm_backend_version)
+ 
+    dri->base.v0.name = "drm";
+ 
++   dri->base.v1.bo_blit = gbm_dri_bo_blit;
++
+    dri->visual_table = gbm_dri_visuals_table;
+    dri->num_visuals = ARRAY_SIZE(gbm_dri_visuals_table);
+ 
+diff --git a/src/gbm/main/gbm.c b/src/gbm/main/gbm.c
+index d81931a..e2351d3 100644
+--- a/src/gbm/main/gbm.c
++++ b/src/gbm/main/gbm.c
+@@ -758,6 +758,37 @@ gbm_format_get_name(uint32_t gbm_format, struct gbm_format_name_desc *desc)
+    return desc->name;
+ }
+ 
++/**
++ * Blit from one buffer object to another
++ *
++ * \param dst_bo The destination buffer object
++ * \param src_bo The source buffer object
++ * \param dst_x0 The X coordinate (top left origin) of the destination rectangle
++ * \param dst_y0 The Y coordinate (top left origin) of the destination rectangle
++ * \param dst_width The width of the destination rectangle
++ * \param dst_height The height of the destination rectangle
++ * \param src_x0 The X coordinate (top left origin) of the source rectangle
++ * \param src_y0 The Y coordinate (top left origin) of the source rectangle
++ * \param src_width The width of the source rectangle
++ * \param src_height The height of the source rectangle
++ * \param flags The flags for the blit
++ * \return 1 on success, 0 otherwise
++ */
++GBM_EXPORT int
++gbm_bo_blit(struct gbm_bo *dst_bo, struct gbm_bo *src_bo,
++            int dst_x0, int dst_y0, int dst_width, int dst_height,
++            int src_x0, int src_y0, int src_width, int src_height,
++            enum gbm_blit_flags flags)
++{
++   if (dst_bo->gbm->v0.backend_version >= 1)
++      return dst_bo->gbm->v1.bo_blit(dst_bo, src_bo,
++                                     dst_x0, dst_y0, dst_width, dst_height,
++                                     src_x0, src_y0, src_width, src_height,
++                                     flags);
++   else
++      return 0;
++}
++
+ /**
+  * A global table of functions and global variables defined in the core GBM
+  * code that need to be accessed directly by GBM backends.
+diff --git a/src/gbm/main/gbm.h b/src/gbm/main/gbm.h
+index 3a0fe73..7c82cd6 100644
+--- a/src/gbm/main/gbm.h
++++ b/src/gbm/main/gbm.h
+@@ -246,6 +246,21 @@ enum gbm_bo_flags {
+    GBM_BO_USE_PROTECTED = (1 << 5),
+ };
+ 
++/**
++ * Flags to control the behaviour of a blit - these are passed to
++ * gbm_bo_blit().
++ */
++enum gbm_blit_flags {
++   /**
++    * Force blit execution in finite time
++    */
++   GBM_BLIT_FLAG_FLUSH  = 0x0001,
++   /**
++    * Flush, and wait for the blit to complete
++    */
++   GBM_BLIT_FLAG_FINISH = 0x0002
++};
++
+ int
+ gbm_device_get_fd(struct gbm_device *gbm);
+ 
+@@ -425,6 +440,12 @@ gbm_surface_destroy(struct gbm_surface *surface);
+ char *
+ gbm_format_get_name(uint32_t gbm_format, struct gbm_format_name_desc *desc);
+ 
++int
++gbm_bo_blit(struct gbm_bo *dst_bo, struct gbm_bo *src_bo,
++            int dst_x0, int dst_y0, int dst_width, int dst_height,
++            int src_x0, int src_y0, int src_width, int src_height,
++            enum gbm_blit_flags flags);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/src/gbm/main/gbm_abi_check.c b/src/gbm/main/gbm_abi_check.c
+index f1137be..02ce23b 100644
+--- a/src/gbm/main/gbm_abi_check.c
++++ b/src/gbm/main/gbm_abi_check.c
+@@ -101,6 +101,21 @@ struct gbm_device_abi0 {
+    struct gbm_device_v0_abi0 v0;
+ };
+ 
++#define GBM_BACKEND_ABI_VERSION_abi1 1
++struct gbm_device_v1_abi1 {
++   int (*bo_blit)(struct gbm_bo *dst_bo, struct gbm_bo *src_bo,
++                  int dst_x0, int dst_y0, int dst_width, int dst_height,
++                  int src_x0, int src_y0, int src_width, int src_height,
++                  enum gbm_blit_flags flags);
++};
++
++struct gbm_device_abi1 {
++   /* Hack to make a gbm_device detectable by its first element. */
++   struct gbm_device *(*dummy)(int);
++   struct gbm_device_v0_abi0 v0;
++   struct gbm_device_v1_abi1 v1;
++};
++
+ /**
+  * GBM buffer object interface corresponding to GBM_BACKEND_ABI_VERSION = 0
+  *
+@@ -359,8 +374,11 @@ int main(int argc, char **argv)
+    CHECK_MEMBER_CURRENT(gbm_device_v0, _abi0, surface_has_free_buffers);
+    CHECK_MEMBER_CURRENT(gbm_device_v0, _abi0, surface_destroy);
+ 
++   CHECK_MEMBER_CURRENT(gbm_device_v1, _abi1, bo_blit);
++
+    /* Size of ABI-versioned substructures verified by above member checks */
+-   CHECK_SIZE_CURRENT  (gbm_device, _abi0);
++   CHECK_SIZE          (gbm_device, _abi0, _abi1);
++   CHECK_SIZE_CURRENT  (gbm_device, _abi1);
+ 
+ 
+    /* Check current gbm_bo ABI against gbm_bo_abi0*/
+diff --git a/src/gbm/main/gbm_backend_abi.h b/src/gbm/main/gbm_backend_abi.h
+index 962ee74..3abf29f 100644
+--- a/src/gbm/main/gbm_backend_abi.h
++++ b/src/gbm/main/gbm_backend_abi.h
+@@ -72,7 +72,7 @@ struct gbm_backend_desc;
+  * Core ABI version: 4
+  * ABI version of a buffer object created by a device from the backend: 4
+  */
+-#define GBM_BACKEND_ABI_VERSION 0
++#define GBM_BACKEND_ABI_VERSION 1
+ 
+ /**
+  * GBM device interface corresponding to GBM_BACKEND_ABI_VERSION = 0
+@@ -149,6 +149,13 @@ struct gbm_device_v0 {
+    void (*surface_destroy)(struct gbm_surface *surface);
+ };
+ 
++struct gbm_device_v1 {
++   int (*bo_blit)(struct gbm_bo *dst_bo, struct gbm_bo *src_bo,
++                  int dst_x0, int dst_y0, int dst_width, int dst_height,
++                  int src_x0, int src_y0, int src_width, int src_height,
++                  enum gbm_blit_flags flags);
++};
++
+ /**
+  * The device used for the memory allocation.
+  *
+@@ -161,6 +168,7 @@ struct gbm_device {
+    /* Hack to make a gbm_device detectable by its first element. */
+    struct gbm_device *(*dummy)(int);
+    struct gbm_device_v0 v0;
++   struct gbm_device_v1 v1;
+ };
+ 
+ /**

+ 42 - 0
recipes-graphics/mesa/mesa-pvr/0025-gbm-don-t-assert-if-DRI-context-creation-fails.patch

@@ -0,0 +1,42 @@
+From 799e67eeeb7d6984df65fa17ce9374416de5f61c Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Fri, 1 Dec 2017 08:31:15 +0000
+Subject: [PATCH] gbm: don't assert if DRI context creation fails
+
+If the DRI backend fails to create a DRI context, return an error,
+rather than asserting.
+
+---
+ src/gbm/backends/dri/gbm_dri.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
+index bd8a80c..2aa9c7c 100644
+--- a/src/gbm/backends/dri/gbm_dri.c
++++ b/src/gbm/backends/dri/gbm_dri.c
+@@ -1245,8 +1245,11 @@ gbm_dri_bo_map(struct gbm_bo *_bo,
+    if (!dri->context)
+       dri->context = dri->dri2->createNewContext(dri->screen, NULL,
+                                                  NULL, NULL);
+-   assert(dri->context);
+    mtx_unlock(&dri->mutex);
++   if (!dri->context) {
++      errno = ENOSYS;
++      return NULL;
++   }
+ 
+    /* GBM flags and DRI flags are the same, so just pass them on */
+    return dri->image->mapImage(dri->context, bo->image, x, y,
+@@ -1374,8 +1377,11 @@ gbm_dri_bo_blit(struct gbm_bo *_dst_bo, struct gbm_bo *_src_bo,
+    if (!dri->context)
+       dri->context = dri->dri2->createNewContext(dri->screen, NULL,
+                                                  NULL, NULL);
+-   assert(dri->context);
+    mtx_unlock(&dri->mutex);
++   if (!dri->context) {
++      errno = ENOSYS;
++      return 0;
++   }
+ 
+    /* GBM flags and DRI flags are the same, so just pass them on */
+    dri->image->blitImage(dri->context, dst_bo->image, src_bo->image,

+ 469 - 0
recipes-graphics/mesa/mesa-pvr/0026-egl-wayland-add-pbuffer-support.patch

@@ -0,0 +1,469 @@
+From 965b7a6be10d89e8cf3bfcf6737a7b9e3375e394 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Fri, 17 Mar 2017 16:23:07 +0000
+Subject: [PATCH] egl/wayland: add pbuffer support
+
+The pbuffer code is based on that in the Surfaceless platform code.
+
+---
+ src/egl/drivers/dri2/egl_dri2.h         |   4 +
+ src/egl/drivers/dri2/platform_wayland.c | 270 ++++++++++++++++++++----
+ 2 files changed, 236 insertions(+), 38 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index 546bc0a..f41f32c 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -405,6 +405,10 @@ struct dri2_egl_surface
+    __DRIimage           *front;
+    unsigned int         visual;
+ 
++#ifdef HAVE_WAYLAND_PLATFORM
++   void                 *swrast_front;
++#endif
++
+    int out_fence_fd;
+    EGLBoolean enable_out_fence;
+ 
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index 811d28b..a096d95 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -427,6 +427,99 @@ dri2_wl_create_pixmap_surface(_EGLDisplay *disp, _EGLConfig *conf,
+    return NULL;
+ }
+ 
++static _EGLSurface *
++dri2_wl_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf,
++                               const EGLint *attrib_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
++   struct dri2_egl_surface *dri2_surf;
++   int visual_idx;
++   const __DRIconfig *config;
++
++   dri2_surf = calloc(1, sizeof *dri2_surf);
++   if (!dri2_surf) {
++      _eglError(EGL_BAD_ALLOC, "dri2_create_surface");
++      return NULL;
++   }
++
++   if (!dri2_init_surface(&dri2_surf->base, disp, EGL_PBUFFER_BIT, conf,
++                          attrib_list, false, NULL))
++      goto cleanup_surf;
++
++   config = dri2_get_dri_config(dri2_conf, EGL_PBUFFER_BIT,
++                                dri2_surf->base.GLColorspace);
++   if (!config) {
++      _eglError(EGL_BAD_MATCH, "Unsupported surfacetype/colorspace configuration");
++      goto cleanup_surf;
++   }
++
++   visual_idx = dri2_wl_visual_idx_from_config(dri2_dpy, config);
++   assert(visual_idx != -1);
++
++   if (dri2_dpy->wl_dmabuf || dri2_dpy->wl_drm) {
++      dri2_surf->format = dri2_wl_visuals[visual_idx].wl_drm_format;
++   } else {
++      assert(dri2_dpy->wl_shm);
++      dri2_surf->format = dri2_wl_visuals[visual_idx].wl_shm_format;
++   }
++
++   if (!dri2_create_drawable(dri2_dpy, config, dri2_surf, dri2_surf))
++       goto cleanup_surf;
++
++   return &dri2_surf->base;
++
++ cleanup_surf:
++   free(dri2_surf);
++
++   return NULL;
++}
++
++static int
++allocate_front_buffer(struct dri2_egl_display *dri2_dpy,
++                      struct dri2_egl_surface *dri2_surf,
++                      EGLBoolean need_name)
++{
++   int use_flags = need_name ? __DRI_IMAGE_USE_SHARE : 0;
++   int visual_idx;
++   unsigned int dri_image_format;
++
++   visual_idx = dri2_wl_visual_idx_from_fourcc(dri2_surf->format);
++   assert(visual_idx != -1);
++   dri_image_format = dri2_wl_visuals[visual_idx].dri_image_format;
++
++   if (!dri2_surf->front)
++      dri2_surf->front = dri2_dpy->image->createImage(dri2_dpy->dri_screen,
++                                                      dri2_surf->base.Width,
++                                                      dri2_surf->base.Height,
++                                                      dri_image_format,
++                                                      use_flags,
++                                                      NULL);
++   if (!dri2_surf->front) {
++      _eglError(EGL_BAD_ALLOC, "failed to allocate front buffer");
++      return -1;
++   }
++
++   return 0;
++}
++
++static void
++free_front_buffer(struct dri2_egl_display *dri2_dpy,
++                  struct dri2_egl_surface *dri2_surf)
++{
++   if (dri2_surf->front) {
++      dri2_dpy->image->destroyImage(dri2_surf->front);
++      dri2_surf->front = NULL;
++   }
++}
++
++static void
++swrast_free_front_buffer(struct dri2_egl_surface *dri2_surf)
++{
++   free(dri2_surf->swrast_front);
++   dri2_surf->swrast_front = NULL;
++}
++
+ /**
+  * Called via eglDestroySurface(), drv->DestroySurface().
+  */
+@@ -453,6 +546,9 @@ dri2_wl_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+    if (dri2_dpy->dri2)
+       dri2_egl_surface_free_local_buffers(dri2_surf);
+ 
++   free_front_buffer(dri2_dpy, dri2_surf);
++   swrast_free_front_buffer(dri2_surf);
++
+    if (dri2_surf->throttle_callback)
+       wl_callback_destroy(dri2_surf->throttle_callback);
+ 
+@@ -462,11 +558,14 @@ dri2_wl_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+       dri2_surf->wl_win->destroy_window_callback = NULL;
+    }
+ 
+-   wl_proxy_wrapper_destroy(dri2_surf->wl_surface_wrapper);
+-   wl_proxy_wrapper_destroy(dri2_surf->wl_dpy_wrapper);
++   if (dri2_surf->wl_surface_wrapper)
++      wl_proxy_wrapper_destroy(dri2_surf->wl_surface_wrapper);
++   if (dri2_surf->wl_dpy_wrapper)
++      wl_proxy_wrapper_destroy(dri2_surf->wl_dpy_wrapper);
+    if (dri2_surf->wl_drm_wrapper)
+       wl_proxy_wrapper_destroy(dri2_surf->wl_drm_wrapper);
+-   wl_event_queue_destroy(dri2_surf->wl_queue);
++   if (dri2_surf->wl_queue)
++      wl_event_queue_destroy(dri2_surf->wl_queue);
+ 
+    dri2_fini_surface(surf);
+    free(surf);
+@@ -628,20 +727,16 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
+ 
+ 
+ static void
+-back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
++bo_to_dri_buffer(struct dri2_egl_display *dri2_dpy, unsigned int attachment,
++                 __DRIimage *image, __DRIbuffer *buffer)
+ {
+-   struct dri2_egl_display *dri2_dpy =
+-      dri2_egl_display(dri2_surf->base.Resource.Display);
+-   __DRIimage *image;
+    int name, pitch, format;
+ 
+-   image = dri2_surf->back->dri_image;
+-
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_NAME, &name);
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &pitch);
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
+ 
+-   buffer->attachment = __DRI_BUFFER_BACK_LEFT;
++   buffer->attachment = attachment;
+    buffer->name = name;
+    buffer->pitch = pitch;
+    buffer->flags = 0;
+@@ -656,12 +751,28 @@ back_bo_to_dri_buffer(struct dri2_egl_surface *dri2_surf, __DRIbuffer *buffer)
+    }
+ }
+ 
+-static int
+-update_buffers(struct dri2_egl_surface *dri2_surf)
++static void
++back_bo_to_dri_buffer(struct dri2_egl_display *dri2_dpy,
++                      struct dri2_egl_surface *dri2_surf,
++                      __DRIbuffer *buffer)
+ {
+-   struct dri2_egl_display *dri2_dpy =
+-      dri2_egl_display(dri2_surf->base.Resource.Display);
++   bo_to_dri_buffer(dri2_dpy, __DRI_BUFFER_BACK_LEFT,
++                    dri2_surf->back->dri_image, buffer);
++}
++
++static void
++front_bo_to_dri_buffer(struct dri2_egl_display *dri2_dpy,
++                       struct dri2_egl_surface *dri2_surf,
++                       __DRIbuffer *buffer)
++{
++   bo_to_dri_buffer(dri2_dpy, __DRI_BUFFER_FRONT_LEFT,
++                    dri2_surf->front, buffer);
++}
+ 
++static int
++update_buffers(struct dri2_egl_display *dri2_dpy,
++               struct dri2_egl_surface *dri2_surf)
++{
+    if (dri2_surf->wl_win &&
+        (dri2_surf->base.Width != dri2_surf->wl_win->width ||
+         dri2_surf->base.Height != dri2_surf->wl_win->height)) {
+@@ -703,12 +814,13 @@ update_buffers(struct dri2_egl_surface *dri2_surf)
+ }
+ 
+ static int
+-update_buffers_if_needed(struct dri2_egl_surface *dri2_surf)
++update_buffers_if_needed(struct dri2_egl_display *dri2_dpy,
++                         struct dri2_egl_surface *dri2_surf)
+ {
+    if (dri2_surf->back != NULL)
+       return 0;
+ 
+-   return update_buffers(dri2_surf);
++   return update_buffers(dri2_dpy, dri2_surf);
+ }
+ 
+ static __DRIbuffer *
+@@ -718,17 +830,25 @@ dri2_wl_get_buffers_with_format(__DRIdrawable * driDrawable,
+                                 int *out_count, void *loaderPrivate)
+ {
+    struct dri2_egl_surface *dri2_surf = loaderPrivate;
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
+    int i, j;
+ 
+-   if (update_buffers(dri2_surf) < 0)
+-      return NULL;
+-
+    for (i = 0, j = 0; i < 2 * count; i += 2, j++) {
+       __DRIbuffer *local;
+ 
+       switch (attachments[i]) {
+       case __DRI_BUFFER_BACK_LEFT:
+-         back_bo_to_dri_buffer(dri2_surf, &dri2_surf->buffers[j]);
++         if (update_buffers(dri2_dpy, dri2_surf) < 0)
++            return NULL;
++
++         back_bo_to_dri_buffer(dri2_dpy, dri2_surf, &dri2_surf->buffers[j]);
++         break;
++      case __DRI_BUFFER_FRONT_LEFT:
++         if (allocate_front_buffer(dri2_dpy, dri2_surf, EGL_TRUE) < 0)
++            return NULL;
++
++         front_bo_to_dri_buffer(dri2_dpy, dri2_surf, &dri2_surf->buffers[j]);
+          break;
+       default:
+          local = dri2_egl_surface_alloc_local_buffer(dri2_surf, attachments[i],
+@@ -798,12 +918,30 @@ image_get_buffers(__DRIdrawable *driDrawable,
+                   struct __DRIimageList *buffers)
+ {
+    struct dri2_egl_surface *dri2_surf = loaderPrivate;
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
+ 
+-   if (update_buffers(dri2_surf) < 0)
+-      return 0;
++   buffers->image_mask = 0;
++   buffers->front = NULL;
++   buffers->back = NULL;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK)
++   {
++      if (update_buffers(dri2_dpy, dri2_surf) < 0)
++         return 0;
+ 
+-   buffers->image_mask = __DRI_IMAGE_BUFFER_BACK;
+-   buffers->back = dri2_surf->back->dri_image;
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
++      buffers->back = dri2_surf->back->dri_image;
++   }
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT)
++   {
++      if (allocate_front_buffer(dri2_dpy, dri2_surf, EGL_FALSE) < 0)
++         return 0;
++
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
++      buffers->front = dri2_surf->front;
++   }
+ 
+    return 1;
+ }
+@@ -1054,6 +1192,9 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+ 
++   if (draw->Type != EGL_WINDOW_BIT)
++      return EGL_TRUE;
++
+    if (!dri2_surf->wl_win)
+       return _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_swap_buffers");
+ 
+@@ -1068,7 +1209,7 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+ 
+    /* Make sure we have a back buffer in case we're swapping without ever
+     * rendering. */
+-   if (update_buffers_if_needed(dri2_surf) < 0)
++   if (update_buffers_if_needed(dri2_dpy, dri2_surf) < 0)
+       return _eglError(EGL_BAD_ALLOC, "dri2_swap_buffers");
+ 
+    if (draw->SwapInterval > 0) {
+@@ -1153,9 +1294,13 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+ static EGLint
+ dri2_wl_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surface)
+ {
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
+ 
+-   if (update_buffers_if_needed(dri2_surf) < 0) {
++   if (surface->Type != EGL_WINDOW_BIT)
++      return 0;
++
++   if (update_buffers_if_needed(dri2_dpy, dri2_surf) < 0) {
+       _eglError(EGL_BAD_ALLOC, "dri2_query_buffer_age");
+       return -1;
+    }
+@@ -1376,6 +1521,7 @@ static const struct dri2_egl_display_vtbl dri2_wl_display_vtbl = {
+    .authenticate = dri2_wl_authenticate,
+    .create_window_surface = dri2_wl_create_window_surface,
+    .create_pixmap_surface = dri2_wl_create_pixmap_surface,
++   .create_pbuffer_surface = dri2_wl_create_pbuffer_surface,
+    .destroy_surface = dri2_wl_destroy_surface,
+    .create_image = dri2_create_image_khr,
+    .swap_buffers = dri2_wl_swap_buffers,
+@@ -1418,7 +1564,7 @@ dri2_wl_add_configs_for_visuals(_EGLDisplay *disp)
+             continue;
+ 
+          dri2_conf = dri2_add_config(disp, dri2_dpy->driver_configs[i],
+-               count + 1, EGL_WINDOW_BIT, NULL, dri2_wl_visuals[j].rgba_shifts, dri2_wl_visuals[j].rgba_sizes);
++               count + 1, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, NULL, dri2_wl_visuals[j].rgba_shifts, dri2_wl_visuals[j].rgba_sizes);
+          if (dri2_conf) {
+             if (dri2_conf->base.ConfigID == count + 1)
+                count++;
+@@ -1654,6 +1800,23 @@ dri2_wl_swrast_get_stride_for_format(int format, int w)
+    return w * (dri2_wl_visuals[visual_idx].bpp / 8);
+ }
+ 
++static EGLBoolean
++swrast_allocate_local_buffer(int format, int w, int h, void **data)
++{
++   int stride, size_map;
++   void *data_map;
++
++   stride = dri2_wl_swrast_get_stride_for_format(format, w);
++   size_map = h * stride;
++
++   data_map = malloc(size_map);
++   if (!data_map)
++      return EGL_FALSE;
++
++   *data = data_map;
++   return EGL_TRUE;
++}
++
+ static EGLBoolean
+ dri2_wl_swrast_allocate_buffer(struct dri2_egl_surface *dri2_surf,
+                                int format, int w, int h,
+@@ -1775,8 +1938,24 @@ swrast_update_buffers(struct dri2_egl_surface *dri2_surf)
+    return 0;
+ }
+ 
++static int
++swrast_allocate_front_buffer(struct dri2_egl_surface *dri2_surf)
++{
++   if (!dri2_surf->swrast_front) {
++      if (!swrast_allocate_local_buffer(dri2_surf->format,
++                                        dri2_surf->base.Width,
++                                        dri2_surf->base.Height,
++                                        &dri2_surf->swrast_front)) {
++         _eglError(EGL_BAD_ALLOC, "failed to allocate front buffer");
++         return -1;
++      }
++   }
++
++   return 0;
++}
++
+ static void*
+-dri2_wl_swrast_get_frontbuffer_data(struct dri2_egl_surface *dri2_surf)
++dri2_wl_swrast_get_currentbuffer_data(struct dri2_egl_surface *dri2_surf)
+ {
+    /* if there has been a resize: */
+    if (!dri2_surf->current)
+@@ -1846,7 +2025,9 @@ dri2_wl_swrast_get_drawable_info(__DRIdrawable * draw,
+ {
+    struct dri2_egl_surface *dri2_surf = loaderPrivate;
+ 
+-   (void) swrast_update_buffers(dri2_surf);
++   if (dri2_surf->base.Type == EGL_WINDOW_BIT)
++      (void) swrast_update_buffers(dri2_surf);
++
+    *x = 0;
+    *y = 0;
+    *w = dri2_surf->base.Width;
+@@ -1865,7 +2046,11 @@ dri2_wl_swrast_get_image(__DRIdrawable * read,
+    int dst_stride = copy_width;
+    char *src, *dst;
+ 
+-   src = dri2_wl_swrast_get_frontbuffer_data(dri2_surf);
++   if (dri2_surf->base.Type == EGL_WINDOW_BIT)
++      src = dri2_wl_swrast_get_currentbuffer_data(dri2_surf);
++   else
++      src = dri2_surf->swrast_front;
++
+    if (!src) {
+       memset(data, 0, copy_width * h);
+       return;
+@@ -1903,14 +2088,20 @@ dri2_wl_swrast_put_image2(__DRIdrawable * draw, int op,
+ 
+    assert(copy_width <= stride);
+ 
+-   (void) swrast_update_buffers(dri2_surf);
+-   dst = dri2_wl_swrast_get_backbuffer_data(dri2_surf);
++   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
++      (void) swrast_update_buffers(dri2_surf);
++      dst = dri2_wl_swrast_get_backbuffer_data(dri2_surf);
+ 
+-   /* partial copy, copy old content */
+-   if (copy_width < dst_stride)
+-      dri2_wl_swrast_get_image(draw, 0, 0,
+-                               dri2_surf->base.Width, dri2_surf->base.Height,
+-                               dst, loaderPrivate);
++      /* partial copy, copy old content */
++      if (copy_width < dst_stride)
++         dri2_wl_swrast_get_image(draw, 0, 0,
++                                  dri2_surf->base.Width, dri2_surf->base.Height,
++                                  dst, loaderPrivate);
++   } else {
++      (void) swrast_allocate_front_buffer(dri2_surf);
++      dst = dri2_surf->swrast_front;
++      assert(dst);
++   }
+ 
+    dst += x_offset;
+    dst += y * dst_stride;
+@@ -1928,7 +2119,9 @@ dri2_wl_swrast_put_image2(__DRIdrawable * draw, int op,
+       src += stride;
+       dst += dst_stride;
+    }
+-   dri2_wl_swrast_commit_backbuffer(dri2_surf);
++
++   if (dri2_surf->base.Type == EGL_WINDOW_BIT)
++      dri2_wl_swrast_commit_backbuffer(dri2_surf);
+ }
+ 
+ static void
+@@ -1996,6 +2189,7 @@ static const struct dri2_egl_display_vtbl dri2_wl_swrast_display_vtbl = {
+    .authenticate = NULL,
+    .create_window_surface = dri2_wl_create_window_surface,
+    .create_pixmap_surface = dri2_wl_create_pixmap_surface,
++   .create_pbuffer_surface = dri2_wl_create_pbuffer_surface,
+    .destroy_surface = dri2_wl_destroy_surface,
+    .create_image = dri2_create_image_khr,
+    .swap_buffers = dri2_wl_swrast_swap_buffers,

+ 119 - 0
recipes-graphics/mesa/mesa-pvr/0027-egl-tizen-support-DRI-driver-handling-of-swap-preser.patch

@@ -0,0 +1,119 @@
+From 446ff9fb0a0137745a2301982c52d187285d9837 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Mon, 18 Dec 2017 19:22:50 +0000
+Subject: [PATCH] egl/tizen: support DRI driver handling of swap preserve
+
+This adds a new flag (__DRI_IMAGE_BUFFER_PREV) to the __DRIimageBufferMask
+enum that allows a DRI driver to request the previous back buffer. This
+will only be returned if the swap behaviour is EGL_BUFFER_PRESERVED and
+should result in the DRI driver preserving the previous content instead of
+this being done in the platform code. For hardware that supports it, this
+should avoid a blit being performed every frame, although this will still
+be necessary under certain conditions, e.g. an empty swap.
+
+---
+ include/GL/internal/dri_interface.h   |  3 +++
+ src/egl/drivers/dri2/platform_tizen.c | 29 +++++++++++++++++++++------
+ 2 files changed, 26 insertions(+), 6 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index b197092..16cc095 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -2073,12 +2073,15 @@ enum __DRIimageBufferMask {
+     * OpenGL ES API and little change to the SurfaceFlinger API.
+     */
+    __DRI_IMAGE_BUFFER_SHARED = (1 << 2),
++#define DRI_IMAGE_HAS_BUFFER_PREV
++   __DRI_IMAGE_BUFFER_PREV = (1 << 31),
+ };
+ 
+ struct __DRIimageList {
+    uint32_t image_mask;
+    __DRIimage *back;
+    __DRIimage *front;
++   __DRIimage *prev;
+ };
+ 
+ #define __DRI_IMAGE_LOADER "DRI_IMAGE_LOADER"
+diff --git a/src/egl/drivers/dri2/platform_tizen.c b/src/egl/drivers/dri2/platform_tizen.c
+index 4946215..2bc9b3e 100644
+--- a/src/egl/drivers/dri2/platform_tizen.c
++++ b/src/egl/drivers/dri2/platform_tizen.c
+@@ -147,7 +147,8 @@ create_image_from_native(struct dri2_egl_surface *dri2_surf,
+ }
+ 
+ static int
+-get_back_bo(struct dri2_egl_surface *dri2_surf, bool allow_update)
++get_back_bo(struct dri2_egl_surface *dri2_surf, bool allow_update,
++            bool allow_preserve)
+ {
+    struct dri2_egl_display *dri2_dpy =
+       dri2_egl_display(dri2_surf->base.Resource.Display);
+@@ -277,7 +278,7 @@ get_back_bo(struct dri2_egl_surface *dri2_surf, bool allow_update)
+    dri2_surf->back->locked = true;
+ 
+    if (dri2_surf->base.SwapBehavior == EGL_BUFFER_PRESERVED &&
+-       dri2_surf->current) {
++       allow_preserve && dri2_surf->current) {
+       _EGLContext *ctx = _eglGetCurrentContext();
+       struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+ 
+@@ -609,7 +610,7 @@ dri2_tizen_swap_buffers_with_damage(_EGLDisplay *dpy, _EGLSurface *draw,
+     * Make sure we have a back buffer in case we're swapping without ever
+     * rendering.
+     */
+-   if (get_back_bo(dri2_surf, false) < 0) {
++   if (get_back_bo(dri2_surf, false, true) < 0) {
+       _eglError(EGL_BAD_ALLOC, "DRI2: failed to get back buffer");
+       return EGL_FALSE;
+    }
+@@ -673,7 +674,7 @@ dri2_tizen_query_buffer_age(_EGLDisplay *dpy, _EGLSurface *surface)
+ {
+    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
+ 
+-   if (get_back_bo(dri2_surf, false) < 0) {
++   if (get_back_bo(dri2_surf, false, true) < 0) {
+       _eglError(EGL_BAD_ALLOC, "DRI2: failed to get back buffer");
+       return -1;
+    }
+@@ -748,14 +749,22 @@ dri2_tizen_get_buffers(__DRIdrawable *driDrawable,
+    buffers->image_mask = 0;
+    buffers->front = NULL;
+    buffers->back = NULL;
++   buffers->prev = NULL;
+ 
+    if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT)
+       if (get_front_bo(dri2_surf) < 0)
+          return 0;
+ 
+-   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK)
+-      if (get_back_bo(dri2_surf, true) < 0)
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
++      /*
++       * The DRI driver has requested the previous buffer so it will take care
++       * of preserving the previous content.
++       */
++      bool allow_preserve = !(buffer_mask & __DRI_IMAGE_BUFFER_PREV);
++
++      if (get_back_bo(dri2_surf, true, allow_preserve) < 0)
+          return 0;
++   }
+ 
+    if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
+       buffers->front = dri2_surf->front;
+@@ -767,6 +776,14 @@ dri2_tizen_get_buffers(__DRIdrawable *driDrawable,
+       buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
+    }
+ 
++   if (buffer_mask & __DRI_IMAGE_BUFFER_PREV) {
++      if (dri2_surf->base.SwapBehavior == EGL_BUFFER_PRESERVED &&
++          dri2_surf->current) {
++         buffers->prev = dri2_surf->current->dri_image;
++         buffers->image_mask |= __DRI_IMAGE_BUFFER_PREV;
++      }
++   }
++
+    return 1;
+ }
+ 

+ 27 - 0
recipes-graphics/mesa/mesa-pvr/0028-egl-eglBindAPI-workaround-for-dEQP-bug.patch

@@ -0,0 +1,27 @@
+From 1a7c1d5f40bd0692460f9a7d164cb7de61826cb9 Mon Sep 17 00:00:00 2001
+From: Eric Engestrom <eric.engestrom@imgtec.com>
+Date: Mon, 25 Sep 2017 15:58:49 +0100
+Subject: [PATCH] egl: eglBindAPI workaround for dEQP bug
+
+dEQP relies on eglBindAPI to only return true if the API can
+successfully be used to create contexts, which the spec does not
+require.
+Until dEQP is fixed, just disable GL on non-X11 platforms.
+
+---
+ src/egl/main/eglcurrent.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/egl/main/eglcurrent.h b/src/egl/main/eglcurrent.h
+index d9a4a90..3257097 100644
+--- a/src/egl/main/eglcurrent.h
++++ b/src/egl/main/eglcurrent.h
+@@ -71,7 +71,7 @@ struct _egl_thread_info
+ static inline EGLBoolean
+ _eglIsApiValid(EGLenum api)
+ {
+-#ifdef ANDROID
++#ifndef HAVE_X11_PLATFORM
+    /* OpenGL is not a valid/supported API on Android */
+    return api == EGL_OPENGL_ES_API;
+ #else

+ 53 - 0
recipes-graphics/mesa/mesa-pvr/0029-GL_EXT_multi_draw_indirect-entry-points.patch

@@ -0,0 +1,53 @@
+From 205f2cd0f14568a573154c35416d29621c5233c2 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Tue, 30 Jan 2018 10:25:11 +0000
+Subject: [PATCH] GL_EXT_multi_draw_indirect entry points
+
+---
+ src/mapi/glapi/gen/es_EXT.xml     | 19 +++++++++++++++++++
+ src/mapi/glapi/gen/static_data.py |  2 ++
+ 2 files changed, 21 insertions(+)
+
+diff --git a/src/mapi/glapi/gen/es_EXT.xml b/src/mapi/glapi/gen/es_EXT.xml
+index fe8f27e..e0d60fa 100644
+--- a/src/mapi/glapi/gen/es_EXT.xml
++++ b/src/mapi/glapi/gen/es_EXT.xml
+@@ -1140,6 +1140,25 @@
+ 
+ </category>
+ 
++<category name="GL_EXT_multi_draw_indirect" number="205">
++
++    <function name="MultiDrawArraysIndirectEXT" es2="3.1" exec="dynamic">
++        <param name="mode" type="GLenum"/>
++        <param name="indirect" type="const GLvoid *"/>
++        <param name="drawcount" type="GLsizei"/>
++        <param name="stride" type="GLsizei"/>
++    </function>
++
++    <function name="MultiDrawElementsIndirectEXT" es2="3.1" exec="dynamic">
++        <param name="mode" type="GLenum"/>
++        <param name="type" type="GLenum"/>
++        <param name="indirect" type="const GLvoid *"/>
++        <param name="drawcount" type="GLsizei"/>
++        <param name="stride" type="GLsizei"/>
++    </function>
++
++</category>
++
+ <category name="GL_EXT_copy_image" number="208">
+ 
+     <function name="CopyImageSubDataEXT" alias="CopyImageSubData" es2="3.0">
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index dc6bdc9..e231c17 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1704,6 +1704,8 @@ offsets = {
+     "FramebufferTextureLayerDownsampleIMG" : 1668,
+     "FramebufferTextureMultiviewOVR" : 1669,
+     "FramebufferTextureMultisampleMultiviewOVR" : 1670,
++    "MultiDrawArraysIndirectEXT" : 1671,
++    "MultiDrawElementsIndirectEXT" : 1672,
+ }
+ 
+ functions = [

+ 492 - 0
recipes-graphics/mesa/mesa-pvr/0030-dri-add-support-for-YUV-DRI-config.patch

@@ -0,0 +1,492 @@
+From 23b1868e2fbf2340a354559ca3e6ed61e723f7c8 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Fri, 22 Dec 2017 17:17:50 +0000
+Subject: [PATCH] dri: add support for YUV DRI config
+
+This is prerequisite for adding support for EGL_EXT_yuv_surface.
+
+This also adds support for NV12 and NV21 EGL configs.
+
+---
+ include/GL/internal/dri_interface.h           | 42 ++++++++-
+ src/gallium/frontends/dri/dri_screen.c        |  8 +-
+ src/gallium/include/pipe/p_format.h           |  2 +
+ src/mesa/drivers/dri/common/utils.c           | 88 +++++++++++++++++--
+ src/mesa/drivers/dri/common/utils.h           |  3 +-
+ src/mesa/drivers/dri/i915/intel_screen.c      |  8 +-
+ src/mesa/drivers/dri/i965/brw_screen.c        | 12 ++-
+ src/mesa/drivers/dri/nouveau/nouveau_screen.c |  4 +-
+ src/mesa/drivers/dri/radeon/radeon_screen.c   |  4 +-
+ src/mesa/main/format_info.py                  |  2 +-
+ src/mesa/main/formats.c                       |  9 ++
+ src/mesa/main/formats.csv                     |  2 +
+ src/mesa/main/formats.h                       |  7 ++
+ src/mesa/main/mtypes.h                        |  9 ++
+ 14 files changed, 181 insertions(+), 19 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 16cc095..4d5a187 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -826,7 +826,13 @@ struct __DRIuseInvalidateExtensionRec {
+ #define __DRI_ATTRIB_GREEN_SHIFT		51
+ #define __DRI_ATTRIB_BLUE_SHIFT			52
+ #define __DRI_ATTRIB_ALPHA_SHIFT		53
+-#define __DRI_ATTRIB_MAX			54
++#define __DRI_ATTRIB_YUV_ORDER			54
++#define __DRI_ATTRIB_YUV_NUMBER_OF_PLANES	55
++#define __DRI_ATTRIB_YUV_SUBSAMPLE		56
++#define __DRI_ATTRIB_YUV_DEPTH_RANGE		57
++#define __DRI_ATTRIB_YUV_CSC_STANDARD		58
++#define __DRI_ATTRIB_YUV_PLANE_BPP		59
++#define __DRI_ATTRIB_MAX			60
+ 
+ /* __DRI_ATTRIB_RENDER_TYPE */
+ #define __DRI_ATTRIB_RGBA_BIT			0x01	
+@@ -834,6 +840,7 @@ struct __DRIuseInvalidateExtensionRec {
+ #define __DRI_ATTRIB_LUMINANCE_BIT		0x04
+ #define __DRI_ATTRIB_FLOAT_BIT			0x08
+ #define __DRI_ATTRIB_UNSIGNED_FLOAT_BIT		0x10
++#define __DRI_ATTRIB_YUV_BIT			0x20
+ 
+ /* __DRI_ATTRIB_CONFIG_CAVEAT */
+ #define __DRI_ATTRIB_SLOW_BIT			0x01
+@@ -860,6 +867,39 @@ struct __DRIuseInvalidateExtensionRec {
+ #define __DRI_ATTRIB_SWAP_COPY                  0x8062
+ #define __DRI_ATTRIB_SWAP_UNDEFINED             0x8063
+ 
++/* __DRI_ATTRIB_YUV_ORDER */
++#define __DRI_ATTRIB_YUV_ORDER_NONE             0x0
++#define __DRI_ATTRIB_YUV_ORDER_YUV_BIT          0x1
++#define __DRI_ATTRIB_YUV_ORDER_YVU_BIT          0x2
++#define __DRI_ATTRIB_YUV_ORDER_YUYV_BIT         0x4
++#define __DRI_ATTRIB_YUV_ORDER_UYVY_BIT         0x8
++#define __DRI_ATTRIB_YUV_ORDER_YVYU_BIT         0x10
++#define __DRI_ATTRIB_YUV_ORDER_VYUY_BIT         0x20
++#define __DRI_ATTRIB_YUV_ORDER_AYUV_BIT         0x40
++
++/* __DRI_ATTRIB_YUV_SUBSAMPLE */
++#define __DRI_ATTRIB_YUV_SUBSAMPLE_NONE         0x0
++#define __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT    0x1
++#define __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_2_BIT    0x2
++#define __DRI_ATTRIB_YUV_SUBSAMPLE_4_4_4_BIT    0x4
++
++/* __DRI_ATTRIB_YUV_DEPTH_RANGE */
++#define __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE       0x0
++#define __DRI_ATTRIB_YUV_DEPTH_RANGE_LIMITED_BIT 0x1
++#define __DRI_ATTRIB_YUV_DEPTH_RANGE_FULL_BIT   0x2
++
++/* __DRI_ATTRIB_YUV_CSC_STANDARD */
++#define __DRI_ATTRIB_YUV_CSC_STANDARD_NONE      0x0
++#define __DRI_ATTRIB_YUV_CSC_STANDARD_601_BIT   0x1
++#define __DRI_ATTRIB_YUV_CSC_STANDARD_709_BIT   0x2
++#define __DRI_ATTRIB_YUV_CSC_STANDARD_2020_BIT  0x4
++
++/* __DRI_ATTRIB_YUV_PLANE_BPP */
++#define __DRI_ATTRIB_YUV_PLANE_BPP_NONE         0x0
++#define __DRI_ATTRIB_YUV_PLANE_BPP_0_BIT        0x1
++#define __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT        0x2
++#define __DRI_ATTRIB_YUV_PLANE_BPP_10_BIT       0x4
++
+ /**
+  * This extension defines the core DRI functionality.
+  *
+diff --git a/src/gallium/frontends/dri/dri_screen.c b/src/gallium/frontends/dri/dri_screen.c
+index b565a1f..37a0d4a 100644
+--- a/src/gallium/frontends/dri/dri_screen.c
++++ b/src/gallium/frontends/dri/dri_screen.c
+@@ -324,7 +324,9 @@ dri_fill_in_modes(struct dri_screen *screen)
+                                         depth_buffer_factor, back_buffer_modes,
+                                         ARRAY_SIZE(back_buffer_modes),
+                                         msaa_modes, 1,
+-                                        GL_TRUE, !mixed_color_depth);
++                                        GL_TRUE, !mixed_color_depth,
++                                        __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                        __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+          configs = driConcatConfigs(configs, new_configs);
+ 
+          /* Multi-sample configs without an accumulation buffer. */
+@@ -334,7 +336,9 @@ dri_fill_in_modes(struct dri_screen *screen)
+                                            depth_buffer_factor, back_buffer_modes,
+                                            ARRAY_SIZE(back_buffer_modes),
+                                            msaa_modes+1, num_msaa_modes-1,
+-                                           GL_FALSE, !mixed_color_depth);
++                                           GL_FALSE, !mixed_color_depth,
++                                           __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                           __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+             configs = driConcatConfigs(configs, new_configs);
+          }
+       }
+diff --git a/src/gallium/include/pipe/p_format.h b/src/gallium/include/pipe/p_format.h
+index 0c93d7d..fd65337 100644
+--- a/src/gallium/include/pipe/p_format.h
++++ b/src/gallium/include/pipe/p_format.h
+@@ -513,6 +513,8 @@ enum pipe_format {
+    PIPE_FORMAT_R4G4B4X4_UNORM,
+    PIPE_FORMAT_B10G10R10X2_SNORM,
+    PIPE_FORMAT_R5G6B5_SRGB,
++   PIPE_FORMAT_YUV420_2PLANE,
++   PIPE_FORMAT_YVU420_2PLANE,
+ 
+    PIPE_FORMAT_COUNT
+ };
+diff --git a/src/mesa/drivers/dri/common/utils.c b/src/mesa/drivers/dri/common/utils.c
+index a3f2bc5..d268dc4 100644
+--- a/src/mesa/drivers/dri/common/utils.c
++++ b/src/mesa/drivers/dri/common/utils.c
+@@ -163,6 +163,21 @@ driGetRendererString( char * buffer, const char * hardware_name,
+  *                          This forces 32-bit color to have 24-bit depth, and
+  *                          16-bit color to have 16-bit depth.
+  *
++ * \param yuv_depth_range YUV pixel depth range. For non-YUV pixel formats this
++ *                        should be \c __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE.
++ *                        Otherwise valid values are
++ *                        \c __DRI_ATTRIB_YUV_DEPTH_RANGE_LIMITED_BIT and
++ *                        \c __DRI_ATTRIB_YUV_DEPTH_RANGE_FULL_BIT. See the
++ *                        EGL_EXT_yuv_surface extension spec for more details.
++ * \param yuv_csc_standard YUV color conversion standard. For non-YUV pixel
++ *                         formats this should be
++ *                         \c __DRI_ATTRIB_YUV_CSC_STANDARD_NONE. Otherwise
++ *                         valid values are
++ *                         \c __DRI_ATTRIB_YUV_CSC_STANDARD_601_BIT,
++ *                         \c __DRI_ATTRIB_YUV_CSC_STANDARD_709_BIT and
++ *                         \c __DRI_ATTRIB_YUV_CSC_STANDARD_2020_BIT. See the
++ *                         EGL_EXT_yuv_surface extension spec for more details.
++ *
+  * \returns
+  * Pointer to any array of pointers to the \c __DRIconfig structures created
+  * for the specified formats.  If there is an error, \c NULL is returned.
+@@ -175,7 +190,8 @@ driCreateConfigs(mesa_format format,
+ 		 unsigned num_depth_stencil_bits,
+ 		 const GLenum * db_modes, unsigned num_db_modes,
+ 		 const uint8_t * msaa_samples, unsigned num_msaa_modes,
+-		 GLboolean enable_accum, GLboolean color_depth_match)
++		 GLboolean enable_accum, GLboolean color_depth_match,
++		 GLint yuv_depth_range, GLint yuv_csc_standard)
+ {
+    static const struct {
+       uint32_t masks[4];
+@@ -214,6 +230,9 @@ driCreateConfigs(mesa_format format,
+       /* MESA_FORMAT_RGBA_FLOAT16 */
+       {{ 0, 0, 0, 0},
+        { 0, 16, 32, 48 }},
++      /* Mesa YUV formats */
++      {{ 0, 0, 0, 0 },
++       { -1, -1, -1, -1}},
+    };
+ 
+    const uint32_t * masks;
+@@ -227,6 +246,11 @@ driCreateConfigs(mesa_format format,
+    int green_bits;
+    int blue_bits;
+    int alpha_bits;
++   int yuv_order = __DRI_ATTRIB_YUV_ORDER_NONE;
++   int yuv_num_planes = 0;
++   int yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_NONE;
++   int yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_NONE;
++   bool is_yuv = false;
+    bool is_srgb;
+    bool is_float;
+ 
+@@ -279,6 +303,33 @@ driCreateConfigs(mesa_format format,
+       masks = format_table[8].masks;
+       shifts = format_table[8].shifts;
+       break;
++   case MESA_FORMAT_YCBCR:
++      masks = format_table[11].masks;
++      shifts = format_table[11].shifts;
++      is_yuv = true; /* FIXME: This should come from formats_info.py */
++      yuv_order = __DRI_ATTRIB_YUV_ORDER_YUYV_BIT;
++      yuv_num_planes = 1;
++      yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_2_BIT;
++      yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT;
++      break;
++   case MESA_FORMAT_YUV420_2PLANE:
++      masks = format_table[11].masks;
++      shifts = format_table[11].shifts;
++      is_yuv = true; /* FIXME: This should come from formats_info.py */
++      yuv_order = __DRI_ATTRIB_YUV_ORDER_YUV_BIT;
++      yuv_num_planes = 2;
++      yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT;
++      yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT;
++      break;
++   case MESA_FORMAT_YVU420_2PLANE:
++      masks = format_table[11].masks;
++      shifts = format_table[11].shifts;
++      is_yuv = true; /* FIXME: This should come from formats_info.py */
++      yuv_order = __DRI_ATTRIB_YUV_ORDER_YVU_BIT;
++      yuv_num_planes = 2;
++      yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT;
++      yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT;
++      break;
+    default:
+       fprintf(stderr, "[%s:%u] Unknown framebuffer type %s (%d).\n",
+               __func__, __LINE__,
+@@ -334,8 +385,12 @@ driCreateConfigs(mesa_format format,
+ 		    modes->greenShift = shifts[1];
+ 		    modes->blueShift  = shifts[2];
+ 		    modes->alphaShift = shifts[3];
+-		    modes->rgbBits   = modes->redBits + modes->greenBits
+-		    	+ modes->blueBits + modes->alphaBits;
++
++		    if (is_yuv)
++			modes->rgbBits = 8;
++		    else
++			modes->rgbBits = modes->redBits + modes->greenBits
++			    + modes->blueBits + modes->alphaBits;
+ 
+ 		    modes->accumRedBits   = 16 * j;
+ 		    modes->accumGreenBits = 16 * j;
+@@ -345,6 +400,8 @@ driCreateConfigs(mesa_format format,
+ 		    modes->stencilBits = stencil_bits[k];
+ 		    modes->depthBits = depth_bits[k];
+ 
++		    modes->rgbMode = !is_yuv;
++
+ 		    if (db_modes[i] == __DRI_ATTRIB_SWAP_NONE) {
+ 		    	modes->doubleBufferMode = GL_FALSE;
+ 		        modes->swapMethod = __DRI_ATTRIB_SWAP_UNDEFINED;
+@@ -357,6 +414,13 @@ driCreateConfigs(mesa_format format,
+ 		    modes->samples = msaa_samples[h];
+ 
+ 		    modes->sRGBCapable = is_srgb;
++
++		    modes->YUVOrder = yuv_order;
++		    modes->YUVNumberOfPlanes = yuv_num_planes;
++		    modes->YUVSubsample = yuv_subsample;
++		    modes->YUVDepthRange = yuv_depth_range;
++		    modes->YUVCSCStandard = yuv_csc_standard;
++		    modes->YUVPlaneBPP = yuv_plane_bpp;
+ 		}
+ 	    }
+ 	}
+@@ -436,10 +500,14 @@ driGetConfigAttribIndex(const __DRIconfig *config,
+         break;
+     __ATTRIB(__DRI_ATTRIB_SAMPLES,			samples);
+     case __DRI_ATTRIB_RENDER_TYPE:
+-        /* no support for color index mode */
+-	*value = __DRI_ATTRIB_RGBA_BIT;
+-        if (config->modes.floatMode)
+-            *value |= __DRI_ATTRIB_FLOAT_BIT;
++	/* no support for color index mode */
++	if (config->modes.rgbMode)
++	    *value = __DRI_ATTRIB_RGBA_BIT;
++	else
++	    *value = __DRI_ATTRIB_YUV_BIT;
++
++	if (config->modes.floatMode)
++	    *value |= __DRI_ATTRIB_FLOAT_BIT;
+ 	break;
+     case __DRI_ATTRIB_CONFIG_CAVEAT:
+ 	if (config->modes.accumRedBits != 0)
+@@ -505,6 +573,12 @@ driGetConfigAttribIndex(const __DRIconfig *config,
+     __ATTRIB(__DRI_ATTRIB_GREEN_SHIFT,			greenShift);
+     __ATTRIB(__DRI_ATTRIB_BLUE_SHIFT,			blueShift);
+     __ATTRIB(__DRI_ATTRIB_ALPHA_SHIFT,			alphaShift);
++    __ATTRIB(__DRI_ATTRIB_YUV_ORDER,			YUVOrder);
++    __ATTRIB(__DRI_ATTRIB_YUV_NUMBER_OF_PLANES,		YUVNumberOfPlanes);
++    __ATTRIB(__DRI_ATTRIB_YUV_SUBSAMPLE,		YUVSubsample);
++    __ATTRIB(__DRI_ATTRIB_YUV_DEPTH_RANGE,		YUVDepthRange);
++    __ATTRIB(__DRI_ATTRIB_YUV_CSC_STANDARD,		YUVCSCStandard);
++    __ATTRIB(__DRI_ATTRIB_YUV_PLANE_BPP,		YUVPlaneBPP);
+     default:
+         /* XXX log an error or smth */
+         return GL_FALSE;
+diff --git a/src/mesa/drivers/dri/common/utils.h b/src/mesa/drivers/dri/common/utils.h
+index 7be0465..ebd98d9 100644
+--- a/src/mesa/drivers/dri/common/utils.h
++++ b/src/mesa/drivers/dri/common/utils.h
+@@ -45,7 +45,8 @@ driCreateConfigs(mesa_format format,
+ 		 unsigned num_depth_stencil_bits,
+ 		 const GLenum * db_modes, unsigned num_db_modes,
+ 		 const uint8_t * msaa_samples, unsigned num_msaa_modes,
+-		 GLboolean enable_accum, GLboolean color_depth_match);
++		 GLboolean enable_accum, GLboolean color_depth_match,
++		 GLint yuv_depth_range, GLint yuv_csc_standards);
+ 
+ __DRIconfig **driConcatConfigs(__DRIconfig **a,
+ 			       __DRIconfig **b);
+diff --git a/src/mesa/drivers/dri/i915/intel_screen.c b/src/mesa/drivers/dri/i915/intel_screen.c
+index 6135357..a94c6de 100644
+--- a/src/mesa/drivers/dri/i915/intel_screen.c
++++ b/src/mesa/drivers/dri/i915/intel_screen.c
+@@ -1083,7 +1083,9 @@ intel_screen_make_configs(__DRIscreen *dri_screen)
+                                      num_depth_stencil_bits,
+                                      back_buffer_modes, 2,
+                                      singlesample_samples, 1,
+-                                     false, false);
++                                     false, false,
++                                     __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                     __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+       configs = driConcatConfigs(configs, new_configs);
+    }
+ 
+@@ -1105,7 +1107,9 @@ intel_screen_make_configs(__DRIscreen *dri_screen)
+                                      depth_bits, stencil_bits, 1,
+                                      back_buffer_modes, 1,
+                                      singlesample_samples, 1,
+-                                     true, false);
++                                     true, false,
++                                     __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                     __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+       configs = driConcatConfigs(configs, new_configs);
+    }
+ 
+diff --git a/src/mesa/drivers/dri/i965/brw_screen.c b/src/mesa/drivers/dri/i965/brw_screen.c
+index 5681111..c450bae 100644
+--- a/src/mesa/drivers/dri/i965/brw_screen.c
++++ b/src/mesa/drivers/dri/i965/brw_screen.c
+@@ -2292,7 +2292,9 @@ brw_screen_make_configs(__DRIscreen *dri_screen)
+                                      num_depth_stencil_bits,
+                                      back_buffer_modes, 2,
+                                      singlesample_samples, 1,
+-                                     false, false);
++                                     false, false,
++                                     __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                     __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+       configs = driConcatConfigs(configs, new_configs);
+    }
+ 
+@@ -2325,7 +2327,9 @@ brw_screen_make_configs(__DRIscreen *dri_screen)
+                                      depth_bits, stencil_bits, 1,
+                                      back_buffer_modes, 1,
+                                      singlesample_samples, 1,
+-                                     true, false);
++                                     true, false,
++                                     __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                     __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+       configs = driConcatConfigs(configs, new_configs);
+    }
+ 
+@@ -2390,7 +2394,9 @@ brw_screen_make_configs(__DRIscreen *dri_screen)
+                                      back_buffer_modes, 1,
+                                      multisample_samples,
+                                      num_msaa_modes,
+-                                     false, false);
++                                     false, false,
++                                     __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++                                     __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+       configs = driConcatConfigs(configs, new_configs);
+    }
+ 
+diff --git a/src/mesa/drivers/dri/nouveau/nouveau_screen.c b/src/mesa/drivers/dri/nouveau/nouveau_screen.c
+index c92efcd..fc87d5e 100644
+--- a/src/mesa/drivers/dri/nouveau/nouveau_screen.c
++++ b/src/mesa/drivers/dri/nouveau/nouveau_screen.c
+@@ -79,7 +79,9 @@ nouveau_get_configs(uint32_t chipset)
+ 					  ARRAY_SIZE(back_buffer_modes),
+ 					  msaa_samples,
+ 					  ARRAY_SIZE(msaa_samples),
+-					  GL_TRUE, chipset < 0x10);
++					  GL_TRUE, chipset < 0x10,
++					  __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++					  __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+ 		assert(config);
+ 
+ 		configs = driConcatConfigs(configs, config);
+diff --git a/src/mesa/drivers/dri/radeon/radeon_screen.c b/src/mesa/drivers/dri/radeon/radeon_screen.c
+index 3764a5d..f762855 100644
+--- a/src/mesa/drivers/dri/radeon/radeon_screen.c
++++ b/src/mesa/drivers/dri/radeon/radeon_screen.c
+@@ -838,7 +838,9 @@ __DRIconfig **radeonInitScreen2(__DRIscreen *psp)
+ 				     ARRAY_SIZE(back_buffer_modes),
+ 				     msaa_samples_array,
+ 				     ARRAY_SIZE(msaa_samples_array),
+-				     GL_TRUE, GL_FALSE);
++				     GL_TRUE, GL_FALSE,
++				     __DRI_ATTRIB_YUV_DEPTH_RANGE_NONE,
++				     __DRI_ATTRIB_YUV_CSC_STANDARD_NONE);
+       configs = driConcatConfigs(configs, new_configs);
+    }
+ 
+diff --git a/src/mesa/main/format_info.py b/src/mesa/main/format_info.py
+index edc0324..d58403e 100644
+--- a/src/mesa/main/format_info.py
++++ b/src/mesa/main/format_info.py
+@@ -29,7 +29,7 @@ import sys
+ def get_gl_base_format(fmat):
+    if fmat.name == 'MESA_FORMAT_NONE':
+       return 'GL_NONE'
+-   elif fmat.name in ['MESA_FORMAT_YCBCR', 'MESA_FORMAT_YCBCR_REV']:
++   elif fmat.name in ['MESA_FORMAT_YCBCR', 'MESA_FORMAT_YCBCR_REV', 'MESA_FORMAT_YUV420_2PLANE', 'MESA_FORMAT_YVU420_2PLANE']:
+       return 'GL_YCBCR_MESA'
+    elif fmat.has_channel('r'):
+       if fmat.has_channel('g'):
+diff --git a/src/mesa/main/formats.c b/src/mesa/main/formats.c
+index 9cd026f..f81caec 100644
+--- a/src/mesa/main/formats.c
++++ b/src/mesa/main/formats.c
+@@ -1452,6 +1452,15 @@ _mesa_format_matches_format_and_type(mesa_format mformat,
+    if (error)
+       *error = GL_NO_ERROR;
+ 
++   switch (mformat) {
++   case MESA_FORMAT_YUV420_2PLANE:
++   case MESA_FORMAT_YVU420_2PLANE:
++      return false;
++
++   default:
++      break;
++   }
++
+    if (_mesa_is_format_compressed(mformat)) {
+       if (error)
+          *error = GL_INVALID_ENUM;
+diff --git a/src/mesa/main/formats.csv b/src/mesa/main/formats.csv
+index 21cdea2..b2d4765 100644
+--- a/src/mesa/main/formats.csv
++++ b/src/mesa/main/formats.csv
+@@ -92,6 +92,8 @@ MESA_FORMAT_A2R10G10B10_UNORM             , packed, 1, 1, 1, un2 , un10, un10, u
+ 
+ MESA_FORMAT_YCBCR                         , other , 1, 1, 1, x16 ,     ,     ,     , xyzw, yuv
+ MESA_FORMAT_YCBCR_REV                     , other , 1, 1, 1, x16 ,     ,     ,     , xyzw, yuv
++MESA_FORMAT_YUV420_2PLANE                 , other , 1, 1, 1, x8  ,     ,     ,     , y___, yuv
++MESA_FORMAT_YVU420_2PLANE                 , other , 1, 1, 1, x8  ,     ,     ,     , y___, yuv
+ 
+ MESA_FORMAT_RG_RB_UNORM8                  , other , 2, 1, 1, x16 ,     ,     ,     , xyz1, rgb
+ MESA_FORMAT_GR_BR_UNORM8                  , other , 2, 1, 1, x16 ,     ,     ,     , xyz1, rgb
+diff --git a/src/mesa/main/formats.h b/src/mesa/main/formats.h
+index 508c9c3..0e778d6 100644
+--- a/src/mesa/main/formats.h
++++ b/src/mesa/main/formats.h
+@@ -617,6 +617,13 @@ typedef enum pipe_format mesa_format;
+ #define MESA_FORMAT_ATC_RGB                      PIPE_FORMAT_ATC_RGB
+ #define MESA_FORMAT_ATC_RGBA_EXPLICIT            PIPE_FORMAT_ATC_RGBA_EXPLICIT
+ #define MESA_FORMAT_ATC_RGBA_INTERPOLATED        PIPE_FORMAT_ATC_RGBA_INTERPOLATED
++
++#define HAVE_MESA_FORMAT_YUV420_2PLANE
++#define MESA_FORMAT_YUV420_2PLANE                PIPE_FORMAT_YUV420_2PLANE
++
++#define HAVE_MESA_FORMAT_YVU420_2PLANE
++#define MESA_FORMAT_YVU420_2PLANE                PIPE_FORMAT_YVU420_2PLANE
++
+ #define MESA_FORMAT_COUNT                        PIPE_FORMAT_COUNT
+ 
+ /* Packed to array format adapters */
+diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
+index 32528a5..8f77d4c 100644
+--- a/src/mesa/main/mtypes.h
++++ b/src/mesa/main/mtypes.h
+@@ -158,6 +158,7 @@ _mesa_varying_slot_in_fs(gl_varying_slot slot)
+  */
+ struct gl_config
+ {
++   GLboolean rgbMode;
+    GLboolean floatMode;
+    GLuint doubleBufferMode;
+    GLuint stereoMode;
+@@ -179,6 +180,14 @@ struct gl_config
+ 
+    /* EXT_framebuffer_sRGB */
+    GLint sRGBCapable;
++
++   /* EXT_yuv_surface */
++   GLint YUVOrder;
++   GLint YUVNumberOfPlanes;
++   GLint YUVSubsample;
++   GLint YUVDepthRange;
++   GLint YUVCSCStandard;
++   GLint YUVPlaneBPP;
+ };
+ 
+ 

+ 539 - 0
recipes-graphics/mesa/mesa-pvr/0031-egl-add-support-for-EXT_yuv_surface.patch

@@ -0,0 +1,539 @@
+From a94bb1db20b8d60184af23a117ae2602e6c50290 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Wed, 20 Dec 2017 17:41:38 +0000
+Subject: [PATCH] egl: add support for EXT_yuv_surface
+
+This implements EXT_yuv_surface but doesn't expose it for any platform.
+
+---
+ include/GL/internal/dri_interface.h |   1 +
+ src/egl/drivers/dri2/egl_dri2.c     |  83 +++++++++
+ src/egl/main/eglapi.c               |   1 +
+ src/egl/main/eglconfig.c            | 255 +++++++++++++++++++++++++++-
+ src/egl/main/eglconfig.h            |  12 ++
+ src/egl/main/egldisplay.h           |   1 +
+ 6 files changed, 348 insertions(+), 5 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 4d5a187..62517e4 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1154,6 +1154,7 @@ enum dri_loader_cap {
+     */
+    DRI_LOADER_CAP_RGBA_ORDERING,
+    DRI_LOADER_CAP_FP16,
++   DRI_LOADER_CAP_YUV_SURFACE_IMG = 0x7001,
+ };
+ 
+ struct __DRIdri2LoaderExtensionRec {
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index db993f8..ee3bcda 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -306,6 +306,7 @@ static const EGLint dri2_to_egl_attribute_map[__DRI_ATTRIB_MAX] = {
+    [__DRI_ATTRIB_MAX_SWAP_INTERVAL]     = EGL_MAX_SWAP_INTERVAL,
+    [__DRI_ATTRIB_MIN_SWAP_INTERVAL]     = EGL_MIN_SWAP_INTERVAL,
+    [__DRI_ATTRIB_YINVERTED]             = EGL_Y_INVERTED_NOK,
++   [__DRI_ATTRIB_YUV_NUMBER_OF_PLANES]  = EGL_YUV_NUMBER_OF_PLANES_EXT,
+ };
+ 
+ const __DRIconfig *
+@@ -432,6 +433,8 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
+             value = EGL_RGB_BUFFER;
+          else if (value & __DRI_ATTRIB_LUMINANCE_BIT)
+             value = EGL_LUMINANCE_BUFFER;
++         else if (value & __DRI_ATTRIB_YUV_BIT)
++            value = EGL_YUV_BUFFER_EXT;
+          else
+             return NULL;
+          base.ColorBufferType = value;
+@@ -536,6 +539,73 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
+          if (disp->Extensions.KHR_mutable_render_buffer)
+             surface_type |= EGL_MUTABLE_RENDER_BUFFER_BIT_KHR;
+          break;
++
++      case __DRI_ATTRIB_YUV_ORDER:
++         if (value & __DRI_ATTRIB_YUV_ORDER_YUV_BIT)
++            value = EGL_YUV_ORDER_YUV_EXT;
++         else if (value & __DRI_ATTRIB_YUV_ORDER_YVU_BIT)
++            value = EGL_YUV_ORDER_YVU_EXT;
++         else if (value & __DRI_ATTRIB_YUV_ORDER_YUYV_BIT)
++            value = EGL_YUV_ORDER_YUYV_EXT;
++         else if (value & __DRI_ATTRIB_YUV_ORDER_UYVY_BIT)
++            value = EGL_YUV_ORDER_UYVY_EXT;
++         else if (value & __DRI_ATTRIB_YUV_ORDER_YVYU_BIT)
++            value = EGL_YUV_ORDER_YVYU_EXT;
++         else if (value & __DRI_ATTRIB_YUV_ORDER_VYUY_BIT)
++            value = EGL_YUV_ORDER_VYUY_EXT;
++         else if (value & __DRI_ATTRIB_YUV_ORDER_AYUV_BIT)
++            value = EGL_YUV_ORDER_AYUV_EXT;
++         else
++            value = EGL_NONE;
++         _eglSetConfigKey(&base, EGL_YUV_ORDER_EXT, value);
++         break;
++
++      case __DRI_ATTRIB_YUV_SUBSAMPLE:
++         if (value & __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT)
++            value = EGL_YUV_SUBSAMPLE_4_2_0_EXT;
++         else if (value & __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_2_BIT)
++            value = EGL_YUV_SUBSAMPLE_4_2_2_EXT;
++         else if (value & __DRI_ATTRIB_YUV_SUBSAMPLE_4_4_4_BIT)
++            value = EGL_YUV_SUBSAMPLE_4_4_4_EXT;
++         else
++            value = EGL_NONE;
++         _eglSetConfigKey(&base, EGL_YUV_SUBSAMPLE_EXT, value);
++         break;
++
++      case __DRI_ATTRIB_YUV_DEPTH_RANGE:
++         if (value & __DRI_ATTRIB_YUV_DEPTH_RANGE_LIMITED_BIT)
++            value = EGL_YUV_DEPTH_RANGE_LIMITED_EXT;
++         else if (value & __DRI_ATTRIB_YUV_DEPTH_RANGE_FULL_BIT)
++            value = EGL_YUV_DEPTH_RANGE_FULL_EXT;
++         else
++            value = EGL_NONE;
++         _eglSetConfigKey(&base, EGL_YUV_DEPTH_RANGE_EXT, value);
++         break;
++
++      case __DRI_ATTRIB_YUV_CSC_STANDARD:
++         if (value & __DRI_ATTRIB_YUV_CSC_STANDARD_601_BIT)
++            value = EGL_YUV_CSC_STANDARD_601_EXT;
++         else if (value & __DRI_ATTRIB_YUV_CSC_STANDARD_709_BIT)
++            value = EGL_YUV_CSC_STANDARD_709_EXT;
++         else if (value & __DRI_ATTRIB_YUV_CSC_STANDARD_2020_BIT)
++            value = EGL_YUV_CSC_STANDARD_2020_EXT;
++         else
++            value = EGL_NONE;
++         _eglSetConfigKey(&base, EGL_YUV_CSC_STANDARD_EXT, value);
++         break;
++
++      case __DRI_ATTRIB_YUV_PLANE_BPP:
++         if (value & __DRI_ATTRIB_YUV_PLANE_BPP_0_BIT)
++            value = EGL_YUV_PLANE_BPP_0_EXT;
++         else if (value & __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT)
++            value = EGL_YUV_PLANE_BPP_8_EXT;
++         else if (value & __DRI_ATTRIB_YUV_PLANE_BPP_10_BIT)
++            value = EGL_YUV_PLANE_BPP_10_EXT;
++         else
++            value = EGL_NONE;
++         _eglSetConfigKey(&base, EGL_YUV_PLANE_BPP_EXT, value);
++         break;
++
+       default:
+          key = dri2_to_egl_attribute_map[attrib];
+          if (key != 0)
+@@ -583,6 +653,17 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
+    base.RenderableType = disp->ClientAPIs;
+    base.Conformant = disp->ClientAPIs;
+ 
++   /*
++    * We assume that if dri_config is YUV then GL_EXT_YUV_target must be
++    * supported, which requires OpenGL ES 3.0.
++    */
++   if (base.ColorBufferType == EGL_YUV_BUFFER_EXT) {
++      base.RenderableType &= EGL_OPENGL_ES3_BIT;
++      base.Conformant &= EGL_OPENGL_ES3_BIT;
++   }
++   if (!base.RenderableType)
++      return NULL;
++
+    base.MinSwapInterval = dri2_dpy->min_swap_interval;
+    base.MaxSwapInterval = dri2_dpy->max_swap_interval;
+ 
+@@ -1014,6 +1095,8 @@ dri2_setup_screen(_EGLDisplay *disp)
+    disp->Extensions.EXT_protected_surface =
+       dri2_renderer_query_integer(dri2_dpy,
+                                   __DRI2_RENDERER_HAS_PROTECTED_CONTENT);
++
++   disp->Extensions.EXT_yuv_surface = EGL_TRUE;
+ }
+ 
+ void
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index e2a7797..6d7f62c 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -512,6 +512,7 @@ _eglCreateExtensionsString(_EGLDisplay *disp)
+    _EGL_CHECK_EXTENSION(EXT_surface_CTA861_3_metadata);
+    _EGL_CHECK_EXTENSION(EXT_surface_SMPTE2086_metadata);
+    _EGL_CHECK_EXTENSION(EXT_swap_buffers_with_damage);
++   _EGL_CHECK_EXTENSION(EXT_yuv_surface);
+ 
+    _EGL_CHECK_EXTENSION(IMG_context_priority);
+ 
+diff --git a/src/egl/main/eglconfig.c b/src/egl/main/eglconfig.c
+index 17d8f35..5e56948 100644
+--- a/src/egl/main/eglconfig.c
++++ b/src/egl/main/eglconfig.c
+@@ -258,6 +258,24 @@ static const struct {
+    { EGL_COLOR_COMPONENT_TYPE_EXT,  ATTRIB_TYPE_ENUM,
+                                     ATTRIB_CRITERION_EXACT,
+                                     EGL_COLOR_COMPONENT_TYPE_FIXED_EXT },
++   { EGL_YUV_ORDER_EXT,             ATTRIB_TYPE_ENUM,
++                                    ATTRIB_CRITERION_EXACT,
++                                    EGL_DONT_CARE },
++   { EGL_YUV_NUMBER_OF_PLANES_EXT,  ATTRIB_TYPE_INTEGER,
++                                    ATTRIB_CRITERION_ATLEAST,
++                                    0 },
++   { EGL_YUV_SUBSAMPLE_EXT,         ATTRIB_TYPE_ENUM,
++                                    ATTRIB_CRITERION_EXACT,
++                                    EGL_DONT_CARE },
++   { EGL_YUV_DEPTH_RANGE_EXT,       ATTRIB_TYPE_ENUM,
++                                    ATTRIB_CRITERION_EXACT,
++                                    EGL_DONT_CARE },
++   { EGL_YUV_CSC_STANDARD_EXT,      ATTRIB_TYPE_ENUM,
++                                    ATTRIB_CRITERION_EXACT,
++                                    EGL_DONT_CARE },
++   { EGL_YUV_PLANE_BPP_EXT,         ATTRIB_TYPE_ENUM,
++                                    ATTRIB_CRITERION_EXACT,
++                                    EGL_DONT_CARE },
+ };
+ 
+ 
+@@ -296,6 +314,28 @@ _eglValidateConfig(const _EGLConfig *conf, EGLBoolean for_matching)
+             if (val > 1 || val < 0)
+                valid = EGL_FALSE;
+             break;
++         case EGL_YUV_NUMBER_OF_PLANES_EXT:
++            /* From the EGL_EXT_yuv_surface spec (v9):
++             *
++             *     The allowed values for EGL_YUV_NUMBER_OF_PLANES_EXT must
++             *     be greater than zero and not more than three.
++             *
++             * However, it also says:
++             *
++             *     Attribute                     Default  Selection  Sort   Sort
++             *                                            Criteria   Order  Priority
++             *     ----------------------------  -------  ---------  -----  --------
++             *     EGL_YUV_NUMBER_OF_PLANES_EXT     0     At least   None
++             *
++             * This means that we need to allow the value 0 when doing config
++             * matching (where it's essentially treated as EGL_DONT_CARE).
++             * Furthermore, this attribute isn't applicable to non-YUV EGL
++             * color buffer types. Allow 0 through here and then do further
++             * validation later on.
++             */
++            if (val < 0 || val > 3)
++               valid = EGL_FALSE;
++            break;
+          default:
+             if (val < 0)
+                valid = EGL_FALSE;
+@@ -318,7 +358,43 @@ _eglValidateConfig(const _EGLConfig *conf, EGLBoolean for_matching)
+                valid = EGL_FALSE;
+             break;
+          case EGL_COLOR_BUFFER_TYPE:
+-            if (val != EGL_RGB_BUFFER && val != EGL_LUMINANCE_BUFFER)
++            if (val != EGL_RGB_BUFFER && val != EGL_LUMINANCE_BUFFER &&
++                val != EGL_YUV_BUFFER_EXT)
++               valid = EGL_FALSE;
++            break;
++         case EGL_YUV_ORDER_EXT:
++            if (val != EGL_NONE &&
++                val != EGL_YUV_ORDER_YUV_EXT && val != EGL_YUV_ORDER_YVU_EXT &&
++                val != EGL_YUV_ORDER_YUYV_EXT && val != EGL_YUV_ORDER_UYVY_EXT &&
++                val != EGL_YUV_ORDER_YVYU_EXT && val != EGL_YUV_ORDER_VYUY_EXT &&
++                val != EGL_YUV_ORDER_AYUV_EXT)
++               valid = EGL_FALSE;
++            break;
++         case EGL_YUV_SUBSAMPLE_EXT:
++            if (val != EGL_NONE &&
++                val != EGL_YUV_SUBSAMPLE_4_2_0_EXT &&
++                val != EGL_YUV_SUBSAMPLE_4_2_2_EXT &&
++                val != EGL_YUV_SUBSAMPLE_4_4_4_EXT)
++               valid = EGL_FALSE;
++            break;
++         case EGL_YUV_DEPTH_RANGE_EXT:
++            if (val != EGL_NONE &&
++                val != EGL_YUV_DEPTH_RANGE_LIMITED_EXT &&
++                val != EGL_YUV_DEPTH_RANGE_FULL_EXT)
++               valid = EGL_FALSE;
++            break;
++         case EGL_YUV_CSC_STANDARD_EXT:
++            if (val != EGL_NONE &&
++                val != EGL_YUV_CSC_STANDARD_601_EXT &&
++                val != EGL_YUV_CSC_STANDARD_709_EXT &&
++                val != EGL_YUV_CSC_STANDARD_2020_EXT)
++               valid = EGL_FALSE;
++            break;
++         case EGL_YUV_PLANE_BPP_EXT:
++            if (val != EGL_NONE &&
++                val != EGL_YUV_PLANE_BPP_0_EXT &&
++                val != EGL_YUV_PLANE_BPP_8_EXT &&
++                val != EGL_YUV_PLANE_BPP_10_EXT)
+                valid = EGL_FALSE;
+             break;
+          case EGL_COLOR_COMPONENT_TYPE_EXT:
+@@ -404,6 +480,11 @@ _eglValidateConfig(const _EGLConfig *conf, EGLBoolean for_matching)
+       if (conf->LuminanceSize + conf->AlphaSize != conf->BufferSize)
+          valid = EGL_FALSE;
+       break;
++   case EGL_YUV_BUFFER_EXT:
++      if (conf->RedSize || conf->GreenSize || conf->BlueSize ||
++          conf->AlphaSize || conf->LuminanceSize)
++         valid = EGL_FALSE;
++      break;
+    }
+    if (!valid) {
+       _eglLog(_EGL_DEBUG, "conflicting color buffer type and channel sizes");
+@@ -430,6 +511,88 @@ _eglValidateConfig(const _EGLConfig *conf, EGLBoolean for_matching)
+       return EGL_FALSE;
+    }
+ 
++   /* From the EGL_EXT_yuv_surface spec (v9):
++    *
++    *   SUBSAMPLE_EXT        NUMBER_OF_PLANES_EXT  ORDER_EXT          PLANE_BPP_EXT
++    *   -------------------  --------------------  -----------------  ------------------
++    *   SUBSAMPLE_4_2_0_EXT         2 or 3         ORDER_YUV_EXT or   PLANE_BPP_8_EXT or
++    *                                              ORDER_YVU_EXT      PLANE_BPP_10_EXT
++    */
++   if (conf->YUVSubsampleEXT == EGL_YUV_SUBSAMPLE_4_2_0_EXT) {
++      if ((!for_matching || conf->YUVNumberOfPlanesEXT != 0) &&
++          conf->YUVNumberOfPlanesEXT != 2 &&
++          conf->YUVNumberOfPlanesEXT != 3)
++         valid = EGL_FALSE;
++      if (conf->YUVOrderEXT != EGL_DONT_CARE &&
++          conf->YUVOrderEXT != EGL_YUV_ORDER_YUV_EXT &&
++          conf->YUVOrderEXT != EGL_YUV_ORDER_YVU_EXT)
++         valid = EGL_FALSE;
++      if (conf->YUVPlaneBPPEXT != EGL_DONT_CARE &&
++          conf->YUVPlaneBPPEXT != EGL_YUV_PLANE_BPP_8_EXT &&
++          conf->YUVPlaneBPPEXT != EGL_YUV_PLANE_BPP_10_EXT)
++         valid = EGL_FALSE;
++   }
++   /* From the EGL_EXT_yuv_surface spec (v9):
++    *
++    *   SUBSAMPLE_EXT        NUMBER_OF_PLANES_EXT  ORDER_EXT          PLANE_BPP_EXT
++    *   -------------------  --------------------  -----------------  ------------------
++    *   SUBSAMPLE_4_2_2_EXT            1           ORDER_YUYV_EXT or  PLANE_BPP_8_EXT or
++    *                                              ORDER_YVYU_EXT or  PLANE_BPP_10_EXT
++    *                                              ORDER_UYVY_EXT or
++    *                                              ORDER_VYUY_EXT
++    *
++    *   SUBSAMPLE_4_2_2_EXT          2 or 3        ORDER_YUV_EXT or   PLANE_BPP_8_EXT or
++    *                                              ORDER_YVU_EXT      PLANE_BPP_10_EXT
++    */
++   else if (conf->YUVSubsampleEXT == EGL_YUV_SUBSAMPLE_4_2_2_EXT) {
++      if ((!for_matching || conf->YUVNumberOfPlanesEXT != 0) &&
++          conf->YUVNumberOfPlanesEXT != 1 &&
++          conf->YUVNumberOfPlanesEXT != 2 &&
++          conf->YUVNumberOfPlanesEXT != 3)
++         valid = EGL_FALSE;
++      if (conf->YUVNumberOfPlanesEXT == 1) {
++         if (conf->YUVOrderEXT != EGL_DONT_CARE &&
++             conf->YUVOrderEXT != EGL_YUV_ORDER_YUYV_EXT &&
++             conf->YUVOrderEXT != EGL_YUV_ORDER_YVYU_EXT &&
++             conf->YUVOrderEXT != EGL_YUV_ORDER_UYVY_EXT &&
++             conf->YUVOrderEXT != EGL_YUV_ORDER_VYUY_EXT)
++            valid = EGL_FALSE;
++      } else if (conf->YUVNumberOfPlanesEXT == 2 ||
++                 conf->YUVNumberOfPlanesEXT == 3) {
++         if (conf->YUVOrderEXT != EGL_DONT_CARE &&
++             conf->YUVOrderEXT != EGL_YUV_ORDER_YUV_EXT &&
++             conf->YUVOrderEXT != EGL_YUV_ORDER_YVU_EXT)
++            valid = EGL_FALSE;
++      }
++      if (conf->YUVPlaneBPPEXT != EGL_DONT_CARE &&
++          conf->YUVPlaneBPPEXT != EGL_YUV_PLANE_BPP_8_EXT &&
++          conf->YUVPlaneBPPEXT != EGL_YUV_PLANE_BPP_10_EXT)
++         valid = EGL_FALSE;
++   }
++   /* From the EGL_EXT_yuv_surface spec (v9):
++    *
++    *   SUBSAMPLE_EXT        NUMBER_OF_PLANES_EXT  ORDER_EXT          PLANE_BPP_EXT
++    *   -------------------  --------------------  -----------------  ------------------
++    *   SUBSAMPLE_4_4_4_EXT            1           ORDER_AYUV_EXT     PLANE_BPP_8_EXT or
++    *                                                                 PLANE_BPP_10_EXT
++    */
++   else if (conf->YUVSubsampleEXT == EGL_YUV_SUBSAMPLE_4_4_4_EXT) {
++      if ((!for_matching || conf->YUVNumberOfPlanesEXT != 0) &&
++          conf->YUVNumberOfPlanesEXT != 1)
++         valid = EGL_FALSE;
++      if (conf->YUVOrderEXT != EGL_DONT_CARE &&
++          conf->YUVOrderEXT != EGL_YUV_ORDER_AYUV_EXT)
++         valid = EGL_FALSE;
++      if (conf->YUVPlaneBPPEXT != EGL_DONT_CARE &&
++          conf->YUVPlaneBPPEXT != EGL_YUV_PLANE_BPP_8_EXT &&
++          conf->YUVPlaneBPPEXT != EGL_YUV_PLANE_BPP_10_EXT)
++         valid = EGL_FALSE;
++   }
++   if (!valid) {
++      _eglLog(_EGL_DEBUG, "invalid YUV subsample/num planes/order/bpp combination");
++      return EGL_FALSE;
++   }
++
+    return valid;
+ }
+ 
+@@ -509,6 +672,28 @@ _eglIsConfigAttribValid(_EGLConfig *conf, EGLint attr)
+       return conf->Display->Extensions.ANDROID_framebuffer_target;
+    case EGL_RECORDABLE_ANDROID:
+       return conf->Display->Extensions.ANDROID_recordable;
++   case EGL_YUV_ORDER_EXT:
++   case EGL_YUV_NUMBER_OF_PLANES_EXT:
++   case EGL_YUV_SUBSAMPLE_EXT:
++   case EGL_YUV_DEPTH_RANGE_EXT:
++   case EGL_YUV_CSC_STANDARD_EXT:
++   case EGL_YUV_PLANE_BPP_EXT:
++      return conf->Display->Extensions.EXT_yuv_surface;
++   default:
++      break;
++   }
++
++   return EGL_TRUE;
++}
++
++static inline EGLBoolean
++_eglIsConfigAttribValueValid(_EGLConfig *conf, EGLint attr, EGLint val)
++{
++   switch (attr) {
++   case EGL_COLOR_BUFFER_TYPE:
++      if (!conf->Display->Extensions.EXT_yuv_surface && val == EGL_YUV_BUFFER_EXT)
++         return EGL_FALSE;
++      break;
+    default:
+       break;
+    }
+@@ -543,6 +728,9 @@ _eglParseConfigAttribList(_EGLConfig *conf, _EGLDisplay *disp,
+       if (!_eglIsConfigAttribValid(conf, attr))
+          return EGL_FALSE;
+ 
++      if (!_eglIsConfigAttribValueValid(conf, attr, val))
++         return EGL_FALSE;
++
+       _eglSetConfigKey(conf, attr, val);
+    }
+ 
+@@ -617,6 +805,7 @@ _eglCompareConfigs(const _EGLConfig *conf1, const _EGLConfig *conf2,
+ 
+    /* the enum values have the desired ordering */
+    STATIC_ASSERT(EGL_RGB_BUFFER < EGL_LUMINANCE_BUFFER);
++   STATIC_ASSERT(EGL_LUMINANCE_BUFFER < EGL_YUV_BUFFER_EXT);
+    val1 = conf1->ColorBufferType - conf2->ColorBufferType;
+    if (val1)
+       return val1;
+@@ -636,16 +825,42 @@ _eglCompareConfigs(const _EGLConfig *conf1, const _EGLConfig *conf2,
+             val1 += conf1->BlueSize;
+             val2 += conf2->BlueSize;
+          }
++         if (criteria->AlphaSize > 0) {
++            val1 += conf1->AlphaSize;
++            val2 += conf2->AlphaSize;
++         }
+       }
+-      else {
++      else if (conf1->ColorBufferType == EGL_LUMINANCE_BUFFER) {
+          if (criteria->LuminanceSize > 0) {
+             val1 += conf1->LuminanceSize;
+             val2 += conf2->LuminanceSize;
+          }
++         if (criteria->AlphaSize > 0) {
++            val1 += conf1->AlphaSize;
++            val2 += conf2->AlphaSize;
++         }
+       }
+-      if (criteria->AlphaSize > 0) {
+-         val1 += conf1->AlphaSize;
+-         val2 += conf2->AlphaSize;
++      else {
++         /* From the EGL_EXT_yuv_surface spec (v9):
++          *
++          *     Special: by larger total number of color bits
++          *     ...
++          *     for YUV color buffers, this returns the integer value with
++          *     respect to the enumeration provided for EGL_YUV_PLANE_BPP_EXT
++          *
++          * and:
++          *
++          *     EGL_BUFFER_SIZE gives the total of the color component bits of
++          *     the color buffer for EGL_RGB_BUFFER or for EGL_LUMINANCE_BUFFER.
++          *     ...
++          *     When EGL_COLOR_BUFFER_TYPE is of type EGL_YUV_BUFFER_EXT,
++          *     this will reflect the enumeration provided as an integer)
++          *     for EGL_YUV_PLANE_BPP_EXT, giving a value of 0, 8 or 10
++          */
++         if (criteria->BufferSize > 0) {
++            val1 = conf1->BufferSize;
++            val2 = conf2->BufferSize;
++         }
+       }
+    }
+    else {
+@@ -664,6 +879,36 @@ _eglCompareConfigs(const _EGLConfig *conf1, const _EGLConfig *conf2,
+          return (val1 - val2);
+    }
+ 
++   if (conf1->YUVOrderEXT != conf2->YUVOrderEXT)
++   {
++      const EGLint yuv_order[] = {
++         EGL_NONE,
++         EGL_YUV_ORDER_YUV_EXT,
++         EGL_YUV_ORDER_YVU_EXT,
++         EGL_YUV_ORDER_YUYV_EXT,
++         EGL_YUV_ORDER_YVYU_EXT,
++         EGL_YUV_ORDER_UYVY_EXT,
++         EGL_YUV_ORDER_VYUY_EXT,
++         EGL_YUV_ORDER_AYUV_EXT,
++      };
++
++      val1 = val2 = 0;
++      for (i = 0; i < ARRAY_SIZE(yuv_order); i++) {
++         if (yuv_order[i] == conf1->YUVOrderEXT) {
++            val1 = i;
++            break;
++         }
++      }
++      for (i = 0; i < ARRAY_SIZE(yuv_order); i++) {
++         if (yuv_order[i] == conf2->YUVOrderEXT) {
++            val2 = i;
++            break;
++         }
++      }
++      if (val1 != val2)
++         return val1 - val2;
++   }
++
+    /* EGL_NATIVE_VISUAL_TYPE cannot be compared here */
+ 
+    return (compare_id) ? (conf1->ConfigID - conf2->ConfigID) : 0;
+diff --git a/src/egl/main/eglconfig.h b/src/egl/main/eglconfig.h
+index 1f2a400..d4a7489 100644
+--- a/src/egl/main/eglconfig.h
++++ b/src/egl/main/eglconfig.h
+@@ -89,6 +89,12 @@ struct _egl_config
+    EGLint FramebufferTargetAndroid;
+    EGLint RecordableAndroid;
+    EGLint ComponentType;
++   EGLint YUVOrderEXT;
++   EGLint YUVNumberOfPlanesEXT;
++   EGLint YUVSubsampleEXT;
++   EGLint YUVDepthRangeEXT;
++   EGLint YUVCSCStandardEXT;
++   EGLint YUVPlaneBPPEXT;
+ };
+ 
+ 
+@@ -139,6 +145,12 @@ _eglOffsetOfConfig(EGLint attr)
+    ATTRIB_MAP(EGL_FRAMEBUFFER_TARGET_ANDROID, FramebufferTargetAndroid);
+    ATTRIB_MAP(EGL_RECORDABLE_ANDROID,        RecordableAndroid);
+    ATTRIB_MAP(EGL_COLOR_COMPONENT_TYPE_EXT,  ComponentType);
++   ATTRIB_MAP(EGL_YUV_ORDER_EXT,             YUVOrderEXT);
++   ATTRIB_MAP(EGL_YUV_NUMBER_OF_PLANES_EXT,  YUVNumberOfPlanesEXT);
++   ATTRIB_MAP(EGL_YUV_SUBSAMPLE_EXT,         YUVSubsampleEXT);
++   ATTRIB_MAP(EGL_YUV_DEPTH_RANGE_EXT,       YUVDepthRangeEXT);
++   ATTRIB_MAP(EGL_YUV_CSC_STANDARD_EXT,      YUVCSCStandardEXT);
++   ATTRIB_MAP(EGL_YUV_PLANE_BPP_EXT,         YUVPlaneBPPEXT);
+ #undef ATTRIB_MAP
+    default:
+       return -1;
+diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
+index 0a7366b..020529c 100644
+--- a/src/egl/main/egldisplay.h
++++ b/src/egl/main/egldisplay.h
+@@ -112,6 +112,7 @@ struct _egl_extensions
+    EGLBoolean EXT_surface_CTA861_3_metadata;
+    EGLBoolean EXT_surface_SMPTE2086_metadata;
+    EGLBoolean EXT_swap_buffers_with_damage;
++   EGLBoolean EXT_yuv_surface;
+ 
+    unsigned int IMG_context_priority;
+ #define  __EGL_CONTEXT_PRIORITY_LOW_BIT    0

+ 33 - 0
recipes-graphics/mesa/mesa-pvr/0032-dri-add-missing-__DRI_IMAGE_COMPONENTS-define-for-EG.patch

@@ -0,0 +1,33 @@
+From 01febf756ccf6ffd8a8a0a16a465f755c32fc2de Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Fri, 2 Feb 2018 16:59:52 +0000
+Subject: [PATCH] dri: add missing __DRI_IMAGE_COMPONENTS define for
+ EGL_TEXTURE_EXTERNAL_WL
+
+The __DRI_IMAGE_COMPONENTS defines have been re-ordered, to make it
+less likely that new defines will be added with the same values as
+existing ones.
+
+---
+ include/GL/internal/dri_interface.h | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 62517e4..6490f6e 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1461,11 +1461,12 @@ struct __DRIdri2ExtensionRec {
+ #define __DRI_IMAGE_COMPONENTS_Y_U_V	0x3003
+ #define __DRI_IMAGE_COMPONENTS_Y_UV	0x3004
+ #define __DRI_IMAGE_COMPONENTS_Y_XUXV	0x3005
++#define __DRI_IMAGE_COMPONENTS_R	0x3006
++#define __DRI_IMAGE_COMPONENTS_RG	0x3007
+ #define __DRI_IMAGE_COMPONENTS_Y_UXVX	0x3008
+ #define __DRI_IMAGE_COMPONENTS_AYUV	0x3009
+ #define __DRI_IMAGE_COMPONENTS_XYUV	0x300A
+-#define __DRI_IMAGE_COMPONENTS_R	0x3006
+-#define __DRI_IMAGE_COMPONENTS_RG	0x3007
++#define __DRI_IMAGE_COMPONENTS_EXTERNAL 0x300B
+ 
+ 
+ /**

+ 72 - 0
recipes-graphics/mesa/mesa-pvr/0033-egl-wayland-expose-EXT_yuv_surface-support.patch

@@ -0,0 +1,72 @@
+From ae4a6061fefd930234998c6d81942edeecafbd10 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Thu, 11 Jan 2018 09:38:47 +0000
+Subject: [PATCH] egl/wayland: expose EXT_yuv_surface support
+
+This adds support for YUYV configs.
+
+---
+ src/egl/drivers/dri2/egl_dri2.c         |  1 +
+ src/egl/drivers/dri2/platform_wayland.c | 15 ++++++++++++++-
+ 2 files changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index ee3bcda..d511d73 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -2769,6 +2769,7 @@ static const struct wl_drm_components_descriptor {
+    { __DRI_IMAGE_COMPONENTS_Y_U_V, EGL_TEXTURE_Y_U_V_WL, 3 },
+    { __DRI_IMAGE_COMPONENTS_Y_UV, EGL_TEXTURE_Y_UV_WL, 2 },
+    { __DRI_IMAGE_COMPONENTS_Y_XUXV, EGL_TEXTURE_Y_XUXV_WL, 2 },
++   { __DRI_IMAGE_COMPONENTS_EXTERNAL, EGL_TEXTURE_EXTERNAL_WL, 1 },
+ };
+ 
+ static _EGLImage *
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index a096d95..72456d5 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -135,6 +135,13 @@ static const struct dri2_wl_visual {
+       { 11, 5, 0, -1 },
+       { 5, 6, 5, 0 },
+    },
++   {
++     "YUYV",
++     WL_DRM_FORMAT_YUYV, WL_SHM_FORMAT_YUYV,
++     __DRI_IMAGE_FORMAT_YUYV, __DRI_IMAGE_FORMAT_NONE, 32,
++     { -1, -1, -1, -1 },
++     { 0, 0, 0, 0 },
++   },
+ };
+ 
+ static_assert(ARRAY_SIZE(dri2_wl_visuals) <= EGL_DRI2_MAX_FORMATS,
+@@ -958,6 +965,7 @@ dri2_wl_get_capability(void *loaderPrivate, enum dri_loader_cap cap)
+ {
+    switch (cap) {
+    case DRI_LOADER_CAP_FP16:
++   case DRI_LOADER_CAP_YUV_SURFACE_IMG:
+       return 1;
+    default:
+       return 0;
+@@ -1552,6 +1560,7 @@ dri2_wl_add_configs_for_visuals(_EGLDisplay *disp)
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    unsigned int format_count[ARRAY_SIZE(dri2_wl_visuals)] = { 0 };
+    unsigned int count = 0;
++   EGLint surface_type;
+    bool assigned;
+ 
+    for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) {
+@@ -1563,8 +1572,12 @@ dri2_wl_add_configs_for_visuals(_EGLDisplay *disp)
+          if (!BITSET_TEST(dri2_dpy->formats, j))
+             continue;
+ 
++         surface_type = EGL_WINDOW_BIT;
++         if (dri2_wl_visuals[j].wl_drm_format != WL_DRM_FORMAT_YUYV)
++            surface_type |= EGL_PBUFFER_BIT;
++
+          dri2_conf = dri2_add_config(disp, dri2_dpy->driver_configs[i],
+-               count + 1, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, NULL, dri2_wl_visuals[j].rgba_shifts, dri2_wl_visuals[j].rgba_sizes);
++               count + 1, surface_type, NULL, dri2_wl_visuals[j].rgba_shifts, dri2_wl_visuals[j].rgba_sizes);
+          if (dri2_conf) {
+             if (dri2_conf->base.ConfigID == count + 1)
+                count++;

+ 355 - 0
recipes-graphics/mesa/mesa-pvr/0034-egl-tizen-expose-EXT_yuv_surface-support.patch

@@ -0,0 +1,355 @@
+From 6bdf417b37b8b8d50facbb610ab2b936bf5cc626 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Tue, 13 Feb 2018 14:47:48 +0000
+Subject: [PATCH] egl/tizen: expose EXT_yuv_surface support
+
+This adds support for NV12 and NV21 configs.
+
+---
+ src/egl/drivers/dri2/platform_tizen.c | 278 ++++++++++++++++++--------
+ 1 file changed, 200 insertions(+), 78 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_tizen.c b/src/egl/drivers/dri2/platform_tizen.c
+index 2bc9b3e..b6478a1 100644
+--- a/src/egl/drivers/dri2/platform_tizen.c
++++ b/src/egl/drivers/dri2/platform_tizen.c
+@@ -100,37 +100,93 @@ create_image_from_native(struct dri2_egl_surface *dri2_surf,
+ {
+    _EGLSurface *surf = &dri2_surf->base;
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(surf->Resource.Display);
++   struct dri2_egl_config *dri2_conf = dri2_egl_config(surf->Config);
++   const __DRIconfig *config =
++      dri2_get_dri_config(dri2_conf, surf->Type, surf->GLColorspace);
+    tbm_bo tbm_buf;
+    tbm_surface_info_s info;
+-   int fd, fourcc, offset, pitch;
++   int fd[TBM_SURF_PLANE_MAX];
++   int offset[TBM_SURF_PLANE_MAX];
++   int pitch[TBM_SURF_PLANE_MAX];
++   int fourcc;
+    __DRIimage *dri_image;
+-
+-   tbm_buf = tbm_surface_internal_get_bo(tbm_surf, 0);
+-   if (!tbm_buf) {
+-      _eglLog(_EGL_DEBUG, "%s: failed to get bo for tbm surface", __func__);
+-      return NULL;
+-   }
++   enum __DRIYUVColorSpace color_space;
++   enum __DRISampleRange sample_range;
++   unsigned csc_standard;
++   unsigned depth_range;
++   unsigned create_error;
+ 
+    if (tbm_surface_get_info(tbm_surf, &info)) {
+       _eglLog(_EGL_DEBUG, "%s: failed to get tbm surface info", __func__);
+       return NULL;
+    }
+ 
+-   fd = tbm_bo_export_fd(tbm_buf);
+    fourcc = dri2_fourcc_from_tbm_format(info.format);
+-   offset = info.planes[0].offset;
+-   pitch = info.planes[0].stride;
+-
+-   dri_image = dri2_dpy->image->createImageFromFds(dri2_dpy->dri_screen,
+-                                                   info.width,
+-                                                   info.height,
+-                                                   fourcc,
+-                                                   &fd,
+-                                                   1,
+-                                                   &pitch,
+-                                                   &offset,
+-                                                   loaderPrivate);
+-   close(fd);
++
++   for (unsigned i = 0; i < info.num_planes; i++) {
++      int index = tbm_surface_internal_get_plane_bo_idx(tbm_surf, i);
++
++      tbm_buf = tbm_surface_internal_get_bo(tbm_surf, index);
++      if (!tbm_buf) {
++         while (i--)
++            close(fd[i]);
++         _eglLog(_EGL_DEBUG, "%s: failed to get bo %d for tbm surface",
++                 __func__, i);
++         return NULL;
++      }
++
++      fd[i] = tbm_bo_export_fd(tbm_buf);
++      offset[i] = info.planes[i].offset;
++      pitch[i] = info.planes[i].stride;
++   }
++
++   dri2_dpy->core->getConfigAttrib(config,
++                                   __DRI_ATTRIB_YUV_CSC_STANDARD, &csc_standard);
++   switch (csc_standard) {
++   case __DRI_ATTRIB_YUV_CSC_STANDARD_601_BIT:
++      color_space = __DRI_YUV_COLOR_SPACE_ITU_REC601;
++      break;
++   case __DRI_ATTRIB_YUV_CSC_STANDARD_709_BIT:
++      color_space = __DRI_YUV_COLOR_SPACE_ITU_REC709;
++      break;
++   case __DRI_ATTRIB_YUV_CSC_STANDARD_2020_BIT:
++      color_space = __DRI_YUV_COLOR_SPACE_ITU_REC2020;
++      break;
++   default:
++      color_space = __DRI_YUV_COLOR_SPACE_UNDEFINED;
++      break;
++   }
++
++   dri2_dpy->core->getConfigAttrib(config,
++                                   __DRI_ATTRIB_YUV_DEPTH_RANGE, &depth_range);
++   switch (depth_range) {
++   case __DRI_ATTRIB_YUV_DEPTH_RANGE_LIMITED_BIT:
++      sample_range = __DRI_YUV_NARROW_RANGE;
++      break;
++   case __DRI_ATTRIB_YUV_DEPTH_RANGE_FULL_BIT:
++      sample_range = __DRI_YUV_FULL_RANGE;
++      break;
++   default:
++      sample_range = __DRI_YUV_RANGE_UNDEFINED;
++      break;
++   }
++
++   dri_image = dri2_dpy->image->createImageFromDmaBufs(dri2_dpy->dri_screen,
++                                                       info.width,
++                                                       info.height,
++                                                       fourcc,
++                                                       fd,
++                                                       info.num_planes,
++                                                       pitch,
++                                                       offset,
++                                                       color_space,
++                                                       sample_range,
++                                                       __DRI_YUV_CHROMA_SITING_UNDEFINED,
++                                                       __DRI_YUV_CHROMA_SITING_UNDEFINED,
++                                                       &create_error,
++                                                       loaderPrivate);
++   for (unsigned i = 0; i < info.num_planes; i++)
++      close(fd[i]);
+ 
+    if (!dri_image) {
+       _eglLog(_EGL_DEBUG, "%s: failed to create dri image from tbm bo", __func__);
+@@ -792,10 +848,22 @@ dri2_tizen_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
+ {
+ }
+ 
++static unsigned
++dri2_tizen_get_capability(void *loaderPrivate, enum dri_loader_cap cap)
++{
++   switch (cap) {
++   case DRI_LOADER_CAP_YUV_SURFACE_IMG:
++      return 1;
++   default:
++      return 0;
++   }
++}
++
+ static const __DRIimageLoaderExtension tizen_image_loader_extension = {
+-   .base             = { __DRI_IMAGE_LOADER, 1 },
++   .base             = { __DRI_IMAGE_LOADER, 2 },
+    .getBuffers       = dri2_tizen_get_buffers,
+    .flushFrontBuffer = dri2_tizen_flush_front_buffer,
++   .getCapability    = dri2_tizen_get_capability,
+ };
+ 
+ static const __DRIextension *image_loader_extensions[] = {
+@@ -804,6 +872,41 @@ static const __DRIextension *image_loader_extensions[] = {
+    NULL,
+ };
+ 
++static EGLBoolean
++derive_yuv_native_visual_from_config(struct dri2_egl_display *dri2_dpy,
++                                     const __DRIconfig *dri_config,
++                                     int *native_visual)
++{
++   unsigned order, subsample, num_planes, plane_bpp;
++
++   dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_ORDER,
++                                   &order);
++   dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_SUBSAMPLE,
++                                   &subsample);
++   dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_NUMBER_OF_PLANES,
++                                   &num_planes);
++   dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_PLANE_BPP,
++                                   &plane_bpp);
++
++   if ((plane_bpp & __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT) == 0)
++      return EGL_FALSE;
++
++   if (num_planes != 2)
++      return EGL_FALSE;
++
++   if (subsample & __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT) {
++      if (order & __DRI_ATTRIB_YUV_ORDER_YUV_BIT) {
++         *native_visual = TBM_FORMAT_NV12;
++         return EGL_TRUE;
++      } else if (order & __DRI_ATTRIB_YUV_ORDER_YVU_BIT) {
++         *native_visual = TBM_FORMAT_NV21;
++         return EGL_TRUE;
++      }
++   }
++
++   return EGL_FALSE;
++}
++
+ static EGLBoolean
+ dri2_tizen_add_configs(_EGLDisplay *dpy)
+ {
+@@ -811,83 +914,102 @@ dri2_tizen_add_configs(_EGLDisplay *dpy)
+    int count = 0;
+ 
+    for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) {
+-      static const struct rgba_shifts_and_sizes pbuffer_sasa[] = {
+-         {
+-            /* ARGB8888 */
+-            { 16, 8, 0, 24 },
+-            { 8, 8, 8, 8 },
+-         },
+-         {
+-            /* RGB888 */
+-            { 16, 8, 0, -1 },
+-            { 8, 8, 8, 0 },
+-         },
+-         {
+-            /* RGB565 */
+-            { 11, 5, 0, -1 },
+-            { 5, 6, 5, 0 },
+-         },
+-      };
+       struct dri2_egl_config *dri2_cfg;
+-      int shifts[4];
+-      unsigned int sizes[4];
++      unsigned int render_type;
+       unsigned int caveat = 0;
+       int surface_type = 0;
+-      tpl_bool_t is_slow;
+       EGLint attr_list[] = {
+          EGL_NATIVE_VISUAL_ID, 0,
+          EGL_CONFIG_CAVEAT, EGL_NONE,
+          EGL_NONE,
+       };
+-      tpl_result_t res;
+-
+-      dri2_get_shifts_and_sizes(dri2_dpy->core, dri2_dpy->driver_configs[i],
+-                                shifts, sizes);
+ 
+       dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
+-                                      __DRI_ATTRIB_BUFFER_SIZE, &depth);
++                                      __DRI_ATTRIB_CONFIG_CAVEAT, &caveat);
+ 
+       dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
+-                                      __DRI_ATTRIB_CONFIG_CAVEAT, &caveat);
++                                      __DRI_ATTRIB_RENDER_TYPE, &render_type);
++
++      if (render_type & __DRI_ATTRIB_RGBA_BIT) {
++         static const struct rgba_shifts_and_sizes pbuffer_sasa[] = {
++            {
++               /* ARGB8888 */
++               { 16, 8, 0, 24 },
++               { 8, 8, 8, 8 },
++            },
++            {
++               /* RGB888 */
++               { 16, 8, 0, -1 },
++               { 8, 8, 8, 0 },
++            },
++            {
++               /* RGB565 */
++               { 11, 5, 0, -1 },
++               { 5, 6, 5, 0 },
++            },
++         };
++         int shifts[4];
++         unsigned int sizes[4];
++         unsigned int depth;
++         tpl_bool_t is_slow;
++         tpl_result_t res;
+ 
+-      res = tpl_display_query_config(dri2_dpy->tpl_dpy, TPL_SURFACE_TYPE_WINDOW,
+-                                     sizes[0], sizes[1], sizes[2], sizes[3],
+-                                     depth, &attr_list[1], &is_slow);
+-      if (res != TPL_ERROR_NONE)
+-         continue;
+-      surface_type |= EGL_WINDOW_BIT;
++         dri2_get_shifts_and_sizes(dri2_dpy->core, dri2_dpy->driver_configs[i],
++                                   shifts, sizes);
+ 
+-      if (is_slow)
+-         caveat |= __DRI_ATTRIB_SLOW_BIT;
++         dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
++                                         __DRI_ATTRIB_BUFFER_SIZE, &depth);
++
++         res = tpl_display_query_config(dri2_dpy->tpl_dpy,
++                                        TPL_SURFACE_TYPE_WINDOW,
++                                        sizes[0], sizes[1], sizes[2], sizes[3],
++                                        depth, &attr_list[1], &is_slow);
++         if (res != TPL_ERROR_NONE)
++            continue;
+ 
+-      res = tpl_display_query_config(dri2_dpy->tpl_dpy, TPL_SURFACE_TYPE_PIXMAP,
+-                                     sizes[0], sizes[1], sizes[2], sizes[3],
+-                                     depth, NULL, &is_slow);
+-      if (res == TPL_ERROR_NONE) {
+-         surface_type |= EGL_PIXMAP_BIT;
++         surface_type |= EGL_WINDOW_BIT;
+ 
+          if (is_slow)
+             caveat |= __DRI_ATTRIB_SLOW_BIT;
+-      }
+ 
+-      for (unsigned j = 0; j < ARRAY_SIZE(pbuffer_sasa); j++) {
+-         const struct rgba_shifts_and_sizes *pbuffer_sas = &pbuffer_sasa[j];
+-
+-         if (shifts[0] == pbuffer_sas->shifts[0] &&
+-             shifts[1] == pbuffer_sas->shifts[1] &&
+-             shifts[2] == pbuffer_sas->shifts[2] &&
+-             shifts[3] == pbuffer_sas->shifts[3] &&
+-             sizes[0] == pbuffer_sas->sizes[0] &&
+-             sizes[1] == pbuffer_sas->sizes[1] &&
+-             sizes[2] == pbuffer_sas->sizes[2] &&
+-             sizes[3] == pbuffer_sas->sizes[3]) {
+-            surface_type |= EGL_PBUFFER_BIT;
+-            break;
++         res = tpl_display_query_config(dri2_dpy->tpl_dpy,
++                                        TPL_SURFACE_TYPE_PIXMAP,
++                                        sizes[0], sizes[1], sizes[2], sizes[3],
++                                        depth, NULL, &is_slow);
++         if (res == TPL_ERROR_NONE) {
++            surface_type |= EGL_PIXMAP_BIT;
++
++            if (is_slow)
++               caveat |= __DRI_ATTRIB_SLOW_BIT;
+          }
+-      }
+ 
+-      if (dri2_dpy->image->base.version >= 9 && dri2_dpy->image->blitImage)
+-         surface_type |= EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
++         for (unsigned j = 0; j < ARRAY_SIZE(pbuffer_sasa); j++) {
++            const struct rgba_shifts_and_sizes *pbuffer_sas = &pbuffer_sasa[j];
++
++            if (shifts[0] == pbuffer_sas->shifts[0] &&
++                shifts[1] == pbuffer_sas->shifts[1] &&
++                shifts[2] == pbuffer_sas->shifts[2] &&
++                shifts[3] == pbuffer_sas->shifts[3] &&
++                sizes[0] == pbuffer_sas->sizes[0] &&
++                sizes[1] == pbuffer_sas->sizes[1] &&
++                sizes[2] == pbuffer_sas->sizes[2] &&
++                sizes[3] == pbuffer_sas->sizes[3]) {
++               surface_type |= EGL_PBUFFER_BIT;
++               break;
++            }
++
++         }
++
++         if (dri2_dpy->image->base.version >= 9 && dri2_dpy->image->blitImage)
++            surface_type |= EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
++      } else if (render_type & __DRI_ATTRIB_YUV_BIT) {
++         if (!derive_yuv_native_visual_from_config(dri2_dpy,
++                                                   dri2_dpy->driver_configs[i],
++                                                   &attr_list[1]))
++            continue;
++         surface_type = EGL_WINDOW_BIT;
++         caveat = 0;
++      }
+ 
+       if (caveat & __DRI_ATTRIB_NON_CONFORMANT_CONFIG)
+          attr_list[3] = EGL_NON_CONFORMANT_CONFIG;

+ 67 - 0
recipes-graphics/mesa/mesa-pvr/0035-gbm-add-some-new-GBM-formats.patch

@@ -0,0 +1,67 @@
+From 7d5e378bf6fb2c000d17805032cb044b38d1218d Mon Sep 17 00:00:00 2001
+From: Silvestrs Timofejevs <silvestrs.timofejevs@imgtec.com>
+Date: Thu, 30 Aug 2018 13:48:53 +0100
+Subject: [PATCH] gbm: add some new GBM formats
+
+GBM_FORMAT_ARGB4444
+GBM_FORMAT_BGR888
+GBM_FORMAT_YUYV
+GBM_FORMAT_YVU444_PACK10_IMG
+
+---
+ src/gbm/backends/dri/gbm_dri.c | 16 ++++++++++++++++
+ src/gbm/main/gbm.h             |  2 ++
+ 2 files changed, 18 insertions(+)
+
+diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
+index 2aa9c7c..5acb15b 100644
+--- a/src/gbm/backends/dri/gbm_dri.c
++++ b/src/gbm/backends/dri/gbm_dri.c
+@@ -514,11 +514,21 @@ static const struct gbm_dri_visual gbm_dri_visuals_table[] = {
+      { 10, 5, 0, 11 },
+      { 5, 5, 5, 1 },
+    },
++   {
++      GBM_FORMAT_ARGB4444, __DRI_IMAGE_FORMAT_ARGB4444,
++      { 8, 4, 0, 12 },
++      { 4, 4, 4, 4 },
++   },
+    {
+      GBM_FORMAT_RGB565, __DRI_IMAGE_FORMAT_RGB565,
+      { 11, 5, 0, -1 },
+      { 5, 6, 5, 0 },
+    },
++   {
++     GBM_FORMAT_BGR888, __DRI_IMAGE_FORMAT_BGR888,
++     { 0, 8, 16, -1 },
++     { 8, 8, 8, 0 },
++   },
+    {
+      GBM_FORMAT_XRGB8888, __DRI_IMAGE_FORMAT_XRGB8888,
+      { 16, 8, 0, -1 },
+@@ -571,6 +581,12 @@ static const struct gbm_dri_visual gbm_dri_visuals_table[] = {
+      { 16, 16, 16, 16 },
+      true,
+    },
++   {
++     GBM_FORMAT_YUYV, __DRI_IMAGE_FORMAT_YUYV,
++   },
++   {
++     GBM_FORMAT_YVU444_PACK10_IMG, __DRI_IMAGE_FORMAT_YVU444_PACK10_IMG,
++   },
+ };
+ 
+ static int
+diff --git a/src/gbm/main/gbm.h b/src/gbm/main/gbm.h
+index 7c82cd6..81308ab 100644
+--- a/src/gbm/main/gbm.h
++++ b/src/gbm/main/gbm.h
+@@ -167,6 +167,8 @@ enum gbm_bo_format {
+ 
+ #define GBM_FORMAT_AYUV		__gbm_fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */
+ 
++#define GBM_FORMAT_YVU444_PACK10_IMG __gbm_fourcc_code('I', 'M', 'G', '2') /* [31:0] unused:Y:Cr:Cb 2:10:10:10 little endian */
++
+ /*
+  * 2 plane YCbCr
+  * index 0 = Y plane, [7:0] Y

+ 1488 - 0
recipes-graphics/mesa/mesa-pvr/0036-egl-add-null-platform.patch

@@ -0,0 +1,1488 @@
+From 7f4c353e7d2778fff8d728c1aed247900610b725 Mon Sep 17 00:00:00 2001
+From: Frank Binns <francisbinns@gmail.com>
+Date: Sun, 5 Jun 2016 12:04:40 +0100
+Subject: [PATCH] egl: add "null" platform
+
+---
+ meson.build                          |   25 +-
+ meson_options.txt                    |    2 +-
+ src/egl/drivers/dri2/egl_dri2.c      |   11 +-
+ src/egl/drivers/dri2/egl_dri2.h      |   57 +-
+ src/egl/drivers/dri2/platform_null.c | 1179 ++++++++++++++++++++++++++
+ src/egl/main/eglapi.c                |    5 +-
+ src/egl/main/egldisplay.c            |    1 +
+ src/egl/main/egldisplay.h            |    1 +
+ src/egl/meson.build                  |    5 +
+ 9 files changed, 1277 insertions(+), 9 deletions(-)
+ create mode 100644 src/egl/drivers/dri2/platform_null.c
+
+diff --git a/meson.build b/meson.build
+index 3d9d345..2018562 100644
+--- a/meson.build
++++ b/meson.build
+@@ -340,7 +340,7 @@ endif
+ _platforms = get_option('platforms')
+ if _platforms.contains('auto')
+   if system_has_kms_drm
+-    _platforms = ['x11', 'wayland']
++    _platforms = ['x11', 'wayland', 'null']
+   elif ['darwin', 'cygwin'].contains(host_machine.system())
+     _platforms = ['x11']
+   elif ['haiku'].contains(host_machine.system())
+@@ -359,6 +359,7 @@ with_platform_wayland = _platforms.contains('wayland')
+ with_platform_haiku = _platforms.contains('haiku')
+ with_platform_windows = _platforms.contains('windows')
+ with_platform_tizen = _platforms.contains('tizen')
++with_platform_null = _platforms.contains('null')
+ 
+ if with_platform_tizen and _platforms.length() != 1
+   error('tizen cannot be enabled at the same time as other platforms')
+@@ -958,6 +959,26 @@ if with_platform_tizen
+   ]
+   pre_args += '-DHAVE_TIZEN_PLATFORM'
+ endif
++if with_platform_null
++  pre_args += '-DHAVE_NULL_PLATFORM'
++endif
++if with_platform_android
++  dep_android = [
++    dependency('cutils'),
++    dependency('hardware'),
++    dependency('sync'),
++  ]
++  if with_gallium
++    dep_android += dependency('backtrace')
++  endif
++  if get_option('platform-sdk-version') >= 26
++    dep_android += dependency('nativewindow')
++  endif
++  pre_args += '-DHAVE_ANDROID_PLATFORM'
++endif
++if with_platform_haiku
++  pre_args += '-DHAVE_HAIKU_PLATFORM'
++endif
+ 
+ prog_python = import('python').find_installation('python3')
+ has_mako = run_command(
+@@ -1583,7 +1604,7 @@ dep_libdrm = dependency(
+   'libdrm', version : '>=' + _drm_ver,
+   # GNU/Hurd includes egl_dri2, without drm.
+   required : (with_dri2 and host_machine.system() != 'gnu') or with_dri3 or
+-             with_platform_tizen
++             with_platform_tizen or with_platform_null
+ )
+ if dep_libdrm.found()
+   pre_args += '-DHAVE_LIBDRM'
+diff --git a/meson_options.txt b/meson_options.txt
+index 8230db9..d90a25f 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -23,7 +23,7 @@ option(
+   type : 'array',
+   value : ['auto'],
+   choices : [
+-    'auto', 'x11', 'wayland', 'haiku', 'android', 'windows', 'tizen',
++    'auto', 'x11', 'wayland', 'haiku', 'android', 'windows', 'tizen', 'null',
+   ],
+   description : 'window systems to support. If this is set to `auto`, all platforms applicable will be enabled.'
+ )
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index d511d73..2f8414e 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1253,6 +1253,9 @@ dri2_initialize(_EGLDisplay *disp)
+    case _EGL_PLATFORM_DEVICE:
+       ret = dri2_initialize_device(disp);
+       break;
++   case _EGL_PLATFORM_NULL:
++      ret = dri2_initialize_null(disp);
++      break;
+    case _EGL_PLATFORM_X11:
+    case _EGL_PLATFORM_XCB:
+       ret = dri2_initialize_x11(disp);
+@@ -1318,8 +1321,6 @@ dri2_display_destroy(_EGLDisplay *disp)
+          dri2_dpy->vtbl->close_screen_notify(disp);
+       dri2_dpy->core->destroyScreen(dri2_dpy->dri_screen);
+    }
+-   if (dri2_dpy->fd >= 0)
+-      close(dri2_dpy->fd);
+ 
+    /* Don't dlclose the driver when building with the address sanitizer, so you
+     * get good symbols from the leak reports.
+@@ -1355,11 +1356,17 @@ dri2_display_destroy(_EGLDisplay *disp)
+          tpl_object_unreference((tpl_object_t *) dri2_dpy->tpl_dpy);
+       break;
+ #endif
++   case _EGL_PLATFORM_NULL:
++      dri2_teardown_null(dri2_dpy);
++      break;
+    default:
+       /* TODO: add teardown for other platforms */
+       break;
+    }
+ 
++   if (dri2_dpy->fd >= 0)
++      close(dri2_dpy->fd);
++
+    /* The drm platform does not create the screen/driver_configs but reuses
+     * the ones from the gbm device. As such the gbm itself is responsible
+     * for the cleanup.
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index f41f32c..eb2127e 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -81,6 +81,10 @@ struct zwp_linux_dmabuf_v1;
+ #include <tbm_surface_internal.h>
+ #endif
+ 
++#ifdef HAVE_NULL_PLATFORM
++#include <xf86drmMode.h>
++#endif
++
+ #include "eglconfig.h"
+ #include "eglcontext.h"
+ #include "egldevice.h"
+@@ -99,6 +103,22 @@ struct zwp_linux_dmabuf_v1;
+ 
+ struct wl_buffer;
+ 
++#ifdef HAVE_NULL_PLATFORM
++struct display_output {
++   bool                   in_use;
++   uint32_t               connector_id;
++   drmModePropertyRes   **connector_prop_res;
++   uint32_t               crtc_id;
++   drmModePropertyRes   **crtc_prop_res;
++   uint32_t               plane_id;
++   drmModePropertyRes   **plane_prop_res;
++   drmModeModeInfo        mode;
++   uint32_t               mode_blob_id;
++   unsigned               formats;
++   drmModeAtomicReq      *atomic_state;
++};
++#endif
++
+ struct dri2_egl_display_vtbl {
+    /* mandatory on Wayland, unused otherwise */
+    int (*authenticate)(_EGLDisplay *disp, uint32_t id);
+@@ -257,6 +277,11 @@ struct dri2_egl_display
+    char                     *device_name;
+ #endif
+ 
++#ifdef HAVE_NULL_PLATFORM
++   bool                      atomic_enabled;
++   struct display_output     output;
++#endif
++
+ #ifdef HAVE_ANDROID_PLATFORM
+    const gralloc_module_t *gralloc;
+    /* gralloc vendor usage bit for front rendering */
+@@ -332,7 +357,10 @@ struct dri2_egl_surface
+    struct wl_display     *wl_dpy_wrapper;
+    struct wl_drm         *wl_drm_wrapper;
+    struct wl_callback    *throttle_callback;
+-   int                    format;
++#endif
++
++#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_NULL_PLATFORM)
++   int			  format;
+ #endif
+ 
+ #ifdef HAVE_DRM_PLATFORM
+@@ -358,9 +386,10 @@ struct dri2_egl_surface
+    __DRIbuffer           *local_buffers[__DRI_BUFFER_COUNT];
+ 
+ #if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_DRM_PLATFORM) || \
+-    defined(HAVE_TIZEN_PLATFORM)
++   defined(HAVE_TIZEN_PLATFORM) || defined(HAVE_NULL_PLATFORM)
+    struct {
+-#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_TIZEN_PLATFORM)
++#if defined(HAVE_WAYLAND_PLATFORM) || defined(HAVE_TIZEN_PLATFORM) || \
++   defined(HAVE_NULL_PLATFORM)
+       __DRIimage         *dri_image;
+ #endif
+ #ifdef HAVE_WAYLAND_PLATFORM
+@@ -377,6 +406,9 @@ struct dri2_egl_surface
+ #endif
+ #ifdef HAVE_TIZEN_PLATFORM
+       tbm_surface_h       tbm_surf;
++#endif
++#ifdef HAVE_NULL_PLATFORM
++      uint32_t            fb_id;
+ #endif
+       bool                locked;
+       int                 age;
+@@ -409,6 +441,10 @@ struct dri2_egl_surface
+    void                 *swrast_front;
+ #endif
+ 
++#ifdef HAVE_NULL_PLATFORM
++   uint32_t             front_fb_id;
++#endif
++
+    int out_fence_fd;
+    EGLBoolean enable_out_fence;
+ 
+@@ -589,6 +625,21 @@ EGLBoolean
+ dri2_initialize_tizen(_EGLDisplay *disp);
+ #endif
+ 
++#ifdef HAVE_NULL_PLATFORM
++EGLBoolean
++dri2_initialize_null(_EGLDisplay *disp);
++void
++dri2_teardown_null(struct dri2_egl_display *dri2_dpy);
++#else
++static inline EGLBoolean
++dri2_initialize_null(_EGLDisplay *disp)
++{
++   return _eglError(EGL_NOT_INITIALIZED, "Null platform not built");
++}
++static inline void
++dri2_teardown_null(struct dri2_egl_display *dri2_dpy) {}
++#endif
++
+ EGLBoolean
+ dri2_initialize_device(_EGLDisplay *disp);
+ static inline void
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+new file mode 100644
+index 0000000..fb03ecc
+--- /dev/null
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -0,0 +1,1179 @@
++/*
++ * Copyright (c) Imagination Technologies Ltd.
++ *
++ * Parts based on platform_wayland, which has:
++ *
++ * Copyright © 2011-2012 Intel Corporation
++ * Copyright © 2012 Collabora, Ltd.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
++ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include <dlfcn.h>
++#include <drm_fourcc.h>
++#include <fcntl.h>
++#include <poll.h>
++#include <stdbool.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <unistd.h>
++#include <xf86drm.h>
++
++#include "egl_dri2.h"
++#include "loader.h"
++
++#define NULL_CARD_MINOR_MAX 63U
++
++/*
++ * Need at least version 4 for __DRI_IMAGE_ATTRIB_WIDTH and
++ * __DRI_IMAGE_ATTRIB_HEIGHT
++ */
++#define NULL_IMAGE_EXTENSION_VERSION_MIN 4
++
++struct object_property {
++   uint32_t object_id;
++   uint32_t prop_id;
++   uint64_t prop_value;
++};
++
++#define object_property_set_named(output, object_type, prop_name, value)    \
++   {                                                                        \
++      .object_id = (output)->object_type##_id,                              \
++      .prop_id = property_id_get_for_name((output)->object_type##_prop_res, \
++                                          prop_name),                       \
++      .prop_value = value,                                                  \
++   }
++
++/*
++ * The index of entries in this table is used as a bitmask in
++ * dri2_dpy->formats, which tracks the formats supported by the display.
++ */
++static const struct dri2_null_format {
++   uint32_t drm_format;
++   int dri_image_format;
++   int rgba_shifts[4];
++   unsigned int rgba_sizes[4];
++} dri2_null_formats[] = {
++   {
++      .drm_format = DRM_FORMAT_XRGB8888,
++      .dri_image_format = __DRI_IMAGE_FORMAT_XRGB8888,
++      .rgba_shifts = { 16, 8, 0, -1 },
++      .rgba_sizes = { 8, 8, 8, 0 },
++   },
++   {
++      .drm_format = DRM_FORMAT_ARGB8888,
++      .dri_image_format = __DRI_IMAGE_FORMAT_ARGB8888,
++      .rgba_shifts = { 16, 8, 0, 24 },
++      .rgba_sizes = { 8, 8, 8, 8 },
++   },
++   {
++      .drm_format = DRM_FORMAT_RGB565,
++      .dri_image_format = __DRI_IMAGE_FORMAT_RGB565,
++      .rgba_shifts = { 11, 5, 0, -1 },
++      .rgba_sizes = { 5, 6, 5, 0 },
++   },
++};
++
++
++static int
++format_idx_get_from_config(struct dri2_egl_display *dri2_dpy,
++                           const __DRIconfig *config)
++{
++   int shifts[4];
++   unsigned int sizes[4];
++
++   dri2_get_shifts_and_sizes(dri2_dpy->core, config, shifts, sizes);
++
++   for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++) {
++      const struct dri2_null_format *format = &dri2_null_formats[i];
++
++      if (shifts[0] == format->rgba_shifts[0] &&
++          shifts[1] == format->rgba_shifts[1] &&
++          shifts[2] == format->rgba_shifts[2] &&
++          shifts[3] == format->rgba_shifts[3] &&
++          sizes[0] == format->rgba_sizes[0] &&
++          sizes[1] == format->rgba_sizes[1] &&
++          sizes[2] == format->rgba_sizes[2] &&
++          sizes[3] == format->rgba_sizes[3]) {
++         return i;
++      }
++   }
++
++   return -1;
++}
++
++static int
++format_idx_get_from_dri_image_format(uint32_t dri_image_format)
++{
++   for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++)
++      if (dri2_null_formats[i].dri_image_format == dri_image_format)
++         return i;
++
++   return -1;
++}
++
++static int
++format_idx_get_from_drm_format(uint32_t drm_format)
++{
++   for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++)
++      if (dri2_null_formats[i].drm_format == drm_format)
++         return i;
++
++   return -1;
++}
++
++static int
++atomic_state_add_object_properties(drmModeAtomicReq *atomic_state,
++                                   const struct object_property *props,
++                                   const unsigned prop_count)
++{
++   for (unsigned i = 0; i < prop_count; i++) {
++      int err;
++
++      if (props[i].prop_id == 0)
++         return -EINVAL;
++
++      err = drmModeAtomicAddProperty(atomic_state, props[i].object_id,
++                                     props[i].prop_id, props[i].prop_value);
++      if (err < 0)
++         return err;
++   }
++
++   return 0;
++}
++
++
++static uint32_t
++property_id_get_for_name(drmModePropertyRes **prop_res, const char *prop_name)
++{
++   if (prop_res)
++      for (unsigned i = 0; prop_res[i]; i++)
++         if (!strcmp(prop_res[i]->name, prop_name))
++            return prop_res[i]->prop_id;
++
++   return 0;
++}
++
++static void
++flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
++             unsigned int tv_usec, void *user_data)
++{
++   bool *plocked = user_data;
++
++   if (plocked)
++      *plocked = false;
++}
++
++static bool
++flip_process(int fd)
++{
++   static drmEventContext evctx =
++      {.version = 2, .page_flip_handler = flip_handler};
++   struct pollfd pfd = {.fd = fd, .events = POLLIN};
++   int ret;
++
++   do {
++      ret = poll(&pfd, 1, -1);
++   } while (ret > 0 && pfd.revents != pfd.events);
++
++   if (ret <= 0)
++      return false;
++
++   drmHandleEvent(fd, &evctx);
++
++   return true;
++}
++
++static drmModePropertyRes **
++object_get_property_resources(int fd, uint32_t object_id, uint32_t object_type)
++{
++   drmModeObjectProperties *props;
++   drmModePropertyRes **prop_res;
++
++   props = drmModeObjectGetProperties(fd, object_id, object_type);
++   if (!props)
++      return NULL;
++
++   prop_res = malloc((props->count_props + 1) * sizeof(*prop_res));
++   if (prop_res) {
++      prop_res[props->count_props] = NULL;
++
++      for (unsigned i = 0; i < props->count_props; i++) {
++         prop_res[i] = drmModeGetProperty(fd, props->props[i]);
++         if (!prop_res[i]) {
++            while (i--) {
++               drmModeFreeProperty(prop_res[i]);
++               free(prop_res);
++               prop_res = NULL;
++            }
++            break;
++         }
++      }
++   }
++
++   drmModeFreeObjectProperties(props);
++
++   return prop_res;
++}
++
++static void
++object_free_property_resources(int fd, drmModePropertyRes **prop_res)
++{
++   for (unsigned i = 0; prop_res[i]; i++)
++      drmModeFreeProperty(prop_res[i]);
++   free(prop_res);
++}
++
++static bool
++object_property_value_for_name(int fd, uint32_t object_id, uint32_t object_type,
++                               const char *prop_name, uint64_t *value_out)
++{
++   drmModeObjectProperties *plane_props;
++   bool found = false;
++
++   plane_props = drmModeObjectGetProperties(fd, object_id, object_type);
++   if (!plane_props)
++      return false;
++
++   for (unsigned i = 0; i < plane_props->count_props; i++) {
++      drmModePropertyRes *prop;
++
++      prop = drmModeGetProperty(fd, plane_props->props[i]);
++      if (!prop)
++         continue;
++
++      found = !strcmp(prop->name, prop_name);
++      drmModeFreeProperty(prop);
++      if (found) {
++         *value_out = plane_props->prop_values[i];
++         break;
++      }
++   }
++
++   drmModeFreeObjectProperties(plane_props);
++
++   return found;
++}
++
++static int
++connector_choose_mode(drmModeConnector *connector)
++{
++   if (!connector->count_modes)
++      return -1;
++
++   for (unsigned i = 0; i < connector->count_modes; i++) {
++      if (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)
++         continue;
++
++      if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED)
++         return i;
++   }
++
++   return 0;
++}
++
++static drmModeConnector *
++connector_get(int fd, drmModeRes *resources)
++{
++   /* Find the first connected connector */
++   for (unsigned i = 0; i < resources->count_connectors; i++) {
++      drmModeConnector *connector;
++
++      connector = drmModeGetConnector(fd, resources->connectors[i]);
++      if (!connector)
++         continue;
++
++      if (connector->connection == DRM_MODE_CONNECTED)
++         return connector;
++
++      drmModeFreeConnector(connector);
++   }
++
++   return NULL;
++}
++
++static drmModeCrtc *
++crtc_get_for_connector(int fd, drmModeRes *resources,
++                       drmModeConnector *connector)
++{
++   for (unsigned i = 0; i < connector->count_encoders; i++) {
++      drmModeEncoder *encoder;
++
++      encoder = drmModeGetEncoder(fd, connector->encoders[i]);
++      if (!encoder)
++         continue;
++
++      for (unsigned j = 0; j < resources->count_crtcs; j++) {
++         if (encoder->possible_crtcs & (1 << j)) {
++            drmModeCrtc *crtc;
++
++            crtc = drmModeGetCrtc(fd, resources->crtcs[j]);
++            if (crtc) {
++               drmModeFreeEncoder(encoder);
++               return crtc;
++            }
++         }
++      }
++
++      drmModeFreeEncoder(encoder);
++   }
++
++   return NULL;
++}
++
++static drmModePlane *
++primary_plane_get_for_crtc(int fd, drmModeRes *resources, drmModeCrtc *crtc)
++{
++   drmModePlaneRes *plane_resources;
++   unsigned crtc_idx;
++
++   plane_resources = drmModeGetPlaneResources(fd);
++   if (!plane_resources)
++      return NULL;
++
++   for (crtc_idx = 0; crtc_idx < resources->count_crtcs; crtc_idx++)
++      if (resources->crtcs[crtc_idx] == crtc->crtc_id)
++         break;
++   assert(crtc_idx != resources->count_crtcs);
++
++   for (unsigned i = 0; i < plane_resources->count_planes; i++) {
++      const uint32_t crtc_bit = 1 << crtc_idx;
++      drmModePlane *plane;
++
++      plane = drmModeGetPlane(fd, plane_resources->planes[i]);
++      if (!plane)
++         continue;
++
++      if (plane->possible_crtcs & crtc_bit) {
++         uint64_t type;
++         bool res;
++
++         res = object_property_value_for_name(fd, plane->plane_id,
++                                              DRM_MODE_OBJECT_PLANE,
++                                              "type", &type);
++         if (res && type == DRM_PLANE_TYPE_PRIMARY) {
++            drmModeFreePlaneResources(plane_resources);
++            return plane;
++         }
++      }
++
++      drmModeFreePlane(plane);
++   }
++
++   drmModeFreePlaneResources(plane_resources);
++
++   return NULL;
++}
++
++static bool
++display_output_atomic_init(int fd, struct display_output *output)
++{
++   drmModePropertyRes **connector_prop_res;
++   drmModePropertyRes **crtc_prop_res;
++   drmModePropertyRes **plane_prop_res;
++   int err;
++
++   connector_prop_res = object_get_property_resources(fd, output->connector_id,
++                                                      DRM_MODE_OBJECT_CONNECTOR);
++   if (!connector_prop_res)
++      return false;
++
++   crtc_prop_res = object_get_property_resources(fd, output->crtc_id,
++                                                 DRM_MODE_OBJECT_CRTC);
++   if (!crtc_prop_res)
++      goto err_free_connector_prop_res;
++
++   plane_prop_res = object_get_property_resources(fd, output->plane_id,
++                                                  DRM_MODE_OBJECT_PLANE);
++   if (!plane_prop_res)
++      goto err_free_crtc_prop_res;
++
++   err = drmModeCreatePropertyBlob(fd, &output->mode, sizeof(output->mode),
++                                   &output->mode_blob_id);
++   if (err)
++      goto err_free_plane_prop_res;
++
++   output->atomic_state = drmModeAtomicAlloc();
++   if (!output->atomic_state)
++      goto err_destroy_mode_prop_blob;
++
++   output->connector_prop_res = connector_prop_res;
++   output->crtc_prop_res = crtc_prop_res;
++   output->plane_prop_res = plane_prop_res;
++
++   return true;
++
++err_destroy_mode_prop_blob:
++   drmModeDestroyPropertyBlob(fd, output->mode_blob_id);
++err_free_plane_prop_res:
++   object_free_property_resources(fd, plane_prop_res);
++err_free_crtc_prop_res:
++   object_free_property_resources(fd, crtc_prop_res);
++err_free_connector_prop_res:
++   object_free_property_resources(fd, connector_prop_res);
++   return false;
++}
++
++static int
++display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id,
++                           uint32_t flags, void *flip_data)
++{
++   const struct object_property obj_props[] = {
++      object_property_set_named(output, plane, "FB_ID", fb_id),
++   };
++   int err;
++
++   /* Reset atomic state */
++   drmModeAtomicSetCursor(output->atomic_state, 0);
++
++   err = atomic_state_add_object_properties(output->atomic_state, obj_props,
++                                            ARRAY_SIZE(obj_props));
++   if (err)
++      return err;
++
++   /*
++    * Don't block - like drmModePageFlip, drmModeAtomicCommit will return
++    * -EBUSY if the commit can't be queued in the kernel.
++    */
++   flags |= DRM_MODE_ATOMIC_NONBLOCK;
++
++   return drmModeAtomicCommit(fd, output->atomic_state, flags, flip_data);
++}
++
++static int
++display_output_atomic_modeset(int fd, struct display_output *output, uint32_t fb_id)
++{
++   /* SRC_W and SRC_H in 16.16 fixed point */
++   const struct object_property obj_props[] = {
++      object_property_set_named(output, connector, "CRTC_ID", output->crtc_id),
++      object_property_set_named(output, crtc, "ACTIVE", 1),
++      object_property_set_named(output, crtc, "MODE_ID", output->mode_blob_id),
++      object_property_set_named(output, plane, "FB_ID", fb_id),
++      object_property_set_named(output, plane, "CRTC_ID", output->crtc_id),
++      object_property_set_named(output, plane, "CRTC_X", 0),
++      object_property_set_named(output, plane, "CRTC_Y", 0),
++      object_property_set_named(output, plane, "CRTC_W", output->mode.hdisplay),
++      object_property_set_named(output, plane, "CRTC_H", output->mode.vdisplay),
++      object_property_set_named(output, plane, "SRC_X", 0),
++      object_property_set_named(output, plane, "SRC_Y", 0),
++      object_property_set_named(output, plane, "SRC_W", output->mode.hdisplay << 16),
++      object_property_set_named(output, plane, "SRC_H", output->mode.vdisplay << 16),
++   };
++   int err;
++
++   /* Reset atomic state */
++   drmModeAtomicSetCursor(output->atomic_state, 0);
++
++   err = atomic_state_add_object_properties(output->atomic_state, obj_props,
++                                            ARRAY_SIZE(obj_props));
++   if (err)
++      return false;
++
++   return drmModeAtomicCommit(fd, output->atomic_state,
++                              DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
++}
++
++static bool
++display_output_init(int fd, struct display_output *output, bool use_atomic)
++{
++   drmModeRes *resources;
++   drmModeConnector *connector;
++   drmModeCrtc *crtc;
++   drmModePlane *plane;
++   unsigned mode_idx;
++
++   resources = drmModeGetResources(fd);
++   if (!resources)
++      return false;
++
++   connector = connector_get(fd, resources);
++   if (!connector)
++      goto err_free_resources;
++
++   crtc = crtc_get_for_connector(fd, resources, connector);
++   if (!crtc)
++      goto err_free_connector;
++
++   plane = primary_plane_get_for_crtc(fd, resources, crtc);
++   if (!plane)
++      goto err_free_crtc;
++
++   mode_idx = connector_choose_mode(connector);
++   if (mode_idx < 0)
++      goto err_free_plane;
++   output->mode = connector->modes[mode_idx];
++
++   /* Record the display supported formats */
++   for (unsigned i = 0; i < plane->count_formats; i++) {
++      int format_idx;
++
++      format_idx = format_idx_get_from_drm_format(plane->formats[i]);
++      if (format_idx == -1)
++         continue;
++
++      output->formats |= (1 << format_idx);
++   }
++   if (!output->formats)
++      goto err_free_plane;
++
++   output->connector_id = connector->connector_id;
++   output->crtc_id = crtc->crtc_id;
++   output->plane_id = plane->plane_id;
++
++   drmModeFreePlane(plane);
++   drmModeFreeCrtc(crtc);
++   drmModeFreeConnector(connector);
++   drmModeFreeResources(resources);
++
++   if (use_atomic) {
++      if (!display_output_atomic_init(fd, output)) {
++         _eglLog(_EGL_DEBUG,
++                 "failed to initialise atomic support (using legacy mode)");
++      }
++   }
++
++   return true;
++
++err_free_plane:
++   drmModeFreePlane(plane);
++err_free_crtc:
++   drmModeFreeCrtc(crtc);
++err_free_connector:
++   drmModeFreeConnector(connector);
++err_free_resources:
++   drmModeFreeResources(resources);
++   return false;
++}
++
++static int
++display_output_flip(int fd, struct display_output *output, uint32_t fb_id,
++                    uint32_t flags, void *flip_data)
++{
++   if (output->atomic_state)
++      return display_output_atomic_flip(fd, output, fb_id, flags, flip_data);
++
++   return drmModePageFlip(fd, output->crtc_id, fb_id, flags, flip_data);
++}
++
++static int
++display_output_modeset(int fd, struct display_output *output, uint32_t fb_id)
++{
++   if (output->atomic_state)
++      return display_output_atomic_modeset(fd, output, fb_id);
++
++   return drmModeSetCrtc(fd, output->crtc_id, fb_id, 0, 0,
++                         &output->connector_id, 1, &output->mode);
++}
++
++static bool
++add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
++                     uint32_t *fb_id_out)
++{
++   uint32_t handles[4] = {0};
++   uint32_t pitches[4] = {0};
++   uint32_t offsets[4] = {0};
++   int handle, stride, width, height, format;
++   int format_idx;
++
++   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HANDLE, &handle);
++   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &stride);
++   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_WIDTH, &width);
++   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HEIGHT, &height);
++   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
++
++   handles[0] = (uint32_t) handle;
++   pitches[0] = (uint32_t) stride;
++
++   format_idx = format_idx_get_from_dri_image_format(format);
++   assert(format_idx != -1);
++
++   return !drmModeAddFB2(dri2_dpy->fd, width, height,
++                         dri2_null_formats[format_idx].drm_format,
++                         handles, pitches, offsets, fb_id_out, 0);
++}
++
++static bool
++get_front_bo(struct dri2_egl_surface *dri2_surf)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   unsigned int use = 0;
++
++   if (dri2_surf->base.Type == EGL_WINDOW_BIT)
++      use |= __DRI_IMAGE_USE_SCANOUT;
++
++   dri2_surf->front = dri2_dpy->image->createImage(dri2_dpy->dri_screen,
++                                                   dri2_surf->base.Width,
++                                                   dri2_surf->base.Height,
++                                                   dri2_surf->format,
++                                                   use,
++                                                   NULL);
++   if (!dri2_surf->front)
++      return false;
++
++   if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
++      if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->front,
++                                &dri2_surf->front_fb_id)) {
++         dri2_dpy->image->destroyImage(dri2_surf->front);
++         dri2_surf->front = NULL;
++         return false;
++      }
++   }
++
++   return true;
++}
++
++static bool
++get_back_bo(struct dri2_egl_surface *dri2_surf)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++
++   if (!dri2_surf->back) {
++      for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++         if (!dri2_surf->color_buffers[i].locked) {
++            dri2_surf->back = &dri2_surf->color_buffers[i];
++            break;
++         }
++      }
++      if (!dri2_surf->back)
++         return false;
++   }
++
++   if (!dri2_surf->back->dri_image) {
++      dri2_surf->back->dri_image =
++         dri2_dpy->image->createImage(dri2_dpy->dri_screen,
++                                      dri2_surf->base.Width,
++                                      dri2_surf->base.Height,
++                                      dri2_surf->format,
++                                      __DRI_IMAGE_USE_SCANOUT,
++                                      NULL);
++      if (!dri2_surf->back->dri_image)
++         return false;
++   }
++
++   if (!dri2_surf->back->fb_id) {
++      if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->back->dri_image,
++                                &dri2_surf->back->fb_id)) {
++         return false;
++      }
++   }
++
++   dri2_surf->back->locked = 1;
++
++   return true;
++}
++
++static _EGLSurface *
++create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
++               const EGLint *attrib_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_config *dri2_config = dri2_egl_config(config);
++   struct dri2_egl_surface *dri2_surf;
++   const __DRIconfig *dri_config;
++   _EGLSurface *surf;
++   int format_idx;
++
++   dri2_surf = calloc(1, sizeof(*dri2_surf));
++   if (!dri2_surf) {
++      _eglError(EGL_BAD_ALLOC, "failed to create surface");
++      return NULL;
++   }
++   surf = &dri2_surf->base;
++
++   if (!dri2_init_surface(surf, disp, type, config, attrib_list, false, NULL))
++      goto err_free_surface;
++
++   dri_config = dri2_get_dri_config(dri2_config, type,
++                                    dri2_surf->base.GLColorspace);
++   if (!dri_config) {
++      _eglError(EGL_BAD_MATCH, "Unsupported surfacetype/colorspace configuration");
++      goto err_free_surface;
++   }
++
++   dri2_surf->dri_drawable =
++      dri2_dpy->image_driver->createNewDrawable(dri2_dpy->dri_screen,
++                                                dri_config, dri2_surf);
++   if (!dri2_surf->dri_drawable) {
++      _eglError(EGL_BAD_ALLOC, "failed to create drawable");
++       goto err_free_surface;
++   }
++
++   format_idx = format_idx_get_from_config(dri2_dpy, dri_config);
++   assert(format_idx != -1);
++
++   dri2_surf->format = dri2_null_formats[format_idx].dri_image_format;
++
++   return surf;
++
++err_free_surface:
++   free(dri2_surf);
++   return NULL;
++}
++
++static void
++destroy_surface(_EGLSurface *surf)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(surf->Resource.Display);
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
++
++   dri2_dpy->core->destroyDrawable(dri2_surf->dri_drawable);
++   dri2_fini_surface(surf);
++   free(surf);
++}
++
++static EGLBoolean
++dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf);
++
++static _EGLSurface *
++dri2_null_create_window_surface(_EGLDisplay *disp, _EGLConfig *config,
++                                void *native_window, const EGLint *attrib_list)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_surface *dri2_surf;
++   _EGLSurface *surf;
++   int err;
++
++   if (dri2_dpy->output.in_use) {
++      _eglError(EGL_BAD_NATIVE_WINDOW, "window in use");
++      return NULL;
++   }
++
++   surf = create_surface(disp, config, EGL_WINDOW_BIT, attrib_list);
++   if (!surf)
++      return NULL;
++   dri2_surf = dri2_egl_surface(surf);
++
++   dri2_surf->base.Width = dri2_dpy->output.mode.hdisplay;
++   dri2_surf->base.Height = dri2_dpy->output.mode.vdisplay;
++
++   if (!get_front_bo(dri2_surf))  {
++      _eglError(EGL_BAD_NATIVE_WINDOW, "window get buffer");
++      goto err_destroy_surface;
++   }
++
++   err = display_output_modeset(dri2_dpy->fd, &dri2_dpy->output,
++                                dri2_surf->front_fb_id);
++   if (err) {
++      _eglError(EGL_BAD_NATIVE_WINDOW, "window set mode");
++      goto err_destroy_surface;
++   }
++
++   dri2_dpy->output.in_use = true;
++
++   return surf;
++
++err_destroy_surface:
++   dri2_null_destroy_surface(disp, surf);
++   return NULL;
++}
++
++static _EGLSurface *
++dri2_null_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *config,
++                                 const EGLint *attrib_list)
++{
++   return create_surface(disp, config, EGL_PBUFFER_BIT, attrib_list);
++}
++
++static EGLBoolean
++dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surf);
++   EGLint type = surf->Type;
++
++   /* If there's a current surface then a page flip has been performed, so make
++    * sure we process the flip event.
++    */
++   if (dri2_surf->current)
++      flip_process(dri2_dpy->fd);
++
++   if (dri2_surf->front)
++      dri2_dpy->image->destroyImage(dri2_surf->front);
++
++   if (dri2_surf->front_fb_id)
++         drmModeRmFB(dri2_dpy->fd, dri2_surf->front_fb_id);
++
++   for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
++      if (dri2_surf->color_buffers[i].fb_id)
++         drmModeRmFB(dri2_dpy->fd, dri2_surf->color_buffers[i].fb_id);
++      if (dri2_surf->color_buffers[i].dri_image)
++         dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
++   }
++
++   destroy_surface(surf);
++
++   if (type == EGL_WINDOW_BIT)
++      dri2_dpy->output.in_use = false;
++
++   return EGL_TRUE;
++}
++
++static EGLBoolean
++dri2_null_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
++   bool *plocked = NULL;
++   uint32_t flags;
++   int err;
++
++   if (dri2_surf->base.Type != EGL_WINDOW_BIT)
++      return EGL_TRUE;
++
++   for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
++      if (dri2_surf->color_buffers[i].age > 0)
++         dri2_surf->color_buffers[i].age++;
++
++   /* Make sure we have a back buffer in case we're swapping without
++    * ever rendering. */
++   if (!get_back_bo(dri2_surf)) {
++      _eglError(EGL_BAD_ALLOC, "dri2_null_swap_buffers");
++      return EGL_FALSE;
++   }
++
++   dri2_flush_drawable_for_swapbuffers(disp, draw);
++   dri2_dpy->flush->invalidate(dri2_surf->dri_drawable);
++
++   if (dri2_surf->current) {
++      /* Wait for the previous flip to happen so the next one can be queued */
++      if (!flip_process(dri2_dpy->fd)) {
++         _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_null_swap_buffers process");
++         return EGL_FALSE;
++      }
++
++      plocked = &dri2_surf->current->locked;
++   }
++
++   flags = DRM_MODE_PAGE_FLIP_EVENT;
++   if (draw->SwapInterval == 0)
++      flags |= DRM_MODE_PAGE_FLIP_ASYNC;
++
++   do {
++      err = display_output_flip(dri2_dpy->fd, &dri2_dpy->output,
++                                dri2_surf->back->fb_id, flags, plocked);
++   } while (err == -EBUSY);
++
++   if (err) {
++      _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_null_swap_buffers flip");
++      dri2_surf->back->locked = false;
++      dri2_surf->back = NULL;
++      return EGL_FALSE;
++   }
++
++   dri2_surf->back->age = 1;
++   dri2_surf->current = dri2_surf->back;
++   dri2_surf->back = NULL;
++
++   return EGL_TRUE;
++}
++
++static EGLint
++dri2_null_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surface)
++{
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(surface);
++
++   if (!get_back_bo(dri2_surf)) {
++      _eglError(EGL_BAD_ALLOC, "failed to get back buffer to query age");
++      return -1;
++   }
++
++   return dri2_surf->back->age;
++}
++
++static struct dri2_egl_display_vtbl dri2_null_display_vtbl = {
++   .create_window_surface = dri2_null_create_window_surface,
++   .create_pbuffer_surface = dri2_null_create_pbuffer_surface,
++   .destroy_surface = dri2_null_destroy_surface,
++   .create_image = dri2_create_image_khr,
++   .swap_buffers = dri2_null_swap_buffers,
++   .query_buffer_age = dri2_null_query_buffer_age,
++   .get_dri_drawable = dri2_surface_get_dri_drawable,
++};
++
++static int
++dri2_null_image_get_buffers(__DRIdrawable *driDrawable, unsigned int format,
++                            uint32_t *stamp, void *loaderPrivate,
++                            uint32_t buffer_mask, struct __DRIimageList *buffers)
++{
++   struct dri2_egl_surface *dri2_surf = loaderPrivate;
++
++   buffers->image_mask = 0;
++   buffers->back = NULL;
++   buffers->front = NULL;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT)
++      if (!get_front_bo(dri2_surf))
++         return 0;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK)
++      if (!get_back_bo(dri2_surf))
++         return 0;
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
++      buffers->front = dri2_surf->front;
++   }
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
++      buffers->back = dri2_surf->back->dri_image;
++   }
++
++   return 1;
++}
++
++static void
++dri2_null_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
++{
++   (void) driDrawable;
++   (void) loaderPrivate;
++}
++
++static const __DRIimageLoaderExtension image_loader_extension = {
++   .base = { __DRI_IMAGE_LOADER, 1 },
++
++   .getBuffers          = dri2_null_image_get_buffers,
++   .flushFrontBuffer    = dri2_null_flush_front_buffer,
++};
++
++static const __DRIextension *image_loader_extensions[] = {
++   &image_loader_extension.base,
++   &image_lookup_extension.base,
++   &use_invalidate.base,
++   NULL,
++};
++
++static bool
++dri2_null_device_is_kms(int fd)
++{
++   drmModeRes *resources;
++   bool is_kms;
++
++   resources = drmModeGetResources(fd);
++   if (!resources)
++      return false;
++
++   is_kms = resources->count_crtcs != 0 &&
++      resources->count_connectors != 0 &&
++      resources->count_encoders != 0;
++
++   drmModeFreeResources(resources);
++
++   return is_kms;
++}
++
++static bool
++dri2_null_probe_device(_EGLDisplay *disp)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   dri2_dpy->fd = -1;
++
++   for (unsigned i = 0; i <= NULL_CARD_MINOR_MAX; i++) {
++      char *card_path;
++
++      if (asprintf(&card_path, DRM_DEV_NAME, DRM_DIR_NAME, i) < 0)
++         continue;
++
++      dri2_dpy->fd = loader_open_device(card_path);
++      free(card_path);
++      if (dri2_dpy->fd < 0)
++         continue;
++
++      if (dri2_null_device_is_kms(dri2_dpy->fd)) {
++         dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
++         if (dri2_dpy->driver_name) {
++            if (dri2_load_driver_dri3(disp)) {
++               _EGLDevice *dev = _eglAddDevice(dri2_dpy->fd, false);
++               if (!dev) {
++                  dlclose(dri2_dpy->driver);
++                  _eglLog(_EGL_WARNING, "DRI2: failed to find EGLDevice");
++               } else {
++                  dri2_dpy->loader_extensions = image_loader_extensions;
++                  dri2_dpy->own_device = 1;
++                  disp->Device = dev;
++                  return true;
++               }
++            }
++            free(dri2_dpy->driver_name);
++            dri2_dpy->driver_name = NULL;
++         }
++      }
++
++      close(dri2_dpy->fd);
++      dri2_dpy->fd = -1;
++   }
++
++   return false;
++}
++
++static EGLBoolean
++dri2_null_add_configs_for_formats(_EGLDisplay *disp)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   unsigned int count = 0;
++
++   for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) {
++      struct dri2_egl_config *dri2_conf;
++      int format_idx;
++
++      format_idx = format_idx_get_from_config(dri2_dpy,
++                                              dri2_dpy->driver_configs[i]);
++      if (format_idx == -1)
++         continue;
++
++      if (!(dri2_dpy->output.formats & (1 << format_idx))) {
++         _eglLog(_EGL_DEBUG, "unsupported drm format 0x%04x",
++                 dri2_null_formats[format_idx].drm_format);
++         continue;
++      }
++
++      dri2_conf = dri2_add_config(disp,
++                                  dri2_dpy->driver_configs[i], count + 1,
++                                  EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
++                                  NULL, NULL, NULL);
++      if (dri2_conf)
++         count++;
++   }
++
++   return count != 0;
++}
++
++EGLBoolean
++dri2_initialize_null(_EGLDisplay *disp)
++{
++   struct dri2_egl_display *dri2_dpy;
++   uint64_t value;
++   int err;
++
++   dri2_dpy = calloc(1, sizeof(*dri2_dpy));
++   if (!dri2_dpy)
++      return _eglError(EGL_BAD_ALLOC, "eglInitialize");
++
++   disp->DriverData = (void *) dri2_dpy;
++
++   if (!dri2_null_probe_device(disp)) {
++      _eglError(EGL_NOT_INITIALIZED, "failed to load driver");
++      goto cleanup;
++   }
++
++   /*
++    * Try to use atomic modesetting if available and fallback to legacy kernel
++    * modesetting if not. If this succeeds then universal planes will also have
++    * been enabled.
++    */
++   err = drmSetClientCap(dri2_dpy->fd, DRM_CLIENT_CAP_ATOMIC, 1);
++   dri2_dpy->atomic_enabled = !err;
++
++   if (!dri2_dpy->atomic_enabled) {
++      /*
++       * Enable universal planes so that we can get the pixel formats for the
++       * primary plane
++       */
++      err = drmSetClientCap(dri2_dpy->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
++      if (err) {
++         _eglError(EGL_NOT_INITIALIZED, "failed to enable universal planes");
++         goto cleanup;
++      }
++
++      dri2_dpy->atomic_enabled = false;
++   }
++
++   if (!dri2_create_screen(disp)) {
++      _eglError(EGL_NOT_INITIALIZED, "failed to create screen");
++      goto cleanup;
++   }
++
++   if (!dri2_setup_extensions(disp)) {
++      _eglError(EGL_NOT_INITIALIZED, "failed to find required DRI extensions");
++      goto cleanup;
++   }
++
++   dri2_setup_screen(disp);
++   dri2_setup_swap_interval(disp, 1);
++
++   err = drmGetCap(dri2_dpy->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
++   if (err || value == 0)
++      dri2_dpy->min_swap_interval = 1;
++
++   if (dri2_dpy->image->base.version < NULL_IMAGE_EXTENSION_VERSION_MIN) {
++      _eglError(EGL_NOT_INITIALIZED, "image extension version too old");
++      goto cleanup;
++   }
++
++   if (!display_output_init(dri2_dpy->fd, &dri2_dpy->output,
++                            dri2_dpy->atomic_enabled)) {
++      _eglError(EGL_NOT_INITIALIZED, "failed to create output");
++      goto cleanup;
++   }
++
++   if (!dri2_null_add_configs_for_formats(disp)) {
++      _eglError(EGL_NOT_INITIALIZED, "failed to add configs");
++      goto cleanup;
++   }
++
++   disp->Extensions.EXT_buffer_age = EGL_TRUE;
++   disp->Extensions.KHR_image_base = EGL_TRUE;
++
++   /* Fill vtbl last to prevent accidentally calling virtual function during
++    * initialization.
++    */
++   dri2_dpy->vtbl = &dri2_null_display_vtbl;
++
++   return EGL_TRUE;
++
++cleanup:
++   dri2_display_destroy(disp);
++   return EGL_FALSE;
++}
++
++void
++dri2_teardown_null(struct dri2_egl_display *dri2_dpy)
++{
++   drmModeAtomicFree(dri2_dpy->output.atomic_state);
++
++   if (dri2_dpy->output.mode_blob_id)
++      drmModeDestroyPropertyBlob(dri2_dpy->fd, dri2_dpy->output.mode_blob_id);
++
++   if (dri2_dpy->output.plane_prop_res) {
++      for (unsigned i = 0; dri2_dpy->output.plane_prop_res[i]; i++)
++         drmModeFreeProperty(dri2_dpy->output.plane_prop_res[i]);
++      free(dri2_dpy->output.plane_prop_res);
++   }
++
++   if (dri2_dpy->output.crtc_prop_res) {
++      for (unsigned i = 0; dri2_dpy->output.crtc_prop_res[i]; i++)
++         drmModeFreeProperty(dri2_dpy->output.crtc_prop_res[i]);
++      free(dri2_dpy->output.crtc_prop_res);
++   }
++
++   if (dri2_dpy->output.connector_prop_res) {
++      for (unsigned i = 0; dri2_dpy->output.connector_prop_res[i]; i++)
++         drmModeFreeProperty(dri2_dpy->output.connector_prop_res[i]);
++      free(dri2_dpy->output.connector_prop_res);
++   }
++}
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 6d7f62c..e4f2c43 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -989,7 +989,10 @@ _eglCreateWindowSurfaceCommon(_EGLDisplay *disp, EGLConfig config,
+ 
+ 
+    if (native_window == NULL)
+-      RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
++#ifdef HAVE_NULL_PLATFORM
++      if (disp && disp->Platform != _EGL_PLATFORM_NULL)
++#endif
++         RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ 
+    if (disp && (disp->Platform == _EGL_PLATFORM_SURFACELESS ||
+                 disp->Platform == _EGL_PLATFORM_DEVICE)) {
+diff --git a/src/egl/main/egldisplay.c b/src/egl/main/egldisplay.c
+index 486ac3b..395f381 100644
+--- a/src/egl/main/egldisplay.c
++++ b/src/egl/main/egldisplay.c
+@@ -78,6 +78,7 @@ static const struct {
+    { _EGL_PLATFORM_SURFACELESS, "surfaceless" },
+    { _EGL_PLATFORM_DEVICE, "device" },
+    { _EGL_PLATFORM_TIZEN, "tizen" },
++   { _EGL_PLATFORM_NULL, "null" },
+ };
+ 
+ 
+diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
+index 020529c..8489af9 100644
+--- a/src/egl/main/egldisplay.h
++++ b/src/egl/main/egldisplay.h
+@@ -53,6 +53,7 @@ enum _egl_platform_type {
+    _EGL_PLATFORM_SURFACELESS,
+    _EGL_PLATFORM_DEVICE,
+    _EGL_PLATFORM_TIZEN,
++   _EGL_PLATFORM_NULL,
+ 
+    _EGL_NUM_PLATFORMS,
+    _EGL_INVALID_PLATFORM = -1
+diff --git a/src/egl/meson.build b/src/egl/meson.build
+index 5749ec8..daa6a3a 100644
+--- a/src/egl/meson.build
++++ b/src/egl/meson.build
+@@ -146,6 +146,11 @@ if with_platform_tizen
+   incs_for_egl += [inc_loader]
+   deps_for_egl += [dep_tizen, dep_libdrm]
+ endif
++if with_platform_null
++  files_egl += files('drivers/dri2/platform_null.c')
++  incs_for_egl += [inc_loader]
++  deps_for_egl += dep_libdrm
++endif
+ 
+ if cc.has_function('mincore')
+   c_args_for_egl += '-DHAVE_MINCORE'

+ 507 - 0
recipes-graphics/mesa/mesa-pvr/0037-egl-add-config-debug-printout.patch

@@ -0,0 +1,507 @@
+From a5c947e12d7b29419f8996ca3a7beeefe424d8dc Mon Sep 17 00:00:00 2001
+From: Silvestrs Timofejevs <silvestrs.timofejevs@imgtec.com>
+Date: Mon, 24 Sep 2018 14:14:25 +0100
+Subject: [PATCH] egl: add config debug printout
+
+Feature to print out EGL returned configs for debug purposes.
+
+'eglChooseConfig' and 'eglGetConfigs' debug information printout is
+enabled when the log level equals '_EGL_DEBUG'. The configs are
+printed, and if any of them are "chosen" they are marked with their
+index in the chosen configs array.
+
+---
+ src/egl/main/eglconfig.c      |  20 ++-
+ src/egl/main/eglconfigdebug.c | 321 ++++++++++++++++++++++++++++++++++
+ src/egl/main/eglconfigdebug.h |  55 ++++++
+ src/egl/main/egllog.c         |   9 +
+ src/egl/main/egllog.h         |   4 +
+ src/egl/meson.build           |   2 +
+ 6 files changed, 407 insertions(+), 4 deletions(-)
+ create mode 100644 src/egl/main/eglconfigdebug.c
+ create mode 100644 src/egl/main/eglconfigdebug.h
+
+diff --git a/src/egl/main/eglconfig.c b/src/egl/main/eglconfig.c
+index 5e56948..ea4b3c1 100644
+--- a/src/egl/main/eglconfig.c
++++ b/src/egl/main/eglconfig.c
+@@ -40,6 +40,7 @@
+ #include "util/macros.h"
+ 
+ #include "eglconfig.h"
++#include "eglconfigdebug.h"
+ #include "egldisplay.h"
+ #include "eglcurrent.h"
+ #include "egllog.h"
+@@ -1039,14 +1040,21 @@ _eglChooseConfig(_EGLDisplay *disp, const EGLint *attrib_list,
+                  EGLConfig *configs, EGLint config_size, EGLint *num_configs)
+ {
+    _EGLConfig criteria;
++   EGLBoolean result;
+ 
+    if (!_eglParseConfigAttribList(&criteria, disp, attrib_list))
+       return _eglError(EGL_BAD_ATTRIBUTE, "eglChooseConfig");
+ 
+-   return _eglFilterConfigArray(disp->Configs,
+-         configs, config_size, num_configs,
+-         _eglFallbackMatch, _eglFallbackCompare,
+-         (void *) &criteria);
++   result = _eglFilterConfigArray(disp->Configs,
++                                  configs, config_size, num_configs,
++                                  _eglFallbackMatch, _eglFallbackCompare,
++                                  (void *) &criteria);
++
++   if (result && (_eglGetLogLevel() == _EGL_DEBUG))
++      eglPrintConfigDebug(disp, configs, *num_configs,
++                          EGL_CONFIG_DEBUG_CHOOSE);
++
++   return result;
+ }
+ 
+ 
+@@ -1096,5 +1104,9 @@ _eglGetConfigs(_EGLDisplay *disp, EGLConfig *configs,
+    *num_config = _eglFlattenArray(disp->Configs, (void *) configs,
+          sizeof(configs[0]), config_size, _eglFlattenConfig);
+ 
++   if (_eglGetLogLevel() == _EGL_DEBUG)
++      eglPrintConfigDebug(disp, configs, *num_config,
++                          EGL_CONFIG_DEBUG_GET);
++
+    return EGL_TRUE;
+ }
+diff --git a/src/egl/main/eglconfigdebug.c b/src/egl/main/eglconfigdebug.c
+new file mode 100644
+index 0000000..92ac41a
+--- /dev/null
++++ b/src/egl/main/eglconfigdebug.c
+@@ -0,0 +1,321 @@
++/*
++ * Copyright 2017 Imagination Technologies.
++ * All Rights Reserved.
++ *
++ * Based on eglinfo, which has copyright:
++ * Copyright (C) 2005  Brian Paul   All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included
++ * in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
++ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <stdio.h>
++#include <stdarg.h>
++
++#include "eglarray.h"
++#include "eglconfig.h"
++#include "eglconfigdebug.h"
++#include "egldisplay.h"
++#include "egllog.h"
++#include "egltypedefs.h"
++
++/* Max debug message length */
++#define CONFIG_DEBUG_MSG_MAX 1000
++
++/*
++ * These are X visual types, so if you're running eglinfo under
++ * something not X, they probably don't make sense.
++ */
++static const char *const vnames[] = { "SG", "GS", "SC", "PC", "TC", "DC" };
++
++struct _printAttributes {
++   EGLint id, size, level;
++   EGLint red, green, blue, alpha;
++   EGLint depth, stencil;
++   EGLint renderable, surfaces;
++   EGLint vid, vtype, caveat, bindRgb, bindRgba;
++   EGLint samples, sampleBuffers;
++   char surfString[100];
++   EGLint colorBufferType;
++   EGLint numPlanes, subsample, order;
++};
++
++static void
++_printHeaderFormat(void)
++{
++   /*
++    * EGL configuration output legend:
++    *
++    * chosen --------------- eglChooseConfig returned config priority,
++    *                        only relevant when eglChooseConfig is called.
++    * id ------------------- EGL_CONFIG_ID
++    * bfsz ----------------- EGL_BUFFER_SIZE
++    * lvl ------------------ EGL_LEVEL
++    *
++    * color size
++    * r -------------------- EGL_RED_SIZE
++    * g -------------------- EGL_GREEN_SIZE
++    * b -------------------- EGL_BLUE_SIZE
++    * a -------------------- EGL_ALPHA_SIZE
++    * dpth ----------------- EGL_DEPTH_SIZE
++    * stcl ----------------- EGL_STENCIL_SIZE
++    *
++    * multisample
++    * ns ------------------- EGL_SAMPLES
++    * b -------------------- EGL_SAMPLE_BUFFERS
++    * visid ---------------- EGL_NATIVE_VISUAL_ID/EGL_NATIVE_VISUAL_TYPE
++    * caveat --------------- EGL_CONFIG_CAVEAT
++    * bind ----------------- EGL_BIND_TO_TEXTURE_RGB/EGL_BIND_TO_TEXTURE_RGBA
++    *
++    * renderable
++    * gl, es, es2, es3, vg - EGL_RENDERABLE_TYPE
++    *
++    * supported
++    * surfaces ------------- EGL_SURFACE_TYPE
++    * colbuf --------------- EGL_COLOR_BUFFER_TYPE
++    *
++    * yuv
++    * p -------------------- EGL_YUV_NUMBER_OF_PLANES_EXT
++    * sub ------------------ EGL_YUV_SUBSAMPLE_EXT
++    * ord ------------------ EGL_YUV_ORDER_EXT
++    */
++   _eglLog(_EGL_DEBUG, "---------------");
++   _eglLog(_EGL_DEBUG, "Configurations:");
++   _eglLog(_EGL_DEBUG, "cho       bf lv  color size  dp st  ms           vis  cav  bi     renderable           supported"
++           " col     yuv    ");
++   _eglLog(_EGL_DEBUG, "sen    id sz  l  r  g  b  a  th cl ns b           id  eat  nd  gl es es2 es3 vg         surfaces"
++           " buf p sub  ord");
++   _eglLog(_EGL_DEBUG, "---------------");
++}
++
++static void
++_snprintfStrcat(char *const msg, const int maxSize, const char *fmt, ...)
++{
++   int maxAllowed;
++   va_list args;
++
++   maxAllowed = maxSize - strlen(msg);
++
++   va_start(args, fmt);
++   (void) vsnprintf(&msg[strlen(msg)], maxAllowed, fmt, args);
++   va_end(args);
++}
++
++static inline const char *_enumToString(EGLint constant)
++{
++   switch (constant) {
++   case EGL_YUV_SUBSAMPLE_4_2_0_EXT: return "420";
++   case EGL_YUV_SUBSAMPLE_4_2_2_EXT: return "422";
++   case EGL_YUV_SUBSAMPLE_4_4_4_EXT: return "444";
++   case EGL_YUV_ORDER_AYUV_EXT: return "AYUV";
++   case EGL_YUV_ORDER_UYVY_EXT: return "UYVY";
++   case EGL_YUV_ORDER_VYUY_EXT: return "VYUY";
++   case EGL_YUV_ORDER_YUYV_EXT: return "YUYV";
++   case EGL_YUV_ORDER_YVYU_EXT: return "YVYU";
++   case EGL_YUV_ORDER_YUV_EXT: return "YUV";
++   case EGL_YUV_ORDER_YVU_EXT: return "YVU";
++   case EGL_LUMINANCE_BUFFER: return "lum";
++   case EGL_YUV_BUFFER_EXT: return "yuv";
++   case EGL_RGB_BUFFER: return "rgb";
++   default: return "?";
++   }
++}
++
++static void
++_eglGetConfigAttrs(_EGLDisplay *const dpy, _EGLConfig *const conf,
++                   struct _printAttributes *const attr)
++{
++   EGLBoolean success = EGL_TRUE;
++
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_CONFIG_ID, &attr->id);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_BUFFER_SIZE, &attr->size);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_LEVEL, &attr->level);
++
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_RED_SIZE, &attr->red);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_GREEN_SIZE, &attr->green);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_BLUE_SIZE, &attr->blue);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_ALPHA_SIZE, &attr->alpha);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_DEPTH_SIZE, &attr->depth);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_STENCIL_SIZE, &attr->stencil);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_NATIVE_VISUAL_ID, &attr->vid);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_NATIVE_VISUAL_TYPE, &attr->vtype);
++
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_CONFIG_CAVEAT, &attr->caveat);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_BIND_TO_TEXTURE_RGB, &attr->bindRgb);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_BIND_TO_TEXTURE_RGBA, &attr->bindRgba);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_RENDERABLE_TYPE, &attr->renderable);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_SURFACE_TYPE, &attr->surfaces);
++
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_SAMPLES, &attr->samples);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_SAMPLE_BUFFERS, &attr->sampleBuffers);
++   success &= _eglGetConfigAttrib(dpy, conf, EGL_COLOR_BUFFER_TYPE, &attr->colorBufferType);
++
++   if (conf->Display->Extensions.EXT_yuv_surface) {
++      success &= _eglGetConfigAttrib(dpy, conf, EGL_YUV_NUMBER_OF_PLANES_EXT,
++                                     &attr->numPlanes);
++      success &= _eglGetConfigAttrib(dpy, conf, EGL_YUV_SUBSAMPLE_EXT, &attr->subsample);
++      success &= _eglGetConfigAttrib(dpy, conf, EGL_YUV_ORDER_EXT, &attr->order);
++   }
++
++   if (!success)
++      _eglLog(_EGL_DEBUG, "%s: config tainted, could not obtain all attributes",
++              __func__);
++}
++
++static void
++_eglPrintConfig(_EGLDisplay *const dpy, _EGLConfig *const conf,
++                char *const printMsg, const int maxMsgSize)
++{
++   struct _printAttributes attr = { 0 };
++
++   _eglGetConfigAttrs(dpy, conf, &attr);
++
++   if (attr.surfaces & EGL_WINDOW_BIT)
++      strcat(attr.surfString, "win,");
++   if (attr.surfaces & EGL_PBUFFER_BIT)
++      strcat(attr.surfString, "pb,");
++   if (attr.surfaces & EGL_PIXMAP_BIT)
++      strcat(attr.surfString, "pix,");
++   if (attr.surfaces & EGL_STREAM_BIT_KHR)
++      strcat(attr.surfString, "str,");
++   if (attr.surfaces & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)
++      strcat(attr.surfString, "prsv,");
++   if (strlen(attr.surfString) > 0)
++      attr.surfString[strlen(attr.surfString) - 1] = 0;
++
++   _snprintfStrcat(printMsg, maxMsgSize,
++                   "0x%03x %2d %2d %2d %2d %2d %2d  %2d %2d %2d%2d 0x%08x%2s     ",
++                   attr.id, attr.size, attr.level,
++                   attr.red, attr.green, attr.blue, attr.alpha,
++                   attr.depth, attr.stencil,
++                   attr.samples, attr.sampleBuffers, attr.vid,
++                   attr.vtype < 6 ? vnames[attr.vtype] : "--");
++
++   _snprintfStrcat(printMsg, maxMsgSize,
++                   "%c  %c   %c  %c   %c   %c   %c %15s",
++                   (attr.caveat != EGL_NONE) ? 'y' : ' ',
++                   (attr.bindRgba) ? 'a' : (attr.bindRgb) ? 'y' : ' ',
++                   (attr.renderable & EGL_OPENGL_BIT) ? 'y' : ' ',
++                   (attr.renderable & EGL_OPENGL_ES_BIT) ? 'y' : ' ',
++                   (attr.renderable & EGL_OPENGL_ES2_BIT) ? 'y' : ' ',
++                   (attr.renderable & EGL_OPENGL_ES3_BIT) ? 'y' : ' ',
++                   (attr.renderable & EGL_OPENVG_BIT) ? 'y' : ' ',
++                   attr.surfString);
++
++   _snprintfStrcat(printMsg, maxMsgSize, " %3.3s",
++                   _enumToString(attr.colorBufferType));
++
++   if (attr.colorBufferType == EGL_YUV_BUFFER_EXT)
++      _snprintfStrcat(printMsg, maxMsgSize, " %1.1d %3.3s %4.4s", attr.numPlanes,
++                      _enumToString(attr.subsample), _enumToString(attr.order));
++
++   _eglLog(_EGL_DEBUG, printMsg);
++}
++
++static void
++_eglMarkChosenConfig(_EGLConfig *const config,
++                     _EGLConfig *const *const chosenConfigs,
++                     const EGLint numConfigs, char *const printMsg,
++                     const int maxMsgSize)
++{
++   const char padding[] = "   ";
++
++   if (chosenConfigs == NULL) {
++      _snprintfStrcat(printMsg, maxMsgSize, "%s ", &padding[0]);
++      return;
++   }
++
++   /* Find a match, "mark" and return */
++   for (EGLint i = 0; i < numConfigs; i++) {
++      if (config == chosenConfigs[i]) {
++         _snprintfStrcat(printMsg, maxMsgSize, "%*d ", strlen(padding), i);
++         return;
++      }
++   }
++
++   _snprintfStrcat(printMsg, maxMsgSize, "%s ", &padding[0]);
++}
++
++static void
++_eglPrintConfigs(_EGLDisplay *const dpy,
++                 EGLConfig *const configs, const EGLint numConfigs,
++                 const enum EGL_CONFIG_DEBUG_OPTION printOption)
++{
++   const int maxMsgSize = CONFIG_DEBUG_MSG_MAX;
++   EGLint numConfigsToPrint;
++   _EGLConfig **configsToPrint;
++   _EGLConfig **chosenConfigs;
++   char *printMsg;
++
++   printMsg = malloc(maxMsgSize);
++   if (!printMsg) {
++      _eglLog(_EGL_DEBUG, "%s: failed to allocate the print message", __func__);
++      return;
++   }
++
++   /*
++    * If the printout request came from the 'eglChooseConfig', all
++    * configs are printed, and the "chosen" configs are marked.
++    */
++   if (printOption == EGL_CONFIG_DEBUG_CHOOSE) {
++      configsToPrint = (_EGLConfig **) dpy->Configs->Elements;
++      numConfigsToPrint = dpy->Configs->Size;
++      chosenConfigs = (_EGLConfig **) configs;
++   } else {
++      assert(printOption == EGL_CONFIG_DEBUG_GET);
++      configsToPrint = (_EGLConfig **) configs;
++      numConfigsToPrint = numConfigs;
++      chosenConfigs = NULL;
++   }
++
++   _printHeaderFormat();
++   for (EGLint i = 0; i < numConfigsToPrint; i++) {
++      _EGLConfig *configToPrint = configsToPrint[i];
++
++      /* "clear" message */
++      printMsg[0] = '\0';
++
++      _eglMarkChosenConfig(configToPrint, chosenConfigs, numConfigs,
++                           printMsg, maxMsgSize);
++
++      _eglPrintConfig(dpy, configToPrint, printMsg, maxMsgSize);
++   }
++
++   free(printMsg);
++}
++
++void eglPrintConfigDebug(_EGLDisplay *const dpy,
++                         EGLConfig *const configs, const EGLint numConfigs,
++                         const enum EGL_CONFIG_DEBUG_OPTION printOption)
++{
++   if (!numConfigs || !configs) {
++      _eglLog(_EGL_DEBUG, "%s: nothing to print", __func__);
++      return;
++   }
++
++   switch (printOption) {
++   case EGL_CONFIG_DEBUG_CHOOSE:
++   case EGL_CONFIG_DEBUG_GET:
++      _eglPrintConfigs(dpy, configs, numConfigs, printOption);
++      break;
++   default:
++      _eglLog(_EGL_DEBUG, "%s: bad debug option", __func__);
++      break;
++   }
++}
+diff --git a/src/egl/main/eglconfigdebug.h b/src/egl/main/eglconfigdebug.h
+new file mode 100644
+index 0000000..562aeff
+--- /dev/null
++++ b/src/egl/main/eglconfigdebug.h
+@@ -0,0 +1,55 @@
++/**************************************************************************
++ * Copyright 2017 Imagination Technologies.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#ifndef EGLCONFIGDEBUG_INCLUDED
++#define EGLCONFIGDEBUG_INCLUDED
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include "egltypedefs.h"
++
++/**
++ * Config printout options.
++ */
++enum EGL_CONFIG_DEBUG_OPTION {
++   EGL_CONFIG_DEBUG_CHOOSE,
++   EGL_CONFIG_DEBUG_GET,
++};
++
++/**
++ * Print the list of configs and the associated attributes.
++ */
++void eglPrintConfigDebug(_EGLDisplay *const dpy,
++                         EGLConfig *const configs, const EGLint numConfigs,
++                         const enum EGL_CONFIG_DEBUG_OPTION printOption);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif /* EGLCONFIGDEBUG_INCLUDED */
+diff --git a/src/egl/main/egllog.c b/src/egl/main/egllog.c
+index 973b760..7a9032f 100644
+--- a/src/egl/main/egllog.c
++++ b/src/egl/main/egllog.c
+@@ -153,6 +153,15 @@ _eglInitLogger(void)
+    }
+ }
+ 
++/**
++ * Return the log level.
++ */
++EGLint
++_eglGetLogLevel(void)
++{
++   return logging.level;
++}
++
+ 
+ /**
+  * Log a message with message logger.
+diff --git a/src/egl/main/egllog.h b/src/egl/main/egllog.h
+index 2a06a34..a1cf977 100644
+--- a/src/egl/main/egllog.h
++++ b/src/egl/main/egllog.h
+@@ -44,6 +44,10 @@ extern "C" {
+ #define _EGL_DEBUG   3   /* useful info for debugging */
+ 
+ 
++extern EGLint
++_eglGetLogLevel(void);
++
++
+ extern void
+ _eglLog(EGLint level, const char *fmtStr, ...);
+ 
+diff --git a/src/egl/meson.build b/src/egl/meson.build
+index daa6a3a..384bb98 100644
+--- a/src/egl/meson.build
++++ b/src/egl/meson.build
+@@ -31,6 +31,8 @@ files_egl = files(
+   'main/eglapi.c',
+   'main/eglarray.c',
+   'main/eglarray.h',
++  'main/eglconfigdebug.c',
++  'main/eglconfigdebug.h',
+   'main/eglconfig.c',
+   'main/eglconfig.h',
+   'main/eglcontext.c',

+ 214 - 0
recipes-graphics/mesa/mesa-pvr/0038-egl-add-support-for-EXT_image_gl_colorspace.patch

@@ -0,0 +1,214 @@
+From afc48c7cfe5e859832e753a987b9405544ce82e8 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Tue, 29 Jan 2019 14:36:25 +0000
+Subject: [PATCH] egl: add support for EXT_image_gl_colorspace
+
+---
+ src/egl/drivers/dri2/egl_dri2.c | 57 +++++++++++++++++++++++++++++++--
+ src/egl/main/eglapi.c           |  1 +
+ src/egl/main/egldisplay.h       |  1 +
+ src/egl/main/eglimage.c         | 14 ++++++++
+ src/egl/main/eglimage.h         |  3 ++
+ 5 files changed, 74 insertions(+), 2 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index 2f8414e..6b26ff9 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1084,6 +1084,9 @@ dri2_setup_screen(_EGLDisplay *disp)
+           dri2_dpy->image->createImageFromBuffer) {
+          disp->Extensions.IMG_cl_image = EGL_TRUE;
+       }
++
++      if (disp->Extensions.KHR_gl_colorspace)
++         disp->Extensions.EXT_image_gl_colorspace = EGL_TRUE;
+    }
+ 
+    if (dri2_dpy->flush_control)
+@@ -2724,6 +2727,11 @@ dri2_create_image_wayland_wl_buffer_tizen(_EGLDisplay *disp, _EGLContext *ctx,
+       return NULL;
+    }
+ 
++   if (attrs.GLColorspace != EGL_GL_COLORSPACE_DEFAULT_EXT) {
++      _eglError(EGL_BAD_MATCH, "unsupported colorspace");
++      return NULL;
++   }
++
+    if (tbm_surface_get_info(tbm_surf, &info)) {
+       _eglError(EGL_BAD_PARAMETER, "tbm_surface_get_info");
+       return NULL;
+@@ -2799,6 +2807,11 @@ dri2_create_image_wayland_wl_buffer(_EGLDisplay *disp, _EGLContext *ctx,
+    if (!_eglParseImageAttribList(&attrs, disp, attr_list))
+       return NULL;
+ 
++   if (attrs.GLColorspace != EGL_GL_COLORSPACE_DEFAULT_EXT) {
++      _eglError(EGL_BAD_MATCH, "unsupported colorspace");
++      return NULL;
++   }
++
+    plane = attrs.PlaneWL;
+    f = buffer->driver_format;
+    if (plane < 0 || plane >= f->nplanes) {
+@@ -2862,6 +2875,11 @@ dri2_create_image_khr_texture(_EGLDisplay *disp, _EGLContext *ctx,
+    if (!_eglParseImageAttribList(&attrs, disp, attr_list))
+       return EGL_NO_IMAGE_KHR;
+ 
++   if (attrs.GLColorspace != EGL_GL_COLORSPACE_DEFAULT_EXT) {
++      _eglError(EGL_BAD_MATCH, "unsupported colorspace");
++      return EGL_NO_IMAGE_KHR;
++   }
++
+    switch (target) {
+    case EGL_GL_TEXTURE_2D_KHR:
+       if (!disp->Extensions.KHR_gl_texture_2D_image) {
+@@ -3007,6 +3025,11 @@ dri2_create_image_mesa_drm_buffer(_EGLDisplay *disp, _EGLContext *ctx,
+       return NULL;
+    }
+ 
++   if (attrs.GLColorspace != EGL_GL_COLORSPACE_DEFAULT_EXT) {
++      _eglError(EGL_BAD_MATCH, "unsupported colorspace");
++      return NULL;
++   }
++
+    switch (attrs.DRMBufferFormatMESA) {
+    case EGL_DRM_BUFFER_FORMAT_ARGB32_MESA:
+       format = __DRI_IMAGE_FORMAT_ARGB8888;
+@@ -3189,6 +3212,23 @@ dri2_num_fourcc_format_planes(EGLint format)
+    }
+ }
+ 
++static int
++dri2_get_srgb_fourcc(int drm_fourcc)
++{
++   switch (drm_fourcc) {
++   case DRM_FORMAT_ARGB8888:
++      return __DRI_IMAGE_FOURCC_SARGB8888;
++   case DRM_FORMAT_ABGR8888:
++      return __DRI_IMAGE_FOURCC_SABGR8888;
++   case DRM_FORMAT_BGR888:
++      return __DRI_IMAGE_FOURCC_SBGR888;
++   default:
++      _eglLog(_EGL_DEBUG, "%s: no matching sRGB FourCC for %#x",
++              __func__, drm_fourcc);
++      return 0;
++   }
++}
++
+ /* Returns the total number of file descriptors. Zero indicates an error. */
+ static unsigned
+ dri2_check_dma_buf_format(const _EGLImageAttribs *attrs)
+@@ -3334,6 +3374,7 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
+    int fds[DMA_BUF_MAX_PLANES];
+    int pitches[DMA_BUF_MAX_PLANES];
+    int offsets[DMA_BUF_MAX_PLANES];
++   int fourcc;
+    uint64_t modifier;
+    bool has_modifier = false;
+    unsigned error;
+@@ -3359,6 +3400,18 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
+    if (!num_fds)
+       return NULL;
+ 
++   if (attrs.GLColorspace == EGL_GL_COLORSPACE_SRGB_KHR) {
++      fourcc = dri2_get_srgb_fourcc(attrs.DMABufFourCC.Value);
++      if (fourcc == 0) {
++         _eglError(EGL_BAD_MATCH, "unsupported colorspace");
++         return NULL;
++      }
++   } else {
++      assert(attrs.GLColorspace == EGL_GL_COLORSPACE_LINEAR_KHR ||
++             attrs.GLColorspace == EGL_GL_COLORSPACE_DEFAULT_EXT);
++      fourcc = attrs.DMABufFourCC.Value;
++   }
++
+    for (unsigned i = 0; i < num_fds; ++i) {
+       fds[i] = attrs.DMABufPlaneFds[i].Value;
+       pitches[i] = attrs.DMABufPlanePitches[i].Value;
+@@ -3403,7 +3456,7 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
+       }
+       dri_image =
+          dri2_dpy->image->createImageFromDmaBufs2(dri2_dpy->dri_screen,
+-            attrs.Width, attrs.Height, attrs.DMABufFourCC.Value,
++            attrs.Width, attrs.Height, fourcc,
+             modifier, fds, num_fds, pitches, offsets,
+             attrs.DMABufYuvColorSpaceHint.Value,
+             attrs.DMABufSampleRangeHint.Value,
+@@ -3415,7 +3468,7 @@ dri2_create_image_dma_buf(_EGLDisplay *disp, _EGLContext *ctx,
+    else {
+       dri_image =
+          dri2_dpy->image->createImageFromDmaBufs(dri2_dpy->dri_screen,
+-            attrs.Width, attrs.Height, attrs.DMABufFourCC.Value,
++            attrs.Width, attrs.Height, fourcc,
+             fds, num_fds, pitches, offsets,
+             attrs.DMABufYuvColorSpaceHint.Value,
+             attrs.DMABufSampleRangeHint.Value,
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index e4f2c43..2d3931d 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -508,6 +508,7 @@ _eglCreateExtensionsString(_EGLDisplay *disp)
+    _EGL_CHECK_EXTENSION(EXT_create_context_robustness);
+    _EGL_CHECK_EXTENSION(EXT_image_dma_buf_import);
+    _EGL_CHECK_EXTENSION(EXT_image_dma_buf_import_modifiers);
++   _EGL_CHECK_EXTENSION(EXT_image_gl_colorspace);
+    _EGL_CHECK_EXTENSION(EXT_protected_surface);
+    _EGL_CHECK_EXTENSION(EXT_surface_CTA861_3_metadata);
+    _EGL_CHECK_EXTENSION(EXT_surface_SMPTE2086_metadata);
+diff --git a/src/egl/main/egldisplay.h b/src/egl/main/egldisplay.h
+index 8489af9..6e5b29f 100644
+--- a/src/egl/main/egldisplay.h
++++ b/src/egl/main/egldisplay.h
+@@ -108,6 +108,7 @@ struct _egl_extensions
+    EGLBoolean EXT_create_context_robustness;
+    EGLBoolean EXT_image_dma_buf_import;
+    EGLBoolean EXT_image_dma_buf_import_modifiers;
++   EGLBoolean EXT_image_gl_colorspace;
+    EGLBoolean EXT_pixel_format_float;
+    EGLBoolean EXT_protected_surface;
+    EGLBoolean EXT_surface_CTA861_3_metadata;
+diff --git a/src/egl/main/eglimage.c b/src/egl/main/eglimage.c
+index 64bf7f2..73d4e85 100644
+--- a/src/egl/main/eglimage.c
++++ b/src/egl/main/eglimage.c
+@@ -46,6 +46,18 @@ _eglParseKHRImageAttribs(_EGLImageAttribs *attrs, _EGLDisplay *disp,
+       attrs->ImagePreserved = val;
+       break;
+ 
++   case EGL_GL_COLORSPACE_KHR:
++      if (!disp->Extensions.EXT_image_gl_colorspace)
++         return EGL_BAD_PARAMETER;
++
++      if (val != EGL_GL_COLORSPACE_SRGB_KHR &&
++          val != EGL_GL_COLORSPACE_LINEAR_KHR &&
++          val != EGL_GL_COLORSPACE_DEFAULT_EXT)
++         return EGL_BAD_PARAMETER;
++
++      attrs->GLColorspace = val;
++      break;
++
+    case EGL_GL_TEXTURE_LEVEL_KHR:
+       if (!disp->Extensions.KHR_gl_texture_2D_image)
+          return EGL_BAD_PARAMETER;
+@@ -285,6 +297,8 @@ _eglParseImageAttribList(_EGLImageAttribs *attrs, _EGLDisplay *disp,
+ 
+    memset(attrs, 0, sizeof(*attrs));
+ 
++   attrs->GLColorspace = EGL_GL_COLORSPACE_DEFAULT_EXT;
++
+    if (!attrib_list)
+       return EGL_TRUE;
+ 
+diff --git a/src/egl/main/eglimage.h b/src/egl/main/eglimage.h
+index 9837f05..fc02d7a 100644
+--- a/src/egl/main/eglimage.h
++++ b/src/egl/main/eglimage.h
+@@ -80,6 +80,9 @@ struct _egl_image_attribs
+    struct _egl_image_attrib_int DMABufChromaHorizontalSiting;
+    struct _egl_image_attrib_int DMABufChromaVerticalSiting;
+ 
++   /* EGL_EXT_image_gl_colorspace */
++   EGLint GLColorspace;
++
+    /* EGL_EXT_protected_surface */
+    EGLBoolean ProtectedContent;
+ };

+ 33 - 0
recipes-graphics/mesa/mesa-pvr/0039-meson-force-C-2011-for-thread_local.patch

@@ -0,0 +1,33 @@
+From 7a283afb295d6377892c9aa4078f836cb43e43f8 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 24 Jun 2019 09:35:39 +0100
+Subject: [PATCH] meson: force C++ 2011 for "thread_local"
+
+For some combinations of Meson and the GNU C++ compiler, Meson does
+not add '-std=c++11' to the command line arguments, resulting in
+compilation errors, due to the use of the "thread_local" keyword (a
+C++ 2011 feature). If the C++ compiler doesn't understand the
+"thread_local" keyword by default, add '-std=c++11' to the compiler
+command line arguments.
+
+---
+ meson.build | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/meson.build b/meson.build
+index 2018562..21d93d3 100644
+--- a/meson.build
++++ b/meson.build
+@@ -32,6 +32,12 @@ project(
+ cc = meson.get_compiler('c')
+ cpp = meson.get_compiler('cpp')
+ 
++if not cpp.compiles('thread_local int x = 0;', name : 'thread_local')
++  if cpp.has_argument('-std=c++11')
++     add_project_arguments('-std=c++11', language : 'cpp')
++  endif
++endif
++
+ null_dep = dependency('', required : false)
+ 
+ if get_option('layout') != 'mirror'

+ 783 - 0
recipes-graphics/mesa/mesa-pvr/0040-dri2-add-support-for-swap-intervals-other-than-1.patch

@@ -0,0 +1,783 @@
+From 6d46bd2dc65bde965fe8d6c53112c61274ac837f Mon Sep 17 00:00:00 2001
+From: Luigi Santivetti <luigi.santivetti@imgtec.com>
+Date: Wed, 19 Jun 2019 16:36:06 +0100
+Subject: [PATCH] dri2: add support for swap intervals other than 1
+
+Before this change, the swap interval was fixed at 1, with page flips
+scheduled on the next vblank. This change allows any swap interval
+between 0 and 10 to be set.
+
+An additional thread is created, so as not to rely on the application
+polling for previously scheduled drm events (be it a flip or a vblank).
+Instead, each call to swap buffers made by the application will be
+queued, and consumed asynchronously by the additional thread. This
+ensures that drm events will be handled as soon as possible,
+regardless of the timing of subsequent calls to swap buffers.
+
+Change-Id: If7c0495df7ddfaa08583a14f820c46e1b97da788
+Signed-off-by: Luigi Santivetti <luigi.santivetti@imgtec.com>
+
+---
+ src/egl/drivers/dri2/egl_dri2.h      |  34 +-
+ src/egl/drivers/dri2/platform_null.c | 541 +++++++++++++++++++++++----
+ 2 files changed, 497 insertions(+), 78 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index eb2127e..64d60ed 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -332,6 +332,23 @@ struct tpl_swap_queue_elem
+ };
+ #endif
+ 
++#ifdef HAVE_NULL_PLATFORM
++struct swap_queue_elem
++{
++   uint32_t             swap_interval;
++   uint32_t             back_id;
++   uint32_t             fb_id;
++};
++
++enum {
++   SWAP_IDLE,
++   SWAP_FLIP,
++   SWAP_VBLANK,
++   SWAP_POLL,
++   SWAP_ERROR,
++};
++#endif
++
+ struct dri2_egl_surface
+ {
+    _EGLSurface          base;
+@@ -370,13 +387,18 @@ struct dri2_egl_surface
+ #ifdef HAVE_TIZEN_PLATFORM
+    tpl_surface_t         *tpl_surf;
+    bool                   reset;
++   struct tpl_swap_queue_elem swap_queue[DRI2_SURFACE_NUM_COLOR_BUFFERS];
++#endif
++#if defined(HAVE_TIZEN_PLATFORM) || defined(HAVE_NULL_PLATFORM)
+    /*
+     * Protects swap_queue_idx_head, swap_queue_idx_tail and
+     * color_buffers.locked.
+     */
+    pthread_mutex_t        mutex;
+    pthread_cond_t         swap_queue_cond;
+-   struct tpl_swap_queue_elem swap_queue[DRI2_SURFACE_NUM_COLOR_BUFFERS];
++#if defined(HAVE_NULL_PLATFORM)
++   pthread_cond_t         swap_unlock_buffer_cond;
++#endif
+    int                    swap_queue_idx_head;
+    int                    swap_queue_idx_tail;
+    pthread_t              swap_queue_processor;
+@@ -412,7 +434,7 @@ struct dri2_egl_surface
+ #endif
+       bool                locked;
+       int                 age;
+-   } color_buffers[4], *back, *current;
++   } color_buffers[DRI2_SURFACE_NUM_COLOR_BUFFERS], *back, *current;
+ #endif
+ 
+ #ifdef HAVE_ANDROID_PLATFORM
+@@ -442,7 +464,13 @@ struct dri2_egl_surface
+ #endif
+ 
+ #ifdef HAVE_NULL_PLATFORM
+-   uint32_t             front_fb_id;
++   uint32_t                front_fb_id;
++   struct swap_queue_elem  swap_queue[DRI2_SURFACE_NUM_COLOR_BUFFERS];
++   struct swap_queue_elem  *swap_data;
++   int                     swap_state;
++   bool                    mutex_init;
++   bool                    cond_init;
++   bool                    cond_init_unlock_buffer;
+ #endif
+ 
+    int out_fence_fd;
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index fb03ecc..5b7c1ec 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -31,6 +31,7 @@
+ #include <drm_fourcc.h>
+ #include <fcntl.h>
+ #include <poll.h>
++#include <pthread.h>
+ #include <stdbool.h>
+ #include <stdint.h>
+ #include <stdlib.h>
+@@ -58,6 +59,17 @@ struct object_property {
+    uint64_t prop_value;
+ };
+ 
++static inline
++uint32_t get_back_buffer_id(struct dri2_egl_surface *dri2_surf)
++{
++   uintptr_t offset = ((uintptr_t) dri2_surf->back) -
++      ((uintptr_t) &dri2_surf->color_buffers[0]);
++
++   assert(dri2_surf->back && !(offset >> 32));
++
++   return (uint32_t) (offset / sizeof(dri2_surf->color_buffers[0]));
++}
++
+ #define object_property_set_named(output, object_type, prop_name, value)    \
+    {                                                                        \
+       .object_id = (output)->object_type##_id,                              \
+@@ -176,36 +188,6 @@ property_id_get_for_name(drmModePropertyRes **prop_res, const char *prop_name)
+    return 0;
+ }
+ 
+-static void
+-flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
+-             unsigned int tv_usec, void *user_data)
+-{
+-   bool *plocked = user_data;
+-
+-   if (plocked)
+-      *plocked = false;
+-}
+-
+-static bool
+-flip_process(int fd)
+-{
+-   static drmEventContext evctx =
+-      {.version = 2, .page_flip_handler = flip_handler};
+-   struct pollfd pfd = {.fd = fd, .events = POLLIN};
+-   int ret;
+-
+-   do {
+-      ret = poll(&pfd, 1, -1);
+-   } while (ret > 0 && pfd.revents != pfd.events);
+-
+-   if (ret <= 0)
+-      return false;
+-
+-   drmHandleEvent(fd, &evctx);
+-
+-   return true;
+-}
+-
+ static drmModePropertyRes **
+ object_get_property_resources(int fd, uint32_t object_id, uint32_t object_type)
+ {
+@@ -495,6 +477,113 @@ display_output_atomic_modeset(int fd, struct display_output *output, uint32_t fb
+                               DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+ }
+ 
++static void
++swap_enqueue_data(struct dri2_egl_surface *dri2_surf, uint32_t back_id,
++                  uint32_t interval)
++{
++   struct swap_queue_elem *swap_data;
++
++   pthread_mutex_lock(&dri2_surf->mutex);
++   swap_data = &dri2_surf->swap_queue[dri2_surf->swap_queue_idx_tail];
++   swap_data->swap_interval = interval;
++   swap_data->fb_id = dri2_surf->back->fb_id;
++   swap_data->back_id = back_id;
++
++   dri2_surf->swap_queue_idx_tail++;
++   dri2_surf->swap_queue_idx_tail %= ARRAY_SIZE(dri2_surf->swap_queue);
++
++   /* Notify the swap thread there is new work to do */
++   pthread_cond_signal(&dri2_surf->swap_queue_cond);
++   pthread_mutex_unlock(&dri2_surf->mutex);
++}
++
++static void
++swap_dequeue_data_start(struct dri2_egl_surface *dri2_surf)
++{
++   pthread_mutex_lock(&dri2_surf->mutex);
++   while (dri2_surf->swap_queue_idx_head == dri2_surf->swap_queue_idx_tail)
++      pthread_cond_wait(&dri2_surf->swap_queue_cond, &dri2_surf->mutex);
++
++   dri2_surf->swap_data =
++      &dri2_surf->swap_queue[dri2_surf->swap_queue_idx_head];
++   pthread_mutex_unlock(&dri2_surf->mutex);
++}
++
++static void
++swap_dequeue_data_finish(struct dri2_egl_surface *dri2_surf)
++{
++   pthread_mutex_lock(&dri2_surf->mutex);
++
++   if (dri2_surf->current)
++      dri2_surf->current->locked = false;
++
++   dri2_surf->current =
++      &dri2_surf->color_buffers[dri2_surf->swap_data->back_id];
++   dri2_surf->swap_state = SWAP_IDLE;
++
++   dri2_surf->swap_queue_idx_head++;
++   dri2_surf->swap_queue_idx_head %= ARRAY_SIZE(dri2_surf->swap_queue);
++
++   /* Notify get_back_bo that a buffer has become available */
++   pthread_cond_signal(&dri2_surf->swap_unlock_buffer_cond);
++   pthread_mutex_unlock(&dri2_surf->mutex);
++}
++
++static void
++flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
++             unsigned int tv_usec, void *flip_data)
++{
++   struct dri2_egl_surface *dri2_surf = flip_data;
++
++   (void) tv_sec;
++   (void) tv_usec;
++   (void) sequence;
++
++   /* Ultimate queueing ops */
++   swap_dequeue_data_finish(dri2_surf);
++}
++
++static void
++vblank_handler(int fd, unsigned int sequence, unsigned int tv_sec,
++               unsigned int tv_usec, void *vblank_data)
++{
++   struct dri2_egl_surface *dri2_surf = vblank_data;
++
++   (void) tv_sec;
++   (void) tv_usec;
++   (void) sequence;
++
++   dri2_surf->swap_state = SWAP_FLIP;
++}
++
++static int
++drm_event_process(int fd)
++{
++   static drmEventContext evctx = {
++      .version = 2,
++      .page_flip_handler = flip_handler,
++      .vblank_handler = vblank_handler
++   };
++   struct pollfd pfd = {.fd = fd, .events = POLLIN};
++   int ret;
++
++   do {
++      ret = poll(&pfd, 1, -1);
++   } while (ret > 0 && pfd.revents != pfd.events);
++
++   if (ret <= 0)
++      /* Man says:
++       *
++       * On error, -1 is returned, and errno is set to indicate the
++       * cause of the error.
++       */
++      return -1;
++
++   drmHandleEvent(fd, &evctx);
++
++   return 0;
++}
++
+ static bool
+ display_output_init(int fd, struct display_output *output, bool use_atomic)
+ {
+@@ -571,10 +660,46 @@ static int
+ display_output_flip(int fd, struct display_output *output, uint32_t fb_id,
+                     uint32_t flags, void *flip_data)
+ {
+-   if (output->atomic_state)
+-      return display_output_atomic_flip(fd, output, fb_id, flags, flip_data);
++   int err;
++
++   do {
++      if (output->atomic_state)
++         err = display_output_atomic_flip(fd, output, fb_id, flags, flip_data);
++      else
++         err = drmModePageFlip(fd, output->crtc_id, fb_id, flags, flip_data);
++   } while (err == -EBUSY);
++
++   return err;
++}
++
++static int
++display_request_vblank(int fd, uint32_t target_frame, uint32_t flags,
++                       void *vblank_data)
++{
++   drmVBlank vblank = {
++      .request = {
++         .type = flags,
++         .sequence = target_frame,
++         .signal = (unsigned long)vblank_data,
++      }
++   };
++
++   return drmWaitVBlank(fd, &vblank);
++}
++
++static int
++display_get_vblank_sequence(int fd, uint32_t *current_vblank_out)
++{
++   drmVBlank vblank = { .request = { .type = DRM_VBLANK_RELATIVE } };
++   int err;
++
++   err = drmWaitVBlank(fd, &vblank);
++   if (err)
++      return err;
+ 
+-   return drmModePageFlip(fd, output->crtc_id, fb_id, flags, flip_data);
++   *current_vblank_out = vblank.reply.sequence;
++
++   return 0;
+ }
+ 
+ static int
+@@ -587,6 +712,213 @@ display_output_modeset(int fd, struct display_output *output, uint32_t fb_id)
+                          &output->connector_id, 1, &output->mode);
+ }
+ 
++static int
++swap_idle_get_target_frame(struct dri2_egl_surface *dri2_surf,
++                           uint32_t *current_vblank_out, uint32_t *target_frame_out)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   int err;
++
++   /* For intarvals bigger than 1, always update current_vblank. The
++    * spec isn't fully clear, nonetheless page 25 and 26 of the PDF of the
++    * EGL 1.5 spec say:
++    *
++    *     "[the parameter interval] indicates the number of swap intervals
++    *      that will elapse before a buffer swap takes place after calling
++    *      eglSwapBuffers."
++    *
++    * We need to guarantee that the target frame is always ahead of the
++    * current vblank by the number of intervals set at the time swapBuffer
++    * is called. For intervals of 1 or 0, we don't need a target frame.
++    */
++   err = display_get_vblank_sequence(dri2_dpy->fd, current_vblank_out);
++   if (err)
++      return err;
++
++   assert(dri2_surf->swap_data->swap_interval > 0);
++
++   /* -1 accounts for vsync locked flip, so get a vblank one frame earlier */
++   *target_frame_out =
++      *current_vblank_out + dri2_surf->swap_data->swap_interval - 1;
++
++   return 0;
++}
++
++static int
++swap_idle_state_transition(struct dri2_egl_surface *dri2_surf,
++                           uint32_t *target_frame_out)
++{
++   uint32_t current_vblank = 0;
++   uint32_t target_frame = 0;
++   int err;
++
++   /* update dri2_surf->swap_data */
++   swap_dequeue_data_start(dri2_surf);
++
++   /* update next target frame */
++   if (dri2_surf->swap_data->swap_interval > 1) {
++      err = swap_idle_get_target_frame(dri2_surf, &current_vblank,
++                                       &target_frame);
++      if (err) {
++         dri2_surf->swap_state = SWAP_ERROR;
++         return err;
++      }
++   }
++
++   dri2_surf->swap_state =
++      target_frame <= current_vblank ? SWAP_FLIP : SWAP_VBLANK;
++   *target_frame_out = target_frame;
++
++   return 0;
++}
++
++static int
++swap_vblank_state_transition(struct dri2_egl_surface *dri2_surf,
++                             uint32_t target_frame)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   uint32_t flags = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
++   int err;
++
++   err = display_request_vblank(dri2_dpy->fd, target_frame,
++                                flags, dri2_surf);
++   if (err) {
++      dri2_surf->swap_state = SWAP_ERROR;
++      return err;
++   }
++
++   dri2_surf->swap_state = SWAP_POLL;
++
++   return 0;
++}
++
++static int
++swap_flip_state_transition(struct dri2_egl_surface *dri2_surf)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   uint32_t flags;
++   int err;
++
++   flags = DRM_MODE_PAGE_FLIP_EVENT;
++   if (dri2_surf->swap_data->swap_interval == 0) {
++      assert(!dri2_dpy->atomic_enabled);
++      flags |= DRM_MODE_PAGE_FLIP_ASYNC;
++   }
++
++   err = display_output_flip(dri2_dpy->fd, &dri2_dpy->output,
++                             dri2_surf->swap_data->fb_id, flags, dri2_surf);
++   if (err) {
++      dri2_surf->swap_state = SWAP_ERROR;
++      return err;
++   }
++
++   dri2_surf->swap_state = SWAP_POLL;
++
++   return 0;
++}
++
++static int
++swap_poll_state_transition(struct dri2_egl_surface *dri2_surf)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   int err;
++
++   /* dri2_surf->swap_state is being set inside the handler */
++   err = drm_event_process(dri2_dpy->fd);
++   if (err) {
++      dri2_surf->swap_state = SWAP_ERROR;
++      return err;
++   }
++
++   return 0;
++}
++
++static inline void
++swap_error_print_message(const int state, const int err)
++{
++   switch (state) {
++   case SWAP_IDLE:
++      _eglLog(_EGL_WARNING,
++              "failed to get a target frame (err=%d)", err);
++      break;
++   case SWAP_FLIP:
++      _eglLog(_EGL_WARNING,
++              "failed to schedule a pageflip (err=%d)", err);
++      break;
++   case SWAP_VBLANK:
++      _eglLog(_EGL_WARNING,
++              "failed to request a vblank event (err=%d)", err);
++      break;
++   case SWAP_POLL:
++      _eglLog(_EGL_WARNING,
++              "failed to poll for drm event (err=%d)", err);
++      break;
++   case SWAP_ERROR:
++      _eglLog(_EGL_WARNING,
++              "failed to swap buffers, unknown swap state");
++      break;
++   default:
++      _eglLog(_EGL_FATAL,
++              "failed to swap buffers (unknown error)");
++   };
++}
++
++static void
++swap_error_state_handler(struct dri2_egl_surface *dri2_surf,
++                         int state, int err)
++{
++   static bool do_log = true;
++
++   if (do_log) {
++      swap_error_print_message(state, err);
++      do_log = false;
++   }
++
++   swap_dequeue_data_finish(dri2_surf);
++}
++
++static void *
++swap_queue_processor_worker(void *data)
++{
++   struct dri2_egl_surface *dri2_surf = data;
++   int state = SWAP_IDLE, err = SWAP_ERROR;
++   uint32_t target_frame = 0;
++
++   assert(dri2_surf->swap_state == SWAP_IDLE);
++
++   while (1) {
++      switch (dri2_surf->swap_state) {
++      case SWAP_IDLE:
++         err = swap_idle_state_transition(dri2_surf, &target_frame);
++         break;
++      case SWAP_VBLANK:
++         err = swap_vblank_state_transition(dri2_surf, target_frame);
++         break;
++      case SWAP_FLIP:
++         err = swap_flip_state_transition(dri2_surf);
++         break;
++      case SWAP_POLL:
++         err = swap_poll_state_transition(dri2_surf);
++         break;
++      case SWAP_ERROR:
++         swap_error_state_handler(dri2_surf, state, err);
++         break;
++      default:
++         dri2_surf->swap_state = SWAP_ERROR;
++         break;
++      }
++
++      if (!err)
++         state = dri2_surf->swap_state;
++   }
++
++   return NULL;
++}
++
+ static bool
+ add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+                      uint32_t *fb_id_out)
+@@ -651,15 +983,18 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
+    struct dri2_egl_display *dri2_dpy =
+       dri2_egl_display(dri2_surf->base.Resource.Display);
+ 
+-   if (!dri2_surf->back) {
++   pthread_mutex_lock(&dri2_surf->mutex);
++   while (!dri2_surf->back) {
+       for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
+          if (!dri2_surf->color_buffers[i].locked) {
+             dri2_surf->back = &dri2_surf->color_buffers[i];
+             break;
+          }
+       }
++
++      /* Wait for a flip to get a buffer off the screen and unlock it */
+       if (!dri2_surf->back)
+-         return false;
++         pthread_cond_wait(&dri2_surf->swap_unlock_buffer_cond, &dri2_surf->mutex);
+    }
+ 
+    if (!dri2_surf->back->dri_image) {
+@@ -671,19 +1006,23 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
+                                       __DRI_IMAGE_USE_SCANOUT,
+                                       NULL);
+       if (!dri2_surf->back->dri_image)
+-         return false;
++         goto err_unlock;
+    }
+ 
+    if (!dri2_surf->back->fb_id) {
+       if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->back->dri_image,
+-                                &dri2_surf->back->fb_id)) {
+-         return false;
+-      }
++                                &dri2_surf->back->fb_id))
++         goto err_unlock;
+    }
+ 
+    dri2_surf->back->locked = 1;
++   pthread_mutex_unlock(&dri2_surf->mutex);
+ 
+    return true;
++
++err_unlock:
++   pthread_mutex_unlock(&dri2_surf->mutex);
++   return false;
+ }
+ 
+ static _EGLSurface *
+@@ -770,6 +1109,26 @@ dri2_null_create_window_surface(_EGLDisplay *disp, _EGLConfig *config,
+    dri2_surf->base.Width = dri2_dpy->output.mode.hdisplay;
+    dri2_surf->base.Height = dri2_dpy->output.mode.vdisplay;
+ 
++   /* After the dri2_surf is created, init thread's data */
++   dri2_surf->mutex_init = !pthread_mutex_init(&dri2_surf->mutex, NULL);
++   if (!dri2_surf->mutex_init) {
++      _eglError(EGL_BAD_ALLOC, "failed to init swap thread mutex");
++      goto err_destroy_surface;
++   }
++
++   dri2_surf->cond_init = !pthread_cond_init(&dri2_surf->swap_queue_cond, NULL);
++   if (!dri2_surf->cond_init) {
++      _eglError(EGL_BAD_ALLOC, "failed to init swap queue condition");
++      goto err_destroy_surface;
++   }
++
++   dri2_surf->cond_init_unlock_buffer =
++      !pthread_cond_init(&dri2_surf->swap_unlock_buffer_cond, NULL);
++   if (!dri2_surf->cond_init_unlock_buffer) {
++      _eglError(EGL_BAD_ALLOC, "failed to init swap buffer unlock condition");
++      goto err_destroy_surface;
++   }
++
+    if (!get_front_bo(dri2_surf))  {
+       _eglError(EGL_BAD_NATIVE_WINDOW, "window get buffer");
+       goto err_destroy_surface;
+@@ -783,6 +1142,14 @@ dri2_null_create_window_surface(_EGLDisplay *disp, _EGLConfig *config,
+    }
+ 
+    dri2_dpy->output.in_use = true;
++   dri2_surf->swap_state = SWAP_IDLE;
++
++   err = pthread_create(&dri2_surf->swap_queue_processor, NULL,
++                        swap_queue_processor_worker, dri2_surf);
++   if (err) {
++      _eglError(EGL_BAD_ALLOC, "failed to create swap thread");
++      goto err_destroy_surface;
++   }
+ 
+    return surf;
+ 
+@@ -808,8 +1175,27 @@ dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+    /* If there's a current surface then a page flip has been performed, so make
+     * sure we process the flip event.
+     */
+-   if (dri2_surf->current)
+-      flip_process(dri2_dpy->fd);
++   if (dri2_surf->swap_queue_processor) {
++      pthread_mutex_lock(&dri2_surf->mutex);
++
++      /* Wait for any outstanding swaps to complete */
++      while (dri2_surf->swap_queue_idx_head != dri2_surf->swap_queue_idx_tail)
++         pthread_cond_wait(&dri2_surf->swap_unlock_buffer_cond,
++                           &dri2_surf->mutex);
++
++      pthread_mutex_unlock(&dri2_surf->mutex);
++      pthread_cancel(dri2_surf->swap_queue_processor);
++      pthread_join(dri2_surf->swap_queue_processor, NULL);
++   }
++
++   if (dri2_surf->cond_init)
++      pthread_cond_destroy(&dri2_surf->swap_queue_cond);
++
++   if (dri2_surf->cond_init_unlock_buffer)
++      pthread_cond_destroy(&dri2_surf->swap_unlock_buffer_cond);
++
++   if (dri2_surf->mutex_init)
++      pthread_mutex_destroy(&dri2_surf->mutex);
+ 
+    if (dri2_surf->front)
+       dri2_dpy->image->destroyImage(dri2_surf->front);
+@@ -837,9 +1223,7 @@ dri2_null_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+-   bool *plocked = NULL;
+-   uint32_t flags;
+-   int err;
++   uint32_t back_id;
+ 
+    if (dri2_surf->base.Type != EGL_WINDOW_BIT)
+       return EGL_TRUE;
+@@ -858,34 +1242,13 @@ dri2_null_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
+    dri2_flush_drawable_for_swapbuffers(disp, draw);
+    dri2_dpy->flush->invalidate(dri2_surf->dri_drawable);
+ 
+-   if (dri2_surf->current) {
+-      /* Wait for the previous flip to happen so the next one can be queued */
+-      if (!flip_process(dri2_dpy->fd)) {
+-         _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_null_swap_buffers process");
+-         return EGL_FALSE;
+-      }
++   back_id = get_back_buffer_id(dri2_surf);
++   assert(dri2_surf->back == &dri2_surf->color_buffers[back_id]);
+ 
+-      plocked = &dri2_surf->current->locked;
+-   }
+-
+-   flags = DRM_MODE_PAGE_FLIP_EVENT;
+-   if (draw->SwapInterval == 0)
+-      flags |= DRM_MODE_PAGE_FLIP_ASYNC;
+-
+-   do {
+-      err = display_output_flip(dri2_dpy->fd, &dri2_dpy->output,
+-                                dri2_surf->back->fb_id, flags, plocked);
+-   } while (err == -EBUSY);
+-
+-   if (err) {
+-      _eglError(EGL_BAD_NATIVE_WINDOW, "dri2_null_swap_buffers flip");
+-      dri2_surf->back->locked = false;
+-      dri2_surf->back = NULL;
+-      return EGL_FALSE;
+-   }
++   swap_enqueue_data(dri2_surf, back_id, draw->SwapInterval);
+ 
++   /* This back buffer is tracked in the swap_data, safe to drop it now */
+    dri2_surf->back->age = 1;
+-   dri2_surf->current = dri2_surf->back;
+    dri2_surf->back = NULL;
+ 
+    return EGL_TRUE;
+@@ -904,11 +1267,20 @@ dri2_null_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surface)
+    return dri2_surf->back->age;
+ }
+ 
++static EGLBoolean
++dri2_null_swap_interval(_EGLDisplay *dpy, _EGLSurface *draw, EGLint interval)
++{
++   _eglLog(_EGL_DEBUG, "DRI2: set swap interval to %d", interval);
++   draw->SwapInterval = interval;
++   return EGL_TRUE;
++}
++
+ static struct dri2_egl_display_vtbl dri2_null_display_vtbl = {
+    .create_window_surface = dri2_null_create_window_surface,
+    .create_pbuffer_surface = dri2_null_create_pbuffer_surface,
+    .destroy_surface = dri2_null_destroy_surface,
+    .create_image = dri2_create_image_khr,
++   .swap_interval = dri2_null_swap_interval,
+    .swap_buffers = dri2_null_swap_buffers,
+    .query_buffer_age = dri2_null_query_buffer_age,
+    .get_dri_drawable = dri2_surface_get_dri_drawable,
+@@ -1062,12 +1434,35 @@ dri2_null_add_configs_for_formats(_EGLDisplay *disp)
+ 
+    return count != 0;
+ }
++static void
++dri2_null_setup_swap_interval(_EGLDisplay *disp)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   const int swap_max_interval = 10; /* Arbitrary max value */
++   uint64_t value;
++   int err;
++
++   dri2_setup_swap_interval(disp, swap_max_interval);
++
++   err = drmGetCap(dri2_dpy->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
++   if (err || value == 0)
++      dri2_dpy->min_swap_interval = 1;
++
++   /**
++    * drm/atomic: Reject FLIP_ASYNC unconditionally
++    * upstream f2cbda2dba11de868759cae9c0d2bab5b8411406
++    *
++    * Only allow swap interval 0 for legacy DRM/KMS and let
++    * the app be aware that swap interval is clamped to 1.
++    */
++   if (dri2_dpy->atomic_enabled)
++      dri2_dpy->min_swap_interval = 1;
++}
+ 
+ EGLBoolean
+ dri2_initialize_null(_EGLDisplay *disp)
+ {
+    struct dri2_egl_display *dri2_dpy;
+-   uint64_t value;
+    int err;
+ 
+    dri2_dpy = calloc(1, sizeof(*dri2_dpy));
+@@ -1114,11 +1509,7 @@ dri2_initialize_null(_EGLDisplay *disp)
+    }
+ 
+    dri2_setup_screen(disp);
+-   dri2_setup_swap_interval(disp, 1);
+-
+-   err = drmGetCap(dri2_dpy->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+-   if (err || value == 0)
+-      dri2_dpy->min_swap_interval = 1;
++   dri2_null_setup_swap_interval(disp);
+ 
+    if (dri2_dpy->image->base.version < NULL_IMAGE_EXTENSION_VERSION_MIN) {
+       _eglError(EGL_NOT_INITIALIZED, "image extension version too old");

+ 212 - 0
recipes-graphics/mesa/mesa-pvr/0041-null_platform-add-support-for-explicit-synchronisati.patch

@@ -0,0 +1,212 @@
+From ced3b5d803cd044526672c8458029b5a6519b86a Mon Sep 17 00:00:00 2001
+From: Silvestrs Timofejevs <silvestrs.timofejevs@imgtec.com>
+Date: Mon, 2 Sep 2019 09:32:01 +0100
+Subject: [PATCH] null_platform: add support for explicit synchronisation
+
+This change adds support for the 'in' fence, the 'out' fence will
+potentially be added in subsequent optimisation changes.
+
+Explicit synchronisation KMS 'in' and 'out' properties have been first
+added in Linux kernel 4.10 as an additional feature to the atomic mode
+setting.
+
+9626014258a5957ff120b3987ee72decdbe0c798 - 'in' property
+beaf5af48034c9e2ebb8b2b1fb12dc4d8aeba99e - 'out' property
+
+Unlike many other features - it doesn't have an in kernel capability
+flag, so the support for it can be tested in the following way:
+KMS creates an additional set of properties when the version of kernel
+is new enough to support it ('in' and 'out' fence properties). When
+the user-space requests all the properties from the kernel via
+drmModeObjectGetProperties it is possible to determine whether the
+required property is supported by the kernel or not.
+
+The explicit synchronisation is used when available, otherwise the
+implicit sync model is used instead. If explicit synchronisation is
+available but 'in' fence failed to attach in KMS, the system falls
+back to the implicit synchronisation model.
+
+egl_dri2.c already creates the GPU 'out' fence that is then used by
+the null_platform as KMS 'in' fence. null_platform takes owenership of
+this fence fd during the swap buffers operation, at which point the
+fence fd is no longer available to be used outside of null_platform.
+
+EGL_LOG_LEVEL=debug should give some useful information in case of
+explicit synchronisation failure. As mentioned above, this failure
+will result in implicit synchronisation being used instead.
+
+Change-Id: Ib9c4a0bc3ea1b21192ee37909d7580d6b7b366ec
+
+---
+ src/egl/drivers/dri2/egl_dri2.h      |  2 +
+ src/egl/drivers/dri2/platform_null.c | 77 +++++++++++++++++++++++++++-
+ 2 files changed, 77 insertions(+), 2 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index 64d60ed..eb6e455 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -106,6 +106,7 @@ struct wl_buffer;
+ #ifdef HAVE_NULL_PLATFORM
+ struct display_output {
+    bool                   in_use;
++   bool                   in_fence_supported;
+    uint32_t               connector_id;
+    drmModePropertyRes   **connector_prop_res;
+    uint32_t               crtc_id;
+@@ -338,6 +339,7 @@ struct swap_queue_elem
+    uint32_t             swap_interval;
+    uint32_t             back_id;
+    uint32_t             fb_id;
++   int                  kms_in_fence_fd;
+ };
+ 
+ enum {
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index 5b7c1ec..d36dc0c 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -401,6 +401,9 @@ display_output_atomic_init(int fd, struct display_output *output)
+    if (!output->atomic_state)
+       goto err_destroy_mode_prop_blob;
+ 
++   if (property_id_get_for_name(plane_prop_res, "IN_FENCE_FD"))
++      output->in_fence_supported = true;
++
+    output->connector_prop_res = connector_prop_res;
+    output->crtc_prop_res = crtc_prop_res;
+    output->plane_prop_res = plane_prop_res;
+@@ -418,6 +421,53 @@ err_free_connector_prop_res:
+    return false;
+ }
+ 
++static void
++display_output_atomic_add_in_fence(struct display_output *output,
++                                   int kms_in_fence_fd)
++{
++   const struct object_property obj_sync_props[] = {
++      object_property_set_named(output, plane, "IN_FENCE_FD", kms_in_fence_fd),
++   };
++   int err;
++
++   /* Explicit synchronisation is not being used */
++   if (kms_in_fence_fd < 0)
++      return;
++
++   err = atomic_state_add_object_properties(output->atomic_state,
++                                            obj_sync_props,
++                                            ARRAY_SIZE(obj_sync_props));
++   if (err)
++      _eglLog(_EGL_DEBUG, "%s: failed to add props ERR = %d", __func__, err);
++}
++
++static void
++atomic_claim_in_fence_fd(struct dri2_egl_surface *dri2_surf,
++                         struct swap_queue_elem *swap_data)
++{
++   /* Explicit synchronisation is not being used */
++   if (!dri2_surf->enable_out_fence)
++      return;
++
++   if (dri2_surf->out_fence_fd < 0) {
++      _eglLog(_EGL_DEBUG, "%s: missing 'in' fence", __func__);
++      return;
++   }
++
++   /* Take ownership of the fd */
++   swap_data->kms_in_fence_fd = dri2_surf->out_fence_fd;
++   dri2_surf->out_fence_fd = -1;
++}
++
++static void
++atomic_relinquish_in_fence_fd(struct dri2_egl_surface *dri2_surf,
++                              struct swap_queue_elem *swap_data)
++{
++   /* KMS is now in control of the fence (post drmModeAtomicCommit) */
++   close(swap_data->kms_in_fence_fd);
++   swap_data->kms_in_fence_fd = -1;
++}
++
+ static int
+ display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id,
+                            uint32_t flags, void *flip_data)
+@@ -425,6 +475,8 @@ display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id
+    const struct object_property obj_props[] = {
+       object_property_set_named(output, plane, "FB_ID", fb_id),
+    };
++   struct dri2_egl_surface *dri2_surf = flip_data;
++   struct swap_queue_elem *swap_data = dri2_surf->swap_data;
+    int err;
+ 
+    /* Reset atomic state */
+@@ -435,13 +487,19 @@ display_output_atomic_flip(int fd, struct display_output *output, uint32_t fb_id
+    if (err)
+       return err;
+ 
++   display_output_atomic_add_in_fence(output, swap_data->kms_in_fence_fd);
++
+    /*
+     * Don't block - like drmModePageFlip, drmModeAtomicCommit will return
+     * -EBUSY if the commit can't be queued in the kernel.
+     */
+    flags |= DRM_MODE_ATOMIC_NONBLOCK;
+ 
+-   return drmModeAtomicCommit(fd, output->atomic_state, flags, flip_data);
++   err = drmModeAtomicCommit(fd, output->atomic_state, flags, flip_data);
++
++   atomic_relinquish_in_fence_fd(dri2_surf, swap_data);
++
++   return err;
+ }
+ 
+ static int
+@@ -489,6 +547,8 @@ swap_enqueue_data(struct dri2_egl_surface *dri2_surf, uint32_t back_id,
+    swap_data->fb_id = dri2_surf->back->fb_id;
+    swap_data->back_id = back_id;
+ 
++   atomic_claim_in_fence_fd(dri2_surf, swap_data);
++
+    dri2_surf->swap_queue_idx_tail++;
+    dri2_surf->swap_queue_idx_tail %= ARRAY_SIZE(dri2_surf->swap_queue);
+ 
+@@ -1025,11 +1085,21 @@ err_unlock:
+    return false;
+ }
+ 
++static void surface_swap_queue_init(struct dri2_egl_surface *dri2_surf)
++{
++   struct swap_queue_elem *swap_queue = &dri2_surf->swap_queue[0];
++   const int num_el = ARRAY_SIZE(dri2_surf->swap_queue);
++
++   for (int i = 0; i < num_el; i++)
++      swap_queue[i].kms_in_fence_fd = -1;
++}
++
+ static _EGLSurface *
+ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+                const EGLint *attrib_list)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   struct display_output *output = &dri2_dpy->output;
+    struct dri2_egl_config *dri2_config = dri2_egl_config(config);
+    struct dri2_egl_surface *dri2_surf;
+    const __DRIconfig *dri_config;
+@@ -1043,7 +1113,8 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+    }
+    surf = &dri2_surf->base;
+ 
+-   if (!dri2_init_surface(surf, disp, type, config, attrib_list, false, NULL))
++   if (!dri2_init_surface(surf, disp, type, config, attrib_list,
++                          output->in_fence_supported, NULL))
+       goto err_free_surface;
+ 
+    dri_config = dri2_get_dri_config(dri2_config, type,
+@@ -1066,6 +1137,8 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+ 
+    dri2_surf->format = dri2_null_formats[format_idx].dri_image_format;
+ 
++   surface_swap_queue_init(dri2_surf);
++
+    return surf;
+ 
+ err_free_surface:

+ 399 - 0
recipes-graphics/mesa/mesa-pvr/0042-egl-null-add-support-for-DRM-image-format-modifiers.patch

@@ -0,0 +1,399 @@
+From dbf265558ceb9688f5ffd626ab8969bd836ca60f Mon Sep 17 00:00:00 2001
+From: Luigi Santivetti <luigi.santivetti@imgtec.com>
+Date: Thu, 26 Sep 2019 13:32:15 +0100
+Subject: [PATCH] egl/null: add support for DRM image format modifiers
+
+This change introduces support for image modifiers to platform_null. In
+order for it to create an image with modifiers, it relies on libdrm to
+iterate all formats with associated modifiers supported by the display
+for the primary drm plane in use.
+
+drmModeFormatModifierBlobIterNext() is added to the DRM api in a different
+change and it is not upstream at present.
+
+Internal notes:
+ [1] IN_FORMATS blobs are available since kernel 4.14:
+  - db1689aa61bd1efb5ce9b896e7aa860a85b7f1b6
+  - https://patchwork.freedesktop.org/patch/168543
+
+ [2] the dri image->base.version threshold is 14.
+  - Unlike for platform_wayland, where no details were found regarding
+  why it's using 15
+  - dri_interface.h makes createImageWithModifiers available since
+  version 14
+  - dri/gbm_dri.c as an example checks for minimum version 14.
+
+Change-Id: I0f7b030f6e1943690692674bf18daabfc153208a
+Signed-off-by: Luigi Santivetti <luigi.santivetti@imgtec.com>
+
+---
+ meson.build                          |   5 +
+ src/egl/drivers/dri2/egl_dri2.h      |   3 +
+ src/egl/drivers/dri2/platform_null.c | 221 +++++++++++++++++++++++----
+ 3 files changed, 203 insertions(+), 26 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index 21d93d3..ca89b8a 100644
+--- a/meson.build
++++ b/meson.build
+@@ -1581,6 +1581,11 @@ if with_gallium_etnaviv
+   _drm_ver = '2.4.89'
+ endif
+ 
++# platform_null relies on DRM format-modifier iterators available since 2.4.108
++if with_platform_null
++  _drm_ver = '2.4.108'
++endif
++
+ # Loop over the enables versions and get the highest libdrm requirement for all
+ # active drivers.
+ _drm_blame = ''
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index eb6e455..32ad720 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -117,6 +117,8 @@ struct display_output {
+    uint32_t               mode_blob_id;
+    unsigned               formats;
+    drmModeAtomicReq      *atomic_state;
++   uint32_t               in_formats_id;
++   struct u_vector        modifiers;
+ };
+ #endif
+ 
+@@ -280,6 +282,7 @@ struct dri2_egl_display
+ 
+ #ifdef HAVE_NULL_PLATFORM
+    bool                      atomic_enabled;
++   bool                      in_formats_enabled;
+    struct display_output     output;
+ #endif
+ 
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index d36dc0c..2c79199 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -40,6 +40,7 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
++#include <util/u_vector.h>
+ #include <xf86drm.h>
+ 
+ #include "egl_dri2.h"
+@@ -156,6 +157,18 @@ format_idx_get_from_drm_format(uint32_t drm_format)
+    return -1;
+ }
+ 
++static inline uint32_t
++blob_id_from_property_value(uint64_t prop_value)
++{
++   /* The KMS properties documetation, 01.org/linuxgraphics, says:
++    *
++    *     For all property types except blob properties the value is a 64-bit
++    *     unsigned integer.
++    */
++   assert(!(prop_value >> 32));
++   return (uint32_t) prop_value;
++}
++
+ static int
+ atomic_state_add_object_properties(drmModeAtomicReq *atomic_state,
+                                    const struct object_property *props,
+@@ -645,7 +658,66 @@ drm_event_process(int fd)
+ }
+ 
+ static bool
+-display_output_init(int fd, struct display_output *output, bool use_atomic)
++plane_init_in_formats(int fd, drmModePlane *plane, struct u_vector *modifiers,
++                      uint32_t *in_formats_id_out, unsigned *formats_out)
++{
++   uint32_t blob_id, prev_fmt = DRM_FORMAT_INVALID, count_formats = 0;
++   drmModeFormatModifierIterator drm_iter = {0};
++   drmModePropertyBlobRes *blob;
++   uint64_t prop_value;
++   int idx, err;
++
++   assert(plane && in_formats_id_out && formats_out);
++
++   err = !object_property_value_for_name(fd, plane->plane_id,
++                                         DRM_MODE_OBJECT_PLANE,
++                                         "IN_FORMATS", &prop_value);
++   if (err)
++      return false;
++
++   blob_id = blob_id_from_property_value(prop_value);
++   blob = drmModeGetPropertyBlob(fd, blob_id);
++
++   while (drmModeFormatModifierBlobIterNext(blob, &drm_iter)) {
++      if (drm_iter.fmt != prev_fmt) {
++         prev_fmt = drm_iter.fmt;
++         count_formats++;
++
++         idx = format_idx_get_from_drm_format(drm_iter.fmt);
++         if (idx < 0)
++            continue;
++
++         *formats_out |= (1 << idx);
++      }
++   }
++
++   drmModeFreePropertyBlob(blob);
++
++   if (!count_formats) {
++      /* None of the formats in the IN_FORMATS blob has associated modifiers */
++      _eglLog(_EGL_WARNING, "no format-modifiers found in IN_FORMATS");
++      return false;
++   }
++
++   if (plane->count_formats != count_formats)
++      /* Only some of the formats in the IN_FORMATS blob have associated modifiers,
++       * try to use this subset.
++       */
++      _eglLog(_EGL_WARNING, "discarding formats without modifiers");
++
++   /* Allocate space for modifiers, if ENOMEM fallback to plane formats */
++   if (!u_vector_init(modifiers, sizeof(uint64_t), 64)) {
++     _eglLog(_EGL_WARNING, "failed to allocate modifiers");
++     return false;
++   }
++
++   *in_formats_id_out = blob_id;
++   return true;
++}
++
++static bool
++display_output_init(int fd, struct display_output *output, bool use_atomic,
++                    bool prefer_in_formats, bool *in_formats_enabled_out)
+ {
+    drmModeRes *resources;
+    drmModeConnector *connector;
+@@ -674,16 +746,34 @@ display_output_init(int fd, struct display_output *output, bool use_atomic)
+       goto err_free_plane;
+    output->mode = connector->modes[mode_idx];
+ 
+-   /* Record the display supported formats */
+-   for (unsigned i = 0; i < plane->count_formats; i++) {
+-      int format_idx;
++   assert(in_formats_enabled_out && !(*in_formats_enabled_out));
+ 
+-      format_idx = format_idx_get_from_drm_format(plane->formats[i]);
+-      if (format_idx == -1)
+-         continue;
++   /* Track display supported formats. Look them up from IN_FORMATS blobs
++    * if they are available, otherwise use plane formats.
++    */
++   if (prefer_in_formats)
++      *in_formats_enabled_out = plane_init_in_formats(fd, plane,
++                                                      &output->modifiers,
++                                                      &output->in_formats_id,
++                                                      &output->formats);
+ 
+-      output->formats |= (1 << format_idx);
++   if (!*in_formats_enabled_out) {
++      _eglLog(_EGL_WARNING, "fallback to plane formats");
++
++      for (unsigned i = 0; i < plane->count_formats; i++) {
++         int format_idx;
++
++         format_idx = format_idx_get_from_drm_format(plane->formats[i]);
++         if (format_idx == -1)
++            continue;
++
++         output->formats |= (1 << format_idx);
++      }
+    }
++
++   /* At this point we can only shut down if the look up failed and
++    * it is safe to pass NULL to drmModeFreeFormats().
++    */
+    if (!output->formats)
+       goto err_free_plane;
+ 
+@@ -983,10 +1073,12 @@ static bool
+ add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+                      uint32_t *fb_id_out)
+ {
++   uint64_t modifiers[4] = {0};
+    uint32_t handles[4] = {0};
+    uint32_t pitches[4] = {0};
+    uint32_t offsets[4] = {0};
+-   int handle, stride, width, height, format;
++   uint32_t flags = 0;
++   int handle, stride, width, height, format, l_mod, h_mod;
+    int format_idx;
+ 
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HANDLE, &handle);
+@@ -1001,9 +1093,47 @@ add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+    format_idx = format_idx_get_from_dri_image_format(format);
+    assert(format_idx != -1);
+ 
+-   return !drmModeAddFB2(dri2_dpy->fd, width, height,
+-                         dri2_null_formats[format_idx].drm_format,
+-                         handles, pitches, offsets, fb_id_out, 0);
++   if (dri2_dpy->in_formats_enabled) {
++      dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &h_mod);
++      dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &l_mod);
++
++      modifiers[0] = combine_u32_into_u64((uint32_t) h_mod, (uint32_t) l_mod);
++      flags |= DRM_MODE_FB_MODIFIERS;
++   }
++
++   return !drmModeAddFB2WithModifiers(dri2_dpy->fd, width, height,
++                                      dri2_null_formats[format_idx].drm_format,
++                                      handles, pitches, offsets, modifiers,
++                                      fb_id_out, flags);
++}
++
++static __DRIimage *
++create_image(struct dri2_egl_surface *dri2_surf, uint32_t flags)
++{
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++   uint32_t count_modifiers;
++   uint64_t *modifiers;
++
++   if (dri2_dpy->in_formats_enabled) {
++      count_modifiers = u_vector_length(&dri2_dpy->output.modifiers);
++      modifiers = u_vector_tail(&dri2_dpy->output.modifiers);
++
++      return dri2_dpy->image->createImageWithModifiers(dri2_dpy->dri_screen,
++                                                       dri2_surf->base.Width,
++                                                       dri2_surf->base.Height,
++                                                       dri2_surf->format,
++                                                       modifiers,
++                                                       count_modifiers,
++                                                       NULL);
++   }
++
++   return dri2_dpy->image->createImage(dri2_dpy->dri_screen,
++                                       dri2_surf->base.Width,
++                                       dri2_surf->base.Height,
++                                       dri2_surf->format,
++                                       flags,
++                                       NULL);
+ }
+ 
+ static bool
+@@ -1016,12 +1146,7 @@ get_front_bo(struct dri2_egl_surface *dri2_surf)
+    if (dri2_surf->base.Type == EGL_WINDOW_BIT)
+       use |= __DRI_IMAGE_USE_SCANOUT;
+ 
+-   dri2_surf->front = dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+-                                                   dri2_surf->base.Width,
+-                                                   dri2_surf->base.Height,
+-                                                   dri2_surf->format,
+-                                                   use,
+-                                                   NULL);
++   dri2_surf->front = create_image(dri2_surf, use);
+    if (!dri2_surf->front)
+       return false;
+ 
+@@ -1058,13 +1183,8 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
+    }
+ 
+    if (!dri2_surf->back->dri_image) {
+-      dri2_surf->back->dri_image =
+-         dri2_dpy->image->createImage(dri2_dpy->dri_screen,
+-                                      dri2_surf->base.Width,
+-                                      dri2_surf->base.Height,
+-                                      dri2_surf->format,
+-                                      __DRI_IMAGE_USE_SCANOUT,
+-                                      NULL);
++      dri2_surf->back->dri_image = create_image(dri2_surf,
++                                                __DRI_IMAGE_USE_SCANOUT);
+       if (!dri2_surf->back->dri_image)
+          goto err_unlock;
+    }
+@@ -1094,6 +1214,30 @@ static void surface_swap_queue_init(struct dri2_egl_surface *dri2_surf)
+       swap_queue[i].kms_in_fence_fd = -1;
+ }
+ 
++static bool
++in_formats_get_modifiers(const int fd, const uint32_t in_formats_id,
++                         const int drm_format, struct u_vector *modifiers)
++{
++   drmModeFormatModifierIterator drm_iter = {0};
++   drmModePropertyBlobRes *blob;
++   uint64_t *mod = NULL;
++
++   blob = drmModeGetPropertyBlob(fd, in_formats_id);
++
++   while (drmModeFormatModifierBlobIterNext(blob, &drm_iter)) {
++      if (drm_iter.fmt == drm_format) {
++         assert(drm_iter.mod != DRM_FORMAT_MOD_INVALID);
++
++         mod = u_vector_add(modifiers);
++         *mod = drm_iter.mod;
++      }
++   }
++
++   drmModeFreePropertyBlob(blob);
++
++   return mod != NULL;
++}
++
+ static _EGLSurface *
+ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+                const EGLint *attrib_list)
+@@ -1105,6 +1249,7 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+    const __DRIconfig *dri_config;
+    _EGLSurface *surf;
+    int format_idx;
++   bool ret;
+ 
+    dri2_surf = calloc(1, sizeof(*dri2_surf));
+    if (!dri2_surf) {
+@@ -1137,6 +1282,15 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+ 
+    dri2_surf->format = dri2_null_formats[format_idx].dri_image_format;
+ 
++   if (dri2_dpy->in_formats_enabled) {
++      ret = in_formats_get_modifiers(dri2_dpy->fd,
++                                     dri2_dpy->output.in_formats_id,
++                                     dri2_null_formats[format_idx].drm_format,
++                                     &dri2_dpy->output.modifiers);
++      if (!ret)
++         goto err_free_surface;
++   }
++
+    surface_swap_queue_init(dri2_surf);
+ 
+    return surf;
+@@ -1536,6 +1690,8 @@ EGLBoolean
+ dri2_initialize_null(_EGLDisplay *disp)
+ {
+    struct dri2_egl_display *dri2_dpy;
++   bool prefer_in_formats = false;
++   uint64_t value;
+    int err;
+ 
+    dri2_dpy = calloc(1, sizeof(*dri2_dpy));
+@@ -1589,8 +1745,19 @@ dri2_initialize_null(_EGLDisplay *disp)
+       goto cleanup;
+    }
+ 
++   err = drmGetCap(dri2_dpy->fd_dpy, DRM_CAP_ADDFB2_MODIFIERS, &value);
++   if (!err && value) {
++      /* in_formats could be supported by the platform, however not being
++       * actually enabled, i.e. in_formats init can still fail.
++       */
++      prefer_in_formats = dri2_dpy->image->base.version >= 14 &&
++         dri2_dpy->image->createImageWithModifiers;
++   }
++
+    if (!display_output_init(dri2_dpy->fd, &dri2_dpy->output,
+-                            dri2_dpy->atomic_enabled)) {
++                            dri2_dpy->atomic_enabled,
++                            prefer_in_formats,
++                            &dri2_dpy->in_formats_enabled)) {
+       _eglError(EGL_NOT_INITIALIZED, "failed to create output");
+       goto cleanup;
+    }
+@@ -1640,4 +1807,6 @@ dri2_teardown_null(struct dri2_egl_display *dri2_dpy)
+          drmModeFreeProperty(dri2_dpy->output.connector_prop_res[i]);
+       free(dri2_dpy->output.connector_prop_res);
+    }
++
++   u_vector_finish(&dri2_dpy->output.modifiers);
+ }

+ 201 - 0
recipes-graphics/mesa/mesa-pvr/0043-egl-query-the-supported-ES2-context-version.patch

@@ -0,0 +1,201 @@
+From 1ae221afb2c03d2b3cd0a607dc6646f33c07341c Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 10 Feb 2020 09:23:03 +0000
+Subject: [PATCH] egl: query the supported ES2 context version
+
+For OpenGL ES contexts, the EGL specification states that querying
+EGL_CONTEXT_CLIENT_VERSION with eglQueryContext may return a version
+that differs from that specified at context creation time. For example,
+if an OpenGL ES2 context is specified at context creation time, an
+OpenGL ES3 context may be created, and so "3" should be returned
+when EGL_CONTEXT_CLIENT_VERSION is queried.
+
+A new EGL driver API function has been added,
+QueryContextClientVersion, that is called when the context client
+version is queried, allowing EGL drivers to override the default
+value (i.e. the version specified at context creation time). If the
+function returns zero, the default is used.
+
+For DRI drivers, QueryContextClientVersion returns zero for all API
+contexts other than OpenGL ES2. For OpenGL ES2, the supported context
+client version is queried via the Query Renderer driver extension, using
+integer query __DRI2_RENDERER_OPENGL_ES2_CONTEXT_CLIENT_VERSION_IMG. If
+the query isn't supported, or the query returns zero, zero is returned
+to the caller.
+
+IMG NOTE: In order to avoid potential name and value clashes, "_IMG"
+has been added to the end of the new query name, this should be removed
+if an attempt is made to push this patch upstream. The value of the new
+query should be adjusted to be the next one in sequence, rather than the
+large value it currently has.
+
+---
+ include/GL/internal/dri_interface.h |  2 ++
+ src/egl/drivers/dri2/egl_dri2.c     | 21 +++++++++++++++++++++
+ src/egl/drivers/haiku/egl_haiku.cpp |  9 +++++++++
+ src/egl/main/eglapi.c               |  2 +-
+ src/egl/main/eglcontext.c           | 16 ++++++++++++++--
+ src/egl/main/eglcontext.h           |  3 ++-
+ src/egl/main/egldriver.h            |  1 +
+ 7 files changed, 50 insertions(+), 4 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 6490f6e..9c7bcac 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -2052,6 +2052,8 @@ typedef struct __DRIDriverVtableExtensionRec {
+ 
+ #define __DRI2_RENDERER_HAS_PROTECTED_CONTENT                 0x000e
+ 
++#define __DRI2_RENDERER_OPENGL_ES2_CONTEXT_CLIENT_VERSION_IMG 0x7001
++
+ typedef struct __DRI2rendererQueryExtensionRec __DRI2rendererQueryExtension;
+ struct __DRI2rendererQueryExtensionRec {
+    __DRIextension base;
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index 6b26ff9..c4a49ca 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -2055,6 +2055,26 @@ dri2_make_current(_EGLDisplay *disp, _EGLSurface *dsurf,
+    return EGL_TRUE;
+ }
+ 
++static EGLint
++dri2_query_context_client_version(_EGLDisplay *disp, _EGLContext *ctx)
++{
++   struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   switch (dri2_ctx->base.ClientAPI) {
++   case EGL_OPENGL_ES_API:
++      switch (dri2_ctx->base.ClientMajorVersion) {
++      case 2:
++         return dri2_renderer_query_integer(dri2_dpy,
++                   __DRI2_RENDERER_OPENGL_ES2_CONTEXT_CLIENT_VERSION_IMG);
++      default:
++         return 0;
++      }
++   default:
++      return 0;
++   }
++}
++
+ __DRIdrawable *
+ dri2_surface_get_dri_drawable(_EGLSurface *surf)
+ {
+@@ -4336,6 +4356,7 @@ const _EGLDriver _eglDriver = {
+    .CreateContext = dri2_create_context,
+    .DestroyContext = dri2_destroy_context,
+    .MakeCurrent = dri2_make_current,
++   .QueryContextClientVersion = dri2_query_context_client_version,
+    .CreateWindowSurface = dri2_create_window_surface,
+    .CreatePixmapSurface = dri2_create_pixmap_surface,
+    .CreatePbufferSurface = dri2_create_pbuffer_surface,
+diff --git a/src/egl/drivers/haiku/egl_haiku.cpp b/src/egl/drivers/haiku/egl_haiku.cpp
+index 18c73c9..2690a82 100644
+--- a/src/egl/drivers/haiku/egl_haiku.cpp
++++ b/src/egl/drivers/haiku/egl_haiku.cpp
+@@ -297,6 +297,14 @@ haiku_make_current(_EGLDisplay *disp, _EGLSurface *dsurf,
+ }
+ 
+ 
++extern "C"
++EGLint
++haiku_dri2_query_context_client_version(_EGLDisplay *disp, _EGLContext *ctx)
++{
++	// Tell caller to use the default value.
++	return 0;
++}
++
+ extern "C"
+ EGLBoolean
+ haiku_swap_buffers(_EGLDisplay *disp, _EGLSurface *surf)
+@@ -316,6 +324,7 @@ const _EGLDriver _eglDriver = {
+ 	.CreateContext = haiku_create_context,
+ 	.DestroyContext = haiku_destroy_context,
+ 	.MakeCurrent = haiku_make_current,
++	.QueryContextClientVersion = haiku_dri2_query_context_client_version,
+ 	.CreateWindowSurface = haiku_create_window_surface,
+ 	.CreatePixmapSurface = haiku_create_pixmap_surface,
+ 	.CreatePbufferSurface = haiku_create_pbuffer_surface,
+diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c
+index 2d3931d..1cbff96 100644
+--- a/src/egl/main/eglapi.c
++++ b/src/egl/main/eglapi.c
+@@ -948,7 +948,7 @@ eglQueryContext(EGLDisplay dpy, EGLContext ctx,
+ 
+    _EGL_CHECK_CONTEXT(disp, context, EGL_FALSE);
+ 
+-   ret = _eglQueryContext(context, attribute, value);
++   ret = _eglQueryContext(disp, context, attribute, value);
+ 
+    RETURN_EGL_EVAL(disp, ret);
+ }
+diff --git a/src/egl/main/eglcontext.c b/src/egl/main/eglcontext.c
+index 15de7c9..7274d24 100644
+--- a/src/egl/main/eglcontext.c
++++ b/src/egl/main/eglcontext.c
+@@ -35,6 +35,7 @@
+ #include "eglcontext.h"
+ #include "egldisplay.h"
+ #include "eglcurrent.h"
++#include "egldriver.h"
+ #include "eglsurface.h"
+ #include "egllog.h"
+ #include "util/macros.h"
+@@ -670,8 +671,19 @@ _eglQueryContextRenderBuffer(_EGLContext *ctx)
+ }
+ 
+ 
++static EGLint
++_eglQueryContextClientVersion(_EGLDisplay *disp, _EGLContext *ctx)
++{
++   EGLint version;
++
++   version = disp->Driver->QueryContextClientVersion(disp, ctx);
++
++   return (version) ? version : ctx->ClientMajorVersion;
++}
++
+ EGLBoolean
+-_eglQueryContext(_EGLContext *c, EGLint attribute, EGLint *value)
++_eglQueryContext(_EGLDisplay *disp, _EGLContext *c,
++                 EGLint attribute, EGLint *value)
+ {
+    if (!value)
+       return _eglError(EGL_BAD_PARAMETER, "eglQueryContext");
+@@ -688,7 +700,7 @@ _eglQueryContext(_EGLContext *c, EGLint attribute, EGLint *value)
+       *value = c->Config ? c->Config->ConfigID : 0;
+       break;
+    case EGL_CONTEXT_CLIENT_VERSION:
+-      *value = c->ClientMajorVersion;
++      *value = _eglQueryContextClientVersion(disp, c);
+       break;
+    case EGL_CONTEXT_CLIENT_TYPE:
+       *value = c->ClientAPI;
+diff --git a/src/egl/main/eglcontext.h b/src/egl/main/eglcontext.h
+index 06029e8..d890217 100644
+--- a/src/egl/main/eglcontext.h
++++ b/src/egl/main/eglcontext.h
+@@ -74,7 +74,8 @@ _eglInitContext(_EGLContext *ctx, _EGLDisplay *disp,
+ 
+ 
+ extern EGLBoolean
+-_eglQueryContext(_EGLContext *ctx, EGLint attribute, EGLint *value);
++_eglQueryContext(_EGLDisplay *disp, _EGLContext *ctx,
++                 EGLint attribute, EGLint *value);
+ 
+ 
+ extern EGLBoolean
+diff --git a/src/egl/main/egldriver.h b/src/egl/main/egldriver.h
+index 12f9a0a..92af8bd 100644
+--- a/src/egl/main/egldriver.h
++++ b/src/egl/main/egldriver.h
+@@ -97,6 +97,7 @@ struct _egl_driver
+    EGLBoolean (*MakeCurrent)(_EGLDisplay *disp,
+                              _EGLSurface *draw, _EGLSurface *read,
+                              _EGLContext *ctx);
++   EGLint (*QueryContextClientVersion)(_EGLDisplay *disp, _EGLContext *ctx);
+ 
+    /* surface funcs */
+    _EGLSurface *(*CreateWindowSurface)(_EGLDisplay *disp, _EGLConfig *config,

+ 142 - 0
recipes-graphics/mesa/mesa-pvr/0044-meson-allow-libGL-to-be-built-without-GLX.patch

@@ -0,0 +1,142 @@
+From 01c91ceb04478e567cae1685d3fe44faefdfecb3 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 11 Jun 2020 12:29:51 +0100
+Subject: [PATCH] meson: allow libGL to be built without GLX
+
+If Meson is run with option "glx" set to "null", build the
+OpenGL library without GLX.
+
+The "eglBindAPI workaround for dEQP bug" change to eglcurrent.h
+(commit 2d46c91040aeb8ebad486214159c34417fbc87db) has been
+modified to use a new EGL_WITH_OPENGL define, which indicates
+whether OpneGL is present or not. This allows EGL to be used
+with OpenGL on platforms other than X11.
+
+---
+ meson.build               |  8 ++++++--
+ meson_options.txt         |  2 +-
+ src/egl/main/eglcurrent.h |  7 +++----
+ src/glx/meson.build       | 20 ++++++++++++++++----
+ src/meson.build           |  2 +-
+ 5 files changed, 27 insertions(+), 12 deletions(-)
+
+diff --git a/meson.build b/meson.build
+index ca89b8a..c0b6264 100644
+--- a/meson.build
++++ b/meson.build
+@@ -485,6 +485,10 @@ if with_egl and not _platforms.contains(egl_native_platform)
+   error('-Degl-native-platform does not specify an enabled platform')
+ endif
+ 
++if with_egl and with_opengl and with_glx != 'disabled'
++   pre_args += '-DEGL_WITH_OPENGL'
++endif
++
+ # Android uses emutls for versions <= P/28. For USE_ELF_TLS we need ELF TLS.
+ use_elf_tls = false
+ if (not ['freebsd', 'openbsd', 'haiku'].contains(host_machine.system()) and
+@@ -502,7 +506,7 @@ if (not ['freebsd', 'openbsd', 'haiku'].contains(host_machine.system()) and
+   endif
+ endif
+ 
+-if with_glx != 'disabled'
++if with_glx != 'disabled' and with_glx != 'null'
+   if not (with_platform_x11 and with_any_opengl)
+     error('Cannot build GLX support without X11 platform support and at least one OpenGL API')
+   elif with_glx == 'gallium-xlib'
+@@ -564,7 +568,7 @@ if with_any_vk and (with_platform_x11 and not with_dri3)
+   error('Vulkan drivers require dri3 for X11 support')
+ endif
+ if with_dri
+-  if with_glx == 'disabled' and not with_egl and not with_gbm
++  if (with_glx == 'disabled' or with_glx == 'null') and not with_egl and not with_gbm
+     error('building dri drivers require at least one windowing system')
+   endif
+ endif
+diff --git a/meson_options.txt b/meson_options.txt
+index d90a25f..d36e714 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -290,7 +290,7 @@ option(
+   'glx',
+   type : 'combo',
+   value : 'auto',
+-  choices : ['auto', 'disabled', 'dri', 'xlib', 'gallium-xlib'],
++  choices : ['auto', 'disabled', 'dri', 'xlib', 'gallium-xlib', 'null'],
+   description : 'Build support for GLX platform'
+ )
+ option(
+diff --git a/src/egl/main/eglcurrent.h b/src/egl/main/eglcurrent.h
+index 3257097..dcc418a 100644
+--- a/src/egl/main/eglcurrent.h
++++ b/src/egl/main/eglcurrent.h
+@@ -71,11 +71,10 @@ struct _egl_thread_info
+ static inline EGLBoolean
+ _eglIsApiValid(EGLenum api)
+ {
+-#ifndef HAVE_X11_PLATFORM
+-   /* OpenGL is not a valid/supported API on Android */
+-   return api == EGL_OPENGL_ES_API;
+-#else
++#ifdef EGL_WITH_OPENGL
+    return (api == EGL_OPENGL_ES_API || api == EGL_OPENGL_API);
++#else
++   return api == EGL_OPENGL_ES_API;
+ #endif
+ }
+ 
+diff --git a/src/glx/meson.build b/src/glx/meson.build
+index 8f642d5..605a971 100644
+--- a/src/glx/meson.build
++++ b/src/glx/meson.build
+@@ -122,7 +122,15 @@ else
+   )
+ endif
+ 
+-libglx = static_library(
++gl_lib_cargs = [
++  '-D_REENTRANT',
++]
++
++if with_glx == 'null'
++  libglx_link = [libglapi]
++  libglx_link_whole = [libglapi_static]
++else
++  libglx = static_library(
+   'glx',
+   [files_libglx, glx_generated],
+   include_directories : [inc_include, inc_src, inc_mapi, inc_mesa, inc_gallium, inc_gallium_aux, inc_glapi, inc_loader],
+@@ -138,13 +146,17 @@ libglx = static_library(
+     idep_mesautil, idep_xmlconfig,
+     dep_libdrm, dep_dri2proto, dep_glproto, dep_x11, dep_glvnd,
+   ],
+-)
++  )
++
++  libglx_link = [libglapi_static,libglapi]
++  libglx_link_whole = [libglx]
++endif
+ 
+ libgl = shared_library(
+   gl_lib_name,
+   [],
+-  link_with : [libglapi_static, libglapi],
+-  link_whole : libglx,
++  link_with : libglx_link,
++  link_whole : libglx_link_whole,
+   link_args : [ld_args_bsymbolic, ld_args_gc_sections, extra_ld_args_libgl],
+   dependencies : [
+     dep_libdrm, dep_dl, dep_m, dep_thread, dep_x11, dep_xcb_glx, dep_xcb,
+diff --git a/src/meson.build b/src/meson.build
+index c3bab27..f6eede5 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -110,7 +110,7 @@ subdir('loader')
+ if with_platform_haiku
+   subdir('hgl')
+ endif
+-if with_glx == 'dri'
++if with_glx == 'dri' or with_glx == 'null'
+   subdir('glx')
+ endif
+ if with_gbm

+ 36 - 0
recipes-graphics/mesa/mesa-pvr/0045-egl-wayland-process-non-resized-window-movement.patch

@@ -0,0 +1,36 @@
+From b873893f09ebdb8a6816c6f1e9abad953695ab3c Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Tue, 25 Aug 2020 14:12:32 +0100
+Subject: [PATCH] egl/wayland: process non-resized window movement
+
+The dx and dy parameters to the wl_egl_window_resize function were
+not being processed unless the window width or height were being
+changed.
+
+---
+ src/egl/drivers/dri2/platform_wayland.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index 72456d5..06272d4 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -266,6 +266,9 @@ resize_callback(struct wl_egl_window *wl_win, void *data)
+    struct dri2_egl_display *dri2_dpy =
+       dri2_egl_display(dri2_surf->base.Resource.Display);
+ 
++   dri2_surf->dx = wl_win->dx;
++   dri2_surf->dy = wl_win->dy;
++
+    if (dri2_surf->base.Width == wl_win->width &&
+        dri2_surf->base.Height == wl_win->height)
+       return;
+@@ -786,8 +789,6 @@ update_buffers(struct dri2_egl_display *dri2_dpy,
+ 
+       dri2_surf->base.Width  = dri2_surf->wl_win->width;
+       dri2_surf->base.Height = dri2_surf->wl_win->height;
+-      dri2_surf->dx = dri2_surf->wl_win->dx;
+-      dri2_surf->dy = dri2_surf->wl_win->dy;
+    }
+ 
+    if (dri2_surf->wl_win &&

+ 537 - 0
recipes-graphics/mesa/mesa-pvr/0046-Separate-EXT_framebuffer_object-from-ARB-version.patch

@@ -0,0 +1,537 @@
+From 01d9921f0768cf26cf37a402b2d9f92a6dbfe2c0 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 10 Mar 2014 13:43:45 +0000
+Subject: [PATCH] Separate EXT_framebuffer_object from ARB version
+
+This patch separates the EXT_framebuffer_object entry points from the ARB
+equivalents.
+
+Probably not all this separation is necessary; it looks like only
+	BindRenderbuffer
+	BindFramebuffer
+	GetFramebufferAttachmentParameteriv
+take advantage of the split.
+
+Next time this patch is implemented, see if it can be trimmed down to
+just the above functions, as it may be more upstreamable.
+
+We may need to implement the EXT restrictions if we want to upstream.
+
+---
+ src/mapi/glapi/gen/EXT_framebuffer_object.xml | 30 +++----
+ src/mapi/glapi/gen/static_data.py             | 15 ++++
+ src/mesa/main/fbobject.c                      | 83 +++++++++++++++++++
+ src/mesa/main/fbobject.h                      | 54 ++++++++++++
+ src/mesa/main/genmipmap.c                     |  6 ++
+ src/mesa/main/genmipmap.h                     |  3 +
+ 6 files changed, 176 insertions(+), 15 deletions(-)
+
+diff --git a/src/mapi/glapi/gen/EXT_framebuffer_object.xml b/src/mapi/glapi/gen/EXT_framebuffer_object.xml
+index 6c0e54a..957b6a3 100644
+--- a/src/mapi/glapi/gen/EXT_framebuffer_object.xml
++++ b/src/mapi/glapi/gen/EXT_framebuffer_object.xml
+@@ -70,7 +70,7 @@
+     </enum>
+     <enum name="INVALID_FRAMEBUFFER_OPERATION_EXT"        value="0x0506"/>
+ 
+-    <function name="IsRenderbufferEXT" alias="IsRenderbuffer">
++    <function name="IsRenderbufferEXT">
+         <param name="renderbuffer" type="GLuint"/>
+ 	<return type="GLboolean"/>
+     </function>
+@@ -81,30 +81,30 @@
+         <glx rop="4316"/>
+     </function>
+ 
+-    <function name="DeleteRenderbuffersEXT" alias="DeleteRenderbuffers">
++    <function name="DeleteRenderbuffersEXT">
+         <param name="n" type="GLsizei"/>
+         <param name="renderbuffers" type="const GLuint *"/>
+     </function>
+ 
+-    <function name="GenRenderbuffersEXT" alias="GenRenderbuffers">
++    <function name="GenRenderbuffersEXT">
+         <param name="n" type="GLsizei"/>
+         <param name="renderbuffers" type="GLuint *"/>
+     </function>
+ 
+-    <function name="RenderbufferStorageEXT" alias="RenderbufferStorage">
++    <function name="RenderbufferStorageEXT">
+         <param name="target" type="GLenum"/>
+         <param name="internalformat" type="GLenum"/>
+         <param name="width" type="GLsizei"/>
+         <param name="height" type="GLsizei"/>
+     </function>
+ 
+-    <function name="GetRenderbufferParameterivEXT" alias="GetRenderbufferParameteriv">
++    <function name="GetRenderbufferParameterivEXT">
+         <param name="target" type="GLenum"/>
+         <param name="pname" type="GLenum"/>
+         <param name="params" type="GLint *"/>
+     </function>
+ 
+-    <function name="IsFramebufferEXT" alias="IsFramebuffer">
++    <function name="IsFramebufferEXT">
+         <param name="framebuffer" type="GLuint"/>
+ 	<return type="GLboolean"/>
+     </function>
+@@ -115,22 +115,22 @@
+         <glx rop="4319"/>
+     </function>
+ 
+-    <function name="DeleteFramebuffersEXT" alias="DeleteFramebuffers">
++    <function name="DeleteFramebuffersEXT">
+         <param name="n" type="GLsizei"/>
+         <param name="framebuffers" type="const GLuint *"/>
+     </function>
+ 
+-    <function name="GenFramebuffersEXT" alias="GenFramebuffers">
++    <function name="GenFramebuffersEXT">
+         <param name="n" type="GLsizei"/>
+         <param name="framebuffers" type="GLuint *"/>
+     </function>
+ 
+-    <function name="CheckFramebufferStatusEXT" alias="CheckFramebufferStatus">
++    <function name="CheckFramebufferStatusEXT">
+         <param name="target" type="GLenum"/>
+ 	<return type="GLenum"/>
+     </function>
+ 
+-    <function name="FramebufferTexture1DEXT" alias="FramebufferTexture1D">
++    <function name="FramebufferTexture1DEXT">
+         <param name="target" type="GLenum"/>
+         <param name="attachment" type="GLenum"/>
+         <param name="textarget" type="GLenum"/>
+@@ -138,7 +138,7 @@
+         <param name="level" type="GLint"/>
+     </function>
+ 
+-    <function name="FramebufferTexture2DEXT" alias="FramebufferTexture2D">
++    <function name="FramebufferTexture2DEXT">
+         <param name="target" type="GLenum"/>
+         <param name="attachment" type="GLenum"/>
+         <param name="textarget" type="GLenum"/>
+@@ -146,7 +146,7 @@
+         <param name="level" type="GLint"/>
+     </function>
+ 
+-    <function name="FramebufferTexture3DEXT" alias="FramebufferTexture3D">
++    <function name="FramebufferTexture3DEXT">
+         <param name="target" type="GLenum"/>
+         <param name="attachment" type="GLenum"/>
+         <param name="textarget" type="GLenum"/>
+@@ -155,21 +155,21 @@
+         <param name="zoffset" type="GLint"/>
+     </function>
+ 
+-    <function name="FramebufferRenderbufferEXT" alias="FramebufferRenderbuffer">
++    <function name="FramebufferRenderbufferEXT">
+         <param name="target" type="GLenum"/>
+         <param name="attachment" type="GLenum"/>
+         <param name="renderbuffertarget" type="GLenum"/>
+         <param name="renderbuffer" type="GLuint"/>
+     </function>
+ 
+-    <function name="GetFramebufferAttachmentParameterivEXT" alias="GetFramebufferAttachmentParameteriv">
++    <function name="GetFramebufferAttachmentParameterivEXT">
+         <param name="target" type="GLenum"/>
+         <param name="attachment" type="GLenum"/>
+         <param name="pname" type="GLenum"/>
+         <param name="params" type="GLint *"/>
+     </function>
+ 
+-    <function name="GenerateMipmapEXT" alias="GenerateMipmap">
++    <function name="GenerateMipmapEXT">
+         <param name="target" type="GLenum"/>
+     </function>
+ </category>
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index e231c17..974f366 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1706,6 +1706,21 @@ offsets = {
+     "FramebufferTextureMultisampleMultiviewOVR" : 1670,
+     "MultiDrawArraysIndirectEXT" : 1671,
+     "MultiDrawElementsIndirectEXT" : 1672,
++    "IsRenderbufferEXT" : 1673,
++    "DeleteRenderbuffersEXT" : 1674,
++    "GenRenderbuffersEXT" : 1675,
++    "RenderbufferStorageEXT" : 1676,
++    "GetRenderbufferParameterivEXT" : 1677,
++    "IsFramebufferEXT" : 1678,
++    "DeleteFramebuffersEXT" : 1679,
++    "GenFramebuffersEXT" : 1680,
++    "CheckFramebufferStatusEXT" : 1681,
++    "FramebufferTexture1DEXT" : 1682,
++    "FramebufferTexture2DEXT" : 1683,
++    "FramebufferTexture3DEXT" : 1684,
++    "FramebufferRenderbufferEXT" : 1685,
++    "GetFramebufferAttachmentParameterivEXT" : 1686,
++    "GenerateMipmapEXT" : 1687,
+ }
+ 
+ functions = [
+diff --git a/src/mesa/main/fbobject.c b/src/mesa/main/fbobject.c
+index 4989316..885aa44 100644
+--- a/src/mesa/main/fbobject.c
++++ b/src/mesa/main/fbobject.c
+@@ -1995,6 +1995,11 @@ _mesa_detach_renderbuffer(struct gl_context *ctx,
+    return progress;
+ }
+ 
++GLboolean GLAPIENTRY
++_mesa_IsRenderbufferEXT(GLuint renderbuffer)
++{
++   return _mesa_IsRenderbuffer(renderbuffer);
++}
+ 
+ void GLAPIENTRY
+ _mesa_DeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers)
+@@ -2722,6 +2727,12 @@ renderbuffer_storage_target(GLenum target, GLenum internalFormat,
+ }
+ 
+ 
++void GLAPIENTRY
++_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
++{
++   _mesa_DeleteRenderbuffers(n, renderbuffers);
++}
++
+ void GLAPIENTRY
+ _mesa_EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
+ {
+@@ -2752,6 +2763,11 @@ _mesa_EGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
+    ctx->Driver.EGLImageTargetRenderbufferStorage(ctx, rb, image);
+ }
+ 
++void GLAPIENTRY
++_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers)
++{
++   _mesa_GenRenderbuffers(n, renderbuffers);
++}
+ 
+ /**
+  * Helper function for _mesa_GetRenderbufferParameteriv() and
+@@ -2784,6 +2800,12 @@ _mesa_RenderbufferStorage(GLenum target, GLenum internalFormat,
+                                NO_SAMPLES, 0, "glRenderbufferStorage");
+ }
+ 
++void GLAPIENTRY
++_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
++                             GLsizei width, GLsizei height)
++{
++   _mesa_RenderbufferStorage(target, internalFormat, width, height);
++}
+ 
+ void GLAPIENTRY
+ _mesa_RenderbufferStorageMultisample(GLenum target, GLsizei samples,
+@@ -2985,6 +3007,11 @@ _mesa_GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname,
+                                  "glGetNamedRenderbufferParameteriv");
+ }
+ 
++void GLAPIENTRY
++_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
++{
++   _mesa_GetRenderbufferParameteriv(target, pname, params);
++}
+ 
+ void GLAPIENTRY
+ _mesa_GetNamedRenderbufferParameterivEXT(GLuint renderbuffer, GLenum pname,
+@@ -3018,6 +3045,11 @@ _mesa_IsFramebuffer(GLuint framebuffer)
+    return GL_FALSE;
+ }
+ 
++GLboolean GLAPIENTRY
++_mesa_IsFramebufferEXT(GLuint framebuffer)
++{
++  return _mesa_IsFramebuffer(framebuffer);
++}
+ 
+ /**
+  * Check if any of the attachments of the given framebuffer are textures
+@@ -3253,6 +3285,11 @@ _mesa_DeleteFramebuffers(GLsizei n, const GLuint *framebuffers)
+    }
+ }
+ 
++void GLAPIENTRY
++_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
++{
++   _mesa_DeleteFramebuffers(n, framebuffers);
++}
+ 
+ /**
+  * This is the implementation for glGenFramebuffers and glCreateFramebuffers.
+@@ -3299,6 +3336,11 @@ create_framebuffers(GLsizei n, GLuint *framebuffers, bool dsa)
+    _mesa_HashUnlockMutex(ctx->Shared->FrameBuffers);
+ }
+ 
++void GLAPIENTRY
++_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
++{
++   _mesa_GenFramebuffers(n, framebuffers);
++}
+ 
+ void GLAPIENTRY
+ _mesa_GenFramebuffers(GLsizei n, GLuint *framebuffers)
+@@ -3338,6 +3380,11 @@ _mesa_check_framebuffer_status(struct gl_context *ctx,
+    return buffer->_Status;
+ }
+ 
++GLenum GLAPIENTRY
++_mesa_CheckFramebufferStatusEXT(GLenum target)
++{
++   return _mesa_CheckFramebufferStatus(target);
++}
+ 
+ GLenum GLAPIENTRY
+ _mesa_CheckFramebufferStatus_no_error(GLenum target)
+@@ -3950,6 +3997,12 @@ _mesa_FramebufferTexture1D_no_error(GLenum target, GLenum attachment,
+                                           texture, level, 0);
+ }
+ 
++void GLAPIENTRY
++_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
++                              GLenum textarget, GLuint texture, GLint level)
++{
++   _mesa_FramebufferTexture1D(target, attachment, textarget, texture, level);
++}
+ 
+ void GLAPIENTRY
+ _mesa_FramebufferTexture1D(GLenum target, GLenum attachment,
+@@ -3990,6 +4043,12 @@ _mesa_FramebufferTexture2DMultisampleEXT(GLenum target, GLenum attachment,
+                                  false);
+ }
+ 
++void GLAPIENTRY
++_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
++                              GLenum textarget, GLuint texture, GLint level)
++{
++   _mesa_FramebufferTexture2D(target, attachment, textarget, texture, level);
++}
+ 
+ void GLAPIENTRY
+ _mesa_FramebufferTexture3D_no_error(GLenum target, GLenum attachment,
+@@ -4100,6 +4159,15 @@ frame_buffer_texture(GLuint framebuffer, GLenum target,
+                              level, 0, layer, layered);
+ }
+ 
++void GLAPIENTRY
++_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
++                              GLenum textarget, GLuint texture,
++                              GLint level, GLint zoffset)
++{
++   _mesa_FramebufferTexture3D(target, attachment, textarget, texture,
++                              level, zoffset);
++}
++
+ void GLAPIENTRY
+ _mesa_FramebufferTextureLayer_no_error(GLenum target, GLenum attachment,
+                                        GLuint texture, GLint level,
+@@ -4353,6 +4421,15 @@ _mesa_FramebufferRenderbuffer(GLenum target, GLenum attachment,
+                                   renderbuffer, "glFramebufferRenderbuffer");
+ }
+ 
++void GLAPIENTRY
++_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
++                                 GLenum renderbufferTarget,
++                                 GLuint renderbuffer)
++{
++   _mesa_FramebufferRenderbuffer(target, attachment, renderbufferTarget,
++                                 renderbuffer);
++}
++
+ void GLAPIENTRY
+ _mesa_NamedFramebufferRenderbuffer_no_error(GLuint framebuffer,
+                                             GLenum attachment,
+@@ -4754,6 +4831,12 @@ invalid_pname_enum:
+    return;
+ }
+ 
++void GLAPIENTRY
++_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
++                                             GLenum pname, GLint *params)
++{
++   _mesa_GetFramebufferAttachmentParameteriv(target, attachment, pname, params);
++}
+ 
+ void GLAPIENTRY
+ _mesa_GetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment,
+diff --git a/src/mesa/main/fbobject.h b/src/mesa/main/fbobject.h
+index 2a32c15..98271d7 100644
+--- a/src/mesa/main/fbobject.h
++++ b/src/mesa/main/fbobject.h
+@@ -145,6 +145,9 @@ _mesa_bind_framebuffers(struct gl_context *ctx,
+ extern GLboolean GLAPIENTRY
+ _mesa_IsRenderbuffer(GLuint renderbuffer);
+ 
++extern GLboolean GLAPIENTRY
++_mesa_IsRenderbufferEXT(GLuint renderbuffer);
++
+ extern void GLAPIENTRY
+ _mesa_BindRenderbuffer(GLenum target, GLuint renderbuffer);
+ 
+@@ -157,12 +160,18 @@ _mesa_DeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers);
+ void GLAPIENTRY
+ _mesa_GenRenderbuffers_no_error(GLsizei n, GLuint *renderbuffers);
+ 
++extern void GLAPIENTRY
++_mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers);
++
+ extern void GLAPIENTRY
+ _mesa_GenRenderbuffers(GLsizei n, GLuint *renderbuffers);
+ 
+ void GLAPIENTRY
+ _mesa_CreateRenderbuffers_no_error(GLsizei n, GLuint *renderbuffers);
+ 
++extern void GLAPIENTRY
++_mesa_GenRenderbuffersEXT(GLsizei n, GLuint *renderbuffers);
++
+ extern void GLAPIENTRY
+ _mesa_CreateRenderbuffers(GLsizei n, GLuint *renderbuffers);
+ 
+@@ -170,6 +179,10 @@ extern void GLAPIENTRY
+ _mesa_RenderbufferStorage(GLenum target, GLenum internalformat,
+                              GLsizei width, GLsizei height);
+ 
++extern void GLAPIENTRY
++_mesa_RenderbufferStorageEXT(GLenum target, GLenum internalformat,
++                             GLsizei width, GLsizei height);
++
+ extern void GLAPIENTRY
+ _mesa_RenderbufferStorageMultisample(GLenum target, GLsizei samples,
+                                      GLenum internalformat,
+@@ -213,6 +226,10 @@ extern void GLAPIENTRY
+ _mesa_GetRenderbufferParameteriv(GLenum target, GLenum pname,
+                                     GLint *params);
+ 
++extern void GLAPIENTRY
++_mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname,
++                                    GLint *params);
++
+ void GLAPIENTRY
+ _mesa_GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname,
+                                       GLint *params);
+@@ -220,6 +237,9 @@ _mesa_GetNamedRenderbufferParameteriv(GLuint renderbuffer, GLenum pname,
+ extern GLboolean GLAPIENTRY
+ _mesa_IsFramebuffer(GLuint framebuffer);
+ 
++extern GLboolean GLAPIENTRY
++_mesa_IsFramebufferEXT(GLuint framebuffer);
++
+ extern void GLAPIENTRY
+ _mesa_BindFramebuffer(GLenum target, GLuint framebuffer);
+ 
+@@ -229,9 +249,15 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer);
+ extern void GLAPIENTRY
+ _mesa_DeleteFramebuffers(GLsizei n, const GLuint *framebuffers);
+ 
++extern void GLAPIENTRY
++_mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers);
++
+ extern void GLAPIENTRY
+ _mesa_GenFramebuffers(GLsizei n, GLuint *framebuffers);
+ 
++extern void GLAPIENTRY
++_mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers);
++
+ extern void GLAPIENTRY
+ _mesa_CreateFramebuffers(GLsizei n, GLuint *framebuffers);
+ 
+@@ -241,6 +267,9 @@ _mesa_CheckFramebufferStatus_no_error(GLenum target);
+ extern GLenum GLAPIENTRY
+ _mesa_CheckFramebufferStatus(GLenum target);
+ 
++extern GLenum GLAPIENTRY
++_mesa_CheckFramebufferStatusEXT(GLenum target);
++
+ extern GLenum GLAPIENTRY
+ _mesa_CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target);
+ 
+@@ -259,6 +288,11 @@ extern void GLAPIENTRY
+ _mesa_FramebufferTexture2D_no_error(GLenum target, GLenum attachment,
+                                     GLenum textarget, GLuint texture,
+                                     GLint level);
++
++extern void GLAPIENTRY
++_mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
++                              GLenum textarget, GLuint texture, GLint level);
++
+ extern void GLAPIENTRY
+ _mesa_FramebufferTexture2D(GLenum target, GLenum attachment,
+                               GLenum textarget, GLuint texture, GLint level);
+@@ -272,6 +306,11 @@ extern void GLAPIENTRY
+ _mesa_FramebufferTexture3D_no_error(GLenum target, GLenum attachment,
+                                     GLenum textarget, GLuint texture,
+                                     GLint level, GLint layer);
++
++extern void GLAPIENTRY
++_mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
++                              GLenum textarget, GLuint texture, GLint level);
++
+ extern void GLAPIENTRY
+ _mesa_FramebufferTexture3D(GLenum target, GLenum attachment,
+                               GLenum textarget, GLuint texture,
+@@ -281,6 +320,12 @@ extern void GLAPIENTRY
+ _mesa_FramebufferTextureLayer_no_error(GLenum target, GLenum attachment,
+                                        GLuint texture, GLint level,
+                                        GLint layer);
++
++extern void GLAPIENTRY
++_mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
++                              GLenum textarget, GLuint texture,
++                              GLint level, GLint zoffset);
++
+ extern void GLAPIENTRY
+ _mesa_FramebufferTextureLayer(GLenum target, GLenum attachment,
+                                  GLuint texture, GLint level, GLint layer);
+@@ -342,6 +387,11 @@ _mesa_NamedFramebufferRenderbuffer_no_error(GLuint framebuffer,
+                                             GLenum renderbuffertarget,
+                                             GLuint renderbuffer);
+ 
++extern void GLAPIENTRY
++_mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
++                                 GLenum renderbuffertarget,
++                                 GLuint renderbuffer);
++
+ extern void GLAPIENTRY
+ _mesa_NamedFramebufferRenderbuffer(GLuint framebuffer, GLenum attachment,
+                                    GLenum renderbuffertarget,
+@@ -390,6 +440,10 @@ _mesa_InvalidateSubFramebuffer_no_error(GLenum target, GLsizei numAttachments,
+                                         const GLenum *attachments, GLint x,
+                                         GLint y, GLsizei width, GLsizei height);
+ 
++extern void GLAPIENTRY
++_mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
++                                             GLenum pname, GLint *params);
++
+ extern void GLAPIENTRY
+ _mesa_InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments,
+                                const GLenum *attachments, GLint x, GLint y,
+diff --git a/src/mesa/main/genmipmap.c b/src/mesa/main/genmipmap.c
+index 36727bb..8d262e5 100644
+--- a/src/mesa/main/genmipmap.c
++++ b/src/mesa/main/genmipmap.c
+@@ -267,3 +267,9 @@ _mesa_GenerateMultiTexMipmapEXT(GLenum texunit, GLenum target)
+    validate_params_and_generate_mipmap(texObj,
+                                        "glGenerateMultiTexMipmapEXT");
+ }
++
++void GLAPIENTRY
++_mesa_GenerateMipmapEXT(GLenum target)
++{
++   _mesa_GenerateMipmap(target);
++}
+diff --git a/src/mesa/main/genmipmap.h b/src/mesa/main/genmipmap.h
+index c661f21..ff3b45a 100644
+--- a/src/mesa/main/genmipmap.h
++++ b/src/mesa/main/genmipmap.h
+@@ -44,6 +44,9 @@ _mesa_GenerateMipmap(GLenum target);
+ void GLAPIENTRY
+ _mesa_GenerateTextureMipmap_no_error(GLuint texture);
+ 
++extern void GLAPIENTRY
++_mesa_GenerateMipmapEXT(GLenum target);
++
+ extern void GLAPIENTRY
+ _mesa_GenerateTextureMipmap(GLuint texture);
+ 

+ 319 - 0
recipes-graphics/mesa/mesa-pvr/0047-egl-null-add-support-for-async-flip-with-front-buffe.patch

@@ -0,0 +1,319 @@
+From 5212f22050e6e92812b14b6cd1fe5029ceeb4225 Mon Sep 17 00:00:00 2001
+From: Luigi Santivetti <luigi.santivetti@imgtec.com>
+Date: Mon, 21 Oct 2019 09:21:52 +0100
+Subject: [PATCH] egl/null: add support for async flip with front buffer
+ rendering
+
+This change enables the application to render into the buffer being
+scanned out if the display driver doesn't support
+DRM_CAP_ASYNC_PAGE_FLIP.
+
+The egl display now tracks the DRM async flip capabilities, allowing
+platform_null to always return the buffer on screen to the application
+when the swap interval is set to 0, but the DRM driver doesn't support
+async flip.
+
+platform_null can be in several alternative states by the time
+eglSwapInterval() is called, following is a summary of them.
+
+  1. platform_null was initialised and it has already flipped. In this
+  case, return to the client API a reference to the buffer on screen
+
+  2. platform_null was initialised but no back buffer was requested yet
+  from the client API. In this case, return a reference to the front
+  buffer
+
+  3. platform_null was initialised and the client API has already got
+  a back buffer, but no flip was scheduled. In this case, make sure
+  to flip and return the buffer on the screen
+
+Note that this change also refactors the color buffers and front
+buffer code. Platform null after this change has a dedicated struct
+for storing front buffer DRI data and framebuffer id.
+
+Also be noted that min_swap_interval is no longer clamped to 1 when
+DRM_CAP_ASYNC_PAGE_FLIP isn't supported. This is because,
+if min_swap_interval is automatically promoted to 1, Mesa EGL will de
+facto prevent the application from requesting any swap interval less
+than 1.
+
+Change-Id: I3930cfcdb30bfb5358166911bcf84a78bdb4548d
+Signed-off-by: Luigi Santivetti <luigi.santivetti@imgtec.com>
+
+---
+ src/egl/drivers/dri2/egl_dri2.h      |   8 +-
+ src/egl/drivers/dri2/platform_null.c | 153 +++++++++++++++++++++------
+ 2 files changed, 129 insertions(+), 32 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index 32ad720..27beae5 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -283,6 +283,7 @@ struct dri2_egl_display
+ #ifdef HAVE_NULL_PLATFORM
+    bool                      atomic_enabled;
+    bool                      in_formats_enabled;
++   bool                      async_flip_enabled;
+    struct display_output     output;
+ #endif
+ 
+@@ -439,8 +440,12 @@ struct dri2_egl_surface
+ #endif
+       bool                locked;
+       int                 age;
++#ifdef HAVE_NULL_PLATFORM
++   } color_buffers[DRI2_SURFACE_NUM_COLOR_BUFFERS], *back, *current, front_buffer;
++#else
+    } color_buffers[DRI2_SURFACE_NUM_COLOR_BUFFERS], *back, *current;
+ #endif
++#endif
+ 
+ #ifdef HAVE_ANDROID_PLATFORM
+    struct ANativeWindow *window;
+@@ -469,12 +474,13 @@ struct dri2_egl_surface
+ #endif
+ 
+ #ifdef HAVE_NULL_PLATFORM
+-   uint32_t                front_fb_id;
+    struct swap_queue_elem  swap_queue[DRI2_SURFACE_NUM_COLOR_BUFFERS];
+    struct swap_queue_elem  *swap_data;
+    int                     swap_state;
+    bool                    mutex_init;
+    bool                    cond_init;
++   bool                    front_render_enabled;
++   bool                    front_render_init;
+    bool                    cond_init_unlock_buffer;
+ #endif
+ 
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index 2c79199..0ce7e60 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -602,6 +602,15 @@ swap_dequeue_data_finish(struct dri2_egl_surface *dri2_surf)
+    pthread_mutex_unlock(&dri2_surf->mutex);
+ }
+ 
++static void
++swap_drain_queue_data(struct dri2_egl_surface *dri2_surf)
++{
++   pthread_mutex_lock(&dri2_surf->mutex);
++   while (dri2_surf->swap_queue_idx_head != dri2_surf->swap_queue_idx_tail)
++      pthread_cond_wait(&dri2_surf->swap_unlock_buffer_cond, &dri2_surf->mutex);
++   pthread_mutex_unlock(&dri2_surf->mutex);
++}
++
+ static void
+ flip_handler(int fd, unsigned int sequence, unsigned int tv_sec,
+              unsigned int tv_usec, void *flip_data)
+@@ -1146,15 +1155,15 @@ get_front_bo(struct dri2_egl_surface *dri2_surf)
+    if (dri2_surf->base.Type == EGL_WINDOW_BIT)
+       use |= __DRI_IMAGE_USE_SCANOUT;
+ 
+-   dri2_surf->front = create_image(dri2_surf, use);
+-   if (!dri2_surf->front)
++   dri2_surf->front_buffer.dri_image = create_image(dri2_surf, use);
++   if (!dri2_surf->front_buffer.dri_image)
+       return false;
+ 
+    if (dri2_surf->base.Type == EGL_WINDOW_BIT) {
+-      if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->front,
+-                                &dri2_surf->front_fb_id)) {
+-         dri2_dpy->image->destroyImage(dri2_surf->front);
+-         dri2_surf->front = NULL;
++      if (!add_fb_for_dri_image(dri2_dpy, dri2_surf->front_buffer.dri_image,
++                                &dri2_surf->front_buffer.fb_id)) {
++         dri2_dpy->image->destroyImage(dri2_surf->front_buffer.dri_image);
++         dri2_surf->front_buffer.dri_image = NULL;
+          return false;
+       }
+    }
+@@ -1362,7 +1371,7 @@ dri2_null_create_window_surface(_EGLDisplay *disp, _EGLConfig *config,
+    }
+ 
+    err = display_output_modeset(dri2_dpy->fd, &dri2_dpy->output,
+-                                dri2_surf->front_fb_id);
++                                dri2_surf->front_buffer.fb_id);
+    if (err) {
+       _eglError(EGL_BAD_NATIVE_WINDOW, "window set mode");
+       goto err_destroy_surface;
+@@ -1392,6 +1401,60 @@ dri2_null_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *config,
+    return create_surface(disp, config, EGL_PBUFFER_BIT, attrib_list);
+ }
+ 
++static void
++dri2_null_init_front_buffer_render(_EGLSurface *draw)
++{
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
++
++   dri2_surf->front_render_init = true;
++
++   /* Drain the queue. swap_buffer_unlock_cond signals for the last time
++    * when the last back buffer in the queue went on screen and it's being
++    * tracked as current by then.
++    */
++   swap_drain_queue_data(dri2_surf);
++
++   /* If previously flipped, take a reference to the current buffer */
++   if (dri2_surf->current) {
++      assert(dri2_surf->current->dri_image);
++      dri2_surf->back = dri2_surf->current;
++
++      for (unsigned i = 0; i < DRI2_SURFACE_NUM_COLOR_BUFFERS; i++)
++         dri2_surf->color_buffers[i].age = 0;
++
++      return;
++   }
++
++   /* If the application hasn't yet fetched a back buffer, then it's not too
++    * late to use front buffer's dri_image and fb_id.
++    */
++   if (!dri2_surf->back) {
++      assert(dri2_surf->front_buffer.dri_image);
++      dri2_surf->back = &dri2_surf->front_buffer;
++
++      /* Don't need to reset buffer age since no flip was requested yet */
++
++      return;
++   }
++
++   /* In order to initialise one color buffer for front buffer rendering,
++    * one page flip must occur.
++    */
++   swap_enqueue_data(dri2_surf, get_back_buffer_id(dri2_surf), 1);
++
++   return dri2_null_init_front_buffer_render(draw);
++}
++
++static void
++dri2_null_disable_front_buffer_render(_EGLSurface *draw)
++{
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
++
++   dri2_surf->front_render_enabled = false;
++   dri2_surf->front_render_init = false;
++   dri2_surf->back = NULL;
++}
++
+ static EGLBoolean
+ dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+ {
+@@ -1403,14 +1466,7 @@ dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+     * sure we process the flip event.
+     */
+    if (dri2_surf->swap_queue_processor) {
+-      pthread_mutex_lock(&dri2_surf->mutex);
+-
+-      /* Wait for any outstanding swaps to complete */
+-      while (dri2_surf->swap_queue_idx_head != dri2_surf->swap_queue_idx_tail)
+-         pthread_cond_wait(&dri2_surf->swap_unlock_buffer_cond,
+-                           &dri2_surf->mutex);
+-
+-      pthread_mutex_unlock(&dri2_surf->mutex);
++      swap_drain_queue_data(dri2_surf);
+       pthread_cancel(dri2_surf->swap_queue_processor);
+       pthread_join(dri2_surf->swap_queue_processor, NULL);
+    }
+@@ -1424,11 +1480,11 @@ dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+    if (dri2_surf->mutex_init)
+       pthread_mutex_destroy(&dri2_surf->mutex);
+ 
+-   if (dri2_surf->front)
+-      dri2_dpy->image->destroyImage(dri2_surf->front);
++   if (dri2_surf->front_buffer.dri_image)
++      dri2_dpy->image->destroyImage(dri2_surf->front_buffer.dri_image);
+ 
+-   if (dri2_surf->front_fb_id)
+-         drmModeRmFB(dri2_dpy->fd, dri2_surf->front_fb_id);
++   if (dri2_surf->front_buffer.fb_id)
++         drmModeRmFB(dri2_dpy->fd, dri2_surf->front_buffer.fb_id);
+ 
+    for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
+       if (dri2_surf->color_buffers[i].fb_id)
+@@ -1455,6 +1511,16 @@ dri2_null_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
+    if (dri2_surf->base.Type != EGL_WINDOW_BIT)
+       return EGL_TRUE;
+ 
++   /* Flush and early return, no swap takes place */
++   if (dri2_surf->front_render_enabled) {
++      dri2_flush_drawable_for_swapbuffers(disp, draw);
++
++      if (!dri2_surf->front_render_init)
++         dri2_null_init_front_buffer_render(draw);
++
++      return EGL_TRUE;
++   }
++
+    for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++)
+       if (dri2_surf->color_buffers[i].age > 0)
+          dri2_surf->color_buffers[i].age++;
+@@ -1497,6 +1563,22 @@ dri2_null_query_buffer_age(_EGLDisplay *disp, _EGLSurface *surface)
+ static EGLBoolean
+ dri2_null_swap_interval(_EGLDisplay *dpy, _EGLSurface *draw, EGLint interval)
+ {
++   struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
++   struct dri2_egl_display *dri2_dpy =
++      dri2_egl_display(dri2_surf->base.Resource.Display);
++
++   /* dri2_dpy tracks whether the display driver is async flip capable.
++    * If it isn't, enable front buffer rendering when swap interval
++    * 0 is passed in from the application.
++    */
++   if (!interval && !dri2_dpy->async_flip_enabled) {
++      if (!dri2_surf->front_render_enabled)
++         dri2_surf->front_render_enabled = true;
++   } else {
++      if (dri2_surf->front_render_enabled)
++         dri2_null_disable_front_buffer_render(draw);
++   }
++
+    _eglLog(_EGL_DEBUG, "DRI2: set swap interval to %d", interval);
+    draw->SwapInterval = interval;
+    return EGL_TRUE;
+@@ -1534,7 +1616,7 @@ dri2_null_image_get_buffers(__DRIdrawable *driDrawable, unsigned int format,
+ 
+    if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
+       buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
+-      buffers->front = dri2_surf->front;
++      buffers->front = dri2_surf->front_buffer.dri_image;
+    }
+ 
+    if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
+@@ -1672,18 +1754,27 @@ dri2_null_setup_swap_interval(_EGLDisplay *disp)
+    dri2_setup_swap_interval(disp, swap_max_interval);
+ 
+    err = drmGetCap(dri2_dpy->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+-   if (err || value == 0)
+-      dri2_dpy->min_swap_interval = 1;
++   if (err || value == 0) {
+ 
+-   /**
+-    * drm/atomic: Reject FLIP_ASYNC unconditionally
+-    * upstream f2cbda2dba11de868759cae9c0d2bab5b8411406
+-    *
+-    * Only allow swap interval 0 for legacy DRM/KMS and let
+-    * the app be aware that swap interval is clamped to 1.
+-    */
+-   if (dri2_dpy->atomic_enabled)
+-      dri2_dpy->min_swap_interval = 1;
++      /* DRM/KMS does not support async page flip. In order to support
++       * swap interval 0, use front buffer rendering.
++       */
++      _eglLog(_EGL_DEBUG,
++              "drm async flip not supported, use front buffer");
++   } else {
++
++      /* drm/atomic: Reject FLIP_ASYNC unconditionally
++       * upstream f2cbda2dba11de868759cae9c0d2bab5b8411406
++       *
++       * Legacy DRM/KMS can use DRM_MODE_PAGE_FLIP_ASYNC, for atomic
++       * drivers fallback to front buffer rendering.
++       */
++      if (dri2_dpy->atomic_enabled)
++         _eglLog(_EGL_DEBUG,
++                 "async flip not supported by atomic, use front buffer");
++      else
++         dri2_dpy->async_flip_enabled = true;
++   }
+ }
+ 
+ EGLBoolean

+ 227 - 0
recipes-graphics/mesa/mesa-pvr/0048-gbm-add-pbuffer-support.patch

@@ -0,0 +1,227 @@
+From e98ab37481eb89284b95a84bdb01a43262841cce Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Fri, 21 Aug 2020 12:13:28 +0100
+Subject: [PATCH] gbm: add pbuffer support
+
+The EGL backend GLX provider for XWayland may get the EGL configs it
+uses to generate the GLX ones from GBM. That platform doesn't support
+pbuffers. When the client tries to match GLX configs with the DRI ones,
+a mismatch in the pbuffer attributes will result in the GLX config
+being rejected.
+
+Although support for creating pbuffers has been added, this isn't
+required when using the EGL backend GLX provider, as indirect GLX
+isn't supported.
+
+---
+ src/egl/drivers/dri2/egl_dri2.h     |   3 +
+ src/egl/drivers/dri2/platform_drm.c | 110 +++++++++++++++++++++++++---
+ 2 files changed, 103 insertions(+), 10 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index 27beae5..f8792ea 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -468,6 +468,9 @@ struct dri2_egl_surface
+    /* surfaceless and device */
+    __DRIimage           *front;
+    unsigned int         visual;
++#ifdef HAVE_DRM_PLATFORM
++   struct gbm_bo       *front_bo;
++#endif
+ 
+ #ifdef HAVE_WAYLAND_PLATFORM
+    void                 *swrast_front;
+diff --git a/src/egl/drivers/dri2/platform_drm.c b/src/egl/drivers/dri2/platform_drm.c
+index 2b32943..258e8b6 100644
+--- a/src/egl/drivers/dri2/platform_drm.c
++++ b/src/egl/drivers/dri2/platform_drm.c
+@@ -41,6 +41,38 @@
+ #include "egl_dri2.h"
+ #include "loader.h"
+ 
++static bool
++dri2_drm_alloc_front_image(struct dri2_egl_surface *dri2_surf)
++{
++   if (!dri2_surf->front_bo) {
++      struct dri2_egl_display *dri2_dpy =
++         dri2_egl_display(dri2_surf->base.Resource.Display);
++
++      struct gbm_dri_surface *surf = dri2_surf->gbm_surf;
++
++      dri2_surf->front_bo = gbm_bo_create(&dri2_dpy->gbm_dri->base,
++                                          surf->base.v0.width,
++                                          surf->base.v0.height,
++                                          surf->base.v0.format,
++                                          surf->base.v0.flags);
++      if (!dri2_surf->front_bo) {
++         _eglError(EGL_BAD_ALLOC, "failed to allocate front buffer");
++         return false;
++      }
++   }
++
++   return true;
++}
++
++static void
++dri2_drm_free_front_image(struct dri2_egl_surface *dri2_surf)
++{
++   if (dri2_surf->front_bo) {
++      gbm_bo_destroy(dri2_surf->front_bo);
++      dri2_surf->front_bo = NULL;
++   }
++}
++
+ static struct gbm_bo *
+ lock_front_buffer(struct gbm_surface *_surf)
+ {
+@@ -138,8 +170,8 @@ dri2_drm_config_is_compatible(struct dri2_egl_display *dri2_dpy,
+ }
+ 
+ static _EGLSurface *
+-dri2_drm_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf,
+-                               void *native_surface, const EGLint *attrib_list)
++dri2_drm_create_surface(_EGLDisplay *disp, EGLint type, _EGLConfig *conf,
++                        void *native_surface, const EGLint *attrib_list)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    struct dri2_egl_config *dri2_conf = dri2_egl_config(conf);
+@@ -154,11 +186,25 @@ dri2_drm_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf,
+       return NULL;
+    }
+ 
+-   if (!dri2_init_surface(&dri2_surf->base, disp, EGL_WINDOW_BIT, conf,
++   if (!dri2_init_surface(&dri2_surf->base, disp, type, conf,
+                           attrib_list, false, native_surface))
+       goto cleanup_surf;
+ 
+-   config = dri2_get_dri_config(dri2_conf, EGL_WINDOW_BIT,
++   if (type == EGL_PBUFFER_BIT) {
++      struct gbm_device *gbm = disp->PlatformDisplay;
++      _EGLSurface *surf = &dri2_surf->base;
++
++      assert(!surface);
++
++      surface = gbm_surface_create(gbm, surf->Width, surf->Height,
++                                   conf->NativeVisualID, GBM_BO_USE_RENDERING);
++      if (!surface) {
++	      _eglError(EGL_BAD_ALLOC, "Failed to allocate pbuffer GBM surface");
++	      goto cleanup_surf;
++      }
++   }
++
++   config = dri2_get_dri_config(dri2_conf, type,
+                                 dri2_surf->base.GLColorspace);
+ 
+    if (!config) {
+@@ -183,11 +229,22 @@ dri2_drm_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf,
+    return &dri2_surf->base;
+ 
+  cleanup_surf:
++   if (type == EGL_PBUFFER_BIT && surface != NULL)
++      gbm_surface_destroy(surface);
++
+    free(dri2_surf);
+ 
+    return NULL;
+ }
+ 
++static _EGLSurface *
++dri2_drm_create_window_surface(_EGLDisplay *disp, _EGLConfig *conf,
++                               void *native_surface, const EGLint *attrib_list)
++{
++   return dri2_drm_create_surface(disp, EGL_WINDOW_BIT, conf,
++                                  native_surface, attrib_list);
++}
++
+ static _EGLSurface *
+ dri2_drm_create_pixmap_surface(_EGLDisplay *disp, _EGLConfig *conf,
+                                void *native_window, const EGLint *attrib_list)
+@@ -202,6 +259,14 @@ dri2_drm_create_pixmap_surface(_EGLDisplay *disp, _EGLConfig *conf,
+    return NULL;
+ }
+ 
++static _EGLSurface *
++dri2_drm_create_pbuffer_surface(_EGLDisplay *disp, _EGLConfig *conf,
++                                const EGLint *attrib_list)
++{
++   return dri2_drm_create_surface(disp, EGL_PBUFFER_BIT, conf,
++                                  NULL, attrib_list);
++}
++
+ static EGLBoolean
+ dri2_drm_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+ {
+@@ -217,6 +282,11 @@ dri2_drm_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+ 
+    dri2_egl_surface_free_local_buffers(dri2_surf);
+ 
++   dri2_drm_free_front_image(dri2_surf);
++
++   if (surf->Type == EGL_PBUFFER_BIT)
++      gbm_surface_destroy(&dri2_surf->gbm_surf->base);
++
+    dri2_fini_surface(surf);
+    free(surf);
+ 
+@@ -402,12 +472,27 @@ dri2_drm_image_get_buffers(__DRIdrawable *driDrawable,
+    struct dri2_egl_surface *dri2_surf = loaderPrivate;
+    struct gbm_dri_bo *bo;
+ 
+-   if (get_back_bo(dri2_surf) < 0)
+-      return 0;
++   buffers->image_mask = 0;
++   buffers->front = NULL;
++   buffers->back = NULL;
+ 
+-   bo = gbm_dri_bo(dri2_surf->back->bo);
+-   buffers->image_mask = __DRI_IMAGE_BUFFER_BACK;
+-   buffers->back = bo->image;
++   if (buffer_mask & __DRI_IMAGE_BUFFER_FRONT) {
++      if (!dri2_drm_alloc_front_image(dri2_surf))
++         return 0;
++
++      bo = gbm_dri_bo(dri2_surf->front_bo);
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_FRONT;
++      buffers->front = bo->image;
++   }
++
++   if (buffer_mask & __DRI_IMAGE_BUFFER_BACK) {
++      if (get_back_bo(dri2_surf) < 0)
++         return 0;
++
++      bo = gbm_dri_bo(dri2_surf->back->bo);
++      buffers->image_mask |= __DRI_IMAGE_BUFFER_BACK;
++      buffers->back = bo->image;
++   }
+ 
+    return 1;
+ }
+@@ -425,6 +510,9 @@ dri2_drm_swap_buffers(_EGLDisplay *disp, _EGLSurface *draw)
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    struct dri2_egl_surface *dri2_surf = dri2_egl_surface(draw);
+ 
++   if (dri2_surf->base.Type != EGL_WINDOW_BIT)
++      return EGL_TRUE;
++
+    if (!dri2_dpy->flush) {
+       dri2_dpy->core->swapBuffers(dri2_surf->dri_drawable);
+       return EGL_TRUE;
+@@ -648,7 +736,8 @@ drm_add_configs_for_visuals(_EGLDisplay *disp)
+          };
+ 
+          dri2_conf = dri2_add_config(disp, dri2_dpy->driver_configs[i],
+-               config_count + 1, EGL_WINDOW_BIT, attr_list, NULL, NULL);
++               config_count + 1, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
++               attr_list, NULL, NULL);
+          if (dri2_conf) {
+             if (dri2_conf->base.ConfigID == config_count + 1)
+                config_count++;
+@@ -672,6 +761,7 @@ static const struct dri2_egl_display_vtbl dri2_drm_display_vtbl = {
+    .authenticate = dri2_drm_authenticate,
+    .create_window_surface = dri2_drm_create_window_surface,
+    .create_pixmap_surface = dri2_drm_create_pixmap_surface,
++   .create_pbuffer_surface = dri2_drm_create_pbuffer_surface,
+    .destroy_surface = dri2_drm_destroy_surface,
+    .create_image = dri2_drm_create_image_khr,
+    .swap_buffers = dri2_drm_swap_buffers,

+ 344 - 0
recipes-graphics/mesa/mesa-pvr/0049-egl-null-expose-EXT_yuv_surface-support.patch

@@ -0,0 +1,344 @@
+From d19c811118e56dfc7210927151ba19559cc0a17e Mon Sep 17 00:00:00 2001
+From: Luigi Santivetti <luigi.santivetti@imgtec.com>
+Date: Thu, 2 Sep 2021 22:47:54 +0100
+Subject: [PATCH] egl/null: expose EXT_yuv_surface support
+
+---
+ include/GL/internal/dri_interface.h  |   2 +
+ src/egl/drivers/dri2/platform_null.c | 177 ++++++++++++++++++++++++---
+ src/mesa/drivers/dri/pvr/pvrutil.c   |   8 ++
+ 3 files changed, 173 insertions(+), 14 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 9c7bcac..888a117 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1411,6 +1411,8 @@ struct __DRIdri2ExtensionRec {
+ #define __DRI_IMAGE_FORMAT_ARGB4444     0x1018
+ #define __DRI_IMAGE_FORMAT_YVU444_PACK10_IMG 0x1019
+ #define __DRI_IMAGE_FORMAT_BGR888       0x101a
++#define __DRI_IMAGE_FORMAT_NV12         0x101b
++#define __DRI_IMAGE_FORMAT_NV21         0x101c
+ 
+ #define __DRI_IMAGE_USE_SHARE		0x0001
+ #define __DRI_IMAGE_USE_SCANOUT		0x0002
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index 0ce7e60..8efac19 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -79,6 +79,35 @@ uint32_t get_back_buffer_id(struct dri2_egl_surface *dri2_surf)
+       .prop_value = value,                                                  \
+    }
+ 
++static const struct dri2_null_yuv_attrib {
++   uint32_t order;
++   uint32_t subsample;
++   uint32_t num_planes;
++   uint32_t plane_bpp;
++} dri2_null_yuv_attribs[] = {
++   {
++      /* __DRI_IMAGE_FORMAT_YUYV */
++      .order = __DRI_ATTRIB_YUV_ORDER_YUYV_BIT,
++      .subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_2_BIT,
++      .num_planes = 1,
++      .plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT,
++   },
++   {
++      /* __DRI_IMAGE_FORMAT_NV12 */
++      .order = __DRI_ATTRIB_YUV_ORDER_YUV_BIT,
++      .subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT,
++      .num_planes = 2,
++      .plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT,
++   },
++   {
++      /* __DRI_IMAGE_FORMAT_NV21 */
++      .order = __DRI_ATTRIB_YUV_ORDER_YVU_BIT,
++      .subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT,
++      .num_planes = 2,
++      .plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT,
++   },
++};
++
+ /*
+  * The index of entries in this table is used as a bitmask in
+  * dri2_dpy->formats, which tracks the formats supported by the display.
+@@ -88,24 +117,49 @@ static const struct dri2_null_format {
+    int dri_image_format;
+    int rgba_shifts[4];
+    unsigned int rgba_sizes[4];
++   const struct dri2_null_yuv_attrib *yuv;
+ } dri2_null_formats[] = {
+    {
+       .drm_format = DRM_FORMAT_XRGB8888,
+       .dri_image_format = __DRI_IMAGE_FORMAT_XRGB8888,
+       .rgba_shifts = { 16, 8, 0, -1 },
+       .rgba_sizes = { 8, 8, 8, 0 },
++      .yuv = NULL,
+    },
+    {
+       .drm_format = DRM_FORMAT_ARGB8888,
+       .dri_image_format = __DRI_IMAGE_FORMAT_ARGB8888,
+       .rgba_shifts = { 16, 8, 0, 24 },
+       .rgba_sizes = { 8, 8, 8, 8 },
++      .yuv = NULL,
+    },
+    {
+       .drm_format = DRM_FORMAT_RGB565,
+       .dri_image_format = __DRI_IMAGE_FORMAT_RGB565,
+       .rgba_shifts = { 11, 5, 0, -1 },
+       .rgba_sizes = { 5, 6, 5, 0 },
++      .yuv = NULL,
++   },
++   {
++      .drm_format = DRM_FORMAT_YUYV,
++      .dri_image_format = __DRI_IMAGE_FORMAT_YUYV,
++      .rgba_shifts = { -1, -1, -1, -1 },
++      .rgba_sizes = { 0, 0, 0, 0 },
++      .yuv = &dri2_null_yuv_attribs[0],
++   },
++   {
++      .drm_format = DRM_FORMAT_NV12,
++      .dri_image_format = __DRI_IMAGE_FORMAT_NV12,
++      .rgba_shifts = { -1, -1, -1, -1 },
++      .rgba_sizes = { 0, 0, 0, 0 },
++      .yuv = &dri2_null_yuv_attribs[1],
++   },
++   {
++      .drm_format = DRM_FORMAT_NV21,
++      .dri_image_format = __DRI_IMAGE_FORMAT_NV21,
++      .rgba_shifts = { -1, -1, -1, -1 },
++      .rgba_sizes = { 0, 0, 0, 0 },
++      .yuv = &dri2_null_yuv_attribs[2],
+    },
+ };
+ 
+@@ -137,6 +191,36 @@ format_idx_get_from_config(struct dri2_egl_display *dri2_dpy,
+    return -1;
+ }
+ 
++static int
++yuv_format_idx_get_from_config(struct dri2_egl_display *dri2_dpy,
++                               const __DRIconfig *dri_config)
++{
++   for (unsigned int i = 0; i < ARRAY_SIZE(dri2_null_formats); i++) {
++      const struct dri2_null_yuv_attrib *yuv = dri2_null_formats[i].yuv;
++      unsigned order, subsample, num_planes, plane_bpp;
++
++      if (!yuv)
++         continue;
++
++      dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_ORDER,
++                                      &order);
++      dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_SUBSAMPLE,
++                                      &subsample);
++      dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_NUMBER_OF_PLANES,
++                                      &num_planes);
++      dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_YUV_PLANE_BPP,
++                                      &plane_bpp);
++
++      if (order != yuv->order || subsample != yuv->subsample ||
++          num_planes != yuv->num_planes || plane_bpp != yuv->plane_bpp)
++         continue;
++
++      return i;
++   }
++
++   return -1;
++}
++
+ static int
+ format_idx_get_from_dri_image_format(uint32_t dri_image_format)
+ {
+@@ -1082,23 +1166,21 @@ static bool
+ add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+                      uint32_t *fb_id_out)
+ {
+-   uint64_t modifiers[4] = {0};
++   int handle, stride, width, height, format, l_mod, h_mod, offset;
++   uint64_t modifier = DRM_FORMAT_MOD_INVALID;
++   uint64_t *modifiers = NULL, mods[4] = {0};
+    uint32_t handles[4] = {0};
+    uint32_t pitches[4] = {0};
+    uint32_t offsets[4] = {0};
++   __DRIimage *p_image;
+    uint32_t flags = 0;
+-   int handle, stride, width, height, format, l_mod, h_mod;
+    int format_idx;
++   int num_planes;
+ 
+-   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HANDLE, &handle);
+-   dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE, &stride);
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_WIDTH, &width);
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_HEIGHT, &height);
+    dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_FORMAT, &format);
+ 
+-   handles[0] = (uint32_t) handle;
+-   pitches[0] = (uint32_t) stride;
+-
+    format_idx = format_idx_get_from_dri_image_format(format);
+    assert(format_idx != -1);
+ 
+@@ -1106,10 +1188,44 @@ add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+       dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &h_mod);
+       dri2_dpy->image->queryImage(image, __DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &l_mod);
+ 
+-      modifiers[0] = combine_u32_into_u64((uint32_t) h_mod, (uint32_t) l_mod);
++      modifier = combine_u32_into_u64((uint32_t) h_mod, (uint32_t) l_mod);
++      modifiers = mods;
++
+       flags |= DRM_MODE_FB_MODIFIERS;
+    }
+ 
++   dri2_dpy->image->queryImage(image,
++                               __DRI_IMAGE_ATTRIB_NUM_PLANES, &num_planes);
++   if (num_planes <= 0)
++      num_planes = 1;
++
++   for (int i = 0; i < num_planes; i++) {
++      if (dri2_dpy->in_formats_enabled) {
++         assert(modifiers && modifier != DRM_FORMAT_MOD_INVALID);
++         modifiers[i] = modifier;
++      }
++
++      p_image = dri2_dpy->image->fromPlanar(image, i, NULL);
++      if (!p_image) {
++         assert(i == 0);
++         p_image = image;
++      }
++
++      dri2_dpy->image->queryImage(p_image, __DRI_IMAGE_ATTRIB_STRIDE,
++                                  &stride);
++      dri2_dpy->image->queryImage(p_image, __DRI_IMAGE_ATTRIB_OFFSET,
++                                  &offset);
++      dri2_dpy->image->queryImage(p_image, __DRI_IMAGE_ATTRIB_HANDLE,
++                                  &handle);
++
++      if (p_image != image)
++         dri2_dpy->image->destroyImage(p_image);
++
++      pitches[i] = (uint32_t) stride;
++      offsets[i] = (uint32_t) offset;
++      handles[i] = (uint32_t) handle;
++   }
++
+    return !drmModeAddFB2WithModifiers(dri2_dpy->fd, width, height,
+                                       dri2_null_formats[format_idx].drm_format,
+                                       handles, pitches, offsets, modifiers,
+@@ -1256,6 +1372,7 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+    struct dri2_egl_config *dri2_config = dri2_egl_config(config);
+    struct dri2_egl_surface *dri2_surf;
+    const __DRIconfig *dri_config;
++   unsigned int render_type;
+    _EGLSurface *surf;
+    int format_idx;
+    bool ret;
+@@ -1286,7 +1403,14 @@ create_surface(_EGLDisplay *disp, _EGLConfig *config, EGLint type,
+        goto err_free_surface;
+    }
+ 
+-   format_idx = format_idx_get_from_config(dri2_dpy, dri_config);
++   if (!dri2_dpy->core->getConfigAttrib(dri_config, __DRI_ATTRIB_RENDER_TYPE,
++                                        &render_type))
++      goto err_free_surface;
++
++   if (render_type & __DRI_ATTRIB_YUV_BIT)
++      format_idx = yuv_format_idx_get_from_config(dri2_dpy, dri_config);
++   else
++      format_idx = format_idx_get_from_config(dri2_dpy, dri_config);
+    assert(format_idx != -1);
+ 
+    dri2_surf->format = dri2_null_formats[format_idx].dri_image_format;
+@@ -1627,6 +1751,17 @@ dri2_null_image_get_buffers(__DRIdrawable *driDrawable, unsigned int format,
+    return 1;
+ }
+ 
++static unsigned
++dri2_null_get_capability(void *loaderPrivate, enum dri_loader_cap cap)
++{
++   switch (cap) {
++   case DRI_LOADER_CAP_YUV_SURFACE_IMG:
++      return 1;
++   default:
++      return 0;
++   }
++}
++
+ static void
+ dri2_null_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
+ {
+@@ -1635,10 +1770,11 @@ dri2_null_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
+ }
+ 
+ static const __DRIimageLoaderExtension image_loader_extension = {
+-   .base = { __DRI_IMAGE_LOADER, 1 },
++   .base = { __DRI_IMAGE_LOADER, 2 },
+ 
+    .getBuffers          = dri2_null_image_get_buffers,
+    .flushFrontBuffer    = dri2_null_flush_front_buffer,
++   .getCapability       = dri2_null_get_capability,
+ };
+ 
+ static const __DRIextension *image_loader_extensions[] = {
+@@ -1720,10 +1856,24 @@ dri2_null_add_configs_for_formats(_EGLDisplay *disp)
+ 
+    for (unsigned i = 0; dri2_dpy->driver_configs[i]; i++) {
+       struct dri2_egl_config *dri2_conf;
++      EGLint surface_type = EGL_WINDOW_BIT;
++      unsigned int render_type;
+       int format_idx;
+ 
+-      format_idx = format_idx_get_from_config(dri2_dpy,
+-                                              dri2_dpy->driver_configs[i]);
++      if (!dri2_dpy->core->getConfigAttrib(dri2_dpy->driver_configs[i],
++                                           __DRI_ATTRIB_RENDER_TYPE,
++                                           &render_type))
++         continue;
++
++      if (render_type & __DRI_ATTRIB_YUV_BIT) {
++         format_idx = yuv_format_idx_get_from_config(dri2_dpy,
++                                                     dri2_dpy->driver_configs[i]);
++      } else {
++         format_idx = format_idx_get_from_config(dri2_dpy,
++                                                 dri2_dpy->driver_configs[i]);
++         surface_type |= EGL_PBUFFER_BIT;
++      }
++
+       if (format_idx == -1)
+          continue;
+ 
+@@ -1735,8 +1885,7 @@ dri2_null_add_configs_for_formats(_EGLDisplay *disp)
+ 
+       dri2_conf = dri2_add_config(disp,
+                                   dri2_dpy->driver_configs[i], count + 1,
+-                                  EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+-                                  NULL, NULL, NULL);
++                                  surface_type, NULL, NULL, NULL);
+       if (dri2_conf)
+          count++;
+    }
+diff --git a/src/mesa/drivers/dri/pvr/pvrutil.c b/src/mesa/drivers/dri/pvr/pvrutil.c
+index 945e18c..d107a5d 100644
+--- a/src/mesa/drivers/dri/pvr/pvrutil.c
++++ b/src/mesa/drivers/dri/pvr/pvrutil.c
+@@ -174,6 +174,10 @@ PVRDRIFormatToFourCC(int dri_format)
+       return DRM_FORMAT_YVU444_PACK10_IMG;
+    case __DRI_IMAGE_FORMAT_BGR888:
+       return DRM_FORMAT_BGR888;
++   case __DRI_IMAGE_FORMAT_NV12:
++      return DRM_FORMAT_NV12;
++   case __DRI_IMAGE_FORMAT_NV21:
++      return DRM_FORMAT_NV21;
+    default:
+       __driUtilMessage("%s: Unknown format: %d", __func__, dri_format);
+       break;
+@@ -230,6 +234,10 @@ PVRDRIFourCCToDRIFormat(int iFourCC)
+       return __DRI_IMAGE_FORMAT_YVU444_PACK10_IMG;
+    case DRM_FORMAT_BGR888:
+       return __DRI_IMAGE_FORMAT_BGR888;
++   case DRM_FORMAT_NV12:
++      return __DRI_IMAGE_FORMAT_NV12;
++   case DRM_FORMAT_NV21:
++      return __DRI_IMAGE_FORMAT_NV21;
+    default:
+       __driUtilMessage("%s: Unknown format: %d", __func__, iFourCC);
+       break;

+ 431 - 0
recipes-graphics/mesa/mesa-pvr/0050-dri-preserve-the-original-FD-for-driver-use.patch

@@ -0,0 +1,431 @@
+From 4163f34fbdbc0516085ba562e0013487b408c7ab Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Tue, 9 Mar 2021 17:15:30 +0000
+Subject: [PATCH] dri: preserve the original FD for driver use.
+
+If an application uses a different GPU from the default, allow the
+file descriptor (FD) for that original GPU/display to be preserved
+for use by drivers. Drivers may wish to use the original FD to
+allocate shared surfaces, to ensure the surface properties are
+compatible with the original GPU/display (e.g. for X11 or Wayland).
+
+This feature is only available on platforms that choose to support
+it, by implementing the new getDisplayFD function in the DRI image,
+and DRI2 loader extensions.
+
+If the feature is available, drivers can obtain the original FD
+by calling the getDisplayFD function in the relevant loader extension.
+Drivers should check the FD is valid before use (i.e. not -1). If
+the FD is valid, it may be equal to the current GPU FD if a different
+GPU is not being used. The FD is owned by the platform, not the
+driver, and the platform is responsible for closing it.
+
+The feature is currently supported by the Wayland, and DRI3 based
+X11 EGL and GLX platforms.
+
+---
+ include/GL/internal/dri_interface.h      | 26 +++++++++++++++++--
+ src/egl/drivers/dri2/egl_dri2.c          | 10 +++++++
+ src/egl/drivers/dri2/egl_dri2.h          |  1 +
+ src/egl/drivers/dri2/platform_wayland.c  | 31 ++++++++++++++++++++--
+ src/egl/drivers/dri2/platform_x11.c      |  3 +++
+ src/egl/drivers/dri2/platform_x11_dri3.c | 27 ++++++++++++++++++-
+ src/glx/dri3_glx.c                       | 33 ++++++++++++++++++++++--
+ src/glx/dri3_priv.h                      |  1 +
+ 8 files changed, 125 insertions(+), 7 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 888a117..2fb440f 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1146,7 +1146,7 @@ struct __DRIbufferRec {
+ };
+ 
+ #define __DRI_DRI2_LOADER "DRI_DRI2Loader"
+-#define __DRI_DRI2_LOADER_VERSION 5
++#define __DRI_DRI2_LOADER_VERSION 6
+ 
+ enum dri_loader_cap {
+    /* Whether the loader handles RGBA channel ordering correctly. If not,
+@@ -1227,6 +1227,17 @@ struct __DRIdri2LoaderExtensionRec {
+      * \since 5
+      */
+     void (*destroyLoaderImageState)(void *loaderPrivate);
++
++    /**
++     * Get the display FD
++     *
++     * Get the FD of the display device.
++     *
++     * \param loaderPrivate The last parameter of createNewScreen or
++     *                      createNewScreen2.
++     * \since 6
++     */
++     int (*getDisplayFD)(void *loaderPrivate);
+ };
+ 
+ /**
+@@ -2131,7 +2142,7 @@ struct __DRIimageList {
+ };
+ 
+ #define __DRI_IMAGE_LOADER "DRI_IMAGE_LOADER"
+-#define __DRI_IMAGE_LOADER_VERSION 4
++#define __DRI_IMAGE_LOADER_VERSION 5
+ 
+ struct __DRIimageLoaderExtensionRec {
+     __DRIextension base;
+@@ -2199,6 +2210,17 @@ struct __DRIimageLoaderExtensionRec {
+      * \since 4
+      */
+     void (*destroyLoaderImageState)(void *loaderPrivate);
++
++    /**
++     * Get the display FD
++     *
++     * Get the FD of the display device.
++     *
++     * \param loaderPrivate The last parameter of createNewScreen or
++     *                      createNewScreen2.
++     * \since 5
++     */
++     int (*getDisplayFD)(void *loaderPrivate);
+ };
+ 
+ /**
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index c4a49ca..1df53ef 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1367,6 +1367,16 @@ dri2_display_destroy(_EGLDisplay *disp)
+       break;
+    }
+ 
++   switch (disp->Platform) {
++   case _EGL_PLATFORM_WAYLAND:
++   case _EGL_PLATFORM_X11:
++      if (dri2_dpy->fd_dpy >= 0 && dri2_dpy->fd_dpy != dri2_dpy->fd)
++         close(dri2_dpy->fd_dpy);
++      break;
++   default:
++      break;
++   }
++
+    if (dri2_dpy->fd >= 0)
+       close(dri2_dpy->fd);
+ 
+diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
+index f8792ea..22e27b4 100644
+--- a/src/egl/drivers/dri2/egl_dri2.h
++++ b/src/egl/drivers/dri2/egl_dri2.h
+@@ -231,6 +231,7 @@ struct dri2_egl_display
+    const __DRIconfigOptionsExtension *configOptions;
+    const __DRImutableRenderBufferDriverExtension *mutable_render_buffer;
+    int                       fd;
++   int                       fd_dpy;
+ 
+    /* dri2_initialize/dri2_terminate increment/decrement this count, so does
+     * dri2_make_current (tracks if there are active contexts/surfaces). */
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index 06272d4..b393e05 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -44,6 +44,7 @@
+ #include "loader.h"
+ #include "util/u_vector.h"
+ #include "util/anon_file.h"
++#include "util/os_file.h"
+ #include "eglglobals.h"
+ 
+ #include <wayland-egl-backend.h>
+@@ -973,21 +974,32 @@ dri2_wl_get_capability(void *loaderPrivate, enum dri_loader_cap cap)
+    }
+ }
+ 
++static int
++dri2_wl_get_display_fd(void *loaderPrivate)
++{
++   _EGLDisplay *disp = loaderPrivate;
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   return dri2_dpy->fd_dpy;
++}
++
+ static const __DRIdri2LoaderExtension dri2_loader_extension = {
+-   .base = { __DRI_DRI2_LOADER, 4 },
++   .base = { __DRI_DRI2_LOADER, 6 },
+ 
+    .getBuffers           = dri2_wl_get_buffers,
+    .flushFrontBuffer     = dri2_wl_flush_front_buffer,
+    .getBuffersWithFormat = dri2_wl_get_buffers_with_format,
+    .getCapability        = dri2_wl_get_capability,
++   .getDisplayFD         = dri2_wl_get_display_fd,
+ };
+ 
+ static const __DRIimageLoaderExtension image_loader_extension = {
+-   .base = { __DRI_IMAGE_LOADER, 2 },
++   .base = { __DRI_IMAGE_LOADER, 5 },
+ 
+    .getBuffers          = image_get_buffers,
+    .flushFrontBuffer    = dri2_wl_flush_front_buffer,
+    .getCapability       = dri2_wl_get_capability,
++   .getDisplayFD        = dri2_wl_get_display_fd,
+ };
+ 
+ static void
+@@ -1640,12 +1652,14 @@ dri2_initialize_wayland_drm(_EGLDisplay *disp)
+ {
+    _EGLDevice *dev;
+    struct dri2_egl_display *dri2_dpy;
++   int fd_old;
+ 
+    dri2_dpy = calloc(1, sizeof *dri2_dpy);
+    if (!dri2_dpy)
+       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+    disp->DriverData = (void *) dri2_dpy;
+    if (disp->PlatformDisplay == NULL) {
+       dri2_dpy->wl_dpy = wl_display_connect(NULL);
+@@ -1690,8 +1704,20 @@ dri2_initialize_wayland_drm(_EGLDisplay *disp)
+        (roundtrip(dri2_dpy) < 0 || !dri2_dpy->authenticated))
+       goto cleanup;
+ 
++   fd_old = dri2_dpy->fd;
++   dri2_dpy->fd_dpy = os_dupfd_cloexec(dri2_dpy->fd);
+    dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd,
+                                                &dri2_dpy->is_different_gpu);
++   if (dri2_dpy->fd == fd_old) {
++      if (dri2_dpy->fd_dpy != -1)
++         close(dri2_dpy->fd_dpy);
++
++      dri2_dpy->fd_dpy = dri2_dpy->fd;
++   } else if (dri2_dpy->fd_dpy == -1) {
++      _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to dup display FD");
++      goto cleanup;
++   }
++
+    dev = _eglAddDevice(dri2_dpy->fd, false);
+    if (!dev) {
+       _eglError(EGL_NOT_INITIALIZED, "DRI2: failed to find EGLDevice");
+@@ -2236,6 +2262,7 @@ dri2_initialize_wayland_swrast(_EGLDisplay *disp)
+       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+    disp->DriverData = (void *) dri2_dpy;
+    if (disp->PlatformDisplay == NULL) {
+       dri2_dpy->wl_dpy = wl_display_connect(NULL);
+diff --git a/src/egl/drivers/dri2/platform_x11.c b/src/egl/drivers/dri2/platform_x11.c
+index 5ffdf13..5cf3ce2 100644
+--- a/src/egl/drivers/dri2/platform_x11.c
++++ b/src/egl/drivers/dri2/platform_x11.c
+@@ -1277,6 +1277,7 @@ dri2_initialize_x11_swrast(_EGLDisplay *disp)
+       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+    if (!dri2_get_xcb_connection(disp, dri2_dpy))
+       goto cleanup;
+ 
+@@ -1364,6 +1365,7 @@ dri2_initialize_x11_dri3(_EGLDisplay *disp)
+       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+    if (!dri2_get_xcb_connection(disp, dri2_dpy))
+       goto cleanup;
+ 
+@@ -1472,6 +1474,7 @@ dri2_initialize_x11_dri2(_EGLDisplay *disp)
+       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+    if (!dri2_get_xcb_connection(disp, dri2_dpy))
+       goto cleanup;
+ 
+diff --git a/src/egl/drivers/dri2/platform_x11_dri3.c b/src/egl/drivers/dri2/platform_x11_dri3.c
+index e117105..0babf9f 100644
+--- a/src/egl/drivers/dri2/platform_x11_dri3.c
++++ b/src/egl/drivers/dri2/platform_x11_dri3.c
+@@ -32,6 +32,7 @@
+ 
+ #include <xf86drm.h>
+ #include "util/macros.h"
++#include "util/os_file.h"
+ 
+ #include "egl_dri2.h"
+ #include "platform_x11_dri3.h"
+@@ -414,11 +415,21 @@ dri3_flush_front_buffer(__DRIdrawable *driDrawable, void *loaderPrivate)
+       _eglLog(_EGL_WARNING, "FIXME: egl/x11 doesn't support front buffer rendering.");
+ }
+ 
++static int
++dri3_get_display_fd(void *loaderPrivate)
++{
++   _EGLDisplay *disp = loaderPrivate;
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   return dri2_dpy->fd_dpy;
++}
++
+ const __DRIimageLoaderExtension dri3_image_loader_extension = {
+-   .base = { __DRI_IMAGE_LOADER, 1 },
++   .base = { __DRI_IMAGE_LOADER, 5 },
+ 
+    .getBuffers          = loader_dri3_get_buffers,
+    .flushFrontBuffer    = dri3_flush_front_buffer,
++   .getDisplayFD        = dri3_get_display_fd,
+ };
+ 
+ static EGLBoolean
+@@ -537,6 +548,7 @@ dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
+    xcb_xfixes_query_version_cookie_t xfixes_query_cookie;
+    xcb_generic_error_t *error;
+    const xcb_query_extension_reply_t *extension;
++   int fd_old;
+ 
+    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_dri3_id);
+    xcb_prefetch_extension_data (dri2_dpy->conn, &xcb_present_id);
+@@ -616,12 +628,25 @@ dri3_x11_connect(struct dri2_egl_display *dri2_dpy)
+       return EGL_FALSE;
+    }
+ 
++   fd_old = dri2_dpy->fd;
++   dri2_dpy->fd_dpy = os_dupfd_cloexec(dri2_dpy->fd);
+    dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd, &dri2_dpy->is_different_gpu);
++   if (dri2_dpy->fd == fd_old) {
++      if (dri2_dpy->fd_dpy != -1)
++         close(dri2_dpy->fd_dpy);
++
++      dri2_dpy->fd_dpy = dri2_dpy->fd;
++   } else if (dri2_dpy->fd_dpy == -1) {
++      _eglLog(_EGL_WARNING, "DRI3: failed to dup display FD");
++      close(dri2_dpy->fd);
++      return EGL_FALSE;
++   }
+ 
+    dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
+    if (!dri2_dpy->driver_name) {
+       _eglLog(_EGL_WARNING, "DRI3: No driver found");
+       close(dri2_dpy->fd);
++      close(dri2_dpy->fd_dpy);
+       return EGL_FALSE;
+    }
+ 
+diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
+index db1b079..1ed6b60 100644
+--- a/src/glx/dri3_glx.c
++++ b/src/glx/dri3_glx.c
+@@ -77,6 +77,7 @@
+ #include "dri3_priv.h"
+ #include "loader.h"
+ #include "dri2.h"
++#include "util/os_file.h"
+ 
+ static struct dri3_drawable *
+ loader_drawable_to_dri3_drawable(struct loader_dri3_drawable *draw) {
+@@ -529,6 +530,14 @@ dri3_flush_swap_buffers(__DRIdrawable *driDrawable, void *loaderPrivate)
+    loader_dri3_swapbuffer_barrier(draw);
+ }
+ 
++static int
++dri3_get_display_fd(void *loaderPrivate)
++{
++   struct dri3_screen *psc = (struct dri3_screen *)loaderPrivate;
++
++   return psc->fd_dpy;
++}
++
+ static void
+ dri_set_background_context(void *loaderPrivate)
+ {
+@@ -548,11 +557,12 @@ dri_is_thread_safe(void *loaderPrivate)
+ /* The image loader extension record for DRI3
+  */
+ static const __DRIimageLoaderExtension imageLoaderExtension = {
+-   .base = { __DRI_IMAGE_LOADER, 3 },
++   .base = { __DRI_IMAGE_LOADER, 5 },
+ 
+    .getBuffers          = loader_dri3_get_buffers,
+    .flushFrontBuffer    = dri3_flush_front_buffer,
+    .flushSwapBuffers    = dri3_flush_swap_buffers,
++   .getDisplayFD        = dri3_get_display_fd,
+ };
+ 
+ const __DRIuseInvalidateExtension dri3UseInvalidate = {
+@@ -618,6 +628,10 @@ dri3_destroy_screen(struct glx_screen *base)
+    loader_dri3_close_screen(psc->driScreen);
+    (*psc->core->destroyScreen) (psc->driScreen);
+    driDestroyConfigs(psc->driver_configs);
++
++   if (psc->fd_dpy != psc->fd)
++      close(psc->fd_dpy);
++
+    close(psc->fd);
+    free(psc);
+ }
+@@ -842,8 +856,9 @@ dri3_create_screen(int screen, struct glx_display * priv)
+    struct dri3_screen *psc;
+    __GLXDRIscreen *psp;
+    struct glx_config *configs = NULL, *visuals = NULL;
+-   char *driverName, *driverNameDisplayGPU, *tmp;
++   char *driverName = NULL, *driverNameDisplayGPU, *tmp;
+    int i;
++   int fd_old;
+ 
+    psc = calloc(1, sizeof *psc);
+    if (psc == NULL)
+@@ -851,6 +866,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
+ 
+    psc->fd = -1;
+    psc->fd_display_gpu = -1;
++   psc->fd_dpy = -1;
+ 
+    if (!glx_screen_init(&psc->base, screen, priv)) {
+       free(psc);
+@@ -871,12 +887,23 @@ dri3_create_screen(int screen, struct glx_display * priv)
+       return NULL;
+    }
+ 
++   fd_old = psc->fd;
++   psc->fd_dpy = os_dupfd_cloexec(psc->fd);
+    psc->fd_display_gpu = fcntl(psc->fd, F_DUPFD_CLOEXEC, 3);
+    psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu);
+    if (!psc->is_different_gpu) {
+       close(psc->fd_display_gpu);
+       psc->fd_display_gpu = -1;
+    }
++   if (psc->fd == fd_old) {
++      if (psc->fd_dpy != -1)
++         close(psc->fd_dpy);
++
++      psc->fd_dpy = psc->fd;
++   } else if (psc->fd_dpy == -1) {
++         ErrorMessageF("Unable to dup the display FD");
++         goto handle_error;
++   }
+ 
+    driverName = loader_get_driver_for_fd(psc->fd);
+    if (!driverName) {
+@@ -1049,6 +1076,8 @@ handle_error:
+    if (psc->driScreenDisplayGPU)
+        psc->core->destroyScreen(psc->driScreenDisplayGPU);
+    psc->driScreenDisplayGPU = NULL;
++   if (psc->fd_dpy >= 0 && psc->fd_dpy != psc->fd)
++      close(psc->fd_dpy);
+    if (psc->fd >= 0)
+       close(psc->fd);
+    if (psc->fd_display_gpu >= 0)
+diff --git a/src/glx/dri3_priv.h b/src/glx/dri3_priv.h
+index c0e833c..b3dccf2 100644
+--- a/src/glx/dri3_priv.h
++++ b/src/glx/dri3_priv.h
+@@ -107,6 +107,7 @@ struct dri3_screen {
+ 
+    void *driver;
+    int fd;
++   int fd_dpy;
+    bool is_different_gpu;
+ 
+    /* fd for display GPU in case of prime */

+ 54 - 0
recipes-graphics/mesa/mesa-pvr/0051-egl-wayland-a-linear-buffer-is-not-needed-with-DRM-f.patch

@@ -0,0 +1,54 @@
+From 1777578c96aba9bac85508d2fc6d6b38407cf9b9 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Wed, 28 Apr 2021 10:57:15 +0100
+Subject: [PATCH] egl/wayland: a linear buffer is not needed with DRM format
+ modifiers
+
+If the compositor supports DRM format modifiers, there is no
+need for an additional linear buffer, as the client can allocate
+buffers with attributes known to the compositor.
+
+---
+ src/egl/drivers/dri2/platform_wayland.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
+index b393e05..60683fa 100644
+--- a/src/egl/drivers/dri2/platform_wayland.c
++++ b/src/egl/drivers/dri2/platform_wayland.c
+@@ -695,7 +695,7 @@ get_back_bo(struct dri2_egl_surface *dri2_surf)
+       use_flags |= __DRI_IMAGE_USE_PROTECTED;
+    }
+ 
+-   if (dri2_dpy->is_different_gpu &&
++   if (dri2_dpy->is_different_gpu && !num_modifiers &&
+        dri2_surf->back->linear_copy == NULL) {
+       /* The LINEAR modifier should be a perfect alias of the LINEAR use
+        * flag; try the new interface first before the old, then fall back. */
+@@ -811,7 +811,7 @@ update_buffers(struct dri2_egl_display *dri2_dpy,
+           dri2_surf->color_buffers[i].wl_buffer) {
+          wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
+          dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+-         if (dri2_dpy->is_different_gpu)
++         if (dri2_surf->color_buffers[i].linear_copy)
+             dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].linear_copy);
+          dri2_surf->color_buffers[i].wl_buffer = NULL;
+          dri2_surf->color_buffers[i].dri_image = NULL;
+@@ -1247,7 +1247,7 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+    if (!dri2_surf->current->wl_buffer) {
+       __DRIimage *image;
+ 
+-      if (dri2_dpy->is_different_gpu)
++      if (dri2_surf->current->linear_copy)
+          image = dri2_surf->current->linear_copy;
+       else
+          image = dri2_surf->current->dri_image;
+@@ -1281,7 +1281,7 @@ dri2_wl_swap_buffers_with_damage(_EGLDisplay *disp,
+ 
+    dri2_flush_drawable_for_swapbuffers(disp, draw);
+ 
+-   if (dri2_dpy->is_different_gpu) {
++   if (dri2_surf->current->linear_copy) {
+       _EGLContext *ctx = _eglGetCurrentContext();
+       struct dri2_egl_context *dri2_ctx = dri2_egl_context(ctx);
+       dri2_dpy->image->blitImage(dri2_ctx->dri_context,

+ 300 - 0
recipes-graphics/mesa/mesa-pvr/0052-dri3-a-linear-buffer-is-not-needed-with-DRM-format-m.patch

@@ -0,0 +1,300 @@
+From a10f534a4b8cc1c07532628b35874cf54a1bfd89 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Wed, 28 Apr 2021 16:33:42 +0100
+Subject: [PATCH] dri3: a linear buffer is not needed with DRM format modifiers
+
+If the X Server supports DRM format modifiers, there is no need
+for an additional linear buffer, as the client can allocate buffers
+with attributes known to the Server.
+
+---
+ src/egl/drivers/dri2/platform_x11_dri3.c |   7 +-
+ src/glx/dri3_glx.c                       | 137 +++++++++++++++--------
+ src/loader/loader_dri3_helper.c          |  15 +++
+ src/loader/loader_dri3_helper.h          |   4 +
+ 4 files changed, 113 insertions(+), 50 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_x11_dri3.c b/src/egl/drivers/dri2/platform_x11_dri3.c
+index 0babf9f..94205a4 100644
+--- a/src/egl/drivers/dri2/platform_x11_dri3.c
++++ b/src/egl/drivers/dri2/platform_x11_dri3.c
+@@ -143,6 +143,7 @@ dri3_create_surface(_EGLDisplay *disp, EGLint type, _EGLConfig *conf,
+    struct dri3_egl_surface *dri3_surf;
+    const __DRIconfig *dri_config;
+    xcb_drawable_t drawable;
++   bool is_incompat_gpu;
+ 
+    dri3_surf = calloc(1, sizeof *dri3_surf);
+    if (!dri3_surf) {
+@@ -150,6 +151,10 @@ dri3_create_surface(_EGLDisplay *disp, EGLint type, _EGLConfig *conf,
+       return NULL;
+    }
+ 
++   is_incompat_gpu = dri2_dpy->is_different_gpu &&
++                     !loader_dri3_has_modifiers(dri2_dpy->multibuffers_available,
++                                                dri2_dpy->image);
++
+    if (!dri2_init_surface(&dri3_surf->surf.base, disp, type, conf,
+                           attrib_list, false, native_surface))
+       goto cleanup_surf;
+@@ -174,7 +179,7 @@ dri3_create_surface(_EGLDisplay *disp, EGLint type, _EGLConfig *conf,
+ 
+    if (loader_dri3_drawable_init(dri2_dpy->conn, drawable,
+                                  dri2_dpy->dri_screen,
+-                                 dri2_dpy->is_different_gpu,
++                                 is_incompat_gpu,
+                                  dri2_dpy->multibuffers_available,
+                                  dri_config,
+                                  &dri2_dpy->loader_dri3_ext,
+diff --git a/src/glx/dri3_glx.c b/src/glx/dri3_glx.c
+index 1ed6b60..025ad01 100644
+--- a/src/glx/dri3_glx.c
++++ b/src/glx/dri3_glx.c
+@@ -354,6 +354,21 @@ dri3_destroy_drawable(__GLXDRIdrawable *base)
+    free(pdraw);
+ }
+ 
++static bool
++dri3_has_multibuffer(const __DRIimageExtension *image,
++                     const struct dri3_display *pdp)
++{
++#ifdef HAVE_DRI3_MODIFIERS
++   return (image && image->base.version >= 15) &&
++          (pdp->dri3Major > 1 ||
++             (pdp->dri3Major == 1 && pdp->dri3Minor >= 2)) &&
++          (pdp->presentMajor > 1 ||
++             (pdp->presentMajor == 1 && pdp->presentMinor >= 2));
++#else
++   return false;
++#endif
++}
++
+ static __GLXDRIdrawable *
+ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
+                      GLXDrawable drawable, struct glx_config *config_base)
+@@ -361,11 +376,9 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
+    struct dri3_drawable *pdraw;
+    struct dri3_screen *psc = (struct dri3_screen *) base;
+    __GLXDRIconfigPrivate *config = (__GLXDRIconfigPrivate *) config_base;
+-   bool has_multibuffer = false;
+-#ifdef HAVE_DRI3_MODIFIERS
+    const struct dri3_display *const pdp = (struct dri3_display *)
+       base->display->dri3Display;
+-#endif
++   bool has_multibuffer = dri3_has_multibuffer(psc->image, pdp);
+ 
+    pdraw = calloc(1, sizeof(*pdraw));
+    if (!pdraw)
+@@ -376,14 +389,6 @@ dri3_create_drawable(struct glx_screen *base, XID xDrawable,
+    pdraw->base.drawable = drawable;
+    pdraw->base.psc = &psc->base;
+ 
+-#ifdef HAVE_DRI3_MODIFIERS
+-   if ((psc->image && psc->image->base.version >= 15) &&
+-       (pdp->dri3Major > 1 || (pdp->dri3Major == 1 && pdp->dri3Minor >= 2)) &&
+-       (pdp->presentMajor > 1 ||
+-        (pdp->presentMajor == 1 && pdp->presentMinor >= 2)))
+-      has_multibuffer = true;
+-#endif
+-
+    (void) __glXInitialize(psc->base.dpy);
+ 
+    if (loader_dri3_drawable_init(XGetXCBConnection(base->dpy),
+@@ -737,13 +742,14 @@ static const struct glx_context_vtable dri3_context_vtable = {
+    .interop_export_object = dri3_interop_export_object
+ };
+ 
+-/** dri3_bind_extensions
++/** dri3_bind_extensions_part1
+  *
+- * Enable all of the extensions supported on DRI3
++ * Enable the extensions supported on DRI3 that don't depend on
++ * whether we are using a different GPU.
+  */
+ static void
+-dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
+-                     const char *driverName)
++dri3_bind_extensions_part1(struct dri3_screen *psc, struct glx_display * priv,
++                           const char *driverName)
+ {
+    const __DRIextension **extensions;
+    unsigned mask;
+@@ -774,16 +780,6 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
+    }
+ 
+    for (i = 0; extensions[i]; i++) {
+-      /* when on a different gpu than the server, the server pixmaps
+-       * can have a tiling mode we can't read. Thus we can't create
+-       * a texture from them.
+-       */
+-      if (!psc->is_different_gpu &&
+-         (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
+-         psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
+-         __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
+-      }
+-
+       if ((strcmp(extensions[i]->name, __DRI2_FLUSH) == 0)) {
+          psc->f = (__DRI2flushExtension *) extensions[i];
+          /* internal driver extension, no GL extension exposed */
+@@ -817,6 +813,33 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,
+    }
+ }
+ 
++/** dri3_bind_extensions_part2
++ *
++ * Enable the extensions supported on DRI3 that depend on whether we
++ * are using a different GPU.
++ */
++static void
++dri3_bind_extensions_part2(struct dri3_screen *psc, struct glx_display * priv,
++                           const char *driverName)
++{
++   const __DRIextension **extensions;
++   int i;
++
++   extensions = psc->core->getExtensions(psc->driScreen);
++
++   for (i = 0; extensions[i]; i++) {
++      /* when on a different gpu than the server, the server pixmaps
++       * can have a tiling mode we can't read. Thus we can't create
++       * a texture from them.
++       */
++      if (!psc->is_different_gpu &&
++         (strcmp(extensions[i]->name, __DRI_TEX_BUFFER) == 0)) {
++         psc->texBuffer = (__DRItexBufferExtension *) extensions[i];
++         __glXEnableDirectExtension(&psc->base, "GLX_EXT_texture_from_pixmap");
++      }
++   }
++}
++
+ static char *
+ dri3_get_driver_name(struct glx_screen *glx_screen)
+ {
+@@ -859,6 +882,8 @@ dri3_create_screen(int screen, struct glx_display * priv)
+    char *driverName = NULL, *driverNameDisplayGPU, *tmp;
+    int i;
+    int fd_old;
++   bool is_different_gpu;
++   bool have_modifiers;
+ 
+    psc = calloc(1, sizeof *psc);
+    if (psc == NULL)
+@@ -890,8 +915,8 @@ dri3_create_screen(int screen, struct glx_display * priv)
+    fd_old = psc->fd;
+    psc->fd_dpy = os_dupfd_cloexec(psc->fd);
+    psc->fd_display_gpu = fcntl(psc->fd, F_DUPFD_CLOEXEC, 3);
+-   psc->fd = loader_get_user_preferred_fd(psc->fd, &psc->is_different_gpu);
+-   if (!psc->is_different_gpu) {
++   psc->fd = loader_get_user_preferred_fd(psc->fd, &is_different_gpu);
++   if (!is_different_gpu) {
+       close(psc->fd_display_gpu);
+       psc->fd_display_gpu = -1;
+    }
+@@ -933,27 +958,6 @@ dri3_create_screen(int screen, struct glx_display * priv)
+       goto handle_error;
+    }
+ 
+-   if (psc->is_different_gpu) {
+-      driverNameDisplayGPU = loader_get_driver_for_fd(psc->fd_display_gpu);
+-      if (driverNameDisplayGPU) {
+-
+-         /* check if driver name is matching so that non mesa drivers
+-          * will not crash. Also need this check since image extension
+-          * pointer from render gpu is shared with display gpu. Image
+-          * extension pointer is shared because it keeps things simple.
+-          */
+-         if (strcmp(driverName, driverNameDisplayGPU) == 0) {
+-            psc->driScreenDisplayGPU =
+-               psc->image_driver->createNewScreen2(screen, psc->fd_display_gpu,
+-                                                   pdp->loader_extensions,
+-                                                   extensions,
+-                                                   &driver_configs, psc);
+-         }
+-
+-         free(driverNameDisplayGPU);
+-      }
+-   }
+-
+    psc->driScreen =
+       psc->image_driver->createNewScreen2(screen, psc->fd,
+                                           pdp->loader_extensions,
+@@ -965,7 +969,42 @@ dri3_create_screen(int screen, struct glx_display * priv)
+       goto handle_error;
+    }
+ 
+-   dri3_bind_extensions(psc, priv, driverName);
++   dri3_bind_extensions_part1(psc, priv, driverName);
++
++   have_modifiers = loader_dri3_has_modifiers(dri3_has_multibuffer(psc->image,
++                                                                   pdp),
++                                              psc->image);
++
++   if (is_different_gpu) {
++      if (have_modifiers) {
++         close(psc->fd_display_gpu);
++         psc->fd_display_gpu = -1;
++      } else {
++         driverNameDisplayGPU = loader_get_driver_for_fd(psc->fd_display_gpu);
++         if (driverNameDisplayGPU) {
++
++            /* check if driver name is matching so that non mesa drivers
++             * will not crash. Also need this check since image extension
++             * pointer from render gpu is shared with display gpu. Image
++             * extension pointer is shared because it keeps things simple.
++             */
++            if (strcmp(driverName, driverNameDisplayGPU) == 0) {
++               psc->driScreenDisplayGPU =
++                  psc->image_driver->createNewScreen2(screen,
++                                                      psc->fd_display_gpu,
++                                                      pdp->loader_extensions,
++                                                      extensions,
++                                                      &driver_configs, psc);
++            }
++
++            free(driverNameDisplayGPU);
++         }
++      }
++   }
++
++   psc->is_different_gpu = is_different_gpu && !have_modifiers;
++
++   dri3_bind_extensions_part2(psc, priv, driverName);
+ 
+    if (!psc->image || psc->image->base.version < 7 || !psc->image->createImageFromFds) {
+       ErrorMessageF("Version 7 or imageFromFds image extension not found\n");
+diff --git a/src/loader/loader_dri3_helper.c b/src/loader/loader_dri3_helper.c
+index ff6d1ff..5c0379a 100644
+--- a/src/loader/loader_dri3_helper.c
++++ b/src/loader/loader_dri3_helper.c
+@@ -2288,3 +2288,18 @@ dri3_find_back_alloc(struct loader_dri3_drawable *draw)
+ 
+    return back;
+ }
++
++bool
++loader_dri3_has_modifiers(bool multiplanes_available,
++                          const __DRIimageExtension *image)
++{
++#ifdef HAVE_DRI3_MODIFIERS
++   return multiplanes_available && image &&
++          image->base.version >= 15 &&
++          image->queryDmaBufModifiers &&
++          image->createImageWithModifiers &&
++          image->createImageFromDmaBufs2;
++#else
++   return false;
++#endif
++}
+diff --git a/src/loader/loader_dri3_helper.h b/src/loader/loader_dri3_helper.h
+index 028e25d..5790f54 100644
+--- a/src/loader/loader_dri3_helper.h
++++ b/src/loader/loader_dri3_helper.h
+@@ -287,4 +287,8 @@ loader_dri3_swapbuffer_barrier(struct loader_dri3_drawable *draw);
+ 
+ void
+ loader_dri3_close_screen(__DRIscreen *dri_screen);
++
++bool
++loader_dri3_has_modifiers(bool multiplanes_available,
++                          const __DRIimageExtension *image);
+ #endif

+ 265 - 0
recipes-graphics/mesa/mesa-pvr/0053-egl-drm-add-support-for-DRI_PRIME-GPU-selection.patch

@@ -0,0 +1,265 @@
+From f92304242d7656cf296e978eb9e4a5fc09050b75 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 20 May 2021 14:43:29 +0100
+Subject: [PATCH] egl/drm: add support for DRI_PRIME GPU selection
+
+Add support for selecting the GPU to be used for rendering using
+the DRI_PRIME environment variable. If a different GPU is selected,
+a duplicate of the file descriptor for the original GPU/display is
+preserved, which can be obtained by calling the getDisplayFD
+function in the image loader extension.
+
+For server side Wayland, the ability to support PRIME is
+determined by checking for the PRIME import and export
+capabilities on the driver file descriptor, which may no
+longer support them if a different GPU from the default has
+been selected. It may be that the driver can still support
+PRIME; for example, by making use of the original (default)
+file descriptor. The driver can indicate it supports PRIME
+via the getCapabilities function in the DRI Image extension.
+
+---
+ include/GL/internal/dri_interface.h |  2 ++
+ src/egl/drivers/dri2/egl_dri2.c     | 10 ++++++++
+ src/egl/drivers/dri2/platform_drm.c | 19 ++++++++++-----
+ src/gbm/backends/dri/gbm_dri.c      | 38 +++++++++++++++++++++++++----
+ src/gbm/backends/dri/gbm_driint.h   |  8 ++++++
+ 5 files changed, 66 insertions(+), 11 deletions(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 2fb440f..080d191 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1548,6 +1548,8 @@ enum __DRIChromaSiting {
+  */
+ /*@{*/
+ #define __DRI_IMAGE_CAP_GLOBAL_NAMES 1
++#define __DRI_IMAGE_CAP_PRIME_IMPORT 0x2000
++#define __DRI_IMAGE_CAP_PRIME_EXPORT 0x4000
+ /*@}*/
+ 
+ /**
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index 1df53ef..ec4ac60 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1368,6 +1368,7 @@ dri2_display_destroy(_EGLDisplay *disp)
+    }
+ 
+    switch (disp->Platform) {
++   case _EGL_PLATFORM_DRM:
+    case _EGL_PLATFORM_WAYLAND:
+    case _EGL_PLATFORM_X11:
+       if (dri2_dpy->fd_dpy >= 0 && dri2_dpy->fd_dpy != dri2_dpy->fd)
+@@ -3943,6 +3944,15 @@ dri2_bind_wayland_display_wl(_EGLDisplay *disp, struct wl_display *wl_dpy)
+        dri2_dpy->image->base.version >= 7 &&
+        dri2_dpy->image->createImageFromFds != NULL)
+       flags |= WAYLAND_DRM_PRIME;
++   else if (dri2_dpy->image->base.version >= 10 &&
++            dri2_dpy->image->getCapabilities != NULL) {
++         int capabilities;
++
++         capabilities = dri2_dpy->image->getCapabilities(dri2_dpy->dri_screen);
++         if ((capabilities & __DRI_IMAGE_CAP_PRIME_IMPORT) != 0 &&
++             (capabilities & __DRI_IMAGE_CAP_PRIME_EXPORT) != 0)
++            flags |= WAYLAND_DRM_PRIME;
++   }
+ 
+    dri2_dpy->wl_server_drm =
+            wayland_drm_init(wl_dpy, device_name,
+diff --git a/src/egl/drivers/dri2/platform_drm.c b/src/egl/drivers/dri2/platform_drm.c
+index 258e8b6..f160ad1 100644
+--- a/src/egl/drivers/dri2/platform_drm.c
++++ b/src/egl/drivers/dri2/platform_drm.c
+@@ -595,7 +595,7 @@ dri2_drm_authenticate(_EGLDisplay *disp, uint32_t id)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ 
+-   return drmAuthMagic(dri2_dpy->fd, id);
++   return drmAuthMagic(dri2_dpy->fd_dpy, id);
+ }
+ 
+ static void
+@@ -782,6 +782,7 @@ dri2_initialize_drm(_EGLDisplay *disp)
+       return _eglError(EGL_BAD_ALLOC, "eglInitialize");
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+    disp->DriverData = (void *) dri2_dpy;
+ 
+    gbm = disp->PlatformDisplay;
+@@ -789,16 +790,16 @@ dri2_initialize_drm(_EGLDisplay *disp)
+       char buf[64];
+       int n = snprintf(buf, sizeof(buf), DRM_DEV_NAME, DRM_DIR_NAME, 0);
+       if (n != -1 && n < sizeof(buf))
+-         dri2_dpy->fd = loader_open_device(buf);
+-      gbm = gbm_create_device(dri2_dpy->fd);
++         dri2_dpy->fd_dpy = loader_open_device(buf);
++      gbm = gbm_create_device(dri2_dpy->fd_dpy);
+       if (gbm == NULL) {
+          err = "DRI2: failed to create gbm device";
+          goto cleanup;
+       }
+       dri2_dpy->own_device = true;
+    } else {
+-      dri2_dpy->fd = os_dupfd_cloexec(gbm_device_get_fd(gbm));
+-      if (dri2_dpy->fd < 0) {
++      dri2_dpy->fd_dpy = os_dupfd_cloexec(gbm_device_get_fd(gbm));
++      if (dri2_dpy->fd_dpy < 0) {
+          err = "DRI2: failed to fcntl() existing gbm device";
+          goto cleanup;
+       }
+@@ -810,6 +811,12 @@ dri2_initialize_drm(_EGLDisplay *disp)
+       goto cleanup;
+    }
+ 
++   if (gbm_dri_device_get_fd(dri2_dpy->gbm_dri) ==
++       gbm_device_get_fd(gbm))
++      dri2_dpy->fd = dri2_dpy->fd_dpy;
++   else
++      dri2_dpy->fd = os_dupfd_cloexec(gbm_dri_device_get_fd(dri2_dpy->gbm_dri));
++
+    dev = _eglAddDevice(dri2_dpy->fd, dri2_dpy->gbm_dri->software);
+    if (!dev) {
+       err = "DRI2: failed to find EGLDevice";
+@@ -872,7 +879,7 @@ dri2_initialize_drm(_EGLDisplay *disp)
+       disp->Extensions.EXT_buffer_age = EGL_TRUE;
+ 
+ #ifdef HAVE_WAYLAND_PLATFORM
+-   dri2_dpy->device_name = loader_get_device_name_for_fd(dri2_dpy->fd);
++   dri2_dpy->device_name = loader_get_device_name_for_fd(dri2_dpy->fd_dpy);
+ #endif
+    dri2_set_WL_bind_wayland_display(disp);
+ 
+diff --git a/src/gbm/backends/dri/gbm_dri.c b/src/gbm/backends/dri/gbm_dri.c
+index 5acb15b..c1586f4 100644
+--- a/src/gbm/backends/dri/gbm_dri.c
++++ b/src/gbm/backends/dri/gbm_dri.c
+@@ -51,6 +51,7 @@
+ #include "loader.h"
+ #include "util/debug.h"
+ #include "util/macros.h"
++#include "util/os_file.h"
+ 
+ /* For importing wl_buffer */
+ #if HAVE_WAYLAND_PLATFORM
+@@ -141,6 +142,14 @@ image_get_buffers(__DRIdrawable *driDrawable,
+                                  surf->dri_private, buffer_mask, buffers);
+ }
+ 
++static int
++dri_get_display_fd(void *loaderPrivate)
++{
++   struct gbm_dri_device *dri = loaderPrivate;
++
++   return dri->base.v0.fd;
++}
++
+ static void
+ swrast_get_drawable_info(__DRIdrawable *driDrawable,
+                          int           *x,
+@@ -220,20 +229,22 @@ static const __DRIimageLookupExtension image_lookup_extension = {
+ };
+ 
+ static const __DRIdri2LoaderExtension dri2_loader_extension = {
+-   .base = { __DRI_DRI2_LOADER, 4 },
++   .base = { __DRI_DRI2_LOADER, 6 },
+ 
+    .getBuffers              = dri_get_buffers,
+    .flushFrontBuffer        = dri_flush_front_buffer,
+    .getBuffersWithFormat    = dri_get_buffers_with_format,
+    .getCapability           = dri_get_capability,
++   .getDisplayFD            = dri_get_display_fd,
+ };
+ 
+ static const __DRIimageLoaderExtension image_loader_extension = {
+-   .base = { __DRI_IMAGE_LOADER, 2 },
++   .base = { __DRI_IMAGE_LOADER, 5 },
+ 
+    .getBuffers          = image_get_buffers,
+    .flushFrontBuffer    = dri_flush_front_buffer,
+    .getCapability       = dri_get_capability,
++   .getDisplayFD        = dri_get_display_fd,
+ };
+ 
+ static const __DRIswrastLoaderExtension swrast_loader_extension = {
+@@ -398,12 +409,12 @@ dri_screen_create_dri2(struct gbm_dri_device *dri, char *driver_name)
+       return -1;
+ 
+    if (dri->dri2->base.version >= 4) {
+-      dri->screen = dri->dri2->createNewScreen2(0, dri->base.v0.fd,
++      dri->screen = dri->dri2->createNewScreen2(0, dri->fd,
+                                                 dri->loader_extensions,
+                                                 dri->driver_extensions,
+                                                 &dri->driver_configs, dri);
+    } else {
+-      dri->screen = dri->dri2->createNewScreen(0, dri->base.v0.fd,
++      dri->screen = dri->dri2->createNewScreen(0, dri->fd,
+                                                dri->loader_extensions,
+                                                &dri->driver_configs, dri);
+    }
+@@ -470,8 +481,20 @@ static int
+ dri_screen_create(struct gbm_dri_device *dri)
+ {
+    char *driver_name;
++   int dup_fd, new_fd;
++   bool is_different_gpu;
+ 
+-   driver_name = loader_get_driver_for_fd(dri->base.v0.fd);
++   dup_fd = os_dupfd_cloexec(dri->fd);
++   if (dup_fd < 0)
++      return -1;
++
++   new_fd = loader_get_user_preferred_fd(dup_fd, &is_different_gpu);
++   if (new_fd == dup_fd)
++      close(new_fd);
++   else
++      dri->fd = new_fd;
++
++   driver_name = loader_get_driver_for_fd(dri->fd);
+    if (!driver_name)
+       return -1;
+ 
+@@ -1424,6 +1447,9 @@ dri_destroy(struct gbm_device *gbm)
+    dlclose(dri->driver);
+    free(dri->driver_name);
+ 
++   if (dri->fd >= 0 && dri->fd != dri->base.v0.fd)
++      close (dri->fd);
++
+    free(dri);
+ }
+ 
+@@ -1445,6 +1471,8 @@ dri_device_create(int fd, uint32_t gbm_backend_version)
+    if (!dri)
+       return NULL;
+ 
++   dri->fd = fd;
++
+    dri->base.v0.fd = fd;
+    dri->base.v0.backend_version = gbm_backend_version;
+    dri->base.v0.bo_create = gbm_dri_bo_create;
+diff --git a/src/gbm/backends/dri/gbm_driint.h b/src/gbm/backends/dri/gbm_driint.h
+index 9e77ba5..e33a96e 100644
+--- a/src/gbm/backends/dri/gbm_driint.h
++++ b/src/gbm/backends/dri/gbm_driint.h
+@@ -61,6 +61,8 @@ struct gbm_dri_visual {
+ struct gbm_dri_device {
+    struct gbm_device base;
+ 
++   int fd;
++
+    void *driver;
+    char *driver_name; /* Name of the DRI module, without the _dri suffix */
+    bool software; /* A software driver was loaded */
+@@ -191,4 +193,10 @@ gbm_dri_bo_unmap_dumb(struct gbm_dri_bo *bo)
+    bo->map = NULL;
+ }
+ 
++static inline int
++gbm_dri_device_get_fd(struct gbm_dri_device *dri)
++{
++   return dri->fd;
++}
++
+ #endif

+ 302 - 0
recipes-graphics/mesa/mesa-pvr/0054-egl-null-add-support-for-DRI_PRIME-GPU-selection.patch

@@ -0,0 +1,302 @@
+From e92a09b72b44a0969983635803022f7dc1a50924 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 20 May 2021 20:16:18 +0100
+Subject: [PATCH] egl/null: add support for DRI_PRIME GPU selection
+
+Add support for selecting the GPU to be used for rendering using
+the DRI_PRIME environment variable. If a different GPU is selected,
+a duplicate of the file descriptor for the original GPU/display is
+preserved, which can be obtained by calling the getDisplayFD
+function in the image loader extension.
+
+This change includes code, in function dri2_null_try_device, to
+skip display devices that are not supported by the PVR driver.
+This is to prevent failure on PC based test systems, that often
+include display hardware that is incompatible with the driver.
+This code would not be needed for systems that don't use the PVR
+driver, or on production systems that use the PVR driver.
+
+---
+ src/egl/drivers/dri2/egl_dri2.c      |   1 +
+ src/egl/drivers/dri2/platform_null.c | 133 +++++++++++++++++++--------
+ 2 files changed, 98 insertions(+), 36 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index ec4ac60..56b5175 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -1369,6 +1369,7 @@ dri2_display_destroy(_EGLDisplay *disp)
+ 
+    switch (disp->Platform) {
+    case _EGL_PLATFORM_DRM:
++   case _EGL_PLATFORM_NULL:
+    case _EGL_PLATFORM_WAYLAND:
+    case _EGL_PLATFORM_X11:
+       if (dri2_dpy->fd_dpy >= 0 && dri2_dpy->fd_dpy != dri2_dpy->fd)
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index 8efac19..0d81151 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -45,6 +45,7 @@
+ 
+ #include "egl_dri2.h"
+ #include "loader.h"
++#include "util/os_file.h"
+ 
+ #define NULL_CARD_MINOR_MAX 63U
+ 
+@@ -975,7 +976,7 @@ swap_idle_get_target_frame(struct dri2_egl_surface *dri2_surf,
+     * current vblank by the number of intervals set at the time swapBuffer
+     * is called. For intervals of 1 or 0, we don't need a target frame.
+     */
+-   err = display_get_vblank_sequence(dri2_dpy->fd, current_vblank_out);
++   err = display_get_vblank_sequence(dri2_dpy->fd_dpy, current_vblank_out);
+    if (err)
+       return err;
+ 
+@@ -1025,7 +1026,7 @@ swap_vblank_state_transition(struct dri2_egl_surface *dri2_surf,
+    uint32_t flags = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT;
+    int err;
+ 
+-   err = display_request_vblank(dri2_dpy->fd, target_frame,
++   err = display_request_vblank(dri2_dpy->fd_dpy, target_frame,
+                                 flags, dri2_surf);
+    if (err) {
+       dri2_surf->swap_state = SWAP_ERROR;
+@@ -1051,7 +1052,7 @@ swap_flip_state_transition(struct dri2_egl_surface *dri2_surf)
+       flags |= DRM_MODE_PAGE_FLIP_ASYNC;
+    }
+ 
+-   err = display_output_flip(dri2_dpy->fd, &dri2_dpy->output,
++   err = display_output_flip(dri2_dpy->fd_dpy, &dri2_dpy->output,
+                              dri2_surf->swap_data->fb_id, flags, dri2_surf);
+    if (err) {
+       dri2_surf->swap_state = SWAP_ERROR;
+@@ -1071,7 +1072,7 @@ swap_poll_state_transition(struct dri2_egl_surface *dri2_surf)
+    int err;
+ 
+    /* dri2_surf->swap_state is being set inside the handler */
+-   err = drm_event_process(dri2_dpy->fd);
++   err = drm_event_process(dri2_dpy->fd_dpy);
+    if (err) {
+       dri2_surf->swap_state = SWAP_ERROR;
+       return err;
+@@ -1226,7 +1227,7 @@ add_fb_for_dri_image(struct dri2_egl_display *dri2_dpy, __DRIimage *image,
+       handles[i] = (uint32_t) handle;
+    }
+ 
+-   return !drmModeAddFB2WithModifiers(dri2_dpy->fd, width, height,
++   return !drmModeAddFB2WithModifiers(dri2_dpy->fd_dpy, width, height,
+                                       dri2_null_formats[format_idx].drm_format,
+                                       handles, pitches, offsets, modifiers,
+                                       fb_id_out, flags);
+@@ -1494,7 +1495,7 @@ dri2_null_create_window_surface(_EGLDisplay *disp, _EGLConfig *config,
+       goto err_destroy_surface;
+    }
+ 
+-   err = display_output_modeset(dri2_dpy->fd, &dri2_dpy->output,
++   err = display_output_modeset(dri2_dpy->fd_dpy, &dri2_dpy->output,
+                                 dri2_surf->front_buffer.fb_id);
+    if (err) {
+       _eglError(EGL_BAD_NATIVE_WINDOW, "window set mode");
+@@ -1608,11 +1609,11 @@ dri2_null_destroy_surface(_EGLDisplay *disp, _EGLSurface *surf)
+       dri2_dpy->image->destroyImage(dri2_surf->front_buffer.dri_image);
+ 
+    if (dri2_surf->front_buffer.fb_id)
+-         drmModeRmFB(dri2_dpy->fd, dri2_surf->front_buffer.fb_id);
++         drmModeRmFB(dri2_dpy->fd_dpy, dri2_surf->front_buffer.fb_id);
+ 
+    for (unsigned i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
+       if (dri2_surf->color_buffers[i].fb_id)
+-         drmModeRmFB(dri2_dpy->fd, dri2_surf->color_buffers[i].fb_id);
++         drmModeRmFB(dri2_dpy->fd_dpy, dri2_surf->color_buffers[i].fb_id);
+       if (dri2_surf->color_buffers[i].dri_image)
+          dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+    }
+@@ -1769,12 +1770,22 @@ dri2_null_flush_front_buffer(__DRIdrawable * driDrawable, void *loaderPrivate)
+    (void) loaderPrivate;
+ }
+ 
++static int
++dri2_null_get_display_fd(void *loaderPrivate)
++{
++   _EGLDisplay *disp = loaderPrivate;
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   return dri2_dpy->fd_dpy;
++}
++
+ static const __DRIimageLoaderExtension image_loader_extension = {
+-   .base = { __DRI_IMAGE_LOADER, 2 },
++   .base = { __DRI_IMAGE_LOADER, 5 },
+ 
+    .getBuffers          = dri2_null_image_get_buffers,
+    .flushFrontBuffer    = dri2_null_flush_front_buffer,
+    .getCapability       = dri2_null_get_capability,
++   .getDisplayFD        = dri2_null_get_display_fd,
+ };
+ 
+ static const __DRIextension *image_loader_extensions[] = {
+@@ -1803,12 +1814,74 @@ dri2_null_device_is_kms(int fd)
+    return is_kms;
+ }
+ 
++static bool
++dri2_null_try_device(_EGLDisplay *disp)
++{
++   struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++
++   if (!dri2_null_device_is_kms(dri2_dpy->fd_dpy))
++      return false;
++
++#if 1
++   /* Skip devices not supported by the pvr driver */
++   {
++      char *driver_name = loader_get_driver_for_fd(dri2_dpy->fd_dpy);
++      bool skip = !driver_name || !!strcmp(driver_name, "pvr");
++
++      free(driver_name);
++
++      if (skip)
++         return false;
++   }
++#endif
++
++   dri2_dpy->fd = os_dupfd_cloexec(dri2_dpy->fd_dpy);
++   if (dri2_dpy->fd < 0) {
++      _eglLog(_EGL_WARNING, "DRI2: failed to dup display FD");
++      dri2_dpy->fd = dri2_dpy->fd_dpy;
++   } else {
++      int fd_old;
++      bool is_different_gpu;
++
++      fd_old = dri2_dpy->fd;
++      dri2_dpy->fd = loader_get_user_preferred_fd(dri2_dpy->fd,
++                                                  &is_different_gpu);
++      if (dri2_dpy->fd == fd_old) {
++         close (dri2_dpy->fd);
++         dri2_dpy->fd = dri2_dpy->fd_dpy;
++      }
++   }
++
++   dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
++   if (!dri2_dpy->driver_name)
++      return false;
++
++   if (dri2_load_driver_dri3(disp)) {
++      _EGLDevice *dev = _eglAddDevice(dri2_dpy->fd, false);
++      if (!dev) {
++         dlclose(dri2_dpy->driver);
++         _eglLog(_EGL_WARNING, "DRI2: failed to find EGLDevice");
++      } else {
++         dri2_dpy->loader_extensions = image_loader_extensions;
++         dri2_dpy->own_device = 1;
++         disp->Device = dev;
++         return true;
++      }
++   }
++
++   free(dri2_dpy->driver_name);
++   dri2_dpy->driver_name = NULL;
++
++   return false;
++}
++
+ static bool
+ dri2_null_probe_device(_EGLDisplay *disp)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ 
+    dri2_dpy->fd = -1;
++   dri2_dpy->fd_dpy = -1;
+ 
+    for (unsigned i = 0; i <= NULL_CARD_MINOR_MAX; i++) {
+       char *card_path;
+@@ -1816,32 +1889,20 @@ dri2_null_probe_device(_EGLDisplay *disp)
+       if (asprintf(&card_path, DRM_DEV_NAME, DRM_DIR_NAME, i) < 0)
+          continue;
+ 
+-      dri2_dpy->fd = loader_open_device(card_path);
++      dri2_dpy->fd_dpy = loader_open_device(card_path);
+       free(card_path);
+-      if (dri2_dpy->fd < 0)
++      if (dri2_dpy->fd_dpy < 0)
+          continue;
+ 
+-      if (dri2_null_device_is_kms(dri2_dpy->fd)) {
+-         dri2_dpy->driver_name = loader_get_driver_for_fd(dri2_dpy->fd);
+-         if (dri2_dpy->driver_name) {
+-            if (dri2_load_driver_dri3(disp)) {
+-               _EGLDevice *dev = _eglAddDevice(dri2_dpy->fd, false);
+-               if (!dev) {
+-                  dlclose(dri2_dpy->driver);
+-                  _eglLog(_EGL_WARNING, "DRI2: failed to find EGLDevice");
+-               } else {
+-                  dri2_dpy->loader_extensions = image_loader_extensions;
+-                  dri2_dpy->own_device = 1;
+-                  disp->Device = dev;
+-                  return true;
+-               }
+-            }
+-            free(dri2_dpy->driver_name);
+-            dri2_dpy->driver_name = NULL;
+-         }
+-      }
++      if (dri2_null_try_device(disp))
++         return true;
++
++      close(dri2_dpy->fd_dpy);
++
++      if (dri2_dpy->fd >= 0 && dri2_dpy->fd != dri2_dpy->fd_dpy)
++         close(dri2_dpy->fd);
+ 
+-      close(dri2_dpy->fd);
++      dri2_dpy->fd_dpy = -1;
+       dri2_dpy->fd = -1;
+    }
+ 
+@@ -1902,7 +1963,7 @@ dri2_null_setup_swap_interval(_EGLDisplay *disp)
+ 
+    dri2_setup_swap_interval(disp, swap_max_interval);
+ 
+-   err = drmGetCap(dri2_dpy->fd, DRM_CAP_ASYNC_PAGE_FLIP, &value);
++   err = drmGetCap(dri2_dpy->fd_dpy, DRM_CAP_ASYNC_PAGE_FLIP, &value);
+    if (err || value == 0) {
+ 
+       /* DRM/KMS does not support async page flip. In order to support
+@@ -1950,7 +2011,7 @@ dri2_initialize_null(_EGLDisplay *disp)
+     * modesetting if not. If this succeeds then universal planes will also have
+     * been enabled.
+     */
+-   err = drmSetClientCap(dri2_dpy->fd, DRM_CLIENT_CAP_ATOMIC, 1);
++   err = drmSetClientCap(dri2_dpy->fd_dpy, DRM_CLIENT_CAP_ATOMIC, 1);
+    dri2_dpy->atomic_enabled = !err;
+ 
+    if (!dri2_dpy->atomic_enabled) {
+@@ -1958,7 +2019,7 @@ dri2_initialize_null(_EGLDisplay *disp)
+        * Enable universal planes so that we can get the pixel formats for the
+        * primary plane
+        */
+-      err = drmSetClientCap(dri2_dpy->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
++      err = drmSetClientCap(dri2_dpy->fd_dpy, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+       if (err) {
+          _eglError(EGL_NOT_INITIALIZED, "failed to enable universal planes");
+          goto cleanup;
+@@ -1994,7 +2055,7 @@ dri2_initialize_null(_EGLDisplay *disp)
+          dri2_dpy->image->createImageWithModifiers;
+    }
+ 
+-   if (!display_output_init(dri2_dpy->fd, &dri2_dpy->output,
++   if (!display_output_init(dri2_dpy->fd_dpy, &dri2_dpy->output,
+                             dri2_dpy->atomic_enabled,
+                             prefer_in_formats,
+                             &dri2_dpy->in_formats_enabled)) {
+@@ -2028,7 +2089,7 @@ dri2_teardown_null(struct dri2_egl_display *dri2_dpy)
+    drmModeAtomicFree(dri2_dpy->output.atomic_state);
+ 
+    if (dri2_dpy->output.mode_blob_id)
+-      drmModeDestroyPropertyBlob(dri2_dpy->fd, dri2_dpy->output.mode_blob_id);
++      drmModeDestroyPropertyBlob(dri2_dpy->fd_dpy, dri2_dpy->output.mode_blob_id);
+ 
+    if (dri2_dpy->output.plane_prop_res) {
+       for (unsigned i = 0; dri2_dpy->output.plane_prop_res[i]; i++)

+ 118 - 0
recipes-graphics/mesa/mesa-pvr/0055-egl-null-introduce-NULL_DRM_DISPLAY.patch

@@ -0,0 +1,118 @@
+From 58e030c23402e2ea7a5e7627daabe1e68f1d8130 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 28 Jun 2021 16:36:15 +0100
+Subject: [PATCH] egl/null: introduce NULL_DRM_DISPLAY
+
+Introduce the NULL_DRM_DISPLAY environment variable, which allows
+a particular DRM display to be selected, rather than the first
+suitable DRM device found.
+
+To select a particular display, NULL_DRM_DISPLAY should be set to
+the card number (i.e. minor number) of the DRM device representing
+the display. For example, NULL_DRM_DISPLAY=2 will select
+/dev/dri/card2.
+
+---
+ src/egl/drivers/dri2/platform_null.c | 65 ++++++++++++++++++++--------
+ 1 file changed, 46 insertions(+), 19 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index 0d81151..c9409c0 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -1819,6 +1819,8 @@ dri2_null_try_device(_EGLDisplay *disp)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+ 
++   dri2_dpy->fd = -1;
++
+    if (!dri2_null_device_is_kms(dri2_dpy->fd_dpy))
+       return false;
+ 
+@@ -1876,34 +1878,56 @@ dri2_null_try_device(_EGLDisplay *disp)
+ }
+ 
+ static bool
+-dri2_null_probe_device(_EGLDisplay *disp)
++dri2_null_probe_device(_EGLDisplay *disp, unsigned minor)
+ {
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
++   char *card_path;
+ 
+-   dri2_dpy->fd = -1;
+-   dri2_dpy->fd_dpy = -1;
++   if (asprintf(&card_path, DRM_DEV_NAME, DRM_DIR_NAME, minor) < 0)
++      goto cleanup;
+ 
+-   for (unsigned i = 0; i <= NULL_CARD_MINOR_MAX; i++) {
+-      char *card_path;
++   dri2_dpy->fd_dpy = loader_open_device(card_path);
++   free(card_path);
++   if (dri2_dpy->fd_dpy < 0)
++      goto cleanup;
+ 
+-      if (asprintf(&card_path, DRM_DEV_NAME, DRM_DIR_NAME, i) < 0)
+-         continue;
++   if (dri2_null_try_device(disp))
++      return true;
+ 
+-      dri2_dpy->fd_dpy = loader_open_device(card_path);
+-      free(card_path);
+-      if (dri2_dpy->fd_dpy < 0)
+-         continue;
++   close(dri2_dpy->fd_dpy);
+ 
+-      if (dri2_null_try_device(disp))
+-         return true;
++   if (dri2_dpy->fd >= 0 && dri2_dpy->fd != dri2_dpy->fd_dpy)
++      close(dri2_dpy->fd);
+ 
+-      close(dri2_dpy->fd_dpy);
++cleanup:
++   dri2_dpy->fd_dpy = -1;
++   dri2_dpy->fd = -1;
+ 
+-      if (dri2_dpy->fd >= 0 && dri2_dpy->fd != dri2_dpy->fd_dpy)
+-         close(dri2_dpy->fd);
++   return false;
++}
++
++static bool
++dri2_null_probe_devices(_EGLDisplay *disp)
++{
++   const char *null_drm_display = getenv("NULL_DRM_DISPLAY");
++
++   if (null_drm_display) {
++      char *endptr;
++      long val = strtol(null_drm_display, &endptr, 10);
+ 
+-      dri2_dpy->fd_dpy = -1;
+-      dri2_dpy->fd = -1;
++      if (endptr != null_drm_display && !*endptr &&
++          val >= 0 && val <= NULL_CARD_MINOR_MAX) {
++         if (dri2_null_probe_device(disp, (unsigned)val))
++            return true;
++      } else {
++         _eglLog(_EGL_FATAL, "NULL_DRM_DISPLAY is invalid: %s",
++                 null_drm_display);
++      }
++   } else {
++      for (unsigned i = 0; i <= NULL_CARD_MINOR_MAX; i++) {
++         if (dri2_null_probe_device(disp, i))
++            return true;
++      }
+    }
+ 
+    return false;
+@@ -2001,7 +2025,10 @@ dri2_initialize_null(_EGLDisplay *disp)
+ 
+    disp->DriverData = (void *) dri2_dpy;
+ 
+-   if (!dri2_null_probe_device(disp)) {
++   dri2_dpy->fd_dpy = -1;
++   dri2_dpy->fd = -1;
++
++   if (!dri2_null_probe_devices(disp)) {
+       _eglError(EGL_NOT_INITIALIZED, "failed to load driver");
+       goto cleanup;
+    }

+ 34 - 0
recipes-graphics/mesa/mesa-pvr/0056-vulkan-wsi-check-the-DRI3-and-Present-XCB-reply-poin.patch

@@ -0,0 +1,34 @@
+From eae8f032db504187079fe3ae3fa56990593cd857 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Mon, 21 Jun 2021 17:05:17 +0100
+Subject: [PATCH] vulkan/wsi: check the DRI3 and Present XCB reply pointers
+
+Check that the DRI3 and Present version replies are not NULL
+before accessing the version fields.
+
+---
+ src/vulkan/wsi/wsi_common_x11.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
+index 9dce78e..9eb624d 100644
+--- a/src/vulkan/wsi/wsi_common_x11.c
++++ b/src/vulkan/wsi/wsi_common_x11.c
+@@ -216,7 +216,7 @@ wsi_x11_connection_create(struct wsi_device *wsi_dev,
+ 
+       ver_cookie = xcb_dri3_query_version(conn, 1, 2);
+       ver_reply = xcb_dri3_query_version_reply(conn, ver_cookie, NULL);
+-      has_dri3_v1_2 =
++      has_dri3_v1_2 = ver_reply != NULL &&
+          (ver_reply->major_version > 1 || ver_reply->minor_version >= 2);
+       free(ver_reply);
+    }
+@@ -230,7 +230,7 @@ wsi_x11_connection_create(struct wsi_device *wsi_dev,
+ 
+       ver_cookie = xcb_present_query_version(conn, 1, 2);
+       ver_reply = xcb_present_query_version_reply(conn, ver_cookie, NULL);
+-      has_present_v1_2 =
++      has_present_v1_2 = ver_reply != NULL &&
+         (ver_reply->major_version > 1 || ver_reply->minor_version >= 2);
+       free(ver_reply);
+    }

+ 141 - 0
recipes-graphics/mesa/mesa-pvr/0057-vulkan-wsi-Allow-host-visible-memory-to-be-requested.patch

@@ -0,0 +1,141 @@
+From 0bfb1babce715e05151d35dec1daa0afe952b9c0 Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Tue, 16 Feb 2021 20:17:32 +0000
+Subject: [PATCH] vulkan/wsi: Allow host visible memory to be requested
+
+Allow host visible memory to be explicitly requested when allocating
+native images.
+
+For a software driver on X11, we need to be able to map the memory on
+the host, in order to present the contents to the X Server.
+
+---
+ src/vulkan/wsi/wsi_common_display.c |  2 +-
+ src/vulkan/wsi/wsi_common_drm.c     | 17 +++++++++++++----
+ src/vulkan/wsi/wsi_common_private.h |  1 +
+ src/vulkan/wsi/wsi_common_wayland.c |  3 ++-
+ src/vulkan/wsi/wsi_common_x11.c     |  1 +
+ 5 files changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
+index aa07cad..71a84e5 100644
+--- a/src/vulkan/wsi/wsi_common_display.c
++++ b/src/vulkan/wsi/wsi_common_display.c
+@@ -1031,7 +1031,7 @@ wsi_display_image_init(VkDevice device_h,
+       return VK_ERROR_DEVICE_LOST;
+ 
+    VkResult result = wsi_create_native_image(&chain->base, create_info,
+-                                             0, NULL, NULL,
++                                             0, NULL, NULL, false,
+                                              &image->base);
+    if (result != VK_SUCCESS)
+       return result;
+diff --git a/src/vulkan/wsi/wsi_common_drm.c b/src/vulkan/wsi/wsi_common_drm.c
+index 70d934a..aabb761 100644
+--- a/src/vulkan/wsi/wsi_common_drm.c
++++ b/src/vulkan/wsi/wsi_common_drm.c
+@@ -66,6 +66,7 @@ wsi_device_matches_drm_fd(const struct wsi_device *wsi, int drm_fd)
+ static uint32_t
+ select_memory_type(const struct wsi_device *wsi,
+                    bool want_device_local,
++                   bool want_host_visible,
+                    uint32_t type_bits)
+ {
+    assert(type_bits);
+@@ -74,8 +75,10 @@ select_memory_type(const struct wsi_device *wsi,
+    for (uint32_t i = 0; i < wsi->memory_props.memoryTypeCount; i++) {
+        const VkMemoryType type = wsi->memory_props.memoryTypes[i];
+        bool local = type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
++       bool host = type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ 
+-       if ((type_bits & (1 << i)) && local == want_device_local)
++       if ((type_bits & (1 << i)) && local == want_device_local &&
++            (!want_host_visible || host))
+          return i;
+        all_local &= local;
+    }
+@@ -83,6 +86,8 @@ select_memory_type(const struct wsi_device *wsi,
+    /* ignore want_device_local when all memory types are device-local */
+    if (all_local) {
+       assert(!want_device_local);
++      /* currently, host visibility is only needed with device local */
++      assert(!want_host_visible);
+       return ffs(type_bits) - 1;
+    }
+ 
+@@ -107,6 +112,7 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+                         uint32_t num_modifier_lists,
+                         const uint32_t *num_modifiers,
+                         const uint64_t *const *modifiers,
++                        bool host_visible,
+                         struct wsi_image *image)
+ {
+    const struct wsi_device *wsi = chain->wsi;
+@@ -317,7 +323,8 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+       .pNext = &memory_dedicated_info,
+       .allocationSize = reqs.size,
+-      .memoryTypeIndex = select_memory_type(wsi, true, reqs.memoryTypeBits),
++      .memoryTypeIndex = select_memory_type(wsi, true, host_visible,
++                                            reqs.memoryTypeBits),
+    };
+    result = wsi->AllocateMemory(chain->device, &memory_info,
+                                 &chain->alloc, &image->memory);
+@@ -488,7 +495,8 @@ wsi_create_prime_image(const struct wsi_swapchain *chain,
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+       .pNext = &prime_memory_dedicated_info,
+       .allocationSize = linear_size,
+-      .memoryTypeIndex = select_memory_type(wsi, false, reqs.memoryTypeBits),
++      .memoryTypeIndex = select_memory_type(wsi, false, false,
++                                            reqs.memoryTypeBits),
+    };
+    result = wsi->AllocateMemory(chain->device, &prime_memory_info,
+                                 &chain->alloc, &image->prime.memory);
+@@ -542,7 +550,8 @@ wsi_create_prime_image(const struct wsi_swapchain *chain,
+       .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+       .pNext = &memory_dedicated_info,
+       .allocationSize = reqs.size,
+-      .memoryTypeIndex = select_memory_type(wsi, true, reqs.memoryTypeBits),
++      .memoryTypeIndex = select_memory_type(wsi, true, false,
++                                            reqs.memoryTypeBits),
+    };
+    result = wsi->AllocateMemory(chain->device, &memory_info,
+                                 &chain->alloc, &image->memory);
+diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h
+index 1fe8211..5ad087b 100644
+--- a/src/vulkan/wsi/wsi_common_private.h
++++ b/src/vulkan/wsi/wsi_common_private.h
+@@ -94,6 +94,7 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+                         uint32_t num_modifier_lists,
+                         const uint32_t *num_modifiers,
+                         const uint64_t *const *modifiers,
++                        bool host_visible,
+                         struct wsi_image *image);
+ 
+ VkResult
+diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
+index 40f5338..983833e 100644
+--- a/src/vulkan/wsi/wsi_common_wayland.c
++++ b/src/vulkan/wsi/wsi_common_wayland.c
+@@ -1074,7 +1074,8 @@ wsi_wl_image_init(struct wsi_wl_swapchain *chain,
+    result = wsi_create_native_image(&chain->base, pCreateInfo,
+                                     chain->num_drm_modifiers > 0 ? 1 : 0,
+                                     &chain->num_drm_modifiers,
+-                                    &chain->drm_modifiers, &image->base);
++                                    &chain->drm_modifiers, false,
++                                    &image->base);
+ 
+    if (result != VK_SUCCESS)
+       return result;
+diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
+index 9eb624d..eb639d6 100644
+--- a/src/vulkan/wsi/wsi_common_x11.c
++++ b/src/vulkan/wsi/wsi_common_x11.c
+@@ -1315,6 +1315,7 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
+    } else {
+       result = wsi_create_native_image(&chain->base, pCreateInfo,
+                                        num_tranches, num_modifiers, modifiers,
++                                       chain->base.wsi->sw,
+                                        &image->base);
+    }
+    if (result < 0)

+ 417 - 0
recipes-graphics/mesa/mesa-pvr/0058-vulkan-wsi-make-the-display-FD-available.patch

@@ -0,0 +1,417 @@
+From d370e0dc9a60f70abe7d150837a2f816be2f1dee Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 17 Jun 2021 17:17:07 +0100
+Subject: [PATCH] vulkan/wsi: make the display FD available
+
+Pass the display FD to the Vulkan image create and memory
+allocation functions when allocating swapchain images.
+
+---
+ src/vulkan/wsi/wsi_common.h         |  14 +++
+ src/vulkan/wsi/wsi_common_display.c |   2 +-
+ src/vulkan/wsi/wsi_common_drm.c     |  22 ++++-
+ src/vulkan/wsi/wsi_common_private.h |   2 +
+ src/vulkan/wsi/wsi_common_wayland.c | 127 ++++++++++++++++++++++------
+ src/vulkan/wsi/wsi_common_x11.c     |  42 ++++++---
+ 6 files changed, 169 insertions(+), 40 deletions(-)
+
+diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
+index d5367db..c2563c6 100644
+--- a/src/vulkan/wsi/wsi_common.h
++++ b/src/vulkan/wsi/wsi_common.h
+@@ -37,6 +37,8 @@
+ #define VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO_MESA (VkStructureType)1000001003
+ #define VK_STRUCTURE_TYPE_WSI_SURFACE_SUPPORTED_COUNTERS_MESA (VkStructureType)1000001005
+ #define VK_STRUCTURE_TYPE_WSI_MEMORY_SIGNAL_SUBMIT_INFO_MESA (VkStructureType)1000001006
++#define VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO2_MESA (VkStructureType)1000001007
++#define VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO2_MESA (VkStructureType)1000001008
+ 
+ /* This is always chained to VkImageCreateInfo when a wsi image is created.
+  * It indicates that the image can be transitioned to/from
+@@ -75,6 +77,18 @@ struct wsi_memory_signal_submit_info {
+     VkDeviceMemory memory;
+ };
+ 
++struct wsi_image_create_info2 {
++    VkStructureType sType;
++    const void *pNext;
++    int display_fd;
++};
++
++struct wsi_memory_allocate_info2 {
++    VkStructureType sType;
++    const void *pNext;
++    int display_fd;
++};
++
+ struct wsi_fence {
+    VkDevice                     device;
+    const struct wsi_device      *wsi_device;
+diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
+index 71a84e5..f135b4e 100644
+--- a/src/vulkan/wsi/wsi_common_display.c
++++ b/src/vulkan/wsi/wsi_common_display.c
+@@ -1032,7 +1032,7 @@ wsi_display_image_init(VkDevice device_h,
+ 
+    VkResult result = wsi_create_native_image(&chain->base, create_info,
+                                              0, NULL, NULL, false,
+-                                             &image->base);
++                                             wsi->fd, &image->base);
+    if (result != VK_SUCCESS)
+       return result;
+ 
+diff --git a/src/vulkan/wsi/wsi_common_drm.c b/src/vulkan/wsi/wsi_common_drm.c
+index aabb761..6201891 100644
+--- a/src/vulkan/wsi/wsi_common_drm.c
++++ b/src/vulkan/wsi/wsi_common_drm.c
+@@ -113,6 +113,7 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+                         const uint32_t *num_modifiers,
+                         const uint64_t *const *modifiers,
+                         bool host_visible,
++                        int display_fd,
+                         struct wsi_image *image)
+ {
+    const struct wsi_device *wsi = chain->wsi;
+@@ -170,6 +171,12 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+       __vk_append_struct(&image_info, &image_format_list);
+    }
+ 
++   struct wsi_image_create_info2 image_wsi_info2 = {
++      .sType = VK_STRUCTURE_TYPE_WSI_IMAGE_CREATE_INFO2_MESA,
++      .display_fd = display_fd,
++   };
++   __vk_append_struct(&image_info, &image_wsi_info2);
++
+    VkImageDrmFormatModifierListCreateInfoEXT image_modifier_list;
+ 
+    uint32_t image_modifier_count = 0, modifier_prop_count = 0;
+@@ -308,9 +315,14 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+       .pNext = NULL,
+       .implicit_sync = true,
+    };
++   const struct wsi_memory_allocate_info2 memory_wsi_info2 = {
++      .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO2_MESA,
++      .pNext = &memory_wsi_info,
++      .display_fd = display_fd,
++   };
+    const VkExportMemoryAllocateInfo memory_export_info = {
+       .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
+-      .pNext = &memory_wsi_info,
++      .pNext = &memory_wsi_info2,
+       .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+    };
+    const VkMemoryDedicatedAllocateInfo memory_dedicated_info = {
+@@ -440,6 +452,7 @@ VkResult
+ wsi_create_prime_image(const struct wsi_swapchain *chain,
+                        const VkSwapchainCreateInfoKHR *pCreateInfo,
+                        bool use_modifier,
++                       int display_fd,
+                        struct wsi_image *image)
+ {
+    const struct wsi_device *wsi = chain->wsi;
+@@ -480,9 +493,14 @@ wsi_create_prime_image(const struct wsi_swapchain *chain,
+       .pNext = NULL,
+       .implicit_sync = true,
+    };
++   const struct wsi_memory_allocate_info2 memory_wsi_info2 = {
++      .sType = VK_STRUCTURE_TYPE_WSI_MEMORY_ALLOCATE_INFO2_MESA,
++      .pNext = &memory_wsi_info,
++      .display_fd = display_fd,
++   };
+    const VkExportMemoryAllocateInfo prime_memory_export_info = {
+       .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
+-      .pNext = &memory_wsi_info,
++      .pNext = &memory_wsi_info2,
+       .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT,
+    };
+    const VkMemoryDedicatedAllocateInfo prime_memory_dedicated_info = {
+diff --git a/src/vulkan/wsi/wsi_common_private.h b/src/vulkan/wsi/wsi_common_private.h
+index 5ad087b..e463281 100644
+--- a/src/vulkan/wsi/wsi_common_private.h
++++ b/src/vulkan/wsi/wsi_common_private.h
+@@ -95,12 +95,14 @@ wsi_create_native_image(const struct wsi_swapchain *chain,
+                         const uint32_t *num_modifiers,
+                         const uint64_t *const *modifiers,
+                         bool host_visible,
++                        int display_fd,
+                         struct wsi_image *image);
+ 
+ VkResult
+ wsi_create_prime_image(const struct wsi_swapchain *chain,
+                        const VkSwapchainCreateInfoKHR *pCreateInfo,
+                        bool use_modifier,
++                       int display_fd,
+                        struct wsi_image *image);
+ 
+ void
+diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
+index 983833e..1109d3f 100644
+--- a/src/vulkan/wsi/wsi_common_wayland.c
++++ b/src/vulkan/wsi/wsi_common_wayland.c
+@@ -32,6 +32,8 @@
+ #include <pthread.h>
+ #include <poll.h>
+ #include <sys/mman.h>
++#include <fcntl.h>
++#include <xf86drm.h>
+ 
+ #include "drm-uapi/drm_fourcc.h"
+ 
+@@ -82,6 +84,9 @@ struct wsi_wl_display {
+ 
+    struct wsi_wayland *wsi_wl;
+ 
++   int                                          fd;
++   bool                                         authenticated;
++
+    /* Points to formats in wsi_wl_display_drm or wsi_wl_display_dmabuf */
+    struct u_vector *                            formats;
+ 
+@@ -261,10 +266,52 @@ wsi_wl_display_add_wl_shm_format(struct wsi_wl_display *display,
+    }
+ }
+ 
++static int
++open_display_device(const char *name)
++{
++   int fd;
++
++#ifdef O_CLOEXEC
++   fd = open(name, O_RDWR | O_CLOEXEC);
++   if (fd != -1 || errno != EINVAL) {
++      return fd;
++   }
++#endif
++
++   fd = open(name, O_RDWR);
++   if (fd != -1) {
++      long flags = fcntl(fd, F_GETFD);
++
++      if (flags != -1) {
++         if (!fcntl(fd, F_SETFD, flags | FD_CLOEXEC))
++             return fd;
++      }
++      close (fd);
++   }
++
++   return -1;
++}
+ 
+ static void
+ drm_handle_device(void *data, struct wl_drm *drm, const char *name)
+ {
++   struct wsi_wl_display *display = data;
++   const int fd = open_display_device(name);
++
++   if (fd != -1) {
++      if (drmGetNodeTypeFromFd(fd) != DRM_NODE_RENDER) {
++         drm_magic_t magic;
++
++         if (drmGetMagic(fd, &magic)) {
++            close(fd);
++	    return;
++         }
++	 wl_drm_authenticate(drm, magic);
++      } else {
++         display->authenticated = true;
++      }
++      display->fd = fd;
++   }
+ }
+ 
+ static uint32_t
+@@ -346,6 +393,9 @@ drm_handle_format(void *data, struct wl_drm *drm, uint32_t wl_format)
+ static void
+ drm_handle_authenticated(void *data, struct wl_drm *drm)
+ {
++   struct wsi_wl_display *display = data;
++
++   display->authenticated = true;
+ }
+ 
+ static void
+@@ -487,6 +537,9 @@ wsi_wl_display_finish(struct wsi_wl_display *display)
+       wl_proxy_wrapper_destroy(display->wl_display_wrapper);
+    if (display->queue)
+       wl_event_queue_destroy(display->queue);
++
++   if (display->fd != -1)
++      close(display->fd);
+ }
+ 
+ static VkResult
+@@ -501,6 +554,7 @@ wsi_wl_display_init(struct wsi_wayland *wsi_wl,
+    display->wsi_wl = wsi_wl;
+    display->wl_display = wl_display;
+    display->sw = sw;
++   display->fd = -1;
+ 
+    if (get_format_list) {
+       if (!u_vector_init(&display->swrast.formats, sizeof(VkFormat), 8) ||
+@@ -542,41 +596,60 @@ wsi_wl_display_init(struct wsi_wayland *wsi_wl,
+    /* Round-trip to get wl_drms and zwp_linux_dmabuf_v1 globals */
+    wl_display_roundtrip_queue(display->wl_display, display->queue);
+ 
++   if (!display->drm.wl_drm && !display->dmabuf.wl_dmabuf && !display->swrast.wl_shm) {
++      result = VK_ERROR_SURFACE_LOST_KHR;
++      goto fail_registry;
++   }
++
+    /* Round-trip again to get formats, modifiers and capabilities */
+-   if (display->drm.wl_drm || display->dmabuf.wl_dmabuf || display->swrast.wl_shm)
+-      wl_display_roundtrip_queue(display->wl_display, display->queue);
++   wl_display_roundtrip_queue(display->wl_display, display->queue);
+ 
+-   if (wsi_wl->wsi->force_bgra8_unorm_first) {
+-      /* Find BGRA8_UNORM in the list and swap it to the first position if we
+-       * can find it.  Some apps get confused if SRGB is first in the list.
+-       */
+-      VkFormat *first_fmt = u_vector_head(display->formats);
+-      VkFormat *iter_fmt;
+-      u_vector_foreach(iter_fmt, display->formats) {
+-         if (*iter_fmt == VK_FORMAT_B8G8R8A8_UNORM) {
+-            *iter_fmt = *first_fmt;
+-            *first_fmt = VK_FORMAT_B8G8R8A8_UNORM;
+-            break;
+-         }
+-      }
++   if (display->fd == -1) {
++      result = VK_ERROR_SURFACE_LOST_KHR;
++      goto fail_registry;
+    }
+ 
+-   /* Prefer the linux-dmabuf protocol if available */
+-   if (display->sw)
+-      display->formats = &display->swrast.formats;
+-   else if (display->dmabuf.wl_dmabuf) {
+-      display->formats = &display->dmabuf.formats;
+-   } else if (display->drm.wl_drm &&
+-       (display->drm.capabilities & WL_DRM_CAPABILITY_PRIME)) {
+-      /* We need prime support for wl_drm */
+-      display->formats = &display->drm.formats;
+-   }
++   wl_display_roundtrip_queue(display->wl_display, display->queue);
+ 
+-   if (!display->formats) {
++   if (!display->authenticated) {
+       result = VK_ERROR_SURFACE_LOST_KHR;
+       goto fail_registry;
+    }
+ 
++   if (get_format_list) {
++      /* Prefer the linux-dmabuf protocol if available */
++      if (display->sw)
++         display->formats = &display->swrast.formats;
++      else if(display->dmabuf.wl_dmabuf &&
++          u_vector_length(&display->dmabuf.formats)) {
++         display->formats = &display->dmabuf.formats;
++      } else if (display->drm.wl_drm &&
++                 display->drm.capabilities & WL_DRM_CAPABILITY_PRIME) {
++         display->formats = &display->drm.formats;
++      }
++
++      if (!display->formats) {
++         result = VK_ERROR_SURFACE_LOST_KHR;
++         goto fail_registry;
++      }
++
++      if (wsi_wl->wsi->force_bgra8_unorm_first) {
++         /* Find BGRA8_UNORM in the list and swap it to the first position if
++          * we can find it.  Some apps get confused if SRGB is first in the
++          * list.
++          */
++         VkFormat *first_fmt = u_vector_tail(display->formats);
++         VkFormat *iter_fmt;
++         u_vector_foreach(iter_fmt, display->formats) {
++            if (*iter_fmt == VK_FORMAT_B8G8R8A8_UNORM) {
++               *iter_fmt = *first_fmt;
++               *first_fmt = VK_FORMAT_B8G8R8A8_UNORM;
++               break;
++            }
++         }
++      }
++   }
++
+    /* We don't need this anymore */
+    wl_registry_destroy(registry);
+ 
+@@ -1075,7 +1148,7 @@ wsi_wl_image_init(struct wsi_wl_swapchain *chain,
+                                     chain->num_drm_modifiers > 0 ? 1 : 0,
+                                     &chain->num_drm_modifiers,
+                                     &chain->drm_modifiers, false,
+-                                    &image->base);
++                                    display->fd, &image->base);
+ 
+    if (result != VK_SUCCESS)
+       return result;
+diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c
+index eb639d6..ba64e26 100644
+--- a/src/vulkan/wsi/wsi_common_x11.c
++++ b/src/vulkan/wsi/wsi_common_x11.c
+@@ -1303,7 +1303,8 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
+                const VkAllocationCallbacks* pAllocator,
+                const uint64_t *const *modifiers,
+                const uint32_t *num_modifiers,
+-               int num_tranches, struct x11_image *image)
++               int num_tranches, int display_fd,
++               struct x11_image *image)
+ {
+    xcb_void_cookie_t cookie;
+    VkResult result;
+@@ -1311,11 +1312,12 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
+ 
+    if (chain->base.use_prime_blit) {
+       bool use_modifier = num_tranches > 0;
+-      result = wsi_create_prime_image(&chain->base, pCreateInfo, use_modifier, &image->base);
++      result = wsi_create_prime_image(&chain->base, pCreateInfo, use_modifier,
++                                      display_fd, &image->base);
+    } else {
+       result = wsi_create_native_image(&chain->base, pCreateInfo,
+                                        num_tranches, num_modifiers, modifiers,
+-                                       chain->base.wsi->sw,
++                                       chain->base.wsi->sw, display_fd,
+                                        &image->base);
+    }
+    if (result < 0)
+@@ -1687,14 +1689,34 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
+                                  modifiers, num_modifiers, &num_tranches,
+                                  pAllocator);
+ 
++
+    uint32_t image = 0;
+-   for (; image < chain->base.image_count; image++) {
+-      result = x11_image_init(device, chain, pCreateInfo, pAllocator,
+-                              (const uint64_t *const *)modifiers,
+-                              num_modifiers, num_tranches,
+-                              &chain->images[image]);
+-      if (result != VK_SUCCESS)
+-         goto fail_init_images;
++   {
++      int display_fd = -1;
++
++      if (!wsi_device->sw) {
++         xcb_screen_iterator_t screen_iter =
++            xcb_setup_roots_iterator(xcb_get_setup(conn));
++         xcb_screen_t *screen = screen_iter.data;
++
++         display_fd = wsi_dri3_open(conn, screen->root, None);
++      }
++
++      for (; image < chain->base.image_count; image++) {
++         result = x11_image_init(device, chain, pCreateInfo, pAllocator,
++                                 (const uint64_t *const *)modifiers,
++                                 num_modifiers, num_tranches,
++                                 display_fd, &chain->images[image]);
++         if (result != VK_SUCCESS) {
++            if (display_fd >= 0)
++               close(display_fd);
++
++            goto fail_init_images;
++         }
++      }
++
++      if (display_fd >= 0)
++         close(display_fd);
+    }
+ 
+    if ((chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ||

+ 1341 - 0
recipes-graphics/mesa/mesa-pvr/0059-pvr-wsi-add-PowerVR-Vulkan-WSI-library.patch

@@ -0,0 +1,1341 @@
+From 65c47424e2dd558d2c79ef5f1b9008fe38b3b0bf Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 18 Mar 2021 13:44:57 +0000
+Subject: [PATCH] pvr/wsi: add PowerVR Vulkan WSI library
+
+PowerVR Vulkan will load the WSI library, libpvr_mesa_wsi.so,
+and lookup the symbol pvr_mesa_wsi_sym_addr, which is a used to
+lookup all other symbols required by Vulkan from the library.
+The function is used by Vulkan to lookup the library initialisation
+function, pvr_mesa_wsi_init. That function is passed the address
+of another lookup function, pvr_vk_mesa_wsi_sym_addr, which is used
+by the WSI library to lookup symbols in Vulkan.
+
+The interface between PVR Vulkan and the WSI library is defined in
+pvr_mesa_wsi_interface.h. Most of the functions defined on the WSI
+side are wrappers around Mesa WSI functions.
+
+---
+ meson.build                          |   1 +
+ meson_options.txt                    |   2 +-
+ src/meson.build                      |   3 +
+ src/pvr/meson.build                  |  23 ++
+ src/pvr/wsi/meson.build              |  75 ++++++
+ src/pvr/wsi/pvr_mesa_wsi_interface.h | 306 ++++++++++++++++++++++++
+ src/pvr/wsi/pvr_wsi.c                | 333 +++++++++++++++++++++++++++
+ src/pvr/wsi/pvr_wsi.h                |  78 +++++++
+ src/pvr/wsi/pvr_wsi_display.c        | 293 +++++++++++++++++++++++
+ src/pvr/wsi/pvr_wsi_wayland.c        |  45 ++++
+ src/pvr/wsi/pvr_wsi_x11.c            |  62 +++++
+ 11 files changed, 1220 insertions(+), 1 deletion(-)
+ create mode 100644 src/pvr/meson.build
+ create mode 100644 src/pvr/wsi/meson.build
+ create mode 100644 src/pvr/wsi/pvr_mesa_wsi_interface.h
+ create mode 100644 src/pvr/wsi/pvr_wsi.c
+ create mode 100644 src/pvr/wsi/pvr_wsi.h
+ create mode 100644 src/pvr/wsi/pvr_wsi_display.c
+ create mode 100644 src/pvr/wsi/pvr_wsi_wayland.c
+ create mode 100644 src/pvr/wsi/pvr_wsi_x11.c
+
+diff --git a/meson.build b/meson.build
+index c0b6264..3f6ddb6 100644
+--- a/meson.build
++++ b/meson.build
+@@ -296,6 +296,7 @@ with_swrast_vk = _vulkan_drivers.contains('swrast')
+ with_virtio_vk = _vulkan_drivers.contains('virtio-experimental')
+ with_freedreno_kgsl = get_option('freedreno-kgsl')
+ with_broadcom_vk = _vulkan_drivers.contains('broadcom')
++with_pvr_vk = _vulkan_drivers.contains('pvr')
+ with_any_vk = _vulkan_drivers.length() != 0
+ 
+ with_any_broadcom = with_gallium_vc4 or with_gallium_v3d or with_broadcom_vk
+diff --git a/meson_options.txt b/meson_options.txt
+index d36e714..e0c25a5 100644
+--- a/meson_options.txt
++++ b/meson_options.txt
+@@ -194,7 +194,7 @@ option(
+   'vulkan-drivers',
+   type : 'array',
+   value : ['auto'],
+-  choices : ['auto', 'amd', 'broadcom', 'freedreno', 'intel', 'panfrost', 'swrast', 'virtio-experimental'],
++  choices : ['auto', 'amd', 'broadcom', 'freedreno', 'intel', 'panfrost', 'swrast', 'virtio-experimental', 'pvr'],
+   description : 'List of vulkan drivers to build. If this is set to auto all drivers applicable to the target OS/architecture will be built'
+ )
+ option(
+diff --git a/src/meson.build b/src/meson.build
+index f6eede5..36fec26 100644
+--- a/src/meson.build
++++ b/src/meson.build
+@@ -90,6 +90,9 @@ endif
+ if with_gallium_panfrost or with_gallium_lima or with_panfrost_vk
+   subdir('panfrost')
+ endif
++if with_pvr_vk
++  subdir('pvr')
++endif
+ if with_gallium_virgl or with_virtio_vk
+   subdir('virtio')
+ endif
+diff --git a/src/pvr/meson.build b/src/pvr/meson.build
+new file mode 100644
+index 0000000..09a6986
+--- /dev/null
++++ b/src/pvr/meson.build
+@@ -0,0 +1,23 @@
++# Copyright (c) Imagination Technologies Ltd.
++
++# Permission is hereby granted, free of charge, to any person obtaining a copy
++# of this software and associated documentation files (the "Software"), to deal
++# in the Software without restriction, including without limitation the rights
++# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++# copies of the Software, and to permit persons to whom the Software is
++# furnished to do so, subject to the following conditions:
++
++# The above copyright notice and this permission notice shall be included in
++# all copies or substantial portions of the Software.
++
++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++# SOFTWARE.
++
++if with_pvr_vk
++  subdir('wsi')
++endif
+diff --git a/src/pvr/wsi/meson.build b/src/pvr/wsi/meson.build
+new file mode 100644
+index 0000000..57c7623
+--- /dev/null
++++ b/src/pvr/wsi/meson.build
+@@ -0,0 +1,75 @@
++# Copyright (c) Imagination Technologies Ltd.
++
++# Permission is hereby granted, free of charge, to any person obtaining a copy
++# of this software and associated documentation files (the "Software"), to deal
++# in the Software without restriction, including without limitation the rights
++# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++# copies of the Software, and to permit persons to whom the Software is
++# furnished to do so, subject to the following conditions:
++
++# The above copyright notice and this permission notice shall be included in
++# all copies or substantial portions of the Software.
++
++# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++# SOFTWARE.
++
++pvr_wsi_args = vulkan_wsi_args
++
++pvr_wsi_depends = [ dep_libdrm, idep_vulkan_util, idep_xmlconfig ]
++
++pvr_wsi_includes = [
++  inc_include,
++  inc_src,
++  inc_vulkan_util,
++  inc_vulkan_wsi
++]
++
++pvr_wsi_src = [ 'pvr_wsi.c' ]
++
++if with_platform_wayland
++  pvr_wsi_args += '-DVK_USE_PLATFORM_WAYLAND_KHR'
++
++  pvr_wsi_depends += dep_wayland_client
++
++  pvr_wsi_src += 'pvr_wsi_wayland.c'
++endif
++
++if with_platform_x11
++  pvr_wsi_args += [
++    '-DVK_USE_PLATFORM_XCB_KHR',
++    '-DVK_USE_PLATFORM_XLIB_KHR',
++  ]
++
++  pvr_wsi_depends += dep_xcb_dri2
++
++  pvr_wsi_src += 'pvr_wsi_x11.c'
++endif
++
++if system_has_kms_drm and not with_platform_android
++  pvr_wsi_args += '-DVK_USE_PLATFORM_DISPLAY_KHR'
++
++  pvr_wsi_src += 'pvr_wsi_display.c'
++endif
++
++if with_xlib_lease
++  pvr_wsi_args += '-DVK_USE_PLATFORM_XLIB_XRANDR_EXT'
++
++  pvr_wsi_depends += dep_xlib_xrandr
++endif
++
++libvulkan_wsi = shared_library(
++  'pvr_mesa_wsi',
++  pvr_wsi_src,
++  include_directories : pvr_wsi_includes,
++  dependencies : pvr_wsi_depends,
++  c_args : pvr_wsi_args,
++  link_with: libvulkan_wsi,
++  gnu_symbol_visibility : 'hidden',
++  build_by_default : true,
++  install : true
++)
+diff --git a/src/pvr/wsi/pvr_mesa_wsi_interface.h b/src/pvr/wsi/pvr_mesa_wsi_interface.h
+new file mode 100644
+index 0000000..b4b1bcb
+--- /dev/null
++++ b/src/pvr/wsi/pvr_mesa_wsi_interface.h
+@@ -0,0 +1,306 @@
++/*************************************************************************/ /*!
++@File
++@Title          PVR interface to the Vulkan WSI layer in Mesa
++@Copyright      Copyright (c) Imagination Technologies Ltd. All Rights Reserved
++@License        MIT
++
++The contents of this file are subject to the MIT license as set out below.
++
++Permission is hereby granted, free of charge, to any person obtaining a copy
++of this software and associated documentation files (the "Software"), to deal
++in the Software without restriction, including without limitation the rights
++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++copies of the Software, and to permit persons to whom the Software is
++furnished to do so, subject to the following conditions:
++
++The above copyright notice and this permission notice shall be included in
++all copies or substantial portions of the Software.
++
++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++THE SOFTWARE.
++*/ /**************************************************************************/
++
++#ifndef PVR_MESA_WSI_INTERFACE_H
++#define PVR_MESA_WSI_INTERFACE_H
++
++#include <stdbool.h>
++#include <stdint.h>
++
++#include <vulkan/vulkan.h>
++
++/*
++ * The pvr_mesa_wsi structure holds the Mesa WSI state, and is opaque to
++ * the PowerVK DDK.
++ */
++struct pvr_mesa_wsi;
++
++/*
++ * Functions defined in Mesa for use by the PowerVR DDK.
++ * All functions have a "pvr_mesa_wsi" prefix.
++ */
++
++void *
++pvr_mesa_wsi_sym_addr(struct pvr_mesa_wsi *mwsi,
++                      const char *name);
++
++VkResult
++pvr_mesa_wsi_init(struct pvr_mesa_wsi **mwsi,
++                  VkPhysicalDevice physicalDevice,
++                  PFN_vkVoidFunction (VKAPI_PTR *pvr_vk_mesa_wsi_sym_addr)
++                     (VkPhysicalDevice physicalDevice, const char *),
++                  const VkAllocationCallbacks *alloc,
++                  int fd,
++                  bool sw);
++
++void
++pvr_mesa_wsi_finish(struct pvr_mesa_wsi *mwsi,
++                    const VkAllocationCallbacks *alloc);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_support(struct pvr_mesa_wsi *mwsi,
++                                        uint32_t queueFamilyIndex,
++                                        VkSurfaceKHR surface,
++                                        VkBool32 *pSupported);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_capabilities(struct pvr_mesa_wsi *mwsi,
++                                             VkSurfaceKHR surface,
++                                             VkSurfaceCapabilitiesKHR *pSurfaceCapabilities);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_capabilities2(struct pvr_mesa_wsi *mwsi,
++                                              const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
++                                              VkSurfaceCapabilities2KHR *pSurfaceCapabilities);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_capabilities2ext(struct pvr_mesa_wsi *mwsi,
++                                                 VkSurfaceKHR surface,
++                                                 VkSurfaceCapabilities2EXT *pSurfaceCapabilities);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_formats(struct pvr_mesa_wsi *mwsi,
++                                        VkSurfaceKHR surface,
++                                        uint32_t *pSurfaceFormatCount,
++                                        VkSurfaceFormatKHR *pSurfaceFormats);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_formats2(struct pvr_mesa_wsi *mwsi,
++                                         const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
++                                         uint32_t *pSurfaceFormatCount,
++                                         VkSurfaceFormat2KHR *pSurfaceFormats);
++
++VkResult
++pvr_mesa_wsi_common_get_surface_present_modes(struct pvr_mesa_wsi *mwsi,
++                                              VkSurfaceKHR surface,
++                                              uint32_t *pPresentModeCount,
++                                              VkPresentModeKHR *pPresentModes);
++
++VkResult
++pvr_mesa_wsi_common_create_swapchain(struct pvr_mesa_wsi *mwsi,
++                                     VkDevice device,
++                                     const VkSwapchainCreateInfoKHR *pCreateInfo,
++                                     const VkAllocationCallbacks *pAllocator,
++                                     VkSwapchainKHR *pSwapchain);
++void
++pvr_mesa_wsi_common_destroy_swapchain(struct pvr_mesa_wsi *mwsi,
++                                      VkDevice device,
++                                      VkSwapchainKHR swapchain,
++                                      const VkAllocationCallbacks *pAllocator);
++
++VkResult
++pvr_mesa_wsi_common_get_images(struct pvr_mesa_wsi *mwsi,
++                               VkSwapchainKHR swapchain,
++                               uint32_t *pSwapchainImageCount,
++                               VkImage *pSwapchainImages);
++
++VkResult
++pvr_mesa_wsi_common_acquire_next_image2(struct pvr_mesa_wsi *mwsi,
++                                        VkDevice device,
++                                        const VkAcquireNextImageInfoKHR *pAcquireInfo,
++                                        uint32_t *pImageIndex);
++
++VkResult
++pvr_mesa_wsi_common_queue_present(struct pvr_mesa_wsi *mwsi,
++                                  VkDevice device,
++                                  VkQueue queue,
++                                  int queue_family_index,
++                                  const VkPresentInfoKHR *pPresentInfo);
++
++VkResult
++pvr_mesa_wsi_common_get_present_rectangles(struct pvr_mesa_wsi *mwsi,
++                                           VkSurfaceKHR surface,
++                                           uint32_t* pRectCount,
++                                           VkRect2D* pRects);
++
++#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
++VkBool32
++pvr_mesa_wsi_get_physical_device_wayland_presentation_support(struct pvr_mesa_wsi *mwsi,
++                                                              uint32_t queueFamilyIndex,
++                                                              void *display);
++
++VkResult
++pvr_mesa_wsi_create_wayland_surface(struct pvr_mesa_wsi *mwsi,
++                                    const VkAllocationCallbacks *pAllocator,
++                                    const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
++                                    VkSurfaceKHR *pSurface);
++#endif
++
++#if defined(VK_USE_PLATFORM_XCB_KHR)
++VkBool32
++pvr_mesa_wsi_get_physical_device_xcb_presentation_support(struct pvr_mesa_wsi *mwsi,
++                                                          uint32_t queueFamilyIndex,
++                                                          void *connection,
++                                                          uint32_t visualId);
++VkResult
++pvr_mesa_wsi_create_xcb_surface(struct pvr_mesa_wsi *mwsi,
++                                const VkAllocationCallbacks *pAllocator,
++                                const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
++                                VkSurfaceKHR *pSurface);
++#endif
++
++#if defined(VK_USE_PLATFORM_XLIB_KHR)
++VkResult
++pvr_mesa_wsi_create_xlib_surface(struct pvr_mesa_wsi *mwsi,
++                                 const VkAllocationCallbacks *pAllocator,
++                                 const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
++                                 VkSurfaceKHR *pSurface);
++#endif
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_properties(struct pvr_mesa_wsi *mwsi,
++							    VkPhysicalDevice physicalDevice,
++							    uint32_t *pPropertyCount,
++							    VkDisplayPropertiesKHR *pProperties);
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_properties2(struct pvr_mesa_wsi *mwsi,
++							     VkPhysicalDevice physicalDevice,
++							     uint32_t *pPropertyCount,
++							     VkDisplayProperties2KHR *pProperties);
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_plane_properties(struct pvr_mesa_wsi *mwsi,
++								  VkPhysicalDevice physicalDevice,
++								  uint32_t *pPropertyCount,
++								  VkDisplayPlanePropertiesKHR *pProperties);
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_plane_properties2(struct pvr_mesa_wsi *mwsi,
++								   VkPhysicalDevice physicalDevice,
++								   uint32_t *pPropertyCount,
++								   VkDisplayPlaneProperties2KHR *pProperties);
++
++VkResult
++pvr_mesa_wsi_display_get_display_plane_supported_displays(struct pvr_mesa_wsi *mwsi,
++							  VkPhysicalDevice physicalDevice,
++							  uint32_t planeIndex,
++							  uint32_t *pDisplayCount,
++							  VkDisplayKHR *pDisplays);
++
++VkResult
++pvr_mesa_wsi_display_get_display_mode_properties(struct pvr_mesa_wsi *mwsi,
++						 VkPhysicalDevice physicalDevice,
++						 VkDisplayKHR display,
++						 uint32_t *pPropertyCount,
++						 VkDisplayModePropertiesKHR *pProperties);
++
++VkResult
++pvr_mesa_wsi_display_get_display_mode_properties2(struct pvr_mesa_wsi *mwsi,
++                                                  VkPhysicalDevice physicalDevice,
++                                                  VkDisplayKHR display,
++                                                  uint32_t *pPropertyCount,
++                                                  VkDisplayModeProperties2KHR *pProperties);
++
++VkResult
++pvr_mesa_wsi_display_create_display_mode(struct pvr_mesa_wsi *mwsi,
++                                         VkPhysicalDevice physicalDevice,
++                                         VkDisplayKHR display,
++                                         const VkDisplayModeCreateInfoKHR *pCreateInfo,
++                                         const VkAllocationCallbacks *pAllocator,
++                                         VkDisplayModeKHR *pMode);
++
++VkResult
++pvr_mesa_wsi_get_display_plane_capabilities(struct pvr_mesa_wsi *mwsi,
++                                            VkPhysicalDevice physicalDevice,
++                                            VkDisplayModeKHR modeKhr,
++                                            uint32_t planeIndex,
++                                            VkDisplayPlaneCapabilitiesKHR *pCapabilities);
++
++VkResult
++pvr_mesa_wsi_get_display_plane_capabilities2(struct pvr_mesa_wsi *mwsi,
++                                             VkPhysicalDevice physicalDevice,
++                                             const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
++                                             VkDisplayPlaneCapabilities2KHR *pCapabilities);
++
++VkResult
++pvr_mesa_wsi_create_display_surface(struct pvr_mesa_wsi *mwsi,
++                                    VkInstance instance,
++                                    const VkAllocationCallbacks *pAllocator,
++                                    const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
++                                    VkSurfaceKHR *pSurface);
++
++VkResult
++pvr_mesa_wsi_release_display(struct pvr_mesa_wsi *mwsi,
++                             VkPhysicalDevice physicalDevice,
++                             VkDisplayKHR display);
++
++#if defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT)
++VkResult
++pvr_mesa_wsi_acquire_xlib_display(struct pvr_mesa_wsi *mwsi,
++                                  VkPhysicalDevice physicalDevice,
++                                  void *dpy,
++                                  VkDisplayKHR display);
++
++VkResult
++pvr_mesa_wsi_get_randr_output_display(struct pvr_mesa_wsi *mwsi,
++                                      VkPhysicalDevice  physicalDevice,
++                                      void *dpy,
++                                      uint32_t output,
++                                      VkDisplayKHR *pDisplay);
++
++#endif
++
++VkResult
++pvr_mesa_wsi_display_power_control(struct pvr_mesa_wsi *mwsi,
++                                   VkDevice device,
++                                   VkDisplayKHR display,
++                                   const VkDisplayPowerInfoEXT *pDisplayPowerInfo);
++
++VkResult
++pvr_mesa_wsi_register_device_event(struct pvr_mesa_wsi *mwsi,
++                                   VkDevice device,
++                                   const VkDeviceEventInfoEXT *pDeviceEventInfo,
++                                   const VkAllocationCallbacks *pAllocator,
++                                   void **pFence,
++                                   int syncFd);
++
++VkResult
++pvr_mesa_wsi_register_display_event(struct pvr_mesa_wsi *mwsi,
++                                    VkDevice device,
++                                    VkDisplayKHR display,
++                                    const VkDisplayEventInfoEXT *pDisplayEventInfo,
++                                    const VkAllocationCallbacks  *pAllocator,
++                                    void **pFence,
++                                    int syncFd);
++
++VkResult
++pvr_mesa_wsi_get_swapchain_counter(struct pvr_mesa_wsi *mwsi,
++                                   VkDevice device,
++                                   VkSwapchainKHR swapchain,
++                                   VkSurfaceCounterFlagBitsEXT flagBits,
++                                   uint64_t *pValue);
++
++/*
++ * Functions defined in the PowerVR DDK for use by Mesa.
++ * All functions have a "pvr_vk_mesa_wsi" prefix.
++ */
++VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL
++pvr_vk_mesa_wsi_sym_addr(VkPhysicalDevice physicalDevice,
++                         const char *name);
++
++#endif /* PVR_MESA_WSI_INTERFACE_H */
+diff --git a/src/pvr/wsi/pvr_wsi.c b/src/pvr/wsi/pvr_wsi.c
+new file mode 100644
+index 0000000..a1fcdfd
+--- /dev/null
++++ b/src/pvr/wsi/pvr_wsi.c
+@@ -0,0 +1,333 @@
++/*
++ * Copyright © Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include <string.h>
++
++#include "pvr_wsi.h"
++#include "pvr_mesa_wsi_interface.h"
++
++VkResult
++pvr_mesa_wsi_init(struct pvr_mesa_wsi **pmwsi,
++                  VkPhysicalDevice physicalDevice,
++                  PFN_vkVoidFunction (VKAPI_PTR *pvr_vk_mesa_wsi_sym_addr)
++                     (VkPhysicalDevice physicalDevice, const char *),
++                  const VkAllocationCallbacks *alloc,
++                  int fd,
++                  bool sw)
++{
++   struct pvr_mesa_wsi *mwsi;
++   VkResult result;
++
++   mwsi = vk_zalloc(alloc, sizeof(*mwsi), 8,
++                    VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
++   if (!mwsi)
++      return VK_ERROR_OUT_OF_HOST_MEMORY;
++
++   mwsi->symtab.pvr_vk_mesa_wsi_sym_addr = pvr_vk_mesa_wsi_sym_addr;
++   mwsi->physicalDevice = physicalDevice;
++
++   result = wsi_device_init(&mwsi->wsi,
++                            physicalDevice,
++                            pvr_vk_mesa_wsi_sym_addr,
++                            alloc,
++                            fd, NULL, sw);
++   if (result != VK_SUCCESS) {
++      vk_free(alloc, mwsi);
++      return result;
++   }
++
++   if (!sw)
++      mwsi->wsi.supports_modifiers = true;
++
++   *pmwsi = mwsi;
++
++   return VK_SUCCESS;
++}
++
++void
++pvr_mesa_wsi_finish(struct pvr_mesa_wsi *mwsi,
++		    const VkAllocationCallbacks *alloc)
++{
++   wsi_device_finish(&mwsi->wsi, alloc);
++
++   vk_free(alloc, mwsi);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_support(struct pvr_mesa_wsi *mwsi,
++                                        uint32_t queueFamilyIndex,
++                                        VkSurfaceKHR surface,
++                                        VkBool32 *pSupported)
++{
++   return wsi_common_get_surface_support(&mwsi->wsi,
++                                         queueFamilyIndex,
++                                         surface,
++                                         pSupported);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_capabilities(struct pvr_mesa_wsi *mwsi,
++                                             VkSurfaceKHR surface,
++                                             VkSurfaceCapabilitiesKHR *pSurfaceCapabilities)
++{
++   return wsi_common_get_surface_capabilities(&mwsi->wsi,
++                                              surface,
++                                              pSurfaceCapabilities);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_capabilities2(struct pvr_mesa_wsi *mwsi,
++                                              const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
++                                              VkSurfaceCapabilities2KHR *pSurfaceCapabilities)
++{
++   return wsi_common_get_surface_capabilities2(&mwsi->wsi,
++                                               pSurfaceInfo,
++                                               pSurfaceCapabilities);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_capabilities2ext(struct pvr_mesa_wsi *mwsi,
++                                                 VkSurfaceKHR surface,
++                                                 VkSurfaceCapabilities2EXT *pSurfaceCapabilities)
++{
++   return wsi_common_get_surface_capabilities2ext(&mwsi->wsi,
++                                                  surface,
++                                                  pSurfaceCapabilities);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_formats(struct pvr_mesa_wsi *mwsi,
++                                        VkSurfaceKHR surface,
++                                        uint32_t *pSurfaceFormatCount,
++                                        VkSurfaceFormatKHR *pSurfaceFormats)
++{
++   return wsi_common_get_surface_formats(&mwsi->wsi,
++                                         surface,
++                                         pSurfaceFormatCount,
++                                         pSurfaceFormats);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_formats2(struct pvr_mesa_wsi *mwsi,
++                                         const VkPhysicalDeviceSurfaceInfo2KHR *pSurfaceInfo,
++                                         uint32_t *pSurfaceFormatCount,
++                                         VkSurfaceFormat2KHR *pSurfaceFormats)
++{
++   return wsi_common_get_surface_formats2(&mwsi->wsi,
++                                          pSurfaceInfo,
++                                          pSurfaceFormatCount,
++                                          pSurfaceFormats);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_surface_present_modes(struct pvr_mesa_wsi *mwsi,
++                                              VkSurfaceKHR surface,
++                                              uint32_t *pPresentModeCount,
++                                              VkPresentModeKHR *pPresentModes)
++{
++   return wsi_common_get_surface_present_modes(&mwsi->wsi,
++                                               surface,
++                                               pPresentModeCount,
++                                               pPresentModes);
++}
++
++VkResult
++pvr_mesa_wsi_common_create_swapchain(struct pvr_mesa_wsi *mwsi,
++                                     VkDevice device,
++                                     const VkSwapchainCreateInfoKHR *pCreateInfo,
++                                     const VkAllocationCallbacks *pAllocator,
++                                     VkSwapchainKHR *pSwapchain)
++{
++   return wsi_common_create_swapchain(&mwsi->wsi,
++                                      device,
++                                      pCreateInfo,
++                                      pAllocator,
++                                      pSwapchain);
++}
++
++void
++pvr_mesa_wsi_common_destroy_swapchain(UNUSED struct pvr_mesa_wsi *mwsi,
++                                      VkDevice device,
++                                      VkSwapchainKHR swapchain,
++                                      const VkAllocationCallbacks *pAllocator)
++{
++   return wsi_common_destroy_swapchain(device,
++                                       swapchain,
++                                       pAllocator);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_images(UNUSED struct pvr_mesa_wsi *mwsi,
++                               VkSwapchainKHR swapchain,
++                               uint32_t *pSwapchainImageCount,
++                               VkImage *pSwapchainImages)
++{
++   return wsi_common_get_images(swapchain,
++                                pSwapchainImageCount,
++                                pSwapchainImages);
++}
++
++VkResult
++pvr_mesa_wsi_common_acquire_next_image2(struct pvr_mesa_wsi *mwsi,
++                                        VkDevice device,
++                                        const VkAcquireNextImageInfoKHR *pAcquireInfo,
++                                        uint32_t *pImageIndex)
++{
++   return wsi_common_acquire_next_image2(&mwsi->wsi,
++                                         device,
++                                         pAcquireInfo,
++                                         pImageIndex);
++}
++
++VkResult
++pvr_mesa_wsi_common_queue_present(struct pvr_mesa_wsi *mwsi,
++                                  VkDevice device,
++                                  VkQueue queue,
++                                  int queue_family_index,
++                                  const VkPresentInfoKHR *pPresentInfo)
++{
++   return wsi_common_queue_present(&mwsi->wsi,
++                                   device,
++                                   queue,
++                                   queue_family_index,
++                                   pPresentInfo);
++}
++
++VkResult
++pvr_mesa_wsi_common_get_present_rectangles(struct pvr_mesa_wsi *mwsi,
++                                           VkSurfaceKHR surface,
++                                           uint32_t* pRectCount,
++                                           VkRect2D* pRects)
++{
++   return wsi_common_get_present_rectangles(&mwsi->wsi,
++                                            surface,
++                                            pRectCount,
++                                            pRects);
++}
++
++/*
++ * The mwsi parameter is currently unused. Note that it is invalid for
++ * pvr_mesa_wsi_init, which is responsible for allocating it.
++*/
++PUBLIC void *
++pvr_mesa_wsi_sym_addr(UNUSED struct pvr_mesa_wsi *mwsi, const char *name)
++{
++   static const struct {
++      char *name;
++      void *addr;
++   } lookup[] = {
++      { "pvr_mesa_wsi_init",
++            pvr_mesa_wsi_init },
++      { "pvr_mesa_wsi_finish",
++            pvr_mesa_wsi_finish },
++      { "pvr_mesa_wsi_common_get_surface_support",
++            pvr_mesa_wsi_common_get_surface_support },
++      { "pvr_mesa_wsi_common_get_surface_capabilities",
++            pvr_mesa_wsi_common_get_surface_capabilities },
++      { "pvr_mesa_wsi_common_get_surface_capabilities2",
++            pvr_mesa_wsi_common_get_surface_capabilities2 },
++      { "pvr_mesa_wsi_common_get_surface_capabilities2ext",
++            pvr_mesa_wsi_common_get_surface_capabilities2ext },
++      { "pvr_mesa_wsi_common_get_surface_formats",
++            pvr_mesa_wsi_common_get_surface_formats },
++      { "pvr_mesa_wsi_common_get_surface_formats2",
++            pvr_mesa_wsi_common_get_surface_formats2 },
++      { "pvr_mesa_wsi_common_get_surface_present_modes",
++            pvr_mesa_wsi_common_get_surface_present_modes },
++      { "pvr_mesa_wsi_common_create_swapchain",
++            pvr_mesa_wsi_common_create_swapchain },
++      { "pvr_mesa_wsi_common_destroy_swapchain",
++            pvr_mesa_wsi_common_destroy_swapchain },
++      { "pvr_mesa_wsi_common_get_images",
++            pvr_mesa_wsi_common_get_images },
++      { "pvr_mesa_wsi_common_acquire_next_image2",
++            pvr_mesa_wsi_common_acquire_next_image2 },
++      { "pvr_mesa_wsi_common_queue_present",
++            pvr_mesa_wsi_common_queue_present },
++      { "pvr_mesa_wsi_common_get_present_rectangles",
++            pvr_mesa_wsi_common_get_present_rectangles },
++#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
++      { "pvr_mesa_wsi_get_physical_device_wayland_presentation_support",
++            pvr_mesa_wsi_get_physical_device_wayland_presentation_support },
++      { "pvr_mesa_wsi_create_wayland_surface",
++            pvr_mesa_wsi_create_wayland_surface },
++#endif
++#if defined(VK_USE_PLATFORM_XCB_KHR)
++      { "pvr_mesa_wsi_get_physical_device_xcb_presentation_support",
++            pvr_mesa_wsi_get_physical_device_xcb_presentation_support },
++      { "pvr_mesa_wsi_create_xcb_surface",
++            pvr_mesa_wsi_create_xcb_surface },
++#endif
++#if defined(VK_USE_PLATFORM_XLIB_KHR)
++      { "pvr_mesa_wsi_create_xlib_surface",
++            pvr_mesa_wsi_create_xlib_surface },
++#endif
++      { "pvr_mesa_wsi_display_get_physical_device_display_properties",
++            pvr_mesa_wsi_display_get_physical_device_display_properties },
++      { "pvr_mesa_wsi_display_get_physical_device_display_properties2",
++            pvr_mesa_wsi_display_get_physical_device_display_properties2 },
++      { "pvr_mesa_wsi_display_get_physical_device_display_plane_properties",
++            pvr_mesa_wsi_display_get_physical_device_display_plane_properties },
++      { "pvr_mesa_wsi_display_get_physical_device_display_plane_properties2",
++            pvr_mesa_wsi_display_get_physical_device_display_plane_properties2 },
++      { "pvr_mesa_wsi_display_get_display_plane_supported_displays",
++            pvr_mesa_wsi_display_get_display_plane_supported_displays },
++      { "pvr_mesa_wsi_display_get_display_mode_properties",
++            pvr_mesa_wsi_display_get_display_mode_properties },
++      { "pvr_mesa_wsi_display_get_display_mode_properties2",
++            pvr_mesa_wsi_display_get_display_mode_properties2 },
++      { "pvr_mesa_wsi_display_create_display_mode",
++            pvr_mesa_wsi_display_create_display_mode },
++      { "pvr_mesa_wsi_get_display_plane_capabilities",
++            pvr_mesa_wsi_get_display_plane_capabilities },
++      { "pvr_mesa_wsi_get_display_plane_capabilities2",
++            pvr_mesa_wsi_get_display_plane_capabilities2 },
++      { "pvr_mesa_wsi_create_display_surface",
++            pvr_mesa_wsi_create_display_surface },
++      { "pvr_mesa_wsi_release_display",
++            pvr_mesa_wsi_release_display },
++#if defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT)
++      { "pvr_mesa_wsi_acquire_xlib_display",
++            pvr_mesa_wsi_acquire_xlib_display },
++      { "pvr_mesa_wsi_get_randr_output_display",
++            pvr_mesa_wsi_get_randr_output_display },
++#endif
++      { "pvr_mesa_wsi_display_power_control",
++            pvr_mesa_wsi_display_power_control },
++      { "pvr_mesa_wsi_register_device_event",
++            pvr_mesa_wsi_register_device_event },
++      { "pvr_mesa_wsi_register_display_event",
++            pvr_mesa_wsi_register_display_event },
++      { "pvr_mesa_wsi_get_swapchain_counter",
++            pvr_mesa_wsi_get_swapchain_counter },
++   };
++   unsigned i;
++
++   for (i = 0; i < ARRAY_SIZE(lookup); i++) {
++      if (!strcmp(name, lookup[i].name))
++         return lookup[i].addr;
++   }
++
++   return NULL;
++}
+diff --git a/src/pvr/wsi/pvr_wsi.h b/src/pvr/wsi/pvr_wsi.h
+new file mode 100644
+index 0000000..142358d
+--- /dev/null
++++ b/src/pvr/wsi/pvr_wsi.h
+@@ -0,0 +1,78 @@
++/*
++ * Copyright © Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#if !defined PVR_WSI_H
++#define PVR_WSI_H
++
++#include "util/macros.h"
++#include "util/u_memory.h"
++#include "util/u_atomic.h"
++
++#include "wsi_common.h"
++
++#define  Container(p, s, m) ((s *) ((uintptr_t)(p) - Offset(s, m)))
++
++#define _MAKE_STRING(x) # x
++#define MAKE_STRING(x) _MAKE_STRING(x)
++
++#define LOOKUP_DDK(mwsi, sym) \
++   mwsi->symtab.pvr_vk_mesa_wsi_sym_addr(MAKE_STRING(sym))
++
++#define JUMP_DDK(mwsi, sym, ...)                          \
++   do {                                                   \
++      void *_entry = p_atomic_read(&mwsi->symtab.sym);    \
++                                                          \
++      if (!_entry) {                                      \
++         _entry = LOOKUP_DDK(mwsi, sym);                  \
++                                                          \
++         if (_entry)                                      \
++            p_atomic_set(&mwsi->symtab.sym, _entry);      \
++      }                                                   \
++                                                          \
++      if (_entry) {                                       \
++         __typeof__(mwsi->symtab.sym) _func = _entry;     \
++                                                          \
++         return _func(__VA_ARGS__);                       \
++      }                                                   \
++   } while(0)
++
++struct pvr_vk_mesa_wsi_sym_tab
++{
++   PFN_vkVoidFunction (VKAPI_PTR *pvr_vk_mesa_wsi_sym_addr)
++      (VkPhysicalDevice physicalDevice, const char *);
++};
++
++struct pvr_mesa_wsi
++{
++   struct wsi_device wsi;
++   struct pvr_vk_mesa_wsi_sym_tab symtab;
++   VkPhysicalDevice physicalDevice;
++};
++
++static inline struct pvr_mesa_wsi *pvr_mesa_wsi(struct wsi_device *wsi_ptr)
++{
++   return Container(wsi_ptr, struct pvr_mesa_wsi, wsi);
++}
++
++#endif
+diff --git a/src/pvr/wsi/pvr_wsi_display.c b/src/pvr/wsi/pvr_wsi_display.c
+new file mode 100644
+index 0000000..65a1c99
+--- /dev/null
++++ b/src/pvr/wsi/pvr_wsi_display.c
+@@ -0,0 +1,293 @@
++/*
++ * Copyright © Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "wsi_common_display.h"
++
++#if defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT)
++#include "wsi_common_x11.h"
++#endif
++
++#include "pvr_wsi.h"
++#include "pvr_mesa_wsi_interface.h"
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_properties(struct pvr_mesa_wsi *mwsi,
++							    VkPhysicalDevice physicalDevice,
++							    uint32_t *pPropertyCount,
++							    VkDisplayPropertiesKHR *pProperties)
++{
++   return wsi_display_get_physical_device_display_properties(physicalDevice,
++                                                             &mwsi->wsi,
++                                                             pPropertyCount,
++                                                             pProperties);
++}
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_properties2(struct pvr_mesa_wsi *mwsi,
++							     VkPhysicalDevice physicalDevice,
++							     uint32_t *pPropertyCount,
++							     VkDisplayProperties2KHR *pProperties)
++{
++   return wsi_display_get_physical_device_display_properties2(physicalDevice,
++                                                              &mwsi->wsi,
++                                                              pPropertyCount,
++                                                              pProperties);
++}
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_plane_properties(struct pvr_mesa_wsi *mwsi,
++								  VkPhysicalDevice physicalDevice,
++								  uint32_t *pPropertyCount,
++								  VkDisplayPlanePropertiesKHR *pProperties)
++{
++   return wsi_display_get_physical_device_display_plane_properties(physicalDevice,
++                                                                   &mwsi->wsi,
++                                                                   pPropertyCount,
++                                                                   pProperties);
++}
++
++VkResult
++pvr_mesa_wsi_display_get_physical_device_display_plane_properties2(struct pvr_mesa_wsi *mwsi,
++								   VkPhysicalDevice physicalDevice,
++								   uint32_t *pPropertyCount,
++								   VkDisplayPlaneProperties2KHR *pProperties)
++{
++   return wsi_display_get_physical_device_display_plane_properties2(physicalDevice,
++                                                                    &mwsi->wsi,
++                                                                    pPropertyCount,
++                                                                    pProperties);
++}
++
++VkResult
++pvr_mesa_wsi_display_get_display_plane_supported_displays(struct pvr_mesa_wsi *mwsi,
++							  VkPhysicalDevice physicalDevice,
++							  uint32_t planeIndex,
++							  uint32_t *pDisplayCount,
++							  VkDisplayKHR *pDisplays)
++{
++   return wsi_display_get_display_plane_supported_displays(physicalDevice,
++                                                           &mwsi->wsi,
++                                                           planeIndex,
++                                                           pDisplayCount,
++                                                           pDisplays);
++
++}
++
++VkResult
++pvr_mesa_wsi_display_get_display_mode_properties(struct pvr_mesa_wsi *mwsi,
++						 VkPhysicalDevice physicalDevice,
++						 VkDisplayKHR display,
++						 uint32_t *pPropertyCount,
++						 VkDisplayModePropertiesKHR *pProperties)
++{
++   return wsi_display_get_display_mode_properties(physicalDevice,
++                                                  &mwsi->wsi,
++                                                  display,
++                                                  pPropertyCount,
++                                                  pProperties);
++}
++
++VkResult
++pvr_mesa_wsi_display_get_display_mode_properties2(struct pvr_mesa_wsi *mwsi,
++                                                  VkPhysicalDevice physicalDevice,
++                                                  VkDisplayKHR display,
++                                                  uint32_t *pPropertyCount,
++                                                  VkDisplayModeProperties2KHR *pProperties)
++{
++   return wsi_display_get_display_mode_properties2(physicalDevice,
++                                                   &mwsi->wsi,
++                                                   display,
++                                                   pPropertyCount,
++                                                   pProperties);
++}
++
++VkResult
++pvr_mesa_wsi_display_create_display_mode(struct pvr_mesa_wsi *mwsi,
++                                         VkPhysicalDevice physicalDevice,
++                                         VkDisplayKHR display,
++                                         const VkDisplayModeCreateInfoKHR *pCreateInfo,
++                                         const VkAllocationCallbacks *pAllocator,
++                                         VkDisplayModeKHR *pMode)
++{
++   return wsi_display_create_display_mode(physicalDevice,
++                                          &mwsi->wsi,
++                                          display,
++                                          pCreateInfo,
++                                          pAllocator,
++                                          pMode);
++}
++
++VkResult
++pvr_mesa_wsi_get_display_plane_capabilities(struct pvr_mesa_wsi *mwsi,
++                                            VkPhysicalDevice physicalDevice,
++                                            VkDisplayModeKHR modeKhr,
++                                            uint32_t planeIndex,
++                                            VkDisplayPlaneCapabilitiesKHR *pCapabilities)
++{
++   return wsi_get_display_plane_capabilities(physicalDevice,
++                                             &mwsi->wsi,
++                                             modeKhr,
++                                             planeIndex,
++                                             pCapabilities);
++}
++
++VkResult
++pvr_mesa_wsi_get_display_plane_capabilities2(struct pvr_mesa_wsi *mwsi,
++                                             VkPhysicalDevice physicalDevice,
++                                             const VkDisplayPlaneInfo2KHR *pDisplayPlaneInfo,
++                                             VkDisplayPlaneCapabilities2KHR *pCapabilities)
++{
++   return wsi_get_display_plane_capabilities2(physicalDevice,
++                                              &mwsi->wsi,
++                                              pDisplayPlaneInfo,
++                                              pCapabilities);
++}
++
++VkResult
++pvr_mesa_wsi_create_display_surface(UNUSED struct pvr_mesa_wsi *mwsi,
++                                    VkInstance instance,
++                                    const VkAllocationCallbacks *pAllocator,
++                                    const VkDisplaySurfaceCreateInfoKHR *pCreateInfo,
++                                    VkSurfaceKHR *pSurface)
++{
++   return wsi_create_display_surface(instance,
++                                      pAllocator,
++                                      pCreateInfo,
++                                      pSurface);
++}
++
++VkResult
++pvr_mesa_wsi_release_display(struct pvr_mesa_wsi *mwsi,
++                             VkPhysicalDevice physicalDevice,
++                             VkDisplayKHR display)
++{
++   return wsi_release_display(physicalDevice,
++                              &mwsi->wsi,
++                              display);
++}
++
++#if defined(VK_USE_PLATFORM_XLIB_XRANDR_EXT)
++VkResult
++pvr_mesa_wsi_acquire_xlib_display(struct pvr_mesa_wsi *mwsi,
++                                  VkPhysicalDevice physicalDevice,
++                                  void *dpy,
++                                  VkDisplayKHR display)
++{
++   return wsi_acquire_xlib_display(physicalDevice,
++                                   &mwsi->wsi,
++                                   dpy,
++                                   display);
++}
++
++VkResult
++pvr_mesa_wsi_get_randr_output_display(struct pvr_mesa_wsi *mwsi,
++                                      VkPhysicalDevice  physicalDevice,
++                                      void *dpy,
++                                      uint32_t output,
++                                      VkDisplayKHR *pDisplay)
++{
++   return wsi_get_randr_output_display(physicalDevice,
++                                       &mwsi->wsi,
++                                       dpy,
++                                       output,
++                                       pDisplay);
++}
++#endif
++
++VkResult
++pvr_mesa_wsi_display_power_control(struct pvr_mesa_wsi *mwsi,
++                                   VkDevice device,
++                                   VkDisplayKHR display,
++                                   const VkDisplayPowerInfoEXT *pDisplayPowerInfo)
++{
++   return wsi_display_power_control(device,
++                                    &mwsi->wsi,
++                                    display,
++                                    pDisplayPowerInfo);
++}
++
++VkResult
++pvr_mesa_wsi_register_device_event(struct pvr_mesa_wsi *mwsi,
++                                   VkDevice device,
++                                   const VkDeviceEventInfoEXT *pDeviceEventInfo,
++                                   const VkAllocationCallbacks *pAllocator,
++                                   void **pFence,
++                                   int syncFd)
++{
++   struct wsi_fence *fence;
++   VkResult ret;
++
++   ret = wsi_register_device_event(device,
++                                   &mwsi->wsi,
++                                   pDeviceEventInfo,
++                                   pAllocator,
++                                   pFence ? &fence : NULL,
++                                   syncFd);
++
++   if (ret == VK_SUCCESS && pFence != NULL)
++	   *pFence = fence;
++
++   return ret;
++}
++
++VkResult
++pvr_mesa_wsi_register_display_event(struct pvr_mesa_wsi *mwsi,
++                                    VkDevice device,
++                                    VkDisplayKHR display,
++                                    const VkDisplayEventInfoEXT *pDisplayEventInfo,
++                                    const VkAllocationCallbacks  *pAllocator,
++                                    void **pFence,
++                                    int syncFd)
++{
++   struct wsi_fence *fence;
++   VkResult ret;
++
++   ret = wsi_register_display_event(device,
++                                    &mwsi->wsi,
++                                    display,
++                                    pDisplayEventInfo,
++                                    pAllocator,
++                                    pFence ? &fence : NULL,
++                                    syncFd);
++
++   if (ret == VK_SUCCESS && pFence != NULL)
++	   *pFence = fence;
++
++   return ret;
++}
++
++VkResult
++pvr_mesa_wsi_get_swapchain_counter(struct pvr_mesa_wsi *mwsi,
++                                   VkDevice device,
++                                   VkSwapchainKHR swapchain,
++                                   VkSurfaceCounterFlagBitsEXT flagBits,
++                                   uint64_t *pValue)
++{
++   return wsi_get_swapchain_counter(device,
++                                    &mwsi->wsi,
++                                    swapchain,
++                                    flagBits,
++                                    pValue);
++}
++
+diff --git a/src/pvr/wsi/pvr_wsi_wayland.c b/src/pvr/wsi/pvr_wsi_wayland.c
+new file mode 100644
+index 0000000..5b05266
+--- /dev/null
++++ b/src/pvr/wsi/pvr_wsi_wayland.c
+@@ -0,0 +1,45 @@
++/*
++ * Copyright © Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "wsi_common_wayland.h"
++
++#include "pvr_wsi.h"
++#include "pvr_mesa_wsi_interface.h"
++
++VkBool32
++pvr_mesa_wsi_get_physical_device_wayland_presentation_support(struct pvr_mesa_wsi *mwsi,
++                                                              uint32_t queueFamilyIndex,
++                                                              void *display)
++{
++   return wsi_wl_get_presentation_support(&mwsi->wsi, display);
++}
++
++VkResult
++pvr_mesa_wsi_create_wayland_surface(UNUSED struct pvr_mesa_wsi *mwsi,
++                                    const VkAllocationCallbacks *pAllocator,
++                                    const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
++                                    VkSurfaceKHR *pSurface)
++{
++   return wsi_create_wl_surface(pAllocator, pCreateInfo, pSurface);
++}
+diff --git a/src/pvr/wsi/pvr_wsi_x11.c b/src/pvr/wsi/pvr_wsi_x11.c
+new file mode 100644
+index 0000000..0a69e92
+--- /dev/null
++++ b/src/pvr/wsi/pvr_wsi_x11.c
+@@ -0,0 +1,62 @@
++/*
++ * Copyright © Imagination Technologies Ltd.
++ *
++ * The contents of this file are subject to the MIT license as set out below.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
++ * THE SOFTWARE.
++ */
++
++#include "wsi_common_x11.h"
++
++#include "pvr_wsi.h"
++#include "pvr_mesa_wsi_interface.h"
++
++VkBool32
++pvr_mesa_wsi_get_physical_device_xcb_presentation_support(struct pvr_mesa_wsi *mwsi,
++                                                          uint32_t queueFamilyIndex,
++                                                          void *connection,
++                                                          uint32_t visual_id)
++{
++   return wsi_get_physical_device_xcb_presentation_support(&mwsi->wsi,
++                                                           queueFamilyIndex,
++                                                           connection,
++                                                           visual_id);
++}
++
++VkResult
++pvr_mesa_wsi_create_xcb_surface(UNUSED struct pvr_mesa_wsi *mwsi,
++                                const VkAllocationCallbacks *pAllocator,
++                                const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
++                                VkSurfaceKHR *pSurface)
++{
++   return wsi_create_xcb_surface(pAllocator,
++				 pCreateInfo,
++				 pSurface);
++}
++
++VkResult
++pvr_mesa_wsi_create_xlib_surface(UNUSED struct pvr_mesa_wsi *mwsi,
++                                 const VkAllocationCallbacks *pAllocator,
++                                 const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
++                                 VkSurfaceKHR *pSurface)
++{
++   return wsi_create_xlib_surface(pAllocator,
++				  pCreateInfo,
++				  pSurface);
++}

+ 74 - 0
recipes-graphics/mesa/mesa-pvr/0060-vulkan-wsi-Disable-use-of-VK_EXT_pci_bus_info.patch

@@ -0,0 +1,74 @@
+From 2e1cdcd98f084bf62a2b1f4a8ecb77f3be8548db Mon Sep 17 00:00:00 2001
+From: brendan King <Brendan.King@imgtec.com>
+Date: Fri, 30 Jul 2021 15:34:13 +0100
+Subject: [PATCH] vulkan/wsi: Disable use of VK_EXT_pci_bus_info
+
+The VK_EXT_pci_bus_info related code has been wrapped in
+VULKAN_WSI_USE_PCI_BUS_INFO, effectively disabling it.
+
+Not all platforms support the VK_EXT_pci_bus_info extension.
+A better fix might be to pass another parameter to wsi_device_init,
+to indicate that the device is a PCI one.
+
+---
+ src/vulkan/wsi/wsi_common.c     | 4 ++++
+ src/vulkan/wsi/wsi_common.h     | 2 ++
+ src/vulkan/wsi/wsi_common_drm.c | 4 ++++
+ 3 files changed, 10 insertions(+)
+
+diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
+index b1360ed..66d6683 100644
+--- a/src/vulkan/wsi/wsi_common.c
++++ b/src/vulkan/wsi/wsi_common.c
+@@ -56,11 +56,15 @@ wsi_device_init(struct wsi_device *wsi,
+    WSI_GET_CB(GetPhysicalDeviceQueueFamilyProperties);
+ #undef WSI_GET_CB
+ 
++#if defined(VULKAN_WSI_USE_PCI_BUS_INFO)
+    wsi->pci_bus_info.sType =
+       VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT;
++#endif
+    VkPhysicalDeviceProperties2 pdp2 = {
+       .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
++#if defined(VULKAN_WSI_USE_PCI_BUS_INFO)
+       .pNext = &wsi->pci_bus_info,
++#endif
+    };
+    GetPhysicalDeviceProperties2(pdevice, &pdp2);
+ 
+diff --git a/src/vulkan/wsi/wsi_common.h b/src/vulkan/wsi/wsi_common.h
+index c2563c6..4a73366 100644
+--- a/src/vulkan/wsi/wsi_common.h
++++ b/src/vulkan/wsi/wsi_common.h
+@@ -112,7 +112,9 @@ struct wsi_device {
+    VkPhysicalDeviceMemoryProperties memory_props;
+    uint32_t queue_family_count;
+ 
++#if defined(VULKAN_WSI_USE_PCI_BUS_INFO)
+    VkPhysicalDevicePCIBusInfoPropertiesEXT pci_bus_info;
++#endif
+ 
+    bool supports_modifiers;
+    uint32_t maxImageDimension2D;
+diff --git a/src/vulkan/wsi/wsi_common_drm.c b/src/vulkan/wsi/wsi_common_drm.c
+index 6201891..49a78cf 100644
+--- a/src/vulkan/wsi/wsi_common_drm.c
++++ b/src/vulkan/wsi/wsi_common_drm.c
+@@ -45,6 +45,7 @@ wsi_device_matches_drm_fd(const struct wsi_device *wsi, int drm_fd)
+    if (ret)
+       return false;
+ 
++#if defined(VULKAN_WSI_USE_PCI_BUS_INFO)
+    bool match = false;
+    switch (fd_device->bustype) {
+    case DRM_BUS_PCI:
+@@ -57,6 +58,9 @@ wsi_device_matches_drm_fd(const struct wsi_device *wsi, int drm_fd)
+    default:
+       break;
+    }
++#else
++   const bool match = true;
++#endif
+ 
+    drmFreeDevice(&fd_device);
+ 

+ 32 - 0
recipes-graphics/mesa/mesa-pvr/0061-vulkan-wsi-default-to-force_bgra8_unorm_first-true.patch

@@ -0,0 +1,32 @@
+From 1669fb1a9b11036ac939635090a3e7bba849f339 Mon Sep 17 00:00:00 2001
+From: brendan King <Brendan.King@imgtec.com>
+Date: Mon, 2 Aug 2021 16:29:36 +0100
+Subject: [PATCH] vulkan/wsi: default to force_bgra8_unorm_first true
+
+If VULKAN_WSI_BGRA8_SNORM_FIRST is not defined, default to
+force_bgra8_unorm_first true.
+
+This brings Mesa WSI into line with IMG WSI with regards to the
+VK_FORMAT_B8G8R8A8_UNORM and VK_FORMAT_B8G8R8A8_SRGB formats.
+With this change, the IMG Vulkan unit test, vkbonjour, will default
+to VK_FORMAT_B8G8R8A8_UNORM rather than VK_FORMAT_B8G8R8A8_SRGB.
+
+---
+ src/vulkan/wsi/wsi_common.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/src/vulkan/wsi/wsi_common.c b/src/vulkan/wsi/wsi_common.c
+index 66d6683..1ffca6f 100644
+--- a/src/vulkan/wsi/wsi_common.c
++++ b/src/vulkan/wsi/wsi_common.c
+@@ -158,6 +158,10 @@ wsi_device_init(struct wsi_device *wsi,
+             driQueryOptionb(dri_options, "vk_wsi_force_bgra8_unorm_first");
+       }
+    }
++#if !defined(VULKAN_WSI_BGRA8_SNORM_FIRST)
++   else
++      wsi->force_bgra8_unorm_first = true;
++#endif
+ 
+    return VK_SUCCESS;
+ #if defined(VK_USE_PLATFORM_XCB_KHR) || \

+ 62 - 0
recipes-graphics/mesa/mesa-pvr/0062-vulkan-wsi-enable-additional-formats-for-Wayland.patch

@@ -0,0 +1,62 @@
+From 5e54d8fe1f2daab845340cf7ad519b85ad27fb27 Mon Sep 17 00:00:00 2001
+From: brendan King <Brendan.King@imgtec.com>
+Date: Mon, 2 Aug 2021 11:21:16 +0100
+Subject: [PATCH] vulkan/wsi: enable additional formats for Wayland
+
+Add VK_FORMAT_R5G6B5_UNORM_PACK16.
+
+This is for compatibility with IMG WSI.
+
+---
+ src/vulkan/wsi/wsi_common_wayland.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/src/vulkan/wsi/wsi_common_wayland.c b/src/vulkan/wsi/wsi_common_wayland.c
+index 1109d3f..e520aa9 100644
+--- a/src/vulkan/wsi/wsi_common_wayland.c
++++ b/src/vulkan/wsi/wsi_common_wayland.c
+@@ -68,6 +68,7 @@ struct wsi_wl_display_dmabuf {
+    struct {
+       struct u_vector                           argb8888;
+       struct u_vector                           xrgb8888;
++      struct u_vector                           rgb565;
+    } modifiers;
+ };
+ 
+@@ -441,6 +442,9 @@ dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *dmabuf,
+    case WL_DRM_FORMAT_XRGB8888:
+       modifiers = &display->dmabuf.modifiers.xrgb8888;
+       break;
++   case WL_DRM_FORMAT_RGB565:
++      modifiers = &display->dmabuf.modifiers.rgb565;
++      break;
+    default:
+       return; /* Unsupported format */
+    }
+@@ -527,6 +531,7 @@ wsi_wl_display_finish(struct wsi_wl_display *display)
+    u_vector_finish(&display->dmabuf.formats);
+    u_vector_finish(&display->dmabuf.modifiers.argb8888);
+    u_vector_finish(&display->dmabuf.modifiers.xrgb8888);
++   u_vector_finish(&display->dmabuf.modifiers.rgb565);
+    if (display->swrast.wl_shm)
+       wl_shm_destroy(display->swrast.wl_shm);
+    if (display->drm.wl_drm)
+@@ -563,6 +568,8 @@ wsi_wl_display_init(struct wsi_wayland *wsi_wl,
+           !u_vector_init(&display->dmabuf.modifiers.argb8888,
+                          sizeof(uint64_t), 32) ||
+           !u_vector_init(&display->dmabuf.modifiers.xrgb8888,
++                         sizeof(uint64_t), 32) ||
++          !u_vector_init(&display->dmabuf.modifiers.rgb565,
+                          sizeof(uint64_t), 32)) {
+          result = VK_ERROR_OUT_OF_HOST_MEMORY;
+          goto fail;
+@@ -1356,6 +1363,9 @@ wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
+       case WL_DRM_FORMAT_XRGB8888:
+          modifiers = &chain->display->dmabuf.modifiers.xrgb8888;
+          break;
++      case WL_DRM_FORMAT_RGB565:
++         modifiers = &chain->display->dmabuf.modifiers.rgb565;
++         break;
+       default:
+          modifiers = NULL;
+          break;

+ 25 - 0
recipes-graphics/mesa/mesa-pvr/0063-vulkan-wsi-enable-additional-formats-for-Display.patch

@@ -0,0 +1,25 @@
+From 4cd52452e7c525e69005989c3c7376158d9d3c7e Mon Sep 17 00:00:00 2001
+From: brendan King <Brendan.King@imgtec.com>
+Date: Tue, 3 Aug 2021 15:44:57 +0100
+Subject: [PATCH] vulkan/wsi: enable additional formats for Display
+
+Add VK_FORMAT_R5G6B5_UNORM_PACK16.
+
+This is for compatibility with IMG WSI.
+
+---
+ src/vulkan/wsi/wsi_common_display.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/src/vulkan/wsi/wsi_common_display.c b/src/vulkan/wsi/wsi_common_display.c
+index f135b4e..298c788 100644
+--- a/src/vulkan/wsi/wsi_common_display.c
++++ b/src/vulkan/wsi/wsi_common_display.c
+@@ -900,6 +900,7 @@ static const struct {
+ } available_surface_formats[] = {
+    { .format = VK_FORMAT_B8G8R8A8_SRGB, .drm_format = DRM_FORMAT_XRGB8888 },
+    { .format = VK_FORMAT_B8G8R8A8_UNORM, .drm_format = DRM_FORMAT_XRGB8888 },
++   { .format = VK_FORMAT_R5G6B5_UNORM_PACK16, .drm_format = DRM_FORMAT_RGB565 },
+ };
+ 
+ static void

+ 180 - 0
recipes-graphics/mesa/mesa-pvr/0064-mesa-main-dri-add-YUV420_3PLANE-and-YVU420_3PLANE.patch

@@ -0,0 +1,180 @@
+From fa99f7580504ea68bf36cf1f0dcf7cdcd0338916 Mon Sep 17 00:00:00 2001
+From: Luigi Santivetti <luigi.santivetti@imgtec.com>
+Date: Thu, 12 Aug 2021 00:55:46 +0100
+Subject: [PATCH] mesa/main: dri: add YUV420_3PLANE and YVU420_3PLANE
+
+---
+ include/GL/internal/dri_interface.h    |  2 ++
+ src/gallium/include/pipe/p_format.h    |  3 +++
+ src/mesa/drivers/dri/common/utils.c    | 18 ++++++++++++++++++
+ src/mesa/drivers/dri/pvr/dri_support.h |  2 ++
+ src/mesa/drivers/dri/pvr/pvrutil.c     | 13 +++++++++++++
+ src/mesa/main/format_info.py           |  2 +-
+ src/mesa/main/formats.c                |  2 ++
+ src/mesa/main/formats.csv              |  2 ++
+ src/mesa/main/formats.h                |  6 ++++++
+ 9 files changed, 49 insertions(+), 1 deletion(-)
+
+diff --git a/include/GL/internal/dri_interface.h b/include/GL/internal/dri_interface.h
+index 080d191..7d9a1bd 100644
+--- a/include/GL/internal/dri_interface.h
++++ b/include/GL/internal/dri_interface.h
+@@ -1424,6 +1424,8 @@ struct __DRIdri2ExtensionRec {
+ #define __DRI_IMAGE_FORMAT_BGR888       0x101a
+ #define __DRI_IMAGE_FORMAT_NV12         0x101b
+ #define __DRI_IMAGE_FORMAT_NV21         0x101c
++#define __DRI_IMAGE_FORMAT_YU12         0x101d
++#define __DRI_IMAGE_FORMAT_YV12         0x101e
+ 
+ #define __DRI_IMAGE_USE_SHARE		0x0001
+ #define __DRI_IMAGE_USE_SCANOUT		0x0002
+diff --git a/src/gallium/include/pipe/p_format.h b/src/gallium/include/pipe/p_format.h
+index fd65337..970f075 100644
+--- a/src/gallium/include/pipe/p_format.h
++++ b/src/gallium/include/pipe/p_format.h
+@@ -516,6 +516,9 @@ enum pipe_format {
+    PIPE_FORMAT_YUV420_2PLANE,
+    PIPE_FORMAT_YVU420_2PLANE,
+ 
++   PIPE_FORMAT_YUV420_3PLANE,
++   PIPE_FORMAT_YVU420_3PLANE,
++
+    PIPE_FORMAT_COUNT
+ };
+ 
+diff --git a/src/mesa/drivers/dri/common/utils.c b/src/mesa/drivers/dri/common/utils.c
+index d268dc4..df5f8cb 100644
+--- a/src/mesa/drivers/dri/common/utils.c
++++ b/src/mesa/drivers/dri/common/utils.c
+@@ -330,6 +330,24 @@ driCreateConfigs(mesa_format format,
+       yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT;
+       yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT;
+       break;
++   case MESA_FORMAT_YUV420_3PLANE:
++      masks = format_table[11].masks;
++      shifts = format_table[11].shifts;
++      is_yuv = true; /* FIXME: This should come from formats_info.py */
++      yuv_order = __DRI_ATTRIB_YUV_ORDER_YUV_BIT;
++      yuv_num_planes = 3;
++      yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT;
++      yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT;
++      break;
++   case MESA_FORMAT_YVU420_3PLANE:
++      masks = format_table[11].masks;
++      shifts = format_table[11].shifts;
++      is_yuv = true; /* FIXME: This should come from formats_info.py */
++      yuv_order = __DRI_ATTRIB_YUV_ORDER_YVU_BIT;
++      yuv_num_planes = 3;
++      yuv_subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT;
++      yuv_plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT;
++      break;
+    default:
+       fprintf(stderr, "[%s:%u] Unknown framebuffer type %s (%d).\n",
+               __func__, __LINE__,
+diff --git a/src/mesa/drivers/dri/pvr/dri_support.h b/src/mesa/drivers/dri/pvr/dri_support.h
+index 6314104..51524a9 100644
+--- a/src/mesa/drivers/dri/pvr/dri_support.h
++++ b/src/mesa/drivers/dri/pvr/dri_support.h
+@@ -92,6 +92,8 @@ typedef enum
+ #define PVRDRI_MESA_FORMAT_YVU420_2PLANE	8
+ #define PVRDRI_MESA_FORMAT_B8G8R8A8_SRGB	9
+ #define PVRDRI_MESA_FORMAT_R8G8B8A8_SRGB	10
++#define PVRDRI_MESA_FORMAT_YUV420_3PLANE	11
++#define PVRDRI_MESA_FORMAT_YVU420_3PLANE	12
+ 
+ typedef struct __DRIimageRec __DRIimage;
+ 
+diff --git a/src/mesa/drivers/dri/pvr/pvrutil.c b/src/mesa/drivers/dri/pvr/pvrutil.c
+index d107a5d..ee11ac5 100644
+--- a/src/mesa/drivers/dri/pvr/pvrutil.c
++++ b/src/mesa/drivers/dri/pvr/pvrutil.c
+@@ -118,6 +118,10 @@ PVRDRIMesaFormatToMesaFormat(int pvrdri_mesa_format)
+       return MESA_FORMAT_B8G8R8A8_SRGB;
+    case PVRDRI_MESA_FORMAT_R8G8B8A8_SRGB:
+       return MESA_FORMAT_R8G8B8A8_SRGB;
++   case PVRDRI_MESA_FORMAT_YUV420_3PLANE:
++         return MESA_FORMAT_YUV420_3PLANE;
++   case PVRDRI_MESA_FORMAT_YVU420_3PLANE:
++         return MESA_FORMAT_YVU420_3PLANE;
+    default:
+       __driUtilMessage("%s: Unknown format: %d", __func__, pvrdri_mesa_format);
+       break;
+@@ -178,6 +182,11 @@ PVRDRIFormatToFourCC(int dri_format)
+       return DRM_FORMAT_NV12;
+    case __DRI_IMAGE_FORMAT_NV21:
+       return DRM_FORMAT_NV21;
++   case __DRI_IMAGE_FORMAT_YU12:
++      return DRM_FORMAT_YUV420;
++   case __DRI_IMAGE_FORMAT_YV12:
++      return DRM_FORMAT_YVU420;
++
+    default:
+       __driUtilMessage("%s: Unknown format: %d", __func__, dri_format);
+       break;
+@@ -238,6 +247,10 @@ PVRDRIFourCCToDRIFormat(int iFourCC)
+       return __DRI_IMAGE_FORMAT_NV12;
+    case DRM_FORMAT_NV21:
+       return __DRI_IMAGE_FORMAT_NV21;
++   case DRM_FORMAT_YUV420:
++      return __DRI_IMAGE_FORMAT_YU12;
++   case DRM_FORMAT_YVU420:
++      return __DRI_IMAGE_FORMAT_YV12;
+    default:
+       __driUtilMessage("%s: Unknown format: %d", __func__, iFourCC);
+       break;
+diff --git a/src/mesa/main/format_info.py b/src/mesa/main/format_info.py
+index d58403e..bc18db7 100644
+--- a/src/mesa/main/format_info.py
++++ b/src/mesa/main/format_info.py
+@@ -29,7 +29,7 @@ import sys
+ def get_gl_base_format(fmat):
+    if fmat.name == 'MESA_FORMAT_NONE':
+       return 'GL_NONE'
+-   elif fmat.name in ['MESA_FORMAT_YCBCR', 'MESA_FORMAT_YCBCR_REV', 'MESA_FORMAT_YUV420_2PLANE', 'MESA_FORMAT_YVU420_2PLANE']:
++   elif fmat.name in ['MESA_FORMAT_YCBCR', 'MESA_FORMAT_YCBCR_REV', 'MESA_FORMAT_YUV420_2PLANE', 'MESA_FORMAT_YVU420_2PLANE', 'MESA_FORMAT_YUV420_3PLANE', 'MESA_FORMAT_YVU420_3PLANE']:
+       return 'GL_YCBCR_MESA'
+    elif fmat.has_channel('r'):
+       if fmat.has_channel('g'):
+diff --git a/src/mesa/main/formats.c b/src/mesa/main/formats.c
+index f81caec..d7b0d0a 100644
+--- a/src/mesa/main/formats.c
++++ b/src/mesa/main/formats.c
+@@ -1455,6 +1455,8 @@ _mesa_format_matches_format_and_type(mesa_format mformat,
+    switch (mformat) {
+    case MESA_FORMAT_YUV420_2PLANE:
+    case MESA_FORMAT_YVU420_2PLANE:
++   case MESA_FORMAT_YUV420_3PLANE:
++   case MESA_FORMAT_YVU420_3PLANE:
+       return false;
+ 
+    default:
+diff --git a/src/mesa/main/formats.csv b/src/mesa/main/formats.csv
+index b2d4765..825443b 100644
+--- a/src/mesa/main/formats.csv
++++ b/src/mesa/main/formats.csv
+@@ -94,6 +94,8 @@ MESA_FORMAT_YCBCR                         , other , 1, 1, 1, x16 ,     ,     ,
+ MESA_FORMAT_YCBCR_REV                     , other , 1, 1, 1, x16 ,     ,     ,     , xyzw, yuv
+ MESA_FORMAT_YUV420_2PLANE                 , other , 1, 1, 1, x8  ,     ,     ,     , y___, yuv
+ MESA_FORMAT_YVU420_2PLANE                 , other , 1, 1, 1, x8  ,     ,     ,     , y___, yuv
++MESA_FORMAT_YUV420_3PLANE                 , other , 1, 1, 1, x8  ,     ,     ,     , y___, yuv
++MESA_FORMAT_YVU420_3PLANE                 , other , 1, 1, 1, x8  ,     ,     ,     , y___, yuv
+ 
+ MESA_FORMAT_RG_RB_UNORM8                  , other , 2, 1, 1, x16 ,     ,     ,     , xyz1, rgb
+ MESA_FORMAT_GR_BR_UNORM8                  , other , 2, 1, 1, x16 ,     ,     ,     , xyz1, rgb
+diff --git a/src/mesa/main/formats.h b/src/mesa/main/formats.h
+index 0e778d6..1ab03d2 100644
+--- a/src/mesa/main/formats.h
++++ b/src/mesa/main/formats.h
+@@ -624,6 +624,12 @@ typedef enum pipe_format mesa_format;
+ #define HAVE_MESA_FORMAT_YVU420_2PLANE
+ #define MESA_FORMAT_YVU420_2PLANE                PIPE_FORMAT_YVU420_2PLANE
+ 
++#define HAVE_MESA_FORMAT_YUV420_3PLANE
++#define MESA_FORMAT_YUV420_3PLANE                PIPE_FORMAT_YUV420_3PLANE
++
++#define HAVE_MESA_FORMAT_YVU420_3PLANE
++#define MESA_FORMAT_YVU420_3PLANE                PIPE_FORMAT_YVU420_3PLANE
++
+ #define MESA_FORMAT_COUNT                        PIPE_FORMAT_COUNT
+ 
+ /* Packed to array format adapters */

+ 55 - 0
recipes-graphics/mesa/mesa-pvr/0065-egl-null-add-support-for-YU12-and-YV12.patch

@@ -0,0 +1,55 @@
+From 033231f1902849d2ff8ff3c82bef02174a62e01d Mon Sep 17 00:00:00 2001
+From: Luigi Santivetti <luigi.santivetti@imgtec.com>
+Date: Mon, 23 Aug 2021 09:18:37 +0100
+Subject: [PATCH] egl/null: add support for YU12 and YV12
+
+---
+ src/egl/drivers/dri2/platform_null.c | 28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+diff --git a/src/egl/drivers/dri2/platform_null.c b/src/egl/drivers/dri2/platform_null.c
+index c9409c0..03fb5e0 100644
+--- a/src/egl/drivers/dri2/platform_null.c
++++ b/src/egl/drivers/dri2/platform_null.c
+@@ -107,6 +107,20 @@ static const struct dri2_null_yuv_attrib {
+       .num_planes = 2,
+       .plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT,
+    },
++   {
++      /* __DRI_IMAGE_FORMAT_YU12 */
++      .order = __DRI_ATTRIB_YUV_ORDER_YUV_BIT,
++      .subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT,
++      .num_planes = 3,
++      .plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT,
++   },
++   {
++      /* __DRI_IMAGE_FORMAT_YV12 */
++      .order = __DRI_ATTRIB_YUV_ORDER_YVU_BIT,
++      .subsample = __DRI_ATTRIB_YUV_SUBSAMPLE_4_2_0_BIT,
++      .num_planes = 3,
++      .plane_bpp = __DRI_ATTRIB_YUV_PLANE_BPP_8_BIT,
++   },
+ };
+ 
+ /*
+@@ -162,6 +176,20 @@ static const struct dri2_null_format {
+       .rgba_sizes = { 0, 0, 0, 0 },
+       .yuv = &dri2_null_yuv_attribs[2],
+    },
++   {
++      .drm_format = DRM_FORMAT_YUV420,
++      .dri_image_format = __DRI_IMAGE_FORMAT_YU12,
++      .rgba_shifts = { -1, -1, -1, -1 },
++      .rgba_sizes = { 0, 0, 0, 0 },
++      .yuv = &dri2_null_yuv_attribs[3],
++   },
++   {
++      .drm_format = DRM_FORMAT_YVU420,
++      .dri_image_format = __DRI_IMAGE_FORMAT_YV12,
++      .rgba_shifts = { -1, -1, -1, -1 },
++      .rgba_sizes = { 0, 0, 0, 0 },
++      .yuv = &dri2_null_yuv_attribs[4],
++   },
+ };
+ 
+ 

+ 57 - 0
recipes-graphics/mesa/mesa-pvr/0066-mesa-partially-revert-pbuffer-attribute-removal.patch

@@ -0,0 +1,57 @@
+From 61e1720715598622318c7763f22f5d0e10fbca74 Mon Sep 17 00:00:00 2001
+From: brendan King <Brendan.King@imgtec.com>
+Date: Thu, 16 Sep 2021 17:46:28 +0100
+Subject: [PATCH] mesa: partially revert pbuffer attribute removal
+
+This partially reverts commit 5ffd1ebe6b3c8c7dd316dd47fac088044222e6ef
+("mesa: Remove misc pbuffer attributes from struct gl_config").
+
+The IMG PowerVR driver sets meaningful values for the maximum
+pbuffer width, height and pixels.
+
+---
+ src/mesa/drivers/dri/common/utils.c | 7 ++++---
+ src/mesa/main/mtypes.h              | 5 +++++
+ 2 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/src/mesa/drivers/dri/common/utils.c b/src/mesa/drivers/dri/common/utils.c
+index df5f8cb..28debe4 100644
+--- a/src/mesa/drivers/dri/common/utils.c
++++ b/src/mesa/drivers/dri/common/utils.c
+@@ -480,6 +480,7 @@ __DRIconfig **driConcatConfigs(__DRIconfig **a,
+     return all;
+ }
+ 
++/* careful, lack of trailing semicolon */
+ #define __ATTRIB(attrib, field) case attrib: *value = config->modes.field; break
+ 
+ /**
+@@ -555,9 +556,9 @@ driGetConfigAttribIndex(const __DRIconfig *config,
+     __ATTRIB(__DRI_ATTRIB_GREEN_MASK,			greenMask);
+     __ATTRIB(__DRI_ATTRIB_BLUE_MASK,			blueMask);
+     __ATTRIB(__DRI_ATTRIB_ALPHA_MASK,			alphaMask);
+-    case __DRI_ATTRIB_MAX_PBUFFER_WIDTH:
+-    case __DRI_ATTRIB_MAX_PBUFFER_HEIGHT:
+-    case __DRI_ATTRIB_MAX_PBUFFER_PIXELS:
++    __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_WIDTH,		maxPbufferWidth);
++    __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_HEIGHT,		maxPbufferHeight);
++    __ATTRIB(__DRI_ATTRIB_MAX_PBUFFER_PIXELS,		maxPbufferPixels);
+     case __DRI_ATTRIB_OPTIMAL_PBUFFER_WIDTH:
+     case __DRI_ATTRIB_OPTIMAL_PBUFFER_HEIGHT:
+     case __DRI_ATTRIB_VISUAL_SELECT_GROUP:
+diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h
+index 8f77d4c..e37c61d 100644
+--- a/src/mesa/main/mtypes.h
++++ b/src/mesa/main/mtypes.h
+@@ -175,6 +175,11 @@ struct gl_config
+    /* ARB_multisample / SGIS_multisample */
+    GLuint samples;
+ 
++   /* GLX 1.3 */
++   GLint maxPbufferWidth;
++   GLint maxPbufferHeight;
++   GLint maxPbufferPixels;
++
+    /* OML_swap_method */
+    GLint swapMethod;
+ 

+ 64 - 0
recipes-graphics/mesa/mesa-pvr/0067-egl_dri2-set-pbuffer-config-attribs-to-0-for-non-pbu.patch

@@ -0,0 +1,64 @@
+From 8dd3c5e884c1ff76bcc67759dd3f6f8559096859 Mon Sep 17 00:00:00 2001
+From: Frank Binns <frank.binns@imgtec.com>
+Date: Wed, 4 Jun 2014 13:43:03 +0100
+Subject: [PATCH] egl_dri2: set pbuffer config attribs to 0 for non-pbuffer
+ configs
+
+If the EGL_PBUFFER_BIT isn't set in the surface type, don't set the
+EGL_MAX_PBUFFER_WIDTH, EGL_MAX_PBUFFER_HEIGHT and
+EGL_MAX_PBUFFER_PIXELS attributes to non-zero values when adding an
+EGL config. If the EGL_PBUFFER_BIT is set, don't override non-zero
+values from the DRI config.
+
+---
+ src/egl/drivers/dri2/egl_dri2.c | 20 ++++++++++++++++++--
+ 1 file changed, 18 insertions(+), 2 deletions(-)
+
+diff --git a/src/egl/drivers/dri2/egl_dri2.c b/src/egl/drivers/dri2/egl_dri2.c
+index 56b5175..1b10c2e 100644
+--- a/src/egl/drivers/dri2/egl_dri2.c
++++ b/src/egl/drivers/dri2/egl_dri2.c
+@@ -407,6 +407,7 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
+    struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
+    _EGLConfig base;
+    unsigned int attrib, value, double_buffer;
++   unsigned int pbuffer_width = 0, pbuffer_height = 0, pbuffer_pixels = 0;
+    bool srgb = false;
+    EGLint key, bind_to_texture_rgb, bind_to_texture_rgba;
+    int dri_shifts[4] = { -1, -1, -1, -1 };
+@@ -530,11 +531,17 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
+          break;
+ 
+       case __DRI_ATTRIB_MAX_PBUFFER_WIDTH:
+-         base.MaxPbufferWidth = _EGL_MAX_PBUFFER_WIDTH;
++         pbuffer_width = (value != 0) ? value : _EGL_MAX_PBUFFER_WIDTH;
+          break;
++
+       case __DRI_ATTRIB_MAX_PBUFFER_HEIGHT:
+-         base.MaxPbufferHeight = _EGL_MAX_PBUFFER_HEIGHT;
++         pbuffer_height = (value != 0) ? value : _EGL_MAX_PBUFFER_HEIGHT;
++         break;
++
++      case __DRI_ATTRIB_MAX_PBUFFER_PIXELS:
++         pbuffer_pixels = value;
+          break;
++
+       case __DRI_ATTRIB_MUTABLE_RENDER_BUFFER:
+          if (disp->Extensions.KHR_mutable_render_buffer)
+             surface_type |= EGL_MUTABLE_RENDER_BUFFER_BIT_KHR;
+@@ -614,6 +621,15 @@ dri2_add_config(_EGLDisplay *disp, const __DRIconfig *dri_config, int id,
+       }
+    }
+ 
++   if (surface_type & EGL_PBUFFER_BIT) {
++      if (pbuffer_pixels == 0)
++         pbuffer_pixels = pbuffer_width * pbuffer_height;
++
++      base.MaxPbufferWidth = pbuffer_width;
++      base.MaxPbufferHeight = pbuffer_height;
++      base.MaxPbufferPixels = pbuffer_pixels;
++   }
++
+    if (attr_list)
+       for (int i = 0; attr_list[i] != EGL_NONE; i += 2)
+          _eglSetConfigKey(&base, attr_list[i], attr_list[i+1]);

+ 143 - 0
recipes-graphics/mesa/mesa-pvr/0068-GL_ARB_geometry_shader4-entry-points.patch

@@ -0,0 +1,143 @@
+From 64890c411e05d8a87e9e692b1d8cf470e7d4488c Mon Sep 17 00:00:00 2001
+From: Brendan King <Brendan.King@imgtec.com>
+Date: Thu, 11 Nov 2021 12:09:38 +0000
+Subject: [PATCH] GL_ARB_geometry_shader4 entry points.
+
+---
+ src/mapi/glapi/gen/ARB_geometry_shader4.xml | 97 +++++++++++++++++++++
+ src/mapi/glapi/gen/gl_API.xml               |  2 +-
+ src/mapi/glapi/gen/static_data.py           |  4 +
+ 3 files changed, 102 insertions(+), 1 deletion(-)
+ create mode 100644 src/mapi/glapi/gen/ARB_geometry_shader4.xml
+
+diff --git a/src/mapi/glapi/gen/ARB_geometry_shader4.xml b/src/mapi/glapi/gen/ARB_geometry_shader4.xml
+new file mode 100644
+index 0000000..d92dc57
+--- /dev/null
++++ b/src/mapi/glapi/gen/ARB_geometry_shader4.xml
+@@ -0,0 +1,97 @@
++<?xml version="1.0"?>
++<!DOCTYPE OpenGLAPI SYSTEM "gl_API.dtd">
++
++<!-- Note: no GLX protocol info yet. -->
++
++
++<OpenGLAPI>
++<category name="GL_ARB_geometry_shader4" number="47">
++    <enum name="GEOMETRY_SHADER_ARB"                      value="0x8DD9"/>
++    <enum name="GEOMETRY_VERTICES_OUT_ARB"                value="0x8DDA"/>
++    <enum name="GEOMETRY_INPUT_TYPE_ARB"                  value="0x8DDB"/>
++    <enum name="GEOMETRY_OUTPUT_TYPE_ARB"                 value="0x8DDC"/>
++
++    <enum name="MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB"
++						count="1" value="0x8C29">
++	<size name="Get" mode="get"/>
++    </enum>
++    <enum name="MAX_GEOMETRY_VARYING_COMPONENTS_ARB"
++						count="1" value="0x8DDD">
++	<size name="Get" mode="get"/>
++    </enum>
++    <enum name="MAX_VERTEX_VARYING_COMPONENTS_ARB"
++						count="1" value="0x8DDE">
++	<size name="Get" mode="get"/>
++    </enum>
++    <enum name="MAX_VARYING_COMPONENTS"
++						count="1" value="0x8B4B">
++	<size name="Get" mode="get"/>
++    </enum>
++    <enum name="MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB"
++						count="1" value="0x8DDF">
++	<size name="Get" mode="get"/>
++    </enum>
++    <enum name="MAX_GEOMETRY_OUTPUT_VERTICES_ARB"
++						count="1" value="0x8DE0">
++	<size name="Get" mode="get"/>
++    </enum>
++    <enum name="MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB"
++						count="1" value="0x8DE1">
++	<size name="Get" mode="get"/>
++    </enum>
++
++    <enum name="LINES_ADJACENCY_ARB"                      value="0xA"/>
++    <enum name="LINE_STRIP_ADJACENCY_ARB"                 value="0xB"/>
++    <enum name="TRIANGLES_ADJACENCY_ARB"                  value="0xC"/>
++    <enum name="TRIANGLE_STRIP_ADJACENCY_ARB"             value="0xD"/>
++
++    <enum name="FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB" value="0x8DA8"/>
++    <enum name="FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB"   value="0x8DA9"/>
++
++
++    <enum name="FRAMEBUFFER_ATTACHMENT_LAYERED_ARB"
++						count="1" value="0x8DA7">
++        <size name="GetFramebufferAttachmentParameteriv" mode="get"/>
++    </enum>
++    <enum name="FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER"
++						count="1" value="0x8CD4">
++        <size name="GetFramebufferAttachmentParameteriv" mode="get"/>
++    </enum>
++
++    <enum name="PROGRAM_POINT_SIZE_ARB"                   value="0x8642"/>
++    <enum name="PROGRAM_POINT_SIZE_ARB"
++						count="1" value="0x8642">
++	<size name="Get" mode="get"/>
++    </enum>
++
++    <function name="ProgramParameteriARB" exec="dynamic">
++        <param name="program" type="GLuint"/>
++        <param name="pname" type="GLenum"/>
++        <param name="value" type="GLint"/>
++    </function>
++
++    <function name="FramebufferTextureARB" exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment" type="GLenum"/>
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++    </function>
++
++    <function name="FramebufferTextureLayerARB" exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment" type="GLenum"/>
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++        <param name="layer" type="GLint"/>
++    </function>
++
++    <function name="FramebufferTextureFaceARB" exec="dynamic">
++        <param name="target" type="GLenum"/>
++        <param name="attachment" type="GLenum"/>
++        <param name="texture" type="GLuint"/>
++        <param name="level" type="GLint"/>
++        <param name="face" type="GLenum"/>
++    </function>
++</category>
++
++</OpenGLAPI>
+diff --git a/src/mapi/glapi/gen/gl_API.xml b/src/mapi/glapi/gen/gl_API.xml
+index 4e35de0..d5a5e7f 100644
+--- a/src/mapi/glapi/gen/gl_API.xml
++++ b/src/mapi/glapi/gen/gl_API.xml
+@@ -8064,7 +8064,7 @@
+ 
+ <!-- 46. GL_ARB_framebuffer_sRGB -->
+ 
+-<!-- 47. GL_ARB_geometry_shader4. There are no intentions to implement this extension -->
++<xi:include href="ARB_geometry_shader4.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ 
+ <!-- 48. GL_ARB_half_float_vertex -->
+ 
+diff --git a/src/mapi/glapi/gen/static_data.py b/src/mapi/glapi/gen/static_data.py
+index 974f366..833ad73 100644
+--- a/src/mapi/glapi/gen/static_data.py
++++ b/src/mapi/glapi/gen/static_data.py
+@@ -1721,6 +1721,10 @@ offsets = {
+     "FramebufferRenderbufferEXT" : 1685,
+     "GetFramebufferAttachmentParameterivEXT" : 1686,
+     "GenerateMipmapEXT" : 1687,
++    "ProgramParameteriARB" : 1688,
++    "FramebufferTextureARB" : 1689,
++    "FramebufferTextureLayerARB" : 1690,
++    "FramebufferTextureFaceARB" : 1691,
+ }
+ 
+ functions = [

+ 2 - 0
recipes-graphics/mesa/mesa-pvr_21.2.1.bb

@@ -0,0 +1,2 @@
+require ${BPN}.inc
+

+ 8 - 0
recipes-graphics/x11-common/files/xserver-nodm-local.conf.in

@@ -0,0 +1,8 @@
+# common environment file for sysvinit and systemd
+
+XSERVER=/usr/local/bin/Xorg
+LD_LIBRARY_PATH=/usr/local/lib/
+DISPLAY=:0
+ARGS=" -br -pn "
+HOME=@HOME@
+USER=@USER@

+ 12 - 0
recipes-graphics/x11-common/files/xserver-nodm-local.service.in

@@ -0,0 +1,12 @@
+[Unit]
+Description=Xserver startup without a display manager
+
+[Service]
+EnvironmentFile=/etc/default/xserver-nodm
+Environment="LD_LIBRARY_PATH=/usr/local/lib/"
+User=@USER@
+ExecStart=/etc/xserver-nodm/Xserver
+Restart=always
+
+[Install]
+Alias=display-manager.service

+ 12 - 0
recipes-graphics/x11-common/xserver-nodm-init_%.bbappend

@@ -0,0 +1,12 @@
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+SRC_URI += " \
+	file://xserver-nodm-local.conf.in \
+	file://xserver-nodm-local.service.in \
+	"
+
+do_install:prepend() {
+	# Replace the systemd service & config to change the LD_LIBRARY_PATH & binary location
+	cp xserver-nodm-local.conf.in ${THISDIR}/${PN}/xserver-nodm.conf.in
+	cp xserver-nodm-local.service.in ${THISDIR}/${PN}/xserver-nodm.service.in
+}

+ 41 - 0
recipes-graphics/xorg-xserver/xserver-xf86-config/xorg.conf

@@ -0,0 +1,41 @@
+Section "Device"
+        Identifier      "Video Device"
+        Driver          "modesetting"
+        # Option                "Atomic"                "true"
+        Option          "NoCursor"              "true"
+EndSection
+
+Section "OutputClass"
+        Identifier      "starfive display"
+        MatchDriver     "starfive"
+        Option          "PrimaryGPU"    "true"
+EndSection
+
+Section "Monitor"
+        Identifier      "Monitor"
+        # Option                "DPMS"                  "false"
+EndSection
+
+Section "Screen"
+        Identifier      "Screen"
+        Monitor         "Monitor"
+        Device          "Video Device"
+EndSection
+
+Section "ServerLayout"
+        Identifier      "Server Layout"
+        Screen          "Screen"
+EndSection
+
+Section "ServerFlags"
+        Option          "DefaultServerLayout"   "Server Layout"
+
+        # Enable support for DRM format modifiers
+        # Option                "Debug"                 "dmabuf_capable"
+
+        # Disable screen blanking. Disable DPMS in the Monitor section as well.
+        # Option                "BlankTime"             "35790"
+        # Option                "StandbyTime"           "35790"
+        # Option                "SuspendTime"           "35790"
+        # Option                "OffTime"               "35790"
+EndSection

+ 2 - 0
recipes-graphics/xorg-xserver/xserver-xf86-config_%.bbappend

@@ -0,0 +1,2 @@
+# provide visionfive2 specific X11 config
+FILESEXTRAPATHS:prepend:visionfive2 := "${THISDIR}/${PN}:"

+ 183 - 0
recipes-graphics/xorg-xserver/xserver-xorg.inc

@@ -0,0 +1,183 @@
+SUMMARY = "The X.Org X server"
+HOMEPAGE = "http://www.x.org"
+SECTION = "x11/base"
+LICENSE = "MIT-X"
+LIC_FILES_CHKSUM = "file://COPYING;md5=5df87950af51ac2c5822094553ea1880"
+
+# xf86-*-* packages depend on an X server built with the xfree86 DDX
+# so we have a virtual to represent that:
+# deprecated, we should use virtual/xserver instead
+PROVIDES = "virtual/xserver-xf86"
+
+# Other packages tend to just care that there is *an* X server:
+PROVIDES += "virtual/xserver"
+
+PE = "2"
+
+XORG_PN = "xorg-server"
+SRC_URI = "${XORG_MIRROR}/individual/xserver/${XORG_PN}-${PV}.tar.xz"
+
+UPSTREAM_CHECK_REGEX = "xorg-server-(?P<pver>\d+(\.(?!99)\d+)+)\.tar"
+
+CVE_PRODUCT = "xorg-server"
+
+S = "${WORKDIR}/${XORG_PN}-${PV}"
+
+inherit autotools pkgconfig
+
+inherit features_check
+REQUIRED_DISTRO_FEATURES = "x11"
+
+LIB_DEPS = "pixman libxfont2 xtrans libxau libxext libxdmcp libdrm libxkbfile libpciaccess"
+DEPENDS = "xorgproto ${LIB_DEPS} font-util"
+
+# Split out some modules and extensions from the main package
+# These aren't needed for basic operations and only take up space:
+#  32.0k   libdri.so
+#  91.0k   libexa.so
+#  336.0k  libglx.so
+#  1360k   libint10.so
+#  180.0k  libwfb.so
+#  320.0k  libxaa.so
+#  124.0k  libxf1bpp.so
+#  84.0k   libxf4bpp.so
+#          librecord.so
+#          libextmod.so
+#          libdbe.so
+
+PACKAGES =+ "${PN}-sdl \
+             ${PN}-fbdev \
+             ${PN}-xvfb \
+             ${PN}-utils \
+             ${PN}-xephyr \
+             ${PN}-xwayland \
+             ${PN}-multimedia-modules \
+             ${PN}-extension-dri \
+             ${PN}-extension-dri2 \
+             ${PN}-extension-glx \
+             ${PN}-extension-record \
+             ${PN}-extension-extmod \
+             ${PN}-extension-dbe \
+             ${PN}-module-libint10 \
+             ${PN}-module-libafb \
+             ${PN}-module-libwfb  \
+             ${PN}-module-libmfb \
+             ${PN}-module-libcfb \
+             ${PN}-module-exa \
+             ${PN}-module-xaa \
+             ${PN}-module-libxf1bpp \
+             ${PN}-module-libxf4bpp \
+             xf86-video-modesetting"
+
+SUMMARY:xf86-video-modesetting = "X.Org X server -- modesetting display driver"
+INSANE_SKIP:${MLPREFIX}xf86-video-modesetting = "xorg-driver-abi"
+
+XSERVER_RRECOMMENDS = "xkeyboard-config rgb xserver-xf86-config xkbcomp xf86-input-libinput"
+RRECOMMENDS:${PN} += "${XSERVER_RRECOMMENDS}"
+RRECOMMENDS:${PN}-xwayland += "${XSERVER_RRECOMMENDS}"
+RDEPENDS:${PN}-xvfb += "xkeyboard-config"
+RDEPENDS:${PN}-module-exa = "${PN} (= ${EXTENDPKGV})"
+
+FILES:${PN} = "${bindir} ${libdir}/X11/Options ${libdir}/X11/Cards ${libdir}/X11/getconfig ${libdir}/X11/etc ${libdir}/modules/*.so ${libdir}/xorg/modules/*.so /etc/X11 ${libdir}/xorg/protocol.txt ${datadir}/X11/xorg.conf.d"
+FILES:${PN}-dev += "${libdir}/xorg/modules/*.la ${libdir}/xorg/modules/*/*.la"
+FILES:${PN}-doc += "${libdir}/X11/doc ${datadir}/X11/xkb/compiled/README.compiled ${localstatedir}/lib/xkb/README.compiled"
+FILES:${PN}-sdl = "${bindir}/Xsdl"
+FILES:${PN}-fbdev = "${bindir}/Xfbdev"
+FILES:${PN}-xvfb = "${bindir}/Xvfb"
+FILES:${PN}-utils = "${bindir}/scanpci ${bindir}/pcitweak ${bindir}/ioport ${bindir}/in[bwl] ${bindir}/out[bwl] ${bindir}/mmap[rw] ${bindir}/gtf ${bindir}/getconfig ${bindir}/getconfig.pl"
+FILES:${PN}-xephyr = "${bindir}/Xephyr"
+FILES:${PN}-xwayland = "${bindir}/Xwayland"
+FILES:${PN}-multimedia-modules = "${libdir}/xorg/modules/multimedia/*drv*"
+FILES:${PN}-extension-dri = "${libdir}/xorg/modules/extensions/libdri.so"
+FILES:${PN}-extension-dri2 = "${libdir}/xorg/modules/extensions/libdri2.so"
+FILES:${PN}-extension-glx = "${libdir}/xorg/modules/extensions/libglx.so"
+FILES:${PN}-extension-record = "${libdir}/xorg/modules/extensions/librecord.so"
+FILES:${PN}-extension-extmod = "${libdir}/xorg/modules/extensions/libextmod.so"
+FILES:${PN}-extension-dbe = "${libdir}/xorg/modules/extensions/libdbe.so"
+FILES:${PN}-module-libint10 = "${libdir}/xorg/modules/libint10.so"
+FILES:${PN}-module-libafb = "${libdir}/xorg/modules/libafb.so"
+FILES:${PN}-module-libwfb = "${libdir}/xorg/modules/libwfb.so"
+FILES:${PN}-module-libmfb = "${libdir}/xorg/modules/libmfb.so"
+FILES:${PN}-module-libcfb = "${libdir}/xorg/modules/libcfb.so"
+FILES:${PN}-module-exa = "${libdir}/xorg/modules/libexa.so"
+FILES:${PN}-module-xaa = "${libdir}/xorg/modules/libxaa.so"
+FILES:${PN}-module-libxf1bpp = "${libdir}/xorg/modules/libxf1bpp.so"
+FILES:${PN}-module-libxf4bpp = "${libdir}/xorg/modules/libxf4bpp.so"
+FILES:xf86-video-modesetting = "${libdir}/xorg/modules/drivers/modesetting_drv.so"
+
+EXTRA_OECONF += "--with-fop=no \
+                 --with-pic \
+                 --disable-static \
+                 --disable-record \
+                 --disable-dmx \
+                 --disable-xnest \
+                 --enable-xvfb \
+                 --enable-composite \
+                 --without-dtrace \
+                 --with-int10=x86emu \
+                 --sysconfdir=/etc/X11 \
+                 --localstatedir=/var \
+                 --with-xkb-output=/var/lib/xkb \
+                 --with-os-name=Linux \
+"
+
+OPENGL_PKGCONFIGS = "dri glx glamor dri3 xshmfence"
+PACKAGECONFIG ??= "dga dri2 udev ${XORG_CRYPTO} \
+                   ${@bb.utils.contains('DISTRO_FEATURES', 'opengl', '${OPENGL_PKGCONFIGS}', '', d)} \
+                   ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'systemd systemd-logind', '', d)} \
+"
+
+PACKAGECONFIG[udev] = "--enable-config-udev,--disable-config-udev,udev"
+PACKAGECONFIG[dga] = "--enable-dga,--disable-dga"
+PACKAGECONFIG[dri] = "--enable-dri,--disable-dri,virtual/mesa"
+PACKAGECONFIG[dri2] = "--enable-dri2,--disable-dri2"
+# DRI3 requires xshmfence to also be enabled
+PACKAGECONFIG[dri3] = "--enable-dri3,--disable-dri3"
+PACKAGECONFIG[glx] = "--enable-glx,--disable-glx,virtual/libgl virtual/libx11"
+PACKAGECONFIG[glamor] = "--enable-glamor,--disable-glamor,libepoxy virtual/libgbm,libegl"
+PACKAGECONFIG[unwind] = "--enable-libunwind,--disable-libunwind,libunwind"
+PACKAGECONFIG[xshmfence] = "--enable-xshmfence,--disable-xshmfence,libxshmfence"
+PACKAGECONFIG[xmlto] = "--with-xmlto, --without-xmlto, xmlto-native docbook-xml-dtd4-native docbook-xsl-stylesheets-native"
+PACKAGECONFIG[systemd-logind] = "--enable-systemd-logind=yes,--enable-systemd-logind=no,dbus,"
+PACKAGECONFIG[systemd] = "--with-systemd-daemon,--without-systemd-daemon,systemd"
+PACKAGECONFIG[xinerama] = "--enable-xinerama,--disable-xinerama"
+PACKAGECONFIG[xwayland] = "--enable-xwayland,--disable-xwayland,wayland wayland-native wayland-protocols libepoxy"
+
+# Xorg requires a SHA1 implementation, pick one
+XORG_CRYPTO ??= "openssl"
+PACKAGECONFIG[openssl] = "--with-sha1=libcrypto,,openssl"
+PACKAGECONFIG[nettle] = "--with-sha1=libnettle,,nettle"
+PACKAGECONFIG[gcrypt] = "--with-sha1=libgcrypt,,libgcrypt"
+
+do_install:append () {
+	# Its assumed base-files creates this for us
+	rmdir ${D}${localstatedir}/log/
+        sed -i -e 's,${libdir}/xorg/modules,${prefix}/lib*/xorg/modules,' ${D}${mandir}/man5/xorg.conf.5
+}
+
+# Add runtime provides for the ABI versions of the video and input subsystems,
+# so that drivers can depend on the relevant version.
+python populate_packages:prepend() {
+    import subprocess
+
+    # Set PKG_CONFIG_PATH so pkg-config looks at the .pc files that are going
+    # into the new package, not the staged ones.
+    newenv = dict(os.environ)
+    newenv["PKG_CONFIG_PATH"] = d.expand("${PKGD}${libdir}/pkgconfig/")
+
+    def get_abi(name):
+        abis = {
+          "video": "abi_videodrv",
+          "input": "abi_xinput"
+        }
+        p = subprocess.Popen(args="pkg-config --variable=%s xorg-server" % abis[name],
+                             shell=True, env=newenv, stdout=subprocess.PIPE)
+        stdout, stderr = p.communicate()
+        output = stdout.decode("utf-8").split(".")[0]
+        mlprefix = d.getVar('MLPREFIX') or ''
+        return "%sxorg-abi-%s-%s" % (mlprefix, name, output)
+
+    pn = d.getVar("PN")
+    d.appendVar("RPROVIDES:" + pn, " " + get_abi("input"))
+    d.appendVar("RPROVIDES:" + pn, " " + get_abi("video"))
+}

+ 45 - 0
recipes-graphics/xorg-xserver/xserver-xorg/0001-Avoid-duplicate-definitions-of-IOPortBase.patch

@@ -0,0 +1,45 @@
+From fc04acfd948ac99d04a5dc08c78f3b13bc0c5c41 Mon Sep 17 00:00:00 2001
+From: Khem Raj <raj.khem@gmail.com>
+Date: Mon, 17 Aug 2020 10:50:51 -0700
+Subject: [PATCH] Avoid duplicate definitions of IOPortBase
+
+This fixed build with gcc10/-fno-common
+
+Fixes
+compiler.h:528: multiple definition of `IOPortBase';
+
+Upstream-Status: Pending
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+---
+ hw/xfree86/common/compiler.h            | 2 +-
+ hw/xfree86/os-support/linux/lnx_video.c | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/hw/xfree86/common/compiler.h b/hw/xfree86/common/compiler.h
+index 2b2008b..c7d617e 100644
+--- a/hw/xfree86/common/compiler.h
++++ b/hw/xfree86/common/compiler.h
+@@ -525,7 +525,7 @@ xf86WriteMmio32Le(__volatile__ void *base, const unsigned long offset,
+ #define PORT_SIZE short
+ #endif
+ 
+-_X_EXPORT unsigned int IOPortBase;      /* Memory mapped I/O port area */
++extern _X_EXPORT unsigned int IOPortBase;      /* Memory mapped I/O port area */
+ 
+ static __inline__ void
+ outb(unsigned PORT_SIZE port, unsigned char val)
+diff --git a/hw/xfree86/os-support/linux/lnx_video.c b/hw/xfree86/os-support/linux/lnx_video.c
+index 04e4509..9dc7316 100644
+--- a/hw/xfree86/os-support/linux/lnx_video.c
++++ b/hw/xfree86/os-support/linux/lnx_video.c
+@@ -78,6 +78,7 @@ xf86OSInitVidMem(VidMemInfoPtr pVidMem)
+ /***************************************************************************/
+ /* I/O Permissions section                                                 */
+ /***************************************************************************/
++_X_EXPORT unsigned int IOPortBase;      /* Memory mapped I/O port area */
+ 
+ #if defined(__powerpc__)
+ volatile unsigned char *ioBase = NULL;
+-- 
+2.28.0
+

+ 34 - 0
recipes-graphics/xorg-xserver/xserver-xorg/0001-Fix-segfault-on-probing-a-non-PCI-platform-device-on.patch

@@ -0,0 +1,34 @@
+From e50c85f4ebf559a3bac4817b41074c43d4691779 Mon Sep 17 00:00:00 2001
+From: Eric Anholt <eric@anholt.net>
+Date: Fri, 26 Oct 2018 17:47:30 -0700
+Subject: [PATCH] Fix segfault on probing a non-PCI platform device on a system
+ with PCI.
+
+Some Broadcom set-top-box boards have PCI busses, but the GPU is still
+probed through DT.  We would dereference a null busid here in that
+case.
+
+Signed-off-by: Eric Anholt <eric@anholt.net>
+
+Upstream-Status: Backport [https://github.com/freedesktop/xorg-xserver/commit/e50c85f4e]
+Signed-off-by: Aníbal Limón <anibal.limon@linaro.org>
+---
+ hw/xfree86/common/xf86platformBus.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/hw/xfree86/common/xf86platformBus.c b/hw/xfree86/common/xf86platformBus.c
+index cef47da03..dadbac6c8 100644
+--- a/hw/xfree86/common/xf86platformBus.c
++++ b/hw/xfree86/common/xf86platformBus.c
+@@ -289,7 +289,7 @@ xf86platformProbe(void)
+     for (i = 0; i < xf86_num_platform_devices; i++) {
+         char *busid = xf86_platform_odev_attributes(i)->busid;
+ 
+-        if (pci && (strncmp(busid, "pci:", 4) == 0)) {
++        if (pci && busid && (strncmp(busid, "pci:", 4) == 0)) {
+             platform_find_pci_info(&xf86_platform_devices[i], busid);
+         }
+ 
+-- 
+2.28.0
+

+ 23 - 0
recipes-graphics/xorg-xserver/xserver-xorg/0001-drmmode_display.c-add-missing-mi.h-include.patch

@@ -0,0 +1,23 @@
+From 84338444179cab7ede1252a11b66e3b8f657e6a4 Mon Sep 17 00:00:00 2001
+From: Alexander Kanavin <alex.kanavin@gmail.com>
+Date: Fri, 7 Feb 2020 20:36:45 +0100
+Subject: [PATCH] drmmode_display.c: add missing mi.h include
+
+Upstream-Status: Pending
+Signed-off-by: Alexander Kanavin <alex.kanavin@gmail.com>
+---
+ hw/xfree86/drivers/modesetting/drmmode_display.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
+index e18cc37..3445cce 100644
+--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
++++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
+@@ -46,6 +46,7 @@
+ #include "xf86Crtc.h"
+ #include "drmmode_display.h"
+ #include "present.h"
++#include "mi.h"
+ 
+ #include <cursorstr.h>
+ 

+ 36 - 0
recipes-graphics/xorg-xserver/xserver-xorg/0001-test-xtest-Initialize-array-with-braces.patch

@@ -0,0 +1,36 @@
+From 8a382c015cd3c69fcfc146ef03dcbf30c77ff207 Mon Sep 17 00:00:00 2001
+From: Khem Raj <raj.khem@gmail.com>
+Date: Fri, 1 Mar 2019 09:47:57 -0800
+Subject: [PATCH] test/xtest: Initialize array with braces
+
+Fixes an error when extra warnings are enabled, this is caught with clang
+
+test/xtest.c:64:23: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces]
+    WindowRec root = {0};
+                      ^
+                      {}
+1 error generated.
+
+Upstream-Status: Pending
+
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+---
+ test/xtest.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/test/xtest.c b/test/xtest.c
+index fc5e433..d7e6620 100644
+--- a/test/xtest.c
++++ b/test/xtest.c
+@@ -61,7 +61,7 @@ xtest_init_devices(void)
+ {
+     ScreenRec screen = {0};
+     ClientRec server_client = {0};
+-    WindowRec root = {0};
++    WindowRec root = {{0}};
+     WindowOptRec optional = {0};
+ 
+     /* random stuff that needs initialization */
+-- 
+2.21.0
+

+ 53 - 0
recipes-graphics/xorg-xserver/xserver-xorg/0001-xf86pciBus.c-use-Intel-ddx-only-for-pre-gen4-hardwar.patch

@@ -0,0 +1,53 @@
+From d77cdc5e1eee26821ab98c947abea53fb7b18fe5 Mon Sep 17 00:00:00 2001
+From: California Sullivan <california.l.sullivan@intel.com>
+Date: Fri, 16 Mar 2018 17:23:11 -0700
+Subject: [PATCH] xf86pciBus.c: use Intel ddx only for pre-gen4 hardware
+
+Use intel ddx only on pre-gen4 hw, newer ones will fall back to modesetting
+Author: Timo Aaltonen <tjaalton@debian.org>
+
+Instead of defaulting to the Intel driver for all Intel hardware, only
+default it for older hardware for which it has shown to be better for.
+
+Others fall through to the -modesetting default.
+
+Upstream-Status: Pending [Debian/Fedora patch
+https://src.fedoraproject.org/rpms/xorg-x11-server/c/ee515e44b07e37689abf48cf2fffb41578f3bc1d]
+
+Signed-off-by: California Sullivan <california.l.sullivan@intel.com>
+---
+ hw/xfree86/common/xf86pciBus.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+diff --git a/hw/xfree86/common/xf86pciBus.c b/hw/xfree86/common/xf86pciBus.c
+index e61ae0cd4..d70c99197 100644
+--- a/hw/xfree86/common/xf86pciBus.c
++++ b/hw/xfree86/common/xf86pciBus.c
+@@ -1173,7 +1173,23 @@ xf86VideoPtrToDriverList(struct pci_device *dev,
+ 		case 0x0bef:
+ 			/* Use fbdev/vesa driver on Oaktrail, Medfield, CDV */
+ 			break;
+-		default:
++		/* Default to intel only on pre-gen4 chips */
++		case 0x3577:
++		case 0x2562:
++		case 0x3582:
++		case 0x358e:
++		case 0x2572:
++		case 0x2582:
++		case 0x258a:
++		case 0x2592:
++		case 0x2772:
++		case 0x27a2:
++		case 0x27ae:
++		case 0x29b2:
++		case 0x29c2:
++		case 0x29d2:
++		case 0xa001:
++		case 0xa011:
+ 			driverList[0] = "intel";
+ 			break;
+         }
+-- 
+2.14.3
+

+ 34 - 0
recipes-graphics/xorg-xserver/xserver-xorg/pkgconfig.patch

@@ -0,0 +1,34 @@
+Upstream-Status: Submitted [https://gitlab.freedesktop.org/xorg/xserver/merge_requests/22]
+Signed-off-by: Ross Burton <ross.burton@intel.com>
+
+From 5f65a6246fe752764045dd1e38912f1dccec71e4 Mon Sep 17 00:00:00 2001
+From: Ross Burton <ross.burton@intel.com>
+Date: Thu, 20 Sep 2018 20:12:24 +0100
+Subject: [PATCH] xorg-server.m4: just all cflags instead of just sdkdir
+
+Instead of fetching just the sdkdir variable of xorg-server using pkg-config,
+simply get all of the CFLAGS.  Aside from completeness, this helps builds in
+sysroots as pkg-config knows what to do with --cflags but doesn't remap
+arbitrary variables.
+
+Signed-off-by: Ross Burton <ross.burton@intel.com>
+---
+ xorg-server.m4 | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/xorg-server.m4 b/xorg-server.m4
+index 18255b91a..195bda5d8 100644
+--- a/xorg-server.m4
++++ b/xorg-server.m4
+@@ -31,7 +31,7 @@ dnl
+ AC_DEFUN([XORG_DRIVER_CHECK_EXT],[
+ 	AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+ 	SAVE_CFLAGS="$CFLAGS"
+-	CFLAGS="$CFLAGS -I`$PKG_CONFIG --variable=sdkdir xorg-server`"
++	CFLAGS="$CFLAGS `$PKG_CONFIG --cflags xorg-server`"
+ 	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+ #include "xorg-server.h"
+ #if !defined $1
+-- 
+2.11.0
+

+ 50 - 0
recipes-graphics/xorg-xserver/xserver-xorg/sdksyms-no-build-path.patch

@@ -0,0 +1,50 @@
+Upstream-Status: Backport [https://gitlab.freedesktop.org/xorg/xserver/merge_requests/253]
+Signed-off-by: Ross Burton <ross.burton@intel.com>
+
+From ca832598d38ba55a001088b57d73c6d7261dc9a7 Mon Sep 17 00:00:00 2001
+From: Ross Burton <ross.burton@intel.com>
+Date: Thu, 1 Aug 2019 15:24:51 +0100
+Subject: [PATCH] sdksyms.sh: don't embed the build path
+
+This script generates a header that has a comment containing the build path for
+no real reason.  As this source can end up deployed on targets in debug packages
+this means there is both potentially sensitive information leakage about the
+build environment, and a source of change for reproducible builds.
+---
+ hw/xfree86/sdksyms.sh | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/hw/xfree86/sdksyms.sh b/hw/xfree86/sdksyms.sh
+index 39e33711d..bdf47a71a 100755
+--- a/hw/xfree86/sdksyms.sh
++++ b/hw/xfree86/sdksyms.sh
+@@ -302,13 +302,16 @@ LC_ALL=C
+ export LC_ALL
+ ${CPP:-cpp} "$@" sdksyms.c > /dev/null || exit $?
+ ${CPP:-cpp} "$@" sdksyms.c | ${AWK:-awk} -v topdir=$topdir '
++function basename(file) {
++    sub(".*/", "", file)
++    return file
++}
+ BEGIN {
+     sdk = 0;
+     print("/*");
+     print(" * These symbols are referenced to ensure they");
+     print(" * will be available in the X Server binary.");
+     print(" */");
+-    printf("/* topdir=%s */\n", topdir);
+     print("_X_HIDDEN void *xorg_symbols[] = {");
+ 
+     printf("sdksyms.c:") > "sdksyms.dep";
+@@ -337,7 +340,7 @@ BEGIN {
+ 	# remove quotes
+ 	gsub(/"/, "", $3);
+ 	line = $2;
+-	header = $3;
++	header = basename($3);
+ 	if (! headers[$3]) {
+ 	    printf(" \\\n  %s", $3) >> "sdksyms.dep";
+ 	    headers[$3] = 1;
+-- 
+2.20.1
+

+ 5 - 0
recipes-graphics/xorg-xserver/xserver-xorg_%.bbappend

@@ -0,0 +1,5 @@
+RDEPENDS:${PN}:append:starfive-visionfive2 = " \
+    xf86-video-modesetting \
+    ${PN}-extension-glx \
+    xserver-xf86-config \
+"

+ 33 - 0
recipes-graphics/xorg-xserver/xserver-xorg_1.20.13.bb

@@ -0,0 +1,33 @@
+require xserver-xorg.inc
+
+SRC_URI += "file://0001-xf86pciBus.c-use-Intel-ddx-only-for-pre-gen4-hardwar.patch \
+           file://pkgconfig.patch \
+           file://0001-test-xtest-Initialize-array-with-braces.patch \
+           file://sdksyms-no-build-path.patch \
+           file://0001-drmmode_display.c-add-missing-mi.h-include.patch \
+           file://0001-Avoid-duplicate-definitions-of-IOPortBase.patch \
+           file://0001-Fix-segfault-on-probing-a-non-PCI-platform-device-on.patch \
+           "
+SRC_URI[sha256sum] = "40aa4e96a56a81a301f15a9b10e06a22700f12b42d9e0e453c7f11d354386300"
+
+# These extensions are now integrated into the server, so declare the migration
+# path for in-place upgrades.
+
+RREPLACES:${PN} =  "${PN}-extension-dri \
+                    ${PN}-extension-dri2 \
+                    ${PN}-extension-record \
+                    ${PN}-extension-extmod \
+                    ${PN}-extension-dbe \
+                   "
+RPROVIDES:${PN} =  "${PN}-extension-dri \
+                    ${PN}-extension-dri2 \
+                    ${PN}-extension-record \
+                    ${PN}-extension-extmod \
+                    ${PN}-extension-dbe \
+                   "
+RCONFLICTS:${PN} = "${PN}-extension-dri \
+                    ${PN}-extension-dri2 \
+                    ${PN}-extension-record \
+                    ${PN}-extension-extmod \
+                    ${PN}-extension-dbe \
+                   "

+ 30 - 0
recipes-graphics/xorg-xserver/xserver-xorg_21.1.4.bb

@@ -0,0 +1,30 @@
+require xserver-xorg.inc
+
+SRC_URI += "file://0001-xf86pciBus.c-use-Intel-ddx-only-for-pre-gen4-hardwar.patch \
+           file://0001-Avoid-duplicate-definitions-of-IOPortBase.patch \
+           file://0001-xkb-fix-some-possible-memleaks-in-XkbGetKbdByName.patch \
+           file://0001-xkb-proof-GetCountedString-against-request-length-at.patch \
+           "
+SRC_URI[sha256sum] = "5cc4be8ee47edb58d4a90e603a59d56b40291ad38371b0bd2471fc3cbee1c587"
+
+# These extensions are now integrated into the server, so declare the migration
+# path for in-place upgrades.
+
+RREPLACES:${PN} =  "${PN}-extension-dri \
+                    ${PN}-extension-dri2 \
+                    ${PN}-extension-record \
+                    ${PN}-extension-extmod \
+                    ${PN}-extension-dbe \
+                   "
+RPROVIDES:${PN} =  "${PN}-extension-dri \
+                    ${PN}-extension-dri2 \
+                    ${PN}-extension-record \
+                    ${PN}-extension-extmod \
+                    ${PN}-extension-dbe \
+                   "
+RCONFLICTS:${PN} = "${PN}-extension-dri \
+                    ${PN}-extension-dri2 \
+                    ${PN}-extension-record \
+                    ${PN}-extension-extmod \
+                    ${PN}-extension-dbe \
+                   "

+ 14 - 0
recipes-kernel/firmware/initramfs-img-gpu-firmware.bb

@@ -0,0 +1,14 @@
+SUMMARY = "IMG GPU firmware installation recipe"
+DESCRIPTION = "Install IMG GPU firmware to the initramfs"
+LICENSE = "CLOSED"
+
+DEPENDS += "visionfive2-pvr-graphics"
+
+FILES:${PN} += "/lib/*"
+
+do_install:append() {
+        install -d ${D}/lib/firmware
+        install -m 0755 ${DEPLOY_DIR}/lib/firmware/* ${D}/lib/firmware
+}
+
+INSANE_SKIP:${PN} += "already-stripped arch"

Некоторые файлы не были показаны из-за большого количества измененных файлов