/* Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* Implements an interface between KGSL and the DRM subsystem. For now this * is pretty simple, but it will take on more of the workload as time goes * on */ #include "drmP.h" #include "drm.h" #include #include #include "kgsl.h" #include "kgsl_device.h" #include "kgsl_drm.h" #include "kgsl_mmu.h" #include "kgsl_sharedmem.h" #define DRIVER_AUTHOR "Qualcomm" #define DRIVER_NAME "kgsl" #define DRIVER_DESC "KGSL DRM" #define DRIVER_DATE "20121107" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 #define DRIVER_PATCHLEVEL 1 #define DRM_KGSL_GEM_FLAG_MAPPED (1 << 0) #define DRM_KGSL_NOT_INITED -1 #define DRM_KGSL_INITED 1 /* Returns true if the memory type is in PMEM */ #ifdef CONFIG_KERNEL_PMEM_SMI_REGION #define TYPE_IS_PMEM(_t) \ (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_EBI) || \ ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_SMI) || \ ((_t) & DRM_KGSL_GEM_TYPE_PMEM)) #else #define TYPE_IS_PMEM(_t) \ (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_EBI) || \ ((_t) & (DRM_KGSL_GEM_TYPE_PMEM | DRM_KGSL_GEM_PMEM_EBI))) #endif /* Returns true if the memory type is regular */ #define TYPE_IS_MEM(_t) \ (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM) || \ ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE) || \ ((_t) & DRM_KGSL_GEM_TYPE_MEM)) #define TYPE_IS_FD(_t) ((_t) & DRM_KGSL_GEM_TYPE_FD_MASK) /* Returns true if KMEM region is uncached */ #define IS_MEM_UNCACHED(_t) \ (((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_KMEM_NOCACHE)) /* Returns true if memory type is secure */ #define TYPE_IS_SECURE(_t) \ ((_t & DRM_KGSL_GEM_TYPE_MEM_MASK) == DRM_KGSL_GEM_TYPE_MEM_SECURE) struct drm_kgsl_gem_object { struct drm_gem_object *obj; uint32_t type; struct kgsl_memdesc memdesc; struct kgsl_pagetable *pagetable; struct ion_handle *ion_handle; uint64_t mmap_offset; int bufcount; int flags; struct list_head list; int active; struct { uint32_t offset; uint32_t gpuaddr; } bufs[DRM_KGSL_GEM_MAX_BUFFERS]; struct genlock_handle *glock_handle[DRM_KGSL_GEM_MAX_BUFFERS]; int bound; int lockpid; /* * Userdata to indicate * Last operation done READ/WRITE * Last user of this memory CPU/GPU */ uint32_t user_pdata; }; static struct ion_client *kgsl_drm_ion_client; static int kgsl_drm_inited = DRM_KGSL_NOT_INITED; /* This is a global list of all the memory currently mapped in the MMU */ static struct list_head kgsl_mem_list; struct kgsl_drm_device_priv { struct kgsl_device *device[KGSL_DEVICE_MAX]; struct kgsl_device_private *devpriv[KGSL_DEVICE_MAX]; }; static int kgsl_gem_memory_allocated(struct drm_gem_object *obj) { struct drm_kgsl_gem_object *priv = obj->driver_private; return priv->memdesc.size ? 1 : 0; } static int kgsl_gem_alloc_memory(struct drm_gem_object *obj) { struct drm_kgsl_gem_object *priv = obj->driver_private; struct kgsl_mmu *mmu; struct sg_table *sg_table; struct scatterlist *s; int index; int result = 0; int mem_flags = 0; /* Return if the memory is already allocated */ if (kgsl_gem_memory_allocated(obj) || TYPE_IS_FD(priv->type)) return 0; if (priv->pagetable == NULL) { mmu = &kgsl_get_device(KGSL_DEVICE_3D0)->mmu; priv->pagetable = kgsl_mmu_getpagetable(mmu, KGSL_MMU_GLOBAL_PT); if (priv->pagetable == NULL) { DRM_ERROR("Unable to get the GPU MMU pagetable\n"); return -EINVAL; } } if (TYPE_IS_PMEM(priv->type)) { if (priv->type == DRM_KGSL_GEM_TYPE_EBI || priv->type & DRM_KGSL_GEM_PMEM_EBI) { priv->ion_handle = ion_alloc(kgsl_drm_ion_client, obj->size * priv->bufcount, PAGE_SIZE, ION_HEAP(ION_SF_HEAP_ID), 0); if (IS_ERR_OR_NULL(priv->ion_handle)) { DRM_ERROR( "Unable to allocate ION Phys memory handle\n"); return -ENOMEM; } priv->memdesc.pagetable = priv->pagetable; result = ion_phys(kgsl_drm_ion_client, priv->ion_handle, (ion_phys_addr_t *) &priv->memdesc.physaddr, &priv->memdesc.size); if (result) { DRM_ERROR( "Unable to get ION Physical memory address\n"); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; return result; } result = memdesc_sg_phys(&priv->memdesc, priv->memdesc.physaddr, priv->memdesc.size); if (result) { DRM_ERROR( "Unable to get sg list\n"); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; return result; } result = kgsl_mmu_get_gpuaddr(priv->pagetable, &priv->memdesc); if (result) { DRM_ERROR( "kgsl_mmu_get_gpuaddr failed. result = %d\n", result); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; return result; } result = kgsl_mmu_map(priv->pagetable, &priv->memdesc); if (result) { DRM_ERROR( "kgsl_mmu_map failed. result = %d\n", result); kgsl_mmu_put_gpuaddr(priv->pagetable, &priv->memdesc); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; return result; } } else return -EINVAL; } else if (TYPE_IS_MEM(priv->type)) { if (priv->type == DRM_KGSL_GEM_TYPE_KMEM || priv->type & DRM_KGSL_GEM_CACHE_MASK) list_add(&priv->list, &kgsl_mem_list); priv->memdesc.pagetable = priv->pagetable; if (!IS_MEM_UNCACHED(priv->type)) mem_flags = ION_FLAG_CACHED; priv->ion_handle = ion_alloc(kgsl_drm_ion_client, obj->size * priv->bufcount, PAGE_SIZE, ION_HEAP(ION_IOMMU_HEAP_ID), mem_flags); if (IS_ERR_OR_NULL(priv->ion_handle)) { DRM_ERROR( "Unable to allocate ION IOMMU memory handle\n"); return -ENOMEM; } sg_table = ion_sg_table(kgsl_drm_ion_client, priv->ion_handle); if (IS_ERR_OR_NULL(priv->ion_handle)) { DRM_ERROR( "Unable to get ION sg table\n"); goto memerr; } priv->memdesc.sg = sg_table->sgl; /* Calculate the size of the memdesc from the sglist */ priv->memdesc.sglen = 0; for (s = priv->memdesc.sg; s != NULL; s = sg_next(s)) { priv->memdesc.size += s->length; priv->memdesc.sglen++; } result = kgsl_mmu_get_gpuaddr(priv->pagetable, &priv->memdesc); if (result) { DRM_ERROR( "kgsl_mmu_get_gpuaddr failed. result = %d\n", result); goto memerr; } result = kgsl_mmu_map(priv->pagetable, &priv->memdesc); if (result) { DRM_ERROR( "kgsl_mmu_map failed. result = %d\n", result); kgsl_mmu_put_gpuaddr(priv->pagetable, &priv->memdesc); goto memerr; } } else if (TYPE_IS_SECURE(priv->type)) { priv->memdesc.pagetable = priv->pagetable; priv->ion_handle = ion_alloc(kgsl_drm_ion_client, obj->size * priv->bufcount, PAGE_SIZE, ION_HEAP(ION_CP_MM_HEAP_ID), ION_FLAG_SECURE); if (IS_ERR_OR_NULL(priv->ion_handle)) { DRM_ERROR("Unable to allocate ION_SECURE memory\n"); return -ENOMEM; } sg_table = ion_sg_table(kgsl_drm_ion_client, priv->ion_handle); if (IS_ERR_OR_NULL(priv->ion_handle)) { DRM_ERROR("Unable to get ION sg table\n"); goto memerr; } priv->memdesc.sg = sg_table->sgl; priv->memdesc.sglen = 0; for (s = priv->memdesc.sg; s != NULL; s = sg_next(s)) { priv->memdesc.size += s->length; priv->memdesc.sglen++; } /* Skip GPU map for secure buffer */ } else return -EINVAL; for (index = 0; index < priv->bufcount; index++) { priv->bufs[index].offset = index * obj->size; priv->bufs[index].gpuaddr = priv->memdesc.gpuaddr + priv->bufs[index].offset; } priv->flags |= DRM_KGSL_GEM_FLAG_MAPPED; return 0; memerr: ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; return -ENOMEM; } static void kgsl_gem_free_memory(struct drm_gem_object *obj) { struct drm_kgsl_gem_object *priv = obj->driver_private; int index; if (!kgsl_gem_memory_allocated(obj) || TYPE_IS_FD(priv->type)) return; if (priv->memdesc.gpuaddr) { kgsl_mmu_unmap(priv->memdesc.pagetable, &priv->memdesc); kgsl_mmu_put_gpuaddr(priv->memdesc.pagetable, &priv->memdesc); } /* ION will take care of freeing the sg table. */ priv->memdesc.sg = NULL; priv->memdesc.sglen = 0; if (priv->ion_handle) ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; memset(&priv->memdesc, 0, sizeof(priv->memdesc)); for (index = 0; index < priv->bufcount; index++) { if (priv->glock_handle[index]) genlock_put_handle(priv->glock_handle[index]); } kgsl_mmu_putpagetable(priv->pagetable); priv->pagetable = NULL; if ((priv->type == DRM_KGSL_GEM_TYPE_KMEM) || (priv->type & DRM_KGSL_GEM_CACHE_MASK)) list_del(&priv->list); priv->flags &= ~DRM_KGSL_GEM_FLAG_MAPPED; } int kgsl_gem_init_object(struct drm_gem_object *obj) { struct drm_kgsl_gem_object *priv; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) { DRM_ERROR("Unable to create GEM object\n"); return -ENOMEM; } obj->driver_private = priv; priv->obj = obj; return 0; } void kgsl_gem_free_object(struct drm_gem_object *obj) { kgsl_gem_free_memory(obj); drm_gem_object_release(obj); kfree(obj->driver_private); kfree(obj); } int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start, unsigned long *len) { struct file *filp; struct drm_device *dev; struct drm_file *file_priv; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret = 0; filp = fget(drm_fd); if (unlikely(filp == NULL)) { DRM_ERROR("Unable to get the DRM file descriptor\n"); return -EINVAL; } file_priv = filp->private_data; if (unlikely(file_priv == NULL)) { DRM_ERROR("Unable to get the file private data\n"); fput(filp); return -EINVAL; } dev = file_priv->minor->dev; if (unlikely(dev == NULL)) { DRM_ERROR("Unable to get the minor device\n"); fput(filp); return -EINVAL; } obj = drm_gem_object_lookup(dev, file_priv, handle); if (unlikely(obj == NULL)) { DRM_ERROR("Invalid GEM handle %x\n", handle); fput(filp); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; /* We can only use the MDP for PMEM regions */ if (TYPE_IS_PMEM(priv->type)) { *start = priv->memdesc.physaddr + priv->bufs[priv->active].offset; *len = priv->memdesc.size; } else { *start = 0; *len = 0; ret = -EINVAL; } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); fput(filp); return ret; } static int kgsl_gem_init_obj(struct drm_device *dev, struct drm_file *file_priv, struct drm_gem_object *obj, int *handle) { struct drm_kgsl_gem_object *priv; int ret; mutex_lock(&dev->struct_mutex); priv = obj->driver_private; memset(&priv->memdesc, 0, sizeof(priv->memdesc)); priv->bufcount = 1; priv->active = 0; priv->bound = 0; priv->type = DRM_KGSL_GEM_TYPE_KMEM; ret = drm_gem_handle_create(file_priv, obj, handle); drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_create *create = data; struct drm_gem_object *obj; int ret, handle; /* Page align the size so we can allocate multiple buffers */ create->size = ALIGN(create->size, 4096); obj = drm_gem_object_alloc(dev, create->size); if (obj == NULL) { DRM_ERROR("Unable to allocate the GEM object\n"); return -ENOMEM; } ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle); if (ret) { drm_gem_object_release(obj); kfree(obj->driver_private); kfree(obj); DRM_ERROR("Unable to initialize GEM object ret = %d\n", ret); return ret; } create->handle = handle; return 0; } int kgsl_gem_create_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_create_fd *args = data; struct file *file; dev_t rdev; struct fb_info *info; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret, put_needed, handle; file = fget_light(args->fd, &put_needed); if (file == NULL) { DRM_ERROR("Unable to get the file object\n"); return -EBADF; } rdev = file->f_dentry->d_inode->i_rdev; /* Only framebuffer objects are supported ATM */ if (MAJOR(rdev) != FB_MAJOR) { DRM_ERROR("File descriptor is not a framebuffer\n"); ret = -EBADF; goto error_fput; } info = registered_fb[MINOR(rdev)]; if (info == NULL) { DRM_ERROR("Framebuffer minor %d is not registered\n", MINOR(rdev)); ret = -EBADF; goto error_fput; } obj = drm_gem_object_alloc(dev, info->fix.smem_len); if (obj == NULL) { DRM_ERROR("Unable to allocate GEM object\n"); ret = -ENOMEM; goto error_fput; } ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle); if (ret) { drm_gem_object_release(obj); kfree(obj->driver_private); kfree(obj); goto error_fput; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; priv->memdesc.physaddr = info->fix.smem_start; priv->type = DRM_KGSL_GEM_TYPE_FD_FBMEM; mutex_unlock(&dev->struct_mutex); args->handle = handle; error_fput: fput_light(file, put_needed); return ret; } int kgsl_gem_create_from_ion_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_create_from_ion *args = data; struct drm_gem_object *obj; struct ion_handle *ion_handle; struct drm_kgsl_gem_object *priv; struct sg_table *sg_table; struct scatterlist *s; int ret, handle; unsigned long size; struct kgsl_mmu *mmu; ion_handle = ion_import_dma_buf(kgsl_drm_ion_client, args->ion_fd); if (IS_ERR_OR_NULL(ion_handle)) { DRM_ERROR("Unable to import dmabuf. Error number = %d\n", (int)PTR_ERR(ion_handle)); return -EINVAL; } ion_handle_get_size(kgsl_drm_ion_client, ion_handle, &size); if (size == 0) { ion_free(kgsl_drm_ion_client, ion_handle); DRM_ERROR( "cannot create GEM object from zero size ION buffer\n"); return -EINVAL; } obj = drm_gem_object_alloc(dev, size); if (obj == NULL) { ion_free(kgsl_drm_ion_client, ion_handle); DRM_ERROR("Unable to allocate the GEM object\n"); return -ENOMEM; } ret = kgsl_gem_init_obj(dev, file_priv, obj, &handle); if (ret) { ion_free(kgsl_drm_ion_client, ion_handle); drm_gem_object_release(obj); kfree(obj->driver_private); kfree(obj); DRM_ERROR("Unable to initialize GEM object ret = %d\n", ret); return ret; } priv = obj->driver_private; priv->ion_handle = ion_handle; priv->type = DRM_KGSL_GEM_TYPE_KMEM; list_add(&priv->list, &kgsl_mem_list); mmu = &kgsl_get_device(KGSL_DEVICE_3D0)->mmu; priv->pagetable = kgsl_mmu_getpagetable(mmu, KGSL_MMU_GLOBAL_PT); priv->memdesc.pagetable = priv->pagetable; sg_table = ion_sg_table(kgsl_drm_ion_client, priv->ion_handle); if (IS_ERR_OR_NULL(priv->ion_handle)) { DRM_ERROR("Unable to get ION sg table\n"); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; kgsl_mmu_putpagetable(priv->pagetable); drm_gem_object_release(obj); kfree(priv); kfree(obj); return -ENOMEM; } priv->memdesc.sg = sg_table->sgl; /* Calculate the size of the memdesc from the sglist */ priv->memdesc.sglen = 0; for (s = priv->memdesc.sg; s != NULL; s = sg_next(s)) { priv->memdesc.size += s->length; priv->memdesc.sglen++; } ret = kgsl_mmu_get_gpuaddr(priv->pagetable, &priv->memdesc); if (ret) { DRM_ERROR("kgsl_mmu_get_gpuaddr failed. ret = %d\n", ret); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; kgsl_mmu_putpagetable(priv->pagetable); drm_gem_object_release(obj); kfree(priv); kfree(obj); return -ENOMEM; } ret = kgsl_mmu_map(priv->pagetable, &priv->memdesc); if (ret) { DRM_ERROR("kgsl_mmu_map failed. ret = %d\n", ret); kgsl_mmu_put_gpuaddr(priv->pagetable, &priv->memdesc); ion_free(kgsl_drm_ion_client, priv->ion_handle); priv->ion_handle = NULL; kgsl_mmu_putpagetable(priv->pagetable); drm_gem_object_release(obj); kfree(priv); kfree(obj); return -ENOMEM; } priv->bufs[0].offset = 0; priv->bufs[0].gpuaddr = priv->memdesc.gpuaddr; priv->flags |= DRM_KGSL_GEM_FLAG_MAPPED; args->handle = handle; return 0; } int kgsl_gem_get_ion_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_get_ion_fd *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret = 0; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; if (TYPE_IS_FD(priv->type)) ret = -EINVAL; else if (TYPE_IS_PMEM(priv->type) || TYPE_IS_MEM(priv->type) || TYPE_IS_SECURE(priv->type)) { if (priv->ion_handle) { args->ion_fd = ion_share_dma_buf_fd( kgsl_drm_ion_client, priv->ion_handle); if (args->ion_fd < 0) { DRM_ERROR( "Could not share ion buffer. Error = %d\n", args->ion_fd); ret = -EINVAL; } } else { DRM_ERROR("GEM object has no ion memory allocated.\n"); ret = -EINVAL; } } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_setmemtype_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_memtype *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret = 0; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; if (TYPE_IS_FD(priv->type)) ret = -EINVAL; else { if (TYPE_IS_PMEM(args->type) || TYPE_IS_MEM(args->type) || TYPE_IS_SECURE(args->type)) priv->type = args->type; else ret = -EINVAL; } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_getmemtype_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_memtype *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; args->type = priv->type; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_unbind_gpu_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { return 0; } int kgsl_gem_bind_gpu_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { return 0; } /* Allocate the memory and prepare it for CPU mapping */ int kgsl_gem_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_alloc *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; ret = kgsl_gem_alloc_memory(obj); if (ret) { DRM_ERROR("Unable to allocate object memory\n"); } args->offset = 0; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_mmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { /* Ion is used for mmap at this time */ return 0; } /* This function is deprecated */ int kgsl_gem_prep_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_prep *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; ret = kgsl_gem_alloc_memory(obj); if (ret) { DRM_ERROR("Unable to allocate object memory\n"); drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_get_bufinfo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_bufinfo *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret = -EINVAL; int index; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; if (!kgsl_gem_memory_allocated(obj)) { DRM_ERROR("Memory not allocated for this object\n"); goto out; } for (index = 0; index < priv->bufcount; index++) { args->offset[index] = priv->bufs[index].offset; args->gpuaddr[index] = priv->bufs[index].gpuaddr; } args->count = priv->bufcount; args->active = priv->active; ret = 0; out: drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } /* Get the genlock handles base off the GEM handle */ int kgsl_gem_get_glock_handles_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_glockinfo *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int index; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; for (index = 0; index < priv->bufcount; index++) { args->glockhandle[index] = genlock_get_fd_handle( priv->glock_handle[index]); } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_set_glock_handles_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_glockinfo *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int index; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; for (index = 0; index < priv->bufcount; index++) { priv->glock_handle[index] = genlock_get_handle_fd( args->glockhandle[index]); } drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_set_bufcount_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_bufcount *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret = -EINVAL; if (args->bufcount < 1 || args->bufcount > DRM_KGSL_GEM_MAX_BUFFERS) return -EINVAL; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; /* It is too much math to worry about what happens if we are already allocated, so just bail if we are */ if (kgsl_gem_memory_allocated(obj)) { DRM_ERROR("Memory already allocated - cannot change" "number of buffers\n"); goto out; } priv->bufcount = args->bufcount; ret = 0; out: drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_get_bufcount_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_bufcount *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; args->bufcount = priv->bufcount; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_set_userdata(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_userdata *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; priv->user_pdata = args->priv_data; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_get_userdata(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_userdata *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; args->priv_data = priv->user_pdata; drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_cache_ops(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_cache_ops *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; unsigned int cache_op = 0; int ret = 0; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); if (!kgsl_gem_memory_allocated(obj)) { DRM_ERROR("Object isn't allocated,can't perform Cache ops.\n"); ret = -EPERM; goto done; } priv = obj->driver_private; if (IS_MEM_UNCACHED(priv->type)) goto done; switch (args->flags) { case DRM_KGSL_GEM_CLEAN_CACHES: cache_op = ION_IOC_CLEAN_CACHES; break; case DRM_KGSL_GEM_INV_CACHES: cache_op = ION_IOC_INV_CACHES; break; case DRM_KGSL_GEM_CLEAN_INV_CACHES: cache_op = ION_IOC_CLEAN_INV_CACHES; break; default: DRM_ERROR("Invalid Cache operation\n"); ret = -EINVAL; goto done; } if ((obj->size != args->length)) { DRM_ERROR("Invalid buffer size "); ret = -EINVAL; goto done; } ret = msm_ion_do_cache_op(kgsl_drm_ion_client, priv->ion_handle, args->vaddr, args->length, cache_op); if (ret) DRM_ERROR("Cache operation failed\n"); done: drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_set_active_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_kgsl_gem_active *args = data; struct drm_gem_object *obj; struct drm_kgsl_gem_object *priv; int ret = -EINVAL; obj = drm_gem_object_lookup(dev, file_priv, args->handle); if (obj == NULL) { DRM_ERROR("Invalid GEM handle %x\n", args->handle); return -EBADF; } mutex_lock(&dev->struct_mutex); priv = obj->driver_private; if (args->active < 0 || args->active >= priv->bufcount) { DRM_ERROR("Invalid active buffer %d\n", args->active); goto out; } priv->active = args->active; ret = 0; out: drm_gem_object_unreference(obj); mutex_unlock(&dev->struct_mutex); return ret; } int kgsl_gem_kmem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; struct drm_kgsl_gem_object *priv; unsigned long offset; struct page *page; int i; mutex_lock(&dev->struct_mutex); priv = obj->driver_private; offset = (unsigned long) vmf->virtual_address - vma->vm_start; i = offset >> PAGE_SHIFT; page = sg_page(&(priv->memdesc.sg[i])); if (!page) { mutex_unlock(&dev->struct_mutex); return VM_FAULT_SIGBUS; } get_page(page); vmf->page = page; mutex_unlock(&dev->struct_mutex); return 0; } int kgsl_gem_phys_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; struct drm_device *dev = obj->dev; struct drm_kgsl_gem_object *priv; unsigned long offset, pfn; int ret = 0; offset = ((unsigned long) vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT; mutex_lock(&dev->struct_mutex); priv = obj->driver_private; pfn = (priv->memdesc.physaddr >> PAGE_SHIFT) + offset; ret = vm_insert_pfn(vma, (unsigned long) vmf->virtual_address, pfn); mutex_unlock(&dev->struct_mutex); switch (ret) { case -ENOMEM: case -EAGAIN: return VM_FAULT_OOM; case -EFAULT: return VM_FAULT_SIGBUS; default: return VM_FAULT_NOPAGE; } } struct drm_ioctl_desc kgsl_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE, kgsl_gem_create_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_PREP, kgsl_gem_prep_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_SETMEMTYPE, kgsl_gem_setmemtype_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_GETMEMTYPE, kgsl_gem_getmemtype_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_BIND_GPU, kgsl_gem_bind_gpu_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_UNBIND_GPU, kgsl_gem_unbind_gpu_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_ALLOC, kgsl_gem_alloc_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_MMAP, kgsl_gem_mmap_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_BUFINFO, kgsl_gem_get_bufinfo_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_ION_FD, kgsl_gem_get_ion_fd_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE_FROM_ION, kgsl_gem_create_from_ion_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_BUFCOUNT, kgsl_gem_set_bufcount_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_BUFCOUNT, kgsl_gem_get_bufcount_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_GLOCK_HANDLES_INFO, kgsl_gem_set_glock_handles_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_GLOCK_HANDLES_INFO, kgsl_gem_get_glock_handles_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_ACTIVE, kgsl_gem_set_active_ioctl, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_SET_USERDATA, kgsl_gem_set_userdata, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_GET_USERDATA, kgsl_gem_get_userdata, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_CACHE_OPS, kgsl_gem_cache_ops, 0), DRM_IOCTL_DEF_DRV(KGSL_GEM_CREATE_FD, kgsl_gem_create_fd_ioctl, DRM_MASTER), }; static const struct file_operations kgsl_drm_driver_fops = { .owner = THIS_MODULE, .open = drm_open, .release = drm_release, .unlocked_ioctl = drm_ioctl, .mmap = drm_gem_mmap, .poll = drm_poll, .fasync = drm_fasync, }; static struct drm_driver driver = { .driver_features = DRIVER_GEM, .gem_init_object = kgsl_gem_init_object, .gem_free_object = kgsl_gem_free_object, .ioctls = kgsl_drm_ioctls, .fops = &kgsl_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; int kgsl_drm_init(struct platform_device *dev) { /* Only initialize once */ if (kgsl_drm_inited == DRM_KGSL_INITED) return 0; kgsl_drm_inited = DRM_KGSL_INITED; driver.num_ioctls = DRM_ARRAY_SIZE(kgsl_drm_ioctls); INIT_LIST_HEAD(&kgsl_mem_list); /* Create ION Client */ kgsl_drm_ion_client = msm_ion_client_create("kgsl_drm"); if (!kgsl_drm_ion_client) { DRM_ERROR("Unable to create ION client\n"); return -ENOMEM; } return drm_platform_init(&driver, dev); } void kgsl_drm_exit(void) { kgsl_drm_inited = DRM_KGSL_NOT_INITED; if (kgsl_drm_ion_client) ion_client_destroy(kgsl_drm_ion_client); kgsl_drm_ion_client = NULL; drm_platform_exit(&driver, driver.kdriver.platform_device); }