xref: /openbmc/linux/drivers/accel/drm_accel.c (revision 1aaba11d)
18bf48897SOded Gabbay // SPDX-License-Identifier: GPL-2.0
28bf48897SOded Gabbay 
38bf48897SOded Gabbay /*
48bf48897SOded Gabbay  * Copyright 2022 HabanaLabs, Ltd.
58bf48897SOded Gabbay  * All Rights Reserved.
68bf48897SOded Gabbay  *
78bf48897SOded Gabbay  */
88bf48897SOded Gabbay 
98bf48897SOded Gabbay #include <linux/debugfs.h>
108bf48897SOded Gabbay #include <linux/device.h>
112c204f3dSOded Gabbay #include <linux/idr.h>
128bf48897SOded Gabbay 
138bf48897SOded Gabbay #include <drm/drm_accel.h>
142c204f3dSOded Gabbay #include <drm/drm_debugfs.h>
152c204f3dSOded Gabbay #include <drm/drm_drv.h>
162c204f3dSOded Gabbay #include <drm/drm_file.h>
178bf48897SOded Gabbay #include <drm/drm_ioctl.h>
188bf48897SOded Gabbay #include <drm/drm_print.h>
198bf48897SOded Gabbay 
202c204f3dSOded Gabbay static DEFINE_SPINLOCK(accel_minor_lock);
212c204f3dSOded Gabbay static struct idr accel_minors_idr;
222c204f3dSOded Gabbay 
238bf48897SOded Gabbay static struct dentry *accel_debugfs_root;
248bf48897SOded Gabbay static struct class *accel_class;
258bf48897SOded Gabbay 
262c204f3dSOded Gabbay static struct device_type accel_sysfs_device_minor = {
272c204f3dSOded Gabbay 	.name = "accel_minor"
282c204f3dSOded Gabbay };
292c204f3dSOded Gabbay 
30dcde56bbSLinus Torvalds static char *accel_devnode(const struct device *dev, umode_t *mode)
318bf48897SOded Gabbay {
328bf48897SOded Gabbay 	return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
338bf48897SOded Gabbay }
348bf48897SOded Gabbay 
358bf48897SOded Gabbay static int accel_sysfs_init(void)
368bf48897SOded Gabbay {
37*1aaba11dSGreg Kroah-Hartman 	accel_class = class_create("accel");
388bf48897SOded Gabbay 	if (IS_ERR(accel_class))
398bf48897SOded Gabbay 		return PTR_ERR(accel_class);
408bf48897SOded Gabbay 
418bf48897SOded Gabbay 	accel_class->devnode = accel_devnode;
428bf48897SOded Gabbay 
438bf48897SOded Gabbay 	return 0;
448bf48897SOded Gabbay }
458bf48897SOded Gabbay 
468bf48897SOded Gabbay static void accel_sysfs_destroy(void)
478bf48897SOded Gabbay {
488bf48897SOded Gabbay 	if (IS_ERR_OR_NULL(accel_class))
498bf48897SOded Gabbay 		return;
508bf48897SOded Gabbay 	class_destroy(accel_class);
518bf48897SOded Gabbay 	accel_class = NULL;
528bf48897SOded Gabbay }
538bf48897SOded Gabbay 
542c204f3dSOded Gabbay static int accel_name_info(struct seq_file *m, void *data)
552c204f3dSOded Gabbay {
562c204f3dSOded Gabbay 	struct drm_info_node *node = (struct drm_info_node *) m->private;
572c204f3dSOded Gabbay 	struct drm_minor *minor = node->minor;
582c204f3dSOded Gabbay 	struct drm_device *dev = minor->dev;
592c204f3dSOded Gabbay 	struct drm_master *master;
602c204f3dSOded Gabbay 
612c204f3dSOded Gabbay 	mutex_lock(&dev->master_mutex);
622c204f3dSOded Gabbay 	master = dev->master;
632c204f3dSOded Gabbay 	seq_printf(m, "%s", dev->driver->name);
642c204f3dSOded Gabbay 	if (dev->dev)
652c204f3dSOded Gabbay 		seq_printf(m, " dev=%s", dev_name(dev->dev));
662c204f3dSOded Gabbay 	if (master && master->unique)
672c204f3dSOded Gabbay 		seq_printf(m, " master=%s", master->unique);
682c204f3dSOded Gabbay 	if (dev->unique)
692c204f3dSOded Gabbay 		seq_printf(m, " unique=%s", dev->unique);
702c204f3dSOded Gabbay 	seq_puts(m, "\n");
712c204f3dSOded Gabbay 	mutex_unlock(&dev->master_mutex);
722c204f3dSOded Gabbay 
732c204f3dSOded Gabbay 	return 0;
742c204f3dSOded Gabbay }
752c204f3dSOded Gabbay 
762c204f3dSOded Gabbay static const struct drm_info_list accel_debugfs_list[] = {
772c204f3dSOded Gabbay 	{"name", accel_name_info, 0}
782c204f3dSOded Gabbay };
792c204f3dSOded Gabbay #define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)
802c204f3dSOded Gabbay 
812c204f3dSOded Gabbay /**
822c204f3dSOded Gabbay  * accel_debugfs_init() - Initialize debugfs for accel minor
832c204f3dSOded Gabbay  * @minor: Pointer to the drm_minor instance.
842c204f3dSOded Gabbay  * @minor_id: The minor's id
852c204f3dSOded Gabbay  *
862c204f3dSOded Gabbay  * This function initializes the drm minor's debugfs members and creates
872c204f3dSOded Gabbay  * a root directory for the minor in debugfs. It also creates common files
882c204f3dSOded Gabbay  * for accelerators and calls the driver's debugfs init callback.
892c204f3dSOded Gabbay  */
902c204f3dSOded Gabbay void accel_debugfs_init(struct drm_minor *minor, int minor_id)
912c204f3dSOded Gabbay {
922c204f3dSOded Gabbay 	struct drm_device *dev = minor->dev;
932c204f3dSOded Gabbay 	char name[64];
942c204f3dSOded Gabbay 
952c204f3dSOded Gabbay 	INIT_LIST_HEAD(&minor->debugfs_list);
962c204f3dSOded Gabbay 	mutex_init(&minor->debugfs_lock);
972c204f3dSOded Gabbay 	sprintf(name, "%d", minor_id);
982c204f3dSOded Gabbay 	minor->debugfs_root = debugfs_create_dir(name, accel_debugfs_root);
992c204f3dSOded Gabbay 
1002c204f3dSOded Gabbay 	drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
1012c204f3dSOded Gabbay 				 minor->debugfs_root, minor);
1022c204f3dSOded Gabbay 
1032c204f3dSOded Gabbay 	if (dev->driver->debugfs_init)
1042c204f3dSOded Gabbay 		dev->driver->debugfs_init(minor);
1052c204f3dSOded Gabbay }
1062c204f3dSOded Gabbay 
1072c204f3dSOded Gabbay /**
1082c204f3dSOded Gabbay  * accel_set_device_instance_params() - Set some device parameters for accel device
1092c204f3dSOded Gabbay  * @kdev: Pointer to the device instance.
1102c204f3dSOded Gabbay  * @index: The minor's index
1112c204f3dSOded Gabbay  *
1122c204f3dSOded Gabbay  * This function creates the dev_t of the device using the accel major and
1132c204f3dSOded Gabbay  * the device's minor number. In addition, it sets the class and type of the
1142c204f3dSOded Gabbay  * device instance to the accel sysfs class and device type, respectively.
1152c204f3dSOded Gabbay  */
1162c204f3dSOded Gabbay void accel_set_device_instance_params(struct device *kdev, int index)
1172c204f3dSOded Gabbay {
1182c204f3dSOded Gabbay 	kdev->devt = MKDEV(ACCEL_MAJOR, index);
1192c204f3dSOded Gabbay 	kdev->class = accel_class;
1202c204f3dSOded Gabbay 	kdev->type = &accel_sysfs_device_minor;
1212c204f3dSOded Gabbay }
1222c204f3dSOded Gabbay 
1232c204f3dSOded Gabbay /**
1242c204f3dSOded Gabbay  * accel_minor_alloc() - Allocates a new accel minor
1252c204f3dSOded Gabbay  *
1262c204f3dSOded Gabbay  * This function access the accel minors idr and allocates from it
1272c204f3dSOded Gabbay  * a new id to represent a new accel minor
1282c204f3dSOded Gabbay  *
1292c204f3dSOded Gabbay  * Return: A new id on success or error code in case idr_alloc failed
1302c204f3dSOded Gabbay  */
1312c204f3dSOded Gabbay int accel_minor_alloc(void)
1322c204f3dSOded Gabbay {
1332c204f3dSOded Gabbay 	unsigned long flags;
1342c204f3dSOded Gabbay 	int r;
1352c204f3dSOded Gabbay 
1362c204f3dSOded Gabbay 	spin_lock_irqsave(&accel_minor_lock, flags);
1372c204f3dSOded Gabbay 	r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
1382c204f3dSOded Gabbay 	spin_unlock_irqrestore(&accel_minor_lock, flags);
1392c204f3dSOded Gabbay 
1402c204f3dSOded Gabbay 	return r;
1412c204f3dSOded Gabbay }
1422c204f3dSOded Gabbay 
1432c204f3dSOded Gabbay /**
1442c204f3dSOded Gabbay  * accel_minor_remove() - Remove an accel minor
1452c204f3dSOded Gabbay  * @index: The minor id to remove.
1462c204f3dSOded Gabbay  *
1472c204f3dSOded Gabbay  * This function access the accel minors idr and removes from
1482c204f3dSOded Gabbay  * it the member with the id that is passed to this function.
1492c204f3dSOded Gabbay  */
1502c204f3dSOded Gabbay void accel_minor_remove(int index)
1512c204f3dSOded Gabbay {
1522c204f3dSOded Gabbay 	unsigned long flags;
1532c204f3dSOded Gabbay 
1542c204f3dSOded Gabbay 	spin_lock_irqsave(&accel_minor_lock, flags);
1552c204f3dSOded Gabbay 	idr_remove(&accel_minors_idr, index);
1562c204f3dSOded Gabbay 	spin_unlock_irqrestore(&accel_minor_lock, flags);
1572c204f3dSOded Gabbay }
1582c204f3dSOded Gabbay 
1592c204f3dSOded Gabbay /**
1602c204f3dSOded Gabbay  * accel_minor_replace() - Replace minor pointer in accel minors idr.
1612c204f3dSOded Gabbay  * @minor: Pointer to the new minor.
1622c204f3dSOded Gabbay  * @index: The minor id to replace.
1632c204f3dSOded Gabbay  *
1642c204f3dSOded Gabbay  * This function access the accel minors idr structure and replaces the pointer
1652c204f3dSOded Gabbay  * that is associated with an existing id. Because the minor pointer can be
1662c204f3dSOded Gabbay  * NULL, we need to explicitly pass the index.
1672c204f3dSOded Gabbay  *
1682c204f3dSOded Gabbay  * Return: 0 for success, negative value for error
1692c204f3dSOded Gabbay  */
1702c204f3dSOded Gabbay void accel_minor_replace(struct drm_minor *minor, int index)
1712c204f3dSOded Gabbay {
1722c204f3dSOded Gabbay 	unsigned long flags;
1732c204f3dSOded Gabbay 
1742c204f3dSOded Gabbay 	spin_lock_irqsave(&accel_minor_lock, flags);
1752c204f3dSOded Gabbay 	idr_replace(&accel_minors_idr, minor, index);
1762c204f3dSOded Gabbay 	spin_unlock_irqrestore(&accel_minor_lock, flags);
1772c204f3dSOded Gabbay }
1782c204f3dSOded Gabbay 
1792c204f3dSOded Gabbay /*
1802c204f3dSOded Gabbay  * Looks up the given minor-ID and returns the respective DRM-minor object. The
1812c204f3dSOded Gabbay  * refence-count of the underlying device is increased so you must release this
1822c204f3dSOded Gabbay  * object with accel_minor_release().
1832c204f3dSOded Gabbay  *
1842c204f3dSOded Gabbay  * The object can be only a drm_minor that represents an accel device.
1852c204f3dSOded Gabbay  *
1862c204f3dSOded Gabbay  * As long as you hold this minor, it is guaranteed that the object and the
1872c204f3dSOded Gabbay  * minor->dev pointer will stay valid! However, the device may get unplugged and
1882c204f3dSOded Gabbay  * unregistered while you hold the minor.
1892c204f3dSOded Gabbay  */
1902c204f3dSOded Gabbay static struct drm_minor *accel_minor_acquire(unsigned int minor_id)
1912c204f3dSOded Gabbay {
1922c204f3dSOded Gabbay 	struct drm_minor *minor;
1932c204f3dSOded Gabbay 	unsigned long flags;
1942c204f3dSOded Gabbay 
1952c204f3dSOded Gabbay 	spin_lock_irqsave(&accel_minor_lock, flags);
1962c204f3dSOded Gabbay 	minor = idr_find(&accel_minors_idr, minor_id);
1972c204f3dSOded Gabbay 	if (minor)
1982c204f3dSOded Gabbay 		drm_dev_get(minor->dev);
1992c204f3dSOded Gabbay 	spin_unlock_irqrestore(&accel_minor_lock, flags);
2002c204f3dSOded Gabbay 
2012c204f3dSOded Gabbay 	if (!minor) {
2022c204f3dSOded Gabbay 		return ERR_PTR(-ENODEV);
2032c204f3dSOded Gabbay 	} else if (drm_dev_is_unplugged(minor->dev)) {
2042c204f3dSOded Gabbay 		drm_dev_put(minor->dev);
2052c204f3dSOded Gabbay 		return ERR_PTR(-ENODEV);
2062c204f3dSOded Gabbay 	}
2072c204f3dSOded Gabbay 
2082c204f3dSOded Gabbay 	return minor;
2092c204f3dSOded Gabbay }
2102c204f3dSOded Gabbay 
2112c204f3dSOded Gabbay static void accel_minor_release(struct drm_minor *minor)
2122c204f3dSOded Gabbay {
2132c204f3dSOded Gabbay 	drm_dev_put(minor->dev);
2142c204f3dSOded Gabbay }
2152c204f3dSOded Gabbay 
2162c204f3dSOded Gabbay /**
2172c204f3dSOded Gabbay  * accel_open - open method for ACCEL file
2182c204f3dSOded Gabbay  * @inode: device inode
2192c204f3dSOded Gabbay  * @filp: file pointer.
2202c204f3dSOded Gabbay  *
2212c204f3dSOded Gabbay  * This function must be used by drivers as their &file_operations.open method.
2222c204f3dSOded Gabbay  * It looks up the correct ACCEL device and instantiates all the per-file
2232c204f3dSOded Gabbay  * resources for it. It also calls the &drm_driver.open driver callback.
2242c204f3dSOded Gabbay  *
2252c204f3dSOded Gabbay  * Return: 0 on success or negative errno value on failure.
2262c204f3dSOded Gabbay  */
2272c204f3dSOded Gabbay int accel_open(struct inode *inode, struct file *filp)
2282c204f3dSOded Gabbay {
2292c204f3dSOded Gabbay 	struct drm_device *dev;
2302c204f3dSOded Gabbay 	struct drm_minor *minor;
2312c204f3dSOded Gabbay 	int retcode;
2322c204f3dSOded Gabbay 
2332c204f3dSOded Gabbay 	minor = accel_minor_acquire(iminor(inode));
2342c204f3dSOded Gabbay 	if (IS_ERR(minor))
2352c204f3dSOded Gabbay 		return PTR_ERR(minor);
2362c204f3dSOded Gabbay 
2372c204f3dSOded Gabbay 	dev = minor->dev;
2382c204f3dSOded Gabbay 
2392c204f3dSOded Gabbay 	atomic_fetch_inc(&dev->open_count);
2402c204f3dSOded Gabbay 
2412c204f3dSOded Gabbay 	/* share address_space across all char-devs of a single device */
2422c204f3dSOded Gabbay 	filp->f_mapping = dev->anon_inode->i_mapping;
2432c204f3dSOded Gabbay 
2442c204f3dSOded Gabbay 	retcode = drm_open_helper(filp, minor);
2452c204f3dSOded Gabbay 	if (retcode)
2462c204f3dSOded Gabbay 		goto err_undo;
2472c204f3dSOded Gabbay 
2482c204f3dSOded Gabbay 	return 0;
2492c204f3dSOded Gabbay 
2502c204f3dSOded Gabbay err_undo:
2512c204f3dSOded Gabbay 	atomic_dec(&dev->open_count);
2522c204f3dSOded Gabbay 	accel_minor_release(minor);
2532c204f3dSOded Gabbay 	return retcode;
2542c204f3dSOded Gabbay }
2552c204f3dSOded Gabbay EXPORT_SYMBOL_GPL(accel_open);
2562c204f3dSOded Gabbay 
2578bf48897SOded Gabbay static int accel_stub_open(struct inode *inode, struct file *filp)
2588bf48897SOded Gabbay {
2592c204f3dSOded Gabbay 	const struct file_operations *new_fops;
2602c204f3dSOded Gabbay 	struct drm_minor *minor;
2612c204f3dSOded Gabbay 	int err;
2622c204f3dSOded Gabbay 
2632c204f3dSOded Gabbay 	minor = accel_minor_acquire(iminor(inode));
2642c204f3dSOded Gabbay 	if (IS_ERR(minor))
2652c204f3dSOded Gabbay 		return PTR_ERR(minor);
2662c204f3dSOded Gabbay 
2672c204f3dSOded Gabbay 	new_fops = fops_get(minor->dev->driver->fops);
2682c204f3dSOded Gabbay 	if (!new_fops) {
2692c204f3dSOded Gabbay 		err = -ENODEV;
2702c204f3dSOded Gabbay 		goto out;
2712c204f3dSOded Gabbay 	}
2722c204f3dSOded Gabbay 
2732c204f3dSOded Gabbay 	replace_fops(filp, new_fops);
2742c204f3dSOded Gabbay 	if (filp->f_op->open)
2752c204f3dSOded Gabbay 		err = filp->f_op->open(inode, filp);
2762c204f3dSOded Gabbay 	else
2772c204f3dSOded Gabbay 		err = 0;
2782c204f3dSOded Gabbay 
2792c204f3dSOded Gabbay out:
2802c204f3dSOded Gabbay 	accel_minor_release(minor);
2812c204f3dSOded Gabbay 
2822c204f3dSOded Gabbay 	return err;
2838bf48897SOded Gabbay }
2848bf48897SOded Gabbay 
2858bf48897SOded Gabbay static const struct file_operations accel_stub_fops = {
2868bf48897SOded Gabbay 	.owner = THIS_MODULE,
2878bf48897SOded Gabbay 	.open = accel_stub_open,
2888bf48897SOded Gabbay 	.llseek = noop_llseek,
2898bf48897SOded Gabbay };
2908bf48897SOded Gabbay 
2918bf48897SOded Gabbay void accel_core_exit(void)
2928bf48897SOded Gabbay {
2938bf48897SOded Gabbay 	unregister_chrdev(ACCEL_MAJOR, "accel");
2948bf48897SOded Gabbay 	debugfs_remove(accel_debugfs_root);
2958bf48897SOded Gabbay 	accel_sysfs_destroy();
2962c204f3dSOded Gabbay 	idr_destroy(&accel_minors_idr);
2978bf48897SOded Gabbay }
2988bf48897SOded Gabbay 
2998bf48897SOded Gabbay int __init accel_core_init(void)
3008bf48897SOded Gabbay {
3018bf48897SOded Gabbay 	int ret;
3028bf48897SOded Gabbay 
3032c204f3dSOded Gabbay 	idr_init(&accel_minors_idr);
3042c204f3dSOded Gabbay 
3058bf48897SOded Gabbay 	ret = accel_sysfs_init();
3068bf48897SOded Gabbay 	if (ret < 0) {
3078bf48897SOded Gabbay 		DRM_ERROR("Cannot create ACCEL class: %d\n", ret);
3088bf48897SOded Gabbay 		goto error;
3098bf48897SOded Gabbay 	}
3108bf48897SOded Gabbay 
3118bf48897SOded Gabbay 	accel_debugfs_root = debugfs_create_dir("accel", NULL);
3128bf48897SOded Gabbay 
3138bf48897SOded Gabbay 	ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);
3148bf48897SOded Gabbay 	if (ret < 0)
3158bf48897SOded Gabbay 		DRM_ERROR("Cannot register ACCEL major: %d\n", ret);
3168bf48897SOded Gabbay 
3178bf48897SOded Gabbay error:
3188bf48897SOded Gabbay 	/*
3198bf48897SOded Gabbay 	 * Any cleanup due to errors will be done in drm_core_exit() that
3208bf48897SOded Gabbay 	 * will call accel_core_exit()
3218bf48897SOded Gabbay 	 */
3228bf48897SOded Gabbay 	return ret;
3238bf48897SOded Gabbay }
324