mirror of https://gitee.com/openkylin/linux.git
298 lines
6.9 KiB
C
298 lines
6.9 KiB
C
// 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);
|