summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c127
-rw-r--r--drivers/gpu/msm/adreno_drawctxt.c3
-rw-r--r--drivers/gpu/msm/kgsl.c152
-rw-r--r--drivers/gpu/msm/kgsl.h3
-rw-r--r--drivers/gpu/msm/kgsl_compat.c2
-rw-r--r--drivers/gpu/msm/kgsl_device.h15
-rw-r--r--drivers/gpu/msm/kgsl_drawobj.c187
-rw-r--r--drivers/gpu/msm/kgsl_drawobj.h33
-rw-r--r--drivers/gpu/msm/kgsl_ioctl.c2
-rw-r--r--include/uapi/linux/msm_kgsl.h32
10 files changed, 498 insertions, 58 deletions
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index cb4108b4e1f9..18c05e930216 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -359,6 +359,13 @@ static inline void _pop_drawobj(struct adreno_context *drawctxt)
drawctxt->queued--;
}
+static void _retire_sparseobj(struct kgsl_drawobj_sparse *sparseobj,
+ struct adreno_context *drawctxt)
+{
+ kgsl_sparse_bind(drawctxt->base.proc_priv, sparseobj);
+ _retire_timestamp(DRAWOBJ(sparseobj));
+}
+
static int _retire_markerobj(struct kgsl_drawobj_cmd *cmdobj,
struct adreno_context *drawctxt)
{
@@ -436,6 +443,8 @@ static struct kgsl_drawobj *_process_drawqueue_get_next_drawobj(
return drawobj;
} else if (drawobj->type == SYNCOBJ_TYPE)
ret = _retire_syncobj(SYNCOBJ(drawobj), drawctxt);
+ else
+ return ERR_PTR(-EINVAL);
if (ret == -EAGAIN)
return ERR_PTR(-EAGAIN);
@@ -670,6 +679,76 @@ static int sendcmd(struct adreno_device *adreno_dev,
return 0;
}
+
+/*
+ * Retires all sync objs from the sparse context
+ * queue and returns one of the below
+ * a) next sparseobj
+ * b) -EAGAIN for syncobj with syncpoints pending
+ * c) -EINVAL for unexpected drawobj
+ * d) NULL for no sparseobj
+ */
+static struct kgsl_drawobj_sparse *_get_next_sparseobj(
+ struct adreno_context *drawctxt)
+{
+ struct kgsl_drawobj *drawobj;
+ unsigned int i = drawctxt->drawqueue_head;
+ int ret = 0;
+
+ if (drawctxt->drawqueue_head == drawctxt->drawqueue_tail)
+ return NULL;
+
+ for (i = drawctxt->drawqueue_head; i != drawctxt->drawqueue_tail;
+ i = DRAWQUEUE_NEXT(i, ADRENO_CONTEXT_DRAWQUEUE_SIZE)) {
+
+ drawobj = drawctxt->drawqueue[i];
+
+ if (drawobj == NULL)
+ return NULL;
+
+ if (drawobj->type == SYNCOBJ_TYPE)
+ ret = _retire_syncobj(SYNCOBJ(drawobj), drawctxt);
+ else if (drawobj->type == SPARSEOBJ_TYPE)
+ return SPARSEOBJ(drawobj);
+ else
+ return ERR_PTR(-EINVAL);
+
+ if (ret == -EAGAIN)
+ return ERR_PTR(-EAGAIN);
+
+ continue;
+ }
+
+ return NULL;
+}
+
+static int _process_drawqueue_sparse(
+ struct adreno_context *drawctxt)
+{
+ struct kgsl_drawobj_sparse *sparseobj;
+ int ret = 0;
+ unsigned int i;
+
+ for (i = 0; i < ADRENO_CONTEXT_DRAWQUEUE_SIZE; i++) {
+
+ spin_lock(&drawctxt->lock);
+ sparseobj = _get_next_sparseobj(drawctxt);
+ if (IS_ERR_OR_NULL(sparseobj)) {
+ if (IS_ERR(sparseobj))
+ ret = PTR_ERR(sparseobj);
+ spin_unlock(&drawctxt->lock);
+ return ret;
+ }
+
+ _pop_drawobj(drawctxt);
+ spin_unlock(&drawctxt->lock);
+
+ _retire_sparseobj(sparseobj, drawctxt);
+ }
+
+ return 0;
+}
+
/**
* dispatcher_context_sendcmds() - Send commands from a context to the GPU
* @adreno_dev: Pointer to the adreno device struct
@@ -689,6 +768,9 @@ static int dispatcher_context_sendcmds(struct adreno_device *adreno_dev,
int inflight = _drawqueue_inflight(dispatch_q);
unsigned int timestamp;
+ if (drawctxt->base.flags & KGSL_CONTEXT_SPARSE)
+ return _process_drawqueue_sparse(drawctxt);
+
if (dispatch_q->inflight >= inflight) {
spin_lock(&drawctxt->lock);
_process_drawqueue_get_next_drawobj(drawctxt);
@@ -1124,6 +1206,31 @@ static void _queue_drawobj(struct adreno_context *drawctxt,
trace_adreno_cmdbatch_queued(drawobj, drawctxt->queued);
}
+static int _queue_sparseobj(struct adreno_device *adreno_dev,
+ struct adreno_context *drawctxt, struct kgsl_drawobj_sparse *sparseobj,
+ uint32_t *timestamp, unsigned int user_ts)
+{
+ struct kgsl_drawobj *drawobj = DRAWOBJ(sparseobj);
+ int ret;
+
+ ret = get_timestamp(drawctxt, drawobj, timestamp, user_ts);
+ if (ret)
+ return ret;
+
+ /*
+ * See if we can fastpath this thing - if nothing is
+ * queued bind/unbind without queueing the context
+ */
+ if (!drawctxt->queued)
+ return 1;
+
+ drawctxt->queued_timestamp = *timestamp;
+ _queue_drawobj(drawctxt, drawobj);
+
+ return 0;
+}
+
+
static int _queue_markerobj(struct adreno_device *adreno_dev,
struct adreno_context *drawctxt, struct kgsl_drawobj_cmd *markerobj,
uint32_t *timestamp, unsigned int user_ts)
@@ -1141,7 +1248,6 @@ static int _queue_markerobj(struct adreno_device *adreno_dev,
*/
if (!drawctxt->queued && kgsl_check_timestamp(drawobj->device,
drawobj->context, drawctxt->queued_timestamp)) {
- trace_adreno_cmdbatch_queued(drawobj, drawctxt->queued);
_retire_timestamp(drawobj);
return 1;
}
@@ -1212,7 +1318,7 @@ static void _queue_syncobj(struct adreno_context *drawctxt,
}
/**
- * adreno_dispactcher_queue_drawobj() - Queue a new draw object in the context
+ * adreno_dispactcher_queue_cmds() - Queue a new draw object in the context
* @dev_priv: Pointer to the device private struct
* @context: Pointer to the kgsl draw context
* @drawobj: Pointer to the array of drawobj's being submitted
@@ -1234,6 +1340,9 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
int ret;
unsigned int i, user_ts;
+ if (!count)
+ return -EINVAL;
+
ret = _check_context_state(&drawctxt->base);
if (ret)
return ret;
@@ -1283,6 +1392,20 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
_queue_syncobj(drawctxt, SYNCOBJ(drawobj[i]),
timestamp);
break;
+ case SPARSEOBJ_TYPE:
+ ret = _queue_sparseobj(adreno_dev, drawctxt,
+ SPARSEOBJ(drawobj[i]),
+ timestamp, user_ts);
+ if (ret == 1) {
+ spin_unlock(&drawctxt->lock);
+ _retire_sparseobj(SPARSEOBJ(drawobj[i]),
+ drawctxt);
+ return 0;
+ } else if (ret) {
+ spin_unlock(&drawctxt->lock);
+ return ret;
+ }
+ break;
default:
spin_unlock(&drawctxt->lock);
return -EINVAL;
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 3a110ed221a8..1cbd2ef4b6b4 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -351,7 +351,8 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
KGSL_CONTEXT_IFH_NOP |
KGSL_CONTEXT_SECURE |
KGSL_CONTEXT_PREEMPT_STYLE_MASK |
- KGSL_CONTEXT_NO_SNAPSHOT);
+ KGSL_CONTEXT_NO_SNAPSHOT |
+ KGSL_CONTEXT_SPARSE);
/* Check for errors before trying to initialize */
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 993f22b26294..bae3884aa277 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1439,6 +1439,17 @@ long kgsl_ioctl_device_waittimestamp_ctxtid(
return result;
}
+static inline bool _check_context_is_sparse(struct kgsl_context *context,
+ uint64_t flags)
+{
+ if ((context->flags & KGSL_CONTEXT_SPARSE) ||
+ (flags & KGSL_DRAWOBJ_SPARSE))
+ return true;
+
+ return false;
+}
+
+
long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
@@ -1463,6 +1474,11 @@ long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
if (context == NULL)
return -EINVAL;
+ if (_check_context_is_sparse(context, param->flags)) {
+ kgsl_context_put(context);
+ return -EINVAL;
+ }
+
cmdobj = kgsl_drawobj_cmd_create(device, context, param->flags,
CMDOBJ_TYPE);
if (IS_ERR(cmdobj)) {
@@ -1558,6 +1574,11 @@ long kgsl_ioctl_submit_commands(struct kgsl_device_private *dev_priv,
if (context == NULL)
return -EINVAL;
+ if (_check_context_is_sparse(context, param->flags)) {
+ kgsl_context_put(context);
+ return -EINVAL;
+ }
+
if (type & SYNCOBJ_TYPE) {
struct kgsl_drawobj_sync *syncobj =
kgsl_drawobj_sync_create(device, context);
@@ -1632,6 +1653,11 @@ long kgsl_ioctl_gpu_command(struct kgsl_device_private *dev_priv,
if (context == NULL)
return -EINVAL;
+ if (_check_context_is_sparse(context, param->flags)) {
+ kgsl_context_put(context);
+ return -EINVAL;
+ }
+
if (type & SYNCOBJ_TYPE) {
struct kgsl_drawobj_sync *syncobj =
kgsl_drawobj_sync_create(device, context);
@@ -3742,6 +3768,128 @@ long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv,
return ret;
}
+long kgsl_ioctl_gpu_sparse_command(struct kgsl_device_private *dev_priv,
+ unsigned int cmd, void *data)
+{
+ struct kgsl_gpu_sparse_command *param = data;
+ struct kgsl_device *device = dev_priv->device;
+ struct kgsl_context *context;
+ struct kgsl_drawobj *drawobj[2];
+ struct kgsl_drawobj_sparse *sparseobj;
+ long result;
+ unsigned int i = 0;
+
+ /* Make sure sparse and syncpoint count isn't too big */
+ if (param->numsparse > KGSL_MAX_SPARSE ||
+ param->numsyncs > KGSL_MAX_SYNCPOINTS)
+ return -EINVAL;
+
+ /* Make sure there is atleast one sparse or sync */
+ if (param->numsparse == 0 && param->numsyncs == 0)
+ return -EINVAL;
+
+ /* Only Sparse commands are supported in this ioctl */
+ if (!(param->flags & KGSL_DRAWOBJ_SPARSE) || (param->flags &
+ (KGSL_DRAWOBJ_SUBMIT_IB_LIST | KGSL_DRAWOBJ_MARKER
+ | KGSL_DRAWOBJ_SYNC)))
+ return -EINVAL;
+
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
+ if (context == NULL)
+ return -EINVAL;
+
+ /* Restrict bind commands to bind context */
+ if (!(context->flags & KGSL_CONTEXT_SPARSE)) {
+ kgsl_context_put(context);
+ return -EINVAL;
+ }
+
+ if (param->numsyncs) {
+ struct kgsl_drawobj_sync *syncobj = kgsl_drawobj_sync_create(
+ device, context);
+ if (IS_ERR(syncobj)) {
+ result = PTR_ERR(syncobj);
+ goto done;
+ }
+
+ drawobj[i++] = DRAWOBJ(syncobj);
+ result = kgsl_drawobj_sync_add_synclist(device, syncobj,
+ to_user_ptr(param->synclist),
+ param->syncsize, param->numsyncs);
+ if (result)
+ goto done;
+ }
+
+ if (param->numsparse) {
+ sparseobj = kgsl_drawobj_sparse_create(device, context,
+ param->flags);
+ if (IS_ERR(sparseobj)) {
+ result = PTR_ERR(sparseobj);
+ goto done;
+ }
+
+ sparseobj->id = param->id;
+ drawobj[i++] = DRAWOBJ(sparseobj);
+ result = kgsl_drawobj_sparse_add_sparselist(device, sparseobj,
+ param->id, to_user_ptr(param->sparselist),
+ param->sparsesize, param->numsparse);
+ if (result)
+ goto done;
+ }
+
+ result = dev_priv->device->ftbl->queue_cmds(dev_priv, context,
+ drawobj, i, &param->timestamp);
+
+done:
+ /*
+ * -EPROTO is a "success" error - it just tells the user that the
+ * context had previously faulted
+ */
+ if (result && result != -EPROTO)
+ while (i--)
+ kgsl_drawobj_destroy(drawobj[i]);
+
+ kgsl_context_put(context);
+ return result;
+}
+
+void kgsl_sparse_bind(struct kgsl_process_private *private,
+ struct kgsl_drawobj_sparse *sparseobj)
+{
+ struct kgsl_sparseobj_node *sparse_node;
+ struct kgsl_mem_entry *virt_entry = NULL;
+ long ret = 0;
+ char *name;
+
+ virt_entry = kgsl_sharedmem_find_id_flags(private, sparseobj->id,
+ KGSL_MEMFLAGS_SPARSE_VIRT);
+ if (virt_entry == NULL)
+ return;
+
+ list_for_each_entry(sparse_node, &sparseobj->sparselist, node) {
+ if (sparse_node->obj.flags & KGSL_SPARSE_BIND) {
+ ret = sparse_bind_range(private, &sparse_node->obj,
+ virt_entry);
+ name = "bind";
+ } else {
+ ret = sparse_unbind_range(&sparse_node->obj,
+ virt_entry);
+ name = "unbind";
+ }
+
+ if (ret)
+ KGSL_CORE_ERR("kgsl: Unable to '%s' ret %ld virt_id %d, phys_id %d, virt_offset %16.16llX, phys_offset %16.16llX, size %16.16llX, flags %16.16llX\n",
+ name, ret, sparse_node->virt_id,
+ sparse_node->obj.id,
+ sparse_node->obj.virtoffset,
+ sparse_node->obj.physoffset,
+ sparse_node->obj.size, sparse_node->obj.flags);
+ }
+
+ kgsl_mem_entry_put(virt_entry);
+}
+EXPORT_SYMBOL(kgsl_sparse_bind);
+
long kgsl_ioctl_gpuobj_info(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
@@ -4656,7 +4804,7 @@ static void kgsl_core_exit(void)
kgsl_driver.class = NULL;
}
- kgsl_drawobj_exit();
+ kgsl_drawobjs_cache_exit();
kgsl_memfree_exit();
unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
@@ -4732,7 +4880,7 @@ static int __init kgsl_core_init(void)
kgsl_events_init();
- result = kgsl_drawobj_init();
+ result = kgsl_drawobjs_cache_init();
if (result)
goto err;
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index fbf9197b6d1b..2a9ac899725c 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -100,6 +100,7 @@ static inline void KGSL_STATS_ADD(uint64_t size, atomic_long_t *stat,
#define KGSL_MAX_NUMIBS 100000
#define KGSL_MAX_SYNCPOINTS 32
+#define KGSL_MAX_SPARSE 1000
struct kgsl_device;
struct kgsl_context;
@@ -432,6 +433,8 @@ long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data);
long kgsl_ioctl_sparse_unbind(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data);
+long kgsl_ioctl_gpu_sparse_command(struct kgsl_device_private *dev_priv,
+ unsigned int cmd, void *data);
void kgsl_mem_entry_destroy(struct kref *kref);
diff --git a/drivers/gpu/msm/kgsl_compat.c b/drivers/gpu/msm/kgsl_compat.c
index 028a9566fa14..6b8d8d34a988 100644
--- a/drivers/gpu/msm/kgsl_compat.c
+++ b/drivers/gpu/msm/kgsl_compat.c
@@ -382,6 +382,8 @@ static const struct kgsl_ioctl kgsl_compat_ioctl_funcs[] = {
kgsl_ioctl_sparse_virt_free),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_BIND,
kgsl_ioctl_sparse_bind),
+ KGSL_IOCTL_FUNC(IOCTL_KGSL_GPU_SPARSE_COMMAND,
+ kgsl_ioctl_gpu_sparse_command),
};
long kgsl_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 7d68c23ad5b7..cb7ffd51460a 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -203,6 +203,18 @@ struct kgsl_memobj_node {
unsigned long priv;
};
+/**
+ * struct kgsl_sparseobj_node - Sparse object descriptor
+ * @node: Local list node for the sparse cmdbatch
+ * @virt_id: Virtual ID to bind/unbind
+ * @obj: struct kgsl_sparse_binding_object
+ */
+struct kgsl_sparseobj_node {
+ struct list_head node;
+ unsigned int virt_id;
+ struct kgsl_sparse_binding_object obj;
+};
+
struct kgsl_device {
struct device *dev;
const char *name;
@@ -639,6 +651,9 @@ long kgsl_ioctl_copy_in(unsigned int kernel_cmd, unsigned int user_cmd,
long kgsl_ioctl_copy_out(unsigned int kernel_cmd, unsigned int user_cmd,
unsigned long, unsigned char *ptr);
+void kgsl_sparse_bind(struct kgsl_process_private *private,
+ struct kgsl_drawobj_sparse *sparse);
+
/**
* kgsl_context_put() - Release context reference count
* @context: Pointer to the KGSL context to be released
diff --git a/drivers/gpu/msm/kgsl_drawobj.c b/drivers/gpu/msm/kgsl_drawobj.c
index 7840daa6a3e2..f8f0e7ccb0d3 100644
--- a/drivers/gpu/msm/kgsl_drawobj.c
+++ b/drivers/gpu/msm/kgsl_drawobj.c
@@ -37,10 +37,12 @@
#include "kgsl_compat.h"
/*
- * Define an kmem cache for the memobj structures since we allocate and free
- * them so frequently
+ * Define an kmem cache for the memobj & sparseobj structures since we
+ * allocate and free them so frequently
*/
static struct kmem_cache *memobjs_cache;
+static struct kmem_cache *sparseobjs_cache;
+
static void drawobj_destroy_object(struct kref *kref)
{
@@ -60,6 +62,9 @@ static void drawobj_destroy_object(struct kref *kref)
case MARKEROBJ_TYPE:
kfree(CMDOBJ(drawobj));
break;
+ case SPARSEOBJ_TYPE:
+ kfree(SPARSEOBJ(drawobj));
+ break;
}
}
@@ -211,6 +216,18 @@ static inline void memobj_list_free(struct list_head *list)
}
}
+static void drawobj_destroy_sparse(struct kgsl_drawobj *drawobj)
+{
+ struct kgsl_sparseobj_node *mem, *tmpmem;
+ struct list_head *list = &SPARSEOBJ(drawobj)->sparselist;
+
+ /* Free the sparse mem here */
+ list_for_each_entry_safe(mem, tmpmem, list, node) {
+ list_del_init(&mem->node);
+ kmem_cache_free(sparseobjs_cache, mem);
+ }
+}
+
static void drawobj_destroy_sync(struct kgsl_drawobj *drawobj)
{
struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj);
@@ -297,6 +314,8 @@ void kgsl_drawobj_destroy(struct kgsl_drawobj *drawobj)
drawobj_destroy_sync(drawobj);
else if (drawobj->type & (CMDOBJ_TYPE | MARKEROBJ_TYPE))
drawobj_destroy_cmd(drawobj);
+ else if (drawobj->type == SPARSEOBJ_TYPE)
+ drawobj_destroy_sparse(drawobj);
else
return;
@@ -610,16 +629,26 @@ int kgsl_drawobj_cmd_add_ibdesc(struct kgsl_device *device,
return 0;
}
-static inline int drawobj_init(struct kgsl_device *device,
- struct kgsl_context *context, struct kgsl_drawobj *drawobj,
+static void *_drawobj_create(struct kgsl_device *device,
+ struct kgsl_context *context, unsigned int size,
unsigned int type)
{
+ void *obj = kzalloc(size, GFP_KERNEL);
+ struct kgsl_drawobj *drawobj;
+
+ if (obj == NULL)
+ return ERR_PTR(-ENOMEM);
+
/*
* Increase the reference count on the context so it doesn't disappear
* during the lifetime of this object
*/
- if (!_kgsl_context_get(context))
- return -ENOENT;
+ if (!_kgsl_context_get(context)) {
+ kfree(obj);
+ return ERR_PTR(-ENOENT);
+ }
+
+ drawobj = obj;
kref_init(&drawobj->refcount);
@@ -627,7 +656,28 @@ static inline int drawobj_init(struct kgsl_device *device,
drawobj->context = context;
drawobj->type = type;
- return 0;
+ return obj;
+}
+
+/**
+ * kgsl_drawobj_sparse_create() - Create a new sparse obj structure
+ * @device: Pointer to a KGSL device struct
+ * @context: Pointer to a KGSL context struct
+ * @flags: Flags for the sparse obj
+ *
+ * Allocate an new kgsl_drawobj_sparse structure
+ */
+struct kgsl_drawobj_sparse *kgsl_drawobj_sparse_create(
+ struct kgsl_device *device,
+ struct kgsl_context *context, unsigned int flags)
+{
+ struct kgsl_drawobj_sparse *sparseobj = _drawobj_create(device,
+ context, sizeof(*sparseobj), SPARSEOBJ_TYPE);
+
+ if (!IS_ERR(sparseobj))
+ INIT_LIST_HEAD(&sparseobj->sparselist);
+
+ return sparseobj;
}
/**
@@ -641,18 +691,13 @@ static inline int drawobj_init(struct kgsl_device *device,
struct kgsl_drawobj_sync *kgsl_drawobj_sync_create(struct kgsl_device *device,
struct kgsl_context *context)
{
- struct kgsl_drawobj_sync *syncobj = kzalloc(sizeof(*syncobj),
- GFP_KERNEL);
- if (syncobj == NULL)
- return ERR_PTR(-ENOMEM);
-
- if (drawobj_init(device, context, DRAWOBJ(syncobj), SYNCOBJ_TYPE)) {
- kfree(syncobj);
- return ERR_PTR(-ENOENT);
- }
+ struct kgsl_drawobj_sync *syncobj = _drawobj_create(device,
+ context, sizeof(*syncobj), SYNCOBJ_TYPE);
/* Add a timer to help debug sync deadlocks */
- setup_timer(&syncobj->timer, syncobj_timer, (unsigned long) syncobj);
+ if (!IS_ERR(syncobj))
+ setup_timer(&syncobj->timer, syncobj_timer,
+ (unsigned long) syncobj);
return syncobj;
}
@@ -671,27 +716,13 @@ struct kgsl_drawobj_cmd *kgsl_drawobj_cmd_create(struct kgsl_device *device,
struct kgsl_context *context, unsigned int flags,
unsigned int type)
{
- struct kgsl_drawobj_cmd *cmdobj = kzalloc(sizeof(*cmdobj), GFP_KERNEL);
- struct kgsl_drawobj *drawobj;
-
- if (cmdobj == NULL)
- return ERR_PTR(-ENOMEM);
-
- type &= CMDOBJ_TYPE | MARKEROBJ_TYPE;
- if (type == 0) {
- kfree(cmdobj);
- return ERR_PTR(-EINVAL);
- }
-
- drawobj = DRAWOBJ(cmdobj);
-
- if (drawobj_init(device, context, drawobj, type)) {
- kfree(cmdobj);
- return ERR_PTR(-ENOENT);
- }
+ struct kgsl_drawobj_cmd *cmdobj = _drawobj_create(device,
+ context, sizeof(*cmdobj),
+ (type & (CMDOBJ_TYPE | MARKEROBJ_TYPE)));
- /* sanitize our flags for drawobj's */
- drawobj->flags = flags & (KGSL_DRAWOBJ_CTX_SWITCH
+ if (!IS_ERR(cmdobj)) {
+ /* sanitize our flags for drawobj's */
+ cmdobj->base.flags = flags & (KGSL_DRAWOBJ_CTX_SWITCH
| KGSL_DRAWOBJ_MARKER
| KGSL_DRAWOBJ_END_OF_FRAME
| KGSL_DRAWOBJ_PWR_CONSTRAINT
@@ -699,8 +730,9 @@ struct kgsl_drawobj_cmd *kgsl_drawobj_cmd_create(struct kgsl_device *device,
| KGSL_DRAWOBJ_PROFILING
| KGSL_DRAWOBJ_PROFILING_KTIME);
- INIT_LIST_HEAD(&cmdobj->cmdlist);
- INIT_LIST_HEAD(&cmdobj->memlist);
+ INIT_LIST_HEAD(&cmdobj->cmdlist);
+ INIT_LIST_HEAD(&cmdobj->memlist);
+ }
return cmdobj;
}
@@ -864,7 +896,7 @@ int kgsl_drawobj_sync_add_syncpoints(struct kgsl_device *device,
return 0;
}
-static int drawobj_add_object(struct list_head *head,
+static int kgsl_drawobj_add_memobject(struct list_head *head,
struct kgsl_command_object *obj)
{
struct kgsl_memobj_node *mem;
@@ -884,6 +916,62 @@ static int drawobj_add_object(struct list_head *head,
return 0;
}
+static int kgsl_drawobj_add_sparseobject(struct list_head *head,
+ struct kgsl_sparse_binding_object *obj, unsigned int virt_id)
+{
+ struct kgsl_sparseobj_node *mem;
+
+ mem = kmem_cache_alloc(sparseobjs_cache, GFP_KERNEL);
+ if (mem == NULL)
+ return -ENOMEM;
+
+ mem->virt_id = virt_id;
+ mem->obj.id = obj->id;
+ mem->obj.virtoffset = obj->virtoffset;
+ mem->obj.physoffset = obj->physoffset;
+ mem->obj.size = obj->size;
+ mem->obj.flags = obj->flags;
+
+ list_add_tail(&mem->node, head);
+ return 0;
+}
+
+int kgsl_drawobj_sparse_add_sparselist(struct kgsl_device *device,
+ struct kgsl_drawobj_sparse *sparseobj, unsigned int id,
+ void __user *ptr, unsigned int size, unsigned int count)
+{
+ struct kgsl_sparse_binding_object obj;
+ int i, ret = 0;
+
+ ret = _verify_input_list(count, ptr, size);
+ if (ret <= 0)
+ return ret;
+
+ for (i = 0; i < count; i++) {
+ memset(&obj, 0, sizeof(obj));
+
+ ret = _copy_from_user(&obj, ptr, sizeof(obj), size);
+ if (ret)
+ return ret;
+
+ if (!(obj.flags & (KGSL_SPARSE_BIND | KGSL_SPARSE_UNBIND)))
+ return -EINVAL;
+
+ ret = kgsl_drawobj_add_sparseobject(&sparseobj->sparselist,
+ &obj, id);
+ if (ret)
+ return ret;
+
+ ptr += sizeof(obj);
+ }
+
+ sparseobj->size = size;
+ sparseobj->count = count;
+
+ return 0;
+}
+
+
#define CMDLIST_FLAGS \
(KGSL_CMDLIST_IB | \
KGSL_CMDLIST_CTXTSWITCH_PREAMBLE | \
@@ -922,7 +1010,7 @@ int kgsl_drawobj_cmd_add_cmdlist(struct kgsl_device *device,
return -EINVAL;
}
- ret = drawobj_add_object(&cmdobj->cmdlist, &obj);
+ ret = kgsl_drawobj_add_memobject(&cmdobj->cmdlist, &obj);
if (ret)
return ret;
@@ -967,7 +1055,8 @@ int kgsl_drawobj_cmd_add_memlist(struct kgsl_device *device,
add_profiling_buffer(device, cmdobj, obj.gpuaddr,
obj.size, obj.id, obj.offset);
else {
- ret = drawobj_add_object(&cmdobj->memlist, &obj);
+ ret = kgsl_drawobj_add_memobject(&cmdobj->memlist,
+ &obj);
if (ret)
return ret;
}
@@ -1018,19 +1107,19 @@ int kgsl_drawobj_sync_add_synclist(struct kgsl_device *device,
return 0;
}
-void kgsl_drawobj_exit(void)
+void kgsl_drawobjs_cache_exit(void)
{
- if (memobjs_cache != NULL)
- kmem_cache_destroy(memobjs_cache);
+ kmem_cache_destroy(memobjs_cache);
+ kmem_cache_destroy(sparseobjs_cache);
}
-int kgsl_drawobj_init(void)
+int kgsl_drawobjs_cache_init(void)
{
memobjs_cache = KMEM_CACHE(kgsl_memobj_node, 0);
- if (memobjs_cache == NULL) {
- KGSL_CORE_ERR("failed to create memobjs_cache");
+ sparseobjs_cache = KMEM_CACHE(kgsl_sparseobj_node, 0);
+
+ if (!memobjs_cache || !sparseobjs_cache)
return -ENOMEM;
- }
return 0;
}
diff --git a/drivers/gpu/msm/kgsl_drawobj.h b/drivers/gpu/msm/kgsl_drawobj.h
index 89ed944c539a..fd9d2bc93f41 100644
--- a/drivers/gpu/msm/kgsl_drawobj.h
+++ b/drivers/gpu/msm/kgsl_drawobj.h
@@ -18,10 +18,13 @@
container_of(obj, struct kgsl_drawobj_sync, base)
#define CMDOBJ(obj) \
container_of(obj, struct kgsl_drawobj_cmd, base)
+#define SPARSEOBJ(obj) \
+ container_of(obj, struct kgsl_drawobj_sparse, base)
#define CMDOBJ_TYPE BIT(0)
#define MARKEROBJ_TYPE BIT(1)
#define SYNCOBJ_TYPE BIT(2)
+#define SPARSEOBJ_TYPE BIT(3)
/**
* struct kgsl_drawobj - KGSL drawobj descriptor
@@ -45,7 +48,7 @@ struct kgsl_drawobj {
* struct kgsl_drawobj_cmd - KGSL command obj, This covers marker
* cmds also since markers are special form of cmds that do not
* need their cmds to be executed.
- * @base: Base kgsl_drawobj
+ * @base: Base kgsl_drawobj, this needs to be the first entry
* @priv: Internal flags
* @global_ts: The ringbuffer timestamp corresponding to this
* command obj
@@ -123,6 +126,22 @@ struct kgsl_drawobj_sync_event {
struct kgsl_device *device;
};
+/**
+ * struct kgsl_drawobj_sparse - KGSl sparse obj descriptor
+ * @base: Base kgsl_obj, this needs to be the first entry
+ * @id: virtual id of the bind/unbind
+ * @sparselist: list of binds/unbinds
+ * @size: Size of kgsl_sparse_bind_object
+ * @count: Number of elements in list
+ */
+struct kgsl_drawobj_sparse {
+ struct kgsl_drawobj base;
+ unsigned int id;
+ struct list_head sparselist;
+ unsigned int size;
+ unsigned int count;
+};
+
#define KGSL_DRAWOBJ_FLAGS \
{ KGSL_DRAWOBJ_MARKER, "MARKER" }, \
{ KGSL_DRAWOBJ_CTX_SWITCH, "CTX_SWITCH" }, \
@@ -172,9 +191,15 @@ int kgsl_drawobj_sync_add_synclist(struct kgsl_device *device,
int kgsl_drawobj_sync_add_sync(struct kgsl_device *device,
struct kgsl_drawobj_sync *syncobj,
struct kgsl_cmd_syncpoint *sync);
-
-int kgsl_drawobj_init(void);
-void kgsl_drawobj_exit(void);
+struct kgsl_drawobj_sparse *kgsl_drawobj_sparse_create(
+ struct kgsl_device *device,
+ struct kgsl_context *context, unsigned int flags);
+int kgsl_drawobj_sparse_add_sparselist(struct kgsl_device *device,
+ struct kgsl_drawobj_sparse *sparseobj, unsigned int id,
+ void __user *ptr, unsigned int size, unsigned int count);
+
+int kgsl_drawobjs_cache_init(void);
+void kgsl_drawobjs_cache_exit(void);
void kgsl_dump_syncpoints(struct kgsl_device *device,
struct kgsl_drawobj_sync *syncobj);
diff --git a/drivers/gpu/msm/kgsl_ioctl.c b/drivers/gpu/msm/kgsl_ioctl.c
index 894e6a4a146b..f7876335d95e 100644
--- a/drivers/gpu/msm/kgsl_ioctl.c
+++ b/drivers/gpu/msm/kgsl_ioctl.c
@@ -100,6 +100,8 @@ static const struct kgsl_ioctl kgsl_ioctl_funcs[] = {
kgsl_ioctl_sparse_virt_free),
KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_BIND,
kgsl_ioctl_sparse_bind),
+ KGSL_IOCTL_FUNC(IOCTL_KGSL_GPU_SPARSE_COMMAND,
+ kgsl_ioctl_gpu_sparse_command),
};
long kgsl_ioctl_copy_in(unsigned int kernel_cmd, unsigned int user_cmd,
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index aac11dbe5984..71fdf6d6e9e5 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -50,6 +50,7 @@
#define KGSL_CONTEXT_IFH_NOP 0x00010000
#define KGSL_CONTEXT_SECURE 0x00020000
#define KGSL_CONTEXT_NO_SNAPSHOT 0x00040000
+#define KGSL_CONTEXT_SPARSE 0x00080000
#define KGSL_CONTEXT_PREEMPT_STYLE_MASK 0x0E000000
#define KGSL_CONTEXT_PREEMPT_STYLE_SHIFT 25
@@ -89,6 +90,7 @@
#define KGSL_CMDBATCH_END_OF_FRAME KGSL_CONTEXT_END_OF_FRAME /* 0x100 */
#define KGSL_CMDBATCH_SYNC KGSL_CONTEXT_SYNC /* 0x400 */
#define KGSL_CMDBATCH_PWR_CONSTRAINT KGSL_CONTEXT_PWR_CONSTRAINT /* 0x800 */
+#define KGSL_CMDBATCH_SPARSE 0x1000 /* 0x1000 */
/*
* Reserve bits [16:19] and bits [28:31] for possible bits shared between
@@ -1556,4 +1558,34 @@ struct kgsl_sparse_bind {
#define IOCTL_KGSL_SPARSE_BIND \
_IOW(KGSL_IOC_TYPE, 0x54, struct kgsl_sparse_bind)
+/**
+ * struct kgsl_gpu_sparse_command - Argument for
+ * IOCTL_KGSL_GPU_SPARSE_COMMAND
+ * @flags: Current flags for the object
+ * @sparselist: List of kgsl_sparse_binding_object to bind/unbind
+ * @synclist: List of kgsl_command_syncpoints
+ * @sparsesize: Size of kgsl_sparse_binding_object
+ * @numsparse: Number of elements in list
+ * @sync_size: Size of kgsl_command_syncpoint structure
+ * @numsyncs: Number of kgsl_command_syncpoints in syncpoint list
+ * @context_id: Context ID submitting the kgsl_gpu_command
+ * @timestamp: Timestamp for the submitted commands
+ * @id: Virtual ID to bind/unbind
+ */
+struct kgsl_gpu_sparse_command {
+ uint64_t flags;
+ uint64_t __user sparselist;
+ uint64_t __user synclist;
+ unsigned int sparsesize;
+ unsigned int numsparse;
+ unsigned int syncsize;
+ unsigned int numsyncs;
+ unsigned int context_id;
+ unsigned int timestamp;
+ unsigned int id;
+};
+
+#define IOCTL_KGSL_GPU_SPARSE_COMMAND \
+ _IOWR(KGSL_IOC_TYPE, 0x55, struct kgsl_gpu_sparse_command)
+
#endif /* _UAPI_MSM_KGSL_H */