xref: /openbmc/linux/drivers/gpu/drm/drm_debugfs.c (revision 3e7759b94a0fcfdd6771caa64a37dda7ce825874)
128a62277SBen Gamari /*
228a62277SBen Gamari  * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com
328a62277SBen Gamari  *
428a62277SBen Gamari  * Copyright 2008 Ben Gamari <bgamari@gmail.com>
528a62277SBen Gamari  *
628a62277SBen Gamari  * Permission is hereby granted, free of charge, to any person obtaining a
728a62277SBen Gamari  * copy of this software and associated documentation files (the "Software"),
828a62277SBen Gamari  * to deal in the Software without restriction, including without limitation
928a62277SBen Gamari  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1028a62277SBen Gamari  * and/or sell copies of the Software, and to permit persons to whom the
1128a62277SBen Gamari  * Software is furnished to do so, subject to the following conditions:
1228a62277SBen Gamari  *
1328a62277SBen Gamari  * The above copyright notice and this permission notice (including the next
1428a62277SBen Gamari  * paragraph) shall be included in all copies or substantial portions of the
1528a62277SBen Gamari  * Software.
1628a62277SBen Gamari  *
1728a62277SBen Gamari  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1828a62277SBen Gamari  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1928a62277SBen Gamari  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2028a62277SBen Gamari  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2128a62277SBen Gamari  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2228a62277SBen Gamari  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2328a62277SBen Gamari  * OTHER DEALINGS IN THE SOFTWARE.
2428a62277SBen Gamari  */
2528a62277SBen Gamari 
2628a62277SBen Gamari #include <linux/debugfs.h>
270500c04eSSam Ravnborg #include <linux/export.h>
2828a62277SBen Gamari #include <linux/seq_file.h>
295a0e3ad6STejun Heo #include <linux/slab.h>
300500c04eSSam Ravnborg #include <linux/uaccess.h>
314834442dSDaniel Vetter 
326559c901SRob Clark #include <drm/drm_atomic.h>
33c6fdea6eSDaniel Vetter #include <drm/drm_auth.h>
348e4bb53cSTomi Valkeinen #include <drm/drm_bridge.h>
350500c04eSSam Ravnborg #include <drm/drm_client.h>
360500c04eSSam Ravnborg #include <drm/drm_debugfs.h>
370500c04eSSam Ravnborg #include <drm/drm_device.h>
380500c04eSSam Ravnborg #include <drm/drm_drv.h>
390500c04eSSam Ravnborg #include <drm/drm_edid.h>
400500c04eSSam Ravnborg #include <drm/drm_file.h>
41c6fdea6eSDaniel Vetter #include <drm/drm_gem.h>
421c9cacbeSMaíra Canal #include <drm/drm_managed.h>
434f66feeaSDanilo Krummrich #include <drm/drm_gpuva_mgr.h>
444834442dSDaniel Vetter 
455bc9cb4dSDaniel Vetter #include "drm_crtc_internal.h"
460500c04eSSam Ravnborg #include "drm_internal.h"
4728a62277SBen Gamari 
4828a62277SBen Gamari #if defined(CONFIG_DEBUG_FS)
4928a62277SBen Gamari 
5028a62277SBen Gamari /***************************************************
5128a62277SBen Gamari  * Initialization, etc.
5228a62277SBen Gamari  **************************************************/
5328a62277SBen Gamari 
drm_name_info(struct seq_file * m,void * data)54c6fdea6eSDaniel Vetter static int drm_name_info(struct seq_file *m, void *data)
55c6fdea6eSDaniel Vetter {
566fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
576fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
58c6fdea6eSDaniel Vetter 	struct drm_master *master;
59c6fdea6eSDaniel Vetter 
60c6fdea6eSDaniel Vetter 	mutex_lock(&dev->master_mutex);
61c6fdea6eSDaniel Vetter 	master = dev->master;
62c6fdea6eSDaniel Vetter 	seq_printf(m, "%s", dev->driver->name);
63c6fdea6eSDaniel Vetter 	if (dev->dev)
64c6fdea6eSDaniel Vetter 		seq_printf(m, " dev=%s", dev_name(dev->dev));
65c6fdea6eSDaniel Vetter 	if (master && master->unique)
66c6fdea6eSDaniel Vetter 		seq_printf(m, " master=%s", master->unique);
67c6fdea6eSDaniel Vetter 	if (dev->unique)
68c6fdea6eSDaniel Vetter 		seq_printf(m, " unique=%s", dev->unique);
69c6fdea6eSDaniel Vetter 	seq_printf(m, "\n");
70c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->master_mutex);
71c6fdea6eSDaniel Vetter 
72c6fdea6eSDaniel Vetter 	return 0;
73c6fdea6eSDaniel Vetter }
74c6fdea6eSDaniel Vetter 
drm_clients_info(struct seq_file * m,void * data)75c6fdea6eSDaniel Vetter static int drm_clients_info(struct seq_file *m, void *data)
76c6fdea6eSDaniel Vetter {
776fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
786fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
79c6fdea6eSDaniel Vetter 	struct drm_file *priv;
80c6fdea6eSDaniel Vetter 	kuid_t uid;
81c6fdea6eSDaniel Vetter 
82c6fdea6eSDaniel Vetter 	seq_printf(m,
83c6fdea6eSDaniel Vetter 		   "%20s %5s %3s master a %5s %10s\n",
84c6fdea6eSDaniel Vetter 		   "command",
854230cea8STvrtko Ursulin 		   "tgid",
86c6fdea6eSDaniel Vetter 		   "dev",
87c6fdea6eSDaniel Vetter 		   "uid",
88c6fdea6eSDaniel Vetter 		   "magic");
89c6fdea6eSDaniel Vetter 
90c6fdea6eSDaniel Vetter 	/* dev->filelist is sorted youngest first, but we want to present
91c6fdea6eSDaniel Vetter 	 * oldest first (i.e. kernel, servers, clients), so walk backwardss.
92c6fdea6eSDaniel Vetter 	 */
93c6fdea6eSDaniel Vetter 	mutex_lock(&dev->filelist_mutex);
94c6fdea6eSDaniel Vetter 	list_for_each_entry_reverse(priv, &dev->filelist, lhead) {
955eff9585SDesmond Cheong Zhi Xi 		bool is_current_master = drm_is_current_master(priv);
96*031ddd28STvrtko Ursulin 		struct task_struct *task;
97*031ddd28STvrtko Ursulin 		struct pid *pid;
98c6fdea6eSDaniel Vetter 
99*031ddd28STvrtko Ursulin 		rcu_read_lock(); /* Locks priv->pid and pid_task()->comm! */
100*031ddd28STvrtko Ursulin 		pid = rcu_dereference(priv->pid);
101*031ddd28STvrtko Ursulin 		task = pid_task(pid, PIDTYPE_TGID);
102c6fdea6eSDaniel Vetter 		uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID;
103c6fdea6eSDaniel Vetter 		seq_printf(m, "%20s %5d %3d   %c    %c %5d %10u\n",
104c6fdea6eSDaniel Vetter 			   task ? task->comm : "<unknown>",
105*031ddd28STvrtko Ursulin 			   pid_vnr(pid),
106c6fdea6eSDaniel Vetter 			   priv->minor->index,
1075eff9585SDesmond Cheong Zhi Xi 			   is_current_master ? 'y' : 'n',
108c6fdea6eSDaniel Vetter 			   priv->authenticated ? 'y' : 'n',
109c6fdea6eSDaniel Vetter 			   from_kuid_munged(seq_user_ns(m), uid),
110c6fdea6eSDaniel Vetter 			   priv->magic);
111c6fdea6eSDaniel Vetter 		rcu_read_unlock();
112c6fdea6eSDaniel Vetter 	}
113c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->filelist_mutex);
114c6fdea6eSDaniel Vetter 	return 0;
115c6fdea6eSDaniel Vetter }
116c6fdea6eSDaniel Vetter 
drm_gem_one_name_info(int id,void * ptr,void * data)117c6fdea6eSDaniel Vetter static int drm_gem_one_name_info(int id, void *ptr, void *data)
118c6fdea6eSDaniel Vetter {
119c6fdea6eSDaniel Vetter 	struct drm_gem_object *obj = ptr;
120c6fdea6eSDaniel Vetter 	struct seq_file *m = data;
121c6fdea6eSDaniel Vetter 
122c6fdea6eSDaniel Vetter 	seq_printf(m, "%6d %8zd %7d %8d\n",
123c6fdea6eSDaniel Vetter 		   obj->name, obj->size,
124c6fdea6eSDaniel Vetter 		   obj->handle_count,
125c6fdea6eSDaniel Vetter 		   kref_read(&obj->refcount));
126c6fdea6eSDaniel Vetter 	return 0;
127c6fdea6eSDaniel Vetter }
128c6fdea6eSDaniel Vetter 
drm_gem_name_info(struct seq_file * m,void * data)129c6fdea6eSDaniel Vetter static int drm_gem_name_info(struct seq_file *m, void *data)
130c6fdea6eSDaniel Vetter {
1316fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
1326fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
133c6fdea6eSDaniel Vetter 
134c6fdea6eSDaniel Vetter 	seq_printf(m, "  name     size handles refcount\n");
135c6fdea6eSDaniel Vetter 
136c6fdea6eSDaniel Vetter 	mutex_lock(&dev->object_name_lock);
137c6fdea6eSDaniel Vetter 	idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m);
138c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->object_name_lock);
139c6fdea6eSDaniel Vetter 
140c6fdea6eSDaniel Vetter 	return 0;
141c6fdea6eSDaniel Vetter }
142c6fdea6eSDaniel Vetter 
1436fd80729SMaíra Canal static const struct drm_debugfs_info drm_debugfs_list[] = {
14428a62277SBen Gamari 	{"name", drm_name_info, 0},
14528a62277SBen Gamari 	{"clients", drm_clients_info, 0},
14628a62277SBen Gamari 	{"gem_names", drm_gem_name_info, DRIVER_GEM},
14728a62277SBen Gamari };
14828a62277SBen Gamari #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
14928a62277SBen Gamari 
15028a62277SBen Gamari 
drm_debugfs_open(struct inode * inode,struct file * file)15128a62277SBen Gamari static int drm_debugfs_open(struct inode *inode, struct file *file)
15228a62277SBen Gamari {
15328a62277SBen Gamari 	struct drm_info_node *node = inode->i_private;
15428a62277SBen Gamari 
15528a62277SBen Gamari 	return single_open(file, node->info_ent->show, node);
15628a62277SBen Gamari }
15728a62277SBen Gamari 
drm_debugfs_entry_open(struct inode * inode,struct file * file)1581c9cacbeSMaíra Canal static int drm_debugfs_entry_open(struct inode *inode, struct file *file)
1591c9cacbeSMaíra Canal {
1601c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry = inode->i_private;
1611c9cacbeSMaíra Canal 	struct drm_debugfs_info *node = &entry->file;
1621c9cacbeSMaíra Canal 
1631c9cacbeSMaíra Canal 	return single_open(file, node->show, entry);
1641c9cacbeSMaíra Canal }
1651c9cacbeSMaíra Canal 
1661c9cacbeSMaíra Canal static const struct file_operations drm_debugfs_entry_fops = {
1671c9cacbeSMaíra Canal 	.owner = THIS_MODULE,
1681c9cacbeSMaíra Canal 	.open = drm_debugfs_entry_open,
1691c9cacbeSMaíra Canal 	.read = seq_read,
1701c9cacbeSMaíra Canal 	.llseek = seq_lseek,
1711c9cacbeSMaíra Canal 	.release = single_release,
1721c9cacbeSMaíra Canal };
17328a62277SBen Gamari 
17428a62277SBen Gamari static const struct file_operations drm_debugfs_fops = {
17528a62277SBen Gamari 	.owner = THIS_MODULE,
17628a62277SBen Gamari 	.open = drm_debugfs_open,
17728a62277SBen Gamari 	.read = seq_read,
17828a62277SBen Gamari 	.llseek = seq_lseek,
17928a62277SBen Gamari 	.release = single_release,
18028a62277SBen Gamari };
18128a62277SBen Gamari 
1824f66feeaSDanilo Krummrich /**
1834f66feeaSDanilo Krummrich  * drm_debugfs_gpuva_info - dump the given DRM GPU VA space
1844f66feeaSDanilo Krummrich  * @m: pointer to the &seq_file to write
1854f66feeaSDanilo Krummrich  * @mgr: the &drm_gpuva_manager representing the GPU VA space
1864f66feeaSDanilo Krummrich  *
1874f66feeaSDanilo Krummrich  * Dumps the GPU VA mappings of a given DRM GPU VA manager.
1884f66feeaSDanilo Krummrich  *
1894f66feeaSDanilo Krummrich  * For each DRM GPU VA space drivers should call this function from their
1904f66feeaSDanilo Krummrich  * &drm_info_list's show callback.
1914f66feeaSDanilo Krummrich  *
1924f66feeaSDanilo Krummrich  * Returns: 0 on success, -ENODEV if the &mgr is not initialized
1934f66feeaSDanilo Krummrich  */
drm_debugfs_gpuva_info(struct seq_file * m,struct drm_gpuva_manager * mgr)1944f66feeaSDanilo Krummrich int drm_debugfs_gpuva_info(struct seq_file *m,
1954f66feeaSDanilo Krummrich 			   struct drm_gpuva_manager *mgr)
1964f66feeaSDanilo Krummrich {
1974f66feeaSDanilo Krummrich 	struct drm_gpuva *va, *kva = &mgr->kernel_alloc_node;
1984f66feeaSDanilo Krummrich 
1994f66feeaSDanilo Krummrich 	if (!mgr->name)
2004f66feeaSDanilo Krummrich 		return -ENODEV;
2014f66feeaSDanilo Krummrich 
2024f66feeaSDanilo Krummrich 	seq_printf(m, "DRM GPU VA space (%s) [0x%016llx;0x%016llx]\n",
2034f66feeaSDanilo Krummrich 		   mgr->name, mgr->mm_start, mgr->mm_start + mgr->mm_range);
2044f66feeaSDanilo Krummrich 	seq_printf(m, "Kernel reserved node [0x%016llx;0x%016llx]\n",
2054f66feeaSDanilo Krummrich 		   kva->va.addr, kva->va.addr + kva->va.range);
2064f66feeaSDanilo Krummrich 	seq_puts(m, "\n");
2074f66feeaSDanilo Krummrich 	seq_puts(m, " VAs | start              | range              | end                | object             | object offset\n");
2084f66feeaSDanilo Krummrich 	seq_puts(m, "-------------------------------------------------------------------------------------------------------------\n");
2094f66feeaSDanilo Krummrich 	drm_gpuva_for_each_va(va, mgr) {
2104f66feeaSDanilo Krummrich 		if (unlikely(va == kva))
2114f66feeaSDanilo Krummrich 			continue;
2124f66feeaSDanilo Krummrich 
2134f66feeaSDanilo Krummrich 		seq_printf(m, "     | 0x%016llx | 0x%016llx | 0x%016llx | 0x%016llx | 0x%016llx\n",
2144f66feeaSDanilo Krummrich 			   va->va.addr, va->va.range, va->va.addr + va->va.range,
21534d7edcfSSteven Price 			   (u64)(uintptr_t)va->gem.obj, va->gem.offset);
2164f66feeaSDanilo Krummrich 	}
2174f66feeaSDanilo Krummrich 
2184f66feeaSDanilo Krummrich 	return 0;
2194f66feeaSDanilo Krummrich }
2204f66feeaSDanilo Krummrich EXPORT_SYMBOL(drm_debugfs_gpuva_info);
22128a62277SBen Gamari 
22228a62277SBen Gamari /**
2230cad7f71SDaniel Vetter  * drm_debugfs_create_files - Initialize a given set of debugfs files for DRM
2240cad7f71SDaniel Vetter  * 			minor
2250cad7f71SDaniel Vetter  * @files: The array of files to create
2260cad7f71SDaniel Vetter  * @count: The number of files given
2270cad7f71SDaniel Vetter  * @root: DRI debugfs dir entry.
2280cad7f71SDaniel Vetter  * @minor: device minor number
22928a62277SBen Gamari  *
23028a62277SBen Gamari  * Create a given set of debugfs files represented by an array of
2310cad7f71SDaniel Vetter  * &struct drm_info_list in the given root directory. These files will be removed
232086f2e5cSNoralf Trønnes  * automatically on drm_debugfs_cleanup().
23328a62277SBen Gamari  */
drm_debugfs_create_files(const struct drm_info_list * files,int count,struct dentry * root,struct drm_minor * minor)234a212d6a5SWambui Karuga void drm_debugfs_create_files(const struct drm_info_list *files, int count,
23528a62277SBen Gamari 			      struct dentry *root, struct drm_minor *minor)
23628a62277SBen Gamari {
23728a62277SBen Gamari 	struct drm_device *dev = minor->dev;
23828a62277SBen Gamari 	struct drm_info_node *tmp;
239987d65d0SGreg Kroah-Hartman 	int i;
24028a62277SBen Gamari 
24128a62277SBen Gamari 	for (i = 0; i < count; i++) {
24228a62277SBen Gamari 		u32 features = files[i].driver_features;
24328a62277SBen Gamari 
24423d498f6SJani Nikula 		if (features && !drm_core_check_all_features(dev, features))
24528a62277SBen Gamari 			continue;
24628a62277SBen Gamari 
2479a298b2aSEric Anholt 		tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
248987d65d0SGreg Kroah-Hartman 		if (tmp == NULL)
249987d65d0SGreg Kroah-Hartman 			continue;
25028a62277SBen Gamari 
25128a62277SBen Gamari 		tmp->minor = minor;
252987d65d0SGreg Kroah-Hartman 		tmp->dent = debugfs_create_file(files[i].name,
253c9ba134eSMaíra Canal 						0444, root, tmp,
254987d65d0SGreg Kroah-Hartman 						&drm_debugfs_fops);
25528a62277SBen Gamari 		tmp->info_ent = &files[i];
256b3e067c0SMarcin Slusarz 
257b3e067c0SMarcin Slusarz 		mutex_lock(&minor->debugfs_lock);
258b3e067c0SMarcin Slusarz 		list_add(&tmp->list, &minor->debugfs_list);
259b3e067c0SMarcin Slusarz 		mutex_unlock(&minor->debugfs_lock);
26028a62277SBen Gamari 	}
26128a62277SBen Gamari }
26228a62277SBen Gamari EXPORT_SYMBOL(drm_debugfs_create_files);
26328a62277SBen Gamari 
drm_debugfs_init(struct drm_minor * minor,int minor_id,struct dentry * root)26428a62277SBen Gamari int drm_debugfs_init(struct drm_minor *minor, int minor_id,
26528a62277SBen Gamari 		     struct dentry *root)
26628a62277SBen Gamari {
26728a62277SBen Gamari 	struct drm_device *dev = minor->dev;
2681c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry, *tmp;
26928a62277SBen Gamari 	char name[64];
27028a62277SBen Gamari 
271b3e067c0SMarcin Slusarz 	INIT_LIST_HEAD(&minor->debugfs_list);
272b3e067c0SMarcin Slusarz 	mutex_init(&minor->debugfs_lock);
27328a62277SBen Gamari 	sprintf(name, "%d", minor_id);
27428a62277SBen Gamari 	minor->debugfs_root = debugfs_create_dir(name, root);
27528a62277SBen Gamari 
2766fd80729SMaíra Canal 	drm_debugfs_add_files(minor->dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES);
27728a62277SBen Gamari 
2783c499ea0SLyude Paul 	if (drm_drv_uses_atomic_modeset(dev)) {
279a212d6a5SWambui Karuga 		drm_atomic_debugfs_init(minor);
2808e4bb53cSTomi Valkeinen 		drm_bridge_debugfs_init(minor);
2816559c901SRob Clark 	}
2826559c901SRob Clark 
2830c51ef86SNoralf Trønnes 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
284a212d6a5SWambui Karuga 		drm_framebuffer_debugfs_init(minor);
285e896c132SNoralf Trønnes 
286a212d6a5SWambui Karuga 		drm_client_debugfs_init(minor);
2870c51ef86SNoralf Trønnes 	}
28845d58b40SNoralf Trønnes 
2897ce84471SWambui Karuga 	if (dev->driver->debugfs_init)
2907ce84471SWambui Karuga 		dev->driver->debugfs_init(minor);
2917ce84471SWambui Karuga 
2921c9cacbeSMaíra Canal 	list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) {
293c9ba134eSMaíra Canal 		debugfs_create_file(entry->file.name, 0444,
2941c9cacbeSMaíra Canal 				    minor->debugfs_root, entry, &drm_debugfs_entry_fops);
2951c9cacbeSMaíra Canal 		list_del(&entry->list);
2961c9cacbeSMaíra Canal 	}
2971c9cacbeSMaíra Canal 
29828a62277SBen Gamari 	return 0;
29928a62277SBen Gamari }
30028a62277SBen Gamari 
drm_debugfs_late_register(struct drm_device * dev)301dbb23cf5SMaíra Canal void drm_debugfs_late_register(struct drm_device *dev)
302dbb23cf5SMaíra Canal {
303dbb23cf5SMaíra Canal 	struct drm_minor *minor = dev->primary;
304dbb23cf5SMaíra Canal 	struct drm_debugfs_entry *entry, *tmp;
305dbb23cf5SMaíra Canal 
306dbb23cf5SMaíra Canal 	if (!minor)
307dbb23cf5SMaíra Canal 		return;
308dbb23cf5SMaíra Canal 
309dbb23cf5SMaíra Canal 	list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) {
310c9ba134eSMaíra Canal 		debugfs_create_file(entry->file.name, 0444,
311dbb23cf5SMaíra Canal 				    minor->debugfs_root, entry, &drm_debugfs_entry_fops);
312dbb23cf5SMaíra Canal 		list_del(&entry->list);
313dbb23cf5SMaíra Canal 	}
314dbb23cf5SMaíra Canal }
31528a62277SBen Gamari 
drm_debugfs_remove_files(const struct drm_info_list * files,int count,struct drm_minor * minor)3167d74795bSLespiau, Damien int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
31728a62277SBen Gamari 			     struct drm_minor *minor)
31828a62277SBen Gamari {
31928a62277SBen Gamari 	struct list_head *pos, *q;
32028a62277SBen Gamari 	struct drm_info_node *tmp;
32128a62277SBen Gamari 	int i;
32228a62277SBen Gamari 
323b3e067c0SMarcin Slusarz 	mutex_lock(&minor->debugfs_lock);
32428a62277SBen Gamari 	for (i = 0; i < count; i++) {
325b3e067c0SMarcin Slusarz 		list_for_each_safe(pos, q, &minor->debugfs_list) {
32628a62277SBen Gamari 			tmp = list_entry(pos, struct drm_info_node, list);
32728a62277SBen Gamari 			if (tmp->info_ent == &files[i]) {
32828a62277SBen Gamari 				debugfs_remove(tmp->dent);
32928a62277SBen Gamari 				list_del(pos);
3309a298b2aSEric Anholt 				kfree(tmp);
33128a62277SBen Gamari 			}
33228a62277SBen Gamari 		}
33328a62277SBen Gamari 	}
334b3e067c0SMarcin Slusarz 	mutex_unlock(&minor->debugfs_lock);
33528a62277SBen Gamari 	return 0;
33628a62277SBen Gamari }
33728a62277SBen Gamari EXPORT_SYMBOL(drm_debugfs_remove_files);
33828a62277SBen Gamari 
drm_debugfs_remove_all_files(struct drm_minor * minor)339086f2e5cSNoralf Trønnes static void drm_debugfs_remove_all_files(struct drm_minor *minor)
340086f2e5cSNoralf Trønnes {
341086f2e5cSNoralf Trønnes 	struct drm_info_node *node, *tmp;
342086f2e5cSNoralf Trønnes 
343086f2e5cSNoralf Trønnes 	mutex_lock(&minor->debugfs_lock);
344086f2e5cSNoralf Trønnes 	list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) {
345086f2e5cSNoralf Trønnes 		debugfs_remove(node->dent);
346086f2e5cSNoralf Trønnes 		list_del(&node->list);
347086f2e5cSNoralf Trønnes 		kfree(node);
348086f2e5cSNoralf Trønnes 	}
349086f2e5cSNoralf Trønnes 	mutex_unlock(&minor->debugfs_lock);
350086f2e5cSNoralf Trønnes }
351086f2e5cSNoralf Trønnes 
drm_debugfs_cleanup(struct drm_minor * minor)352b792e640SGreg Kroah-Hartman void drm_debugfs_cleanup(struct drm_minor *minor)
35328a62277SBen Gamari {
35428a62277SBen Gamari 	if (!minor->debugfs_root)
355b792e640SGreg Kroah-Hartman 		return;
35628a62277SBen Gamari 
357086f2e5cSNoralf Trønnes 	drm_debugfs_remove_all_files(minor);
35828a62277SBen Gamari 
359086f2e5cSNoralf Trønnes 	debugfs_remove_recursive(minor->debugfs_root);
36028a62277SBen Gamari 	minor->debugfs_root = NULL;
36128a62277SBen Gamari }
36228a62277SBen Gamari 
3631c9cacbeSMaíra Canal /**
3641c9cacbeSMaíra Canal  * drm_debugfs_add_file - Add a given file to the DRM device debugfs file list
3651c9cacbeSMaíra Canal  * @dev: drm device for the ioctl
3661c9cacbeSMaíra Canal  * @name: debugfs file name
3671c9cacbeSMaíra Canal  * @show: show callback
3681c9cacbeSMaíra Canal  * @data: driver-private data, should not be device-specific
3691c9cacbeSMaíra Canal  *
3701c9cacbeSMaíra Canal  * Add a given file entry to the DRM device debugfs file list to be created on
3711c9cacbeSMaíra Canal  * drm_debugfs_init.
3721c9cacbeSMaíra Canal  */
drm_debugfs_add_file(struct drm_device * dev,const char * name,int (* show)(struct seq_file *,void *),void * data)3731c9cacbeSMaíra Canal void drm_debugfs_add_file(struct drm_device *dev, const char *name,
3741c9cacbeSMaíra Canal 			  int (*show)(struct seq_file*, void*), void *data)
3751c9cacbeSMaíra Canal {
3761c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry = drmm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
3771c9cacbeSMaíra Canal 
3781c9cacbeSMaíra Canal 	if (!entry)
3791c9cacbeSMaíra Canal 		return;
3801c9cacbeSMaíra Canal 
3811c9cacbeSMaíra Canal 	entry->file.name = name;
3821c9cacbeSMaíra Canal 	entry->file.show = show;
3831c9cacbeSMaíra Canal 	entry->file.data = data;
3841c9cacbeSMaíra Canal 	entry->dev = dev;
3851c9cacbeSMaíra Canal 
3861c9cacbeSMaíra Canal 	mutex_lock(&dev->debugfs_mutex);
3871c9cacbeSMaíra Canal 	list_add(&entry->list, &dev->debugfs_list);
3881c9cacbeSMaíra Canal 	mutex_unlock(&dev->debugfs_mutex);
3891c9cacbeSMaíra Canal }
3901c9cacbeSMaíra Canal EXPORT_SYMBOL(drm_debugfs_add_file);
3911c9cacbeSMaíra Canal 
3921c9cacbeSMaíra Canal /**
3931c9cacbeSMaíra Canal  * drm_debugfs_add_files - Add an array of files to the DRM device debugfs file list
3941c9cacbeSMaíra Canal  * @dev: drm device for the ioctl
3951c9cacbeSMaíra Canal  * @files: The array of files to create
3961c9cacbeSMaíra Canal  * @count: The number of files given
3971c9cacbeSMaíra Canal  *
3981c9cacbeSMaíra Canal  * Add a given set of debugfs files represented by an array of
3991c9cacbeSMaíra Canal  * &struct drm_debugfs_info in the DRM device debugfs file list.
4001c9cacbeSMaíra Canal  */
drm_debugfs_add_files(struct drm_device * dev,const struct drm_debugfs_info * files,int count)4011c9cacbeSMaíra Canal void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count)
4021c9cacbeSMaíra Canal {
4031c9cacbeSMaíra Canal 	int i;
4041c9cacbeSMaíra Canal 
4051c9cacbeSMaíra Canal 	for (i = 0; i < count; i++)
4061c9cacbeSMaíra Canal 		drm_debugfs_add_file(dev, files[i].name, files[i].show, files[i].data);
4071c9cacbeSMaíra Canal }
4081c9cacbeSMaíra Canal EXPORT_SYMBOL(drm_debugfs_add_files);
4091c9cacbeSMaíra Canal 
connector_show(struct seq_file * m,void * data)41030f65707SThomas Wood static int connector_show(struct seq_file *m, void *data)
41130f65707SThomas Wood {
41230f65707SThomas Wood 	struct drm_connector *connector = m->private;
41330f65707SThomas Wood 
4146140cf20SJani Nikula 	seq_printf(m, "%s\n", drm_get_connector_force_name(connector->force));
41530f65707SThomas Wood 
41630f65707SThomas Wood 	return 0;
41730f65707SThomas Wood }
41830f65707SThomas Wood 
connector_open(struct inode * inode,struct file * file)41930f65707SThomas Wood static int connector_open(struct inode *inode, struct file *file)
42030f65707SThomas Wood {
42130f65707SThomas Wood 	struct drm_connector *dev = inode->i_private;
42230f65707SThomas Wood 
42330f65707SThomas Wood 	return single_open(file, connector_show, dev);
42430f65707SThomas Wood }
42530f65707SThomas Wood 
connector_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)42630f65707SThomas Wood static ssize_t connector_write(struct file *file, const char __user *ubuf,
42730f65707SThomas Wood 			       size_t len, loff_t *offp)
42830f65707SThomas Wood {
42930f65707SThomas Wood 	struct seq_file *m = file->private_data;
43030f65707SThomas Wood 	struct drm_connector *connector = m->private;
43130f65707SThomas Wood 	char buf[12];
43230f65707SThomas Wood 
43330f65707SThomas Wood 	if (len > sizeof(buf) - 1)
43430f65707SThomas Wood 		return -EINVAL;
43530f65707SThomas Wood 
43630f65707SThomas Wood 	if (copy_from_user(buf, ubuf, len))
43730f65707SThomas Wood 		return -EFAULT;
43830f65707SThomas Wood 
43930f65707SThomas Wood 	buf[len] = '\0';
44030f65707SThomas Wood 
441c704b170SMichael Tretter 	if (sysfs_streq(buf, "on"))
44230f65707SThomas Wood 		connector->force = DRM_FORCE_ON;
443c704b170SMichael Tretter 	else if (sysfs_streq(buf, "digital"))
44430f65707SThomas Wood 		connector->force = DRM_FORCE_ON_DIGITAL;
445c704b170SMichael Tretter 	else if (sysfs_streq(buf, "off"))
44630f65707SThomas Wood 		connector->force = DRM_FORCE_OFF;
447c704b170SMichael Tretter 	else if (sysfs_streq(buf, "unspecified"))
44830f65707SThomas Wood 		connector->force = DRM_FORCE_UNSPECIFIED;
44930f65707SThomas Wood 	else
45030f65707SThomas Wood 		return -EINVAL;
45130f65707SThomas Wood 
45230f65707SThomas Wood 	return len;
45330f65707SThomas Wood }
45430f65707SThomas Wood 
edid_show(struct seq_file * m,void * data)4554cf2b281SThomas Wood static int edid_show(struct seq_file *m, void *data)
4564cf2b281SThomas Wood {
45791ec9ab4SJani Nikula 	return drm_edid_override_show(m->private, m);
4584cf2b281SThomas Wood }
4594cf2b281SThomas Wood 
edid_open(struct inode * inode,struct file * file)4604cf2b281SThomas Wood static int edid_open(struct inode *inode, struct file *file)
4614cf2b281SThomas Wood {
4624cf2b281SThomas Wood 	struct drm_connector *dev = inode->i_private;
4634cf2b281SThomas Wood 
4644cf2b281SThomas Wood 	return single_open(file, edid_show, dev);
4654cf2b281SThomas Wood }
4664cf2b281SThomas Wood 
edid_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)4674cf2b281SThomas Wood static ssize_t edid_write(struct file *file, const char __user *ubuf,
4684cf2b281SThomas Wood 			  size_t len, loff_t *offp)
4694cf2b281SThomas Wood {
4704cf2b281SThomas Wood 	struct seq_file *m = file->private_data;
4714cf2b281SThomas Wood 	struct drm_connector *connector = m->private;
4724cf2b281SThomas Wood 	char *buf;
4734cf2b281SThomas Wood 	int ret;
4744cf2b281SThomas Wood 
4754cf2b281SThomas Wood 	buf = memdup_user(ubuf, len);
4764cf2b281SThomas Wood 	if (IS_ERR(buf))
4774cf2b281SThomas Wood 		return PTR_ERR(buf);
4784cf2b281SThomas Wood 
4796aa145bcSJani Nikula 	if (len == 5 && !strncmp(buf, "reset", 5))
4806aa145bcSJani Nikula 		ret = drm_edid_override_reset(connector);
4816aa145bcSJani Nikula 	else
4826aa145bcSJani Nikula 		ret = drm_edid_override_set(connector, buf, len);
4834cf2b281SThomas Wood 
4844cf2b281SThomas Wood 	kfree(buf);
4854cf2b281SThomas Wood 
4866aa145bcSJani Nikula 	return ret ? ret : len;
4874cf2b281SThomas Wood }
4884cf2b281SThomas Wood 
48941752663SBhanuprakash Modem /*
49041752663SBhanuprakash Modem  * Returns the min and max vrr vfreq through the connector's debugfs file.
49141752663SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/vrr_range
49241752663SBhanuprakash Modem  */
vrr_range_show(struct seq_file * m,void * data)49341752663SBhanuprakash Modem static int vrr_range_show(struct seq_file *m, void *data)
49441752663SBhanuprakash Modem {
49541752663SBhanuprakash Modem 	struct drm_connector *connector = m->private;
49641752663SBhanuprakash Modem 
49741752663SBhanuprakash Modem 	if (connector->status != connector_status_connected)
49841752663SBhanuprakash Modem 		return -ENODEV;
49941752663SBhanuprakash Modem 
500c7943bb3SVille Syrjälä 	seq_printf(m, "Min: %u\n", connector->display_info.monitor_range.min_vfreq);
501c7943bb3SVille Syrjälä 	seq_printf(m, "Max: %u\n", connector->display_info.monitor_range.max_vfreq);
50241752663SBhanuprakash Modem 
50341752663SBhanuprakash Modem 	return 0;
50441752663SBhanuprakash Modem }
50541752663SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(vrr_range);
50641752663SBhanuprakash Modem 
50767d935b4SBhanuprakash Modem /*
50867d935b4SBhanuprakash Modem  * Returns Connector's max supported bpc through debugfs file.
50967d935b4SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc
51067d935b4SBhanuprakash Modem  */
output_bpc_show(struct seq_file * m,void * data)51167d935b4SBhanuprakash Modem static int output_bpc_show(struct seq_file *m, void *data)
51267d935b4SBhanuprakash Modem {
51367d935b4SBhanuprakash Modem 	struct drm_connector *connector = m->private;
51467d935b4SBhanuprakash Modem 
51567d935b4SBhanuprakash Modem 	if (connector->status != connector_status_connected)
51667d935b4SBhanuprakash Modem 		return -ENODEV;
51767d935b4SBhanuprakash Modem 
51867d935b4SBhanuprakash Modem 	seq_printf(m, "Maximum: %u\n", connector->display_info.bpc);
51967d935b4SBhanuprakash Modem 
52067d935b4SBhanuprakash Modem 	return 0;
52167d935b4SBhanuprakash Modem }
52267d935b4SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(output_bpc);
52367d935b4SBhanuprakash Modem 
5244cf2b281SThomas Wood static const struct file_operations drm_edid_fops = {
5254cf2b281SThomas Wood 	.owner = THIS_MODULE,
5264cf2b281SThomas Wood 	.open = edid_open,
5274cf2b281SThomas Wood 	.read = seq_read,
5284cf2b281SThomas Wood 	.llseek = seq_lseek,
5294cf2b281SThomas Wood 	.release = single_release,
5304cf2b281SThomas Wood 	.write = edid_write
5314cf2b281SThomas Wood };
5324cf2b281SThomas Wood 
5334cf2b281SThomas Wood 
53430f65707SThomas Wood static const struct file_operations drm_connector_fops = {
53530f65707SThomas Wood 	.owner = THIS_MODULE,
53630f65707SThomas Wood 	.open = connector_open,
53730f65707SThomas Wood 	.read = seq_read,
53830f65707SThomas Wood 	.llseek = seq_lseek,
53930f65707SThomas Wood 	.release = single_release,
54030f65707SThomas Wood 	.write = connector_write
54130f65707SThomas Wood };
54230f65707SThomas Wood 
drm_debugfs_connector_add(struct drm_connector * connector)543b792e640SGreg Kroah-Hartman void drm_debugfs_connector_add(struct drm_connector *connector)
54430f65707SThomas Wood {
54530f65707SThomas Wood 	struct drm_minor *minor = connector->dev->primary;
546b792e640SGreg Kroah-Hartman 	struct dentry *root;
54730f65707SThomas Wood 
54830f65707SThomas Wood 	if (!minor->debugfs_root)
549b792e640SGreg Kroah-Hartman 		return;
55030f65707SThomas Wood 
55130f65707SThomas Wood 	root = debugfs_create_dir(connector->name, minor->debugfs_root);
55230f65707SThomas Wood 	connector->debugfs_entry = root;
55330f65707SThomas Wood 
55430f65707SThomas Wood 	/* force */
555c9ba134eSMaíra Canal 	debugfs_create_file("force", 0644, root, connector,
55630f65707SThomas Wood 			    &drm_connector_fops);
55730f65707SThomas Wood 
5584cf2b281SThomas Wood 	/* edid */
559c9ba134eSMaíra Canal 	debugfs_create_file("edid_override", 0644, root, connector,
560b792e640SGreg Kroah-Hartman 			    &drm_edid_fops);
56141752663SBhanuprakash Modem 
56241752663SBhanuprakash Modem 	/* vrr range */
563c9ba134eSMaíra Canal 	debugfs_create_file("vrr_range", 0444, root, connector,
56441752663SBhanuprakash Modem 			    &vrr_range_fops);
5652509969aSDouglas Anderson 
56667d935b4SBhanuprakash Modem 	/* max bpc */
56767d935b4SBhanuprakash Modem 	debugfs_create_file("output_bpc", 0444, root, connector,
56867d935b4SBhanuprakash Modem 			    &output_bpc_fops);
56967d935b4SBhanuprakash Modem 
5702509969aSDouglas Anderson 	if (connector->funcs->debugfs_init)
5712509969aSDouglas Anderson 		connector->funcs->debugfs_init(connector, root);
57230f65707SThomas Wood }
57330f65707SThomas Wood 
drm_debugfs_connector_remove(struct drm_connector * connector)57430f65707SThomas Wood void drm_debugfs_connector_remove(struct drm_connector *connector)
57530f65707SThomas Wood {
57630f65707SThomas Wood 	if (!connector->debugfs_entry)
57730f65707SThomas Wood 		return;
57830f65707SThomas Wood 
57930f65707SThomas Wood 	debugfs_remove_recursive(connector->debugfs_entry);
58030f65707SThomas Wood 
58130f65707SThomas Wood 	connector->debugfs_entry = NULL;
58230f65707SThomas Wood }
58330f65707SThomas Wood 
drm_debugfs_crtc_add(struct drm_crtc * crtc)584b792e640SGreg Kroah-Hartman void drm_debugfs_crtc_add(struct drm_crtc *crtc)
5859edbf1faSTomeu Vizoso {
5869edbf1faSTomeu Vizoso 	struct drm_minor *minor = crtc->dev->primary;
5879edbf1faSTomeu Vizoso 	struct dentry *root;
5889edbf1faSTomeu Vizoso 	char *name;
58928a62277SBen Gamari 
5909edbf1faSTomeu Vizoso 	name = kasprintf(GFP_KERNEL, "crtc-%d", crtc->index);
5919edbf1faSTomeu Vizoso 	if (!name)
592b792e640SGreg Kroah-Hartman 		return;
5939edbf1faSTomeu Vizoso 
5949edbf1faSTomeu Vizoso 	root = debugfs_create_dir(name, minor->debugfs_root);
5959edbf1faSTomeu Vizoso 	kfree(name);
5969edbf1faSTomeu Vizoso 
5979edbf1faSTomeu Vizoso 	crtc->debugfs_entry = root;
5989edbf1faSTomeu Vizoso 
599b792e640SGreg Kroah-Hartman 	drm_debugfs_crtc_crc_add(crtc);
6009edbf1faSTomeu Vizoso }
6019edbf1faSTomeu Vizoso 
drm_debugfs_crtc_remove(struct drm_crtc * crtc)6029edbf1faSTomeu Vizoso void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
6039edbf1faSTomeu Vizoso {
6049edbf1faSTomeu Vizoso 	debugfs_remove_recursive(crtc->debugfs_entry);
6059edbf1faSTomeu Vizoso 	crtc->debugfs_entry = NULL;
6069edbf1faSTomeu Vizoso }
6079edbf1faSTomeu Vizoso 
6089edbf1faSTomeu Vizoso #endif /* CONFIG_DEBUG_FS */
609