From: Thomas Zimmermann <tzimmermann@suse.de>
To: javierm@redhat.com, maarten.lankhorst@linux.intel.com,
mripard@kernel.org, airlied@gmail.com, daniel@ffwll.ch,
linus.walleij@linaro.org
Cc: linux-aspeed@lists.ozlabs.org, dri-devel@lists.freedesktop.org,
linux-stm32@st-md-mailman.stormreply.com,
linux-amlogic@lists.infradead.org, linux-sunxi@lists.linux.dev,
linux-arm-kernel@lists.infradead.org,
Thomas Zimmermann <tzimmermann@suse.de>
Subject: [PATCH v2 01/25] drm/fbdev-dma: Implement fbdev emulation for GEM DMA helpers
Date: Mon, 13 Mar 2023 16:51:14 +0100 [thread overview]
Message-ID: <20230313155138.20584-2-tzimmermann@suse.de> (raw)
In-Reply-To: <20230313155138.20584-1-tzimmermann@suse.de>
Implement fbdev emulation that is optimized for drivers that use
DMA helpers. The buffers may no tbe moveable, may not require damage
handling and have to be located in system memory. This allows fbdev
emulation to operate directly on the buffer and mmap it to userspace.
Besides those constraints, the emulation works like in the generic
code. As an internal DRM client provides, it receives hotplug, restore
and unregister events. The DRM client is independent from the fbdev
probing, which runs on the first successful hotplug event.
The emulation is part of the DMA helper module and not build unless
DMA helpers and fbdev emulation has been configured.
Tested with vc4.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
---
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/drm_fbdev_dma.c | 275 ++++++++++++++++++++++++++++++++
include/drm/drm_fbdev_dma.h | 15 ++
3 files changed, 291 insertions(+)
create mode 100644 drivers/gpu/drm/drm_fbdev_dma.c
create mode 100644 include/drm/drm_fbdev_dma.h
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 1e04d135e866..a33257d2bc7f 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o
obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o
drm_dma_helper-y := drm_gem_dma_helper.o
+drm_dma_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fbdev_dma.o
drm_dma_helper-$(CONFIG_DRM_KMS_HELPER) += drm_fb_dma_helper.o
obj-$(CONFIG_DRM_GEM_DMA_HELPER) += drm_dma_helper.o
diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c
new file mode 100644
index 000000000000..cf553ac12a0f
--- /dev/null
+++ b/drivers/gpu/drm/drm_fbdev_dma.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: MIT
+
+#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_dma_helper.h>
+
+#include <drm/drm_fbdev_dma.h>
+
+/*
+ * struct fb_ops
+ */
+
+static int drm_fbdev_dma_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_dma_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;
+}
+
+static void drm_fbdev_dma_fb_destroy(struct fb_info *info)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+
+ if (!fb_helper->dev)
+ return;
+
+ drm_fb_helper_fini(fb_helper);
+
+ drm_client_buffer_vunmap(fb_helper->buffer);
+ drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_release(&fb_helper->client);
+ drm_fb_helper_unprepare(fb_helper);
+ kfree(fb_helper);
+}
+
+static int drm_fbdev_dma_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_WARN_ON_ONCE(dev, !fb_helper->dev->driver->gem_prime_mmap))
+ return -ENODEV;
+
+ return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
+}
+
+static const struct fb_ops drm_fbdev_dma_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = drm_fbdev_dma_fb_open,
+ .fb_release = drm_fbdev_dma_fb_release,
+ .fb_read = drm_fb_helper_sys_read,
+ .fb_write = drm_fb_helper_sys_write,
+ DRM_FB_HELPER_DEFAULT_OPS,
+ .fb_fillrect = drm_fb_helper_sys_fillrect,
+ .fb_copyarea = drm_fb_helper_sys_copyarea,
+ .fb_imageblit = drm_fb_helper_sys_imageblit,
+ .fb_destroy = drm_fbdev_dma_fb_destroy,
+ .fb_mmap = drm_fbdev_dma_fb_mmap,
+};
+
+/*
+ * struct drm_fb_helper
+ */
+
+static int drm_fbdev_dma_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 drm_gem_dma_object *dma_obj;
+ struct drm_framebuffer *fb;
+ struct fb_info *info;
+ u32 format;
+ struct iosys_map map;
+ 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);
+ dma_obj = to_drm_gem_dma_obj(buffer->gem);
+
+ fb = buffer->fb;
+ if (drm_WARN_ON(dev, fb->funcs->dirty)) {
+ ret = -ENODEV; /* damage handling not supported; use generic emulation */
+ goto err_drm_client_buffer_delete;
+ }
+
+ ret = drm_client_buffer_vmap(buffer, &map);
+ if (ret) {
+ goto err_drm_client_buffer_delete;
+ } else if (drm_WARN_ON(dev, map.is_iomem)) {
+ ret = -ENODEV; /* I/O memory not supported; use generic emulation */
+ goto err_drm_client_buffer_delete;
+ }
+
+ fb_helper->buffer = buffer;
+ fb_helper->fb = buffer->fb;
+
+ info = drm_fb_helper_alloc_info(fb_helper);
+ if (IS_ERR(info)) {
+ ret = PTR_ERR(info);
+ goto err_drm_client_buffer_vunmap;
+ }
+
+ drm_fb_helper_fill_info(info, fb_helper, sizes);
+
+ info->fbops = &drm_fbdev_dma_fb_ops;
+ info->flags = FBINFO_DEFAULT;
+
+ /* screen */
+ info->flags |= FBINFO_VIRTFB; /* system memory */
+ if (dma_obj->map_noncoherent)
+ info->flags |= FBINFO_READS_FAST; /* signal caching */
+ info->screen_size = sizes->surface_height * fb->pitches[0];
+ info->screen_buffer = map.vaddr;
+ info->fix.smem_len = info->screen_size;
+
+#if IS_ENABLED(CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM)
+ /*
+ * Shamelessly leak the physical address to user-space.
+ */
+ if (fb_helper->hint_leak_smem_start && !info->fix.smem_start)
+ info->fix.smem_start = page_to_phys(virt_to_page(info->screen_buffer));
+#endif
+
+ return 0;
+
+err_drm_client_buffer_vunmap:
+ fb_helper->fb = NULL;
+ fb_helper->buffer = NULL;
+ drm_client_buffer_vunmap(buffer);
+err_drm_client_buffer_delete:
+ drm_client_framebuffer_delete(buffer);
+ return ret;
+}
+
+static const struct drm_fb_helper_funcs drm_fbdev_dma_helper_funcs = {
+ .fb_probe = drm_fbdev_dma_helper_fb_probe,
+};
+
+/*
+ * struct drm_client_funcs
+ */
+
+static void drm_fbdev_dma_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_dma_client_restore(struct drm_client_dev *client)
+{
+ drm_fb_helper_lastclose(client->dev);
+
+ return 0;
+}
+
+static int drm_fbdev_dma_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-dma: Failed to setup generic emulation (ret=%d)\n", ret);
+ return ret;
+}
+
+static const struct drm_client_funcs drm_fbdev_dma_client_funcs = {
+ .owner = THIS_MODULE,
+ .unregister = drm_fbdev_dma_client_unregister,
+ .restore = drm_fbdev_dma_client_restore,
+ .hotplug = drm_fbdev_dma_client_hotplug,
+};
+
+/**
+ * drm_fbdev_dma_setup() - Setup fbdev emulation for GEM DMA helpers
+ * @dev: DRM device
+ * @preferred_bpp: Preferred bits per pixel for the device.
+ * @dev->mode_config.preferred_depth is used if this is zero.
+ *
+ * This function sets up fbdev emulation for GEM DMA drivers that support
+ * dumb buffers with a virtual address and that can be mmap'ed.
+ * drm_fbdev_dma_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().
+ *
+ * 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_dma_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_dma_helper_funcs);
+
+ ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_dma_client_funcs);
+ if (ret) {
+ drm_err(dev, "Failed to register client: %d\n", ret);
+ goto err_drm_client_init;
+ }
+
+ ret = drm_fbdev_dma_client_hotplug(&fb_helper->client);
+ if (ret)
+ drm_dbg_kms(dev, "client hotplug ret=%d\n", ret);
+
+ drm_client_register(&fb_helper->client);
+
+ return;
+
+err_drm_client_init:
+ drm_fb_helper_unprepare(fb_helper);
+ kfree(fb_helper);
+}
+EXPORT_SYMBOL(drm_fbdev_dma_setup);
diff --git a/include/drm/drm_fbdev_dma.h b/include/drm/drm_fbdev_dma.h
new file mode 100644
index 000000000000..2da7ee784133
--- /dev/null
+++ b/include/drm/drm_fbdev_dma.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef DRM_FBDEV_DMA_H
+#define DRM_FBDEV_DMA_H
+
+struct drm_device;
+
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+void drm_fbdev_dma_setup(struct drm_device *dev, unsigned int preferred_bpp);
+#else
+static inline void drm_fbdev_dma_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{ }
+#endif
+
+#endif
--
2.39.2
next prev parent reply other threads:[~2023-03-13 15:51 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-03-13 15:51 [PATCH v2 00/25] drm/dma-helper: Add dedicated fbdev emulation Thomas Zimmermann
2023-03-13 15:51 ` Thomas Zimmermann [this message]
2023-03-13 15:51 ` [PATCH v2 02/25] arm/hdlcd: Use GEM DMA " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 03/25] arm/malidp: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 04/25] drm/aspeed: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 05/25] drm/atmel-hlcdc: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 06/25] drm/fsl-dcu: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 07/25] drm/imx/dcss: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 08/25] drm/imx: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 09/25] drm/kmb: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 10/25] drm/logicvc: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 11/25] drm/meson: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 12/25] drm/mxsfb/lcdif: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 13/25] drm/mxsfb: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 14/25] drm/sti: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 15/25] drm/stm: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 16/25] drm/sun4i: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 17/25] drm/tidss: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 18/25] drm/tilcdc: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 19/25] drm/arcpgu: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 20/25] drm/tve200: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 21/25] drm/vc4: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 22/25] drm/xlnx: " Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 23/25] drm/mcde: Do not use dirty GEM FB handling Thomas Zimmermann
2023-03-13 15:51 ` [PATCH v2 24/25] drm/mcde: Use GEM DMA fbdev emulation Thomas Zimmermann
2023-03-13 21:40 ` Linus Walleij
2023-03-13 15:51 ` [PATCH v2 25/25] drm/pl111: " Thomas Zimmermann
2023-03-13 21:41 ` Linus Walleij
2023-03-13 21:37 ` [PATCH v2 00/25] drm/dma-helper: Add dedicated " Linus Walleij
2023-03-14 16:09 ` Thomas Zimmermann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230313155138.20584-2-tzimmermann@suse.de \
--to=tzimmermann@suse.de \
--cc=airlied@gmail.com \
--cc=daniel@ffwll.ch \
--cc=dri-devel@lists.freedesktop.org \
--cc=javierm@redhat.com \
--cc=linus.walleij@linaro.org \
--cc=linux-amlogic@lists.infradead.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-aspeed@lists.ozlabs.org \
--cc=linux-stm32@st-md-mailman.stormreply.com \
--cc=linux-sunxi@lists.linux.dev \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox