diff --git a/.gitignore b/.gitignore
index 88aa4a9..56b1812 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-SOURCES/mutter-40.8.tar.xz
+SOURCES/mutter-40.9.tar.xz
diff --git a/.mutter.metadata b/.mutter.metadata
index 63676b6..4e26344 100644
--- a/.mutter.metadata
+++ b/.mutter.metadata
@@ -1 +1 @@
-68d490f395c64f470105e3fb5b75878ce6e80640 SOURCES/mutter-40.8.tar.xz
+f9b20f8330ecdb76fc887c4a99c03b406e26fd66 SOURCES/mutter-40.9.tar.xz
diff --git a/SOURCES/eglstream-overview-fixes.patch b/SOURCES/eglstream-overview-fixes.patch
new file mode 100644
index 0000000..55b06be
--- /dev/null
+++ b/SOURCES/eglstream-overview-fixes.patch
@@ -0,0 +1,324 @@
+From d3d8ab8eabc3178f3c31ee71dcc926297ff1c1b0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 8 Feb 2022 17:14:06 +0100
+Subject: [PATCH 1/2] shaped-texture: Pass along the snippet to the texture
+ tower
+
+The snippet is used make sure the right source is sampled in the shader.
+This wasn't done in the texture tower, meaning the textures from the
+tower were not correct.
+
+Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/528
+---
+ src/compositor/meta-shaped-texture.c |  2 ++
+ src/compositor/meta-texture-tower.c  | 27 +++++++++++++++++++++++++++
+ src/compositor/meta-texture-tower.h  |  3 +++
+ 3 files changed, 32 insertions(+)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 095dd246f0c0..68919c5f1c5c 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -1242,6 +1242,8 @@ meta_shaped_texture_set_snippet (MetaShapedTexture *stex,
+   g_clear_pointer (&stex->snippet, cogl_object_unref);
+   if (snippet)
+     stex->snippet = cogl_object_ref (snippet);
++
++  meta_texture_tower_set_snippet (stex->paint_tower, snippet);
+ }
+ 
+ /**
+diff --git a/src/compositor/meta-texture-tower.c b/src/compositor/meta-texture-tower.c
+index 1fc4623e5e54..0ae717abe4d4 100644
+--- a/src/compositor/meta-texture-tower.c
++++ b/src/compositor/meta-texture-tower.c
+@@ -62,6 +62,7 @@ struct _MetaTextureTower
+   CoglOffscreen *fbos[MAX_TEXTURE_LEVELS];
+   Box invalid[MAX_TEXTURE_LEVELS];
+   CoglPipeline *pipeline_template;
++  CoglSnippet *snippet;
+ };
+ 
+ /**
+@@ -97,6 +98,7 @@ meta_texture_tower_free (MetaTextureTower *tower)
+     cogl_object_unref (tower->pipeline_template);
+ 
+   meta_texture_tower_set_base_texture (tower, NULL);
++  cogl_clear_object (&tower->snippet);
+ 
+   g_free (tower);
+ }
+@@ -216,6 +218,28 @@ meta_texture_tower_update_area (MetaTextureTower *tower,
+     }
+ }
+ 
++void
++meta_texture_tower_set_snippet (MetaTextureTower *tower,
++                                CoglSnippet      *snippet)
++{
++  int i;
++
++  if (tower->snippet == snippet)
++    return;
++
++  g_clear_pointer (&tower->snippet, cogl_object_unref);
++
++  if (snippet)
++    tower->snippet = cogl_object_ref (snippet);
++
++  for (i = 1; i < tower->n_levels; i++)
++    {
++      cogl_clear_object (&tower->textures[i]);
++      g_clear_object (&tower->fbos[i]);
++    }
++  cogl_clear_object (&tower->pipeline_template);
++}
++
+ /* It generally looks worse if we scale up a window texture by even a
+  * small amount than if we scale it down using bilinear filtering, so
+  * we always pick the *larger* adjacent level. */
+@@ -408,6 +432,9 @@ texture_tower_revalidate (MetaTextureTower *tower,
+   pipeline = cogl_pipeline_copy (tower->pipeline_template);
+   cogl_pipeline_set_layer_texture (pipeline, 0, tower->textures[level - 1]);
+ 
++  if (tower->snippet && level == 1)
++    cogl_pipeline_add_layer_snippet (pipeline, 0, tower->snippet);
++
+   cogl_framebuffer_draw_textured_rectangle (fb, pipeline,
+                                             invalid->x1, invalid->y1,
+                                             invalid->x2, invalid->y2,
+diff --git a/src/compositor/meta-texture-tower.h b/src/compositor/meta-texture-tower.h
+index 1f5b371467c9..5522dfbb16ac 100644
+--- a/src/compositor/meta-texture-tower.h
++++ b/src/compositor/meta-texture-tower.h
+@@ -63,6 +63,9 @@ void              meta_texture_tower_update_area       (MetaTextureTower *tower,
+ CoglTexture      *meta_texture_tower_get_paint_texture (MetaTextureTower    *tower,
+                                                         ClutterPaintContext *paint_context);
+ 
++void meta_texture_tower_set_snippet (MetaTextureTower *tower,
++                                     CoglSnippet      *snippet);
++
+ G_END_DECLS
+ 
+ #endif /* __META_TEXTURE_TOWER_H__ */
+-- 
+2.34.1
+
+
+From b78a24dfebf56b04538058ff731890ee997d0d10 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Wed, 9 Feb 2022 12:41:14 +0100
+Subject: [PATCH 2/2] shaped-texture: Paint with the right layer snippet
+
+When we get passed a "snippet" to the shaped texture, it's added as a
+pipeline layer snippet to change how the source texture is sampled. When
+we draw from a texture tower however we have allocated regular textures
+which doesn't need any special layer snippet, so create separate
+pipelines for those that doesn't use that snippet.
+
+Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/528
+---
+ src/compositor/meta-shaped-texture.c | 126 +++++++++++++++++++++------
+ 1 file changed, 98 insertions(+), 28 deletions(-)
+
+diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c
+index 68919c5f1c5c..0d09f2f4e164 100644
+--- a/src/compositor/meta-shaped-texture.c
++++ b/src/compositor/meta-shaped-texture.c
+@@ -91,8 +91,12 @@ struct _MetaShapedTexture
+   CoglSnippet *snippet;
+ 
+   CoglPipeline *base_pipeline;
++  CoglPipeline *unmasked_pipeline;
++  CoglPipeline *unmasked_tower_pipeline;
+   CoglPipeline *masked_pipeline;
++  CoglPipeline *masked_tower_pipeline;
+   CoglPipeline *unblended_pipeline;
++  CoglPipeline *unblended_tower_pipeline;
+ 
+   gboolean is_y_inverted;
+ 
+@@ -243,8 +247,12 @@ static void
+ meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex)
+ {
+   g_clear_pointer (&stex->base_pipeline, cogl_object_unref);
++  g_clear_pointer (&stex->unmasked_pipeline, cogl_object_unref);
++  g_clear_pointer (&stex->unmasked_tower_pipeline, cogl_object_unref);
+   g_clear_pointer (&stex->masked_pipeline, cogl_object_unref);
++  g_clear_pointer (&stex->masked_tower_pipeline, cogl_object_unref);
+   g_clear_pointer (&stex->unblended_pipeline, cogl_object_unref);
++  g_clear_pointer (&stex->unblended_tower_pipeline, cogl_object_unref);
+ }
+ 
+ static void
+@@ -381,9 +389,6 @@ get_base_pipeline (MetaShapedTexture *stex,
+ 
+   cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix);
+ 
+-  if (stex->snippet)
+-    cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
+-
+   stex->base_pipeline = pipeline;
+ 
+   return stex->base_pipeline;
+@@ -391,47 +396,112 @@ get_base_pipeline (MetaShapedTexture *stex,
+ 
+ static CoglPipeline *
+ get_unmasked_pipeline (MetaShapedTexture *stex,
+-                       CoglContext       *ctx)
++                       CoglContext       *ctx,
++                       CoglTexture       *tex)
+ {
+-  return get_base_pipeline (stex, ctx);
++  if (stex->texture == tex)
++    {
++      CoglPipeline *pipeline;
++
++      if (stex->unmasked_pipeline)
++        return stex->unmasked_pipeline;
++
++      pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
++      if (stex->snippet)
++        cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
++
++      stex->unmasked_pipeline = pipeline;
++      return pipeline;
++    }
++  else
++    {
++      CoglPipeline *pipeline;
++
++      if (stex->unmasked_tower_pipeline)
++        return stex->unmasked_tower_pipeline;
++
++      pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
++      stex->unmasked_tower_pipeline = pipeline;
++      return pipeline;
++    }
+ }
+ 
+ static CoglPipeline *
+ get_masked_pipeline (MetaShapedTexture *stex,
+-                     CoglContext       *ctx)
++                     CoglContext       *ctx,
++                     CoglTexture       *tex)
+ {
+-  CoglPipeline *pipeline;
++  if (stex->texture == tex)
++    {
++      CoglPipeline *pipeline;
+ 
+-  if (stex->masked_pipeline)
+-    return stex->masked_pipeline;
++      if (stex->masked_pipeline)
++        return stex->masked_pipeline;
+ 
+-  pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
+-  cogl_pipeline_set_layer_combine (pipeline, 1,
+-                                   "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
+-                                   NULL);
++      pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
++      cogl_pipeline_set_layer_combine (pipeline, 1,
++                                       "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
++                                       NULL);
++      if (stex->snippet)
++        cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
+ 
+-  stex->masked_pipeline = pipeline;
++      stex->masked_pipeline = pipeline;
++      return pipeline;
++    }
++  else
++    {
++      CoglPipeline *pipeline;
+ 
+-  return pipeline;
++      if (stex->masked_tower_pipeline)
++        return stex->masked_tower_pipeline;
++
++      pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
++      cogl_pipeline_set_layer_combine (pipeline, 1,
++                                       "RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
++                                       NULL);
++
++      stex->masked_tower_pipeline = pipeline;
++      return pipeline;
++    }
+ }
+ 
+ static CoglPipeline *
+ get_unblended_pipeline (MetaShapedTexture *stex,
+-                        CoglContext       *ctx)
++                        CoglContext       *ctx,
++                        CoglTexture       *tex)
+ {
+-  CoglPipeline *pipeline;
++  if (stex->texture == tex)
++    {
++      CoglPipeline *pipeline;
+ 
+-  if (stex->unblended_pipeline)
+-    return stex->unblended_pipeline;
++      if (stex->unblended_pipeline)
++        return stex->unblended_pipeline;
+ 
+-  pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
+-  cogl_pipeline_set_layer_combine (pipeline, 0,
+-                                   "RGBA = REPLACE (TEXTURE)",
+-                                   NULL);
++      pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
++      cogl_pipeline_set_layer_combine (pipeline, 0,
++                                       "RGBA = REPLACE (TEXTURE)",
++                                       NULL);
++      if (stex->snippet)
++        cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet);
+ 
+-  stex->unblended_pipeline = pipeline;
++      stex->unblended_pipeline = pipeline;
++      return pipeline;
++    }
++  else
++    {
++      CoglPipeline *pipeline;
+ 
+-  return pipeline;
++      if (stex->unblended_tower_pipeline)
++        return stex->unblended_tower_pipeline;
++
++      pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx));
++      cogl_pipeline_set_layer_combine (pipeline, 0,
++                                       "RGBA = REPLACE (TEXTURE)",
++                                       NULL);
++
++      stex->unblended_tower_pipeline = pipeline;
++      return pipeline;
++    }
+ }
+ 
+ static CoglPipeline *
+@@ -742,7 +812,7 @@ do_paint_content (MetaShapedTexture   *stex,
+         {
+           CoglPipeline *opaque_pipeline;
+ 
+-          opaque_pipeline = get_unblended_pipeline (stex, ctx);
++          opaque_pipeline = get_unblended_pipeline (stex, ctx, paint_tex);
+           cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
+           cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter);
+ 
+@@ -786,11 +856,11 @@ do_paint_content (MetaShapedTexture   *stex,
+ 
+       if (stex->mask_texture == NULL)
+         {
+-          blended_pipeline = get_unmasked_pipeline (stex, ctx);
++          blended_pipeline = get_unmasked_pipeline (stex, ctx, paint_tex);
+         }
+       else
+         {
+-          blended_pipeline = get_masked_pipeline (stex, ctx);
++          blended_pipeline = get_masked_pipeline (stex, ctx, paint_tex);
+           cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture);
+           cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter);
+         }
+-- 
+2.34.1
+
diff --git a/SOURCES/hw-cursor-dumb-buffer.patch b/SOURCES/hw-cursor-dumb-buffer.patch
new file mode 100644
index 0000000..a2fc4e5
--- /dev/null
+++ b/SOURCES/hw-cursor-dumb-buffer.patch
@@ -0,0 +1,490 @@
+From acb3e966b26ea55019a148d6482f5aa4c05275a9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 8 Feb 2022 15:58:33 +0100
+Subject: [PATCH 1/4] drm-buffer: Keep track of handle as well
+
+This handle is used by the legacy KMS API; lets avoid having to have GBM
+specific code where this is done by letting the MetaDrmBuffer API, that
+already has this information, expose it.
+---
+ src/backends/native/meta-drm-buffer-dumb.c    |  1 +
+ src/backends/native/meta-drm-buffer-gbm.c     |  1 +
+ src/backends/native/meta-drm-buffer-import.c  |  1 +
+ src/backends/native/meta-drm-buffer-private.h |  1 +
+ src/backends/native/meta-drm-buffer.c         | 10 ++++++++++
+ src/backends/native/meta-drm-buffer.h         |  2 ++
+ 6 files changed, 16 insertions(+)
+
+diff --git a/src/backends/native/meta-drm-buffer-dumb.c b/src/backends/native/meta-drm-buffer-dumb.c
+index 373eb5c73581..b5e15528a1c6 100644
+--- a/src/backends/native/meta-drm-buffer-dumb.c
++++ b/src/backends/native/meta-drm-buffer-dumb.c
+@@ -194,6 +194,7 @@ init_dumb_buffer_in_impl (MetaKmsImpl  *impl,
+     .width = data->width,
+     .height = data->height,
+     .format = data->format,
++    .handle = create_arg.handle,
+     .handles = { create_arg.handle },
+     .strides = { create_arg.pitch },
+   };
+diff --git a/src/backends/native/meta-drm-buffer-gbm.c b/src/backends/native/meta-drm-buffer-gbm.c
+index 48ee9eb048ba..fa45dd98b91e 100644
+--- a/src/backends/native/meta-drm-buffer-gbm.c
++++ b/src/backends/native/meta-drm-buffer-gbm.c
+@@ -119,6 +119,7 @@ init_fb_id (MetaDrmBufferGbm  *buffer_gbm,
+   fb_args.width = gbm_bo_get_width (bo);
+   fb_args.height = gbm_bo_get_height (bo);
+   fb_args.format = gbm_bo_get_format (bo);
++  fb_args.handle = gbm_bo_get_handle (bo).u32;
+ 
+   if (!meta_drm_buffer_ensure_fb_id (META_DRM_BUFFER (buffer_gbm),
+                                      use_modifiers, &fb_args, error))
+diff --git a/src/backends/native/meta-drm-buffer-import.c b/src/backends/native/meta-drm-buffer-import.c
+index 1e5a38246172..ea744953cb9b 100644
+--- a/src/backends/native/meta-drm-buffer-import.c
++++ b/src/backends/native/meta-drm-buffer-import.c
+@@ -125,6 +125,7 @@ import_gbm_buffer (MetaDrmBufferImport  *buffer_import,
+   fb_args.width = gbm_bo_get_width (primary_bo);
+   fb_args.height = gbm_bo_get_height (primary_bo);
+   fb_args.format = gbm_bo_get_format (primary_bo);
++  fb_args.handle = gbm_bo_get_handle (primary_bo).u32;
+ 
+   imported_bo = dmabuf_to_gbm_bo (importer,
+                                   dmabuf_fd,
+diff --git a/src/backends/native/meta-drm-buffer-private.h b/src/backends/native/meta-drm-buffer-private.h
+index 2c77eb957948..f660d3a7f363 100644
+--- a/src/backends/native/meta-drm-buffer-private.h
++++ b/src/backends/native/meta-drm-buffer-private.h
+@@ -33,6 +33,7 @@ typedef struct _MetaDrmFbArgs
+   uint32_t offsets[4];
+   uint32_t strides[4];
+   uint64_t modifiers[4];
++  uint32_t handle;
+ } MetaDrmFbArgs;
+ 
+ struct _MetaDrmBufferClass
+diff --git a/src/backends/native/meta-drm-buffer.c b/src/backends/native/meta-drm-buffer.c
+index 81a36196e528..a0f8055b3107 100644
+--- a/src/backends/native/meta-drm-buffer.c
++++ b/src/backends/native/meta-drm-buffer.c
+@@ -50,6 +50,7 @@ typedef struct _MetaDrmBufferPrivate
+ {
+   MetaKmsDevice *device;
+   uint32_t fb_id;
++  uint32_t handle;
+ } MetaDrmBufferPrivate;
+ 
+ G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaDrmBuffer, meta_drm_buffer,
+@@ -139,6 +140,7 @@ meta_drm_buffer_ensure_fb_in_impl (MetaDrmBuffer        *buffer,
+     }
+ 
+   priv->fb_id = fb_id;
++  priv->handle = fb_args->handle;
+ 
+   return TRUE;
+ }
+@@ -242,6 +244,14 @@ meta_drm_buffer_get_fb_id (MetaDrmBuffer *buffer)
+   return priv->fb_id;
+ }
+ 
++uint32_t
++meta_drm_buffer_get_handle (MetaDrmBuffer *buffer)
++{
++  MetaDrmBufferPrivate *priv = meta_drm_buffer_get_instance_private (buffer);
++
++  return priv->handle;
++}
++
+ int
+ meta_drm_buffer_get_width (MetaDrmBuffer *buffer)
+ {
+diff --git a/src/backends/native/meta-drm-buffer.h b/src/backends/native/meta-drm-buffer.h
+index 1647d399e7f2..476264ce753e 100644
+--- a/src/backends/native/meta-drm-buffer.h
++++ b/src/backends/native/meta-drm-buffer.h
+@@ -34,6 +34,8 @@ G_DECLARE_DERIVABLE_TYPE (MetaDrmBuffer,
+ 
+ uint32_t meta_drm_buffer_get_fb_id (MetaDrmBuffer *buffer);
+ 
++uint32_t meta_drm_buffer_get_handle (MetaDrmBuffer *buffer);
++
+ int meta_drm_buffer_get_width (MetaDrmBuffer *buffer);
+ 
+ int meta_drm_buffer_get_height (MetaDrmBuffer *buffer);
+-- 
+2.34.1
+
+
+From bdd2f1dd0a67743085c84f6e060160fbd28122df Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 8 Feb 2022 16:00:17 +0100
+Subject: [PATCH 2/4] kms/impl-device/simple: Get the buffer handle from
+ MetaDrmBuffer
+
+This avoids buffer implementation specific code where it shouldn't
+matter.
+---
+ src/backends/native/meta-kms-impl-device-simple.c | 15 ++-------------
+ 1 file changed, 2 insertions(+), 13 deletions(-)
+
+diff --git a/src/backends/native/meta-kms-impl-device-simple.c b/src/backends/native/meta-kms-impl-device-simple.c
+index 1519962b8ed7..1663a2e9cacd 100644
+--- a/src/backends/native/meta-kms-impl-device-simple.c
++++ b/src/backends/native/meta-kms-impl-device-simple.c
+@@ -1182,20 +1182,9 @@ process_cursor_plane_assignment (MetaKmsImplDevice       *impl_device,
+       height = plane_assignment->dst_rect.height;
+ 
+       if (plane_assignment->buffer)
+-        {
+-          MetaDrmBufferGbm *buffer_gbm =
+-            META_DRM_BUFFER_GBM (plane_assignment->buffer);
+-          struct gbm_bo *bo;
+-          union gbm_bo_handle handle;
+-
+-          bo = meta_drm_buffer_gbm_get_bo (buffer_gbm);
+-          handle = gbm_bo_get_handle (bo);
+-          handle_u32 = handle.u32;
+-        }
++        handle_u32 = meta_drm_buffer_get_handle (plane_assignment->buffer);
+       else
+-        {
+-          handle_u32 = 0;
+-        }
++        handle_u32 = 0;
+ 
+       meta_topic (META_DEBUG_KMS,
+                   "[simple] Setting HW cursor of CRTC %u (%s) to %u "
+-- 
+2.34.1
+
+
+From d781f140b7197d3c9cd67f8fded4e2b58a4eda92 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 8 Feb 2022 15:05:32 +0100
+Subject: [PATCH 3/4] cursor-renderer/native: Move buffer creation to helper
+
+---
+ .../native/meta-cursor-renderer-native.c      | 134 ++++++++++++------
+ 1 file changed, 89 insertions(+), 45 deletions(-)
+
+diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
+index 098ef24bdf2e..2bcbe6b2c8b0 100644
+--- a/src/backends/native/meta-cursor-renderer-native.c
++++ b/src/backends/native/meta-cursor-renderer-native.c
+@@ -1215,6 +1215,81 @@ ensure_cursor_priv (MetaCursorSprite *cursor_sprite)
+   return cursor_priv;
+ }
+ 
++static MetaDrmBuffer *
++create_cursor_drm_buffer_gbm (MetaGpuKms  *gpu_kms,
++                              uint8_t     *pixels,
++                              int          width,
++                              int          height,
++                              int          stride,
++                              int          cursor_width,
++                              int          cursor_height,
++                              uint32_t     format,
++                              GError     **error)
++{
++  MetaKmsDevice *kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
++  struct gbm_device *gbm_device;
++  struct gbm_bo *bo;
++  uint8_t buf[4 * cursor_width * cursor_height];
++  int i;
++  MetaDrmBufferGbm *buffer_gbm;
++
++  gbm_device = meta_gbm_device_from_gpu (gpu_kms);
++  if (!gbm_device_is_format_supported (gbm_device, format,
++                                       GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
++                   "Buffer format not supported");
++      return NULL;
++    }
++
++  bo = gbm_bo_create (gbm_device, cursor_width, cursor_height,
++                      format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
++  if (!bo)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
++                   "Failed to allocate gbm_bo: %s", g_strerror (errno));
++      return NULL;
++    }
++
++  memset (buf, 0, sizeof (buf));
++  for (i = 0; i < height; i++)
++    memcpy (buf + i * 4 * cursor_width, pixels + i * stride, width * 4);
++  if (gbm_bo_write (bo, buf, cursor_width * cursor_height * 4) != 0)
++    {
++      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
++                   "Failed write to gbm_bo: %s", g_strerror (errno));
++      gbm_bo_destroy (bo);
++      return NULL;
++    }
++
++  buffer_gbm = meta_drm_buffer_gbm_new_take (kms_device, bo, FALSE, error);
++  if (!buffer_gbm)
++    {
++      gbm_bo_destroy (bo);
++      return NULL;
++    }
++
++  return META_DRM_BUFFER (buffer_gbm);
++}
++
++static MetaDrmBuffer *
++create_cursor_drm_buffer (MetaGpuKms  *gpu_kms,
++                          uint8_t     *pixels,
++                          int          width,
++                          int          height,
++                          int          stride,
++                          int          cursor_width,
++                          int          cursor_height,
++                          uint32_t     format,
++                          GError     **error)
++{
++  return create_cursor_drm_buffer_gbm (gpu_kms, pixels,
++                                       width, height, stride,
++                                       cursor_width, cursor_height,
++                                       format,
++                                       error);
++}
++
+ static void
+ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
+                                        MetaGpuKms               *gpu_kms,
+@@ -1226,8 +1301,9 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
+                                        uint32_t                  gbm_format)
+ {
+   uint64_t cursor_width, cursor_height;
++  MetaDrmBuffer *buffer;
+   MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data;
+-  struct gbm_device *gbm_device;
++  g_autoptr (GError) error = NULL;
+ 
+   cursor_renderer_gpu_data =
+     meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms);
+@@ -1244,52 +1320,20 @@ load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native,
+       return;
+     }
+ 
+-  gbm_device = meta_gbm_device_from_gpu (gpu_kms);
+-  if (gbm_device_is_format_supported (gbm_device, gbm_format,
+-                                      GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
+-    {
+-      MetaKmsDevice *kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
+-      struct gbm_bo *bo;
+-      uint8_t buf[4 * cursor_width * cursor_height];
+-      uint i;
+-      g_autoptr (GError) error = NULL;
+-      MetaDrmBufferGbm *buffer_gbm;
+-
+-      bo = gbm_bo_create (gbm_device, cursor_width, cursor_height,
+-                          gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+-      if (!bo)
+-        {
+-          meta_warning ("Failed to allocate HW cursor buffer");
+-          return;
+-        }
+-
+-      memset (buf, 0, sizeof(buf));
+-      for (i = 0; i < height; i++)
+-        memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4);
+-      if (gbm_bo_write (bo, buf, cursor_width * cursor_height * 4) != 0)
+-        {
+-          meta_warning ("Failed to write cursors buffer data: %s",
+-                        g_strerror (errno));
+-          gbm_bo_destroy (bo);
+-          return;
+-        }
+-
+-      buffer_gbm = meta_drm_buffer_gbm_new_take (kms_device, bo, FALSE, &error);
+-      if (!buffer_gbm)
+-        {
+-          meta_warning ("Failed to create DRM buffer wrapper: %s",
+-                        error->message);
+-          gbm_bo_destroy (bo);
+-          return;
+-        }
+-
+-      set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms,
+-                                        META_DRM_BUFFER (buffer_gbm));
+-    }
+-  else
++  buffer = create_cursor_drm_buffer (gpu_kms,
++                                     pixels,
++                                     width, height, rowstride,
++                                     cursor_width,
++                                     cursor_height,
++                                     gbm_format,
++                                     &error);
++  if (!buffer)
+     {
+-      meta_warning ("HW cursor for format %d not supported", gbm_format);
++      g_warning ("Realizing HW cursor failed: %s", error->message);
++      return;
+     }
++
++  set_pending_cursor_sprite_buffer (cursor_sprite, gpu_kms, buffer);
+ }
+ 
+ static gboolean
+-- 
+2.34.1
+
+
+From 717baf8dc1dd2f043350dd354bc4905285ba3337 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
+Date: Tue, 8 Feb 2022 15:38:37 +0100
+Subject: [PATCH 4/4] cursor-renderer/native: Support allocating dumb buffers
+
+---
+ .../native/meta-cursor-renderer-native.c      | 88 ++++++++++++++-----
+ 1 file changed, 67 insertions(+), 21 deletions(-)
+
+diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c
+index 2bcbe6b2c8b0..18ffdf37dac4 100644
+--- a/src/backends/native/meta-cursor-renderer-native.c
++++ b/src/backends/native/meta-cursor-renderer-native.c
+@@ -39,6 +39,7 @@
+ #include "backends/meta-monitor-manager-private.h"
+ #include "backends/meta-output.h"
+ #include "backends/native/meta-crtc-kms.h"
++#include "backends/native/meta-drm-buffer-dumb.h"
+ #include "backends/native/meta-drm-buffer-gbm.h"
+ #include "backends/native/meta-kms-device.h"
+ #include "backends/native/meta-kms-plane.h"
+@@ -1216,24 +1217,23 @@ ensure_cursor_priv (MetaCursorSprite *cursor_sprite)
+ }
+ 
+ static MetaDrmBuffer *
+-create_cursor_drm_buffer_gbm (MetaGpuKms  *gpu_kms,
+-                              uint8_t     *pixels,
+-                              int          width,
+-                              int          height,
+-                              int          stride,
+-                              int          cursor_width,
+-                              int          cursor_height,
+-                              uint32_t     format,
+-                              GError     **error)
++create_cursor_drm_buffer_gbm (MetaGpuKms         *gpu_kms,
++                              struct gbm_device  *gbm_device,
++                              uint8_t            *pixels,
++                              int                 width,
++                              int                 height,
++                              int                 stride,
++                              int                 cursor_width,
++                              int                 cursor_height,
++                              uint32_t            format,
++                              GError            **error)
+ {
+   MetaKmsDevice *kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
+-  struct gbm_device *gbm_device;
+   struct gbm_bo *bo;
+   uint8_t buf[4 * cursor_width * cursor_height];
+   int i;
+   MetaDrmBufferGbm *buffer_gbm;
+ 
+-  gbm_device = meta_gbm_device_from_gpu (gpu_kms);
+   if (!gbm_device_is_format_supported (gbm_device, format,
+                                        GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
+     {
+@@ -1272,6 +1272,38 @@ create_cursor_drm_buffer_gbm (MetaGpuKms  *gpu_kms,
+   return META_DRM_BUFFER (buffer_gbm);
+ }
+ 
++static MetaDrmBuffer *
++create_cursor_drm_buffer_dumb (MetaGpuKms         *gpu_kms,
++                               uint8_t            *pixels,
++                               int                 width,
++                               int                 height,
++                               int                 stride,
++                               int                 cursor_width,
++                               int                 cursor_height,
++                               uint32_t            format,
++                               GError            **error)
++{
++  MetaKmsDevice *kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
++  MetaDrmBufferDumb *buffer_dumb;
++  int i;
++  uint8_t *data;
++
++  buffer_dumb = meta_drm_buffer_dumb_new (kms_device,
++                                          cursor_width, cursor_height,
++                                          format,
++                                          error);
++  if (!buffer_dumb)
++    return NULL;
++
++  data = meta_drm_buffer_dumb_get_data (buffer_dumb);
++
++  memset (data, 0, cursor_width * cursor_height * 4);
++  for (i = 0; i < height; i++)
++    memcpy (data + i * 4 * cursor_width, pixels + i * stride, width * 4);
++
++  return META_DRM_BUFFER (buffer_dumb);
++}
++
+ static MetaDrmBuffer *
+ create_cursor_drm_buffer (MetaGpuKms  *gpu_kms,
+                           uint8_t     *pixels,
+@@ -1283,11 +1315,27 @@ create_cursor_drm_buffer (MetaGpuKms  *gpu_kms,
+                           uint32_t     format,
+                           GError     **error)
+ {
+-  return create_cursor_drm_buffer_gbm (gpu_kms, pixels,
+-                                       width, height, stride,
+-                                       cursor_width, cursor_height,
+-                                       format,
+-                                       error);
++  struct gbm_device *gbm_device;
++
++  gbm_device = meta_gbm_device_from_gpu (gpu_kms);
++  if (gbm_device)
++    {
++      return create_cursor_drm_buffer_gbm (gpu_kms, gbm_device,
++                                           pixels,
++                                           width, height, stride,
++                                           cursor_width, cursor_height,
++                                           format,
++                                           error);
++    }
++  else
++    {
++      return create_cursor_drm_buffer_dumb (gpu_kms,
++                                            pixels,
++                                            width, height, stride,
++                                            cursor_width, cursor_height,
++                                            format,
++                                            error);
++    }
+ }
+ 
+ static void
+@@ -1629,6 +1677,9 @@ realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer      *renderer,
+         }
+ 
+       gbm_device = meta_gbm_device_from_gpu (gpu_kms);
++      if (!gbm_device)
++        return;
++
+       bo = gbm_bo_import (gbm_device,
+                           GBM_BO_IMPORT_WL_BUFFER,
+                           buffer,
+@@ -1807,13 +1858,8 @@ init_hw_cursor_support_for_gpu (MetaGpuKms *gpu_kms)
+ {
+   MetaKmsDevice *kms_device = meta_gpu_kms_get_kms_device (gpu_kms);
+   MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data;
+-  struct gbm_device *gbm_device;
+   uint64_t width, height;
+ 
+-  gbm_device = meta_gbm_device_from_gpu (gpu_kms);
+-  if (!gbm_device)
+-    return;
+-
+   cursor_renderer_gpu_data =
+     meta_create_cursor_renderer_native_gpu_data (gpu_kms);
+ 
+-- 
+2.34.1
+
diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec
index df7d223..f85c9df 100644
--- a/SPECS/mutter.spec
+++ b/SPECS/mutter.spec
@@ -9,8 +9,8 @@
 %global tarball_version %%(echo %{version} | tr '~' '.')
 
 Name:          mutter
-Version:       40.8
-Release:       2%{?dist}
+Version:       40.9
+Release:       1%{?dist}
 Summary:       Window and compositing manager based on Clutter
 
 License:       GPLv2+
@@ -62,6 +62,12 @@ Patch12: 0001-backends-x11-Fix-key-repeat-of-on-screen-keyboard-fo.patch
 # Backport monitor configuration policy feature (#2046159)
 Patch13: monitor-config-policy.patch
 
+# Backport dumb buffer cursor support (EGLStream) (#2055015)
+Patch14: hw-cursor-dumb-buffer.patch
+
+# Backport EGLStream overview fixes (#2052584)
+Patch15: eglstream-overview-fixes.patch
+
 BuildRequires: chrpath
 BuildRequires: pango-devel
 BuildRequires: startup-notification-devel
@@ -208,6 +214,18 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop
 %{_datadir}/mutter-%{mutter_api_version}/tests
 
 %changelog
+* Tue Feb 22 2022 Florian Müllner <fmuellner@redhat.com> - 40.9-1
+- Update to 40.9
+  Resolves: #2056414
+
+* Mon Feb 21 2022 Jonas Ådahl <jadahl@redhat.com> - 40.8-4
+- Backport EGLStream overview fixes
+  Resolves: #2052584
+
+* Mon Feb 21 2022 Jonas Ådahl <jadahl@redhat.com> - 40.8-3
+- Backport dumb buffer cursor support (EGLStream)
+  Resolves: #2055015
+
 * Fri Feb 04 2022 Jonas Ådahl <jadahl@redhat.com> - 40.8-2
 - Backport monitor configuration policy feature
   Resolves: #2046159