Home | History | Annotate | Line # | Download | only in drm
drm_debugfs.c revision 1.4
      1 /*	$NetBSD: drm_debugfs.c,v 1.4 2018/08/27 04:58:19 riastradh Exp $	*/
      2 
      3 /**
      4  * \file drm_debugfs.c
      5  * debugfs support for DRM
      6  *
      7  * \author Ben Gamari <bgamari (at) gmail.com>
      8  */
      9 
     10 /*
     11  * Created: Sun Dec 21 13:08:50 2008 by bgamari (at) gmail.com
     12  *
     13  * Copyright 2008 Ben Gamari <bgamari (at) gmail.com>
     14  *
     15  * Permission is hereby granted, free of charge, to any person obtaining a
     16  * copy of this software and associated documentation files (the "Software"),
     17  * to deal in the Software without restriction, including without limitation
     18  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     19  * and/or sell copies of the Software, and to permit persons to whom the
     20  * Software is furnished to do so, subject to the following conditions:
     21  *
     22  * The above copyright notice and this permission notice (including the next
     23  * paragraph) shall be included in all copies or substantial portions of the
     24  * Software.
     25  *
     26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     29  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     30  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     31  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     32  * OTHER DEALINGS IN THE SOFTWARE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: drm_debugfs.c,v 1.4 2018/08/27 04:58:19 riastradh Exp $");
     37 
     38 #include <linux/debugfs.h>
     39 #include <linux/seq_file.h>
     40 #include <linux/slab.h>
     41 #include <linux/export.h>
     42 #include <drm/drmP.h>
     43 #include <drm/drm_edid.h>
     44 #include "drm_internal.h"
     45 
     46 #if defined(CONFIG_DEBUG_FS)
     47 
     48 /***************************************************
     49  * Initialization, etc.
     50  **************************************************/
     51 
     52 static const struct drm_info_list drm_debugfs_list[] = {
     53 	{"name", drm_name_info, 0},
     54 	{"vm", drm_vm_info, 0},
     55 	{"clients", drm_clients_info, 0},
     56 	{"bufs", drm_bufs_info, 0},
     57 	{"gem_names", drm_gem_name_info, DRIVER_GEM},
     58 	{"vma", drm_vma_info, 0},
     59 };
     60 #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
     61 
     62 
     63 static int drm_debugfs_open(struct inode *inode, struct file *file)
     64 {
     65 	struct drm_info_node *node = inode->i_private;
     66 
     67 	return single_open(file, node->info_ent->show, node);
     68 }
     69 
     70 
     71 static const struct file_operations drm_debugfs_fops = {
     72 	.owner = THIS_MODULE,
     73 	.open = drm_debugfs_open,
     74 	.read = seq_read,
     75 	.llseek = seq_lseek,
     76 	.release = single_release,
     77 };
     78 
     79 
     80 /**
     81  * Initialize a given set of debugfs files for a device
     82  *
     83  * \param files The array of files to create
     84  * \param count The number of files given
     85  * \param root DRI debugfs dir entry.
     86  * \param minor device minor number
     87  * \return Zero on success, non-zero on failure
     88  *
     89  * Create a given set of debugfs files represented by an array of
     90  * gdm_debugfs_lists in the given root directory.
     91  */
     92 int drm_debugfs_create_files(const struct drm_info_list *files, int count,
     93 			     struct dentry *root, struct drm_minor *minor)
     94 {
     95 	struct drm_device *dev = minor->dev;
     96 	struct dentry *ent;
     97 	struct drm_info_node *tmp;
     98 	int i, ret;
     99 
    100 	for (i = 0; i < count; i++) {
    101 		u32 features = files[i].driver_features;
    102 
    103 		if (features != 0 &&
    104 		    (dev->driver->driver_features & features) != features)
    105 			continue;
    106 
    107 		tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
    108 		if (tmp == NULL) {
    109 			ret = -1;
    110 			goto fail;
    111 		}
    112 		ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
    113 					  root, tmp, &drm_debugfs_fops);
    114 		if (!ent) {
    115 			DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n",
    116 				  root->d_name.name, files[i].name);
    117 			kfree(tmp);
    118 			ret = -1;
    119 			goto fail;
    120 		}
    121 
    122 		tmp->minor = minor;
    123 		tmp->dent = ent;
    124 		tmp->info_ent = &files[i];
    125 
    126 		mutex_lock(&minor->debugfs_lock);
    127 		list_add(&tmp->list, &minor->debugfs_list);
    128 		mutex_unlock(&minor->debugfs_lock);
    129 	}
    130 	return 0;
    131 
    132 fail:
    133 	drm_debugfs_remove_files(files, count, minor);
    134 	return ret;
    135 }
    136 EXPORT_SYMBOL(drm_debugfs_create_files);
    137 
    138 /**
    139  * Initialize the DRI debugfs filesystem for a device
    140  *
    141  * \param dev DRM device
    142  * \param minor device minor number
    143  * \param root DRI debugfs dir entry.
    144  *
    145  * Create the DRI debugfs root entry "/sys/kernel/debug/dri", the device debugfs root entry
    146  * "/sys/kernel/debug/dri/%minor%/", and each entry in debugfs_list as
    147  * "/sys/kernel/debug/dri/%minor%/%name%".
    148  */
    149 int drm_debugfs_init(struct drm_minor *minor, int minor_id,
    150 		     struct dentry *root)
    151 {
    152 	struct drm_device *dev = minor->dev;
    153 	char name[64];
    154 	int ret;
    155 
    156 	INIT_LIST_HEAD(&minor->debugfs_list);
    157 	mutex_init(&minor->debugfs_lock);
    158 	snprintf(name, sizeof(name), "%d", minor_id);
    159 	minor->debugfs_root = debugfs_create_dir(name, root);
    160 	if (!minor->debugfs_root) {
    161 		DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s\n", name);
    162 		return -1;
    163 	}
    164 
    165 	ret = drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES,
    166 				       minor->debugfs_root, minor);
    167 	if (ret) {
    168 		debugfs_remove(minor->debugfs_root);
    169 		minor->debugfs_root = NULL;
    170 		DRM_ERROR("Failed to create core drm debugfs files\n");
    171 		return ret;
    172 	}
    173 
    174 	if (dev->driver->debugfs_init) {
    175 		ret = dev->driver->debugfs_init(minor);
    176 		if (ret) {
    177 			DRM_ERROR("DRM: Driver failed to initialize "
    178 				  "/sys/kernel/debug/dri.\n");
    179 			return ret;
    180 		}
    181 	}
    182 	return 0;
    183 }
    184 
    185 
    186 /**
    187  * Remove a list of debugfs files
    188  *
    189  * \param files The list of files
    190  * \param count The number of files
    191  * \param minor The minor of which we should remove the files
    192  * \return always zero.
    193  *
    194  * Remove all debugfs entries created by debugfs_init().
    195  */
    196 int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
    197 			     struct drm_minor *minor)
    198 {
    199 	struct list_head *pos, *q;
    200 	struct drm_info_node *tmp;
    201 	int i;
    202 
    203 	mutex_lock(&minor->debugfs_lock);
    204 	for (i = 0; i < count; i++) {
    205 		list_for_each_safe(pos, q, &minor->debugfs_list) {
    206 			tmp = list_entry(pos, struct drm_info_node, list);
    207 			if (tmp->info_ent == &files[i]) {
    208 				debugfs_remove(tmp->dent);
    209 				list_del(pos);
    210 				kfree(tmp);
    211 			}
    212 		}
    213 	}
    214 	mutex_unlock(&minor->debugfs_lock);
    215 	return 0;
    216 }
    217 EXPORT_SYMBOL(drm_debugfs_remove_files);
    218 
    219 /**
    220  * Cleanup the debugfs filesystem resources.
    221  *
    222  * \param minor device minor number.
    223  * \return always zero.
    224  *
    225  * Remove all debugfs entries created by debugfs_init().
    226  */
    227 int drm_debugfs_cleanup(struct drm_minor *minor)
    228 {
    229 	struct drm_device *dev = minor->dev;
    230 
    231 	if (!minor->debugfs_root)
    232 		return 0;
    233 
    234 	if (dev->driver->debugfs_cleanup)
    235 		dev->driver->debugfs_cleanup(minor);
    236 
    237 	drm_debugfs_remove_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, minor);
    238 
    239 	debugfs_remove(minor->debugfs_root);
    240 	minor->debugfs_root = NULL;
    241 
    242 	return 0;
    243 }
    244 
    245 static int connector_show(struct seq_file *m, void *data)
    246 {
    247 	struct drm_connector *connector = m->private;
    248 	const char *status;
    249 
    250 	switch (connector->force) {
    251 	case DRM_FORCE_ON:
    252 		status = "on\n";
    253 		break;
    254 
    255 	case DRM_FORCE_ON_DIGITAL:
    256 		status = "digital\n";
    257 		break;
    258 
    259 	case DRM_FORCE_OFF:
    260 		status = "off\n";
    261 		break;
    262 
    263 	case DRM_FORCE_UNSPECIFIED:
    264 		status = "unspecified\n";
    265 		break;
    266 
    267 	default:
    268 		return 0;
    269 	}
    270 
    271 	seq_puts(m, status);
    272 
    273 	return 0;
    274 }
    275 
    276 static int connector_open(struct inode *inode, struct file *file)
    277 {
    278 	struct drm_connector *dev = inode->i_private;
    279 
    280 	return single_open(file, connector_show, dev);
    281 }
    282 
    283 static ssize_t connector_write(struct file *file, const char __user *ubuf,
    284 			       size_t len, loff_t *offp)
    285 {
    286 	struct seq_file *m = file->private_data;
    287 	struct drm_connector *connector = m->private;
    288 	char buf[12];
    289 
    290 	if (len > sizeof(buf) - 1)
    291 		return -EINVAL;
    292 
    293 	if (copy_from_user(buf, ubuf, len))
    294 		return -EFAULT;
    295 
    296 	buf[len] = '\0';
    297 
    298 	if (!strcmp(buf, "on"))
    299 		connector->force = DRM_FORCE_ON;
    300 	else if (!strcmp(buf, "digital"))
    301 		connector->force = DRM_FORCE_ON_DIGITAL;
    302 	else if (!strcmp(buf, "off"))
    303 		connector->force = DRM_FORCE_OFF;
    304 	else if (!strcmp(buf, "unspecified"))
    305 		connector->force = DRM_FORCE_UNSPECIFIED;
    306 	else
    307 		return -EINVAL;
    308 
    309 	return len;
    310 }
    311 
    312 static int edid_show(struct seq_file *m, void *data)
    313 {
    314 	struct drm_connector *connector = m->private;
    315 	struct drm_property_blob *edid = connector->edid_blob_ptr;
    316 
    317 	if (connector->override_edid && edid)
    318 		seq_write(m, edid->data, edid->length);
    319 
    320 	return 0;
    321 }
    322 
    323 static int edid_open(struct inode *inode, struct file *file)
    324 {
    325 	struct drm_connector *dev = inode->i_private;
    326 
    327 	return single_open(file, edid_show, dev);
    328 }
    329 
    330 static ssize_t edid_write(struct file *file, const char __user *ubuf,
    331 			  size_t len, loff_t *offp)
    332 {
    333 	struct seq_file *m = file->private_data;
    334 	struct drm_connector *connector = m->private;
    335 	char *buf;
    336 	struct edid *edid;
    337 	int ret;
    338 
    339 	buf = memdup_user(ubuf, len);
    340 	if (IS_ERR(buf))
    341 		return PTR_ERR(buf);
    342 
    343 	edid = (struct edid *) buf;
    344 
    345 	if (len == 5 && !strncmp(buf, "reset", 5)) {
    346 		connector->override_edid = false;
    347 		ret = drm_mode_connector_update_edid_property(connector, NULL);
    348 	} else if (len < EDID_LENGTH ||
    349 		   EDID_LENGTH * (1 + edid->extensions) > len)
    350 		ret = -EINVAL;
    351 	else {
    352 		connector->override_edid = false;
    353 		ret = drm_mode_connector_update_edid_property(connector, edid);
    354 		if (!ret)
    355 			connector->override_edid = true;
    356 	}
    357 
    358 	kfree(buf);
    359 
    360 	return (ret) ? ret : len;
    361 }
    362 
    363 static const struct file_operations drm_edid_fops = {
    364 	.owner = THIS_MODULE,
    365 	.open = edid_open,
    366 	.read = seq_read,
    367 	.llseek = seq_lseek,
    368 	.release = single_release,
    369 	.write = edid_write
    370 };
    371 
    372 
    373 static const struct file_operations drm_connector_fops = {
    374 	.owner = THIS_MODULE,
    375 	.open = connector_open,
    376 	.read = seq_read,
    377 	.llseek = seq_lseek,
    378 	.release = single_release,
    379 	.write = connector_write
    380 };
    381 
    382 int drm_debugfs_connector_add(struct drm_connector *connector)
    383 {
    384 	struct drm_minor *minor = connector->dev->primary;
    385 	struct dentry *root, *ent;
    386 
    387 	if (!minor->debugfs_root)
    388 		return -1;
    389 
    390 	root = debugfs_create_dir(connector->name, minor->debugfs_root);
    391 	if (!root)
    392 		return -ENOMEM;
    393 
    394 	connector->debugfs_entry = root;
    395 
    396 	/* force */
    397 	ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector,
    398 				  &drm_connector_fops);
    399 	if (!ent)
    400 		goto error;
    401 
    402 	/* edid */
    403 	ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root,
    404 				  connector, &drm_edid_fops);
    405 	if (!ent)
    406 		goto error;
    407 
    408 	return 0;
    409 
    410 error:
    411 	debugfs_remove_recursive(connector->debugfs_entry);
    412 	connector->debugfs_entry = NULL;
    413 	return -ENOMEM;
    414 }
    415 
    416 void drm_debugfs_connector_remove(struct drm_connector *connector)
    417 {
    418 	if (!connector->debugfs_entry)
    419 		return;
    420 
    421 	debugfs_remove_recursive(connector->debugfs_entry);
    422 
    423 	connector->debugfs_entry = NULL;
    424 }
    425 
    426 #endif /* CONFIG_DEBUG_FS */
    427 
    428