diff options
Diffstat (limited to 'drivers/gpu/drm/drm_gem_vram_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_gem_vram_helper.c | 349 |
1 files changed, 348 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index becf1013e02b..2dae03170d1b 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -1,8 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include <drm/drm_debugfs.h> +#include <drm/drm_device.h> +#include <drm/drm_file.h> #include <drm/drm_gem_ttm_helper.h> #include <drm/drm_gem_vram_helper.h> -#include <drm/drm_device.h> #include <drm/drm_mode.h> #include <drm/drm_prime.h> #include <drm/drm_vram_mm_helper.h> @@ -15,6 +17,11 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * * This library provides a GEM buffer object that is backed by video RAM * (VRAM). It can be used for framebuffer devices with dedicated memory. + * + * The data structure &struct drm_vram_mm and its helpers implement a memory + * manager for simple framebuffer devices with dedicated video memory. Buffer + * objects are either placed in video RAM or evicted to system memory. The rsp. + * buffer object is provided by &struct drm_gem_vram_object. */ /* @@ -736,3 +743,343 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs = { .vunmap = drm_gem_vram_object_vunmap, .print_info = drm_gem_ttm_print_info, }; + +/* + * VRAM memory manager + */ + +/* + * TTM TT + */ + +static void backend_func_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func backend_func = { + .destroy = backend_func_destroy +}; + +/* + * TTM BO device + */ + +static struct ttm_tt *bo_driver_ttm_tt_create(struct ttm_buffer_object *bo, + uint32_t page_flags) +{ + struct ttm_tt *tt; + int ret; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) + return NULL; + + tt->func = &backend_func; + + ret = ttm_tt_init(tt, bo, page_flags); + if (ret < 0) + goto err_ttm_tt_init; + + return tt; + +err_ttm_tt_init: + kfree(tt); + return NULL; +} + +static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + return -EINVAL; + } + return 0; +} + +static void bo_driver_evict_flags(struct ttm_buffer_object *bo, + struct ttm_placement *placement) +{ + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev); + + if (vmm->funcs && vmm->funcs->evict_flags) + vmm->funcs->evict_flags(bo, placement); +} + +static int bo_driver_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev); + + if (!vmm->funcs || !vmm->funcs->verify_access) + return 0; + return vmm->funcs->verify_access(bo, filp); +} + +static void bo_driver_move_notify(struct ttm_buffer_object *bo, + bool evict, + struct ttm_mem_reg *new_mem) +{ + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bo->bdev); + + if (!vmm->funcs || !vmm->funcs->move_notify) + return; + vmm->funcs->move_notify(bo, evict, new_mem); +} + +static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = bdev->man + mem->mem_type; + struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev); + + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + + mem->bus.addr = NULL; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + + switch (mem->mem_type) { + case TTM_PL_SYSTEM: /* nothing to do */ + mem->bus.offset = 0; + mem->bus.base = 0; + mem->bus.is_iomem = false; + break; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = vmm->vram_base; + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void bo_driver_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ } + +static struct ttm_bo_driver bo_driver = { + .ttm_tt_create = bo_driver_ttm_tt_create, + .ttm_tt_populate = ttm_pool_populate, + .ttm_tt_unpopulate = ttm_pool_unpopulate, + .init_mem_type = bo_driver_init_mem_type, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = bo_driver_evict_flags, + .verify_access = bo_driver_verify_access, + .move_notify = bo_driver_move_notify, + .io_mem_reserve = bo_driver_io_mem_reserve, + .io_mem_free = bo_driver_io_mem_free, +}; + +/* + * struct drm_vram_mm + */ + +#if defined(CONFIG_DEBUG_FS) +static int drm_vram_mm_debugfs(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_vram_mm *vmm = node->minor->dev->vram_mm; + struct drm_mm *mm = vmm->bdev.man[TTM_PL_VRAM].priv; + struct ttm_bo_global *glob = vmm->bdev.glob; + struct drm_printer p = drm_seq_file_printer(m); + + spin_lock(&glob->lru_lock); + drm_mm_print(mm, &p); + spin_unlock(&glob->lru_lock); + return 0; +} + +static const struct drm_info_list drm_vram_mm_debugfs_list[] = { + { "vram-mm", drm_vram_mm_debugfs, 0, NULL }, +}; +#endif + +/** + * drm_vram_mm_debugfs_init() - Register VRAM MM debugfs file. + * + * @minor: drm minor device. + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_debugfs_init(struct drm_minor *minor) +{ + int ret = 0; + +#if defined(CONFIG_DEBUG_FS) + ret = drm_debugfs_create_files(drm_vram_mm_debugfs_list, + ARRAY_SIZE(drm_vram_mm_debugfs_list), + minor->debugfs_root, minor); +#endif + return ret; +} +EXPORT_SYMBOL(drm_vram_mm_debugfs_init); + +/** + * drm_vram_mm_init() - Initialize an instance of VRAM MM. + * @vmm: the VRAM MM instance to initialize + * @dev: the DRM device + * @vram_base: the base address of the video memory + * @vram_size: the size of the video memory in bytes + * @funcs: callback functions for buffer objects + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_init(struct drm_vram_mm *vmm, struct drm_device *dev, + uint64_t vram_base, size_t vram_size, + const struct drm_vram_mm_funcs *funcs) +{ + int ret; + + vmm->vram_base = vram_base; + vmm->vram_size = vram_size; + vmm->funcs = funcs; + + ret = ttm_bo_device_init(&vmm->bdev, &bo_driver, + dev->anon_inode->i_mapping, + dev->vma_offset_manager, + true); + if (ret) + return ret; + + ret = ttm_bo_init_mm(&vmm->bdev, TTM_PL_VRAM, vram_size >> PAGE_SHIFT); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drm_vram_mm_init); + +/** + * drm_vram_mm_cleanup() - Cleans up an initialized instance of VRAM MM. + * @vmm: the VRAM MM instance to clean up + */ +void drm_vram_mm_cleanup(struct drm_vram_mm *vmm) +{ + ttm_bo_device_release(&vmm->bdev); +} +EXPORT_SYMBOL(drm_vram_mm_cleanup); + +/** + * drm_vram_mm_mmap() - Helper for implementing &struct file_operations.mmap() + * @filp: the mapping's file structure + * @vma: the mapping's memory area + * @vmm: the VRAM MM instance + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_mmap(struct file *filp, struct vm_area_struct *vma, + struct drm_vram_mm *vmm) +{ + return ttm_bo_mmap(filp, vma, &vmm->bdev); +} +EXPORT_SYMBOL(drm_vram_mm_mmap); + +/* + * Helpers for integration with struct drm_device + */ + +/** + * drm_vram_helper_alloc_mm - Allocates a device's instance of \ + &struct drm_vram_mm + * @dev: the DRM device + * @vram_base: the base address of the video memory + * @vram_size: the size of the video memory in bytes + * @funcs: callback functions for buffer objects + * + * Returns: + * The new instance of &struct drm_vram_mm on success, or + * an ERR_PTR()-encoded errno code otherwise. + */ +struct drm_vram_mm *drm_vram_helper_alloc_mm( + struct drm_device *dev, uint64_t vram_base, size_t vram_size, + const struct drm_vram_mm_funcs *funcs) +{ + int ret; + + if (WARN_ON(dev->vram_mm)) + return dev->vram_mm; + + dev->vram_mm = kzalloc(sizeof(*dev->vram_mm), GFP_KERNEL); + if (!dev->vram_mm) + return ERR_PTR(-ENOMEM); + + ret = drm_vram_mm_init(dev->vram_mm, dev, vram_base, vram_size, funcs); + if (ret) + goto err_kfree; + + return dev->vram_mm; + +err_kfree: + kfree(dev->vram_mm); + dev->vram_mm = NULL; + return ERR_PTR(ret); +} +EXPORT_SYMBOL(drm_vram_helper_alloc_mm); + +/** + * drm_vram_helper_release_mm - Releases a device's instance of \ + &struct drm_vram_mm + * @dev: the DRM device + */ +void drm_vram_helper_release_mm(struct drm_device *dev) +{ + if (!dev->vram_mm) + return; + + drm_vram_mm_cleanup(dev->vram_mm); + kfree(dev->vram_mm); + dev->vram_mm = NULL; +} +EXPORT_SYMBOL(drm_vram_helper_release_mm); + +/* + * Helpers for &struct file_operations + */ + +/** + * drm_vram_mm_file_operations_mmap() - \ + Implements &struct file_operations.mmap() + * @filp: the mapping's file structure + * @vma: the mapping's memory area + * + * Returns: + * 0 on success, or + * a negative error code otherwise. + */ +int drm_vram_mm_file_operations_mmap( + struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv = filp->private_data; + struct drm_device *dev = file_priv->minor->dev; + + if (WARN_ONCE(!dev->vram_mm, "VRAM MM not initialized")) + return -EINVAL; + + return drm_vram_mm_mmap(filp, vma, dev->vram_mm); +} +EXPORT_SYMBOL(drm_vram_mm_file_operations_mmap); |