drm/fbdev-generic: Convert to fbdev-ttm
authorThomas Zimmermann <tzimmermann@suse.de>
Fri, 19 Apr 2024 08:29:35 +0000 (10:29 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Thu, 2 May 2024 09:33:32 +0000 (11:33 +0200)
Only TTM-based drivers use fbdev-generic. Rename it to fbdev-ttm and
change the symbol infix from _generic_ to _ttm_. Link the source file
into TTM helpers, so that it is only build if TTM-based drivers have
been selected. Select DRM_TTM_HELPER for loongson.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240419083331.7761-43-tzimmermann@suse.de
15 files changed:
Documentation/gpu/drm-kms-helpers.rst
drivers/gpu/drm/Makefile
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/drm_fbdev_generic.c [deleted file]
drivers/gpu/drm/drm_fbdev_ttm.c [new file with mode: 0644]
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c
drivers/gpu/drm/loongson/Kconfig
drivers/gpu/drm/loongson/lsdc_drv.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/qxl/qxl_drv.c
drivers/gpu/drm/tiny/bochs.c
drivers/gpu/drm/vboxvideo/vbox_drv.c
drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
include/drm/drm_fbdev_generic.h [deleted file]
include/drm/drm_fbdev_ttm.h [new file with mode: 0644]

index 59cfe8a7a8bace8e24d2f032e59084e06ddca557..e46ab9b670acd50163a4798bc7c1c458677e693c 100644 (file)
@@ -116,7 +116,7 @@ fbdev Helper Functions Reference
 .. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
    :export:
 
-.. kernel-doc:: drivers/gpu/drm/drm_fbdev_generic.c
+.. kernel-doc:: drivers/gpu/drm/drm_fbdev_ttm.c
    :export:
 
 format Helper Functions Reference
index fc793c6dd2998073f6ef9b284ad43a87916b44f3..68cc9258ffc4b88041efc7f6ade13b827e46fba0 100644 (file)
@@ -118,6 +118,7 @@ drm_vram_helper-y := drm_gem_vram_helper.o
 obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o
 
 drm_ttm_helper-y := drm_gem_ttm_helper.o
+drm_ttm_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_ttm.o
 obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
 
 #
@@ -143,9 +144,7 @@ drm_kms_helper-y := \
        drm_self_refresh_helper.o \
        drm_simple_kms_helper.o
 drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
-drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += \
-       drm_fbdev_generic.o \
-       drm_fb_helper.o
+drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
 obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
 
 #
index ea14f1c8f430443ac46c9447ba7488085bac30e9..4f76aa606ac9ded8e7720a9fec48f5499e642665 100644 (file)
@@ -24,7 +24,7 @@
 
 #include <drm/amdgpu_drm.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_pciids.h>
@@ -2318,9 +2318,9 @@ retry_init:
            !list_empty(&adev_to_drm(adev)->mode_config.connector_list)) {
                /* select 8 bpp console on low vram cards */
                if (adev->gmc.real_vram_size <= (32*1024*1024))
-                       drm_fbdev_generic_setup(adev_to_drm(adev), 8);
+                       drm_fbdev_ttm_setup(adev_to_drm(adev), 8);
                else
-                       drm_fbdev_generic_setup(adev_to_drm(adev), 32);
+                       drm_fbdev_ttm_setup(adev_to_drm(adev), 32);
        }
 
        ret = amdgpu_debugfs_init(adev);
diff --git a/drivers/gpu/drm/drm_fbdev_generic.c b/drivers/gpu/drm/drm_fbdev_generic.c
deleted file mode 100644 (file)
index 97e579c..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-#include <linux/moduleparam.h>
-#include <linux/vmalloc.h>
-
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem.h>
-#include <drm/drm_print.h>
-
-#include <drm/drm_fbdev_generic.h>
-
-/* @user: 1=userspace, 0=fbcon */
-static int drm_fbdev_generic_fb_open(struct fb_info *info, int user)
-{
-       struct drm_fb_helper *fb_helper = info->par;
-
-       /* No need to take a ref for fbcon because it unbinds on unregister */
-       if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
-               return -ENODEV;
-
-       return 0;
-}
-
-static int drm_fbdev_generic_fb_release(struct fb_info *info, int user)
-{
-       struct drm_fb_helper *fb_helper = info->par;
-
-       if (user)
-               module_put(fb_helper->dev->driver->fops->owner);
-
-       return 0;
-}
-
-FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_generic,
-                                  drm_fb_helper_damage_range,
-                                  drm_fb_helper_damage_area);
-
-static void drm_fbdev_generic_fb_destroy(struct fb_info *info)
-{
-       struct drm_fb_helper *fb_helper = info->par;
-       void *shadow = info->screen_buffer;
-
-       if (!fb_helper->dev)
-               return;
-
-       fb_deferred_io_cleanup(info);
-       drm_fb_helper_fini(fb_helper);
-       vfree(shadow);
-       drm_client_framebuffer_delete(fb_helper->buffer);
-
-       drm_client_release(&fb_helper->client);
-       drm_fb_helper_unprepare(fb_helper);
-       kfree(fb_helper);
-}
-
-static const struct fb_ops drm_fbdev_generic_fb_ops = {
-       .owner          = THIS_MODULE,
-       .fb_open        = drm_fbdev_generic_fb_open,
-       .fb_release     = drm_fbdev_generic_fb_release,
-       FB_DEFAULT_DEFERRED_OPS(drm_fbdev_generic),
-       DRM_FB_HELPER_DEFAULT_OPS,
-       .fb_destroy     = drm_fbdev_generic_fb_destroy,
-};
-
-/*
- * This function uses the client API to create a framebuffer backed by a dumb buffer.
- */
-static int drm_fbdev_generic_helper_fb_probe(struct drm_fb_helper *fb_helper,
-                                            struct drm_fb_helper_surface_size *sizes)
-{
-       struct drm_client_dev *client = &fb_helper->client;
-       struct drm_device *dev = fb_helper->dev;
-       struct drm_client_buffer *buffer;
-       struct fb_info *info;
-       size_t screen_size;
-       void *screen_buffer;
-       u32 format;
-       int ret;
-
-       drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
-                   sizes->surface_width, sizes->surface_height,
-                   sizes->surface_bpp);
-
-       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);
-       if (IS_ERR(buffer))
-               return PTR_ERR(buffer);
-
-       fb_helper->buffer = buffer;
-       fb_helper->fb = buffer->fb;
-
-       screen_size = buffer->gem->size;
-       screen_buffer = vzalloc(screen_size);
-       if (!screen_buffer) {
-               ret = -ENOMEM;
-               goto err_drm_client_framebuffer_delete;
-       }
-
-       info = drm_fb_helper_alloc_info(fb_helper);
-       if (IS_ERR(info)) {
-               ret = PTR_ERR(info);
-               goto err_vfree;
-       }
-
-       drm_fb_helper_fill_info(info, fb_helper, sizes);
-
-       info->fbops = &drm_fbdev_generic_fb_ops;
-
-       /* screen */
-       info->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
-       info->screen_buffer = screen_buffer;
-       info->fix.smem_len = screen_size;
-
-       /* deferred I/O */
-       fb_helper->fbdefio.delay = HZ / 20;
-       fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
-
-       info->fbdefio = &fb_helper->fbdefio;
-       ret = fb_deferred_io_init(info);
-       if (ret)
-               goto err_drm_fb_helper_release_info;
-
-       return 0;
-
-err_drm_fb_helper_release_info:
-       drm_fb_helper_release_info(fb_helper);
-err_vfree:
-       vfree(screen_buffer);
-err_drm_client_framebuffer_delete:
-       fb_helper->fb = NULL;
-       fb_helper->buffer = NULL;
-       drm_client_framebuffer_delete(buffer);
-       return ret;
-}
-
-static void drm_fbdev_generic_damage_blit_real(struct drm_fb_helper *fb_helper,
-                                              struct drm_clip_rect *clip,
-                                              struct iosys_map *dst)
-{
-       struct drm_framebuffer *fb = fb_helper->fb;
-       size_t offset = clip->y1 * fb->pitches[0];
-       size_t len = clip->x2 - clip->x1;
-       unsigned int y;
-       void *src;
-
-       switch (drm_format_info_bpp(fb->format, 0)) {
-       case 1:
-               offset += clip->x1 / 8;
-               len = DIV_ROUND_UP(len + clip->x1 % 8, 8);
-               break;
-       case 2:
-               offset += clip->x1 / 4;
-               len = DIV_ROUND_UP(len + clip->x1 % 4, 4);
-               break;
-       case 4:
-               offset += clip->x1 / 2;
-               len = DIV_ROUND_UP(len + clip->x1 % 2, 2);
-               break;
-       default:
-               offset += clip->x1 * fb->format->cpp[0];
-               len *= fb->format->cpp[0];
-               break;
-       }
-
-       src = fb_helper->info->screen_buffer + offset;
-       iosys_map_incr(dst, offset); /* go to first pixel within clip rect */
-
-       for (y = clip->y1; y < clip->y2; y++) {
-               iosys_map_memcpy_to(dst, 0, src, len);
-               iosys_map_incr(dst, fb->pitches[0]);
-               src += fb->pitches[0];
-       }
-}
-
-static int drm_fbdev_generic_damage_blit(struct drm_fb_helper *fb_helper,
-                                        struct drm_clip_rect *clip)
-{
-       struct drm_client_buffer *buffer = fb_helper->buffer;
-       struct iosys_map map, dst;
-       int ret;
-
-       /*
-        * We have to pin the client buffer to its current location while
-        * flushing the shadow buffer. In the general case, concurrent
-        * modesetting operations could try to move the buffer and would
-        * fail. The modeset has to be serialized by acquiring the reservation
-        * object of the underlying BO here.
-        *
-        * For fbdev emulation, we only have to protect against fbdev modeset
-        * operations. Nothing else will involve the client buffer's BO. So it
-        * is sufficient to acquire struct drm_fb_helper.lock here.
-        */
-       mutex_lock(&fb_helper->lock);
-
-       ret = drm_client_buffer_vmap_local(buffer, &map);
-       if (ret)
-               goto out;
-
-       dst = map;
-       drm_fbdev_generic_damage_blit_real(fb_helper, clip, &dst);
-
-       drm_client_buffer_vunmap_local(buffer);
-
-out:
-       mutex_unlock(&fb_helper->lock);
-
-       return ret;
-}
-
-static int drm_fbdev_generic_helper_fb_dirty(struct drm_fb_helper *helper,
-                                            struct drm_clip_rect *clip)
-{
-       struct drm_device *dev = helper->dev;
-       int ret;
-
-       /* Call damage handlers only if necessary */
-       if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))
-               return 0;
-
-       ret = drm_fbdev_generic_damage_blit(helper, clip);
-       if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret))
-               return ret;
-
-       if (helper->fb->funcs->dirty) {
-               ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
-               if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
-                       return ret;
-       }
-
-       return 0;
-}
-
-static const struct drm_fb_helper_funcs drm_fbdev_generic_helper_funcs = {
-       .fb_probe = drm_fbdev_generic_helper_fb_probe,
-       .fb_dirty = drm_fbdev_generic_helper_fb_dirty,
-};
-
-static void drm_fbdev_generic_client_unregister(struct drm_client_dev *client)
-{
-       struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
-
-       if (fb_helper->info) {
-               drm_fb_helper_unregister_info(fb_helper);
-       } else {
-               drm_client_release(&fb_helper->client);
-               drm_fb_helper_unprepare(fb_helper);
-               kfree(fb_helper);
-       }
-}
-
-static int drm_fbdev_generic_client_restore(struct drm_client_dev *client)
-{
-       drm_fb_helper_lastclose(client->dev);
-
-       return 0;
-}
-
-static int drm_fbdev_generic_client_hotplug(struct drm_client_dev *client)
-{
-       struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
-       struct drm_device *dev = client->dev;
-       int ret;
-
-       if (dev->fb_helper)
-               return drm_fb_helper_hotplug_event(dev->fb_helper);
-
-       ret = drm_fb_helper_init(dev, fb_helper);
-       if (ret)
-               goto err_drm_err;
-
-       if (!drm_drv_uses_atomic_modeset(dev))
-               drm_helper_disable_unused_functions(dev);
-
-       ret = drm_fb_helper_initial_config(fb_helper);
-       if (ret)
-               goto err_drm_fb_helper_fini;
-
-       return 0;
-
-err_drm_fb_helper_fini:
-       drm_fb_helper_fini(fb_helper);
-err_drm_err:
-       drm_err(dev, "fbdev: Failed to setup generic emulation (ret=%d)\n", ret);
-       return ret;
-}
-
-static const struct drm_client_funcs drm_fbdev_generic_client_funcs = {
-       .owner          = THIS_MODULE,
-       .unregister     = drm_fbdev_generic_client_unregister,
-       .restore        = drm_fbdev_generic_client_restore,
-       .hotplug        = drm_fbdev_generic_client_hotplug,
-};
-
-/**
- * drm_fbdev_generic_setup() - Setup generic fbdev emulation
- * @dev: DRM device
- * @preferred_bpp: Preferred bits per pixel for the device.
- *
- * This function sets up generic fbdev emulation for drivers that supports
- * dumb buffers with a virtual address and that can be mmap'ed.
- * drm_fbdev_generic_setup() shall be called after the DRM driver registered
- * the new DRM device with drm_dev_register().
- *
- * Restore, hotplug events and teardown are all taken care of. Drivers that do
- * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
- * Simple drivers might use drm_mode_config_helper_suspend().
- *
- * In order to provide fixed mmap-able memory ranges, generic fbdev emulation
- * uses a shadow buffer in system memory. The implementation blits the shadow
- * fbdev buffer onto the real buffer in regular intervals.
- *
- * This function is safe to call even when there are no connectors present.
- * Setup will be retried on the next hotplug event.
- *
- * The fbdev is destroyed by drm_dev_unregister().
- */
-void drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
-{
-       struct drm_fb_helper *fb_helper;
-       int ret;
-
-       drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
-       drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
-
-       fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
-       if (!fb_helper)
-               return;
-       drm_fb_helper_prepare(dev, fb_helper, preferred_bpp, &drm_fbdev_generic_helper_funcs);
-
-       ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_generic_client_funcs);
-       if (ret) {
-               drm_err(dev, "Failed to register client: %d\n", ret);
-               goto err_drm_client_init;
-       }
-
-       drm_client_register(&fb_helper->client);
-
-       return;
-
-err_drm_client_init:
-       drm_fb_helper_unprepare(fb_helper);
-       kfree(fb_helper);
-       return;
-}
-EXPORT_SYMBOL(drm_fbdev_generic_setup);
diff --git a/drivers/gpu/drm/drm_fbdev_ttm.c b/drivers/gpu/drm/drm_fbdev_ttm.c
new file mode 100644 (file)
index 0000000..bb7898c
--- /dev/null
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: MIT
+
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_print.h>
+
+#include <drm/drm_fbdev_ttm.h>
+
+/* @user: 1=userspace, 0=fbcon */
+static int drm_fbdev_ttm_fb_open(struct fb_info *info, int user)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+
+       /* No need to take a ref for fbcon because it unbinds on unregister */
+       if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
+               return -ENODEV;
+
+       return 0;
+}
+
+static int drm_fbdev_ttm_fb_release(struct fb_info *info, int user)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+
+       if (user)
+               module_put(fb_helper->dev->driver->fops->owner);
+
+       return 0;
+}
+
+FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_ttm,
+                                  drm_fb_helper_damage_range,
+                                  drm_fb_helper_damage_area);
+
+static void drm_fbdev_ttm_fb_destroy(struct fb_info *info)
+{
+       struct drm_fb_helper *fb_helper = info->par;
+       void *shadow = info->screen_buffer;
+
+       if (!fb_helper->dev)
+               return;
+
+       fb_deferred_io_cleanup(info);
+       drm_fb_helper_fini(fb_helper);
+       vfree(shadow);
+       drm_client_framebuffer_delete(fb_helper->buffer);
+
+       drm_client_release(&fb_helper->client);
+       drm_fb_helper_unprepare(fb_helper);
+       kfree(fb_helper);
+}
+
+static const struct fb_ops drm_fbdev_ttm_fb_ops = {
+       .owner          = THIS_MODULE,
+       .fb_open        = drm_fbdev_ttm_fb_open,
+       .fb_release     = drm_fbdev_ttm_fb_release,
+       FB_DEFAULT_DEFERRED_OPS(drm_fbdev_ttm),
+       DRM_FB_HELPER_DEFAULT_OPS,
+       .fb_destroy     = drm_fbdev_ttm_fb_destroy,
+};
+
+/*
+ * This function uses the client API to create a framebuffer backed by a dumb buffer.
+ */
+static int drm_fbdev_ttm_helper_fb_probe(struct drm_fb_helper *fb_helper,
+                                            struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_client_dev *client = &fb_helper->client;
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_client_buffer *buffer;
+       struct fb_info *info;
+       size_t screen_size;
+       void *screen_buffer;
+       u32 format;
+       int ret;
+
+       drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
+                   sizes->surface_width, sizes->surface_height,
+                   sizes->surface_bpp);
+
+       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);
+       if (IS_ERR(buffer))
+               return PTR_ERR(buffer);
+
+       fb_helper->buffer = buffer;
+       fb_helper->fb = buffer->fb;
+
+       screen_size = buffer->gem->size;
+       screen_buffer = vzalloc(screen_size);
+       if (!screen_buffer) {
+               ret = -ENOMEM;
+               goto err_drm_client_framebuffer_delete;
+       }
+
+       info = drm_fb_helper_alloc_info(fb_helper);
+       if (IS_ERR(info)) {
+               ret = PTR_ERR(info);
+               goto err_vfree;
+       }
+
+       drm_fb_helper_fill_info(info, fb_helper, sizes);
+
+       info->fbops = &drm_fbdev_ttm_fb_ops;
+
+       /* screen */
+       info->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
+       info->screen_buffer = screen_buffer;
+       info->fix.smem_len = screen_size;
+
+       /* deferred I/O */
+       fb_helper->fbdefio.delay = HZ / 20;
+       fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
+
+       info->fbdefio = &fb_helper->fbdefio;
+       ret = fb_deferred_io_init(info);
+       if (ret)
+               goto err_drm_fb_helper_release_info;
+
+       return 0;
+
+err_drm_fb_helper_release_info:
+       drm_fb_helper_release_info(fb_helper);
+err_vfree:
+       vfree(screen_buffer);
+err_drm_client_framebuffer_delete:
+       fb_helper->fb = NULL;
+       fb_helper->buffer = NULL;
+       drm_client_framebuffer_delete(buffer);
+       return ret;
+}
+
+static void drm_fbdev_ttm_damage_blit_real(struct drm_fb_helper *fb_helper,
+                                          struct drm_clip_rect *clip,
+                                          struct iosys_map *dst)
+{
+       struct drm_framebuffer *fb = fb_helper->fb;
+       size_t offset = clip->y1 * fb->pitches[0];
+       size_t len = clip->x2 - clip->x1;
+       unsigned int y;
+       void *src;
+
+       switch (drm_format_info_bpp(fb->format, 0)) {
+       case 1:
+               offset += clip->x1 / 8;
+               len = DIV_ROUND_UP(len + clip->x1 % 8, 8);
+               break;
+       case 2:
+               offset += clip->x1 / 4;
+               len = DIV_ROUND_UP(len + clip->x1 % 4, 4);
+               break;
+       case 4:
+               offset += clip->x1 / 2;
+               len = DIV_ROUND_UP(len + clip->x1 % 2, 2);
+               break;
+       default:
+               offset += clip->x1 * fb->format->cpp[0];
+               len *= fb->format->cpp[0];
+               break;
+       }
+
+       src = fb_helper->info->screen_buffer + offset;
+       iosys_map_incr(dst, offset); /* go to first pixel within clip rect */
+
+       for (y = clip->y1; y < clip->y2; y++) {
+               iosys_map_memcpy_to(dst, 0, src, len);
+               iosys_map_incr(dst, fb->pitches[0]);
+               src += fb->pitches[0];
+       }
+}
+
+static int drm_fbdev_ttm_damage_blit(struct drm_fb_helper *fb_helper,
+                                    struct drm_clip_rect *clip)
+{
+       struct drm_client_buffer *buffer = fb_helper->buffer;
+       struct iosys_map map, dst;
+       int ret;
+
+       /*
+        * We have to pin the client buffer to its current location while
+        * flushing the shadow buffer. In the general case, concurrent
+        * modesetting operations could try to move the buffer and would
+        * fail. The modeset has to be serialized by acquiring the reservation
+        * object of the underlying BO here.
+        *
+        * For fbdev emulation, we only have to protect against fbdev modeset
+        * operations. Nothing else will involve the client buffer's BO. So it
+        * is sufficient to acquire struct drm_fb_helper.lock here.
+        */
+       mutex_lock(&fb_helper->lock);
+
+       ret = drm_client_buffer_vmap_local(buffer, &map);
+       if (ret)
+               goto out;
+
+       dst = map;
+       drm_fbdev_ttm_damage_blit_real(fb_helper, clip, &dst);
+
+       drm_client_buffer_vunmap_local(buffer);
+
+out:
+       mutex_unlock(&fb_helper->lock);
+
+       return ret;
+}
+
+static int drm_fbdev_ttm_helper_fb_dirty(struct drm_fb_helper *helper,
+                                        struct drm_clip_rect *clip)
+{
+       struct drm_device *dev = helper->dev;
+       int ret;
+
+       /* Call damage handlers only if necessary */
+       if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))
+               return 0;
+
+       ret = drm_fbdev_ttm_damage_blit(helper, clip);
+       if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret))
+               return ret;
+
+       if (helper->fb->funcs->dirty) {
+               ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
+               if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
+                       return ret;
+       }
+
+       return 0;
+}
+
+static const struct drm_fb_helper_funcs drm_fbdev_ttm_helper_funcs = {
+       .fb_probe = drm_fbdev_ttm_helper_fb_probe,
+       .fb_dirty = drm_fbdev_ttm_helper_fb_dirty,
+};
+
+static void drm_fbdev_ttm_client_unregister(struct drm_client_dev *client)
+{
+       struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+
+       if (fb_helper->info) {
+               drm_fb_helper_unregister_info(fb_helper);
+       } else {
+               drm_client_release(&fb_helper->client);
+               drm_fb_helper_unprepare(fb_helper);
+               kfree(fb_helper);
+       }
+}
+
+static int drm_fbdev_ttm_client_restore(struct drm_client_dev *client)
+{
+       drm_fb_helper_lastclose(client->dev);
+
+       return 0;
+}
+
+static int drm_fbdev_ttm_client_hotplug(struct drm_client_dev *client)
+{
+       struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
+       struct drm_device *dev = client->dev;
+       int ret;
+
+       if (dev->fb_helper)
+               return drm_fb_helper_hotplug_event(dev->fb_helper);
+
+       ret = drm_fb_helper_init(dev, fb_helper);
+       if (ret)
+               goto err_drm_err;
+
+       if (!drm_drv_uses_atomic_modeset(dev))
+               drm_helper_disable_unused_functions(dev);
+
+       ret = drm_fb_helper_initial_config(fb_helper);
+       if (ret)
+               goto err_drm_fb_helper_fini;
+
+       return 0;
+
+err_drm_fb_helper_fini:
+       drm_fb_helper_fini(fb_helper);
+err_drm_err:
+       drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
+       return ret;
+}
+
+static const struct drm_client_funcs drm_fbdev_ttm_client_funcs = {
+       .owner          = THIS_MODULE,
+       .unregister     = drm_fbdev_ttm_client_unregister,
+       .restore        = drm_fbdev_ttm_client_restore,
+       .hotplug        = drm_fbdev_ttm_client_hotplug,
+};
+
+/**
+ * drm_fbdev_ttm_setup() - Setup fbdev emulation for TTM-based drivers
+ * @dev: DRM device
+ * @preferred_bpp: Preferred bits per pixel for the device.
+ *
+ * This function sets up fbdev emulation for TTM-based drivers that support
+ * dumb buffers with a virtual address and that can be mmap'ed.
+ * drm_fbdev_ttm_setup() shall be called after the DRM driver registered
+ * the new DRM device with drm_dev_register().
+ *
+ * Restore, hotplug events and teardown are all taken care of. Drivers that do
+ * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
+ * Simple drivers might use drm_mode_config_helper_suspend().
+ *
+ * In order to provide fixed mmap-able memory ranges, fbdev emulation
+ * uses a shadow buffer in system memory. The implementation blits the shadow
+ * fbdev buffer onto the real buffer in regular intervals.
+ *
+ * This function is safe to call even when there are no connectors present.
+ * Setup will be retried on the next hotplug event.
+ *
+ * The fbdev is destroyed by drm_dev_unregister().
+ */
+void drm_fbdev_ttm_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{
+       struct drm_fb_helper *fb_helper;
+       int ret;
+
+       drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
+       drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
+
+       fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
+       if (!fb_helper)
+               return;
+       drm_fb_helper_prepare(dev, fb_helper, preferred_bpp, &drm_fbdev_ttm_helper_funcs);
+
+       ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_ttm_client_funcs);
+       if (ret) {
+               drm_err(dev, "Failed to register client: %d\n", ret);
+               goto err_drm_client_init;
+       }
+
+       drm_client_register(&fb_helper->client);
+
+       return;
+
+err_drm_client_init:
+       drm_fb_helper_unprepare(fb_helper);
+       kfree(fb_helper);
+       return;
+}
+EXPORT_SYMBOL(drm_fbdev_ttm_setup);
index 57c21ec452b707b26a2f686cda540ca6baecca47..9f9b19ea05879974ea6c8ca4e675b3aef04ab288 100644 (file)
@@ -17,7 +17,7 @@
 #include <drm/drm_aperture.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_vram_helper.h>
 #include <drm/drm_managed.h>
@@ -339,7 +339,7 @@ static int hibmc_pci_probe(struct pci_dev *pdev,
                goto err_unload;
        }
 
-       drm_fbdev_generic_setup(dev, 32);
+       drm_fbdev_ttm_setup(dev, 32);
 
        return 0;
 
index 8e59753e532de29dc49aeb79ef667054c96fbc7f..9ed463a76ae290bc1883608090f26af73e9f32f1 100644 (file)
@@ -6,6 +6,7 @@ config DRM_LOONGSON
        depends on LOONGARCH || MIPS || COMPILE_TEST
        select DRM_KMS_HELPER
        select DRM_TTM
+       select DRM_TTM_HELPER
        select I2C
        select I2C_ALGOBIT
        help
index d8ff60b46abe6e6f46d2b04935c2fc11ced6beb0..adc7344d2f807e96a5e41c5f3cab73480b6dcba3 100644 (file)
@@ -10,7 +10,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_modeset_helper.h>
@@ -314,7 +314,7 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret)
                return ret;
 
-       drm_fbdev_generic_setup(ddev, 32);
+       drm_fbdev_ttm_setup(ddev, 32);
 
        return 0;
 }
index a947e1d5f309ae525e8087d13899f1efd1e8e73b..a58c31089613ebc67076937885e20bee1c54bda8 100644 (file)
@@ -32,7 +32,7 @@
 
 #include <drm/drm_aperture.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_gem_ttm_helper.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_vblank.h>
@@ -846,9 +846,9 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
                goto fail_drm_dev_init;
 
        if (nouveau_drm(drm_dev)->client.device.info.ram_size <= 32 * 1024 * 1024)
-               drm_fbdev_generic_setup(drm_dev, 8);
+               drm_fbdev_ttm_setup(drm_dev, 8);
        else
-               drm_fbdev_generic_setup(drm_dev, 32);
+               drm_fbdev_ttm_setup(drm_dev, 32);
 
        quirk_broken_nv_runpm(pdev);
        return 0;
index beee5563031aa8c01dbacd0ca459336c2b0eb063..5eb3f5719fdf3cedc07f695e448c324cfeaea672 100644 (file)
@@ -37,7 +37,7 @@
 #include <drm/drm_aperture.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem_ttm_helper.h>
 #include <drm/drm_module.h>
@@ -118,7 +118,7 @@ qxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret)
                goto modeset_cleanup;
 
-       drm_fbdev_generic_setup(&qdev->ddev, 32);
+       drm_fbdev_ttm_setup(&qdev->ddev, 32);
        return 0;
 
 modeset_cleanup:
index c23c9f0cf49cb9f226c9dba56b215d9d2e2f519c..2d7ad808cc0e2fd4a24afa51af885af345632bff 100644 (file)
@@ -7,7 +7,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_edid.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -670,7 +670,7 @@ static int bochs_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent
        if (ret)
                goto err_hw_fini;
 
-       drm_fbdev_generic_setup(dev, 32);
+       drm_fbdev_ttm_setup(dev, 32);
        return ret;
 
 err_hw_fini:
index cd9e66a06596a7a834af4917aa9f1f59811827a3..ef36834c8673c93585710adb74a3777392615f0a 100644 (file)
@@ -14,7 +14,7 @@
 #include <drm/drm_aperture.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_file.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_managed.h>
@@ -80,7 +80,7 @@ static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (ret)
                goto err_irq_fini;
 
-       drm_fbdev_generic_setup(&vbox->ddev, 32);
+       drm_fbdev_ttm_setup(&vbox->ddev, 32);
 
        return 0;
 
index bdad93864b980f959831d2779728dae0398079e9..4bf6da2b15fe9088a926016c5a11b8e6733a5747 100644 (file)
@@ -37,7 +37,7 @@
 
 #include <drm/drm_aperture.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fbdev_generic.h>
+#include <drm/drm_fbdev_ttm.h>
 #include <drm/drm_gem_ttm_helper.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_module.h>
@@ -1685,7 +1685,7 @@ static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        vmw_fifo_resource_inc(vmw);
        vmw_svga_enable(vmw);
-       drm_fbdev_generic_setup(&vmw->drm,  0);
+       drm_fbdev_ttm_setup(&vmw->drm,  0);
 
        vmw_debugfs_gem_init(vmw);
        vmw_debugfs_resource_managers_init(vmw);
diff --git a/include/drm/drm_fbdev_generic.h b/include/drm/drm_fbdev_generic.h
deleted file mode 100644 (file)
index 7579934..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-
-#ifndef DRM_FBDEV_GENERIC_H
-#define DRM_FBDEV_GENERIC_H
-
-struct drm_device;
-
-#ifdef CONFIG_DRM_FBDEV_EMULATION
-void drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp);
-#else
-static inline void drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
-{ }
-#endif
-
-#endif
diff --git a/include/drm/drm_fbdev_ttm.h b/include/drm/drm_fbdev_ttm.h
new file mode 100644 (file)
index 0000000..9e6c3bd
--- /dev/null
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DRM_FBDEV_TTM_H
+#define DRM_FBDEV_TTM_H
+
+struct drm_device;
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+void drm_fbdev_ttm_setup(struct drm_device *dev, unsigned int preferred_bpp);
+#else
+static inline void drm_fbdev_ttm_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{ }
+#endif
+
+#endif