On Wed, Nov 16, 2016 at 8:43 AM, Rongrong Zou <zourongrong@xxxxxxxxx> wrote: > Hibmc have 32m video memory which can be accessed through PCIe by host, > we use ttm to manage these memory. > > Signed-off-by: Rongrong Zou <zourongrong@xxxxxxxxx> Reviewed-by: Sean Paul <seanpaul@xxxxxxxxxxxx> > --- > drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +- > drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 14 + > drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 41 ++ > drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c | 485 ++++++++++++++++++++++++ > 4 files changed, 541 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c > > diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile > index 47962a0..19ed0ef 100644 > --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile > +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile > @@ -1,4 +1,4 @@ > ccflags-y := -Iinclude/drm > -hibmc-drm-y := hibmc_drm_drv.o > +hibmc-drm-y := hibmc_drm_drv.o hibmc_ttm.o > > obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o > diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c > index 6d20580..521f69f 100644 > --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c > +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c > @@ -28,6 +28,7 @@ > .release = drm_release, > .unlocked_ioctl = drm_ioctl, > .compat_ioctl = drm_compat_ioctl, > + .mmap = hibmc_mmap, > .poll = drm_poll, > .read = drm_read, > .llseek = no_llseek, > @@ -43,6 +44,7 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe) > } > > static struct drm_driver hibmc_driver = { > + .driver_features = DRIVER_GEM, > .fops = &hibmc_fops, > .name = "hibmc", > .date = "20160828", > @@ -52,6 +54,10 @@ static void hibmc_disable_vblank(struct drm_device *dev, unsigned int pipe) > .get_vblank_counter = drm_vblank_no_hw_counter, > .enable_vblank = hibmc_enable_vblank, > .disable_vblank = hibmc_disable_vblank, > + .gem_free_object_unlocked = hibmc_gem_free_object, > + .dumb_create = hibmc_dumb_create, > + .dumb_map_offset = hibmc_dumb_mmap_offset, > + .dumb_destroy = drm_gem_dumb_destroy, > }; > > static int hibmc_pm_suspend(struct device *dev) > @@ -194,6 +200,10 @@ static int hibmc_hw_init(struct hibmc_drm_private *priv) > > static int hibmc_unload(struct drm_device *dev) > { > + struct hibmc_drm_private *priv = dev->dev_private; > + > + hibmc_mm_fini(priv); > + dev->dev_private = NULL; > return 0; > } > > @@ -214,6 +224,10 @@ static int hibmc_load(struct drm_device *dev) > if (ret) > goto err; > > + ret = hibmc_mm_init(priv); > + if (ret) > + goto err; > + > return 0; > > err: > diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h > index 840cd5a..dcd304d 100644 > --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h > +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h > @@ -20,6 +20,8 @@ > #define HIBMC_DRM_DRV_H > > #include <drm/drmP.h> > +#include <drm/drm_gem.h> > +#include <drm/ttm/ttm_bo_driver.h> > > struct hibmc_drm_private { > /* hw */ > @@ -31,11 +33,50 @@ struct hibmc_drm_private { > /* drm */ > struct drm_device *dev; > > + /* ttm */ > + struct drm_global_reference mem_global_ref; > + struct ttm_bo_global_ref bo_global_ref; > + struct ttm_bo_device bdev; > + bool initialized; > + > + bool mm_inited; > +}; > + > +struct hibmc_bo { > + struct ttm_buffer_object bo; > + struct ttm_placement placement; > + struct ttm_bo_kmap_obj kmap; > + struct drm_gem_object gem; > + struct ttm_place placements[3]; > + int pin_count; > }; > > +static inline struct hibmc_bo *hibmc_bo(struct ttm_buffer_object *bo) > +{ > + return container_of(bo, struct hibmc_bo, bo); > +} > + > +static inline struct hibmc_bo *gem_to_hibmc_bo(struct drm_gem_object *gem) > +{ > + return container_of(gem, struct hibmc_bo, gem); > +} > + > void hibmc_set_power_mode(struct hibmc_drm_private *priv, > unsigned int power_mode); > void hibmc_set_current_gate(struct hibmc_drm_private *priv, > unsigned int gate); > > +int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel, > + struct drm_gem_object **obj); > +int hibmc_mm_init(struct hibmc_drm_private *hibmc); > +void hibmc_mm_fini(struct hibmc_drm_private *hibmc); > +int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr); > +int hibmc_bo_unpin(struct hibmc_bo *bo); > +void hibmc_gem_free_object(struct drm_gem_object *obj); > +int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, > + struct drm_mode_create_dumb *args); > +int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, > + u32 handle, u64 *offset); > +int hibmc_mmap(struct file *filp, struct vm_area_struct *vma); > + > #endif > diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c > new file mode 100644 > index 0000000..036d3ac > --- /dev/null > +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_ttm.c > @@ -0,0 +1,485 @@ > +/* Hisilicon Hibmc SoC drm driver > + * > + * Based on the bochs drm driver. > + * > + * Copyright (c) 2016 Huawei Limited. > + * > + * Author: > + * Rongrong Zou <zourongrong@xxxxxxxxxx> > + * Rongrong Zou <zourongrong@xxxxxxxxx> > + * Jianhua Li <lijianhua@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + */ > + > +#include <ttm/ttm_page_alloc.h> > + > +#include "hibmc_drm_drv.h" > + > +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) > + > +static inline struct hibmc_drm_private * > +hibmc_bdev(struct ttm_bo_device *bd) > +{ > + return container_of(bd, struct hibmc_drm_private, bdev); > +} > + > +static int > +hibmc_ttm_mem_global_init(struct drm_global_reference *ref) > +{ > + return ttm_mem_global_init(ref->object); > +} > + > +static void > +hibmc_ttm_mem_global_release(struct drm_global_reference *ref) > +{ > + ttm_mem_global_release(ref->object); > +} > + > +static int hibmc_ttm_global_init(struct hibmc_drm_private *hibmc) > +{ > + int ret; > + > + hibmc->mem_global_ref.global_type = DRM_GLOBAL_TTM_MEM; > + hibmc->mem_global_ref.size = sizeof(struct ttm_mem_global); > + hibmc->mem_global_ref.init = &hibmc_ttm_mem_global_init; > + hibmc->mem_global_ref.release = &hibmc_ttm_mem_global_release; > + ret = drm_global_item_ref(&hibmc->mem_global_ref); > + if (ret) { > + DRM_ERROR("could not get ref on ttm global: %d\n", ret); > + return ret; > + } > + > + hibmc->bo_global_ref.mem_glob = > + hibmc->mem_global_ref.object; > + hibmc->bo_global_ref.ref.global_type = DRM_GLOBAL_TTM_BO; > + hibmc->bo_global_ref.ref.size = sizeof(struct ttm_bo_global); > + hibmc->bo_global_ref.ref.init = &ttm_bo_global_init; > + hibmc->bo_global_ref.ref.release = &ttm_bo_global_release; > + ret = drm_global_item_ref(&hibmc->bo_global_ref.ref); > + if (ret) { > + DRM_ERROR("failed setting up TTM BO subsystem: %d\n", ret); > + drm_global_item_unref(&hibmc->mem_global_ref); > + return ret; > + } > + return 0; > +} > + > +static void > +hibmc_ttm_global_release(struct hibmc_drm_private *hibmc) > +{ > + drm_global_item_unref(&hibmc->bo_global_ref.ref); > + drm_global_item_unref(&hibmc->mem_global_ref); > + hibmc->mem_global_ref.release = NULL; > +} > + > +static void hibmc_bo_ttm_destroy(struct ttm_buffer_object *tbo) > +{ > + struct hibmc_bo *bo = container_of(tbo, struct hibmc_bo, bo); > + > + drm_gem_object_release(&bo->gem); > + kfree(bo); > +} > + > +static bool hibmc_ttm_bo_is_hibmc_bo(struct ttm_buffer_object *bo) > +{ > + return bo->destroy == &hibmc_bo_ttm_destroy; > +} > + > +static int > +hibmc_bo_init_mem_type(struct ttm_bo_device *bdev, u32 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: > + DRM_ERROR("unsupported memory type %u\n", type); > + return -EINVAL; > + } > + return 0; > +} > + > +void hibmc_ttm_placement(struct hibmc_bo *bo, int domain) > +{ > + u32 count = 0; > + u32 i; > + > + bo->placement.placement = bo->placements; > + bo->placement.busy_placement = bo->placements; > + if (domain & TTM_PL_FLAG_VRAM) > + bo->placements[count++].flags = TTM_PL_FLAG_WC | > + TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; > + if (domain & TTM_PL_FLAG_SYSTEM) > + bo->placements[count++].flags = TTM_PL_MASK_CACHING | > + TTM_PL_FLAG_SYSTEM; > + if (!count) > + bo->placements[count++].flags = TTM_PL_MASK_CACHING | > + TTM_PL_FLAG_SYSTEM; > + > + bo->placement.num_placement = count; > + bo->placement.num_busy_placement = count; > + for (i = 0; i < count; i++) { > + bo->placements[i].fpfn = 0; > + bo->placements[i].lpfn = 0; > + } > +} > + > +static void > +hibmc_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) > +{ > + struct hibmc_bo *hibmcbo = hibmc_bo(bo); > + > + if (!hibmc_ttm_bo_is_hibmc_bo(bo)) > + return; > + > + hibmc_ttm_placement(hibmcbo, TTM_PL_FLAG_SYSTEM); > + *pl = hibmcbo->placement; > +} > + > +static int hibmc_bo_verify_access(struct ttm_buffer_object *bo, > + struct file *filp) > +{ > + struct hibmc_bo *hibmcbo = hibmc_bo(bo); > + > + return drm_vma_node_verify_access(&hibmcbo->gem.vma_node, > + filp->private_data); > +} > + > +static int hibmc_ttm_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 hibmc_drm_private *hibmc = hibmc_bdev(bdev); > + > + mem->bus.addr = NULL; > + mem->bus.offset = 0; > + mem->bus.size = mem->num_pages << PAGE_SHIFT; > + mem->bus.base = 0; > + mem->bus.is_iomem = false; > + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) > + return -EINVAL; > + switch (mem->mem_type) { > + case TTM_PL_SYSTEM: > + /* system memory */ > + return 0; > + case TTM_PL_VRAM: > + mem->bus.offset = mem->start << PAGE_SHIFT; > + mem->bus.base = pci_resource_start(hibmc->dev->pdev, 0); > + mem->bus.is_iomem = true; > + break; > + default: > + return -EINVAL; > + } > + return 0; > +} > + > +static void hibmc_ttm_backend_destroy(struct ttm_tt *tt) > +{ > + ttm_tt_fini(tt); > + kfree(tt); > +} > + > +static struct ttm_backend_func hibmc_tt_backend_func = { > + .destroy = &hibmc_ttm_backend_destroy, > +}; > + > +static struct ttm_tt *hibmc_ttm_tt_create(struct ttm_bo_device *bdev, > + unsigned long size, > + u32 page_flags, > + struct page *dummy_read_page) > +{ > + struct ttm_tt *tt; > + int ret; > + > + tt = kzalloc(sizeof(*tt), GFP_KERNEL); > + if (!tt) { > + DRM_ERROR("failed to allocate ttm_tt\n"); > + return NULL; > + } > + tt->func = &hibmc_tt_backend_func; > + ret = ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page); > + if (ret) { > + DRM_ERROR("failed to initialize ttm_tt: %d\n", ret); > + kfree(tt); > + return NULL; > + } > + return tt; > +} > + > +static int hibmc_ttm_tt_populate(struct ttm_tt *ttm) > +{ > + return ttm_pool_populate(ttm); > +} > + > +static void hibmc_ttm_tt_unpopulate(struct ttm_tt *ttm) > +{ > + ttm_pool_unpopulate(ttm); > +} > + > +struct ttm_bo_driver hibmc_bo_driver = { > + .ttm_tt_create = hibmc_ttm_tt_create, > + .ttm_tt_populate = hibmc_ttm_tt_populate, > + .ttm_tt_unpopulate = hibmc_ttm_tt_unpopulate, > + .init_mem_type = hibmc_bo_init_mem_type, > + .evict_flags = hibmc_bo_evict_flags, > + .move = NULL, > + .verify_access = hibmc_bo_verify_access, > + .io_mem_reserve = &hibmc_ttm_io_mem_reserve, > + .io_mem_free = NULL, > + .lru_tail = &ttm_bo_default_lru_tail, > + .swap_lru_tail = &ttm_bo_default_swap_lru_tail, > +}; > + > +int hibmc_mm_init(struct hibmc_drm_private *hibmc) > +{ > + int ret; > + struct drm_device *dev = hibmc->dev; > + struct ttm_bo_device *bdev = &hibmc->bdev; > + > + ret = hibmc_ttm_global_init(hibmc); > + if (ret) > + return ret; > + > + ret = ttm_bo_device_init(&hibmc->bdev, > + hibmc->bo_global_ref.ref.object, > + &hibmc_bo_driver, > + dev->anon_inode->i_mapping, > + DRM_FILE_PAGE_OFFSET, > + true); > + if (ret) { > + hibmc_ttm_global_release(hibmc); > + DRM_ERROR("error initializing bo driver: %d\n", ret); > + return ret; > + } > + > + ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, > + hibmc->fb_size >> PAGE_SHIFT); > + if (ret) { > + hibmc_ttm_global_release(hibmc); > + DRM_ERROR("failed ttm VRAM init: %d\n", ret); > + return ret; > + } > + > + hibmc->mm_inited = true; > + return 0; > +} > + > +void hibmc_mm_fini(struct hibmc_drm_private *hibmc) > +{ > + if (!hibmc->mm_inited) > + return; > + > + ttm_bo_device_release(&hibmc->bdev); > + hibmc_ttm_global_release(hibmc); > + hibmc->mm_inited = false; > +} > + > +static void hibmc_bo_unref(struct hibmc_bo **bo) > +{ > + struct ttm_buffer_object *tbo; > + > + if ((*bo) == NULL) > + return; > + > + tbo = &((*bo)->bo); > + ttm_bo_unref(&tbo); > + *bo = NULL; > +} > + > +int hibmc_bo_create(struct drm_device *dev, int size, int align, > + u32 flags, struct hibmc_bo **phibmcbo) > +{ > + struct hibmc_drm_private *hibmc = dev->dev_private; > + struct hibmc_bo *hibmcbo; > + size_t acc_size; > + int ret; > + > + hibmcbo = kzalloc(sizeof(*hibmcbo), GFP_KERNEL); > + if (!hibmcbo) { > + DRM_ERROR("failed to allocate hibmcbo\n"); > + return -ENOMEM; > + } > + ret = drm_gem_object_init(dev, &hibmcbo->gem, size); > + if (ret) { > + DRM_ERROR("failed to initialize drm gem object: %d\n", ret); > + kfree(hibmcbo); > + return ret; > + } > + > + hibmcbo->bo.bdev = &hibmc->bdev; > + > + hibmc_ttm_placement(hibmcbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); > + > + acc_size = ttm_bo_dma_acc_size(&hibmc->bdev, size, > + sizeof(struct hibmc_bo)); > + > + ret = ttm_bo_init(&hibmc->bdev, &hibmcbo->bo, size, > + ttm_bo_type_device, &hibmcbo->placement, > + align >> PAGE_SHIFT, false, NULL, acc_size, > + NULL, NULL, hibmc_bo_ttm_destroy); > + if (ret) { > + hibmc_bo_unref(&hibmcbo); > + DRM_ERROR("failed to initialize ttm_bo: %d\n", ret); > + return ret; > + } > + > + *phibmcbo = hibmcbo; > + return 0; > +} > + > +int hibmc_bo_pin(struct hibmc_bo *bo, u32 pl_flag, u64 *gpu_addr) > +{ > + int i, ret; > + > + if (bo->pin_count) { > + bo->pin_count++; > + if (gpu_addr) > + *gpu_addr = bo->bo.offset; > + return 0; > + } > + > + hibmc_ttm_placement(bo, pl_flag); > + for (i = 0; i < bo->placement.num_placement; i++) > + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; > + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); > + if (ret) > + return ret; > + > + bo->pin_count = 1; > + if (gpu_addr) > + *gpu_addr = bo->bo.offset; > + return 0; > +} > + > +int hibmc_bo_unpin(struct hibmc_bo *bo) > +{ > + int i, ret; > + > + if (!bo->pin_count) { > + DRM_ERROR("unpin bad %p\n", bo); > + return 0; > + } > + bo->pin_count--; > + if (bo->pin_count) > + return 0; > + > + for (i = 0; i < bo->placement.num_placement ; i++) > + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; > + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); > + if (ret) { > + DRM_ERROR("validate failed for unpin: %d\n", ret); > + return ret; > + } > + > + return 0; > +} > + > +int hibmc_mmap(struct file *filp, struct vm_area_struct *vma) > +{ > + struct drm_file *file_priv; > + struct hibmc_drm_private *hibmc; > + > + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) > + return -EINVAL; > + > + file_priv = filp->private_data; > + hibmc = file_priv->minor->dev->dev_private; > + return ttm_bo_mmap(filp, vma, &hibmc->bdev); > +} > + > +int hibmc_gem_create(struct drm_device *dev, u32 size, bool iskernel, > + struct drm_gem_object **obj) > +{ > + struct hibmc_bo *hibmcbo; > + int ret; > + > + *obj = NULL; > + > + size = PAGE_ALIGN(size); > + if (size == 0) { > + DRM_ERROR("error: zero size\n"); > + return -EINVAL; > + } > + > + ret = hibmc_bo_create(dev, size, 0, 0, &hibmcbo); > + if (ret) { > + if (ret != -ERESTARTSYS) > + DRM_ERROR("failed to allocate GEM object: %d\n", ret); > + return ret; > + } > + *obj = &hibmcbo->gem; > + return 0; > +} > + > +int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, > + struct drm_mode_create_dumb *args) > +{ > + struct drm_gem_object *gobj; > + u32 handle; > + int ret; > + > + args->pitch = ALIGN(args->width * DIV_ROUND_UP(args->bpp, 8), 16); > + args->size = args->pitch * args->height; > + > + ret = hibmc_gem_create(dev, args->size, false, > + &gobj); > + if (ret) { > + DRM_ERROR("failed to create GEM object: %d\n", ret); > + return ret; > + } > + > + ret = drm_gem_handle_create(file, gobj, &handle); > + drm_gem_object_unreference_unlocked(gobj); > + if (ret) { > + DRM_ERROR("failed to unreference GEM object: %d\n", ret); > + return ret; > + } > + > + args->handle = handle; > + return 0; > +} > + > +void hibmc_gem_free_object(struct drm_gem_object *obj) > +{ > + struct hibmc_bo *hibmcbo = gem_to_hibmc_bo(obj); > + > + hibmc_bo_unref(&hibmcbo); > +} > + > +static u64 hibmc_bo_mmap_offset(struct hibmc_bo *bo) > +{ > + return drm_vma_node_offset_addr(&bo->bo.vma_node); > +} > + > +int hibmc_dumb_mmap_offset(struct drm_file *file, struct drm_device *dev, > + u32 handle, u64 *offset) > +{ > + struct drm_gem_object *obj; > + struct hibmc_bo *bo; > + > + obj = drm_gem_object_lookup(file, handle); > + if (!obj) > + return -ENOENT; > + > + bo = gem_to_hibmc_bo(obj); > + *offset = hibmc_bo_mmap_offset(bo); > + > + drm_gem_object_unreference_unlocked(obj); > + return 0; > +} > -- > 1.9.1 > _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel