diff options
Diffstat (limited to 'drivers/gpu/msm/kgsl.c')
| -rw-r--r-- | drivers/gpu/msm/kgsl.c | 152 |
1 files changed, 150 insertions, 2 deletions
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; |
