18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * psb GEM interface 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011, Intel Corporation. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Alan Cox 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * TODO: 108c2ecf20Sopenharmony_ci * - we need to work out if the MMU is relevant (eg for 118c2ecf20Sopenharmony_ci * accelerated operations on a GEM object) 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <drm/drm.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_vma_manager.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "psb_drv.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_civoid psb_gem_free_object(struct drm_gem_object *obj) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* Remove the list map if one is present */ 268c2ecf20Sopenharmony_ci drm_gem_free_mmap_offset(obj); 278c2ecf20Sopenharmony_ci drm_gem_object_release(obj); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci /* This must occur last as it frees up the memory of the GEM object */ 308c2ecf20Sopenharmony_ci psb_gtt_free_range(obj->dev, gtt); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciint psb_gem_get_aperture(struct drm_device *dev, void *data, 348c2ecf20Sopenharmony_ci struct drm_file *file) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci return -EINVAL; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/** 408c2ecf20Sopenharmony_ci * psb_gem_create - create a mappable object 418c2ecf20Sopenharmony_ci * @file: the DRM file of the client 428c2ecf20Sopenharmony_ci * @dev: our device 438c2ecf20Sopenharmony_ci * @size: the size requested 448c2ecf20Sopenharmony_ci * @handlep: returned handle (opaque number) 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * Create a GEM object, fill in the boilerplate and attach a handle to 478c2ecf20Sopenharmony_ci * it so that userspace can speak about it. This does the core work 488c2ecf20Sopenharmony_ci * for the various methods that do/will create GEM objects for things 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ciint psb_gem_create(struct drm_file *file, struct drm_device *dev, u64 size, 518c2ecf20Sopenharmony_ci u32 *handlep, int stolen, u32 align) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct gtt_range *r; 548c2ecf20Sopenharmony_ci int ret; 558c2ecf20Sopenharmony_ci u32 handle; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci size = roundup(size, PAGE_SIZE); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* Allocate our object - for now a direct gtt range which is not 608c2ecf20Sopenharmony_ci stolen memory backed */ 618c2ecf20Sopenharmony_ci r = psb_gtt_alloc_range(dev, size, "gem", 0, PAGE_SIZE); 628c2ecf20Sopenharmony_ci if (r == NULL) { 638c2ecf20Sopenharmony_ci dev_err(dev->dev, "no memory for %lld byte GEM object\n", size); 648c2ecf20Sopenharmony_ci return -ENOSPC; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci /* Initialize the extra goodies GEM needs to do all the hard work */ 678c2ecf20Sopenharmony_ci if (drm_gem_object_init(dev, &r->gem, size) != 0) { 688c2ecf20Sopenharmony_ci psb_gtt_free_range(dev, r); 698c2ecf20Sopenharmony_ci /* GEM doesn't give an error code so use -ENOMEM */ 708c2ecf20Sopenharmony_ci dev_err(dev->dev, "GEM init failed for %lld\n", size); 718c2ecf20Sopenharmony_ci return -ENOMEM; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci /* Limit the object to 32bit mappings */ 748c2ecf20Sopenharmony_ci mapping_set_gfp_mask(r->gem.filp->f_mapping, GFP_KERNEL | __GFP_DMA32); 758c2ecf20Sopenharmony_ci /* Give the object a handle so we can carry it more easily */ 768c2ecf20Sopenharmony_ci ret = drm_gem_handle_create(file, &r->gem, &handle); 778c2ecf20Sopenharmony_ci if (ret) { 788c2ecf20Sopenharmony_ci dev_err(dev->dev, "GEM handle failed for %p, %lld\n", 798c2ecf20Sopenharmony_ci &r->gem, size); 808c2ecf20Sopenharmony_ci drm_gem_object_release(&r->gem); 818c2ecf20Sopenharmony_ci psb_gtt_free_range(dev, r); 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci /* We have the initial and handle reference but need only one now */ 858c2ecf20Sopenharmony_ci drm_gem_object_put(&r->gem); 868c2ecf20Sopenharmony_ci *handlep = handle; 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * psb_gem_dumb_create - create a dumb buffer 928c2ecf20Sopenharmony_ci * @drm_file: our client file 938c2ecf20Sopenharmony_ci * @dev: our device 948c2ecf20Sopenharmony_ci * @args: the requested arguments copied from userspace 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Allocate a buffer suitable for use for a frame buffer of the 978c2ecf20Sopenharmony_ci * form described by user space. Give userspace a handle by which 988c2ecf20Sopenharmony_ci * to reference it. 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ciint psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, 1018c2ecf20Sopenharmony_ci struct drm_mode_create_dumb *args) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64); 1048c2ecf20Sopenharmony_ci args->size = args->pitch * args->height; 1058c2ecf20Sopenharmony_ci return psb_gem_create(file, dev, args->size, &args->handle, 0, 1068c2ecf20Sopenharmony_ci PAGE_SIZE); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/** 1108c2ecf20Sopenharmony_ci * psb_gem_fault - pagefault handler for GEM objects 1118c2ecf20Sopenharmony_ci * @vma: the VMA of the GEM object 1128c2ecf20Sopenharmony_ci * @vmf: fault detail 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * Invoked when a fault occurs on an mmap of a GEM managed area. GEM 1158c2ecf20Sopenharmony_ci * does most of the work for us including the actual map/unmap calls 1168c2ecf20Sopenharmony_ci * but we need to do the actual page work. 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * This code eventually needs to handle faulting objects in and out 1198c2ecf20Sopenharmony_ci * of the GTT and repacking it when we run out of space. We can put 1208c2ecf20Sopenharmony_ci * that off for now and for our simple uses 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * The VMA was set up by GEM. In doing so it also ensured that the 1238c2ecf20Sopenharmony_ci * vma->vm_private_data points to the GEM object that is backing this 1248c2ecf20Sopenharmony_ci * mapping. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_civm_fault_t psb_gem_fault(struct vm_fault *vmf) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 1298c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 1308c2ecf20Sopenharmony_ci struct gtt_range *r; 1318c2ecf20Sopenharmony_ci int err; 1328c2ecf20Sopenharmony_ci vm_fault_t ret; 1338c2ecf20Sopenharmony_ci unsigned long pfn; 1348c2ecf20Sopenharmony_ci pgoff_t page_offset; 1358c2ecf20Sopenharmony_ci struct drm_device *dev; 1368c2ecf20Sopenharmony_ci struct drm_psb_private *dev_priv; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci obj = vma->vm_private_data; /* GEM object */ 1398c2ecf20Sopenharmony_ci dev = obj->dev; 1408c2ecf20Sopenharmony_ci dev_priv = dev->dev_private; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci r = container_of(obj, struct gtt_range, gem); /* Get the gtt range */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Make sure we don't parallel update on a fault, nor move or remove 1458c2ecf20Sopenharmony_ci something from beneath our feet */ 1468c2ecf20Sopenharmony_ci mutex_lock(&dev_priv->mmap_mutex); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* For now the mmap pins the object and it stays pinned. As things 1498c2ecf20Sopenharmony_ci stand that will do us no harm */ 1508c2ecf20Sopenharmony_ci if (r->mmapping == 0) { 1518c2ecf20Sopenharmony_ci err = psb_gtt_pin(r); 1528c2ecf20Sopenharmony_ci if (err < 0) { 1538c2ecf20Sopenharmony_ci dev_err(dev->dev, "gma500: pin failed: %d\n", err); 1548c2ecf20Sopenharmony_ci ret = vmf_error(err); 1558c2ecf20Sopenharmony_ci goto fail; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci r->mmapping = 1; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Page relative to the VMA start - we must calculate this ourselves 1618c2ecf20Sopenharmony_ci because vmf->pgoff is the fake GEM offset */ 1628c2ecf20Sopenharmony_ci page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* CPU view of the page, don't go via the GART for CPU writes */ 1658c2ecf20Sopenharmony_ci if (r->stolen) 1668c2ecf20Sopenharmony_ci pfn = (dev_priv->stolen_base + r->offset) >> PAGE_SHIFT; 1678c2ecf20Sopenharmony_ci else 1688c2ecf20Sopenharmony_ci pfn = page_to_pfn(r->pages[page_offset]); 1698c2ecf20Sopenharmony_ci ret = vmf_insert_pfn(vma, vmf->address, pfn); 1708c2ecf20Sopenharmony_cifail: 1718c2ecf20Sopenharmony_ci mutex_unlock(&dev_priv->mmap_mutex); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci} 175