Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 00/10] drm: Support simple-framebuffer devices and firmware fbs
This patchset adds support for simple-framebuffer platform devices and a handover mechanism for native drivers to take-over control of the hardware. The new driver, called simpledrm, binds to a simple-frambuffer platform device. The kernel's boot code creates such devices for firmware-provided framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot loader sets up the framebuffers. Description via device tree is also an option. Simpledrm is small enough to be linked into the kernel. The driver's main purpose is to provide graphical output during the early phases of the boot process, before the native DRM drivers are available. Native drivers are typically loaded from an initrd ram disk. Occationally simpledrm can also serve as interim solution on graphics hardware without native DRM driver. So far distributions rely on fbdev drivers, such as efifb, vesafb or simplefb, for early-boot graphical output. However fbdev is deprecated and the drivers do not provide DRM interfaces for modern userspace. Patches 1 and 2 prepare the DRM format helpers for simpledrm. Patches 3 and 4 add a hand-over mechanism. Simpledrm acquires it's framebuffer's I/O-memory range and provides a callback function to be removed by a native driver. The native driver will remove simpledrm before taking over the hardware. The removal is integrated into existing helpers, so drivers use it automatically. Patches 5 to 10 add the simpledrm driver. It's build on simple DRM helpers and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During pageflips, SHMEM buffers are copied into the framebuffer memory, similar to cirrus or mgag200. The code in patches 8 and 9 handles clocks and regulators. It's based on the simplefb drivers, but has been modified for DRM. I've also been working on fastboot support (i.e., flicker-free booting). This requires state-readout from simpledrm via generic interfaces, as outlined in [1]. I do have some prototype code, but it will take a while to get this ready. Simpledrm will then support it. I've tested simpledrm with x86 EFI and VESA framebuffers, which both work reliably. The fbdev console and Weston work automatically. Xorg requires manual configuration of the device. Xorgs current modesetting driver does not work with both, platform and PCI device, for the same physical hardware. Once configured, X11 works. I looked into X11, but couldn't see an easy way of fixing the problem. With the push towards Wayland+Xwayland I expect the problem to become a non-issue soon. Additional testing has been reported at [2]. One cosmetical issue is that simpledrm's device file is card0 and the native driver's device file is card1. After simpledrm has been kicked out, only card1 is left. This does not seem to be a practical problem however. TODO/IDEAS: * provide deferred takeover * provide bootsplash DRM client * make simplekms usable with ARM-EFI fbs v2: * rename to simpledrm, aperture helpers * reorganized patches * use hotplug helpers for removal (Daniel) * added DT match tables (Rob) * use shadow-plane helpers * lots of minor cleanups [1] https://lore.kernel.org/dri-devel/CAKMK7uHtqHy_oz4W7F+hmp9iqp7W5Ra8CxPvJ=9BwmvfU-O0gg at mail.gmail.com/ [2] https://lore.kernel.org/dri-devel/1761762.3HQLrFs1K7 at nerdopolis/ Thomas Zimmermann (10): drm/format-helper: Pass destination pitch to drm_fb_memcpy_dstclip() drm/format-helper: Add blitter functions drm/aperture: Move fbdev conflict helpers into drm_aperture.h drm/aperture: Add infrastructure for aperture ownership drm: Add simpledrm driver drm/simpledrm: Add fbdev emulation drm/simpledrm: Initialize framebuffer data from device-tree node drm/simpledrm: Acquire clocks from DT device node drm/simpledrm: Acquire regulators from DT device node drm/simpledrm: Acquire memory aperture for framebuffer Documentation/gpu/drm-internals.rst | 12 + MAINTAINERS | 7 + drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_aperture.c | 287 ++++++++ drivers/gpu/drm/drm_format_helper.c | 96 ++- drivers/gpu/drm/mgag200/mgag200_mode.c | 2 +- drivers/gpu/drm/tiny/Kconfig | 17 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/cirrus.c | 2 +- drivers/gpu/drm/tiny/simpledrm.c | 932 +++++++++++++++++++++++++ include/drm/drm_aperture.h | 96 +++ include/drm/drm_fb_helper.h | 56 +- include/drm/drm_format_helper.h | 10 +- 14 files changed, 1466 insertions(+), 60 deletions(-) create mode 100644 drivers/gpu/drm/drm_aperture.c create mode 100644 drivers/gpu/drm/tiny/simpledrm.c create mode 100644 include/drm/drm_aperture.h -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 01/10] drm/format-helper: Pass destination pitch to drm_fb_memcpy_dstclip()
The memcpy's destination buffer might have a different pitch than the source. Support different pitches as function argument. Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/drm_format_helper.c | 9 +++++---- drivers/gpu/drm/mgag200/mgag200_mode.c | 2 +- drivers/gpu/drm/tiny/cirrus.c | 2 +- include/drm/drm_format_helper.h | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index c043ca364c86..8d5a683afea7 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -52,6 +52,7 @@ EXPORT_SYMBOL(drm_fb_memcpy); /** * drm_fb_memcpy_dstclip - Copy clip buffer * @dst: Destination buffer (iomem) + * @dst_pitch: Number of bytes between two consecutive scanlines within dst * @vaddr: Source buffer * @fb: DRM framebuffer * @clip: Clip rectangle area to copy @@ -59,12 +60,12 @@ EXPORT_SYMBOL(drm_fb_memcpy); * This function applies clipping on dst, i.e. the destination is a * full (iomem) framebuffer but only the clip rect content is copied over. */ -void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, - struct drm_framebuffer *fb, +void drm_fb_memcpy_dstclip(void __iomem *dst, unsigned int dst_pitch, + void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip) { unsigned int cpp = fb->format->cpp[0]; - unsigned int offset = clip_offset(clip, fb->pitches[0], cpp); + unsigned int offset = clip_offset(clip, dst_pitch, cpp); size_t len = (clip->x2 - clip->x1) * cpp; unsigned int y, lines = clip->y2 - clip->y1; @@ -73,7 +74,7 @@ void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, for (y = 0; y < lines; y++) { memcpy_toio(dst, vaddr, len); vaddr += fb->pitches[0]; - dst += fb->pitches[0]; + dst += dst_pitch; } } EXPORT_SYMBOL(drm_fb_memcpy_dstclip); diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index cece3e57fb27..9d576240faed 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1554,7 +1554,7 @@ mgag200_handle_damage(struct mga_device *mdev, struct drm_framebuffer *fb, { void *vmap = map->vaddr; /* TODO: Use mapping abstraction properly */ - drm_fb_memcpy_dstclip(mdev->vram, vmap, fb, clip); + drm_fb_memcpy_dstclip(mdev->vram, fb->pitches[0], vmap, fb, clip); /* Always scanout image at VRAM offset 0 */ mgag200_set_startadd(mdev, (u32)0); diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index ad922c3ec681..ae5643b0a6f4 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -323,7 +323,7 @@ static int cirrus_fb_blit_rect(struct drm_framebuffer *fb, const struct dma_buf_ return -ENODEV; if (cirrus->cpp == fb->format->cpp[0]) - drm_fb_memcpy_dstclip(cirrus->vram, + drm_fb_memcpy_dstclip(cirrus->vram, fb->pitches[0], vmap, fb, rect); else if (fb->format->cpp[0] == 4 && cirrus->cpp == 2) diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h index 5f9e37032468..2b5036a5fbe7 100644 --- a/include/drm/drm_format_helper.h +++ b/include/drm/drm_format_helper.h @@ -11,7 +11,7 @@ struct drm_rect; void drm_fb_memcpy(void *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip); -void drm_fb_memcpy_dstclip(void __iomem *dst, void *vaddr, +void drm_fb_memcpy_dstclip(void __iomem *dst, unsigned int dst_pitch, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip); void drm_fb_swab(void *dst, void *src, struct drm_framebuffer *fb, -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 02/10] drm/format-helper: Add blitter functions
The blitter functions copy a framebuffer to I/O memory using one of the existing conversion functions. Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/drm_format_helper.c | 87 +++++++++++++++++++++++++++++ include/drm/drm_format_helper.h | 8 +++ 2 files changed, 95 insertions(+) diff --git a/drivers/gpu/drm/drm_format_helper.c b/drivers/gpu/drm/drm_format_helper.c index 8d5a683afea7..0e885cd34107 100644 --- a/drivers/gpu/drm/drm_format_helper.c +++ b/drivers/gpu/drm/drm_format_helper.c @@ -344,3 +344,90 @@ void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, } EXPORT_SYMBOL(drm_fb_xrgb8888_to_gray8); +/** + * drm_fb_blit_rect_dstclip - Copy parts of a framebuffer to display memory + * @dst: The display memory to copy to + * @dst_pitch: Number of bytes between two consecutive scanlines within dst + * @dst_format: FOURCC code of the display's color format + * @vmap: The framebuffer memory to copy from + * @fb: The framebuffer to copy from + * @clip: Clip rectangle area to copy + * + * This function copies parts of a framebuffer to display memory. If the + * formats of the display and the framebuffer mismatch, the blit function + * will attempt to convert between them. + * + * Use drm_fb_blit_dstclip() to copy the full framebuffer. + * + * Returns: + * 0 on success, or + * -EINVAL if the color-format conversion failed, or + * a negative error code otherwise. + */ +int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch, + uint32_t dst_format, void *vmap, + struct drm_framebuffer *fb, + struct drm_rect *clip) +{ + uint32_t fb_format = fb->format->format; + + /* treat alpha channel like filler bits */ + if (fb_format == DRM_FORMAT_ARGB8888) + fb_format = DRM_FORMAT_XRGB8888; + if (dst_format == DRM_FORMAT_ARGB8888) + dst_format = DRM_FORMAT_XRGB8888; + + if (dst_format == fb_format) { + drm_fb_memcpy_dstclip(dst, dst_pitch, vmap, fb, clip); + return 0; + + } else if (dst_format == DRM_FORMAT_RGB565) { + if (fb_format == DRM_FORMAT_XRGB8888) { + drm_fb_xrgb8888_to_rgb565_dstclip(dst, dst_pitch, + vmap, fb, clip, + false); + return 0; + } + } else if (dst_format == DRM_FORMAT_RGB888) { + if (fb_format == DRM_FORMAT_XRGB8888) { + drm_fb_xrgb8888_to_rgb888_dstclip(dst, dst_pitch, + vmap, fb, clip); + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_fb_blit_rect_dstclip); + +/** + * drm_fb_blit_dstclip - Copy framebuffer to display memory + * @dst: The display memory to copy to + * @dst_pitch: Number of bytes between two consecutive scanlines within dst + * @dst_format: FOURCC code of the display's color format + * @vmap: The framebuffer memory to copy from + * @fb: The framebuffer to copy from + * + * This function copies a full framebuffer to display memory. If the formats + * of the display and the framebuffer mismatch, the copy function will + * attempt to convert between them. + * + * See drm_fb_blit_rect_dstclip() for more inforamtion. + * + * Returns: + * 0 on success, or a negative error code otherwise. + */ +int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch, + uint32_t dst_format, void *vmap, + struct drm_framebuffer *fb) +{ + struct drm_rect fullscreen = { + .x1 = 0, + .x2 = fb->width, + .y1 = 0, + .y2 = fb->height, + }; + return drm_fb_blit_rect_dstclip(dst, dst_pitch, dst_format, vmap, fb, + &fullscreen); +} +EXPORT_SYMBOL(drm_fb_blit_dstclip); diff --git a/include/drm/drm_format_helper.h b/include/drm/drm_format_helper.h index 2b5036a5fbe7..4e0258a61311 100644 --- a/include/drm/drm_format_helper.h +++ b/include/drm/drm_format_helper.h @@ -28,4 +28,12 @@ void drm_fb_xrgb8888_to_rgb888_dstclip(void __iomem *dst, unsigned int dst_pitch void drm_fb_xrgb8888_to_gray8(u8 *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_rect *clip); +int drm_fb_blit_rect_dstclip(void __iomem *dst, unsigned int dst_pitch, + uint32_t dst_format, void *vmap, + struct drm_framebuffer *fb, + struct drm_rect *rect); +int drm_fb_blit_dstclip(void __iomem *dst, unsigned int dst_pitch, + uint32_t dst_format, void *vmap, + struct drm_framebuffer *fb); + #endif /* __LINUX_DRM_FORMAT_HELPER_H */ -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 03/10] drm/aperture: Move fbdev conflict helpers into drm_aperture.h
Fbdev's helpers for handling conflicting framebuffers are related to framebuffer apertures, not console emulation. Therefore move them into a drm_aperture.h, which will contain the interfaces for the new aperture helpers. Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- Documentation/gpu/drm-internals.rst | 6 +++ include/drm/drm_aperture.h | 60 +++++++++++++++++++++++++++++ include/drm/drm_fb_helper.h | 56 ++------------------------- 3 files changed, 69 insertions(+), 53 deletions(-) create mode 100644 include/drm/drm_aperture.h diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index 12272b168580..4c7642d2ca34 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -75,6 +75,12 @@ update it, its value is mostly useless. The DRM core prints it to the kernel log at initialization time and passes it to userspace through the DRM_IOCTL_VERSION ioctl. +Managing Ownership of the Framebuffer Aperture +---------------------------------------------- + +.. kernel-doc:: include/drm/drm_aperture.h + :internal: + Device Instance and Driver Handling ----------------------------------- diff --git a/include/drm/drm_aperture.h b/include/drm/drm_aperture.h new file mode 100644 index 000000000000..13766efe9517 --- /dev/null +++ b/include/drm/drm_aperture.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef _DRM_APERTURE_H_ +#define _DRM_APERTURE_H_ + +#include <linux/fb.h> +#include <linux/vgaarb.h> + +/** + * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers + * @a: memory range, users of which are to be removed + * @name: requesting driver name + * @primary: also kick vga16fb if present + * + * This function removes framebuffer devices (initialized by firmware/bootloader) + * which use memory range described by @a. If @a is NULL all such devices are + * removed. + */ +static inline int +drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, + const char *name, bool primary) +{ +#if IS_REACHABLE(CONFIG_FB) + return remove_conflicting_framebuffers(a, name, primary); +#else + return 0; +#endif +} + +/** + * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured + * framebuffers for PCI devices + * @pdev: PCI device + * @name: requesting driver name + * + * This function removes framebuffer devices (eg. initialized by firmware) + * using memory range configured for any of @pdev's memory bars. + * + * The function assumes that PCI device with shadowed ROM drives a primary + * display and so kicks out vga16fb. + */ +static inline int +drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, + const char *name) +{ + int ret = 0; + + /* + * WARNING: Apparently we must kick fbdev drivers before vgacon, + * otherwise the vga fbdev driver falls over. + */ +#if IS_REACHABLE(CONFIG_FB) + ret = remove_conflicting_pci_framebuffers(pdev, name); +#endif + if (ret == 0) + ret = vga_remove_vgacon(pdev); + return ret; +} + +#endif diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 3b273f9ca39a..d06a3942fddb 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -30,13 +30,13 @@ #ifndef DRM_FB_HELPER_H #define DRM_FB_HELPER_H -struct drm_fb_helper; - +#include <drm/drm_aperture.h> #include <drm/drm_client.h> #include <drm/drm_crtc.h> #include <drm/drm_device.h> #include <linux/kgdb.h> -#include <linux/vgaarb.h> + +struct drm_fb_helper; enum mode_set_atomic { LEAVE_ATOMIC_MODE_SET, @@ -451,54 +451,4 @@ drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) #endif -/** - * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers - * @a: memory range, users of which are to be removed - * @name: requesting driver name - * @primary: also kick vga16fb if present - * - * This function removes framebuffer devices (initialized by firmware/bootloader) - * which use memory range described by @a. If @a is NULL all such devices are - * removed. - */ -static inline int -drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, - const char *name, bool primary) -{ -#if IS_REACHABLE(CONFIG_FB) - return remove_conflicting_framebuffers(a, name, primary); -#else - return 0; -#endif -} - -/** - * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices - * @pdev: PCI device - * @name: requesting driver name - * - * This function removes framebuffer devices (eg. initialized by firmware) - * using memory range configured for any of @pdev's memory bars. - * - * The function assumes that PCI device with shadowed ROM drives a primary - * display and so kicks out vga16fb. - */ -static inline int -drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, - const char *name) -{ - int ret = 0; - - /* - * WARNING: Apparently we must kick fbdev drivers before vgacon, - * otherwise the vga fbdev driver falls over. - */ -#if IS_REACHABLE(CONFIG_FB) - ret = remove_conflicting_pci_framebuffers(pdev, name); -#endif - if (ret == 0) - ret = vga_remove_vgacon(pdev); - return ret; -} - #endif -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 04/10] drm/aperture: Add infrastructure for aperture ownership
Platform devices might operate on firmware framebuffers, such as VESA or EFI. Before a native driver for the graphics hardware can take over the device, it has to remove any platform driver that operates on the firmware framebuffer. Aperture helpers provide the infrastructure for platform drivers to acquire firmware framebuffers, and for native drivers to remove them later on. It works similar to the related fbdev mechanism. During initialization, the platform driver acquires the firmware framebuffer's I/O memory and provides a callback to be removed. The native driver later uses this information to remove any platform driver for it's framebuffer I/O memory. The aperture removal code is integrated into the existing code for removing conflicting framebuffers, so native drivers use it automatically. v2: * rename plaform helpers to aperture helpers * tie to device lifetime with devm_ functions * removed unsued remove() callback * rename kickout to detach * make struct drm_aperture private * rebase onto existing drm_aperture.h header file * use MIT license only for simplicity * documentation Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- Documentation/gpu/drm-internals.rst | 6 + drivers/gpu/drm/Kconfig | 7 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_aperture.c | 287 ++++++++++++++++++++++++++++ include/drm/drm_aperture.h | 38 +++- 5 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/drm_aperture.c diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index 4c7642d2ca34..06af044c882f 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -78,9 +78,15 @@ DRM_IOCTL_VERSION ioctl. Managing Ownership of the Framebuffer Aperture ---------------------------------------------- +.. kernel-doc:: drivers/gpu/drm/drm_aperture.c + :doc: overview + .. kernel-doc:: include/drm/drm_aperture.h :internal: +.. kernel-doc:: drivers/gpu/drm/drm_aperture.c + :export: + Device Instance and Driver Handling ----------------------------------- diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 1461652921be..b9d3fb91d22d 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -221,6 +221,13 @@ config DRM_SCHED tristate depends on DRM +config DRM_APERTURE + bool + depends on DRM + help + Controls ownership of graphics apertures. Required to + synchronize with firmware-based drivers. + source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/arm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 5eb5bf7c16e3..c9ecb02df0f3 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -32,6 +32,7 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o drm-$(CONFIG_PCI) += drm_pci.o drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o +drm-$(CONFIG_DRM_APERTURE) += drm_aperture.o drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o diff --git a/drivers/gpu/drm/drm_aperture.c b/drivers/gpu/drm/drm_aperture.c new file mode 100644 index 000000000000..4b02b5fed0a1 --- /dev/null +++ b/drivers/gpu/drm/drm_aperture.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: MIT + +#include <linux/device.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <drm/drm_aperture.h> +#include <drm/drm_drv.h> +#include <drm/drm_print.h> + +/** + * DOC: overview + * + * A graphics device might be supported by different drivers, but only one + * driver can be active at any given time. Many systems load a generic + * graphics drivers, such as EFI-GOP or VESA, early during the boot process. + * During later boot stages, they replace the generic driver with a dedicated, + * hardware-specific driver. To take over the device the dedicated driver + * first has to remove the generic driver. DRM aperture functions manage + * ownership of DRM framebuffer memory and hand-over between drivers. + * + * DRM drivers should call drm_fb_helper_remove_conflicting_framebuffers() + * at the top of their probe function. The function removes any generic + * driver that is currently associated with the given framebuffer memory. + * If the framebuffer is located at PCI BAR 0, the rsp code looks as in the + * example given below. + * + * .. code-block:: c + * + * static int remove_conflicting_framebuffers(struct pci_dev *pdev) + * { + * struct apertures_struct *ap; + * bool primary = false; + * int ret; + * + * ap = alloc_apertures(1); + * if (!ap) + * return -ENOMEM; + * + * ap->ranges[0].base = pci_resource_start(pdev, 0); + * ap->ranges[0].size = pci_resource_len(pdev, 0); + * + * #ifdef CONFIG_X86 + * primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; + * #endif + * ret = drm_fb_helper_remove_conflicting_framebuffers(ap, "example driver", primary); + * kfree(ap); + * + * return ret; + * } + * + * static int probe(struct pci_dev *pdev) + * { + * int ret; + * + * // Remove any generic drivers... + * ret = remove_conflicting_framebuffers(pdev); + * if (ret) + * return ret; + * + * // ... and initialize the hardware. + * ... + * + * drm_dev_register(); + * + * return 0; + * } + * + * For PCI devices it is often sufficient to use drm_fb_helper_remove_conflicting_pci_framebuffers() + * and let it detect the framebuffer apertures automatically. + * + * .. code-block:: c + * + * static int probe(struct pci_dev *pdev) + * { + * int ret; + * + * // Remove any generic drivers... + * ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "example driver"); + * if (ret) + * return ret; + * + * // ... and initialize the hardware. + * ... + * + * drm_dev_register(); + * + * return 0; + * } + * + * Drivers that are susceptible to being removed be other drivers, such as + * generic EFI or VESA drivers, have to register themselves as owners of their + * given framebuffer memory. Ownership of the framebuffer memory is achived + * by calling devm_aperture_acquire(). On success, the driver is the owner + * of the framebuffer range. The function fails if the framebuffer is already + * by another driver. See below for an example. + * + * .. code-block:: c + * + * static struct drm_aperture_funcs ap_funcs = { + * .detach = ... + * }; + * + * static int acquire_framebuffers(struct drm_device *dev, struct pci_dev *pdev) + * { + * resource_size_t start, len; + * struct drm_aperture *ap; + * + * base = pci_resource_start(pdev, 0); + * size = pci_resource_len(pdev, 0); + * + * ap = devm_acquire_aperture(dev, base, size, &ap_funcs); + * if (IS_ERR(ap)) + * return PTR_ERR(ap); + * + * return 0; + * } + * + * static int probe(struct pci_dev *pdev) + * { + * struct drm_device *dev; + * int ret; + * + * // ... Initialize the device... + * dev = devm_drm_dev_alloc(); + * ... + * + * // ... and acquire ownership of the framebuffer. + * ret = acquire_framebuffers(dev, pdev); + * if (ret) + * return ret; + * + * drm_dev_register(); + * + * return 0; + * } + * + * The generic driver is now subject to forced removal by other drivers. This + * is when the detach function in struct &drm_aperture_funcs comes into play. + * When a driver calls drm_fb_helper_remove_conflicting_framebuffers() et al + * for the registered framebuffer range, the DRM core calls struct + * &drm_aperture_funcs.detach and the generic driver has to onload itself. It + * may not access the device's registers, framebuffer memory, ROM, etc after + * detach returned. If the driver supports hotplugging, detach can be treated + * like an unplug event. + * + * .. code-block:: c + * + * static void detach_from_device(struct drm_device *dev, + * resource_size_t base, + * resource_size_t size) + * { + * // Signal unplug + * drm_dev_unplug(dev); + * + * // Maybe do other clean-up operations + * ... + * } + * + * static struct drm_aperture_funcs ap_funcs = { + * .detach = detach_from_device, + * }; + */ + +/** + * struct drm_aperture - Represents a DRM framebuffer aperture + * + * This structure has no public fields. + */ +struct drm_aperture { + struct drm_device *dev; + resource_size_t base; + resource_size_t size; + + const struct drm_aperture_funcs *funcs; + + struct list_head lh; +}; + +static LIST_HEAD(drm_apertures); + +static DEFINE_MUTEX(drm_apertures_lock); + +static bool overlap(resource_size_t base1, resource_size_t end1, + resource_size_t base2, resource_size_t end2) +{ + return (base1 < end2) && (end1 > base2); +} + +static void devm_aperture_acquire_release(void *data) +{ + struct drm_aperture *ap = data; + bool detached = !ap->dev; + + if (!detached) + mutex_lock(&drm_apertures_lock); + + list_del(&ap->lh); + + if (!detached) + mutex_unlock(&drm_apertures_lock); +} + +/** + * devm_aperture_acquire - Acquires ownership of a framebuffer on behalf of a DRM driver. + * @dev: the DRM device to own the framebuffer memory + * @base: the framebuffer's byte offset in physical memory + * @size: the framebuffer size in bytes + * @funcs: callback functions + * + * Installs the given device as the new owner. The function fails if the + * framebuffer range, or parts of it, is currently owned by another driver. + * To evict current owners, callers should use + * drm_fb_helper_remove_conflicting_framebuffers() et al. before calling this + * function. Acquired apertures are released automatically if the underlying + * device goes away. + * + * Returns: + * An instance of struct &drm_aperture on success, or a pointer-encoded + * errno value otherwise. + */ +struct drm_aperture * +devm_aperture_acquire(struct drm_device *dev, + resource_size_t base, resource_size_t size, + const struct drm_aperture_funcs *funcs) +{ + size_t end = base + size; + struct list_head *pos; + struct drm_aperture *ap; + int ret; + + mutex_lock(&drm_apertures_lock); + + list_for_each(pos, &drm_apertures) { + ap = container_of(pos, struct drm_aperture, lh); + if (overlap(base, end, ap->base, ap->base + ap->size)) + return ERR_PTR(-EBUSY); + } + + ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL); + if (!ap) + return ERR_PTR(-ENOMEM); + + ap->dev = dev; + ap->base = base; + ap->size = size; + ap->funcs = funcs; + INIT_LIST_HEAD(&ap->lh); + + list_add(&ap->lh, &drm_apertures); + + mutex_unlock(&drm_apertures_lock); + + ret = devm_add_action_or_reset(dev->dev, devm_aperture_acquire_release, ap); + if (ret) + return ERR_PTR(ret); + + return ap; +} +EXPORT_SYMBOL(devm_aperture_acquire); + +void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size) +{ + resource_size_t end = base + size; + struct list_head *pos, *n; + + mutex_lock(&drm_apertures_lock); + + list_for_each_safe(pos, n, &drm_apertures) { + struct drm_aperture *ap + container_of(pos, struct drm_aperture, lh); + struct drm_device *dev = ap->dev; + + if (!overlap(base, end, ap->base, ap->base + ap->size)) + continue; + + ap->dev = NULL; /* detach from device */ + if (drm_WARN_ON(dev, !ap->funcs->detach)) + continue; + ap->funcs->detach(dev, ap->base, ap->size); + } + + mutex_unlock(&drm_apertures_lock); +} +EXPORT_SYMBOL(drm_aperture_detach_drivers); diff --git a/include/drm/drm_aperture.h b/include/drm/drm_aperture.h index 13766efe9517..696cec75ef78 100644 --- a/include/drm/drm_aperture.h +++ b/include/drm/drm_aperture.h @@ -4,8 +4,30 @@ #define _DRM_APERTURE_H_ #include <linux/fb.h> +#include <linux/pci.h> #include <linux/vgaarb.h> +struct drm_aperture; +struct drm_device; + +struct drm_aperture_funcs { + void (*detach)(struct drm_device *dev, resource_size_t base, resource_size_t size); +}; + +struct drm_aperture * +devm_aperture_acquire(struct drm_device *dev, + resource_size_t base, resource_size_t size, + const struct drm_aperture_funcs *funcs); + +#if defined(CONFIG_DRM_APERTURE) +void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size); +#else +static inline void +drm_aperture_detach_drivers(resource_size_t base, resource_size_t size) +{ +} +#endif + /** * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers * @a: memory range, users of which are to be removed @@ -20,6 +42,11 @@ static inline int drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, const char *name, bool primary) { + int i; + + for (i = 0; i < a->count; ++i) + drm_aperture_detach_drivers(a->ranges[i].base, a->ranges[i].size); + #if IS_REACHABLE(CONFIG_FB) return remove_conflicting_framebuffers(a, name, primary); #else @@ -43,7 +70,16 @@ static inline int drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, const char *name) { - int ret = 0; + resource_size_t base, size; + int bar, ret = 0; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) + continue; + base = pci_resource_start(pdev, bar); + size = pci_resource_len(pdev, bar); + drm_aperture_detach_drivers(base, size); + } /* * WARNING: Apparently we must kick fbdev drivers before vgacon, -- 2.30.1
The simpledrm driver is a DRM driver for simplefb framebuffers as provided by the kernel's boot code. This driver enables basic graphical output on many different graphics devices that are provided by the platform (e.g., EFI, VESA, embedded framebuffers). With the kernel's simplefb infrastructure, the kernel receives a pre-configured framebuffer from the system (i.e., firmware, boot loader). It creates a platform device to which simpledrm attaches. The system's framebuffer consists of a memory range, size and format. Based on these values, simpledrm creates a DRM devices. No actual modesetting is possible. v2: * rename driver to simpledrm * add dri-devel to MAINTAINERS entry * put native format first in primary-plane format list (Daniel) * inline simplekms_device_cleanup() (Daniel) * use helpers for shadow-buffered planes * fix whitespace errors Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- MAINTAINERS | 7 + drivers/gpu/drm/tiny/Kconfig | 16 + drivers/gpu/drm/tiny/Makefile | 1 + drivers/gpu/drm/tiny/simpledrm.c | 527 +++++++++++++++++++++++++++++++ 4 files changed, 551 insertions(+) create mode 100644 drivers/gpu/drm/tiny/simpledrm.c diff --git a/MAINTAINERS b/MAINTAINERS index 3dc7b57be31d..e9d53daf8b58 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5744,6 +5744,13 @@ S: Orphan / Obsolete F: drivers/gpu/drm/savage/ F: include/uapi/drm/savage_drm.h +DRM DRIVER FOR SIMPLE FRAMEBUFFERS +M: Thomas Zimmermann <tzimmermann at suse.de> +L: dri-devel at lists.freedesktop.org +S: Maintained +T: git git://anongit.freedesktop.org/drm/drm-misc +F: drivers/gpu/drm/tiny/simplekms.c + DRM DRIVER FOR SIS VIDEO CARDS S: Orphan / Obsolete F: drivers/gpu/drm/sis/ diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index 9bbaa1a69050..d46f95d9196d 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -38,6 +38,22 @@ config DRM_GM12U320 This is a KMS driver for projectors which use the GM12U320 chipset for video transfer over USB2/3, such as the Acer C120 mini projector. +config DRM_SIMPLEDRM + tristate "Simple framebuffer driver" + depends on DRM + select DRM_GEM_SHMEM_HELPER + select DRM_KMS_HELPER + help + DRM driver for simple platform-provided framebuffers. + + This driver assumes that the display hardware has been initialized + by the firmware or bootloader before the kernel boots. Scanout + buffer, size, and display format must be provided via device tree, + UEFI, VESA, etc. + + On x86 and compatible, you should also select CONFIG_X86_SYSFB to + use UEFI and VESA framebuffers. + config TINYDRM_HX8357D tristate "DRM support for HX8357D display panels" depends on DRM && SPI diff --git a/drivers/gpu/drm/tiny/Makefile b/drivers/gpu/drm/tiny/Makefile index bef6780bdd6f..9cc847e756da 100644 --- a/drivers/gpu/drm/tiny/Makefile +++ b/drivers/gpu/drm/tiny/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_ARCPGU) += arcpgu.o obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus.o obj-$(CONFIG_DRM_GM12U320) += gm12u320.o +obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm.o obj-$(CONFIG_TINYDRM_HX8357D) += hx8357d.o obj-$(CONFIG_TINYDRM_ILI9225) += ili9225.o obj-$(CONFIG_TINYDRM_ILI9341) += ili9341.o diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c new file mode 100644 index 000000000000..0422c549b97a --- /dev/null +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -0,0 +1,527 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/platform_data/simplefb.h> +#include <linux/platform_device.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_format_helper.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_gem_shmem_helper.h> +#include <drm/drm_managed.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + +#define DRIVER_NAME "simpledrm" +#define DRIVER_DESC "DRM driver for simple-framebuffer platform devices" +#define DRIVER_DATE "20200625" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +/* + * Assume a monitor resolution of 96 dpi to + * get a somewhat reasonable screen size. + */ +#define RES_MM(d) \ + (((d) * 254ul) / (96ul * 10ul)) + +#define SIMPLEDRM_MODE(hd, vd) \ + DRM_SIMPLE_MODE(hd, vd, RES_MM(hd), RES_MM(vd)) + +/* + * Helpers for simplefb + */ + +static int +simplefb_get_validated_int(struct drm_device *dev, const char *name, + uint32_t value) +{ + if (value > INT_MAX) { + drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", + name, value); + return -EINVAL; + } + return (int)value; +} + +static int +simplefb_get_validated_int0(struct drm_device *dev, const char *name, + uint32_t value) +{ + if (!value) { + drm_err(dev, "simplefb: invalid framebuffer %s of %u\n", + name, value); + return -EINVAL; + } + return simplefb_get_validated_int(dev, name, value); +} + +static const struct drm_format_info * +simplefb_get_validated_format(struct drm_device *dev, const char *format_name) +{ + static const struct simplefb_format formats[] = SIMPLEFB_FORMATS; + const struct simplefb_format *fmt = formats; + const struct simplefb_format *end = fmt + ARRAY_SIZE(formats); + + if (!format_name) { + drm_err(dev, "simplefb: missing framebuffer format\n"); + return ERR_PTR(-EINVAL); + } + + while (fmt < end) { + if (!strcmp(format_name, fmt->name)) + return drm_format_info(fmt->fourcc); + ++fmt; + } + + drm_err(dev, "simplefb: unknown framebuffer format %s\n", + format_name); + + return ERR_PTR(-EINVAL); +} + +static int +simplefb_get_width_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int0(dev, "width", pd->width); +} + +static int +simplefb_get_height_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int0(dev, "height", pd->height); +} + +static int +simplefb_get_stride_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_int(dev, "stride", pd->stride); +} + +static const struct drm_format_info * +simplefb_get_format_pd(struct drm_device *dev, + const struct simplefb_platform_data *pd) +{ + return simplefb_get_validated_format(dev, pd->format); +} + +/* + * Simple Framebuffer device + */ + +struct simpledrm_device { + struct drm_device dev; + struct platform_device *pdev; + + /* simplefb settings */ + struct drm_display_mode mode; + const struct drm_format_info *format; + unsigned int pitch; + + /* memory management */ + struct resource *mem; + void __iomem *screen_base; + + /* modesetting */ + uint32_t formats[8]; + size_t nformats; + struct drm_connector connector; + struct drm_simple_display_pipe pipe; +}; + +static struct simpledrm_device *simpledrm_device_of_dev(struct drm_device *dev) +{ + return container_of(dev, struct simpledrm_device, dev); +} + +/* + * Simplefb settings + */ + +static struct drm_display_mode simpledrm_mode(unsigned int width, + unsigned int height) +{ + struct drm_display_mode mode = { SIMPLEDRM_MODE(width, height) }; + + mode.clock = 60 /* Hz */ * mode.hdisplay * mode.vdisplay; + drm_mode_set_name(&mode); + + return mode; +} + +static int simpledrm_device_init_fb(struct simpledrm_device *sdev) +{ + int width, height, stride; + const struct drm_format_info *format; + struct drm_format_name_buf buf; + struct drm_device *dev = &sdev->dev; + struct platform_device *pdev = sdev->pdev; + const struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); + + if (pd) { + width = simplefb_get_width_pd(dev, pd); + if (width < 0) + return width; + height = simplefb_get_height_pd(dev, pd); + if (height < 0) + return height; + stride = simplefb_get_stride_pd(dev, pd); + if (stride < 0) + return stride; + format = simplefb_get_format_pd(dev, pd); + if (IS_ERR(format)) + return PTR_ERR(format); + } else { + drm_err(dev, "no simplefb configuration found\n"); + return -ENODEV; + } + + sdev->mode = simpledrm_mode(width, height); + sdev->format = format; + sdev->pitch = stride; + + drm_dbg_kms(dev, "display mode={" DRM_MODE_FMT "}\n", + DRM_MODE_ARG(&sdev->mode)); + drm_dbg_kms(dev, + "framebuffer format=\"%s\", size=%dx%d, stride=%d byte\n", + drm_get_format_name(format->format, &buf), width, + height, stride); + + return 0; +} + +/* + * Memory management + */ + +static int simpledrm_device_init_mm(struct simpledrm_device *sdev) +{ + struct platform_device *pdev = sdev->pdev; + struct resource *mem; + void __iomem *screen_base; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -EINVAL; + + screen_base = devm_ioremap_wc(&pdev->dev, mem->start, + resource_size(mem)); + if (!screen_base) + return -ENOMEM; + + sdev->mem = mem; + sdev->screen_base = screen_base; + + return 0; +} + +/* + * Modesetting + */ + +/* + * Support all formats of simplefb and maybe more; in order + * of preference. The display's update function will do any + * conversion necessary. + * + * TODO: Add blit helpers for remaining formats and uncomment + * constants. + */ +static const uint32_t simpledrm_default_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, + //DRM_FORMAT_XRGB1555, + //DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGB888, + //DRM_FORMAT_XRGB2101010, + //DRM_FORMAT_ARGB2101010, +}; + +static const uint64_t simpledrm_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + +static int simpledrm_connector_helper_get_modes(struct drm_connector *connector) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(connector->dev); + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &sdev->mode); + if (!mode) + return 0; + + if (mode->name[0] == '\0') + drm_mode_set_name(mode); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + + if (mode->width_mm) + connector->display_info.width_mm = mode->width_mm; + if (mode->height_mm) + connector->display_info.height_mm = mode->height_mm; + + return 1; +} + +static const struct drm_connector_helper_funcs simpledrm_connector_helper_funcs = { + .get_modes = simpledrm_connector_helper_get_modes, +}; + +static const struct drm_connector_funcs simpledrm_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int +simpledrm_simple_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe, + const struct drm_display_mode *mode) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(pipe->connector->dev); + + if (mode->hdisplay != sdev->mode.hdisplay && + mode->vdisplay != sdev->mode.vdisplay) + return MODE_ONE_SIZE; + else if (mode->hdisplay != sdev->mode.hdisplay) + return MODE_ONE_WIDTH; + else if (mode->vdisplay != sdev->mode.vdisplay) + return MODE_ONE_HEIGHT; + + return MODE_OK; +} + +static void +simpledrm_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state, + struct drm_plane_state *plane_state) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(pipe->connector->dev); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + void *vmap = shadow_plane_state->map[0].vaddr; /* TODO: Use mapping abstraction properly */ + + if (!fb) + return; + + drm_fb_blit_dstclip(sdev->screen_base, sdev->pitch, + sdev->format->format, vmap, fb); +} + +static void +simpledrm_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *old_plane_state) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(pipe->connector->dev); + struct drm_plane_state *plane_state = pipe->plane.state; + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + void *vmap = shadow_plane_state->map[0].vaddr; /* TODO: Use mapping abstraction properly */ + struct drm_framebuffer *fb = plane_state->fb; + struct drm_rect clip; + + if (!fb) + return; + + if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &clip)) + return; + + drm_fb_blit_rect_dstclip(sdev->screen_base, sdev->pitch, + sdev->format->format, vmap, fb, &clip); +} + +static const struct drm_simple_display_pipe_funcs +simpledrm_simple_display_pipe_funcs = { + .mode_valid = simpledrm_simple_display_pipe_mode_valid, + .enable = simpledrm_simple_display_pipe_enable, + .update = simpledrm_simple_display_pipe_update, + DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS, +}; + +static const struct drm_mode_config_funcs simpledrm_mode_config_funcs = { + .fb_create = drm_gem_fb_create_with_dirty, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static const uint32_t *simpledrm_device_formats(struct simpledrm_device *sdev, + size_t *nformats_out) +{ + struct drm_device *dev = &sdev->dev; + size_t i; + + if (sdev->nformats) + goto out; /* don't rebuild list on recurring calls */ + + /* native format goes first */ + sdev->formats[0] = sdev->format->format; + sdev->nformats = 1; + + /* default formats go second */ + for (i = 0; i < ARRAY_SIZE(simpledrm_default_formats); ++i) { + if (simpledrm_default_formats[i] == sdev->format->format) + continue; /* native format already went first */ + sdev->formats[sdev->nformats] = simpledrm_default_formats[i]; + sdev->nformats++; + } + + /* + * TODO: The simpledrm driver converts framebuffers to the native + * format when copying them to device memory. If there are more + * formats listed than supported by the driver, the native format + * is not supported by the conversion helpers. Therefore *only* + * support the native format and add a conversion helper ASAP. + */ + if (drm_WARN_ONCE(dev, i != sdev->nformats, + "format conversion helpers required for %p4cc", + &sdev->format->format)) { + sdev->nformats = 1; + } + +out: + *nformats_out = sdev->nformats; + return sdev->formats; +} + +static int simpledrm_device_init_modeset(struct simpledrm_device *sdev) +{ + struct drm_device *dev = &sdev->dev; + struct drm_display_mode *mode = &sdev->mode; + struct drm_connector *connector = &sdev->connector; + struct drm_simple_display_pipe *pipe = &sdev->pipe; + const uint32_t *formats; + size_t nformats; + int ret; + + ret = drmm_mode_config_init(dev); + if (ret) + return ret; + + dev->mode_config.min_width = mode->hdisplay; + dev->mode_config.max_width = mode->hdisplay; + dev->mode_config.min_height = mode->vdisplay; + dev->mode_config.max_height = mode->vdisplay; + dev->mode_config.prefer_shadow = true; + dev->mode_config.preferred_depth = sdev->format->cpp[0] * 8; + dev->mode_config.funcs = &simpledrm_mode_config_funcs; + + ret = drm_connector_init(dev, connector, &simpledrm_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (ret) + return ret; + drm_connector_helper_add(connector, &simpledrm_connector_helper_funcs); + + formats = simpledrm_device_formats(sdev, &nformats); + + ret = drm_simple_display_pipe_init(dev, pipe, &simpledrm_simple_display_pipe_funcs, + formats, nformats, simpledrm_format_modifiers, + connector); + if (ret) + return ret; + + drm_mode_config_reset(dev); + + return 0; +} + +/* + * Init / Cleanup + */ + +static struct simpledrm_device * +simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev) +{ + struct simpledrm_device *sdev; + int ret; + + sdev = devm_drm_dev_alloc(&pdev->dev, drv, struct simpledrm_device, + dev); + if (IS_ERR(sdev)) + return ERR_CAST(sdev); + sdev->pdev = pdev; + + ret = simpledrm_device_init_fb(sdev); + if (ret) + return ERR_PTR(ret); + ret = simpledrm_device_init_mm(sdev); + if (ret) + return ERR_PTR(ret); + ret = simpledrm_device_init_modeset(sdev); + if (ret) + return ERR_PTR(ret); + + return sdev; +} + +/* + * DRM driver + */ + +DEFINE_DRM_GEM_FOPS(simpledrm_fops); + +static struct drm_driver simpledrm_driver = { + DRM_GEM_SHMEM_DRIVER_OPS, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET, + .fops = &simpledrm_fops, +}; + +/* + * Platform driver + */ + +static int simpledrm_probe(struct platform_device *pdev) +{ + struct simpledrm_device *sdev; + struct drm_device *dev; + int ret; + + sdev = simpledrm_device_create(&simpledrm_driver, pdev); + if (IS_ERR(sdev)) + return PTR_ERR(sdev); + dev = &sdev->dev; + + ret = drm_dev_register(dev, 0); + if (ret) + return ret; + + return 0; +} + +static int simpledrm_remove(struct platform_device *pdev) +{ + struct simpledrm_device *sdev = platform_get_drvdata(pdev); + struct drm_device *dev = &sdev->dev; + + drm_dev_unregister(dev); + + return 0; +} + +static struct platform_driver simpledrm_platform_driver = { + .driver = { + .name = "simple-framebuffer", /* connect to sysfb */ + }, + .probe = simpledrm_probe, + .remove = simpledrm_remove, +}; + +module_platform_driver(simpledrm_platform_driver); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 06/10] drm/simpledrm: Add fbdev emulation
This displays a console on simpledrm's framebuffer. The default framebuffer format is being used. Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Reviewed-by: Daniel Vetter <daniel.vetter at ffwll.ch> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/tiny/simpledrm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 0422c549b97a..4f0d4ec0b432 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -8,6 +8,7 @@ #include <drm/drm_damage_helper.h> #include <drm/drm_device.h> #include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_format_helper.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> @@ -500,6 +501,8 @@ static int simpledrm_probe(struct platform_device *pdev) if (ret) return ret; + drm_fbdev_generic_setup(dev, 0); + return 0; } -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 07/10] drm/simpledrm: Initialize framebuffer data from device-tree node
A firmware framebuffer might also be specified via device-tree files. If no device platform data is given, try the DT device node. v2: * add Device Tree match table * clean-up parser wrappers Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/tiny/simpledrm.c | 89 ++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 4f0d4ec0b432..c9cef2b50de6 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -114,6 +114,74 @@ simplefb_get_format_pd(struct drm_device *dev, return simplefb_get_validated_format(dev, pd->format); } +static int +simplefb_read_u32_of(struct drm_device *dev, struct device_node *of_node, + const char *name, u32 *value) +{ + int ret = of_property_read_u32(of_node, name, value); + + if (ret) + drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n", + name, ret); + return ret; +} + +static int +simplefb_read_string_of(struct drm_device *dev, struct device_node *of_node, + const char *name, const char **value) +{ + int ret = of_property_read_string(of_node, name, value); + + if (ret) + drm_err(dev, "simplefb: cannot parse framebuffer %s: error %d\n", + name, ret); + return ret; +} + +static int +simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 width; + int ret = simplefb_read_u32_of(dev, of_node, "width", &width); + + if (ret) + return ret; + return simplefb_get_validated_int0(dev, "width", width); +} + +static int +simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 height; + int ret = simplefb_read_u32_of(dev, of_node, "height", &height); + + if (ret) + return ret; + return simplefb_get_validated_int0(dev, "height", height); +} + +static int +simplefb_get_stride_of(struct drm_device *dev, struct device_node *of_node) +{ + u32 stride; + int ret = simplefb_read_u32_of(dev, of_node, "stride", &stride); + + if (ret) + return ret; + return simplefb_get_validated_int(dev, "stride", stride); +} + +static const struct drm_format_info * +simplefb_get_format_of(struct drm_device *dev, struct device_node *of_node) +{ + const char *format; + int ret = simplefb_read_string_of(dev, of_node, "format", &format); + + if (ret) + return ERR_PTR(ret); + return simplefb_get_validated_format(dev, format); +} + /* * Simple Framebuffer device */ @@ -166,6 +234,7 @@ static int simpledrm_device_init_fb(struct simpledrm_device *sdev) struct drm_device *dev = &sdev->dev; struct platform_device *pdev = sdev->pdev; const struct simplefb_platform_data *pd = dev_get_platdata(&pdev->dev); + struct device_node *of_node = pdev->dev.of_node; if (pd) { width = simplefb_get_width_pd(dev, pd); @@ -180,6 +249,19 @@ static int simpledrm_device_init_fb(struct simpledrm_device *sdev) format = simplefb_get_format_pd(dev, pd); if (IS_ERR(format)) return PTR_ERR(format); + } else if (of_node) { + width = simplefb_get_width_of(dev, of_node); + if (width < 0) + return width; + height = simplefb_get_height_of(dev, of_node); + if (height < 0) + return height; + stride = simplefb_get_stride_of(dev, of_node); + if (stride < 0) + return stride; + format = simplefb_get_format_of(dev, of_node); + if (IS_ERR(format)) + return PTR_ERR(format); } else { drm_err(dev, "no simplefb configuration found\n"); return -ENODEV; @@ -516,9 +598,16 @@ static int simpledrm_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id simpledrm_of_match_table[] = { + { .compatible = "simple-framebuffer", }, + { }, +}; +MODULE_DEVICE_TABLE(of, simpledrm_of_match_table); + static struct platform_driver simpledrm_platform_driver = { .driver = { .name = "simple-framebuffer", /* connect to sysfb */ + .of_match_table = simpledrm_of_match_table, }, .probe = simpledrm_probe, .remove = simpledrm_remove, -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 08/10] drm/simpledrm: Acquire clocks from DT device node
Make sure required hardware clocks are enabled while the firmware framebuffer is in use. The basic code has been taken from the simplefb driver and adapted to DRM. Clocks are released automatically via devres helpers. Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/tiny/simpledrm.c | 108 +++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index c9cef2b50de6..10ca3373b61f 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include <linux/clk.h> +#include <linux/of_clk.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> @@ -190,6 +192,12 @@ struct simpledrm_device { struct drm_device dev; struct platform_device *pdev; + /* clocks */ +#if defined CONFIG_OF && defined CONFIG_COMMON_CLK + unsigned int clk_count; + struct clk **clks; +#endif + /* simplefb settings */ struct drm_display_mode mode; const struct drm_format_info *format; @@ -211,6 +219,103 @@ static struct simpledrm_device *simpledrm_device_of_dev(struct drm_device *dev) return container_of(dev, struct simpledrm_device, dev); } +/* + * Hardware + */ + +#if defined CONFIG_OF && defined CONFIG_COMMON_CLK +/* + * Clock handling code. + * + * Here we handle the clocks property of our "simple-framebuffer" dt node. + * This is necessary so that we can make sure that any clocks needed by + * the display engine that the bootloader set up for us (and for which it + * provided a simplefb dt node), stay up, for the life of the simplefb + * driver. + * + * When the driver unloads, we cleanly disable, and then release the clocks. + * + * We only complain about errors here, no action is taken as the most likely + * error can only happen due to a mismatch between the bootloader which set + * up simplefb, and the clock definitions in the device tree. Chances are + * that there are no adverse effects, and if there are, a clean teardown of + * the fb probe will not help us much either. So just complain and carry on, + * and hope that the user actually gets a working fb at the end of things. + */ + +static void simpledrm_device_release_clocks(void *res) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(res); + unsigned int i; + + for (i = 0; i < sdev->clk_count; ++i) { + if (sdev->clks[i]) { + clk_disable_unprepare(sdev->clks[i]); + clk_put(sdev->clks[i]); + } + } +} + +static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) +{ + struct drm_device *dev = &sdev->dev; + struct platform_device *pdev = sdev->pdev; + struct device_node *of_node = pdev->dev.of_node; + struct clk *clock; + unsigned int i; + int ret; + + if (dev_get_platdata(&pdev->dev) || !of_node) + return 0; + + sdev->clk_count = of_clk_get_parent_count(of_node); + if (!sdev->clk_count) + return 0; + + sdev->clks = drmm_kzalloc(dev, sdev->clk_count * sizeof(sdev->clks[0]), + GFP_KERNEL); + if (!sdev->clks) + return -ENOMEM; + + for (i = 0; i < sdev->clk_count; ++i) { + clock = of_clk_get(of_node, i); + if (IS_ERR(clock)) { + ret = PTR_ERR(clock); + if (ret == -EPROBE_DEFER) + goto err; + drm_err(dev, "clock %u not found: %d\n", i, ret); + continue; + } + ret = clk_prepare_enable(clock); + if (ret) { + drm_err(dev, "failed to enable clock %u: %d\n", + i, ret); + clk_put(clock); + } + sdev->clks[i] = clock; + } + + return devm_add_action_or_reset(&pdev->dev, + simpledrm_device_release_clocks, + sdev); + +err: + while (i) { + --i; + if (sdev->clks[i]) { + clk_disable_unprepare(sdev->clks[i]); + clk_put(sdev->clks[i]); + } + } + return ret; +} +#else +static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) +{ + return 0; +} +#endif + /* * Simplefb settings */ @@ -534,6 +639,9 @@ simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev) return ERR_CAST(sdev); sdev->pdev = pdev; + ret = simpledrm_device_init_clocks(sdev); + if (ret) + return ERR_PTR(ret); ret = simpledrm_device_init_fb(sdev); if (ret) return ERR_PTR(ret); -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 09/10] drm/simpledrm: Acquire regulators from DT device node
Make sure required hardware regulators are enabled while the firmware framebuffer is in use. The basic code has been taken from the simplefb driver and adapted to DRM. Regulators are released automatically via devres helpers. v2: * use strscpy() Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/tiny/simpledrm.c | 128 +++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 10ca3373b61f..2e27eeb791a1 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -4,6 +4,7 @@ #include <linux/of_clk.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <drm/drm_atomic_state_helper.h> #include <drm/drm_connector.h> @@ -197,6 +198,11 @@ struct simpledrm_device { unsigned int clk_count; struct clk **clks; #endif + /* regulators */ +#if defined CONFIG_OF && defined CONFIG_REGULATOR + unsigned int regulator_count; + struct regulator **regulators; +#endif /* simplefb settings */ struct drm_display_mode mode; @@ -316,6 +322,125 @@ static int simpledrm_device_init_clocks(struct simpledrm_device *sdev) } #endif +#if defined CONFIG_OF && defined CONFIG_REGULATOR + +#define SUPPLY_SUFFIX "-supply" + +/* + * Regulator handling code. + * + * Here we handle the num-supplies and vin*-supply properties of our + * "simple-framebuffer" dt node. This is necessary so that we can make sure + * that any regulators needed by the display hardware that the bootloader + * set up for us (and for which it provided a simplefb dt node), stay up, + * for the life of the simplefb driver. + * + * When the driver unloads, we cleanly disable, and then release the + * regulators. + * + * We only complain about errors here, no action is taken as the most likely + * error can only happen due to a mismatch between the bootloader which set + * up simplefb, and the regulator definitions in the device tree. Chances are + * that there are no adverse effects, and if there are, a clean teardown of + * the fb probe will not help us much either. So just complain and carry on, + * and hope that the user actually gets a working fb at the end of things. + */ + +static void simpledrm_device_release_regulators(void *res) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(res); + unsigned int i; + + for (i = 0; i < sdev->regulator_count; ++i) { + if (sdev->regulators[i]) { + regulator_disable(sdev->regulators[i]); + regulator_put(sdev->regulators[i]); + } + } +} + +static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) +{ + struct drm_device *dev = &sdev->dev; + struct platform_device *pdev = sdev->pdev; + struct device_node *of_node = pdev->dev.of_node; + struct property *prop; + struct regulator *regulator; + const char *p; + unsigned int count = 0, i = 0; + int ret; + + if (dev_get_platdata(&pdev->dev) || !of_node) + return 0; + + /* Count the number of regulator supplies */ + for_each_property_of_node(of_node, prop) { + p = strstr(prop->name, SUPPLY_SUFFIX); + if (p && p != prop->name) + ++count; + } + + if (!count) + return 0; + + sdev->regulators = drmm_kzalloc(dev, + count * sizeof(sdev->regulators[0]), + GFP_KERNEL); + if (!sdev->regulators) + return -ENOMEM; + + for_each_property_of_node(of_node, prop) { + char name[32]; /* 32 is max size of property name */ + size_t len; + + p = strstr(prop->name, SUPPLY_SUFFIX); + if (!p || p == prop->name) + continue; + len = strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1; + strscpy(name, prop->name, min(sizeof(name), len)); + + regulator = regulator_get_optional(&pdev->dev, name); + if (IS_ERR(regulator)) { + ret = PTR_ERR(regulator); + if (ret == -EPROBE_DEFER) + goto err; + drm_err(dev, "regulator %s not found: %d\n", + name, ret); + continue; + } + + ret = regulator_enable(regulator); + if (ret) { + drm_err(dev, "failed to enable regulator %u: %d\n", + i, ret); + regulator_put(regulator); + } + + sdev->regulators[i++] = regulator; + } + sdev->regulator_count = i; + + return devm_add_action_or_reset(&pdev->dev, + simpledrm_device_release_regulators, + sdev); + +err: + while (i) { + --i; + if (sdev->regulators[i]) { + regulator_disable(sdev->regulators[i]); + regulator_put(sdev->regulators[i]); + } + } + return ret; +} +#else +static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) +{ + return 0; +} +#endif + /* * Simplefb settings */ @@ -640,6 +765,9 @@ simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev) sdev->pdev = pdev; ret = simpledrm_device_init_clocks(sdev); + if (ret) + return ERR_PTR(ret); + ret = simpledrm_device_init_regulators(sdev); if (ret) return ERR_PTR(ret); ret = simpledrm_device_init_fb(sdev); -- 2.30.1
Thomas Zimmermann
2021-Mar-18 10:29 UTC
[PATCH v2 10/10] drm/simpledrm: Acquire memory aperture for framebuffer
We register the simplekms device with the DRM platform helpers. A native driver for the graphics hardware will kick-out the simpledrm driver before taking over the device. v2: * adapt to aperture changes * use drm_dev_unplug() and drm_dev_enter/exit() * don't split error string Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> Tested-by: nerdopolis <bluescreen_avenger at verizon.net> --- drivers/gpu/drm/tiny/Kconfig | 1 + drivers/gpu/drm/tiny/simpledrm.c | 83 ++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/tiny/Kconfig b/drivers/gpu/drm/tiny/Kconfig index d46f95d9196d..5b72dd8e93f9 100644 --- a/drivers/gpu/drm/tiny/Kconfig +++ b/drivers/gpu/drm/tiny/Kconfig @@ -41,6 +41,7 @@ config DRM_GM12U320 config DRM_SIMPLEDRM tristate "Simple framebuffer driver" depends on DRM + select DRM_APERTURE select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER help diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 2e27eeb791a1..67d33af19086 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -5,7 +5,9 @@ #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/spinlock.h> +#include <drm/drm_aperture.h> #include <drm/drm_atomic_state_helper.h> #include <drm/drm_connector.h> #include <drm/drm_damage_helper.h> @@ -37,6 +39,12 @@ #define SIMPLEDRM_MODE(hd, vd) \ DRM_SIMPLE_MODE(hd, vd, RES_MM(hd), RES_MM(vd)) +/* + * Protects the platform device's drvdata against + * concurrent manipulation. + */ +static DEFINE_SPINLOCK(simpledrm_drvdata_lock); + /* * Helpers for simplefb */ @@ -515,16 +523,53 @@ static int simpledrm_device_init_fb(struct simpledrm_device *sdev) * Memory management */ +static void simpledrm_aperture_detach(struct drm_device *dev, resource_size_t base, + resource_size_t size) +{ + struct simpledrm_device *sdev = simpledrm_device_of_dev(dev); + struct platform_device *pdev = sdev->pdev; + + if (WARN_ON(drm_dev_is_unplugged(dev))) + return; /* BUG: driver already got detached */ + + /* + * If simpledrm gets detached from the aperture, it's like unplugging + * the device. So call drm_dev_unplug(). + */ + drm_dev_unplug(dev); + + spin_lock(&simpledrm_drvdata_lock); + sdev = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); /* required; see simpledrm_remove() */ + spin_unlock(&simpledrm_drvdata_lock); +} + +static const struct drm_aperture_funcs simpledrm_aperture_funcs = { + .detach = simpledrm_aperture_detach, +}; + static int simpledrm_device_init_mm(struct simpledrm_device *sdev) { + struct drm_device *dev = &sdev->dev; struct platform_device *pdev = sdev->pdev; struct resource *mem; + struct drm_aperture *ap; void __iomem *screen_base; + int ret; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) return -EINVAL; + ap = devm_aperture_acquire(dev, mem->start, resource_size(mem), + &simpledrm_aperture_funcs); + if (IS_ERR(ap)) { + ret = PTR_ERR(ap); + drm_err(dev, "could not acquire memory range [0x%llx:0x%llx]: error %d\n", + mem->start, mem->end, ret); + return ret; + } + screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); if (!screen_base) @@ -625,12 +670,18 @@ simpledrm_simple_display_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); struct drm_framebuffer *fb = plane_state->fb; void *vmap = shadow_plane_state->map[0].vaddr; /* TODO: Use mapping abstraction properly */ + struct drm_device *dev = &sdev->dev; + int idx; if (!fb) return; + if (!drm_dev_enter(dev, &idx)) + return; + drm_fb_blit_dstclip(sdev->screen_base, sdev->pitch, sdev->format->format, vmap, fb); + drm_dev_exit(idx); } static void @@ -642,7 +693,9 @@ simpledrm_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); void *vmap = shadow_plane_state->map[0].vaddr; /* TODO: Use mapping abstraction properly */ struct drm_framebuffer *fb = plane_state->fb; + struct drm_device *dev = &sdev->dev; struct drm_rect clip; + int idx; if (!fb) return; @@ -650,8 +703,13 @@ simpledrm_simple_display_pipe_update(struct drm_simple_display_pipe *pipe, if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &clip)) return; + if (!drm_dev_enter(dev, &idx)) + return; + drm_fb_blit_rect_dstclip(sdev->screen_base, sdev->pitch, sdev->format->format, vmap, fb, &clip); + + drm_dev_exit(idx); } static const struct drm_simple_display_pipe_funcs @@ -826,10 +884,29 @@ static int simpledrm_probe(struct platform_device *pdev) static int simpledrm_remove(struct platform_device *pdev) { - struct simpledrm_device *sdev = platform_get_drvdata(pdev); - struct drm_device *dev = &sdev->dev; + struct simpledrm_device *sdev; + + spin_lock(&simpledrm_drvdata_lock); + sdev = platform_get_drvdata(pdev); + platform_set_drvdata(pdev, NULL); + spin_unlock(&simpledrm_drvdata_lock); + + /* + * The platform driver shares its reference to dev with the + * platform helpers for apertures. That reference is either + * released here when unloading the driver; or it's released + * when the driver gets kicked out by another driver. In the + * latter case, the aperture release routine clears the data + * field of the platform device. + * + * Therefore, sdev being NULL is a valid state if the driver + * has been kicked out by another DRM driver. In this case, + * it's all been cleaned up and we can return immediately. + */ + if (!sdev) + return 0; - drm_dev_unregister(dev); + drm_dev_unplug(&sdev->dev); return 0; } -- 2.30.1
Hans de Goede
2021-Mar-25 11:29 UTC
[PATCH v2 00/10] drm: Support simple-framebuffer devices and firmware fbs
Hi, On 3/18/21 11:29 AM, Thomas Zimmermann wrote:> This patchset adds support for simple-framebuffer platform devices and > a handover mechanism for native drivers to take-over control of the > hardware. > > The new driver, called simpledrm, binds to a simple-frambuffer platform > device. The kernel's boot code creates such devices for firmware-provided > framebuffers, such as EFI-GOP or VESA. Typically the BIOS, UEFI or boot > loader sets up the framebuffers. Description via device tree is also an > option. > > Simpledrm is small enough to be linked into the kernel. The driver's main > purpose is to provide graphical output during the early phases of the boot > process, before the native DRM drivers are available. Native drivers are > typically loaded from an initrd ram disk. Occationally simpledrm can also > serve as interim solution on graphics hardware without native DRM driver. > > So far distributions rely on fbdev drivers, such as efifb, vesafb or > simplefb, for early-boot graphical output. However fbdev is deprecated and > the drivers do not provide DRM interfaces for modern userspace. > > Patches 1 and 2 prepare the DRM format helpers for simpledrm. > > Patches 3 and 4 add a hand-over mechanism. Simpledrm acquires it's > framebuffer's I/O-memory range and provides a callback function to be > removed by a native driver. The native driver will remove simpledrm before > taking over the hardware. The removal is integrated into existing helpers, > so drivers use it automatically. > > Patches 5 to 10 add the simpledrm driver. It's build on simple DRM helpers > and SHMEM. It supports 16-bit, 24-bit and 32-bit RGB framebuffers. During > pageflips, SHMEM buffers are copied into the framebuffer memory, similar > to cirrus or mgag200. The code in patches 8 and 9 handles clocks and > regulators. It's based on the simplefb drivers, but has been modified for > DRM.Thank you for your work on this, this is very interesting.> I've also been working on fastboot support (i.e., flicker-free booting). > This requires state-readout from simpledrm via generic interfaces, as > outlined in [1]. I do have some prototype code, but it will take a while > to get this ready. Simpledrm will then support it. > > I've tested simpledrm with x86 EFI and VESA framebuffers, which both work > reliably. The fbdev console and Weston work automatically. Xorg requires > manual configuration of the device. Xorgs current modesetting driver does > not work with both, platform and PCI device, for the same physical > hardware. Once configured, X11 works. I looked into X11, but couldn't see > an easy way of fixing the problem. With the push towards Wayland+Xwayland > I expect the problem to become a non-issue soon. Additional testing has > been reported at [2]. > > One cosmetical issue is that simpledrm's device file is card0 and the > native driver's device file is card1. After simpledrm has been kicked out, > only card1 is left. This does not seem to be a practical problem however. > > TODO/IDEAS: > > * provide deferred takeoverI'm not sure what you mean with this ? Currently deferred-takeover is handled in the fbcon code. Current flickerfree boot works like this (assuming a single LCD panel in a laptop): 1. EFI/GOP sets up the framebuffer, draws a vendor logo 2. The bootloader runs in silent mode and does not touch anything gfx related 3. kernel boots, with a loglevel of 3 so only CRIT/EMERG messages are shown 2. efifb loads; and tells fbcon that a framebuffer is now available for it to "bind" to. Since CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y fbcon defers taking over the console and leaves the dummy-console driver in place (unless there have already been kernel messages logged, which there shouldn't because loglevel=3) 3. i915 loads, reads out the hw state compares this to the preferred-mode for the panel which it would set, they match, nothing happens. i915 takes ownership of the scanout-buffer set up by the GOP, but leaves it in place. i915 also removes the efifb /dev/fb0 and installs its own /dev/fb0 fbdev compat device, fbcon is notified of this, but is still deferred and leaves the dummy console driver in place as console driver. 4. Plymouth loads, allocates a new scan-out buffer at the panel's preferred resolution, plymouth reads the vendor-logo through the BGRT ACPI interface and fills the scanout-buffer with the vendor-logo + a spinner. Then plymouth installs the new scanout-buffer on the crtc, this is done atomically during vsync, so the user sees no changes, other then the spinner appearing (note the active VT is now in graphical mode) 5. From here on not flickering is a userspace problem AFAICT this should work fine with simplekms too, unless it clears the screen to black when it binds. An addition to the above sequence, if at any time either the kernel or userspace prints a message to the console; and at that time a fbdev is registered then fbcon will takeover as the console driver from the dummy driver and it will start drawing to the registered fbdev (1), destroying the framebuffer contents. Also if any messages where printend while no fbdev was registered, then fbcon will takeover the console as soon as a fbdev gets registered. So since we already have deferred-takeover in the fbcon code, I wonder what you mean when you are talking about "provide deferred takeover" for simplekms? Regards, Hans 1) Except when the VT has been switched to GFX mode when this happens, then fbcon will delay using the fbdev until the VT is switched back to text mode. p.s. This has the interesting side effect then when logging into a desktop GUI session: kernel -> plymouth -> gdm -> GNOME user session There never is any output to the text-console and fbcon never takes-over, so on many Laptops running say Fedora workstation the fbcon code is actually unused until the user manually switches to another virtual-console to log in in text-mode: [hans at x1 ~]$ dmesg | grep -E 'fbcon|Console:|Truecolor' [ 0.258904] Console: colour dummy device 80x25 [ 1.274726] efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0 [ 1.274768] fbcon: Deferring console take-over [ 2.540894] fbcon: i915drmfb (fb0) is primary device [ 2.540896] fbcon: Deferring console take-over [hans at x1 ~]$ uptime 12:29:39 up 4:19, 1 user, load average: 0.58, 0.75, 0.81 Look mom no fbcon> * provide bootsplash DRM client > * make simplekms usable with ARM-EFI fbs > > v2: > * rename to simpledrm, aperture helpers > * reorganized patches > * use hotplug helpers for removal (Daniel) > * added DT match tables (Rob) > * use shadow-plane helpers > * lots of minor cleanups > > [1] https://lore.kernel.org/dri-devel/CAKMK7uHtqHy_oz4W7F+hmp9iqp7W5Ra8CxPvJ=9BwmvfU-O0gg at mail.gmail.com/ > [2] https://lore.kernel.org/dri-devel/1761762.3HQLrFs1K7 at nerdopolis/ > > Thomas Zimmermann (10): > drm/format-helper: Pass destination pitch to drm_fb_memcpy_dstclip() > drm/format-helper: Add blitter functions > drm/aperture: Move fbdev conflict helpers into drm_aperture.h > drm/aperture: Add infrastructure for aperture ownership > drm: Add simpledrm driver > drm/simpledrm: Add fbdev emulation > drm/simpledrm: Initialize framebuffer data from device-tree node > drm/simpledrm: Acquire clocks from DT device node > drm/simpledrm: Acquire regulators from DT device node > drm/simpledrm: Acquire memory aperture for framebuffer > > Documentation/gpu/drm-internals.rst | 12 + > MAINTAINERS | 7 + > drivers/gpu/drm/Kconfig | 7 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/drm_aperture.c | 287 ++++++++ > drivers/gpu/drm/drm_format_helper.c | 96 ++- > drivers/gpu/drm/mgag200/mgag200_mode.c | 2 +- > drivers/gpu/drm/tiny/Kconfig | 17 + > drivers/gpu/drm/tiny/Makefile | 1 + > drivers/gpu/drm/tiny/cirrus.c | 2 +- > drivers/gpu/drm/tiny/simpledrm.c | 932 +++++++++++++++++++++++++ > include/drm/drm_aperture.h | 96 +++ > include/drm/drm_fb_helper.h | 56 +- > include/drm/drm_format_helper.h | 10 +- > 14 files changed, 1466 insertions(+), 60 deletions(-) > create mode 100644 drivers/gpu/drm/drm_aperture.c > create mode 100644 drivers/gpu/drm/tiny/simpledrm.c > create mode 100644 include/drm/drm_aperture.h > > -- > 2.30.1 >
Daniel Vetter
2021-Apr-08 09:48 UTC
[PATCH v2 04/10] drm/aperture: Add infrastructure for aperture ownership
On Thu, Mar 18, 2021 at 11:29:15AM +0100, Thomas Zimmermann wrote:> Platform devices might operate on firmware framebuffers, such as VESA or > EFI. Before a native driver for the graphics hardware can take over the > device, it has to remove any platform driver that operates on the firmware > framebuffer. Aperture helpers provide the infrastructure for platform > drivers to acquire firmware framebuffers, and for native drivers to remove > them later on. > > It works similar to the related fbdev mechanism. During initialization, the > platform driver acquires the firmware framebuffer's I/O memory and provides > a callback to be removed. The native driver later uses this information to > remove any platform driver for it's framebuffer I/O memory. > > The aperture removal code is integrated into the existing code for removing > conflicting framebuffers, so native drivers use it automatically. > > v2: > * rename plaform helpers to aperture helpers > * tie to device lifetime with devm_ functions > * removed unsued remove() callback > * rename kickout to detach > * make struct drm_aperture private > * rebase onto existing drm_aperture.h header file > * use MIT license only for simplicity > * documentation > > Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> > Tested-by: nerdopolis <bluescreen_avenger at verizon.net>Bunch of bikesheds for your considerations below, but overall lgtm. Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch> Cheers, Daniel> --- > Documentation/gpu/drm-internals.rst | 6 + > drivers/gpu/drm/Kconfig | 7 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/drm_aperture.c | 287 ++++++++++++++++++++++++++++ > include/drm/drm_aperture.h | 38 +++- > 5 files changed, 338 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/drm_aperture.c > > diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst > index 4c7642d2ca34..06af044c882f 100644 > --- a/Documentation/gpu/drm-internals.rst > +++ b/Documentation/gpu/drm-internals.rst > @@ -78,9 +78,15 @@ DRM_IOCTL_VERSION ioctl. > Managing Ownership of the Framebuffer Aperture > ---------------------------------------------- > > +.. kernel-doc:: drivers/gpu/drm/drm_aperture.c > + :doc: overview > + > .. kernel-doc:: include/drm/drm_aperture.h > :internal: > > +.. kernel-doc:: drivers/gpu/drm/drm_aperture.c > + :export: > + > Device Instance and Driver Handling > ----------------------------------- > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 1461652921be..b9d3fb91d22d 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -221,6 +221,13 @@ config DRM_SCHED > tristate > depends on DRM > > +config DRM_APERTURE > + bool > + depends on DRM > + help > + Controls ownership of graphics apertures. Required to > + synchronize with firmware-based drivers.Uh I'm not a big fan of Kconfig and .ko modules for every little helper code. Imo just stuff this into the drm kms helpers and done. Or stuff it into drm core code, I think either is a good case for this. Everything is its own module means we need to EXPORT_SYMBOL more stuff, and then drivers get funny ideas about using these internals ...> + > source "drivers/gpu/drm/i2c/Kconfig" > > source "drivers/gpu/drm/arm/Kconfig" > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 5eb5bf7c16e3..c9ecb02df0f3 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -32,6 +32,7 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o > drm-$(CONFIG_PCI) += drm_pci.o > drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o > drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o > +drm-$(CONFIG_DRM_APERTURE) += drm_aperture.o > > drm_vram_helper-y := drm_gem_vram_helper.o > obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o > diff --git a/drivers/gpu/drm/drm_aperture.c b/drivers/gpu/drm/drm_aperture.c > new file mode 100644 > index 000000000000..4b02b5fed0a1 > --- /dev/null > +++ b/drivers/gpu/drm/drm_aperture.c > @@ -0,0 +1,287 @@ > +// SPDX-License-Identifier: MIT > + > +#include <linux/device.h> > +#include <linux/list.h> > +#include <linux/mutex.h> > +#include <linux/slab.h> > +#include <linux/types.h> > + > +#include <drm/drm_aperture.h> > +#include <drm/drm_drv.h> > +#include <drm/drm_print.h> > + > +/** > + * DOC: overview > + * > + * A graphics device might be supported by different drivers, but only one > + * driver can be active at any given time. Many systems load a generic > + * graphics drivers, such as EFI-GOP or VESA, early during the boot process. > + * During later boot stages, they replace the generic driver with a dedicated, > + * hardware-specific driver. To take over the device the dedicated driver > + * first has to remove the generic driver. DRM aperture functions manage > + * ownership of DRM framebuffer memory and hand-over between drivers. > + * > + * DRM drivers should call drm_fb_helper_remove_conflicting_framebuffers() > + * at the top of their probe function. The function removes any generic > + * driver that is currently associated with the given framebuffer memory. > + * If the framebuffer is located at PCI BAR 0, the rsp code looks as in the > + * example given below. > + * > + * .. code-block:: c > + * > + * static int remove_conflicting_framebuffers(struct pci_dev *pdev) > + * { > + * struct apertures_struct *ap; > + * bool primary = false; > + * int ret; > + * > + * ap = alloc_apertures(1); > + * if (!ap) > + * return -ENOMEM; > + * > + * ap->ranges[0].base = pci_resource_start(pdev, 0); > + * ap->ranges[0].size = pci_resource_len(pdev, 0); > + * > + * #ifdef CONFIG_X86 > + * primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; > + * #endif > + * ret = drm_fb_helper_remove_conflicting_framebuffers(ap, "example driver", primary); > + * kfree(ap); > + * > + * return ret; > + * } > + * > + * static int probe(struct pci_dev *pdev) > + * { > + * int ret; > + * > + * // Remove any generic drivers... > + * ret = remove_conflicting_framebuffers(pdev); > + * if (ret) > + * return ret; > + * > + * // ... and initialize the hardware. > + * ... > + * > + * drm_dev_register(); > + * > + * return 0; > + * } > + * > + * For PCI devices it is often sufficient to use drm_fb_helper_remove_conflicting_pci_framebuffers() > + * and let it detect the framebuffer apertures automatically.Maybe just me, but to avoid overstretching the attention spawn of doc readers I'd avoid this example here. And maybe make the recommendation stronger, e.g. "PCI device drivers can avoid open-coding remove_conflicting_framebuffers() by calling drm_fb_helper_remove_conflicting_pci_framebuffers()."> + * > + * .. code-block:: c > + * > + * static int probe(struct pci_dev *pdev) > + * { > + * int ret; > + * > + * // Remove any generic drivers... > + * ret = drm_fb_helper_remove_conflicting_pci_framebuffers(pdev, "example driver"); > + * if (ret) > + * return ret; > + * > + * // ... and initialize the hardware. > + * ... > + * > + * drm_dev_register(); > + * > + * return 0; > + * } > + * > + * Drivers that are susceptible to being removed be other drivers, such as > + * generic EFI or VESA drivers, have to register themselves as owners of their > + * given framebuffer memory. Ownership of the framebuffer memory is achived > + * by calling devm_aperture_acquire(). On success, the driver is the owner > + * of the framebuffer range. The function fails if the framebuffer is already > + * by another driver. See below for an example. > + * > + * .. code-block:: c > + * > + * static struct drm_aperture_funcs ap_funcs = { > + * .detach = ...Is there really value in allowing/forcing drivers to set up their own detach ops? You already make this specific to struct drm_device, an implementation that just calls drm_dev_unplug feels like the right thing to do? Or maybe we should tie this more into the struct device mode and force an unload that way? That way devm cleanup would work as one expects, and avoid the need for anything specific (hopefully) in this detach callback. Just feels a bit like we're reinventing half of the driver model here, badly.> + * }; > + * > + * static int acquire_framebuffers(struct drm_device *dev, struct pci_dev *pdev) > + * { > + * resource_size_t start, len; > + * struct drm_aperture *ap; > + * > + * base = pci_resource_start(pdev, 0); > + * size = pci_resource_len(pdev, 0); > + * > + * ap = devm_acquire_aperture(dev, base, size, &ap_funcs); > + * if (IS_ERR(ap)) > + * return PTR_ERR(ap); > + * > + * return 0; > + * } > + * > + * static int probe(struct pci_dev *pdev) > + * { > + * struct drm_device *dev; > + * int ret; > + * > + * // ... Initialize the device... > + * dev = devm_drm_dev_alloc(); > + * ... > + * > + * // ... and acquire ownership of the framebuffer. > + * ret = acquire_framebuffers(dev, pdev); > + * if (ret) > + * return ret; > + * > + * drm_dev_register(); > + * > + * return 0; > + * } > + * > + * The generic driver is now subject to forced removal by other drivers. This > + * is when the detach function in struct &drm_aperture_funcs comes into play. > + * When a driver calls drm_fb_helper_remove_conflicting_framebuffers() et al > + * for the registered framebuffer range, the DRM core calls struct > + * &drm_aperture_funcs.detach and the generic driver has to onload itself. It > + * may not access the device's registers, framebuffer memory, ROM, etc after > + * detach returned. If the driver supports hotplugging, detach can be treated > + * like an unplug event. > + * > + * .. code-block:: c > + * > + * static void detach_from_device(struct drm_device *dev, > + * resource_size_t base, > + * resource_size_t size) > + * { > + * // Signal unplug > + * drm_dev_unplug(dev); > + * > + * // Maybe do other clean-up operations > + * ... > + * } > + * > + * static struct drm_aperture_funcs ap_funcs = { > + * .detach = detach_from_device, > + * }; > + */ > + > +/** > + * struct drm_aperture - Represents a DRM framebuffer aperture > + * > + * This structure has no public fields. > + */ > +struct drm_aperture { > + struct drm_device *dev; > + resource_size_t base; > + resource_size_t size; > + > + const struct drm_aperture_funcs *funcs; > + > + struct list_head lh; > +}; > + > +static LIST_HEAD(drm_apertures); > + > +static DEFINE_MUTEX(drm_apertures_lock); > + > +static bool overlap(resource_size_t base1, resource_size_t end1, > + resource_size_t base2, resource_size_t end2) > +{ > + return (base1 < end2) && (end1 > base2); > +} > + > +static void devm_aperture_acquire_release(void *data) > +{ > + struct drm_aperture *ap = data; > + bool detached = !ap->dev; > + > + if (!detached)Uh this needs a comment that if ap->dev is NULL then we're called from drm_aperture_detach_drivers() and hence the lock is already held.> + mutex_lock(&drm_apertures_lock);and an else locdep_assert_held(&drm_apertures_lock); here to check that. I was scratching my head first quite a bit how you'd solve the deadlock, this is a neat solution (much simpler than anything I came up with in my head). But needs comments.> + > + list_del(&ap->lh); > + > + if (!detached) > + mutex_unlock(&drm_apertures_lock); > +} > + > +/** > + * devm_aperture_acquire - Acquires ownership of a framebuffer on behalf of a DRM driver. > + * @dev: the DRM device to own the framebuffer memory > + * @base: the framebuffer's byte offset in physical memory > + * @size: the framebuffer size in bytes > + * @funcs: callback functions > + * > + * Installs the given device as the new owner. The function fails if the > + * framebuffer range, or parts of it, is currently owned by another driver. > + * To evict current owners, callers should use > + * drm_fb_helper_remove_conflicting_framebuffers() et al. before calling this > + * function. Acquired apertures are released automatically if the underlying > + * device goes away. > + * > + * Returns: > + * An instance of struct &drm_aperture on success, or a pointer-encoded > + * errno value otherwise. > + */ > +struct drm_aperture * > +devm_aperture_acquire(struct drm_device *dev, > + resource_size_t base, resource_size_t size, > + const struct drm_aperture_funcs *funcs) > +{ > + size_t end = base + size; > + struct list_head *pos; > + struct drm_aperture *ap; > + int ret; > + > + mutex_lock(&drm_apertures_lock); > + > + list_for_each(pos, &drm_apertures) { > + ap = container_of(pos, struct drm_aperture, lh); > + if (overlap(base, end, ap->base, ap->base + ap->size)) > + return ERR_PTR(-EBUSY); > + } > + > + ap = devm_kzalloc(dev->dev, sizeof(*ap), GFP_KERNEL); > + if (!ap) > + return ERR_PTR(-ENOMEM); > + > + ap->dev = dev; > + ap->base = base; > + ap->size = size; > + ap->funcs = funcs; > + INIT_LIST_HEAD(&ap->lh); > + > + list_add(&ap->lh, &drm_apertures); > + > + mutex_unlock(&drm_apertures_lock); > + > + ret = devm_add_action_or_reset(dev->dev, devm_aperture_acquire_release, ap); > + if (ret) > + return ERR_PTR(ret); > + > + return ap; > +} > +EXPORT_SYMBOL(devm_aperture_acquire); > + > +void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size) > +{ > + resource_size_t end = base + size; > + struct list_head *pos, *n; > + > + mutex_lock(&drm_apertures_lock); > + > + list_for_each_safe(pos, n, &drm_apertures) { > + struct drm_aperture *ap > + container_of(pos, struct drm_aperture, lh); > + struct drm_device *dev = ap->dev; > + > + if (!overlap(base, end, ap->base, ap->base + ap->size)) > + continue; > + > + ap->dev = NULL; /* detach from device */ > + if (drm_WARN_ON(dev, !ap->funcs->detach)) > + continue; > + ap->funcs->detach(dev, ap->base, ap->size); > + } > + > + mutex_unlock(&drm_apertures_lock); > +} > +EXPORT_SYMBOL(drm_aperture_detach_drivers);Is this just exported because of the inline functions in the headers? Imo better to make them proper functions (they're big after your patch¬ perf critical, so not good candidates for inlining anyway).> diff --git a/include/drm/drm_aperture.h b/include/drm/drm_aperture.h > index 13766efe9517..696cec75ef78 100644 > --- a/include/drm/drm_aperture.h > +++ b/include/drm/drm_aperture.h > @@ -4,8 +4,30 @@ > #define _DRM_APERTURE_H_ > > #include <linux/fb.h> > +#include <linux/pci.h> > #include <linux/vgaarb.h> > > +struct drm_aperture; > +struct drm_device; > + > +struct drm_aperture_funcs { > + void (*detach)(struct drm_device *dev, resource_size_t base, resource_size_t size); > +}; > + > +struct drm_aperture * > +devm_aperture_acquire(struct drm_device *dev, > + resource_size_t base, resource_size_t size, > + const struct drm_aperture_funcs *funcs); > + > +#if defined(CONFIG_DRM_APERTURE) > +void drm_aperture_detach_drivers(resource_size_t base, resource_size_t size); > +#else > +static inline void > +drm_aperture_detach_drivers(resource_size_t base, resource_size_t size) > +{ > +} > +#endif > + > /** > * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers > * @a: memory range, users of which are to be removed > @@ -20,6 +42,11 @@ static inline int > drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, > const char *name, bool primary) > { > + int i; > + > + for (i = 0; i < a->count; ++i) > + drm_aperture_detach_drivers(a->ranges[i].base, a->ranges[i].size); > + > #if IS_REACHABLE(CONFIG_FB) > return remove_conflicting_framebuffers(a, name, primary); > #else > @@ -43,7 +70,16 @@ static inline int > drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, > const char *name) > { > - int ret = 0; > + resource_size_t base, size; > + int bar, ret = 0; > + > + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) { > + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) > + continue; > + base = pci_resource_start(pdev, bar); > + size = pci_resource_len(pdev, bar); > + drm_aperture_detach_drivers(base, size); > + } > > /* > * WARNING: Apparently we must kick fbdev drivers before vgacon, > -- > 2.30.1 >-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch
Daniel Vetter
2021-Apr-08 09:50 UTC
[PATCH v2 03/10] drm/aperture: Move fbdev conflict helpers into drm_aperture.h
On Thu, Mar 18, 2021 at 11:29:14AM +0100, Thomas Zimmermann wrote:> Fbdev's helpers for handling conflicting framebuffers are related to > framebuffer apertures, not console emulation. Therefore move them into a > drm_aperture.h, which will contain the interfaces for the new aperture > helpers. > > Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de> > Tested-by: nerdopolis <bluescreen_avenger at verizon.net> > --- > Documentation/gpu/drm-internals.rst | 6 +++ > include/drm/drm_aperture.h | 60 +++++++++++++++++++++++++++++ > include/drm/drm_fb_helper.h | 56 ++------------------------- > 3 files changed, 69 insertions(+), 53 deletions(-) > create mode 100644 include/drm/drm_aperture.h > > diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst > index 12272b168580..4c7642d2ca34 100644 > --- a/Documentation/gpu/drm-internals.rst > +++ b/Documentation/gpu/drm-internals.rst > @@ -75,6 +75,12 @@ update it, its value is mostly useless. The DRM core prints it to the > kernel log at initialization time and passes it to userspace through the > DRM_IOCTL_VERSION ioctl. > > +Managing Ownership of the Framebuffer Aperture > +---------------------------------------------- > + > +.. kernel-doc:: include/drm/drm_aperture.h > + :internal: > + > Device Instance and Driver Handling > ----------------------------------- > > diff --git a/include/drm/drm_aperture.h b/include/drm/drm_aperture.h > new file mode 100644 > index 000000000000..13766efe9517 > --- /dev/null > +++ b/include/drm/drm_aperture.h > @@ -0,0 +1,60 @@ > +/* SPDX-License-Identifier: MIT */ > + > +#ifndef _DRM_APERTURE_H_ > +#define _DRM_APERTURE_H_ > + > +#include <linux/fb.h> > +#include <linux/vgaarb.h> > + > +/** > + * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffersAnnoying bikeshed, but I'd give them drm_aperture_ prefixes, for ocd consistency. Also make them real functions, they're quite big and will grow more in the next patch. I'm also not super happy about the naming here but oh well. Either way: Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>> + * @a: memory range, users of which are to be removed > + * @name: requesting driver name > + * @primary: also kick vga16fb if present > + * > + * This function removes framebuffer devices (initialized by firmware/bootloader) > + * which use memory range described by @a. If @a is NULL all such devices are > + * removed. > + */ > +static inline int > +drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, > + const char *name, bool primary) > +{ > +#if IS_REACHABLE(CONFIG_FB) > + return remove_conflicting_framebuffers(a, name, primary); > +#else > + return 0; > +#endif > +} > + > +/** > + * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured > + * framebuffers for PCI devices > + * @pdev: PCI device > + * @name: requesting driver name > + * > + * This function removes framebuffer devices (eg. initialized by firmware) > + * using memory range configured for any of @pdev's memory bars. > + * > + * The function assumes that PCI device with shadowed ROM drives a primary > + * display and so kicks out vga16fb. > + */ > +static inline int > +drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, > + const char *name) > +{ > + int ret = 0; > + > + /* > + * WARNING: Apparently we must kick fbdev drivers before vgacon, > + * otherwise the vga fbdev driver falls over. > + */ > +#if IS_REACHABLE(CONFIG_FB) > + ret = remove_conflicting_pci_framebuffers(pdev, name); > +#endif > + if (ret == 0) > + ret = vga_remove_vgacon(pdev); > + return ret; > +} > + > +#endif > diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h > index 3b273f9ca39a..d06a3942fddb 100644 > --- a/include/drm/drm_fb_helper.h > +++ b/include/drm/drm_fb_helper.h > @@ -30,13 +30,13 @@ > #ifndef DRM_FB_HELPER_H > #define DRM_FB_HELPER_H > > -struct drm_fb_helper; > - > +#include <drm/drm_aperture.h> > #include <drm/drm_client.h> > #include <drm/drm_crtc.h> > #include <drm/drm_device.h> > #include <linux/kgdb.h> > -#include <linux/vgaarb.h> > + > +struct drm_fb_helper; > > enum mode_set_atomic { > LEAVE_ATOMIC_MODE_SET, > @@ -451,54 +451,4 @@ drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) > > #endif > > -/** > - * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers > - * @a: memory range, users of which are to be removed > - * @name: requesting driver name > - * @primary: also kick vga16fb if present > - * > - * This function removes framebuffer devices (initialized by firmware/bootloader) > - * which use memory range described by @a. If @a is NULL all such devices are > - * removed. > - */ > -static inline int > -drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, > - const char *name, bool primary) > -{ > -#if IS_REACHABLE(CONFIG_FB) > - return remove_conflicting_framebuffers(a, name, primary); > -#else > - return 0; > -#endif > -} > - > -/** > - * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices > - * @pdev: PCI device > - * @name: requesting driver name > - * > - * This function removes framebuffer devices (eg. initialized by firmware) > - * using memory range configured for any of @pdev's memory bars. > - * > - * The function assumes that PCI device with shadowed ROM drives a primary > - * display and so kicks out vga16fb. > - */ > -static inline int > -drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, > - const char *name) > -{ > - int ret = 0; > - > - /* > - * WARNING: Apparently we must kick fbdev drivers before vgacon, > - * otherwise the vga fbdev driver falls over. > - */ > -#if IS_REACHABLE(CONFIG_FB) > - ret = remove_conflicting_pci_framebuffers(pdev, name); > -#endif > - if (ret == 0) > - ret = vga_remove_vgacon(pdev); > - return ret; > -} > - > #endif > -- > 2.30.1 >-- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch