blob: 354f659345214b43b1d41d0bcfc3f217fad50616 [file] [log] [blame]
/* Copyright (c) 2009-2013, 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 <linux/msm_ion.h>
#include <linux/genlock.h>
#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(
0xffffffff, "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);
}