| // SPDX-License-Identifier: GPL-2.0-or-later |
| |
| #include <drm/drm_device.h> |
| #include <drm/drm_file.h> |
| #include <drm/drm_vram_mm_helper.h> |
| |
| #include <drm/ttm/ttm_page_alloc.h> |
| |
| /** |
| * DOC: overview |
| * |
| * 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. These |
| * helper functions work well with &struct drm_gem_vram_object. |
| */ |
| |
| /* |
| * 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 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, |
| .io_mem_reserve = bo_driver_io_mem_reserve, |
| .io_mem_free = bo_driver_io_mem_free, |
| }; |
| |
| /* |
| * struct drm_vram_mm |
| */ |
| |
| /** |
| * 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, |
| 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); |