[PATCH 01/15] drm: Add |struct drm_gem_ttm_object| and helpers

Thomas Zimmermann tzimmermann at suse.de
Mon Apr 8 09:21:30 UTC 2019


The type |struct drm_gem_ttm_object| implements a TTM buffer object
for simple framebuffer devices with dedicated video memory. The BO
is either located in VRAM or system memory. The implementation has
been created from the respective code in ast, bochs and mgag200. These
drivers copy their implementation from each other; except for the
names of several data types.

Signed-off-by: Thomas Zimmermann <tzimmermann at suse.de>
---
 Documentation/gpu/drm-mm.rst            |  12 +
 drivers/gpu/drm/Kconfig                 |  13 +
 drivers/gpu/drm/Makefile                |   4 +
 drivers/gpu/drm/drm_gem_ttm_helper.c    | 367 ++++++++++++++++++++++++
 drivers/gpu/drm/drm_ttm_helper_common.c |   6 +
 include/drm/drm_gem_ttm_helper.h        |  91 ++++++
 6 files changed, 493 insertions(+)
 create mode 100644 drivers/gpu/drm/drm_gem_ttm_helper.c
 create mode 100644 drivers/gpu/drm/drm_ttm_helper_common.c
 create mode 100644 include/drm/drm_gem_ttm_helper.h

diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst
index 54a696d961a7..596e5f15f459 100644
--- a/Documentation/gpu/drm-mm.rst
+++ b/Documentation/gpu/drm-mm.rst
@@ -380,6 +380,18 @@ GEM CMA Helper Functions Reference
 .. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
    :export:
 
+GEM TTM Helper Functions Reference
+----------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_gem_ttm_helper.c
+   :doc: overview
+
+.. kernel-doc:: include/drm/drm_gem_ttm_helper.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_gem_ttm_helper.c
+   :export:
+
 VMA Offset Manager
 ==================
 
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index bcbc4234893a..684bd327eb07 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -160,6 +160,12 @@ config DRM_TTM
 	  GPU memory types. Will be enabled automatically if a device driver
 	  uses it.
 
+config DRM_TTM_HELPER
+	tristate
+	depends on DRM && DRM_TTM
+	help
+	  Helpers for TTM-based memory management
+
 config DRM_GEM_CMA_HELPER
 	bool
 	depends on DRM
@@ -179,6 +185,13 @@ config DRM_GEM_SHMEM_HELPER
 	help
 	  Choose this if you need the GEM shmem helper functions
 
+config DRM_GEM_TTM_HELPER
+	bool
+	depends on DRM
+	select DRM_TTM_HELPER
+	help
+	  Choose this if you need the GEM TTM helper functions
+
 config DRM_VM
 	bool
 	depends on DRM && MMU
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index d97c0a3a8cfc..acd0f3e4f079 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -33,6 +33,10 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o
 drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
 drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 
+drm_ttm_helper-y := drm_ttm_helper_common.o
+drm_ttm_helper-$(CONFIG_DRM_GEM_TTM_HELPER) += drm_gem_ttm_helper.o
+obj-$(CONFIG_DRM_TTM_HELPER) += drm_ttm_helper.o
+
 drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_dsc.o drm_probe_helper.o \
 		drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
 		drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
diff --git a/drivers/gpu/drm/drm_gem_ttm_helper.c b/drivers/gpu/drm/drm_gem_ttm_helper.c
new file mode 100644
index 000000000000..d52d3c439917
--- /dev/null
+++ b/drivers/gpu/drm/drm_gem_ttm_helper.c
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <drm/drm_gem_ttm_helper.h>
+#include <drm/ttm/ttm_page_alloc.h>
+
+/**
+ * DOC: overview
+ *
+ * This library provides a GEM object that is backed by TTM-managed
+ * memory. It can be used for simple framebuffer devices with dedicated
+ * memory.
+ */
+
+/*
+ * Buffer-objects helpers
+ */
+
+static void drm_gem_ttm_cleanup(struct drm_gem_ttm_object *gbo)
+{
+	/* We got here via ttm_bo_put(), which means that the
+	 * TTM buffer object in 'bo' has already been cleaned
+	 * up; only release the GEM object. */
+	drm_gem_object_release(&gbo->gem);
+}
+
+static void drm_gem_ttm_destroy(struct drm_gem_ttm_object *gbo)
+{
+	drm_gem_ttm_cleanup(gbo);
+	kfree(gbo);
+}
+
+static void ttm_buffer_object_destroy(struct ttm_buffer_object *bo)
+{
+	struct drm_gem_ttm_object *gbo = drm_gem_ttm_of_bo(bo);
+	drm_gem_ttm_destroy(gbo);
+}
+
+static void drm_gem_ttm_placement(struct drm_gem_ttm_object *gbo, int pl_flag)
+{
+	unsigned int i;
+	unsigned int c = 0;
+
+	gbo->placement.placement = gbo->placements;
+	gbo->placement.busy_placement = gbo->placements;
+
+	if (pl_flag & TTM_PL_FLAG_VRAM)
+		gbo->placements[c++].flags = TTM_PL_FLAG_WC |
+					     TTM_PL_FLAG_UNCACHED |
+					     TTM_PL_FLAG_VRAM;
+
+	if (pl_flag & TTM_PL_FLAG_SYSTEM)
+		gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
+					     TTM_PL_FLAG_SYSTEM;
+
+	if (!c)
+		gbo->placements[c++].flags = TTM_PL_MASK_CACHING |
+					     TTM_PL_FLAG_SYSTEM;
+
+	gbo->placement.num_placement = c;
+	gbo->placement.num_busy_placement = c;
+
+	for (i = 0; i < c; ++i) {
+		gbo->placements[i].fpfn = 0;
+		gbo->placements[i].lpfn = 0;
+	}
+}
+
+static int drm_gem_ttm_init(struct drm_device *dev,
+			    struct ttm_bo_device *bdev,
+			    struct drm_gem_ttm_object *gbo,
+			    unsigned long size, uint32_t pg_align,
+			    bool interruptible)
+{
+	int ret;
+	size_t acc_size;
+
+	ret = drm_gem_object_init(dev, &gbo->gem, size);
+	if (ret)
+		return ret;
+
+	acc_size = ttm_bo_dma_acc_size(bdev, size, sizeof(*gbo));
+
+	gbo->bo.bdev = bdev;
+	drm_gem_ttm_placement(gbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
+
+	ret = ttm_bo_init(bdev, &gbo->bo, size, ttm_bo_type_device,
+			  &gbo->placement, pg_align, interruptible, acc_size,
+			  NULL, NULL, ttm_buffer_object_destroy);
+	if (ret)
+		goto err_drm_gem_object_release;
+
+	return 0;
+
+err_drm_gem_object_release:
+	drm_gem_object_release(&gbo->gem);
+	return ret;
+}
+
+/**
+ * drm_gem_ttm_create() - Creates a TTM-backed GEM object
+ * @dev:		the DRM device
+ * @bdev:		the TTM BO device backing the object
+ * @size:		the buffer size in bytes
+ * @pg_align:		the buffer's alignment in multiples of the page size
+ * @interruptible:	sleep interruptible if waiting for memory
+ *
+ * Returns:
+ * A new instance of struct_gem_ttm_object on success, or
+ * an ERR_PTR-encoded error code otherwise.
+ */
+struct drm_gem_ttm_object* drm_gem_ttm_create(struct drm_device *dev,
+					      struct ttm_bo_device *bdev,
+					      unsigned long size,
+					      uint32_t pg_align,
+					      bool interruptible)
+{
+	struct drm_gem_ttm_object *gbo;
+	int ret;
+
+	gbo = kzalloc(sizeof(*gbo), GFP_KERNEL);
+	if (!gbo)
+		return ERR_PTR(-ENOMEM);
+
+	ret = drm_gem_ttm_init(dev, bdev, gbo, size, pg_align, interruptible);
+	if (ret < 0)
+		goto err_kfree;
+
+	return gbo;
+
+err_kfree:
+	kfree(gbo);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(drm_gem_ttm_create);
+
+/**
+ * drm_gem_ttm_put() - Releases a reference to a TTM-backed GEM object
+ * @gbo:	the GEM-TTM object
+ *
+ * See ttm_bo_put() for more information.
+ */
+void drm_gem_ttm_put(struct drm_gem_ttm_object *gbo)
+{
+	ttm_bo_put(&gbo->bo);
+}
+EXPORT_SYMBOL(drm_gem_ttm_put);
+
+/**
+ * drm_gem_ttm_reserve() - Reserves a TTM-backed GEM object
+ * @gbo:	the GEM-TTM object
+ * @no_wait:	don't wait for buffer object to become available
+ *
+ * See ttm_bo_reserve() for more information.
+ *
+ * Returns:
+ * 0 on success, or
+ * a negative error code otherwise
+ */
+int drm_gem_ttm_reserve(struct drm_gem_ttm_object *gbo, bool no_wait)
+{
+	return ttm_bo_reserve(&gbo->bo, true, no_wait, NULL);
+}
+EXPORT_SYMBOL(drm_gem_ttm_reserve);
+
+/**
+ * drm_gem_ttm_unreserve() - Release a reservation acquired by drm_gem_ttm_reserve
+ * @gbo:	the GEM TTM object
+ *
+ * See ttm_bo_unreserve() for more information.
+ */
+void drm_gem_ttm_unreserve(struct drm_gem_ttm_object *gbo)
+{
+	ttm_bo_unreserve(&gbo->bo);
+}
+EXPORT_SYMBOL(drm_gem_ttm_unreserve);
+
+/**
+ * drm_gem_ttm_mmap_offset() - Returns a GEM TTM object's mmap offset
+ * @gbo:	the GEM TTM object
+ *
+ * See drm_vma_node_offset_addr() for more information.
+ *
+ * Returns:
+ * The buffer object's offset for userspace mappings on success, or
+ * 0 if no offset is allocated.
+ */
+u64 drm_gem_ttm_mmap_offset(struct drm_gem_ttm_object *gbo)
+{
+	return drm_vma_node_offset_addr(&gbo->bo.vma_node);
+}
+EXPORT_SYMBOL(drm_gem_ttm_mmap_offset);
+
+/**
+ * drm_gem_ttm_vram_offset() - Returns a GEM-TTM object's offset in video memory
+ * @gbo:	the GEM TTM object
+ *
+ * This function returns the buffer object's offset in the device's video
+ * memory. The buffer object has to be pinned to TT_PL_VRAM.
+ *
+ * Returns:
+ * The buffer object's offset in video memory on success, or
+ * a negative error code otherwise.
+ */
+s64 drm_gem_ttm_vram_offset(struct drm_gem_ttm_object *gbo)
+{
+	if (!gbo->pin_count)
+		return (s64)-ENODEV;
+	return gbo->bo.offset;
+}
+EXPORT_SYMBOL(drm_gem_ttm_vram_offset);
+
+/**
+ * drm_gem_ttm_pin() - Pins a GEM-TTM object in a region
+ * @gbo:	the GEM TTM object
+ * @pl_flag:	a bitmask of possible memory regions
+ *
+ * Pinning a buffer object ensures that it is not evicted from
+ * a memory region. A pinned buffer object has to be unpinned before
+ * it can be pinned to another region.
+ *
+ * Returns:
+ * 0 on success, or
+ * a negative error code otherwise.
+ */
+int drm_gem_ttm_pin(struct drm_gem_ttm_object *gbo, u32 pl_flag)
+{
+	int i, ret;
+	struct ttm_operation_ctx ctx = { false, false };
+
+	if (gbo->pin_count) {
+		++gbo->pin_count;
+		return 0;
+	}
+
+	drm_gem_ttm_placement(gbo, pl_flag);
+	for (i = 0; i < gbo->placement.num_placement; ++i)
+		gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
+
+	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
+	if (ret < 0)
+		return ret;
+
+	gbo->pin_count = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_gem_ttm_pin);
+
+/**
+ * drm_gem_ttm_unpin() - Unpins a GEM-TTM object
+ * @gbo:	the GEM TTM object
+ *
+ * Returns:
+ * 0 on success, or
+ * a negative error code otherwise.
+ */
+int drm_gem_ttm_unpin(struct drm_gem_ttm_object *gbo)
+{
+	int i, ret;
+	struct ttm_operation_ctx ctx = { false, false };
+
+	if (!gbo->pin_count)
+		return 0;
+
+	--gbo->pin_count;
+	if (gbo->pin_count)
+		return 0;
+
+	for (i = 0; i < gbo->placement.num_placement ; ++i)
+		gbo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
+
+	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_gem_ttm_unpin);
+
+/**
+ * drm_gem_ttm_push_to_system() - \
+	Unpins a GEM TTM object and moves it to system memory
+ * @gbo:	the GEM TTM object
+ *
+ * This operation only works if the caller holds the final pin on the
+ * buffer object.
+ *
+ * Returns:
+ * 0 on success, or
+ * a negative error code otherwise.
+ */
+int drm_gem_ttm_push_to_system(struct drm_gem_ttm_object *gbo)
+{
+	int i, ret;
+	struct ttm_operation_ctx ctx = { false, false };
+
+	if (!gbo->pin_count)
+		return 0;
+
+	--gbo->pin_count;
+	if (gbo->pin_count)
+		return 0;
+
+	if (gbo->kmap.virtual)
+		ttm_bo_kunmap(&gbo->kmap);
+
+	drm_gem_ttm_placement(gbo, TTM_PL_FLAG_SYSTEM);
+	for (i = 0; i < gbo->placement.num_placement ; ++i)
+		gbo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT;
+
+	ret = ttm_bo_validate(&gbo->bo, &gbo->placement, &ctx);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_gem_ttm_push_to_system);
+
+/**
+ * drm_gem_ttm_kmap() - Maps a GEM TTM object into kernel address space.
+ * @gbo:	the GEM TTM object
+ * @map:	establish a mapping if necessary
+ *
+ * This function maps the buffer object into the kernel's address space
+ * or returns the current mapping. If the parameter map is false, the
+ * function only queries the current mapping, but does not establish a
+ * new one.
+ *
+ * Returns:
+ * The buffers virtual address if mapped, or
+ * NULL is not mapped, or
+ * an ERR_PTR()-encoded error code otherwise.
+ */
+void* drm_gem_ttm_kmap(struct drm_gem_ttm_object *gbo, bool map)
+{
+	int ret;
+
+	if (gbo->kmap.virtual || !map)
+		return gbo->kmap.virtual;
+
+	ret = ttm_bo_kmap(&gbo->bo, 0, gbo->bo.num_pages, &gbo->kmap);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return gbo->kmap.virtual;
+}
+EXPORT_SYMBOL(drm_gem_ttm_kmap);
+
+/**
+ * drm_gem_ttm_kunmap() - Unmaps a GEM TTM object.
+ * @gbo:	the GEM TTM object
+ */
+void drm_gem_ttm_kunmap(struct drm_gem_ttm_object *gbo)
+{
+	if (!gbo->kmap.virtual)
+		return;
+
+	ttm_bo_kunmap(&gbo->kmap);
+	gbo->kmap.virtual = NULL;
+}
+EXPORT_SYMBOL(drm_gem_ttm_kunmap);
+
+bool drm_is_gem_ttm(struct ttm_buffer_object *bo)
+{
+	return (bo->destroy == ttm_buffer_object_destroy);
+}
+EXPORT_SYMBOL(drm_is_gem_ttm);
diff --git a/drivers/gpu/drm/drm_ttm_helper_common.c b/drivers/gpu/drm/drm_ttm_helper_common.c
new file mode 100644
index 000000000000..cacd0f69facc
--- /dev/null
+++ b/drivers/gpu/drm/drm_ttm_helper_common.c
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <linux/module.h>
+
+MODULE_DESCRIPTION("DRM TTM memory-management helpers");
+MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_gem_ttm_helper.h b/include/drm/drm_gem_ttm_helper.h
new file mode 100644
index 000000000000..662dcb8d4770
--- /dev/null
+++ b/include/drm/drm_gem_ttm_helper.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef DRM_GEM_TTM_HELPER_H
+#define DRM_GEM_TTM_HELPER_H
+
+#include <drm/drm_gem.h>
+#include <drm/ttm/ttm_bo_api.h>
+#include <drm/ttm/ttm_placement.h>
+#include <linux/kernel.h> /* for container_of() */
+
+struct filp;
+
+/*
+ * Buffer-object helpers
+ */
+
+/**
+ * struct drm_gem_ttm_object - GEM object backed by TTM-managed memory
+ *
+ * The type struct drm_gem_ttm_object represents a GEM object that is
+ * backed by TTM-managed memory. It can be used for simple frambuffer
+ * devices with dedicated memory. Current supported memory regions are
+ * TTM_PL_SYSTEM and TTM_PL_VRAM. TTM_PL_TT could probably be added if
+ * necessary.
+ */
+struct drm_gem_ttm_object {
+	/**
+	 * GEM object
+	 */
+        struct drm_gem_object gem;
+
+	/**
+	 * TTM buffer object
+	 */
+        struct ttm_buffer_object bo;
+
+	/**
+	 * Mapping information for bo
+	 */
+        struct ttm_bo_kmap_obj kmap;
+
+	/* Supported placements are VRAM and SYSTEM */
+        struct ttm_placement placement;
+        struct ttm_place placements[3];
+
+        int pin_count;
+};
+
+/**
+ * Returns the container of type struct drm_gem_ttm_object
+ * for field bo.
+ * @bo:		the TTM buffer object
+ * Returns:	The containing GEM-TTM object
+ */
+static inline struct drm_gem_ttm_object* drm_gem_ttm_of_bo(
+	struct ttm_buffer_object *bo)
+{
+	return container_of(bo, struct drm_gem_ttm_object, bo);
+}
+
+/**
+ * Returns the container of type struct drm_gem_ttm_object
+ * for field gem.
+ * @gem:	the GEM object
+ * Returns:	The containing GEM-TTM object
+ */
+static inline struct drm_gem_ttm_object* drm_gem_ttm_of_gem(
+	struct drm_gem_object *gem)
+{
+	return container_of(gem, struct drm_gem_ttm_object, gem);
+}
+
+struct drm_gem_ttm_object* drm_gem_ttm_create(struct drm_device *dev,
+					      struct ttm_bo_device* bdev,
+					      unsigned long size,
+					      uint32_t pg_align,
+					      bool interruptible);
+void drm_gem_ttm_put(struct drm_gem_ttm_object *gbo);
+int drm_gem_ttm_reserve(struct drm_gem_ttm_object *gbo, bool no_wait);
+void drm_gem_ttm_unreserve(struct drm_gem_ttm_object *gbo);
+u64 drm_gem_ttm_mmap_offset(struct drm_gem_ttm_object *gbo);
+s64 drm_gem_ttm_vram_offset(struct drm_gem_ttm_object *gbo);
+int drm_gem_ttm_pin(struct drm_gem_ttm_object *gbo, u32 pl_flag);
+int drm_gem_ttm_unpin(struct drm_gem_ttm_object *gbo);
+int drm_gem_ttm_push_to_system(struct drm_gem_ttm_object *gbo);
+void* drm_gem_ttm_kmap(struct drm_gem_ttm_object *gbo, bool map);
+void drm_gem_ttm_kunmap(struct drm_gem_ttm_object *gbo);
+
+bool drm_is_gem_ttm(struct ttm_buffer_object *bo);
+
+#endif
-- 
2.21.0



More information about the dri-devel mailing list