| /* 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 = kcalloc(nr_in_syncobjs, sizeof(*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 = kcalloc(nr_syncobjs, sizeof(*post_deps), |
| 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); |
| } |
| } |
| } |