xref: /openbmc/linux/drivers/gpu/drm/drm_debugfs.c (revision 8e4bb53c902ed2b06a2c4778e6dbb2c1eeec4960)
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>
34*8e4bb53cSTomi 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 
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 
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) {
95c6fdea6eSDaniel Vetter 		struct task_struct *task;
965eff9585SDesmond Cheong Zhi Xi 		bool is_current_master = drm_is_current_master(priv);
97c6fdea6eSDaniel Vetter 
98c6fdea6eSDaniel Vetter 		rcu_read_lock(); /* locks pid_task()->comm */
994230cea8STvrtko Ursulin 		task = pid_task(priv->pid, PIDTYPE_TGID);
100c6fdea6eSDaniel Vetter 		uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID;
101c6fdea6eSDaniel Vetter 		seq_printf(m, "%20s %5d %3d   %c    %c %5d %10u\n",
102c6fdea6eSDaniel Vetter 			   task ? task->comm : "<unknown>",
103c6fdea6eSDaniel Vetter 			   pid_vnr(priv->pid),
104c6fdea6eSDaniel Vetter 			   priv->minor->index,
1055eff9585SDesmond Cheong Zhi Xi 			   is_current_master ? 'y' : 'n',
106c6fdea6eSDaniel Vetter 			   priv->authenticated ? 'y' : 'n',
107c6fdea6eSDaniel Vetter 			   from_kuid_munged(seq_user_ns(m), uid),
108c6fdea6eSDaniel Vetter 			   priv->magic);
109c6fdea6eSDaniel Vetter 		rcu_read_unlock();
110c6fdea6eSDaniel Vetter 	}
111c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->filelist_mutex);
112c6fdea6eSDaniel Vetter 	return 0;
113c6fdea6eSDaniel Vetter }
114c6fdea6eSDaniel Vetter 
115c6fdea6eSDaniel Vetter static int drm_gem_one_name_info(int id, void *ptr, void *data)
116c6fdea6eSDaniel Vetter {
117c6fdea6eSDaniel Vetter 	struct drm_gem_object *obj = ptr;
118c6fdea6eSDaniel Vetter 	struct seq_file *m = data;
119c6fdea6eSDaniel Vetter 
120c6fdea6eSDaniel Vetter 	seq_printf(m, "%6d %8zd %7d %8d\n",
121c6fdea6eSDaniel Vetter 		   obj->name, obj->size,
122c6fdea6eSDaniel Vetter 		   obj->handle_count,
123c6fdea6eSDaniel Vetter 		   kref_read(&obj->refcount));
124c6fdea6eSDaniel Vetter 	return 0;
125c6fdea6eSDaniel Vetter }
126c6fdea6eSDaniel Vetter 
127c6fdea6eSDaniel Vetter static int drm_gem_name_info(struct seq_file *m, void *data)
128c6fdea6eSDaniel Vetter {
1296fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
1306fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
131c6fdea6eSDaniel Vetter 
132c6fdea6eSDaniel Vetter 	seq_printf(m, "  name     size handles refcount\n");
133c6fdea6eSDaniel Vetter 
134c6fdea6eSDaniel Vetter 	mutex_lock(&dev->object_name_lock);
135c6fdea6eSDaniel Vetter 	idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m);
136c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->object_name_lock);
137c6fdea6eSDaniel Vetter 
138c6fdea6eSDaniel Vetter 	return 0;
139c6fdea6eSDaniel Vetter }
140c6fdea6eSDaniel Vetter 
1416fd80729SMaíra Canal static const struct drm_debugfs_info drm_debugfs_list[] = {
14228a62277SBen Gamari 	{"name", drm_name_info, 0},
14328a62277SBen Gamari 	{"clients", drm_clients_info, 0},
14428a62277SBen Gamari 	{"gem_names", drm_gem_name_info, DRIVER_GEM},
14528a62277SBen Gamari };
14628a62277SBen Gamari #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
14728a62277SBen Gamari 
14828a62277SBen Gamari 
14928a62277SBen Gamari static int drm_debugfs_open(struct inode *inode, struct file *file)
15028a62277SBen Gamari {
15128a62277SBen Gamari 	struct drm_info_node *node = inode->i_private;
15228a62277SBen Gamari 
15328a62277SBen Gamari 	return single_open(file, node->info_ent->show, node);
15428a62277SBen Gamari }
15528a62277SBen Gamari 
1561c9cacbeSMaíra Canal static int drm_debugfs_entry_open(struct inode *inode, struct file *file)
1571c9cacbeSMaíra Canal {
1581c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry = inode->i_private;
1591c9cacbeSMaíra Canal 	struct drm_debugfs_info *node = &entry->file;
1601c9cacbeSMaíra Canal 
1611c9cacbeSMaíra Canal 	return single_open(file, node->show, entry);
1621c9cacbeSMaíra Canal }
1631c9cacbeSMaíra Canal 
1641c9cacbeSMaíra Canal static const struct file_operations drm_debugfs_entry_fops = {
1651c9cacbeSMaíra Canal 	.owner = THIS_MODULE,
1661c9cacbeSMaíra Canal 	.open = drm_debugfs_entry_open,
1671c9cacbeSMaíra Canal 	.read = seq_read,
1681c9cacbeSMaíra Canal 	.llseek = seq_lseek,
1691c9cacbeSMaíra Canal 	.release = single_release,
1701c9cacbeSMaíra Canal };
17128a62277SBen Gamari 
17228a62277SBen Gamari static const struct file_operations drm_debugfs_fops = {
17328a62277SBen Gamari 	.owner = THIS_MODULE,
17428a62277SBen Gamari 	.open = drm_debugfs_open,
17528a62277SBen Gamari 	.read = seq_read,
17628a62277SBen Gamari 	.llseek = seq_lseek,
17728a62277SBen Gamari 	.release = single_release,
17828a62277SBen Gamari };
17928a62277SBen Gamari 
1804f66feeaSDanilo Krummrich /**
1814f66feeaSDanilo Krummrich  * drm_debugfs_gpuva_info - dump the given DRM GPU VA space
1824f66feeaSDanilo Krummrich  * @m: pointer to the &seq_file to write
1834f66feeaSDanilo Krummrich  * @mgr: the &drm_gpuva_manager representing the GPU VA space
1844f66feeaSDanilo Krummrich  *
1854f66feeaSDanilo Krummrich  * Dumps the GPU VA mappings of a given DRM GPU VA manager.
1864f66feeaSDanilo Krummrich  *
1874f66feeaSDanilo Krummrich  * For each DRM GPU VA space drivers should call this function from their
1884f66feeaSDanilo Krummrich  * &drm_info_list's show callback.
1894f66feeaSDanilo Krummrich  *
1904f66feeaSDanilo Krummrich  * Returns: 0 on success, -ENODEV if the &mgr is not initialized
1914f66feeaSDanilo Krummrich  */
1924f66feeaSDanilo Krummrich int drm_debugfs_gpuva_info(struct seq_file *m,
1934f66feeaSDanilo Krummrich 			   struct drm_gpuva_manager *mgr)
1944f66feeaSDanilo Krummrich {
1954f66feeaSDanilo Krummrich 	struct drm_gpuva *va, *kva = &mgr->kernel_alloc_node;
1964f66feeaSDanilo Krummrich 
1974f66feeaSDanilo Krummrich 	if (!mgr->name)
1984f66feeaSDanilo Krummrich 		return -ENODEV;
1994f66feeaSDanilo Krummrich 
2004f66feeaSDanilo Krummrich 	seq_printf(m, "DRM GPU VA space (%s) [0x%016llx;0x%016llx]\n",
2014f66feeaSDanilo Krummrich 		   mgr->name, mgr->mm_start, mgr->mm_start + mgr->mm_range);
2024f66feeaSDanilo Krummrich 	seq_printf(m, "Kernel reserved node [0x%016llx;0x%016llx]\n",
2034f66feeaSDanilo Krummrich 		   kva->va.addr, kva->va.addr + kva->va.range);
2044f66feeaSDanilo Krummrich 	seq_puts(m, "\n");
2054f66feeaSDanilo Krummrich 	seq_puts(m, " VAs | start              | range              | end                | object             | object offset\n");
2064f66feeaSDanilo Krummrich 	seq_puts(m, "-------------------------------------------------------------------------------------------------------------\n");
2074f66feeaSDanilo Krummrich 	drm_gpuva_for_each_va(va, mgr) {
2084f66feeaSDanilo Krummrich 		if (unlikely(va == kva))
2094f66feeaSDanilo Krummrich 			continue;
2104f66feeaSDanilo Krummrich 
2114f66feeaSDanilo Krummrich 		seq_printf(m, "     | 0x%016llx | 0x%016llx | 0x%016llx | 0x%016llx | 0x%016llx\n",
2124f66feeaSDanilo Krummrich 			   va->va.addr, va->va.range, va->va.addr + va->va.range,
21334d7edcfSSteven Price 			   (u64)(uintptr_t)va->gem.obj, va->gem.offset);
2144f66feeaSDanilo Krummrich 	}
2154f66feeaSDanilo Krummrich 
2164f66feeaSDanilo Krummrich 	return 0;
2174f66feeaSDanilo Krummrich }
2184f66feeaSDanilo Krummrich EXPORT_SYMBOL(drm_debugfs_gpuva_info);
21928a62277SBen Gamari 
22028a62277SBen Gamari /**
2210cad7f71SDaniel Vetter  * drm_debugfs_create_files - Initialize a given set of debugfs files for DRM
2220cad7f71SDaniel Vetter  * 			minor
2230cad7f71SDaniel Vetter  * @files: The array of files to create
2240cad7f71SDaniel Vetter  * @count: The number of files given
2250cad7f71SDaniel Vetter  * @root: DRI debugfs dir entry.
2260cad7f71SDaniel Vetter  * @minor: device minor number
22728a62277SBen Gamari  *
22828a62277SBen Gamari  * Create a given set of debugfs files represented by an array of
2290cad7f71SDaniel Vetter  * &struct drm_info_list in the given root directory. These files will be removed
230086f2e5cSNoralf Trønnes  * automatically on drm_debugfs_cleanup().
23128a62277SBen Gamari  */
232a212d6a5SWambui Karuga void drm_debugfs_create_files(const struct drm_info_list *files, int count,
23328a62277SBen Gamari 			      struct dentry *root, struct drm_minor *minor)
23428a62277SBen Gamari {
23528a62277SBen Gamari 	struct drm_device *dev = minor->dev;
23628a62277SBen Gamari 	struct drm_info_node *tmp;
237987d65d0SGreg Kroah-Hartman 	int i;
23828a62277SBen Gamari 
23928a62277SBen Gamari 	for (i = 0; i < count; i++) {
24028a62277SBen Gamari 		u32 features = files[i].driver_features;
24128a62277SBen Gamari 
24223d498f6SJani Nikula 		if (features && !drm_core_check_all_features(dev, features))
24328a62277SBen Gamari 			continue;
24428a62277SBen Gamari 
2459a298b2aSEric Anholt 		tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
246987d65d0SGreg Kroah-Hartman 		if (tmp == NULL)
247987d65d0SGreg Kroah-Hartman 			continue;
24828a62277SBen Gamari 
24928a62277SBen Gamari 		tmp->minor = minor;
250987d65d0SGreg Kroah-Hartman 		tmp->dent = debugfs_create_file(files[i].name,
251c9ba134eSMaíra Canal 						0444, root, tmp,
252987d65d0SGreg Kroah-Hartman 						&drm_debugfs_fops);
25328a62277SBen Gamari 		tmp->info_ent = &files[i];
254b3e067c0SMarcin Slusarz 
255b3e067c0SMarcin Slusarz 		mutex_lock(&minor->debugfs_lock);
256b3e067c0SMarcin Slusarz 		list_add(&tmp->list, &minor->debugfs_list);
257b3e067c0SMarcin Slusarz 		mutex_unlock(&minor->debugfs_lock);
25828a62277SBen Gamari 	}
25928a62277SBen Gamari }
26028a62277SBen Gamari EXPORT_SYMBOL(drm_debugfs_create_files);
26128a62277SBen Gamari 
26228a62277SBen Gamari int drm_debugfs_init(struct drm_minor *minor, int minor_id,
26328a62277SBen Gamari 		     struct dentry *root)
26428a62277SBen Gamari {
26528a62277SBen Gamari 	struct drm_device *dev = minor->dev;
2661c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry, *tmp;
26728a62277SBen Gamari 	char name[64];
26828a62277SBen Gamari 
269b3e067c0SMarcin Slusarz 	INIT_LIST_HEAD(&minor->debugfs_list);
270b3e067c0SMarcin Slusarz 	mutex_init(&minor->debugfs_lock);
27128a62277SBen Gamari 	sprintf(name, "%d", minor_id);
27228a62277SBen Gamari 	minor->debugfs_root = debugfs_create_dir(name, root);
27328a62277SBen Gamari 
2746fd80729SMaíra Canal 	drm_debugfs_add_files(minor->dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES);
27528a62277SBen Gamari 
2763c499ea0SLyude Paul 	if (drm_drv_uses_atomic_modeset(dev)) {
277a212d6a5SWambui Karuga 		drm_atomic_debugfs_init(minor);
278*8e4bb53cSTomi Valkeinen 		drm_bridge_debugfs_init(minor);
2796559c901SRob Clark 	}
2806559c901SRob Clark 
2810c51ef86SNoralf Trønnes 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
282a212d6a5SWambui Karuga 		drm_framebuffer_debugfs_init(minor);
283e896c132SNoralf Trønnes 
284a212d6a5SWambui Karuga 		drm_client_debugfs_init(minor);
2850c51ef86SNoralf Trønnes 	}
28645d58b40SNoralf Trønnes 
2877ce84471SWambui Karuga 	if (dev->driver->debugfs_init)
2887ce84471SWambui Karuga 		dev->driver->debugfs_init(minor);
2897ce84471SWambui Karuga 
2901c9cacbeSMaíra Canal 	list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) {
291c9ba134eSMaíra Canal 		debugfs_create_file(entry->file.name, 0444,
2921c9cacbeSMaíra Canal 				    minor->debugfs_root, entry, &drm_debugfs_entry_fops);
2931c9cacbeSMaíra Canal 		list_del(&entry->list);
2941c9cacbeSMaíra Canal 	}
2951c9cacbeSMaíra Canal 
29628a62277SBen Gamari 	return 0;
29728a62277SBen Gamari }
29828a62277SBen Gamari 
299dbb23cf5SMaíra Canal void drm_debugfs_late_register(struct drm_device *dev)
300dbb23cf5SMaíra Canal {
301dbb23cf5SMaíra Canal 	struct drm_minor *minor = dev->primary;
302dbb23cf5SMaíra Canal 	struct drm_debugfs_entry *entry, *tmp;
303dbb23cf5SMaíra Canal 
304dbb23cf5SMaíra Canal 	if (!minor)
305dbb23cf5SMaíra Canal 		return;
306dbb23cf5SMaíra Canal 
307dbb23cf5SMaíra Canal 	list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) {
308c9ba134eSMaíra Canal 		debugfs_create_file(entry->file.name, 0444,
309dbb23cf5SMaíra Canal 				    minor->debugfs_root, entry, &drm_debugfs_entry_fops);
310dbb23cf5SMaíra Canal 		list_del(&entry->list);
311dbb23cf5SMaíra Canal 	}
312dbb23cf5SMaíra Canal }
31328a62277SBen Gamari 
3147d74795bSLespiau, Damien int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
31528a62277SBen Gamari 			     struct drm_minor *minor)
31628a62277SBen Gamari {
31728a62277SBen Gamari 	struct list_head *pos, *q;
31828a62277SBen Gamari 	struct drm_info_node *tmp;
31928a62277SBen Gamari 	int i;
32028a62277SBen Gamari 
321b3e067c0SMarcin Slusarz 	mutex_lock(&minor->debugfs_lock);
32228a62277SBen Gamari 	for (i = 0; i < count; i++) {
323b3e067c0SMarcin Slusarz 		list_for_each_safe(pos, q, &minor->debugfs_list) {
32428a62277SBen Gamari 			tmp = list_entry(pos, struct drm_info_node, list);
32528a62277SBen Gamari 			if (tmp->info_ent == &files[i]) {
32628a62277SBen Gamari 				debugfs_remove(tmp->dent);
32728a62277SBen Gamari 				list_del(pos);
3289a298b2aSEric Anholt 				kfree(tmp);
32928a62277SBen Gamari 			}
33028a62277SBen Gamari 		}
33128a62277SBen Gamari 	}
332b3e067c0SMarcin Slusarz 	mutex_unlock(&minor->debugfs_lock);
33328a62277SBen Gamari 	return 0;
33428a62277SBen Gamari }
33528a62277SBen Gamari EXPORT_SYMBOL(drm_debugfs_remove_files);
33628a62277SBen Gamari 
337086f2e5cSNoralf Trønnes static void drm_debugfs_remove_all_files(struct drm_minor *minor)
338086f2e5cSNoralf Trønnes {
339086f2e5cSNoralf Trønnes 	struct drm_info_node *node, *tmp;
340086f2e5cSNoralf Trønnes 
341086f2e5cSNoralf Trønnes 	mutex_lock(&minor->debugfs_lock);
342086f2e5cSNoralf Trønnes 	list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) {
343086f2e5cSNoralf Trønnes 		debugfs_remove(node->dent);
344086f2e5cSNoralf Trønnes 		list_del(&node->list);
345086f2e5cSNoralf Trønnes 		kfree(node);
346086f2e5cSNoralf Trønnes 	}
347086f2e5cSNoralf Trønnes 	mutex_unlock(&minor->debugfs_lock);
348086f2e5cSNoralf Trønnes }
349086f2e5cSNoralf Trønnes 
350b792e640SGreg Kroah-Hartman void drm_debugfs_cleanup(struct drm_minor *minor)
35128a62277SBen Gamari {
35228a62277SBen Gamari 	if (!minor->debugfs_root)
353b792e640SGreg Kroah-Hartman 		return;
35428a62277SBen Gamari 
355086f2e5cSNoralf Trønnes 	drm_debugfs_remove_all_files(minor);
35628a62277SBen Gamari 
357086f2e5cSNoralf Trønnes 	debugfs_remove_recursive(minor->debugfs_root);
35828a62277SBen Gamari 	minor->debugfs_root = NULL;
35928a62277SBen Gamari }
36028a62277SBen Gamari 
3611c9cacbeSMaíra Canal /**
3621c9cacbeSMaíra Canal  * drm_debugfs_add_file - Add a given file to the DRM device debugfs file list
3631c9cacbeSMaíra Canal  * @dev: drm device for the ioctl
3641c9cacbeSMaíra Canal  * @name: debugfs file name
3651c9cacbeSMaíra Canal  * @show: show callback
3661c9cacbeSMaíra Canal  * @data: driver-private data, should not be device-specific
3671c9cacbeSMaíra Canal  *
3681c9cacbeSMaíra Canal  * Add a given file entry to the DRM device debugfs file list to be created on
3691c9cacbeSMaíra Canal  * drm_debugfs_init.
3701c9cacbeSMaíra Canal  */
3711c9cacbeSMaíra Canal void drm_debugfs_add_file(struct drm_device *dev, const char *name,
3721c9cacbeSMaíra Canal 			  int (*show)(struct seq_file*, void*), void *data)
3731c9cacbeSMaíra Canal {
3741c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry = drmm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
3751c9cacbeSMaíra Canal 
3761c9cacbeSMaíra Canal 	if (!entry)
3771c9cacbeSMaíra Canal 		return;
3781c9cacbeSMaíra Canal 
3791c9cacbeSMaíra Canal 	entry->file.name = name;
3801c9cacbeSMaíra Canal 	entry->file.show = show;
3811c9cacbeSMaíra Canal 	entry->file.data = data;
3821c9cacbeSMaíra Canal 	entry->dev = dev;
3831c9cacbeSMaíra Canal 
3841c9cacbeSMaíra Canal 	mutex_lock(&dev->debugfs_mutex);
3851c9cacbeSMaíra Canal 	list_add(&entry->list, &dev->debugfs_list);
3861c9cacbeSMaíra Canal 	mutex_unlock(&dev->debugfs_mutex);
3871c9cacbeSMaíra Canal }
3881c9cacbeSMaíra Canal EXPORT_SYMBOL(drm_debugfs_add_file);
3891c9cacbeSMaíra Canal 
3901c9cacbeSMaíra Canal /**
3911c9cacbeSMaíra Canal  * drm_debugfs_add_files - Add an array of files to the DRM device debugfs file list
3921c9cacbeSMaíra Canal  * @dev: drm device for the ioctl
3931c9cacbeSMaíra Canal  * @files: The array of files to create
3941c9cacbeSMaíra Canal  * @count: The number of files given
3951c9cacbeSMaíra Canal  *
3961c9cacbeSMaíra Canal  * Add a given set of debugfs files represented by an array of
3971c9cacbeSMaíra Canal  * &struct drm_debugfs_info in the DRM device debugfs file list.
3981c9cacbeSMaíra Canal  */
3991c9cacbeSMaíra Canal void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count)
4001c9cacbeSMaíra Canal {
4011c9cacbeSMaíra Canal 	int i;
4021c9cacbeSMaíra Canal 
4031c9cacbeSMaíra Canal 	for (i = 0; i < count; i++)
4041c9cacbeSMaíra Canal 		drm_debugfs_add_file(dev, files[i].name, files[i].show, files[i].data);
4051c9cacbeSMaíra Canal }
4061c9cacbeSMaíra Canal EXPORT_SYMBOL(drm_debugfs_add_files);
4071c9cacbeSMaíra Canal 
40830f65707SThomas Wood static int connector_show(struct seq_file *m, void *data)
40930f65707SThomas Wood {
41030f65707SThomas Wood 	struct drm_connector *connector = m->private;
41130f65707SThomas Wood 
4126140cf20SJani Nikula 	seq_printf(m, "%s\n", drm_get_connector_force_name(connector->force));
41330f65707SThomas Wood 
41430f65707SThomas Wood 	return 0;
41530f65707SThomas Wood }
41630f65707SThomas Wood 
41730f65707SThomas Wood static int connector_open(struct inode *inode, struct file *file)
41830f65707SThomas Wood {
41930f65707SThomas Wood 	struct drm_connector *dev = inode->i_private;
42030f65707SThomas Wood 
42130f65707SThomas Wood 	return single_open(file, connector_show, dev);
42230f65707SThomas Wood }
42330f65707SThomas Wood 
42430f65707SThomas Wood static ssize_t connector_write(struct file *file, const char __user *ubuf,
42530f65707SThomas Wood 			       size_t len, loff_t *offp)
42630f65707SThomas Wood {
42730f65707SThomas Wood 	struct seq_file *m = file->private_data;
42830f65707SThomas Wood 	struct drm_connector *connector = m->private;
42930f65707SThomas Wood 	char buf[12];
43030f65707SThomas Wood 
43130f65707SThomas Wood 	if (len > sizeof(buf) - 1)
43230f65707SThomas Wood 		return -EINVAL;
43330f65707SThomas Wood 
43430f65707SThomas Wood 	if (copy_from_user(buf, ubuf, len))
43530f65707SThomas Wood 		return -EFAULT;
43630f65707SThomas Wood 
43730f65707SThomas Wood 	buf[len] = '\0';
43830f65707SThomas Wood 
439c704b170SMichael Tretter 	if (sysfs_streq(buf, "on"))
44030f65707SThomas Wood 		connector->force = DRM_FORCE_ON;
441c704b170SMichael Tretter 	else if (sysfs_streq(buf, "digital"))
44230f65707SThomas Wood 		connector->force = DRM_FORCE_ON_DIGITAL;
443c704b170SMichael Tretter 	else if (sysfs_streq(buf, "off"))
44430f65707SThomas Wood 		connector->force = DRM_FORCE_OFF;
445c704b170SMichael Tretter 	else if (sysfs_streq(buf, "unspecified"))
44630f65707SThomas Wood 		connector->force = DRM_FORCE_UNSPECIFIED;
44730f65707SThomas Wood 	else
44830f65707SThomas Wood 		return -EINVAL;
44930f65707SThomas Wood 
45030f65707SThomas Wood 	return len;
45130f65707SThomas Wood }
45230f65707SThomas Wood 
4534cf2b281SThomas Wood static int edid_show(struct seq_file *m, void *data)
4544cf2b281SThomas Wood {
45591ec9ab4SJani Nikula 	return drm_edid_override_show(m->private, m);
4564cf2b281SThomas Wood }
4574cf2b281SThomas Wood 
4584cf2b281SThomas Wood static int edid_open(struct inode *inode, struct file *file)
4594cf2b281SThomas Wood {
4604cf2b281SThomas Wood 	struct drm_connector *dev = inode->i_private;
4614cf2b281SThomas Wood 
4624cf2b281SThomas Wood 	return single_open(file, edid_show, dev);
4634cf2b281SThomas Wood }
4644cf2b281SThomas Wood 
4654cf2b281SThomas Wood static ssize_t edid_write(struct file *file, const char __user *ubuf,
4664cf2b281SThomas Wood 			  size_t len, loff_t *offp)
4674cf2b281SThomas Wood {
4684cf2b281SThomas Wood 	struct seq_file *m = file->private_data;
4694cf2b281SThomas Wood 	struct drm_connector *connector = m->private;
4704cf2b281SThomas Wood 	char *buf;
4714cf2b281SThomas Wood 	int ret;
4724cf2b281SThomas Wood 
4734cf2b281SThomas Wood 	buf = memdup_user(ubuf, len);
4744cf2b281SThomas Wood 	if (IS_ERR(buf))
4754cf2b281SThomas Wood 		return PTR_ERR(buf);
4764cf2b281SThomas Wood 
4776aa145bcSJani Nikula 	if (len == 5 && !strncmp(buf, "reset", 5))
4786aa145bcSJani Nikula 		ret = drm_edid_override_reset(connector);
4796aa145bcSJani Nikula 	else
4806aa145bcSJani Nikula 		ret = drm_edid_override_set(connector, buf, len);
4814cf2b281SThomas Wood 
4824cf2b281SThomas Wood 	kfree(buf);
4834cf2b281SThomas Wood 
4846aa145bcSJani Nikula 	return ret ? ret : len;
4854cf2b281SThomas Wood }
4864cf2b281SThomas Wood 
48741752663SBhanuprakash Modem /*
48841752663SBhanuprakash Modem  * Returns the min and max vrr vfreq through the connector's debugfs file.
48941752663SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/vrr_range
49041752663SBhanuprakash Modem  */
49141752663SBhanuprakash Modem static int vrr_range_show(struct seq_file *m, void *data)
49241752663SBhanuprakash Modem {
49341752663SBhanuprakash Modem 	struct drm_connector *connector = m->private;
49441752663SBhanuprakash Modem 
49541752663SBhanuprakash Modem 	if (connector->status != connector_status_connected)
49641752663SBhanuprakash Modem 		return -ENODEV;
49741752663SBhanuprakash Modem 
498c7943bb3SVille Syrjälä 	seq_printf(m, "Min: %u\n", connector->display_info.monitor_range.min_vfreq);
499c7943bb3SVille Syrjälä 	seq_printf(m, "Max: %u\n", connector->display_info.monitor_range.max_vfreq);
50041752663SBhanuprakash Modem 
50141752663SBhanuprakash Modem 	return 0;
50241752663SBhanuprakash Modem }
50341752663SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(vrr_range);
50441752663SBhanuprakash Modem 
50567d935b4SBhanuprakash Modem /*
50667d935b4SBhanuprakash Modem  * Returns Connector's max supported bpc through debugfs file.
50767d935b4SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc
50867d935b4SBhanuprakash Modem  */
50967d935b4SBhanuprakash Modem static int output_bpc_show(struct seq_file *m, void *data)
51067d935b4SBhanuprakash Modem {
51167d935b4SBhanuprakash Modem 	struct drm_connector *connector = m->private;
51267d935b4SBhanuprakash Modem 
51367d935b4SBhanuprakash Modem 	if (connector->status != connector_status_connected)
51467d935b4SBhanuprakash Modem 		return -ENODEV;
51567d935b4SBhanuprakash Modem 
51667d935b4SBhanuprakash Modem 	seq_printf(m, "Maximum: %u\n", connector->display_info.bpc);
51767d935b4SBhanuprakash Modem 
51867d935b4SBhanuprakash Modem 	return 0;
51967d935b4SBhanuprakash Modem }
52067d935b4SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(output_bpc);
52167d935b4SBhanuprakash Modem 
5224cf2b281SThomas Wood static const struct file_operations drm_edid_fops = {
5234cf2b281SThomas Wood 	.owner = THIS_MODULE,
5244cf2b281SThomas Wood 	.open = edid_open,
5254cf2b281SThomas Wood 	.read = seq_read,
5264cf2b281SThomas Wood 	.llseek = seq_lseek,
5274cf2b281SThomas Wood 	.release = single_release,
5284cf2b281SThomas Wood 	.write = edid_write
5294cf2b281SThomas Wood };
5304cf2b281SThomas Wood 
5314cf2b281SThomas Wood 
53230f65707SThomas Wood static const struct file_operations drm_connector_fops = {
53330f65707SThomas Wood 	.owner = THIS_MODULE,
53430f65707SThomas Wood 	.open = connector_open,
53530f65707SThomas Wood 	.read = seq_read,
53630f65707SThomas Wood 	.llseek = seq_lseek,
53730f65707SThomas Wood 	.release = single_release,
53830f65707SThomas Wood 	.write = connector_write
53930f65707SThomas Wood };
54030f65707SThomas Wood 
541b792e640SGreg Kroah-Hartman void drm_debugfs_connector_add(struct drm_connector *connector)
54230f65707SThomas Wood {
54330f65707SThomas Wood 	struct drm_minor *minor = connector->dev->primary;
544b792e640SGreg Kroah-Hartman 	struct dentry *root;
54530f65707SThomas Wood 
54630f65707SThomas Wood 	if (!minor->debugfs_root)
547b792e640SGreg Kroah-Hartman 		return;
54830f65707SThomas Wood 
54930f65707SThomas Wood 	root = debugfs_create_dir(connector->name, minor->debugfs_root);
55030f65707SThomas Wood 	connector->debugfs_entry = root;
55130f65707SThomas Wood 
55230f65707SThomas Wood 	/* force */
553c9ba134eSMaíra Canal 	debugfs_create_file("force", 0644, root, connector,
55430f65707SThomas Wood 			    &drm_connector_fops);
55530f65707SThomas Wood 
5564cf2b281SThomas Wood 	/* edid */
557c9ba134eSMaíra Canal 	debugfs_create_file("edid_override", 0644, root, connector,
558b792e640SGreg Kroah-Hartman 			    &drm_edid_fops);
55941752663SBhanuprakash Modem 
56041752663SBhanuprakash Modem 	/* vrr range */
561c9ba134eSMaíra Canal 	debugfs_create_file("vrr_range", 0444, root, connector,
56241752663SBhanuprakash Modem 			    &vrr_range_fops);
5632509969aSDouglas Anderson 
56467d935b4SBhanuprakash Modem 	/* max bpc */
56567d935b4SBhanuprakash Modem 	debugfs_create_file("output_bpc", 0444, root, connector,
56667d935b4SBhanuprakash Modem 			    &output_bpc_fops);
56767d935b4SBhanuprakash Modem 
5682509969aSDouglas Anderson 	if (connector->funcs->debugfs_init)
5692509969aSDouglas Anderson 		connector->funcs->debugfs_init(connector, root);
57030f65707SThomas Wood }
57130f65707SThomas Wood 
57230f65707SThomas Wood void drm_debugfs_connector_remove(struct drm_connector *connector)
57330f65707SThomas Wood {
57430f65707SThomas Wood 	if (!connector->debugfs_entry)
57530f65707SThomas Wood 		return;
57630f65707SThomas Wood 
57730f65707SThomas Wood 	debugfs_remove_recursive(connector->debugfs_entry);
57830f65707SThomas Wood 
57930f65707SThomas Wood 	connector->debugfs_entry = NULL;
58030f65707SThomas Wood }
58130f65707SThomas Wood 
582b792e640SGreg Kroah-Hartman void drm_debugfs_crtc_add(struct drm_crtc *crtc)
5839edbf1faSTomeu Vizoso {
5849edbf1faSTomeu Vizoso 	struct drm_minor *minor = crtc->dev->primary;
5859edbf1faSTomeu Vizoso 	struct dentry *root;
5869edbf1faSTomeu Vizoso 	char *name;
58728a62277SBen Gamari 
5889edbf1faSTomeu Vizoso 	name = kasprintf(GFP_KERNEL, "crtc-%d", crtc->index);
5899edbf1faSTomeu Vizoso 	if (!name)
590b792e640SGreg Kroah-Hartman 		return;
5919edbf1faSTomeu Vizoso 
5929edbf1faSTomeu Vizoso 	root = debugfs_create_dir(name, minor->debugfs_root);
5939edbf1faSTomeu Vizoso 	kfree(name);
5949edbf1faSTomeu Vizoso 
5959edbf1faSTomeu Vizoso 	crtc->debugfs_entry = root;
5969edbf1faSTomeu Vizoso 
597b792e640SGreg Kroah-Hartman 	drm_debugfs_crtc_crc_add(crtc);
5989edbf1faSTomeu Vizoso }
5999edbf1faSTomeu Vizoso 
6009edbf1faSTomeu Vizoso void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
6019edbf1faSTomeu Vizoso {
6029edbf1faSTomeu Vizoso 	debugfs_remove_recursive(crtc->debugfs_entry);
6039edbf1faSTomeu Vizoso 	crtc->debugfs_entry = NULL;
6049edbf1faSTomeu Vizoso }
6059edbf1faSTomeu Vizoso 
6069edbf1faSTomeu Vizoso #endif /* CONFIG_DEBUG_FS */
607