diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index af3b7395bf69..c964064651d5 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
 }
 
 static struct drm_client_buffer *
-drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			 bool fbdev)
 {
 	const struct drm_format_info *info = drm_format_info(format);
 	struct drm_mode_create_dumb dumb_args = { };
@@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
 	dumb_args.width = width;
 	dumb_args.height = height;
 	dumb_args.bpp = info->cpp[0] * 8;
-	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
+	if (fbdev)
+		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
+	else
+		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
 	if (ret)
 		goto err_delete;
 
@@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * @width: Framebuffer width
  * @height: Framebuffer height
  * @format: Buffer format
+ * @fbdev: True if the client provides an fbdev device, or false otherwise
  *
  * This function creates a &drm_client_buffer which consists of a
  * &drm_framebuffer backed by a dumb buffer.
@@ -411,12 +416,13 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * Pointer to a client buffer or an error pointer on failure.
  */
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev)
 {
 	struct drm_client_buffer *buffer;
 	int ret;
 
-	buffer = drm_client_buffer_create(client, width, height, format);
+	buffer = drm_client_buffer_create(client, width, height, format, fbdev);
 	if (IS_ERR(buffer))
 		return buffer;
 
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 63279e984342..34e532953b5a 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -101,6 +101,9 @@ int drm_mode_getresources(struct drm_device *dev,
 int drm_mode_create_dumb(struct drm_device *dev,
 			 struct drm_mode_create_dumb *args,
 			 struct drm_file *file_priv);
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv);
 int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
 			  struct drm_file *file_priv);
 
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index ad17fa21cebb..a605b4c4e4a3 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -57,14 +57,10 @@
  * a hardware-specific ioctl to allocate suitable buffer objects.
  */
 
-int drm_mode_create_dumb(struct drm_device *dev,
-			 struct drm_mode_create_dumb *args,
-			 struct drm_file *file_priv)
+static int drm_mode_sanitize_args(struct drm_mode_create_dumb *args)
 {
 	u32 cpp, stride, size;
 
-	if (!dev->driver->dumb_create)
-		return -ENOSYS;
 	if (!args->width || !args->height || !args->bpp)
 		return -EINVAL;
 
@@ -93,6 +89,21 @@ int drm_mode_create_dumb(struct drm_device *dev,
 	args->pitch = 0;
 	args->size = 0;
 
+	return 0;
+}
+
+int drm_mode_create_dumb(struct drm_device *dev,
+			 struct drm_mode_create_dumb *args,
+			 struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
 	return dev->driver->dumb_create(file_priv, dev, args);
 }
 
@@ -102,6 +113,21 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
 	return drm_mode_create_dumb(dev, data, file_priv);
 }
 
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create_fbdev)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
+	return dev->driver->dumb_create_fbdev(file_priv, dev, args);
+}
+
 /**
  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 660ec5038c4e..9d1140a789f4 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -655,6 +655,15 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_framebuffer *fb = fb_helper->fb;
 
+	/*
+	 * If the driver provides an fbdev create function, it is expected
+	 * to provide its own implementation of deferred I/O. Shadow
+	 * buffering can still be enabled with the prefer_shadow_fbdev
+	 * flag.
+	 */
+	if (dev->driver->dumb_create_fbdev)
+		return dev->mode_config.prefer_shadow_fbdev;
+
 	return dev->mode_config.prefer_shadow_fbdev ||
 	       dev->mode_config.prefer_shadow ||
 	       fb->funcs->dirty;
@@ -2130,6 +2139,7 @@ static void drm_fbdev_fb_destroy(struct fb_info *info)
 static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
 	struct drm_fb_helper *fb_helper = info->par;
+	struct drm_device *dev = fb_helper->dev;
 
 	if (drm_fbdev_use_shadow_fb(fb_helper))
 		/*
@@ -2137,14 +2147,20 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 		 * deferred I/O.
 		 */
 		return fb_deferred_io_mmap(info, vma);
-	else if (fb_helper->dev->driver->gem_prime_mmap)
+	else if (!dev->driver->dumb_create_fbdev && drm_fbdev_use_deferred_io(fb_helper))
+		/*
+		 * Drivers without fbdev dumb-create helper rely on fbdev's
+		 * deferred I/O.
+		 */
+		return fb_deferred_io_mmap(info, vma);
+	else if (dev->driver->gem_prime_mmap)
 		/*
 		 * Either directly mmap'ed or with deferred I/O; drivers
 		 * without shadow buffer handle mmap themselves.
 		 */
-		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
-	else
-		return -ENODEV;
+		return dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
+
+	return -ENODEV;
 }
 
 static bool drm_fbdev_use_iomem(struct fb_info *info)
@@ -2395,7 +2411,7 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 
 	format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
 	buffer = drm_client_framebuffer_create(client, sizes->surface_width,
-					       sizes->surface_height, format);
+					       sizes->surface_height, format, true);
 	if (IS_ERR(buffer))
 		return PTR_ERR(buffer);
 
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 4fc8018eddda..8d80d5594bea 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -153,7 +153,8 @@ struct drm_client_buffer {
 };
 
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format);
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev);
 void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
 int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect);
 int drm_client_buffer_vmap(struct drm_client_buffer *buffer,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index f6159acb8856..da4a095de1e7 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -379,6 +379,23 @@ struct drm_driver {
 	int (*dumb_create)(struct drm_file *file_priv,
 			   struct drm_device *dev,
 			   struct drm_mode_create_dumb *args);
+	/**
+	 * @dumb_create_fbdev:
+	 *
+	 * This creates a new dumb buffer for use with DRM fbdev clients and
+	 * returns the resulting buffer handle. This handle can then be wrapped
+	 * up into a framebuffer modeset object.
+	 *
+	 * This is an interface for kernel clients and never called via ioctl.
+	 *
+	 * See @dumb_create for more information.
+	 *
+	 * Returns:
+	 * Zero on success, negative errno on failure.
+	 */
+	int (*dumb_create_fbdev)(struct drm_file *file_priv,
+				 struct drm_device *dev,
+				 struct drm_mode_create_dumb *args);
 	/**
 	 * @dumb_map_offset:
 	 *
