| /* SPDX-License-Identifier: GPL-2.0-only */ | |
| /* Copyright (C) 2020 Google, Inc */ | |
| #include "drm/drm_drv.h" | |
| #include "msm_drv.h" | |
| #include "msm_syncobj.h" | |
| struct drm_syncobj ** | |
| msm_syncobj_parse_deps(struct drm_device *dev, | |
| struct drm_sched_job *job, | |
| struct drm_file *file, | |
| uint64_t in_syncobjs_addr, | |
| uint32_t nr_in_syncobjs, | |
| size_t syncobj_stride) | |
| { | |
| struct drm_syncobj **syncobjs = NULL; | |
| struct drm_msm_syncobj syncobj_desc = {0}; | |
| int ret = 0; | |
| uint32_t i, j; | |
| syncobjs = kzalloc_objs(*syncobjs, nr_in_syncobjs, | |
| GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); | |
| if (!syncobjs) | |
| return ERR_PTR(-ENOMEM); | |
| for (i = 0; i < nr_in_syncobjs; ++i) { | |
| uint64_t address = in_syncobjs_addr + i * syncobj_stride; | |
| if (copy_from_user(&syncobj_desc, | |
| u64_to_user_ptr(address), | |
| min(syncobj_stride, sizeof(syncobj_desc)))) { | |
| ret = -EFAULT; | |
| break; | |
| } | |
| if (syncobj_desc.point && | |
| !drm_core_check_feature(dev, DRIVER_SYNCOBJ_TIMELINE)) { | |
| ret = UERR(EOPNOTSUPP, dev, "syncobj timeline unsupported"); | |
| break; | |
| } | |
| if (syncobj_desc.flags & ~MSM_SYNCOBJ_FLAGS) { | |
| ret = UERR(EINVAL, dev, "invalid syncobj flags: %x", syncobj_desc.flags); | |
| break; | |
| } | |
| ret = drm_sched_job_add_syncobj_dependency(job, file, | |
| syncobj_desc.handle, | |
| syncobj_desc.point); | |
| if (ret) | |
| break; | |
| if (syncobj_desc.flags & MSM_SYNCOBJ_RESET) { | |
| syncobjs[i] = drm_syncobj_find(file, syncobj_desc.handle); | |
| if (!syncobjs[i]) { | |
| ret = UERR(EINVAL, dev, "invalid syncobj handle: %u", i); | |
| break; | |
| } | |
| } | |
| } | |
| if (ret) { | |
| for (j = 0; j <= i; ++j) { | |
| if (syncobjs[j]) | |
| drm_syncobj_put(syncobjs[j]); | |
| } | |
| kfree(syncobjs); | |
| return ERR_PTR(ret); | |
| } | |
| return syncobjs; | |
| } | |
| void | |
| msm_syncobj_reset(struct drm_syncobj **syncobjs, uint32_t nr_syncobjs) | |
| { | |
| uint32_t i; | |
| for (i = 0; syncobjs && i < nr_syncobjs; ++i) { | |
| if (syncobjs[i]) | |
| drm_syncobj_replace_fence(syncobjs[i], NULL); | |
| } | |
| } | |
| struct msm_syncobj_post_dep * | |
| msm_syncobj_parse_post_deps(struct drm_device *dev, | |
| struct drm_file *file, | |
| uint64_t syncobjs_addr, | |
| uint32_t nr_syncobjs, | |
| size_t syncobj_stride) | |
| { | |
| struct msm_syncobj_post_dep *post_deps; | |
| struct drm_msm_syncobj syncobj_desc = {0}; | |
| int ret = 0; | |
| uint32_t i, j; | |
| post_deps = kzalloc_objs(*post_deps, nr_syncobjs, | |
| GFP_KERNEL | __GFP_NOWARN | __GFP_NORETRY); | |
| if (!post_deps) | |
| return ERR_PTR(-ENOMEM); | |
| for (i = 0; i < nr_syncobjs; ++i) { | |
| uint64_t address = syncobjs_addr + i * syncobj_stride; | |
| if (copy_from_user(&syncobj_desc, | |
| u64_to_user_ptr(address), | |
| min(syncobj_stride, sizeof(syncobj_desc)))) { | |
| ret = -EFAULT; | |
| break; | |
| } | |
| post_deps[i].point = syncobj_desc.point; | |
| if (syncobj_desc.flags) { | |
| ret = UERR(EINVAL, dev, "invalid syncobj flags"); | |
| break; | |
| } | |
| if (syncobj_desc.point) { | |
| if (!drm_core_check_feature(dev, | |
| DRIVER_SYNCOBJ_TIMELINE)) { | |
| ret = UERR(EOPNOTSUPP, dev, "syncobj timeline unsupported"); | |
| break; | |
| } | |
| post_deps[i].chain = dma_fence_chain_alloc(); | |
| if (!post_deps[i].chain) { | |
| ret = -ENOMEM; | |
| break; | |
| } | |
| } | |
| post_deps[i].syncobj = | |
| drm_syncobj_find(file, syncobj_desc.handle); | |
| if (!post_deps[i].syncobj) { | |
| ret = UERR(EINVAL, dev, "invalid syncobj handle"); | |
| break; | |
| } | |
| } | |
| if (ret) { | |
| for (j = 0; j <= i; ++j) { | |
| dma_fence_chain_free(post_deps[j].chain); | |
| if (post_deps[j].syncobj) | |
| drm_syncobj_put(post_deps[j].syncobj); | |
| } | |
| kfree(post_deps); | |
| return ERR_PTR(ret); | |
| } | |
| return post_deps; | |
| } | |
| void | |
| msm_syncobj_process_post_deps(struct msm_syncobj_post_dep *post_deps, | |
| uint32_t count, struct dma_fence *fence) | |
| { | |
| uint32_t i; | |
| for (i = 0; post_deps && i < count; ++i) { | |
| if (post_deps[i].chain) { | |
| drm_syncobj_add_point(post_deps[i].syncobj, | |
| post_deps[i].chain, | |
| fence, post_deps[i].point); | |
| post_deps[i].chain = NULL; | |
| } else { | |
| drm_syncobj_replace_fence(post_deps[i].syncobj, | |
| fence); | |
| } | |
| } | |
| } |