225 lines · c
1// SPDX-License-Identifier: GPL-2.02 3/*4 * Copyright 2022 HabanaLabs, Ltd.5 * All Rights Reserved.6 *7 */8 9#include <linux/debugfs.h>10#include <linux/device.h>11#include <linux/xarray.h>12 13#include <drm/drm_accel.h>14#include <drm/drm_auth.h>15#include <drm/drm_debugfs.h>16#include <drm/drm_drv.h>17#include <drm/drm_file.h>18#include <drm/drm_ioctl.h>19#include <drm/drm_print.h>20 21DEFINE_XARRAY_ALLOC(accel_minors_xa);22 23static struct dentry *accel_debugfs_root;24 25static const struct device_type accel_sysfs_device_minor = {26 .name = "accel_minor"27};28 29static char *accel_devnode(const struct device *dev, umode_t *mode)30{31 return kasprintf(GFP_KERNEL, "accel/%s", dev_name(dev));32}33 34static const struct class accel_class = {35 .name = "accel",36 .devnode = accel_devnode,37};38 39static int accel_sysfs_init(void)40{41 return class_register(&accel_class);42}43 44static void accel_sysfs_destroy(void)45{46 class_unregister(&accel_class);47}48 49static int accel_name_info(struct seq_file *m, void *data)50{51 struct drm_info_node *node = (struct drm_info_node *) m->private;52 struct drm_minor *minor = node->minor;53 struct drm_device *dev = minor->dev;54 struct drm_master *master;55 56 mutex_lock(&dev->master_mutex);57 master = dev->master;58 seq_printf(m, "%s", dev->driver->name);59 if (dev->dev)60 seq_printf(m, " dev=%s", dev_name(dev->dev));61 if (master && master->unique)62 seq_printf(m, " master=%s", master->unique);63 if (dev->unique)64 seq_printf(m, " unique=%s", dev->unique);65 seq_puts(m, "\n");66 mutex_unlock(&dev->master_mutex);67 68 return 0;69}70 71static const struct drm_info_list accel_debugfs_list[] = {72 {"name", accel_name_info, 0}73};74#define ACCEL_DEBUGFS_ENTRIES ARRAY_SIZE(accel_debugfs_list)75 76/**77 * accel_debugfs_init() - Initialize debugfs for device78 * @dev: Pointer to the device instance.79 *80 * This function creates a root directory for the device in debugfs.81 */82void accel_debugfs_init(struct drm_device *dev)83{84 drm_debugfs_dev_init(dev, accel_debugfs_root);85}86 87/**88 * accel_debugfs_register() - Register debugfs for device89 * @dev: Pointer to the device instance.90 *91 * Creates common files for accelerators.92 */93void accel_debugfs_register(struct drm_device *dev)94{95 struct drm_minor *minor = dev->accel;96 97 minor->debugfs_root = dev->debugfs_root;98 99 drm_debugfs_create_files(accel_debugfs_list, ACCEL_DEBUGFS_ENTRIES,100 dev->debugfs_root, minor);101}102 103/**104 * accel_set_device_instance_params() - Set some device parameters for accel device105 * @kdev: Pointer to the device instance.106 * @index: The minor's index107 *108 * This function creates the dev_t of the device using the accel major and109 * the device's minor number. In addition, it sets the class and type of the110 * device instance to the accel sysfs class and device type, respectively.111 */112void accel_set_device_instance_params(struct device *kdev, int index)113{114 kdev->devt = MKDEV(ACCEL_MAJOR, index);115 kdev->class = &accel_class;116 kdev->type = &accel_sysfs_device_minor;117}118 119/**120 * accel_open - open method for ACCEL file121 * @inode: device inode122 * @filp: file pointer.123 *124 * This function must be used by drivers as their &file_operations.open method.125 * It looks up the correct ACCEL device and instantiates all the per-file126 * resources for it. It also calls the &drm_driver.open driver callback.127 *128 * Return: 0 on success or negative errno value on failure.129 */130int accel_open(struct inode *inode, struct file *filp)131{132 struct drm_device *dev;133 struct drm_minor *minor;134 int retcode;135 136 minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));137 if (IS_ERR(minor))138 return PTR_ERR(minor);139 140 dev = minor->dev;141 142 atomic_fetch_inc(&dev->open_count);143 144 /* share address_space across all char-devs of a single device */145 filp->f_mapping = dev->anon_inode->i_mapping;146 147 retcode = drm_open_helper(filp, minor);148 if (retcode)149 goto err_undo;150 151 return 0;152 153err_undo:154 atomic_dec(&dev->open_count);155 drm_minor_release(minor);156 return retcode;157}158EXPORT_SYMBOL_GPL(accel_open);159 160static int accel_stub_open(struct inode *inode, struct file *filp)161{162 const struct file_operations *new_fops;163 struct drm_minor *minor;164 int err;165 166 minor = drm_minor_acquire(&accel_minors_xa, iminor(inode));167 if (IS_ERR(minor))168 return PTR_ERR(minor);169 170 new_fops = fops_get(minor->dev->driver->fops);171 if (!new_fops) {172 err = -ENODEV;173 goto out;174 }175 176 replace_fops(filp, new_fops);177 if (filp->f_op->open)178 err = filp->f_op->open(inode, filp);179 else180 err = 0;181 182out:183 drm_minor_release(minor);184 185 return err;186}187 188static const struct file_operations accel_stub_fops = {189 .owner = THIS_MODULE,190 .open = accel_stub_open,191 .llseek = noop_llseek,192};193 194void accel_core_exit(void)195{196 unregister_chrdev(ACCEL_MAJOR, "accel");197 debugfs_remove(accel_debugfs_root);198 accel_sysfs_destroy();199 WARN_ON(!xa_empty(&accel_minors_xa));200}201 202int __init accel_core_init(void)203{204 int ret;205 206 ret = accel_sysfs_init();207 if (ret < 0) {208 DRM_ERROR("Cannot create ACCEL class: %d\n", ret);209 goto error;210 }211 212 accel_debugfs_root = debugfs_create_dir("accel", NULL);213 214 ret = register_chrdev(ACCEL_MAJOR, "accel", &accel_stub_fops);215 if (ret < 0)216 DRM_ERROR("Cannot register ACCEL major: %d\n", ret);217 218error:219 /*220 * Any cleanup due to errors will be done in drm_core_exit() that221 * will call accel_core_exit()222 */223 return ret;224}225