Merge changes Ib8cc1147,I33f17670,Ib00873ba into main

* changes:
  gralloc: Avoid using std::optional in shared memory metadata
  gralloc: Move buffer metadata accessing into cros_gralloc_buffer
  gralloc: Move buffer metadata initialization into common layer
diff --git a/Android.bp b/Android.bp
index baeffc2..1f62e5b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -37,6 +37,7 @@
 
     srcs: [
         "amdgpu.c",
+        "backend_mock.c",
         "drv.c",
         "drv_array_helpers.c",
         "drv_helpers.c",
@@ -162,21 +163,50 @@
         },
         android: {
             shared_libs: [
-                "libdrm",
                 "libcutils",
-                "liblog"
+                "liblog",
+            ],
+            static_libs: [
+                "libdrm",
             ],
         },
     },
     apex_available: [
         "//apex_available:platform",
-        "com.android.virt",
+        "//apex_available:anyapex",
     ],
     vendor_available: true,
+    product_available: true,
 
     export_include_dirs: ["."],
 }
 
+// Rust bindings to minigbm, generated in a way compatible with gbm crate.
+rust_bindgen {
+    name: "libgbm_sys",
+    crate_name: "gbm_sys",
+    wrapper_src: "rust/gbm_wrapper.h",
+    source_stem: "bindings",
+    bindgen_flags: [
+        "--blocklist-type=__BINDGEN_TMP_.*",
+        "--allowlist-type=^gbm_.*$",
+        "--allowlist-function=^gbm_.*$",
+        "--allowlist-var=GBM_.*|gbm_.*$",
+        "--constified-enum-module=^gbm_.*$",
+    ],
+    shared_libs: ["libgbm"],
+    host_supported: true,
+    vendor_available: true,
+    product_available: true,
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
+    visibility: [
+        "//external/rust/crates/gbm",
+    ],
+}
+
 // Generic
 cc_library_shared {
     name: "libminigbm_gralloc",
diff --git a/METADATA b/METADATA
index b281ea4..033bcb8 100644
--- a/METADATA
+++ b/METADATA
@@ -1,17 +1,16 @@
 name: "minigbm"
-description:
-    ""
-
+description: ""
 third_party {
-  url {
-    type: HOMEPAGE
-    value: "https://www.chromium.org/"
-  }
-  url {
-    type: GIT
-    value: "https://chromium.googlesource.com/chromiumos/platform/minigbm/"
-  }
-  version: ""
-  last_upgrade_date { year: 2018 month: 6 day: 25 }
   license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 3
+    day: 12
+  }
+  homepage: "https://www.chromium.org/"
+  identifier {
+    type: "Git"
+    value: "https://chromium.googlesource.com/chromiumos/platform/minigbm/"
+    version: "40b28f098a29589b3f72de1ba6753d46a616c48b"
+  }
 }
diff --git a/Makefile b/Makefile
index b45226d..db27cf6 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,10 @@
 CFLAGS += -std=c99 -Wall -Wsign-compare -Wpointer-arith -Wcast-qual \
 	  -Wcast-align -D_GNU_SOURCE=1 -D_FILE_OFFSET_BITS=64
 
+# Dependencies that all gtest based unittests should have.
+UNITTEST_LIBS := -lcap -lgtest -lgmock
+UNITTEST_DEPS := gbm_unittest.o testrunner.o gbm.o dri.o drv_array_helpers.o drv_helpers.o drv.o backend_mock.o virtgpu_cross_domain.o virtgpu_virgl.o virtgpu.o msm.o vc4.o amdgpu.o i915.o mediatek.o dumb_driver.o
+
 ifdef DRV_AMDGPU
 	CFLAGS += $(shell $(PKG_CONFIG) --cflags libdrm_amdgpu)
 	LDLIBS += -ldrm_amdgpu -ldl
@@ -52,6 +56,13 @@
 
 clean: CLEAN($(MINIGBM_FILENAME))
 
+CXX_BINARY(gbm_unittest): CXXFLAGS += -Wno-write-strings \
+						$(GTEST_CXXFLAGS)
+CXX_BINARY(gbm_unittest): LDLIBS += $(UNITTEST_LIBS)
+CXX_BINARY(gbm_unittest): $(UNITTEST_DEPS)
+clean: CLEAN(gbm_unittest)
+tests: TEST(CXX_BINARY(gbm_unittest))
+
 install: all
 	mkdir -p $(DESTDIR)/$(LIBDIR)
 	install -D -m 755 $(OUT)/$(MINIGBM_FILENAME) $(DESTDIR)/$(LIBDIR)
diff --git a/backend_mock.c b/backend_mock.c
new file mode 100644
index 0000000..ae0d758
--- /dev/null
+++ b/backend_mock.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2023 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "drv_priv.h"
+
+static int backend_mock_init(struct driver *drv) {
+	return 0;
+}
+
+const struct backend backend_mock = {
+	.name = "Mock Backend",
+	.init = backend_mock_init,
+};
diff --git a/cros_gralloc/aidl/Android.bp b/cros_gralloc/aidl/Android.bp
index da8f670..b52f806 100644
--- a/cros_gralloc/aidl/Android.bp
+++ b/cros_gralloc/aidl/Android.bp
@@ -49,3 +49,15 @@
         "Main.cpp",
     ],
 }
+
+filegroup {
+    name: "allocator.minigbm.rc",
+    srcs: ["allocator.rc"]
+}
+
+prebuilt_etc {
+    name: "allocator.minigbm.xml",
+    src: "allocator.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
diff --git a/cros_gralloc/mapper_stablec/Android.bp b/cros_gralloc/mapper_stablec/Android.bp
index b25be17..401c177 100644
--- a/cros_gralloc/mapper_stablec/Android.bp
+++ b/cros_gralloc/mapper_stablec/Android.bp
@@ -42,3 +42,10 @@
     ],
     cpp_std: "c++20",
 }
+
+prebuilt_etc {
+    name: "mapper.minigbm.xml",
+    src: "mapper.minigbm.xml",
+    sub_dir: "vintf",
+    installable: false,
+}
diff --git a/drv.c b/drv.c
index e3a71cb..ea3bd64 100644
--- a/drv.c
+++ b/drv.c
@@ -6,6 +6,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -55,6 +56,8 @@
 extern const struct backend backend_udl;
 extern const struct backend backend_vkms;
 
+extern const struct backend backend_mock;
+
 static const struct backend *drv_backend_list[] = {
 #ifdef DRV_AMDGPU
 	&backend_amdgpu,
@@ -71,7 +74,7 @@
 	&backend_evdi,	    &backend_komeda,	&backend_marvell, &backend_mediatek,
 	&backend_meson,	    &backend_nouveau,	&backend_radeon,  &backend_rockchip,
 	&backend_sun4i_drm, &backend_synaptics, &backend_udl,	  &backend_virtgpu,
-	&backend_vkms
+	&backend_vkms,	    &backend_mock
 };
 
 void drv_preload(bool load)
@@ -118,7 +121,8 @@
 
 	const char *minigbm_debug;
 	minigbm_debug = drv_get_os_option(MINIGBM_DEBUG);
-	drv->compression = (minigbm_debug == NULL) || (strcmp(minigbm_debug, "nocompression") != 0);
+	drv->compression = (minigbm_debug == NULL) || (strstr(minigbm_debug, "nocompression") == NULL);
+	drv->log_bos = (minigbm_debug && strstr(minigbm_debug, "log_bos") != NULL);
 
 	drv->fd = fd;
 	drv->backend = drv_get_backend(fd);
@@ -365,6 +369,9 @@
 
 	drv_bo_acquire(bo);
 
+	if (drv->log_bos)
+		drv_bo_log_info(bo, "legacy created");
+
 	return bo;
 }
 
@@ -402,6 +409,9 @@
 
 	drv_bo_acquire(bo);
 
+	if (drv->log_bos)
+		drv_bo_log_info(bo, "created");
+
 	return bo;
 }
 
@@ -460,6 +470,9 @@
 		bo->meta.total_size += bo->meta.sizes[plane];
 	}
 
+	if (drv->log_bos)
+		drv_bo_log_info(bo, "imported");
+
 	return bo;
 
 destroy_bo:
@@ -574,6 +587,11 @@
 	return ret;
 }
 
+bool drv_bo_cached(struct bo *bo)
+{
+	return bo->meta.cached;
+}
+
 int drv_bo_invalidate(struct bo *bo, struct mapping *mapping)
 {
 	int ret = 0;
@@ -710,6 +728,26 @@
 	return bo->meta.total_size;
 }
 
+void drv_bo_log_info(const struct bo *bo, const char *prefix)
+{
+	const struct bo_metadata *meta = &bo->meta;
+
+	drv_logd("%s %s bo %p: %dx%d '%c%c%c%c' tiling %d plane %zu mod 0x%" PRIx64 " use 0x%" PRIx64 " size %zu\n",
+		 prefix, bo->drv->backend->name, bo,
+		 meta->width, meta->height,
+		 meta->format & 0xff,
+		 (meta->format >> 8) & 0xff,
+		 (meta->format >> 16) & 0xff,
+		 (meta->format >> 24) & 0xff,
+		 meta->tiling, meta->num_planes, meta->format_modifier,
+		 meta->use_flags, meta->total_size);
+	for (uint32_t i = 0; i < meta->num_planes; i++) {
+		drv_logd("  bo %p plane %d: offset %d size %d stride %d\n",
+			 bo, i, meta->offsets[i], meta->sizes[i],
+			 meta->strides[i]);
+	}
+}
+
 /*
  * Map internal fourcc codes back to standard fourcc codes.
  */
@@ -727,11 +765,11 @@
 						   out_use_flags);
 }
 
-void drv_log_prefix(enum drv_log_level level, const char *prefix, const char *file, int line,
+void drv_log_prefix(enum drv_log_level level, const char *prefix, const char *func, int line,
 		    const char *format, ...)
 {
 	char buf[50];
-	snprintf(buf, sizeof(buf), "[%s:%s(%d)]", prefix, basename(file), line);
+	snprintf(buf, sizeof(buf), "[%s:%s(%d)]", prefix, func, line);
 
 	va_list args;
 	va_start(args, format);
diff --git a/drv.h b/drv.h
index 106c29c..b61aedf 100644
--- a/drv.h
+++ b/drv.h
@@ -83,6 +83,21 @@
 #ifndef I915_FORMAT_MOD_4_TILED
 #define I915_FORMAT_MOD_4_TILED         fourcc_mod_code(INTEL, 9)
 #endif
+
+#ifndef I915_FORMAT_MOD_4_TILED_MTL_RC_CCS
+//TODO: remove this defination once drm_fourcc.h contains it.
+/*
+ * Intel color control surfaces (CCS) for display ver 14 render compression.
+ *
+ * The main surface is tile4 and at plane index 0, the CCS is linear and
+ * at index 1. A 64B CCS cache line corresponds to an area of 4x1 tiles in
+ * main surface. In other words, 4 bits in CCS map to a main surface cache
+ * line pair. The main surface pitch is required to be a multiple of four
+ * tile4 widths.
+ */
+#define I915_FORMAT_MOD_4_TILED_MTL_RC_CCS fourcc_mod_code(INTEL, 13)
+#endif
+
 // clang-format on
 struct driver;
 struct bo;
@@ -161,6 +176,8 @@
 
 int drv_bo_unmap(struct bo *bo, struct mapping *mapping);
 
+bool drv_bo_cached(struct bo *bo);
+
 int drv_bo_invalidate(struct bo *bo, struct mapping *mapping);
 
 int drv_bo_flush(struct bo *bo, struct mapping *mapping);
@@ -193,6 +210,8 @@
 
 size_t drv_bo_get_total_size(struct bo *bo);
 
+void drv_bo_log_info(const struct bo *bo, const char *prefix);
+
 uint32_t drv_get_standard_fourcc(uint32_t fourcc_internal);
 
 uint32_t drv_bytes_per_pixel_from_format(uint32_t format, size_t plane);
@@ -221,7 +240,7 @@
 
 #define _drv_log(level, format, ...)                                                               \
 	do {                                                                                       \
-		drv_log_prefix(level, "minigbm", __FILE__, __LINE__, format, ##__VA_ARGS__);       \
+		drv_log_prefix(level, "minigbm", __func__, __LINE__, format, ##__VA_ARGS__);       \
 	} while (0)
 
 #define drv_loge(format, ...) _drv_log(DRV_LOGE, format, ##__VA_ARGS__)
diff --git a/drv_priv.h b/drv_priv.h
index d9888d0..337b58e 100644
--- a/drv_priv.h
+++ b/drv_priv.h
@@ -27,6 +27,7 @@
 	uint64_t format_modifier;
 	uint64_t use_flags;
 	size_t total_size;
+	bool cached;
 
 	/*
 	 * Most of the following metadata is virtgpu cross_domain specific.  However, that backend
@@ -70,6 +71,7 @@
 	struct drv_array *mappings;
 	struct drv_array *combos;
 	bool compression;
+	bool log_bos;
 };
 
 struct backend {
diff --git a/gbm.c b/gbm.c
index 190347e..408fb98 100644
--- a/gbm.c
+++ b/gbm.c
@@ -87,10 +87,21 @@
 							     const uint64_t *modifiers,
 							     const unsigned int count)
 {
+	return gbm_surface_create_with_modifiers2(gbm, width, height, format, modifiers, count, 0);
+}
+
+PUBLIC struct gbm_surface *
+gbm_surface_create_with_modifiers2(struct gbm_device *gbm, uint32_t width, uint32_t height,
+				   uint32_t format, const uint64_t *modifiers,
+				   const unsigned int count, uint32_t flags)
+{
 	if (count != 0 || modifiers != NULL)
 		return NULL;
 
-	return gbm_surface_create(gbm, width, height, format, 0);
+	if (flags != 0)
+		return NULL;
+
+	return gbm_surface_create(gbm, width, height, format, flags);
 }
 
 PUBLIC struct gbm_bo *gbm_surface_lock_front_buffer(struct gbm_surface *surface)
@@ -161,8 +172,19 @@
 						   uint32_t height, uint32_t format,
 						   const uint64_t *modifiers, uint32_t count)
 {
+	return gbm_bo_create_with_modifiers2(gbm, width, height, format, modifiers, count, 0);
+}
+
+PUBLIC struct gbm_bo *gbm_bo_create_with_modifiers2(struct gbm_device *gbm, uint32_t width,
+						    uint32_t height, uint32_t format,
+						    const uint64_t *modifiers,
+						    const unsigned int count, uint32_t flags)
+{
 	struct gbm_bo *bo;
 
+	if (flags != 0)
+		return NULL;
+
 	bo = gbm_bo_new(gbm, format);
 
 	if (!bo)
@@ -267,6 +289,13 @@
 	return gbm_bo_map2(bo, x, y, width, height, transfer_flags, stride, map_data, 0);
 }
 
+PUBLIC enum gbm_bo_map_cache_mode gbm_bo_get_map_info(struct gbm_bo *bo)
+{
+	if (drv_bo_cached(bo->bo))
+		return GBM_BO_MAP_CACHE_CACHED;
+	return GBM_BO_MAP_CACHE_WC;
+}
+
 PUBLIC void gbm_bo_unmap(struct gbm_bo *bo, void *map_data)
 {
 	assert(bo);
@@ -323,7 +352,7 @@
 	return drv_bo_get_num_planes(bo->bo);
 }
 
-PUBLIC union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo, size_t plane)
+PUBLIC union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo, int plane)
 {
 	return (union gbm_bo_handle)drv_bo_get_plane_handle(bo->bo, (size_t)plane).u64;
 }
@@ -333,12 +362,12 @@
 	return drv_bo_get_plane_fd(bo->bo, plane);
 }
 
-PUBLIC uint32_t gbm_bo_get_offset(struct gbm_bo *bo, size_t plane)
+PUBLIC uint32_t gbm_bo_get_offset(struct gbm_bo *bo, int plane)
 {
 	return drv_bo_get_plane_offset(bo->bo, (size_t)plane);
 }
 
-PUBLIC uint32_t gbm_bo_get_stride_for_plane(struct gbm_bo *bo, size_t plane)
+PUBLIC uint32_t gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane)
 {
 	return drv_bo_get_plane_stride(bo->bo, (size_t)plane);
 }
diff --git a/gbm.h b/gbm.h
index 7fae11c..8e32769 100644
--- a/gbm.h
+++ b/gbm.h
@@ -339,6 +339,16 @@
                              uint32_t format,
                              const uint64_t *modifiers,
                              const unsigned int count);
+
+struct gbm_bo *
+gbm_bo_create_with_modifiers2(struct gbm_device *gbm,
+                              uint32_t width,
+                              uint32_t height,
+                              uint32_t format,
+                              const uint64_t *modifiers,
+                              const unsigned int count,
+                              uint32_t flags);
+
 #define GBM_BO_IMPORT_WL_BUFFER         0x5501
 #define GBM_BO_IMPORT_EGL_IMAGE         0x5502
 #define GBM_BO_IMPORT_FD                0x5503
@@ -403,6 +413,24 @@
            uint32_t x, uint32_t y, uint32_t width, uint32_t height,
            uint32_t flags, uint32_t *stride, void **map_data);
 
+/**
+ * Enum to indicate the cache attributes of CPU mapping returned by
+ * gbm_bo_map()
+ *
+ * Note that definition aligns with VIRTIO_GPU_MAP_CACHE_*, RUTABAGA_MAP_CACHE_*,
+ * and VIRGL_RENDERER_MAP_CACHE_* (but skipping the _NONE and _UNCACHED values as
+ * those don't actually make sense to use).
+ */
+enum gbm_bo_map_cache_mode {
+   /*GBM_BO_MAP_CACHE_NONE = 0,*/
+   GBM_BO_MAP_CACHE_CACHED = 1,
+   /*GBM_BO_MAP_CACHE_UNCACHED = 2,*/
+   GBM_BO_MAP_CACHE_WC = 3,
+};
+
+enum gbm_bo_map_cache_mode
+gbm_bo_get_map_info(struct gbm_bo *bo);
+
 void
 gbm_bo_unmap(struct gbm_bo *bo, void *map_data);
 
@@ -416,7 +444,7 @@
 gbm_bo_get_stride(struct gbm_bo *bo);
 
 uint32_t
-gbm_bo_get_stride_for_plane(struct gbm_bo *bo, size_t plane);
+gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane);
 
 uint32_t
 gbm_bo_get_format(struct gbm_bo *bo);
@@ -425,7 +453,7 @@
 gbm_bo_get_bpp(struct gbm_bo *bo);
 
 uint32_t
-gbm_bo_get_offset(struct gbm_bo *bo, size_t plane);
+gbm_bo_get_offset(struct gbm_bo *bo, int plane);
 
 struct gbm_device *
 gbm_bo_get_device(struct gbm_bo *bo);
@@ -443,7 +471,7 @@
 gbm_bo_get_plane_count(struct gbm_bo *bo);
 
 union gbm_bo_handle
-gbm_bo_get_handle_for_plane(struct gbm_bo *bo, size_t plane);
+gbm_bo_get_handle_for_plane(struct gbm_bo *bo, int plane);
 
 int
 gbm_bo_get_fd_for_plane(struct gbm_bo *bo, int plane);
@@ -473,6 +501,15 @@
                                   const uint64_t *modifiers,
                                   const unsigned int count);
 
+struct gbm_surface *
+gbm_surface_create_with_modifiers2(struct gbm_device *gbm,
+                                   uint32_t width,
+                                   uint32_t height,
+                                   uint32_t format,
+                                   const uint64_t *modifiers,
+                                   const unsigned int count,
+                                   uint32_t flags);
+
 struct gbm_bo *
 gbm_surface_lock_front_buffer(struct gbm_surface *surface);
 
diff --git a/gbm_unittest.cc b/gbm_unittest.cc
new file mode 100644
index 0000000..9f2a46f
--- /dev/null
+++ b/gbm_unittest.cc
@@ -0,0 +1,82 @@
+/* Copyright 2023 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test gbm.h module code using gtest.
+ */
+
+#include <drm/drm_fourcc.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <xf86drm.h>
+
+#include "gbm.h"
+
+class MockDrm
+{
+ public:
+	MOCK_METHOD(drmVersionPtr, drmGetVersion, (int fd));
+	MOCK_METHOD(void, drmFreeVersion, (drmVersionPtr v));
+};
+
+// Define a mock version of drmGetVersion
+drmVersionPtr drmGetVersion(int fd)
+{
+	drmVersionPtr mock_version = new drmVersion();
+	mock_version->name = "Mock Backend";
+	return mock_version;
+}
+
+// Define a mock version of drmFreeVersion
+void drmFreeVersion(drmVersionPtr v)
+{
+	delete(v);
+}
+
+/* TODO : This is a protocol to add unit tests for the public APIs in minigbm.
+ *
+ * The ultimate goal would be cover more APIs and the input combinations.
+ * Set fd to 0 for now, it doesn't have any particular meaning
+ */
+
+TEST(gbm_unit_test, create_device)
+{
+	MockDrm mock_drm; // Create a mock object
+
+	EXPECT_CALL(mock_drm, drmGetVersion(testing::_))
+	    .WillRepeatedly(testing::Invoke(&mock_drm, &MockDrm::drmGetVersion));
+
+	struct gbm_device *gbm_device = gbm_create_device(0);
+
+	ASSERT_TRUE(gbm_device);
+
+	gbm_device_destroy(gbm_device);
+}
+
+TEST(gbm_unit_test, valid_fd)
+{
+	MockDrm mock_drm; // Create a mock object
+
+	EXPECT_CALL(mock_drm, drmGetVersion(testing::_))
+	    .WillRepeatedly(testing::Invoke(&mock_drm, &MockDrm::drmGetVersion));
+	struct gbm_device *gbm_device = gbm_create_device(99);
+	int fd = gbm_device_get_fd(gbm_device);
+
+	ASSERT_EQ(fd, 99);
+
+	gbm_device_destroy(gbm_device);
+}
+
+TEST(gbm_unit_test, valid_backend_name)
+{
+	MockDrm mock_drm; // Create a mock object
+
+	EXPECT_CALL(mock_drm, drmGetVersion(testing::_))
+	    .WillRepeatedly(testing::Invoke(&mock_drm, &MockDrm::drmGetVersion));
+	struct gbm_device *gbm_device = gbm_create_device(0);
+	const char *backend_name = gbm_device_get_backend_name(gbm_device);
+
+	ASSERT_STREQ(backend_name, "Mock Backend");
+
+	gbm_device_destroy(gbm_device);
+}
diff --git a/i915.c b/i915.c
index 96333df..b9e377f 100644
--- a/i915.c
+++ b/i915.c
@@ -37,21 +37,16 @@
 static const uint64_t gen_modifier_order[] = { I915_FORMAT_MOD_Y_TILED_CCS, I915_FORMAT_MOD_Y_TILED,
 					       I915_FORMAT_MOD_X_TILED, DRM_FORMAT_MOD_LINEAR };
 
-static const uint64_t gen12_modifier_order_without_mc[] = { I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS,
-							    I915_FORMAT_MOD_Y_TILED,
-							    I915_FORMAT_MOD_X_TILED,
-							    DRM_FORMAT_MOD_LINEAR };
-
-static const uint64_t gen12_modifier_order_with_mc[] = { I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS,
-								I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS,
-								I915_FORMAT_MOD_Y_TILED,
-								I915_FORMAT_MOD_X_TILED,
-								DRM_FORMAT_MOD_LINEAR };
+static const uint64_t gen12_modifier_order[] = {
+	I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS, I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS,
+	I915_FORMAT_MOD_Y_TILED, I915_FORMAT_MOD_X_TILED, DRM_FORMAT_MOD_LINEAR
+};
 
 static const uint64_t gen11_modifier_order[] = { I915_FORMAT_MOD_Y_TILED, I915_FORMAT_MOD_X_TILED,
 						 DRM_FORMAT_MOD_LINEAR };
 
-static const uint64_t xe_lpdp_modifier_order[] = { I915_FORMAT_MOD_4_TILED, I915_FORMAT_MOD_X_TILED,
+static const uint64_t xe_lpdp_modifier_order[] = { I915_FORMAT_MOD_4_TILED_MTL_RC_CCS,
+						   I915_FORMAT_MOD_4_TILED, I915_FORMAT_MOD_X_TILED,
 						   DRM_FORMAT_MOD_LINEAR };
 
 struct modifier_support_t {
@@ -70,7 +65,6 @@
 	bool is_mtl;
 	int32_t num_fences_avail;
 	bool has_mmap_offset;
-	bool is_media_compression_enabled;
 };
 
 static void i915_info_from_device_id(struct i915_device *i915)
@@ -120,9 +114,10 @@
 	};
 	const uint16_t adlp_ids[] = { 0x46A0, 0x46A1, 0x46A2, 0x46A3, 0x46A6, 0x46A8, 0x46AA,
 				      0x462A, 0x4626, 0x4628, 0x46B0, 0x46B1, 0x46B2, 0x46B3,
-				      0x46C0, 0x46C1, 0x46C2, 0x46C3, 0x46D0, 0x46D1, 0x46D2 };
+				      0x46C0, 0x46C1, 0x46C2, 0x46C3, 0x46D0, 0x46D1, 0x46D2,
+				      0x46D3, 0x46D4 };
 
-	const uint16_t rplp_ids[] = { 0xA720, 0xA721, 0xA7A0, 0xA7A1, 0xA7A8, 0xA7A9 };
+	const uint16_t rplp_ids[] = { 0xA720, 0xA721, 0xA7A0, 0xA7A1, 0xA7A8, 0xA7A9, 0xA7AA, 0xA7AB, 0xA7AC, 0xA7AD };
 
 	const uint16_t mtl_ids[] = { 0x7D40, 0x7D60, 0x7D45, 0x7D55, 0x7DD5 };
 
@@ -200,13 +195,12 @@
 		i915->modifier.order = xe_lpdp_modifier_order;
 		i915->modifier.count = ARRAY_SIZE(xe_lpdp_modifier_order);
 	} else if (i915->graphics_version == 12) {
-		if (i915->is_media_compression_enabled) {
-			i915->modifier.order = gen12_modifier_order_with_mc;
-			i915->modifier.count = ARRAY_SIZE(gen12_modifier_order_with_mc);
-		} else {
-			i915->modifier.order = gen12_modifier_order_without_mc;
-			i915->modifier.count = ARRAY_SIZE(gen12_modifier_order_without_mc);
-		}
+		/*
+		 * On ADL platforms of gen 12 onwards, Intel media compression is supported for
+		 * video decoding on Chrome.
+		 */
+		i915->modifier.order = gen12_modifier_order;
+		i915->modifier.count = ARRAY_SIZE(gen12_modifier_order);
 	} else if (i915->graphics_version == 11) {
 		i915->modifier.order = gen11_modifier_order;
 		i915->modifier.count = ARRAY_SIZE(gen11_modifier_order);
@@ -308,14 +302,10 @@
 				     ARRAY_SIZE(scanout_render_formats), &metadata_4_tiled,
 				     scanout_and_render_not_linear);
 	} else {
-		struct format_metadata metadata_y_tiled = {
-			.tiling = I915_TILING_Y,
-			.priority = 3,
-			.modifier =
-			    (i915->graphics_version == 12 && i915->is_media_compression_enabled)
-				? I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS
-				: I915_FORMAT_MOD_Y_TILED
-		};
+		struct format_metadata metadata_y_tiled = { .tiling = I915_TILING_Y,
+							    .priority = 3,
+							    .modifier = I915_FORMAT_MOD_Y_TILED };
+
 /* Support y-tiled NV12 and P010 for libva */
 #ifdef I915_SCANOUT_Y_TILED
 		const uint64_t nv12_usage =
@@ -327,13 +317,6 @@
 		const uint64_t nv12_usage = BO_USE_TEXTURE | BO_USE_HW_VIDEO_DECODER;
 		const uint64_t p010_usage = nv12_usage;
 #endif
-		drv_add_combination(drv, DRM_FORMAT_NV12, &metadata_y_tiled, nv12_usage);
-		drv_add_combination(drv, DRM_FORMAT_P010, &metadata_y_tiled, p010_usage);
-
-		/* Don't allocate media compressed buffers for formats other than NV12
-		 * and P010.
-		 */
-		metadata_y_tiled.modifier = I915_FORMAT_MOD_Y_TILED;
 		drv_add_combinations(drv, render_formats, ARRAY_SIZE(render_formats),
 				     &metadata_y_tiled, render_not_linear);
 		/* Y-tiled scanout isn't available on old platforms so we add
@@ -342,6 +325,8 @@
 		drv_add_combinations(drv, scanout_render_formats,
 				     ARRAY_SIZE(scanout_render_formats), &metadata_y_tiled,
 				     render_not_linear);
+		drv_add_combination(drv, DRM_FORMAT_NV12, &metadata_y_tiled, nv12_usage);
+		drv_add_combination(drv, DRM_FORMAT_P010, &metadata_y_tiled, p010_usage);
 	}
 	return 0;
 }
@@ -459,17 +444,6 @@
 	if (!i915)
 		return -ENOMEM;
 
-	const char *enable_intel_media_compression_env_var =
-	    getenv("ENABLE_INTEL_MEDIA_COMPRESSION");
-	if (enable_intel_media_compression_env_var == NULL) {
-		drv_logd("Failed to get ENABLE_INTEL_MEDIA_COMPRESSION");
-		i915->is_media_compression_enabled = false;
-	} else {
-		i915->is_media_compression_enabled =
-		    (drv->compression) &&
-		    (strcmp(enable_intel_media_compression_env_var, "1") == 0);
-	}
-
 	get_param.param = I915_PARAM_CHIPSET_ID;
 	get_param.value = &(i915->device_id);
 	ret = drmIoctl(drv->fd, DRM_IOCTL_I915_GETPARAM, &get_param);
@@ -584,15 +558,11 @@
 {
 	size_t num_planes = drv_num_planes_from_format(format);
 	if (modifier == I915_FORMAT_MOD_Y_TILED_CCS ||
-	    modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS) {
+	    modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS ||
+	    modifier == I915_FORMAT_MOD_4_TILED_MTL_RC_CCS) {
 		assert(num_planes == 1);
 		return 2;
 	} else if (modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS) {
-		assert(drv);
-		struct i915_device *i915 = drv->priv;
-		assert(i915 && i915->is_media_compression_enabled);
-		(void)i915;
-
 		assert(num_planes == 2);
 		return 4;
 	}
@@ -615,6 +585,19 @@
 		if (!combo)
 			return -EINVAL;
 		modifier = combo->metadata.modifier;
+		/*
+		 * Media compression modifiers should not be picked automatically by minigbm based
+		 * on |use_flags|. Instead the client should request them explicitly through
+		 * gbm_bo_create_with_modifiers().
+		 */
+		assert(modifier != I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS);
+		/* TODO(b/323863689): Account for driver's bandwidth compression in minigbm for
+		 * media compressed buffers. */
+	}
+	if (modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS &&
+	    !(format == DRM_FORMAT_NV12 || format == DRM_FORMAT_P010)) {
+		drv_loge("Media compression is only supported for NV12 and P010\n");
+		return -EINVAL;
 	}
 
 	/*
@@ -655,9 +638,6 @@
 		modifier = DRM_FORMAT_MOD_LINEAR;
 	}
 
-	assert(modifier != I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS ||
-	       i915->is_media_compression_enabled);
-
 	switch (modifier) {
 	case DRM_FORMAT_MOD_LINEAR:
 		bo->meta.tiling = I915_TILING_NONE;
@@ -675,6 +655,7 @@
 		bo->meta.tiling = I915_TILING_Y;
 		break;
 	case I915_FORMAT_MOD_4_TILED:
+	case I915_FORMAT_MOD_4_TILED_MTL_RC_CCS:
 		bo->meta.tiling = I915_TILING_4;
 		break;
 	}
@@ -735,10 +716,17 @@
 		bo->meta.total_size = offset;
 	} else if (modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS ||
 		   modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS) {
+		/*
+		 * Media compression modifiers should only be possible via the
+		 * gbm_bo_create_with_modifiers() path, i.e., the minigbm client needs to
+		 * explicitly request it.
+		 */
 		assert(modifier != I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS ||
-		       i915->is_media_compression_enabled);
+		       use_flags == BO_USE_NONE);
 		assert(modifier != I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS ||
-		       (format == DRM_FORMAT_NV12 || format == DRM_FORMAT_P010));
+		       bo->meta.use_flags == BO_USE_NONE);
+		assert(modifier != I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS ||
+		       (!!modifiers && count > 0));
 		assert(drv_num_planes_from_format(format) > 0);
 
 		uint32_t offset = 0;
@@ -794,6 +782,39 @@
 		/* Total number of planes & sizes */
 		bo->meta.num_planes = plane + a_plane;
 		bo->meta.total_size = offset;
+	} else if (modifier == I915_FORMAT_MOD_4_TILED_MTL_RC_CCS) {
+
+		/*
+		 * considering only 128 byte compression and one cache line of
+		 * aux buffer(64B) contains compression status of 4-Y tiles.
+		 * Which is 4 * (128B * 32L).
+		 * line stride(bytes) is 4 * 128B
+		 * and tile stride(lines) is 32L
+		 */
+		uint32_t stride = ALIGN(drv_stride_from_format(format, width, 0), 512);
+		stride = ALIGN(stride, 256);
+
+		height = ALIGN(drv_height_from_format(format, height, 0), 32);
+
+
+		bo->meta.strides[0] = stride;
+		/* size calculation and alignment are 64KB aligned
+		 * size as per spec
+		 */
+		bo->meta.sizes[0] = ALIGN(stride * height, 65536);
+		bo->meta.offsets[0] = 0;
+
+		/* Aux buffer is linear and page aligned. It is placed after
+		 * other planes and aligned to main buffer stride.
+		 */
+		bo->meta.strides[1] = bo->meta.strides[0] / 8;
+
+		/* Aligned to page size */
+		bo->meta.sizes[1] = ALIGN(bo->meta.sizes[0] / 256, getpagesize());
+		bo->meta.offsets[1] = bo->meta.sizes[0];
+		/* Total number of planes & sizes */
+		bo->meta.num_planes = 2;
+		bo->meta.total_size = bo->meta.sizes[0] + bo->meta.sizes[1];
 	} else {
 		return i915_bo_from_format(bo, width, height, format);
 	}
@@ -858,6 +879,10 @@
 			return -errno;
 		}
 	}
+
+	bo->meta.cached = (i915->has_llc || i915->is_mtl) &&
+			  !(bo->meta.use_flags & BO_USE_SCANOUT);
+
 	return 0;
 }
 
@@ -907,7 +932,8 @@
 	if ((bo->meta.format_modifier == I915_FORMAT_MOD_Y_TILED_CCS) ||
 	    (bo->meta.format_modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS) ||
 	    (bo->meta.format_modifier == I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS) ||
-	    (bo->meta.format_modifier == I915_FORMAT_MOD_4_TILED))
+	    (bo->meta.format_modifier == I915_FORMAT_MOD_4_TILED) ||
+	    (bo->meta.format_modifier == I915_FORMAT_MOD_4_TILED_MTL_RC_CCS))
 		return MAP_FAILED;
 
 	if (bo->meta.tiling == I915_TILING_NONE) {
diff --git a/mediatek.c b/mediatek.c
index 2d60462..6ebde6e 100644
--- a/mediatek.c
+++ b/mediatek.c
@@ -47,6 +47,17 @@
 #define DONT_USE_64_ALIGNMENT_FOR_VIDEO_BUFFERS
 #endif
 
+// Devices newer than MT8186 support AR30 overlays and 10-bit video.
+// clang-format off
+#if !defined(MTK_MT8173) && \
+    !defined(MTK_MT8183) && \
+    !defined(MTK_MT8186) && \
+    !defined(MTK_MT8192)
+// clang-format on
+#define SUPPORT_P010
+#define SUPPORT_AR30_OVERLAYS
+#endif
+
 // For Mali Sigurd based GPUs, the texture unit reads outside the specified texture dimensions.
 // Therefore, certain formats require extra memory padding to its allocated surface to prevent the
 // hardware from reading outside an allocation. For YVU420, we need additional padding for the last
@@ -71,6 +82,9 @@
 	DRM_FORMAT_NV21,
 	DRM_FORMAT_YUYV,
 #endif
+#ifdef SUPPORT_P010
+	DRM_FORMAT_P010,
+#endif
 #ifdef SUPPORT_FP16_AND_10BIT_ABGR
 	DRM_FORMAT_ABGR2101010,
 	DRM_FORMAT_ABGR16161616F,
@@ -83,6 +97,9 @@
 static const uint32_t video_yuv_formats[] = {
 	DRM_FORMAT_NV21,
 	DRM_FORMAT_NV12,
+#ifdef SUPPORT_P010
+	DRM_FORMAT_P010,
+#endif
 	DRM_FORMAT_YUYV,
 	DRM_FORMAT_YVU420,
 	DRM_FORMAT_YVU420_ANDROID
@@ -110,7 +127,13 @@
 	drv_add_combinations(drv, texture_source_formats, ARRAY_SIZE(texture_source_formats),
 			     &LINEAR_METADATA, BO_USE_TEXTURE_MASK | BO_USE_PROTECTED);
 
-	drv_add_combination(drv, DRM_FORMAT_R8, &LINEAR_METADATA, BO_USE_SW_MASK | BO_USE_LINEAR | BO_USE_PROTECTED);
+	drv_add_combination(drv, DRM_FORMAT_R8, &LINEAR_METADATA,
+			    BO_USE_SW_MASK | BO_USE_LINEAR | BO_USE_PROTECTED);
+
+#ifdef SUPPORT_AR30_OVERLAYS
+	drv_add_combination(drv, DRM_FORMAT_ARGB2101010, &LINEAR_METADATA,
+			    BO_USE_TEXTURE | BO_USE_SCANOUT | BO_USE_PROTECTED | BO_USE_LINEAR);
+#endif
 
 	/* YUYV format for video overlay and camera subsystem. */
 	drv_add_combination(drv, DRM_FORMAT_YUYV, &LINEAR_METADATA,
@@ -124,12 +147,26 @@
 	metadata.tiling = TILE_TYPE_LINEAR;
 	metadata.priority = 1;
 	metadata.modifier = DRM_FORMAT_MOD_LINEAR;
-	drv_modify_combination(drv, DRM_FORMAT_YVU420, &metadata, BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
-	drv_modify_combination(drv, DRM_FORMAT_YVU420_ANDROID, &metadata, BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
+	drv_modify_combination(drv, DRM_FORMAT_YVU420, &metadata,
+			       BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
+#ifdef MTK_MT8173
+	/*
+	 * b/292507490: The MT8173 decoder can output YUV420 only. Some CTS tests feed the
+	 * decoded buffer to the hardware encoder and the tests allocate the buffer with
+	 * DRM_FORMAT_FLEX_YCbCr_420_888 with the mask of BO_USE_HW_VIDEO_ENCODER |
+	 * BO_USE_HW_VIDEO_DECODER. Therefore, we have to allocate YUV420 in the case.
+	 */
+	drv_modify_combination(drv, DRM_FORMAT_YVU420, &metadata, BO_USE_HW_VIDEO_ENCODER);
+#endif
+	drv_modify_combination(drv, DRM_FORMAT_YVU420_ANDROID, &metadata,
+			       BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
 #ifdef USE_NV12_FOR_HW_VIDEO_DECODING
 	// TODO(hiroh): Switch to use NV12 for video decoder on MT8173 as well.
-	drv_modify_combination(drv, DRM_FORMAT_NV12, &metadata, BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
+	drv_modify_combination(drv, DRM_FORMAT_NV12, &metadata,
+			       BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
 #endif
+	drv_modify_combination(drv, DRM_FORMAT_P010, &metadata,
+			       BO_USE_HW_VIDEO_DECODER | BO_USE_PROTECTED);
 
 	/*
 	 * R8 format is used for Android's HAL_PIXEL_FORMAT_BLOB for input/output from
@@ -176,6 +213,11 @@
 	const bool is_camera_preview =
 	    (bo->meta.use_flags & BO_USE_SCANOUT) && (bo->meta.use_flags & BO_USE_CAMERA_WRITE);
 	const bool is_hw_video_encoder = bo->meta.use_flags & BO_USE_HW_VIDEO_ENCODER;
+#ifdef MTK_MT8173
+	const bool is_mt8173_video_decoder = bo->meta.use_flags & BO_USE_HW_VIDEO_DECODER;
+#else
+	const bool is_mt8173_video_decoder = false;
+#endif
 	/*
 	 * Android sends blobs for encoding in the shape of a single-row pixel buffer. Use R8 +
 	 * single row as a proxy for Android HAL_PIXEL_FORMAT_BLOB until a drm equivalent is
@@ -203,7 +245,24 @@
 	stride = ALIGN(stride, 64);
 #endif
 
-	if ((is_hw_video_encoder && !is_format_blob) || is_camera_preview) {
+	/*
+	 * The mediatek video decoder requires to align width and height by 64. But this is
+	 * the requirement for mediatek tiled format (e.g. MT21 and MM21). The buffers are
+	 * not allocated by minigbm. So we don't have to care about it. The tiled buffer is
+	 * converted to NV12 or YV12, which is allocated by minigbm. V4L2 MDP doesn't
+	 * require any special alignment for them.
+	 * On the other hand, the mediatek video encoder reuqires a padding on each plane.
+	 * When both video decoder and encoder use flag is masked (in some CTS test), we
+	 * align with the encoder alignment.
+	 * However, V4L2VideoDecodeAccelerator used on MT8173 fails handling the buffer with
+	 * padding, although V4L2VideoDecoder used on MT8183 and later can do. We workaround
+	 * this problem to allocate a buffer without padding on MT8173. This works because
+	 * MT8173 decoder's output NV12 is converted to YV12 buffer that is allocated with
+	 * video encoder usage mask only and thus have padding in Android.
+	 * See go/mediatek-video-buffer-alignment-note for detail.
+	 */
+	if ((is_hw_video_encoder && !is_mt8173_video_decoder && !is_format_blob) ||
+	    is_camera_preview) {
 		uint32_t aligned_height = ALIGN(height, 32);
 		uint32_t padding[DRV_MAX_PLANES] = { 0 };
 
@@ -432,12 +491,24 @@
 			break;
 		}
 #endif
+		/*
+		 * b/292507490: The MT8173 decoder can output YUV420 only. Some CTS tests feed the
+		 * decoded buffer to the hardware encoder and the tests allocate the buffer with
+		 * DRM_FORMAT_FLEX_YCbCr_420_888 with the mask of BO_USE_HW_VIDEO_ENCODER |
+		 * BO_USE_HW_VIDEO_DECODER. Therefore, we have to allocate YUV420 in the case.
+		 */
 		if (use_flags &
 		    (BO_USE_CAMERA_READ | BO_USE_CAMERA_WRITE | BO_USE_HW_VIDEO_ENCODER)) {
+#ifndef MTK_MT8173
 			*out_format = DRM_FORMAT_NV12;
 			break;
+#else
+			if (!(use_flags & BO_USE_HW_VIDEO_DECODER)) {
+				*out_format = DRM_FORMAT_NV12;
+				break;
+			}
+#endif
 		}
-
 		/* HACK: See b/139714614 */
 		*out_format = DRM_FORMAT_YVU420;
 		*out_use_flags &= ~BO_USE_SCANOUT;
diff --git a/rust/gbm_wrapper.h b/rust/gbm_wrapper.h
new file mode 100644
index 0000000..abcf3af
--- /dev/null
+++ b/rust/gbm_wrapper.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2024 Google LLC
+ */
+
+#include <gbm.h>
+
+// bindgen doesn't create constants for macros that expand to other macros
+// https://github.com/Smithay/gbm.rs/blob/606c4260e0147256fb5c2901bbe837c0dc7d9f2d/gbm-sys/build.rs#L16
+
+const unsigned int __BINDGEN_TMP_GBM_BO_IMPORT_WL_BUFFER = GBM_BO_IMPORT_WL_BUFFER;
+#undef GBM_BO_IMPORT_WL_BUFFER
+const unsigned int GBM_BO_IMPORT_WL_BUFFER = __BINDGEN_TMP_GBM_BO_IMPORT_WL_BUFFER;
+#define GBM_BO_IMPORT_WL_BUFFER GBM_BO_IMPORT_WL_BUFFER
+
+const unsigned int __BINDGEN_TMP_GBM_BO_IMPORT_EGL_IMAGE = GBM_BO_IMPORT_EGL_IMAGE;
+#undef GBM_BO_IMPORT_EGL_IMAGE
+const unsigned int GBM_BO_IMPORT_EGL_IMAGE = __BINDGEN_TMP_GBM_BO_IMPORT_EGL_IMAGE;
+#define GBM_BO_IMPORT_EGL_IMAGE GBM_BO_IMPORT_EGL_IMAGE
+
+const unsigned int __BINDGEN_TMP_GBM_BO_IMPORT_FD = GBM_BO_IMPORT_FD;
+#undef GBM_BO_IMPORT_FD
+const unsigned int GBM_BO_IMPORT_FD = __BINDGEN_TMP_GBM_BO_IMPORT_FD;
+#define GBM_BO_IMPORT_FD GBM_BO_IMPORT_FD
+
+const unsigned int __BINDGEN_TMP_GBM_BO_IMPORT_FD_MODIFIER = GBM_BO_IMPORT_FD_MODIFIER;
+#undef GBM_BO_IMPORT_FD_MODIFIER
+const unsigned int GBM_BO_IMPORT_FD_MODIFIER = __BINDGEN_TMP_GBM_BO_IMPORT_FD_MODIFIER;
+#define GBM_BO_IMPORT_FD_MODIFIER GBM_BO_IMPORT_FD_MODIFIER
diff --git a/testrunner.cc b/testrunner.cc
new file mode 100644
index 0000000..37a858f
--- /dev/null
+++ b/testrunner.cc
@@ -0,0 +1,15 @@
+/* Copyright 2023 The ChromiumOS Authors
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Main entrypoint for gtest.
+ */
+
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv)
+{
+	testing::InitGoogleTest(&argc, argv);
+
+	return RUN_ALL_TESTS();
+}
diff --git a/virtgpu.h b/virtgpu.h
index 3f68731..b2f82ba 100644
--- a/virtgpu.h
+++ b/virtgpu.h
@@ -23,3 +23,9 @@
 	param_guest_vram,
 	param_max,
 };
+
+#define VIRTIO_GPU_CAPSET_VIRGL 1
+#define VIRTIO_GPU_CAPSET_VIRGL2 2
+#define VIRTIO_GPU_CAPSET_GFXSTREAM 3
+#define VIRTIO_GPU_CAPSET_VENUS 4
+#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5
diff --git a/virtgpu_cross_domain.c b/virtgpu_cross_domain.c
index dc3fbc1..85589dc 100644
--- a/virtgpu_cross_domain.c
+++ b/virtgpu_cross_domain.c
@@ -16,7 +16,6 @@
 #include "util.h"
 #include "virtgpu.h"
 
-#define CAPSET_CROSS_DOMAIN 5
 #define CAPSET_CROSS_FAKE 30
 
 static const uint32_t scanout_render_formats[] = { DRM_FORMAT_ABGR8888, DRM_FORMAT_ARGB8888,
@@ -246,7 +245,7 @@
 	if (!params[param_context_init].value)
 		return -ENOTSUP;
 
-	if ((params[param_supported_capset_ids].value & (1 << CAPSET_CROSS_DOMAIN)) == 0)
+	if ((params[param_supported_capset_ids].value & (1 << VIRTIO_GPU_CAPSET_CROSS_DOMAIN)) == 0)
 		return -ENOTSUP;
 
 	if (!params[param_resource_blob].value)
@@ -275,7 +274,7 @@
 	priv->ring_addr = MAP_FAILED;
 	drv->priv = priv;
 
-	args.cap_set_id = CAPSET_CROSS_DOMAIN;
+	args.cap_set_id = VIRTIO_GPU_CAPSET_CROSS_DOMAIN;
 	args.size = sizeof(struct CrossDomainCapabilities);
 	args.addr = (unsigned long long)&cross_domain_caps;
 
@@ -296,7 +295,7 @@
 	// Intialize the cross domain context.  Create one fence context to wait for metadata
 	// queries.
 	ctx_set_params[0].param = VIRTGPU_CONTEXT_PARAM_CAPSET_ID;
-	ctx_set_params[0].value = CAPSET_CROSS_DOMAIN;
+	ctx_set_params[0].value = VIRTIO_GPU_CAPSET_CROSS_DOMAIN;
 	ctx_set_params[1].param = VIRTGPU_CONTEXT_PARAM_NUM_RINGS;
 	ctx_set_params[1].value = 1;
 
diff --git a/virtgpu_virgl.c b/virtgpu_virgl.c
index b8517fc..222703e 100644
--- a/virtgpu_virgl.c
+++ b/virtgpu_virgl.c
@@ -549,14 +549,31 @@
 	int ret;
 	struct drm_virtgpu_get_caps cap_args = { 0 };
 
+	memset(caps, 0, sizeof(union virgl_caps));
 	*caps_is_v2 = 0;
-	cap_args.addr = (unsigned long long)caps;
-	if (params[param_capset_fix].value) {
+
+	if (params[param_supported_capset_ids].value) {
+		drv_logi("Supported CAPSET IDs: %u.", params[param_supported_capset_ids].value);
+		if (params[param_supported_capset_ids].value & (1 << VIRTIO_GPU_CAPSET_VIRGL2)) {
+			*caps_is_v2 = 1;
+		} else if (params[param_supported_capset_ids].value &
+			   (1 << VIRTIO_GPU_CAPSET_VIRGL)) {
+			*caps_is_v2 = 0;
+		} else {
+			drv_logi("Unrecognized CAPSET IDs: %u. Assuming all zero caps.",
+				 params[param_supported_capset_ids].value);
+			return 0;
+		}
+	} else if (params[param_capset_fix].value) {
 		*caps_is_v2 = 1;
-		cap_args.cap_set_id = 2;
+	}
+
+	cap_args.addr = (unsigned long long)caps;
+	if (*caps_is_v2) {
+		cap_args.cap_set_id = VIRTIO_GPU_CAPSET_VIRGL2;
 		cap_args.size = sizeof(union virgl_caps);
 	} else {
-		cap_args.cap_set_id = 1;
+		cap_args.cap_set_id = VIRTIO_GPU_CAPSET_VIRGL;
 		cap_args.size = sizeof(struct virgl_caps_v1);
 	}
 
@@ -566,7 +583,7 @@
 		*caps_is_v2 = 0;
 
 		// Fallback to v1
-		cap_args.cap_set_id = 1;
+		cap_args.cap_set_id = VIRTIO_GPU_CAPSET_VIRGL;
 		cap_args.size = sizeof(struct virgl_caps_v1);
 
 		ret = drmIoctl(drv->fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &cap_args);