diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index a148b91c..dcfe4a80 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -3,5 +3,6 @@ obj-m += drm/tegra/ obj-m += host1x/ +obj-m += host1x-fence/ obj-m += host1x-nvhost/ obj-m += nvgpu/ diff --git a/drivers/gpu/host1x-fence/Makefile b/drivers/gpu/host1x-fence/Makefile new file mode 100644 index 00000000..ab3f6560 --- /dev/null +++ b/drivers/gpu/host1x-fence/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Tegra Host1x-fence Driver. +# +# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. +# + +ccflags-y += -I$(srctree.nvidia)/include +ccflags-y += -I$(srctree.nvidia)/include/uapi/linux +ccflags-y += -I$(srctree.host1x)/include +ccflags-y += -DCONFIG_TEGRA_HOST1X +ccflags-y += -Werror + +host1x-fence-objs = dev.o + +obj-m += host1x-fence.o diff --git a/drivers/gpu/host1x-fence/dev.c b/drivers/gpu/host1x-fence/dev.c new file mode 100644 index 00000000..544a4741 --- /dev/null +++ b/drivers/gpu/host1x-fence/dev.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Host1x fence UAPI + * + * Copyright (c) 2022, NVIDIA Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/uapi/linux/host1x-fence.h" + +static struct host1x_uapi { + struct class *class; + + struct cdev cdev; + struct device *dev; + dev_t dev_num; +} uapi_data; + +static int dev_file_open(struct inode *inode, struct file *file) +{ + struct platform_device *host1x_pdev; + struct device_node *np; + + static const struct of_device_id host1x_match[] = { + { .compatible = "nvidia,tegra186-host1x", }, + { .compatible = "nvidia,tegra194-host1x", }, + {}, + }; + + np = of_find_matching_node(NULL, host1x_match); + if (!np) { + return -ENODEV; + } + + host1x_pdev = of_find_device_by_node(np); + if (!host1x_pdev) { + return -EAGAIN; + } + + file->private_data = platform_get_drvdata(host1x_pdev); + + return 0; +} + +static int host1x_fence_create_fd(struct host1x_syncpt *sp, u32 threshold) +{ + struct sync_file *file; + struct dma_fence *f; + int fd; + + f = host1x_fence_create(sp, threshold); + if (IS_ERR(f)) + return PTR_ERR(f); + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + dma_fence_put(f); + return fd; + } + + file = sync_file_create(f); + dma_fence_put(f); + if (!file) + return -ENOMEM; + + fd_install(fd, file->file); + + return fd; +} + +static int dev_file_ioctl_create_fence(struct host1x *host1x, void __user *data) +{ + struct host1x_create_fence args; + struct host1x_syncpt *syncpt; + unsigned long copy_err; + int fd; + + copy_err = copy_from_user(&args, data, sizeof(args)); + if (copy_err) + return -EFAULT; + + if (args.reserved[0]) + return -EINVAL; + + syncpt = host1x_syncpt_get_by_id_noref(host1x, args.id); + if (!syncpt) + return -EINVAL; + + fd = host1x_fence_create_fd(syncpt, args.threshold); + if (fd < 0) + return fd; + + args.fence_fd = fd; + + copy_err = copy_to_user(data, &args, sizeof(args)); + if (copy_err) + return -EFAULT; + + return 0; +} + +static int dev_file_ioctl_fence_extract(struct host1x *host1x, void __user *data) +{ + struct host1x_fence_extract_fence __user *fences_user_ptr; + struct dma_fence *fence, **fences; + struct host1x_fence_extract args; + struct dma_fence_array *array; + unsigned int num_fences, i; + unsigned long copy_err; + int err; + + copy_err = copy_from_user(&args, data, sizeof(args)); + if (copy_err) + return -EFAULT; + + fences_user_ptr = u64_to_user_ptr(args.fences_ptr); + + if (args.reserved[0] || args.reserved[1]) + return -EINVAL; + + fence = sync_file_get_fence(args.fence_fd); + if (!fence) + return -EINVAL; + + array = to_dma_fence_array(fence); + if (array) { + fences = array->fences; + num_fences = array->num_fences; + } else { + fences = &fence; + num_fences = 1; + } + + for (i = 0; i < min(num_fences, args.num_fences); i++) { + struct host1x_fence_extract_fence f; + + err = host1x_fence_extract(fences[i], &f.id, &f.threshold); + if (err) + goto put_fence; + + copy_err = copy_to_user(fences_user_ptr + i, &f, sizeof(f)); + if (copy_err) { + err = -EFAULT; + goto put_fence; + } + } + + args.num_fences = num_fences; + + copy_err = copy_to_user(data, &args, sizeof(args)); + if (copy_err) { + err = -EFAULT; + goto put_fence; + } + + return 0; + +put_fence: + dma_fence_put(fence); + + return err; +} + +static long dev_file_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *data = (void __user *)arg; + long err; + + switch (cmd) { + case HOST1X_IOCTL_CREATE_FENCE: + err = dev_file_ioctl_create_fence(file->private_data, data); + break; + + case HOST1X_IOCTL_FENCE_EXTRACT: + err = dev_file_ioctl_fence_extract(file->private_data, data); + break; + + default: + err = -ENOTTY; + } + + return err; +} + +static const struct file_operations dev_file_fops = { + .owner = THIS_MODULE, + .open = dev_file_open, + .unlocked_ioctl = dev_file_ioctl, + .compat_ioctl = dev_file_ioctl, +}; + +static char *host1x_fence_devnode(struct device *dev, umode_t *mode) +{ + *mode = 0666; + return NULL; +} + +static int host1x_uapi_init(struct host1x_uapi *uapi) +{ + int err; + dev_t dev_num; + + err = alloc_chrdev_region(&dev_num, 0, 1, "host1x-fence"); + if (err) + return err; + + uapi->class = class_create(THIS_MODULE, "host1x-fence"); + if (IS_ERR(uapi->class)) { + err = PTR_ERR(uapi->class); + goto unregister_chrdev_region; + } + uapi->class->devnode = host1x_fence_devnode; + + cdev_init(&uapi->cdev, &dev_file_fops); + err = cdev_add(&uapi->cdev, dev_num, 1); + if (err) + goto destroy_class; + + uapi->dev = device_create(uapi->class, NULL, + dev_num, NULL, "host1x-fence"); + if (IS_ERR(uapi->dev)) { + err = PTR_ERR(uapi->dev); + goto del_cdev; + } + + cdev_add(&uapi->cdev, dev_num, 1); + + uapi->dev_num = dev_num; + + return 0; + +del_cdev: + cdev_del(&uapi->cdev); +destroy_class: + class_destroy(uapi->class); +unregister_chrdev_region: + unregister_chrdev_region(dev_num, 1); + + return err; +} + +static int __init tegra_host1x_init(void) +{ + return host1x_uapi_init(&uapi_data); +} +module_init(tegra_host1x_init); + +static void __exit tegra_host1x_exit(void) +{ +} +module_exit(tegra_host1x_exit); + +MODULE_DESCRIPTION("Host1x fence UAPI"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/host1x-fence/include/uapi/linux/host1x-fence.h b/drivers/gpu/host1x-fence/include/uapi/linux/host1x-fence.h new file mode 100644 index 00000000..ce68c022 --- /dev/null +++ b/drivers/gpu/host1x-fence/include/uapi/linux/host1x-fence.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* Copyright (c) 2022 NVIDIA Corporation */ + +#ifndef _UAPI__LINUX_HOST1X_FENCE_H +#define _UAPI__LINUX_HOST1X_FENCE_H + +#include +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +struct host1x_create_fence { + /** + * @id: [in] + * + * ID of the syncpoint to create a fence for. + */ + __u32 id; + + /** + * @threshold: [in] + * + * When the syncpoint reaches this value, the fence will be signaled. + * The syncpoint is considered to have reached the threshold when the + * following condition is true: + * + * ((value - threshold) & 0x80000000U) == 0U + * + */ + __u32 threshold; + + /** + * @fence_fd: [out] + * + * New sync_file file descriptor containing the created fence. + */ + __s32 fence_fd; + + __u32 reserved[1]; +}; + +struct host1x_fence_extract_fence { + __u32 id; + __u32 threshold; +}; + +struct host1x_fence_extract { + /** + * @fence_fd: [in] + * + * sync_file file descriptor + */ + __s32 fence_fd; + + /** + * @num_fences: [in,out] + * + * In: size of the `fences_ptr` array counted in elements. + * Out: required size of the `fences_ptr` array counted in elements. + */ + __u32 num_fences; + + /** + * @fences_ptr: [in] + * + * Pointer to array of `struct host1x_fence_extract_fence`. + */ + __u64 fences_ptr; + + __u32 reserved[2]; +}; + +#define HOST1X_IOCTL_CREATE_FENCE _IOWR('X', 0x02, struct host1x_create_fence) +#define HOST1X_IOCTL_FENCE_EXTRACT _IOWR('X', 0x05, struct host1x_fence_extract) + +#if defined(__cplusplus) +} +#endif + +#endif