diff options
| -rw-r--r-- | drivers/gpu/msm/adreno_dispatch.c | 127 | ||||
| -rw-r--r-- | drivers/gpu/msm/adreno_drawctxt.c | 3 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl.c | 152 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl.h | 3 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl_compat.c | 2 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl_device.h | 15 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl_drawobj.c | 187 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl_drawobj.h | 33 | ||||
| -rw-r--r-- | drivers/gpu/msm/kgsl_ioctl.c | 2 | ||||
| -rw-r--r-- | include/uapi/linux/msm_kgsl.h | 32 |
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, ¶m->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 */ |
