162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Copyright 2022 HabanaLabs, Ltd.
562306a36Sopenharmony_ci * All Rights Reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/debugfs.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/idr.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <drm/drm_accel.h>
1462306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
1562306a36Sopenharmony_ci#include <drm/drm_drv.h>
1662306a36Sopenharmony_ci#include <drm/drm_file.h>
1762306a36Sopenharmony_ci#include <drm/drm_ioctl.h>
1862306a36Sopenharmony_ci#include <drm/drm_print.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(accel_minor_lock);
2162306a36Sopenharmony_cistatic struct idr accel_minors_idr;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic struct dentry *accel_debugfs_root;
2462306a36Sopenharmony_cistatic struct class *accel_class;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic struct device_type accel_sysfs_device_minor = {
2762306a36Sopenharmony_ci	.name = "accel_minor"
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic char *accel_devnode(const struct device *dev, umode_t *mode)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int accel_sysfs_init(void)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	accel_class = class_create("accel");
3862306a36Sopenharmony_ci	if (IS_ERR(accel_class))
3962306a36Sopenharmony_ci		return PTR_ERR(accel_class);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	accel_class->devnode = accel_devnode;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	return 0;
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void accel_sysfs_destroy(void)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(accel_class))
4962306a36Sopenharmony_ci		return;
5062306a36Sopenharmony_ci	class_destroy(accel_class);
5162306a36Sopenharmony_ci	accel_class = NULL;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int accel_name_info(struct seq_file *m, void *data)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct drm_info_node *node = (struct drm_info_node *) m->private;
5762306a36Sopenharmony_ci	struct drm_minor *minor = node->minor;
5862306a36Sopenharmony_ci	struct drm_device *dev = minor->dev;
5962306a36Sopenharmony_ci	struct drm_master *master;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	mutex_lock(&dev->master_mutex);
6262306a36Sopenharmony_ci	master = dev->master;
6362306a36Sopenharmony_ci	seq_printf(m, "%s", dev->driver->name);
6462306a36Sopenharmony_ci	if (dev->dev)
6562306a36Sopenharmony_ci		seq_printf(m, " dev=%s", dev_name(dev->dev));
6662306a36Sopenharmony_ci	if (master && master->unique)
6762306a36Sopenharmony_ci		seq_printf(m, " master=%s", master->unique);
6862306a36Sopenharmony_ci	if (dev->unique)
6962306a36Sopenharmony_ci		seq_printf(m, " unique=%s", dev->unique);
7062306a36Sopenharmony_ci	seq_puts(m, "\n");
7162306a36Sopenharmony_ci	mutex_unlock(&dev->master_mutex);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return 0;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct drm_info_list accel_debugfs_list[] = {
7762306a36Sopenharmony_ci	{"name", accel_name_info, 0}
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci#define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/**
8262306a36Sopenharmony_ci * accel_debugfs_init() - Initialize debugfs for accel minor
8362306a36Sopenharmony_ci * @minor: Pointer to the drm_minor instance.
8462306a36Sopenharmony_ci * @minor_id: The minor's id
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * This function initializes the drm minor's debugfs members and creates
8762306a36Sopenharmony_ci * a root directory for the minor in debugfs. It also creates common files
8862306a36Sopenharmony_ci * for accelerators and calls the driver's debugfs init callback.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_civoid accel_debugfs_init(struct drm_minor *minor, int minor_id)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct drm_device *dev = minor->dev;
9362306a36Sopenharmony_ci	char name[64];
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	INIT_LIST_HEAD(&minor->debugfs_list);
9662306a36Sopenharmony_ci	mutex_init(&minor->debugfs_lock);
9762306a36Sopenharmony_ci	sprintf(name, "%d", minor_id);
9862306a36Sopenharmony_ci	minor->debugfs_root = debugfs_create_dir(name, accel_debugfs_root);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,
10162306a36Sopenharmony_ci				 minor->debugfs_root, minor);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	if (dev->driver->debugfs_init)
10462306a36Sopenharmony_ci		dev->driver->debugfs_init(minor);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/**
10862306a36Sopenharmony_ci * accel_set_device_instance_params() - Set some device parameters for accel device
10962306a36Sopenharmony_ci * @kdev: Pointer to the device instance.
11062306a36Sopenharmony_ci * @index: The minor's index
11162306a36Sopenharmony_ci *
11262306a36Sopenharmony_ci * This function creates the dev_t of the device using the accel major and
11362306a36Sopenharmony_ci * the device's minor number. In addition, it sets the class and type of the
11462306a36Sopenharmony_ci * device instance to the accel sysfs class and device type, respectively.
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_civoid accel_set_device_instance_params(struct device *kdev, int index)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	kdev->devt = MKDEV(ACCEL_MAJOR, index);
11962306a36Sopenharmony_ci	kdev->class = accel_class;
12062306a36Sopenharmony_ci	kdev->type = &accel_sysfs_device_minor;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/**
12462306a36Sopenharmony_ci * accel_minor_alloc() - Allocates a new accel minor
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * This function access the accel minors idr and allocates from it
12762306a36Sopenharmony_ci * a new id to represent a new accel minor
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Return: A new id on success or error code in case idr_alloc failed
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ciint accel_minor_alloc(void)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	unsigned long flags;
13462306a36Sopenharmony_ci	int r;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	spin_lock_irqsave(&accel_minor_lock, flags);
13762306a36Sopenharmony_ci	r = idr_alloc(&accel_minors_idr, NULL, 0, ACCEL_MAX_MINORS, GFP_NOWAIT);
13862306a36Sopenharmony_ci	spin_unlock_irqrestore(&accel_minor_lock, flags);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return r;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/**
14462306a36Sopenharmony_ci * accel_minor_remove() - Remove an accel minor
14562306a36Sopenharmony_ci * @index: The minor id to remove.
14662306a36Sopenharmony_ci *
14762306a36Sopenharmony_ci * This function access the accel minors idr and removes from
14862306a36Sopenharmony_ci * it the member with the id that is passed to this function.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_civoid accel_minor_remove(int index)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	unsigned long flags;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	spin_lock_irqsave(&accel_minor_lock, flags);
15562306a36Sopenharmony_ci	idr_remove(&accel_minors_idr, index);
15662306a36Sopenharmony_ci	spin_unlock_irqrestore(&accel_minor_lock, flags);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/**
16062306a36Sopenharmony_ci * accel_minor_replace() - Replace minor pointer in accel minors idr.
16162306a36Sopenharmony_ci * @minor: Pointer to the new minor.
16262306a36Sopenharmony_ci * @index: The minor id to replace.
16362306a36Sopenharmony_ci *
16462306a36Sopenharmony_ci * This function access the accel minors idr structure and replaces the pointer
16562306a36Sopenharmony_ci * that is associated with an existing id. Because the minor pointer can be
16662306a36Sopenharmony_ci * NULL, we need to explicitly pass the index.
16762306a36Sopenharmony_ci *
16862306a36Sopenharmony_ci * Return: 0 for success, negative value for error
16962306a36Sopenharmony_ci */
17062306a36Sopenharmony_civoid accel_minor_replace(struct drm_minor *minor, int index)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	unsigned long flags;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	spin_lock_irqsave(&accel_minor_lock, flags);
17562306a36Sopenharmony_ci	idr_replace(&accel_minors_idr, minor, index);
17662306a36Sopenharmony_ci	spin_unlock_irqrestore(&accel_minor_lock, flags);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/*
18062306a36Sopenharmony_ci * Looks up the given minor-ID and returns the respective DRM-minor object. The
18162306a36Sopenharmony_ci * refence-count of the underlying device is increased so you must release this
18262306a36Sopenharmony_ci * object with accel_minor_release().
18362306a36Sopenharmony_ci *
18462306a36Sopenharmony_ci * The object can be only a drm_minor that represents an accel device.
18562306a36Sopenharmony_ci *
18662306a36Sopenharmony_ci * As long as you hold this minor, it is guaranteed that the object and the
18762306a36Sopenharmony_ci * minor->dev pointer will stay valid! However, the device may get unplugged and
18862306a36Sopenharmony_ci * unregistered while you hold the minor.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_cistatic struct drm_minor *accel_minor_acquire(unsigned int minor_id)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct drm_minor *minor;
19362306a36Sopenharmony_ci	unsigned long flags;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	spin_lock_irqsave(&accel_minor_lock, flags);
19662306a36Sopenharmony_ci	minor = idr_find(&accel_minors_idr, minor_id);
19762306a36Sopenharmony_ci	if (minor)
19862306a36Sopenharmony_ci		drm_dev_get(minor->dev);
19962306a36Sopenharmony_ci	spin_unlock_irqrestore(&accel_minor_lock, flags);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (!minor) {
20262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
20362306a36Sopenharmony_ci	} else if (drm_dev_is_unplugged(minor->dev)) {
20462306a36Sopenharmony_ci		drm_dev_put(minor->dev);
20562306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return minor;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void accel_minor_release(struct drm_minor *minor)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	drm_dev_put(minor->dev);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/**
21762306a36Sopenharmony_ci * accel_open - open method for ACCEL file
21862306a36Sopenharmony_ci * @inode: device inode
21962306a36Sopenharmony_ci * @filp: file pointer.
22062306a36Sopenharmony_ci *
22162306a36Sopenharmony_ci * This function must be used by drivers as their &file_operations.open method.
22262306a36Sopenharmony_ci * It looks up the correct ACCEL device and instantiates all the per-file
22362306a36Sopenharmony_ci * resources for it. It also calls the &drm_driver.open driver callback.
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * Return: 0 on success or negative errno value on failure.
22662306a36Sopenharmony_ci */
22762306a36Sopenharmony_ciint accel_open(struct inode *inode, struct file *filp)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct drm_device *dev;
23062306a36Sopenharmony_ci	struct drm_minor *minor;
23162306a36Sopenharmony_ci	int retcode;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	minor = accel_minor_acquire(iminor(inode));
23462306a36Sopenharmony_ci	if (IS_ERR(minor))
23562306a36Sopenharmony_ci		return PTR_ERR(minor);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	dev = minor->dev;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	atomic_fetch_inc(&dev->open_count);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* share address_space across all char-devs of a single device */
24262306a36Sopenharmony_ci	filp->f_mapping = dev->anon_inode->i_mapping;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	retcode = drm_open_helper(filp, minor);
24562306a36Sopenharmony_ci	if (retcode)
24662306a36Sopenharmony_ci		goto err_undo;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cierr_undo:
25162306a36Sopenharmony_ci	atomic_dec(&dev->open_count);
25262306a36Sopenharmony_ci	accel_minor_release(minor);
25362306a36Sopenharmony_ci	return retcode;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(accel_open);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int accel_stub_open(struct inode *inode, struct file *filp)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	const struct file_operations *new_fops;
26062306a36Sopenharmony_ci	struct drm_minor *minor;
26162306a36Sopenharmony_ci	int err;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	minor = accel_minor_acquire(iminor(inode));
26462306a36Sopenharmony_ci	if (IS_ERR(minor))
26562306a36Sopenharmony_ci		return PTR_ERR(minor);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	new_fops = fops_get(minor->dev->driver->fops);
26862306a36Sopenharmony_ci	if (!new_fops) {
26962306a36Sopenharmony_ci		err = -ENODEV;
27062306a36Sopenharmony_ci		goto out;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	replace_fops(filp, new_fops);
27462306a36Sopenharmony_ci	if (filp->f_op->open)
27562306a36Sopenharmony_ci		err = filp->f_op->open(inode, filp);
27662306a36Sopenharmony_ci	else
27762306a36Sopenharmony_ci		err = 0;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ciout:
28062306a36Sopenharmony_ci	accel_minor_release(minor);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return err;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic const struct file_operations accel_stub_fops = {
28662306a36Sopenharmony_ci	.owner = THIS_MODULE,
28762306a36Sopenharmony_ci	.open = accel_stub_open,
28862306a36Sopenharmony_ci	.llseek = noop_llseek,
28962306a36Sopenharmony_ci};
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_civoid accel_core_exit(void)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	unregister_chrdev(ACCEL_MAJOR, "accel");
29462306a36Sopenharmony_ci	debugfs_remove(accel_debugfs_root);
29562306a36Sopenharmony_ci	accel_sysfs_destroy();
29662306a36Sopenharmony_ci	idr_destroy(&accel_minors_idr);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ciint __init accel_core_init(void)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int ret;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	idr_init(&accel_minors_idr);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ret = accel_sysfs_init();
30662306a36Sopenharmony_ci	if (ret < 0) {
30762306a36Sopenharmony_ci		DRM_ERROR("Cannot create ACCEL class: %d\n", ret);
30862306a36Sopenharmony_ci		goto error;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	accel_debugfs_root = debugfs_create_dir("accel", NULL);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);
31462306a36Sopenharmony_ci	if (ret < 0)
31562306a36Sopenharmony_ci		DRM_ERROR("Cannot register ACCEL major: %d\n", ret);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cierror:
31862306a36Sopenharmony_ci	/*
31962306a36Sopenharmony_ci	 * Any cleanup due to errors will be done in drm_core_exit() that
32062306a36Sopenharmony_ci	 * will call accel_core_exit()
32162306a36Sopenharmony_ci	 */
32262306a36Sopenharmony_ci	return ret;
32362306a36Sopenharmony_ci}
324