xref: /linux-6.15/drivers/gpu/drm/drm_debugfs.c (revision 72443c73)
128a62277SBen Gamari /*
228a62277SBen Gamari  * Created: Sun Dec 21 13:08:50 2008 by [email protected]
328a62277SBen Gamari  *
428a62277SBen Gamari  * Copyright 2008 Ben Gamari <[email protected]>
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_debugfs.h>
360500c04eSSam Ravnborg #include <drm/drm_device.h>
370500c04eSSam Ravnborg #include <drm/drm_drv.h>
380500c04eSSam Ravnborg #include <drm/drm_edid.h>
390500c04eSSam Ravnborg #include <drm/drm_file.h>
40c6fdea6eSDaniel Vetter #include <drm/drm_gem.h>
411c9cacbeSMaíra Canal #include <drm/drm_managed.h>
42f72c2db4SDanilo Krummrich #include <drm/drm_gpuvm.h>
434834442dSDaniel Vetter 
445bc9cb4dSDaniel Vetter #include "drm_crtc_internal.h"
450500c04eSSam Ravnborg #include "drm_internal.h"
4628a62277SBen Gamari 
4728a62277SBen Gamari /***************************************************
4828a62277SBen Gamari  * Initialization, etc.
4928a62277SBen Gamari  **************************************************/
5028a62277SBen Gamari 
drm_name_info(struct seq_file * m,void * data)51c6fdea6eSDaniel Vetter static int drm_name_info(struct seq_file *m, void *data)
52c6fdea6eSDaniel Vetter {
536fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
546fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
55c6fdea6eSDaniel Vetter 	struct drm_master *master;
56c6fdea6eSDaniel Vetter 
57c6fdea6eSDaniel Vetter 	mutex_lock(&dev->master_mutex);
58c6fdea6eSDaniel Vetter 	master = dev->master;
59c6fdea6eSDaniel Vetter 	seq_printf(m, "%s", dev->driver->name);
60c6fdea6eSDaniel Vetter 	if (dev->dev)
61c6fdea6eSDaniel Vetter 		seq_printf(m, " dev=%s", dev_name(dev->dev));
62c6fdea6eSDaniel Vetter 	if (master && master->unique)
63c6fdea6eSDaniel Vetter 		seq_printf(m, " master=%s", master->unique);
64c6fdea6eSDaniel Vetter 	if (dev->unique)
65c6fdea6eSDaniel Vetter 		seq_printf(m, " unique=%s", dev->unique);
66c6fdea6eSDaniel Vetter 	seq_printf(m, "\n");
67c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->master_mutex);
68c6fdea6eSDaniel Vetter 
69c6fdea6eSDaniel Vetter 	return 0;
70c6fdea6eSDaniel Vetter }
71c6fdea6eSDaniel Vetter 
drm_clients_info(struct seq_file * m,void * data)72c6fdea6eSDaniel Vetter static int drm_clients_info(struct seq_file *m, void *data)
73c6fdea6eSDaniel Vetter {
746fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
756fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
76c6fdea6eSDaniel Vetter 	struct drm_file *priv;
77c6fdea6eSDaniel Vetter 	kuid_t uid;
78c6fdea6eSDaniel Vetter 
79c6fdea6eSDaniel Vetter 	seq_printf(m,
8056c594d8SPierre-Eric Pelloux-Prayer 		   "%20s %5s %3s master a %5s %10s %*s\n",
81c6fdea6eSDaniel Vetter 		   "command",
824230cea8STvrtko Ursulin 		   "tgid",
83c6fdea6eSDaniel Vetter 		   "dev",
84c6fdea6eSDaniel Vetter 		   "uid",
8556c594d8SPierre-Eric Pelloux-Prayer 		   "magic",
8656c594d8SPierre-Eric Pelloux-Prayer 		   DRM_CLIENT_NAME_MAX_LEN,
8756c594d8SPierre-Eric Pelloux-Prayer 		   "name");
88c6fdea6eSDaniel Vetter 
89c6fdea6eSDaniel Vetter 	/* dev->filelist is sorted youngest first, but we want to present
90c6fdea6eSDaniel Vetter 	 * oldest first (i.e. kernel, servers, clients), so walk backwardss.
91c6fdea6eSDaniel Vetter 	 */
92c6fdea6eSDaniel Vetter 	mutex_lock(&dev->filelist_mutex);
93c6fdea6eSDaniel Vetter 	list_for_each_entry_reverse(priv, &dev->filelist, lhead) {
945eff9585SDesmond Cheong Zhi Xi 		bool is_current_master = drm_is_current_master(priv);
951c7a387fSTvrtko Ursulin 		struct task_struct *task;
961c7a387fSTvrtko Ursulin 		struct pid *pid;
97c6fdea6eSDaniel Vetter 
9856c594d8SPierre-Eric Pelloux-Prayer 		mutex_lock(&priv->client_name_lock);
991c7a387fSTvrtko Ursulin 		rcu_read_lock(); /* Locks priv->pid and pid_task()->comm! */
1001c7a387fSTvrtko Ursulin 		pid = rcu_dereference(priv->pid);
1011c7a387fSTvrtko Ursulin 		task = pid_task(pid, PIDTYPE_TGID);
102c6fdea6eSDaniel Vetter 		uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID;
10356c594d8SPierre-Eric Pelloux-Prayer 		seq_printf(m, "%20s %5d %3d   %c    %c %5d %10u %*s\n",
104c6fdea6eSDaniel Vetter 			   task ? task->comm : "<unknown>",
1051c7a387fSTvrtko 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),
11056c594d8SPierre-Eric Pelloux-Prayer 			   priv->magic,
11156c594d8SPierre-Eric Pelloux-Prayer 			   DRM_CLIENT_NAME_MAX_LEN,
11256c594d8SPierre-Eric Pelloux-Prayer 			   priv->client_name ? priv->client_name : "<unset>");
113c6fdea6eSDaniel Vetter 		rcu_read_unlock();
11456c594d8SPierre-Eric Pelloux-Prayer 		mutex_unlock(&priv->client_name_lock);
115c6fdea6eSDaniel Vetter 	}
116c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->filelist_mutex);
117c6fdea6eSDaniel Vetter 	return 0;
118c6fdea6eSDaniel Vetter }
119c6fdea6eSDaniel Vetter 
drm_gem_one_name_info(int id,void * ptr,void * data)120c6fdea6eSDaniel Vetter static int drm_gem_one_name_info(int id, void *ptr, void *data)
121c6fdea6eSDaniel Vetter {
122c6fdea6eSDaniel Vetter 	struct drm_gem_object *obj = ptr;
123c6fdea6eSDaniel Vetter 	struct seq_file *m = data;
124c6fdea6eSDaniel Vetter 
125c6fdea6eSDaniel Vetter 	seq_printf(m, "%6d %8zd %7d %8d\n",
126c6fdea6eSDaniel Vetter 		   obj->name, obj->size,
127c6fdea6eSDaniel Vetter 		   obj->handle_count,
128c6fdea6eSDaniel Vetter 		   kref_read(&obj->refcount));
129c6fdea6eSDaniel Vetter 	return 0;
130c6fdea6eSDaniel Vetter }
131c6fdea6eSDaniel Vetter 
drm_gem_name_info(struct seq_file * m,void * data)132c6fdea6eSDaniel Vetter static int drm_gem_name_info(struct seq_file *m, void *data)
133c6fdea6eSDaniel Vetter {
1346fd80729SMaíra Canal 	struct drm_debugfs_entry *entry = m->private;
1356fd80729SMaíra Canal 	struct drm_device *dev = entry->dev;
136c6fdea6eSDaniel Vetter 
137c6fdea6eSDaniel Vetter 	seq_printf(m, "  name     size handles refcount\n");
138c6fdea6eSDaniel Vetter 
139c6fdea6eSDaniel Vetter 	mutex_lock(&dev->object_name_lock);
140c6fdea6eSDaniel Vetter 	idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m);
141c6fdea6eSDaniel Vetter 	mutex_unlock(&dev->object_name_lock);
142c6fdea6eSDaniel Vetter 
143c6fdea6eSDaniel Vetter 	return 0;
144c6fdea6eSDaniel Vetter }
145c6fdea6eSDaniel Vetter 
1466fd80729SMaíra Canal static const struct drm_debugfs_info drm_debugfs_list[] = {
14728a62277SBen Gamari 	{"name", drm_name_info, 0},
14828a62277SBen Gamari 	{"clients", drm_clients_info, 0},
14928a62277SBen Gamari 	{"gem_names", drm_gem_name_info, DRIVER_GEM},
15028a62277SBen Gamari };
15128a62277SBen Gamari #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
15228a62277SBen Gamari 
15328a62277SBen Gamari 
drm_debugfs_open(struct inode * inode,struct file * file)15428a62277SBen Gamari static int drm_debugfs_open(struct inode *inode, struct file *file)
15528a62277SBen Gamari {
15628a62277SBen Gamari 	struct drm_info_node *node = inode->i_private;
15728a62277SBen Gamari 
1587a0f2178SChristian König 	if (!device_is_registered(node->minor->kdev))
1597a0f2178SChristian König 		return -ENODEV;
1607a0f2178SChristian König 
16128a62277SBen Gamari 	return single_open(file, node->info_ent->show, node);
16228a62277SBen Gamari }
16328a62277SBen Gamari 
drm_debugfs_entry_open(struct inode * inode,struct file * file)1641c9cacbeSMaíra Canal static int drm_debugfs_entry_open(struct inode *inode, struct file *file)
1651c9cacbeSMaíra Canal {
1661c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry = inode->i_private;
1671c9cacbeSMaíra Canal 	struct drm_debugfs_info *node = &entry->file;
1687a0f2178SChristian König 	struct drm_minor *minor = entry->dev->primary ?: entry->dev->accel;
1697a0f2178SChristian König 
1707a0f2178SChristian König 	if (!device_is_registered(minor->kdev))
1717a0f2178SChristian König 		return -ENODEV;
1721c9cacbeSMaíra Canal 
1731c9cacbeSMaíra Canal 	return single_open(file, node->show, entry);
1741c9cacbeSMaíra Canal }
1751c9cacbeSMaíra Canal 
1761c9cacbeSMaíra Canal static const struct file_operations drm_debugfs_entry_fops = {
1771c9cacbeSMaíra Canal 	.owner = THIS_MODULE,
1781c9cacbeSMaíra Canal 	.open = drm_debugfs_entry_open,
1791c9cacbeSMaíra Canal 	.read = seq_read,
1801c9cacbeSMaíra Canal 	.llseek = seq_lseek,
1811c9cacbeSMaíra Canal 	.release = single_release,
1821c9cacbeSMaíra Canal };
18328a62277SBen Gamari 
18428a62277SBen Gamari static const struct file_operations drm_debugfs_fops = {
18528a62277SBen Gamari 	.owner = THIS_MODULE,
18628a62277SBen Gamari 	.open = drm_debugfs_open,
18728a62277SBen Gamari 	.read = seq_read,
18828a62277SBen Gamari 	.llseek = seq_lseek,
18928a62277SBen Gamari 	.release = single_release,
19028a62277SBen Gamari };
19128a62277SBen Gamari 
1924f66feeaSDanilo Krummrich /**
1934f66feeaSDanilo Krummrich  * drm_debugfs_gpuva_info - dump the given DRM GPU VA space
1944f66feeaSDanilo Krummrich  * @m: pointer to the &seq_file to write
195f72c2db4SDanilo Krummrich  * @gpuvm: the &drm_gpuvm representing the GPU VA space
1964f66feeaSDanilo Krummrich  *
1974f66feeaSDanilo Krummrich  * Dumps the GPU VA mappings of a given DRM GPU VA manager.
1984f66feeaSDanilo Krummrich  *
1994f66feeaSDanilo Krummrich  * For each DRM GPU VA space drivers should call this function from their
2004f66feeaSDanilo Krummrich  * &drm_info_list's show callback.
2014f66feeaSDanilo Krummrich  *
202f72c2db4SDanilo Krummrich  * Returns: 0 on success, -ENODEV if the &gpuvm is not initialized
2034f66feeaSDanilo Krummrich  */
drm_debugfs_gpuva_info(struct seq_file * m,struct drm_gpuvm * gpuvm)2044f66feeaSDanilo Krummrich int drm_debugfs_gpuva_info(struct seq_file *m,
205f72c2db4SDanilo Krummrich 			   struct drm_gpuvm *gpuvm)
2064f66feeaSDanilo Krummrich {
207f72c2db4SDanilo Krummrich 	struct drm_gpuva *va, *kva = &gpuvm->kernel_alloc_node;
2084f66feeaSDanilo Krummrich 
209f72c2db4SDanilo Krummrich 	if (!gpuvm->name)
2104f66feeaSDanilo Krummrich 		return -ENODEV;
2114f66feeaSDanilo Krummrich 
2124f66feeaSDanilo Krummrich 	seq_printf(m, "DRM GPU VA space (%s) [0x%016llx;0x%016llx]\n",
213f72c2db4SDanilo Krummrich 		   gpuvm->name, gpuvm->mm_start, gpuvm->mm_start + gpuvm->mm_range);
2144f66feeaSDanilo Krummrich 	seq_printf(m, "Kernel reserved node [0x%016llx;0x%016llx]\n",
2154f66feeaSDanilo Krummrich 		   kva->va.addr, kva->va.addr + kva->va.range);
2164f66feeaSDanilo Krummrich 	seq_puts(m, "\n");
2174f66feeaSDanilo Krummrich 	seq_puts(m, " VAs | start              | range              | end                | object             | object offset\n");
2184f66feeaSDanilo Krummrich 	seq_puts(m, "-------------------------------------------------------------------------------------------------------------\n");
219f72c2db4SDanilo Krummrich 	drm_gpuvm_for_each_va(va, gpuvm) {
2204f66feeaSDanilo Krummrich 		if (unlikely(va == kva))
2214f66feeaSDanilo Krummrich 			continue;
2224f66feeaSDanilo Krummrich 
2234f66feeaSDanilo Krummrich 		seq_printf(m, "     | 0x%016llx | 0x%016llx | 0x%016llx | 0x%016llx | 0x%016llx\n",
2244f66feeaSDanilo Krummrich 			   va->va.addr, va->va.range, va->va.addr + va->va.range,
22534d7edcfSSteven Price 			   (u64)(uintptr_t)va->gem.obj, va->gem.offset);
2264f66feeaSDanilo Krummrich 	}
2274f66feeaSDanilo Krummrich 
2284f66feeaSDanilo Krummrich 	return 0;
2294f66feeaSDanilo Krummrich }
2304f66feeaSDanilo Krummrich EXPORT_SYMBOL(drm_debugfs_gpuva_info);
23128a62277SBen Gamari 
23228a62277SBen Gamari /**
2330cad7f71SDaniel Vetter  * drm_debugfs_create_files - Initialize a given set of debugfs files for DRM
2340cad7f71SDaniel Vetter  * 			minor
2350cad7f71SDaniel Vetter  * @files: The array of files to create
2360cad7f71SDaniel Vetter  * @count: The number of files given
2370cad7f71SDaniel Vetter  * @root: DRI debugfs dir entry.
2380cad7f71SDaniel Vetter  * @minor: device minor number
23928a62277SBen Gamari  *
24028a62277SBen Gamari  * Create a given set of debugfs files represented by an array of
2410cad7f71SDaniel Vetter  * &struct drm_info_list in the given root directory. These files will be removed
2428e455145SChristian König  * automatically on drm_debugfs_dev_fini().
24328a62277SBen Gamari  */
drm_debugfs_create_files(const struct drm_info_list * files,int count,struct dentry * root,struct drm_minor * minor)244a212d6a5SWambui Karuga void drm_debugfs_create_files(const struct drm_info_list *files, int count,
24528a62277SBen Gamari 			      struct dentry *root, struct drm_minor *minor)
24628a62277SBen Gamari {
24728a62277SBen Gamari 	struct drm_device *dev = minor->dev;
24828a62277SBen Gamari 	struct drm_info_node *tmp;
249987d65d0SGreg Kroah-Hartman 	int i;
25028a62277SBen Gamari 
25128a62277SBen Gamari 	for (i = 0; i < count; i++) {
25228a62277SBen Gamari 		u32 features = files[i].driver_features;
25328a62277SBen Gamari 
25423d498f6SJani Nikula 		if (features && !drm_core_check_all_features(dev, features))
25528a62277SBen Gamari 			continue;
25628a62277SBen Gamari 
2578e455145SChristian König 		tmp = drmm_kzalloc(dev, sizeof(*tmp), GFP_KERNEL);
258987d65d0SGreg Kroah-Hartman 		if (tmp == NULL)
259987d65d0SGreg Kroah-Hartman 			continue;
26028a62277SBen Gamari 
26128a62277SBen Gamari 		tmp->minor = minor;
262987d65d0SGreg Kroah-Hartman 		tmp->dent = debugfs_create_file(files[i].name,
263c9ba134eSMaíra Canal 						0444, root, tmp,
264987d65d0SGreg Kroah-Hartman 						&drm_debugfs_fops);
26528a62277SBen Gamari 		tmp->info_ent = &files[i];
26628a62277SBen Gamari 	}
26728a62277SBen Gamari }
26828a62277SBen Gamari EXPORT_SYMBOL(drm_debugfs_create_files);
26928a62277SBen Gamari 
drm_debugfs_remove_files(const struct drm_info_list * files,int count,struct dentry * root,struct drm_minor * minor)2708e455145SChristian König int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
2718e455145SChristian König 			     struct dentry *root, struct drm_minor *minor)
2728e455145SChristian König {
2738e455145SChristian König 	int i;
2748e455145SChristian König 
2758e455145SChristian König 	for (i = 0; i < count; i++) {
2768e455145SChristian König 		struct dentry *dent = debugfs_lookup(files[i].name, root);
2778e455145SChristian König 
2788e455145SChristian König 		if (!dent)
2798e455145SChristian König 			continue;
2808e455145SChristian König 
2818e455145SChristian König 		drmm_kfree(minor->dev, d_inode(dent)->i_private);
2828e455145SChristian König 		debugfs_remove(dent);
2838e455145SChristian König 	}
2848e455145SChristian König 	return 0;
2858e455145SChristian König }
2868e455145SChristian König EXPORT_SYMBOL(drm_debugfs_remove_files);
2878e455145SChristian König 
2880b30d57aSChristian König /**
2890b30d57aSChristian König  * drm_debugfs_dev_init - create debugfs directory for the device
2900b30d57aSChristian König  * @dev: the device which we want to create the directory for
2910b30d57aSChristian König  * @root: the parent directory depending on the device type
2920b30d57aSChristian König  *
2930b30d57aSChristian König  * Creates the debugfs directory for the device under the given root directory.
2940b30d57aSChristian König  */
drm_debugfs_dev_init(struct drm_device * dev,struct dentry * root)2950b30d57aSChristian König void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root)
2960b30d57aSChristian König {
2970b30d57aSChristian König 	dev->debugfs_root = debugfs_create_dir(dev->unique, root);
2980b30d57aSChristian König }
2990b30d57aSChristian König 
3000b30d57aSChristian König /**
3010b30d57aSChristian König  * drm_debugfs_dev_fini - cleanup debugfs directory
3020b30d57aSChristian König  * @dev: the device to cleanup the debugfs stuff
3030b30d57aSChristian König  *
3040b30d57aSChristian König  * Remove the debugfs directory, might be called multiple times.
3050b30d57aSChristian König  */
drm_debugfs_dev_fini(struct drm_device * dev)3060b30d57aSChristian König void drm_debugfs_dev_fini(struct drm_device *dev)
3070b30d57aSChristian König {
3080b30d57aSChristian König 	debugfs_remove_recursive(dev->debugfs_root);
3090b30d57aSChristian König 	dev->debugfs_root = NULL;
3100b30d57aSChristian König }
3110b30d57aSChristian König 
drm_debugfs_dev_register(struct drm_device * dev)3120b30d57aSChristian König void drm_debugfs_dev_register(struct drm_device *dev)
3130b30d57aSChristian König {
3140b30d57aSChristian König 	drm_debugfs_add_files(dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES);
3150b30d57aSChristian König 
3160b30d57aSChristian König 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
3170b30d57aSChristian König 		drm_framebuffer_debugfs_init(dev);
3180b30d57aSChristian König 		drm_client_debugfs_init(dev);
3190b30d57aSChristian König 	}
320d0b3c318SDmitry Baryshkov 	if (drm_drv_uses_atomic_modeset(dev))
3210b30d57aSChristian König 		drm_atomic_debugfs_init(dev);
3220b30d57aSChristian König }
3230b30d57aSChristian König 
drm_debugfs_register(struct drm_minor * minor,int minor_id,struct dentry * root)3240b30d57aSChristian König int drm_debugfs_register(struct drm_minor *minor, int minor_id,
32528a62277SBen Gamari 			 struct dentry *root)
32628a62277SBen Gamari {
32728a62277SBen Gamari 	struct drm_device *dev = minor->dev;
32828a62277SBen Gamari 	char name[64];
32928a62277SBen Gamari 
33028a62277SBen Gamari 	sprintf(name, "%d", minor_id);
3310b30d57aSChristian König 	minor->debugfs_symlink = debugfs_create_symlink(name, root,
3320b30d57aSChristian König 							dev->unique);
33328a62277SBen Gamari 
3340b30d57aSChristian König 	/* TODO: Only for compatibility with drivers */
3350b30d57aSChristian König 	minor->debugfs_root = dev->debugfs_root;
33645d58b40SNoralf Trønnes 
337e76e7ec8SChristian König 	if (dev->driver->debugfs_init && dev->render != minor)
3387ce84471SWambui Karuga 		dev->driver->debugfs_init(minor);
3397ce84471SWambui Karuga 
34028a62277SBen Gamari 	return 0;
34128a62277SBen Gamari }
34228a62277SBen Gamari 
drm_debugfs_unregister(struct drm_minor * minor)3438e455145SChristian König void drm_debugfs_unregister(struct drm_minor *minor)
34428a62277SBen Gamari {
3450b30d57aSChristian König 	debugfs_remove(minor->debugfs_symlink);
3460b30d57aSChristian König 	minor->debugfs_symlink = NULL;
34728a62277SBen Gamari }
34828a62277SBen Gamari 
3491c9cacbeSMaíra Canal /**
3501c9cacbeSMaíra Canal  * drm_debugfs_add_file - Add a given file to the DRM device debugfs file list
3511c9cacbeSMaíra Canal  * @dev: drm device for the ioctl
3521c9cacbeSMaíra Canal  * @name: debugfs file name
3531c9cacbeSMaíra Canal  * @show: show callback
3541c9cacbeSMaíra Canal  * @data: driver-private data, should not be device-specific
3551c9cacbeSMaíra Canal  *
3561c9cacbeSMaíra Canal  * Add a given file entry to the DRM device debugfs file list to be created on
3571c9cacbeSMaíra Canal  * drm_debugfs_init.
3581c9cacbeSMaíra Canal  */
drm_debugfs_add_file(struct drm_device * dev,const char * name,int (* show)(struct seq_file *,void *),void * data)3591c9cacbeSMaíra Canal void drm_debugfs_add_file(struct drm_device *dev, const char *name,
3601c9cacbeSMaíra Canal 			  int (*show)(struct seq_file*, void*), void *data)
3611c9cacbeSMaíra Canal {
3621c9cacbeSMaíra Canal 	struct drm_debugfs_entry *entry = drmm_kzalloc(dev, sizeof(*entry), GFP_KERNEL);
3631c9cacbeSMaíra Canal 
3641c9cacbeSMaíra Canal 	if (!entry)
3651c9cacbeSMaíra Canal 		return;
3661c9cacbeSMaíra Canal 
3671c9cacbeSMaíra Canal 	entry->file.name = name;
3681c9cacbeSMaíra Canal 	entry->file.show = show;
3691c9cacbeSMaíra Canal 	entry->file.data = data;
3701c9cacbeSMaíra Canal 	entry->dev = dev;
3711c9cacbeSMaíra Canal 
372ec9c7073SChristian König 	debugfs_create_file(name, 0444, dev->debugfs_root, entry,
373ec9c7073SChristian König 			    &drm_debugfs_entry_fops);
3741c9cacbeSMaíra Canal }
3751c9cacbeSMaíra Canal EXPORT_SYMBOL(drm_debugfs_add_file);
3761c9cacbeSMaíra Canal 
3771c9cacbeSMaíra Canal /**
3781c9cacbeSMaíra Canal  * drm_debugfs_add_files - Add an array of files to the DRM device debugfs file list
3791c9cacbeSMaíra Canal  * @dev: drm device for the ioctl
3801c9cacbeSMaíra Canal  * @files: The array of files to create
3811c9cacbeSMaíra Canal  * @count: The number of files given
3821c9cacbeSMaíra Canal  *
3831c9cacbeSMaíra Canal  * Add a given set of debugfs files represented by an array of
3841c9cacbeSMaíra Canal  * &struct drm_debugfs_info in the DRM device debugfs file list.
3851c9cacbeSMaíra Canal  */
drm_debugfs_add_files(struct drm_device * dev,const struct drm_debugfs_info * files,int count)3861c9cacbeSMaíra Canal void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count)
3871c9cacbeSMaíra Canal {
3881c9cacbeSMaíra Canal 	int i;
3891c9cacbeSMaíra Canal 
3901c9cacbeSMaíra Canal 	for (i = 0; i < count; i++)
3911c9cacbeSMaíra Canal 		drm_debugfs_add_file(dev, files[i].name, files[i].show, files[i].data);
3921c9cacbeSMaíra Canal }
3931c9cacbeSMaíra Canal EXPORT_SYMBOL(drm_debugfs_add_files);
3941c9cacbeSMaíra Canal 
connector_show(struct seq_file * m,void * data)39530f65707SThomas Wood static int connector_show(struct seq_file *m, void *data)
39630f65707SThomas Wood {
39730f65707SThomas Wood 	struct drm_connector *connector = m->private;
39830f65707SThomas Wood 
3996140cf20SJani Nikula 	seq_printf(m, "%s\n", drm_get_connector_force_name(connector->force));
40030f65707SThomas Wood 
40130f65707SThomas Wood 	return 0;
40230f65707SThomas Wood }
40330f65707SThomas Wood 
connector_open(struct inode * inode,struct file * file)40430f65707SThomas Wood static int connector_open(struct inode *inode, struct file *file)
40530f65707SThomas Wood {
40630f65707SThomas Wood 	struct drm_connector *dev = inode->i_private;
40730f65707SThomas Wood 
40830f65707SThomas Wood 	return single_open(file, connector_show, dev);
40930f65707SThomas Wood }
41030f65707SThomas Wood 
connector_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)41130f65707SThomas Wood static ssize_t connector_write(struct file *file, const char __user *ubuf,
41230f65707SThomas Wood 			       size_t len, loff_t *offp)
41330f65707SThomas Wood {
41430f65707SThomas Wood 	struct seq_file *m = file->private_data;
41530f65707SThomas Wood 	struct drm_connector *connector = m->private;
41630f65707SThomas Wood 	char buf[12];
41730f65707SThomas Wood 
41830f65707SThomas Wood 	if (len > sizeof(buf) - 1)
41930f65707SThomas Wood 		return -EINVAL;
42030f65707SThomas Wood 
42130f65707SThomas Wood 	if (copy_from_user(buf, ubuf, len))
42230f65707SThomas Wood 		return -EFAULT;
42330f65707SThomas Wood 
42430f65707SThomas Wood 	buf[len] = '\0';
42530f65707SThomas Wood 
426c704b170SMichael Tretter 	if (sysfs_streq(buf, "on"))
42730f65707SThomas Wood 		connector->force = DRM_FORCE_ON;
428c704b170SMichael Tretter 	else if (sysfs_streq(buf, "digital"))
42930f65707SThomas Wood 		connector->force = DRM_FORCE_ON_DIGITAL;
430c704b170SMichael Tretter 	else if (sysfs_streq(buf, "off"))
43130f65707SThomas Wood 		connector->force = DRM_FORCE_OFF;
432c704b170SMichael Tretter 	else if (sysfs_streq(buf, "unspecified"))
43330f65707SThomas Wood 		connector->force = DRM_FORCE_UNSPECIFIED;
43430f65707SThomas Wood 	else
43530f65707SThomas Wood 		return -EINVAL;
43630f65707SThomas Wood 
43730f65707SThomas Wood 	return len;
43830f65707SThomas Wood }
43930f65707SThomas Wood 
edid_show(struct seq_file * m,void * data)4404cf2b281SThomas Wood static int edid_show(struct seq_file *m, void *data)
4414cf2b281SThomas Wood {
44291ec9ab4SJani Nikula 	return drm_edid_override_show(m->private, m);
4434cf2b281SThomas Wood }
4444cf2b281SThomas Wood 
edid_open(struct inode * inode,struct file * file)4454cf2b281SThomas Wood static int edid_open(struct inode *inode, struct file *file)
4464cf2b281SThomas Wood {
4474cf2b281SThomas Wood 	struct drm_connector *dev = inode->i_private;
4484cf2b281SThomas Wood 
4494cf2b281SThomas Wood 	return single_open(file, edid_show, dev);
4504cf2b281SThomas Wood }
4514cf2b281SThomas Wood 
edid_write(struct file * file,const char __user * ubuf,size_t len,loff_t * offp)4524cf2b281SThomas Wood static ssize_t edid_write(struct file *file, const char __user *ubuf,
4534cf2b281SThomas Wood 			  size_t len, loff_t *offp)
4544cf2b281SThomas Wood {
4554cf2b281SThomas Wood 	struct seq_file *m = file->private_data;
4564cf2b281SThomas Wood 	struct drm_connector *connector = m->private;
4574cf2b281SThomas Wood 	char *buf;
4584cf2b281SThomas Wood 	int ret;
4594cf2b281SThomas Wood 
4604cf2b281SThomas Wood 	buf = memdup_user(ubuf, len);
4614cf2b281SThomas Wood 	if (IS_ERR(buf))
4624cf2b281SThomas Wood 		return PTR_ERR(buf);
4634cf2b281SThomas Wood 
4646aa145bcSJani Nikula 	if (len == 5 && !strncmp(buf, "reset", 5))
4656aa145bcSJani Nikula 		ret = drm_edid_override_reset(connector);
4666aa145bcSJani Nikula 	else
4676aa145bcSJani Nikula 		ret = drm_edid_override_set(connector, buf, len);
4684cf2b281SThomas Wood 
4694cf2b281SThomas Wood 	kfree(buf);
4704cf2b281SThomas Wood 
4716aa145bcSJani Nikula 	return ret ? ret : len;
4724cf2b281SThomas Wood }
4734cf2b281SThomas Wood 
47441752663SBhanuprakash Modem /*
47541752663SBhanuprakash Modem  * Returns the min and max vrr vfreq through the connector's debugfs file.
47641752663SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/vrr_range
47741752663SBhanuprakash Modem  */
vrr_range_show(struct seq_file * m,void * data)47841752663SBhanuprakash Modem static int vrr_range_show(struct seq_file *m, void *data)
47941752663SBhanuprakash Modem {
48041752663SBhanuprakash Modem 	struct drm_connector *connector = m->private;
48141752663SBhanuprakash Modem 
48241752663SBhanuprakash Modem 	if (connector->status != connector_status_connected)
48341752663SBhanuprakash Modem 		return -ENODEV;
48441752663SBhanuprakash Modem 
485c7943bb3SVille Syrjälä 	seq_printf(m, "Min: %u\n", connector->display_info.monitor_range.min_vfreq);
486c7943bb3SVille Syrjälä 	seq_printf(m, "Max: %u\n", connector->display_info.monitor_range.max_vfreq);
48741752663SBhanuprakash Modem 
48841752663SBhanuprakash Modem 	return 0;
48941752663SBhanuprakash Modem }
49041752663SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(vrr_range);
49141752663SBhanuprakash Modem 
49267d935b4SBhanuprakash Modem /*
49367d935b4SBhanuprakash Modem  * Returns Connector's max supported bpc through debugfs file.
49467d935b4SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/output_bpc
49567d935b4SBhanuprakash Modem  */
output_bpc_show(struct seq_file * m,void * data)49667d935b4SBhanuprakash Modem static int output_bpc_show(struct seq_file *m, void *data)
49767d935b4SBhanuprakash Modem {
49867d935b4SBhanuprakash Modem 	struct drm_connector *connector = m->private;
49967d935b4SBhanuprakash Modem 
50067d935b4SBhanuprakash Modem 	if (connector->status != connector_status_connected)
50167d935b4SBhanuprakash Modem 		return -ENODEV;
50267d935b4SBhanuprakash Modem 
50367d935b4SBhanuprakash Modem 	seq_printf(m, "Maximum: %u\n", connector->display_info.bpc);
50467d935b4SBhanuprakash Modem 
50567d935b4SBhanuprakash Modem 	return 0;
50667d935b4SBhanuprakash Modem }
50767d935b4SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(output_bpc);
50867d935b4SBhanuprakash Modem 
5094cf2b281SThomas Wood static const struct file_operations drm_edid_fops = {
5104cf2b281SThomas Wood 	.owner = THIS_MODULE,
5114cf2b281SThomas Wood 	.open = edid_open,
5124cf2b281SThomas Wood 	.read = seq_read,
5134cf2b281SThomas Wood 	.llseek = seq_lseek,
5144cf2b281SThomas Wood 	.release = single_release,
5154cf2b281SThomas Wood 	.write = edid_write
5164cf2b281SThomas Wood };
5174cf2b281SThomas Wood 
5184cf2b281SThomas Wood 
51930f65707SThomas Wood static const struct file_operations drm_connector_fops = {
52030f65707SThomas Wood 	.owner = THIS_MODULE,
52130f65707SThomas Wood 	.open = connector_open,
52230f65707SThomas Wood 	.read = seq_read,
52330f65707SThomas Wood 	.llseek = seq_lseek,
52430f65707SThomas Wood 	.release = single_release,
52530f65707SThomas Wood 	.write = connector_write
52630f65707SThomas Wood };
52730f65707SThomas Wood 
528c602e495SMaxime Ripard static ssize_t
audio_infoframe_read(struct file * filp,char __user * ubuf,size_t count,loff_t * ppos)529c602e495SMaxime Ripard audio_infoframe_read(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos)
530c602e495SMaxime Ripard {
531c602e495SMaxime Ripard 	struct drm_connector_hdmi_infoframe *infoframe;
532c602e495SMaxime Ripard 	struct drm_connector *connector;
533c602e495SMaxime Ripard 	union hdmi_infoframe *frame;
534c602e495SMaxime Ripard 	u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
535c602e495SMaxime Ripard 	ssize_t len = 0;
536c602e495SMaxime Ripard 
537c602e495SMaxime Ripard 	connector = filp->private_data;
538c602e495SMaxime Ripard 	mutex_lock(&connector->hdmi.infoframes.lock);
539c602e495SMaxime Ripard 
540c602e495SMaxime Ripard 	infoframe = &connector->hdmi.infoframes.audio;
541c602e495SMaxime Ripard 	if (!infoframe->set)
542c602e495SMaxime Ripard 		goto out;
543c602e495SMaxime Ripard 
544c602e495SMaxime Ripard 	frame = &infoframe->data;
545c602e495SMaxime Ripard 	len = hdmi_infoframe_pack(frame, buf, sizeof(buf));
546c602e495SMaxime Ripard 	if (len < 0)
547c602e495SMaxime Ripard 		goto out;
548c602e495SMaxime Ripard 
549c602e495SMaxime Ripard 	len = simple_read_from_buffer(ubuf, count, ppos, buf, len);
550c602e495SMaxime Ripard 
551c602e495SMaxime Ripard out:
552c602e495SMaxime Ripard 	mutex_unlock(&connector->hdmi.infoframes.lock);
553c602e495SMaxime Ripard 	return len;
554c602e495SMaxime Ripard }
555c602e495SMaxime Ripard 
556c602e495SMaxime Ripard static const struct file_operations audio_infoframe_fops = {
557c602e495SMaxime Ripard 	.owner   = THIS_MODULE,
558c602e495SMaxime Ripard 	.open    = simple_open,
559c602e495SMaxime Ripard 	.read    = audio_infoframe_read,
560c602e495SMaxime Ripard };
561c602e495SMaxime Ripard 
create_hdmi_audio_infoframe_file(struct drm_connector * connector,struct dentry * parent)562c602e495SMaxime Ripard static int create_hdmi_audio_infoframe_file(struct drm_connector *connector,
563c602e495SMaxime Ripard 					    struct dentry *parent)
564c602e495SMaxime Ripard {
565c602e495SMaxime Ripard 	struct dentry *file;
566c602e495SMaxime Ripard 
567c602e495SMaxime Ripard 	file = debugfs_create_file("audio", 0400, parent, connector, &audio_infoframe_fops);
568c602e495SMaxime Ripard 	if (IS_ERR(file))
569c602e495SMaxime Ripard 		return PTR_ERR(file);
570c602e495SMaxime Ripard 
571c602e495SMaxime Ripard 	return 0;
572c602e495SMaxime Ripard }
573c602e495SMaxime Ripard 
574c602e495SMaxime Ripard #define DEFINE_INFOFRAME_FILE(_f) \
575c602e495SMaxime Ripard static ssize_t _f##_read_infoframe(struct file *filp, \
576c602e495SMaxime Ripard 				   char __user *ubuf, \
577c602e495SMaxime Ripard 				   size_t count,      \
578c602e495SMaxime Ripard 				   loff_t *ppos)      \
579c602e495SMaxime Ripard { \
580c602e495SMaxime Ripard 	struct drm_connector_hdmi_infoframe *infoframe; \
581c602e495SMaxime Ripard 	struct drm_connector_state *conn_state; \
582c602e495SMaxime Ripard 	struct drm_connector *connector; \
583c602e495SMaxime Ripard 	union hdmi_infoframe *frame; \
584c602e495SMaxime Ripard 	struct drm_device *dev; \
585f0fa69b5SDerek Foreman 	u8 buf[HDMI_INFOFRAME_SIZE(MAX)]; \
586c602e495SMaxime Ripard 	ssize_t len = 0; \
587c602e495SMaxime Ripard 	\
588c602e495SMaxime Ripard 	connector = filp->private_data; \
589c602e495SMaxime Ripard 	dev = connector->dev; \
590c602e495SMaxime Ripard 	\
591c602e495SMaxime Ripard 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); \
592c602e495SMaxime Ripard 	\
593c602e495SMaxime Ripard 	conn_state = connector->state; \
594c602e495SMaxime Ripard 	infoframe = &conn_state->hdmi.infoframes._f; \
595c602e495SMaxime Ripard 	if (!infoframe->set) \
596c602e495SMaxime Ripard 		goto out; \
597c602e495SMaxime Ripard 	\
598c602e495SMaxime Ripard 	frame = &infoframe->data; \
599c602e495SMaxime Ripard 	len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); \
600c602e495SMaxime Ripard 	if (len < 0) \
601c602e495SMaxime Ripard 		goto out; \
602c602e495SMaxime Ripard 	\
603c602e495SMaxime Ripard 	len = simple_read_from_buffer(ubuf, count, ppos, buf, len); \
604c602e495SMaxime Ripard 	\
605c602e495SMaxime Ripard out: \
606c602e495SMaxime Ripard 	drm_modeset_unlock(&dev->mode_config.connection_mutex); \
607c602e495SMaxime Ripard 	return len; \
608c602e495SMaxime Ripard } \
609c602e495SMaxime Ripard \
610c602e495SMaxime Ripard static const struct file_operations _f##_infoframe_fops = { \
611c602e495SMaxime Ripard 	.owner = THIS_MODULE, \
612c602e495SMaxime Ripard 	.open = simple_open, \
613c602e495SMaxime Ripard 	.read = _f##_read_infoframe, \
614c602e495SMaxime Ripard }; \
615c602e495SMaxime Ripard \
616c602e495SMaxime Ripard static int create_hdmi_## _f ## _infoframe_file(struct drm_connector *connector, \
617c602e495SMaxime Ripard 						struct dentry *parent) \
618c602e495SMaxime Ripard { \
619c602e495SMaxime Ripard 	struct dentry *file; \
620c602e495SMaxime Ripard 	\
621c602e495SMaxime Ripard 	file = debugfs_create_file(#_f, 0400, parent, connector, &_f ## _infoframe_fops); \
622c602e495SMaxime Ripard 	if (IS_ERR(file)) \
623c602e495SMaxime Ripard 		return PTR_ERR(file); \
624c602e495SMaxime Ripard 	\
625c602e495SMaxime Ripard 	return 0; \
626c602e495SMaxime Ripard }
627c602e495SMaxime Ripard 
628c602e495SMaxime Ripard DEFINE_INFOFRAME_FILE(avi);
629c602e495SMaxime Ripard DEFINE_INFOFRAME_FILE(hdmi);
630c602e495SMaxime Ripard DEFINE_INFOFRAME_FILE(hdr_drm);
631c602e495SMaxime Ripard DEFINE_INFOFRAME_FILE(spd);
632c602e495SMaxime Ripard 
create_hdmi_infoframe_files(struct drm_connector * connector,struct dentry * parent)633c602e495SMaxime Ripard static int create_hdmi_infoframe_files(struct drm_connector *connector,
634c602e495SMaxime Ripard 				       struct dentry *parent)
635c602e495SMaxime Ripard {
636c602e495SMaxime Ripard 	int ret;
637c602e495SMaxime Ripard 
638c602e495SMaxime Ripard 	ret = create_hdmi_audio_infoframe_file(connector, parent);
639c602e495SMaxime Ripard 	if (ret)
640c602e495SMaxime Ripard 		return ret;
641c602e495SMaxime Ripard 
642c602e495SMaxime Ripard 	ret = create_hdmi_avi_infoframe_file(connector, parent);
643c602e495SMaxime Ripard 	if (ret)
644c602e495SMaxime Ripard 		return ret;
645c602e495SMaxime Ripard 
646c602e495SMaxime Ripard 	ret = create_hdmi_hdmi_infoframe_file(connector, parent);
647c602e495SMaxime Ripard 	if (ret)
648c602e495SMaxime Ripard 		return ret;
649c602e495SMaxime Ripard 
650c602e495SMaxime Ripard 	ret = create_hdmi_hdr_drm_infoframe_file(connector, parent);
651c602e495SMaxime Ripard 	if (ret)
652c602e495SMaxime Ripard 		return ret;
653c602e495SMaxime Ripard 
654c602e495SMaxime Ripard 	ret = create_hdmi_spd_infoframe_file(connector, parent);
655c602e495SMaxime Ripard 	if (ret)
656c602e495SMaxime Ripard 		return ret;
657c602e495SMaxime Ripard 
658c602e495SMaxime Ripard 	return 0;
659c602e495SMaxime Ripard }
660c602e495SMaxime Ripard 
hdmi_debugfs_add(struct drm_connector * connector)661c602e495SMaxime Ripard static void hdmi_debugfs_add(struct drm_connector *connector)
662c602e495SMaxime Ripard {
663c602e495SMaxime Ripard 	struct dentry *dir;
664c602e495SMaxime Ripard 
665c602e495SMaxime Ripard 	if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
666c602e495SMaxime Ripard 	      connector->connector_type == DRM_MODE_CONNECTOR_HDMIB))
667c602e495SMaxime Ripard 		return;
668c602e495SMaxime Ripard 
669c602e495SMaxime Ripard 	dir = debugfs_create_dir("infoframes", connector->debugfs_entry);
670c602e495SMaxime Ripard 	if (IS_ERR(dir))
671c602e495SMaxime Ripard 		return;
672c602e495SMaxime Ripard 
673c602e495SMaxime Ripard 	create_hdmi_infoframe_files(connector, dir);
674c602e495SMaxime Ripard }
675c602e495SMaxime Ripard 
drm_debugfs_connector_add(struct drm_connector * connector)676b792e640SGreg Kroah-Hartman void drm_debugfs_connector_add(struct drm_connector *connector)
67730f65707SThomas Wood {
6780b30d57aSChristian König 	struct drm_device *dev = connector->dev;
679b792e640SGreg Kroah-Hartman 	struct dentry *root;
68030f65707SThomas Wood 
6810b30d57aSChristian König 	if (!dev->debugfs_root)
682b792e640SGreg Kroah-Hartman 		return;
68330f65707SThomas Wood 
6840b30d57aSChristian König 	root = debugfs_create_dir(connector->name, dev->debugfs_root);
68530f65707SThomas Wood 	connector->debugfs_entry = root;
68630f65707SThomas Wood 
68730f65707SThomas Wood 	/* force */
688c9ba134eSMaíra Canal 	debugfs_create_file("force", 0644, root, connector,
68930f65707SThomas Wood 			    &drm_connector_fops);
69030f65707SThomas Wood 
6914cf2b281SThomas Wood 	/* edid */
692c9ba134eSMaíra Canal 	debugfs_create_file("edid_override", 0644, root, connector,
693b792e640SGreg Kroah-Hartman 			    &drm_edid_fops);
69441752663SBhanuprakash Modem 
69541752663SBhanuprakash Modem 	/* vrr range */
696c9ba134eSMaíra Canal 	debugfs_create_file("vrr_range", 0444, root, connector,
69741752663SBhanuprakash Modem 			    &vrr_range_fops);
6982509969aSDouglas Anderson 
69967d935b4SBhanuprakash Modem 	/* max bpc */
70067d935b4SBhanuprakash Modem 	debugfs_create_file("output_bpc", 0444, root, connector,
70167d935b4SBhanuprakash Modem 			    &output_bpc_fops);
70267d935b4SBhanuprakash Modem 
703c602e495SMaxime Ripard 	hdmi_debugfs_add(connector);
704c602e495SMaxime Ripard 
7052509969aSDouglas Anderson 	if (connector->funcs->debugfs_init)
7062509969aSDouglas Anderson 		connector->funcs->debugfs_init(connector, root);
70730f65707SThomas Wood }
70830f65707SThomas Wood 
drm_debugfs_connector_remove(struct drm_connector * connector)70930f65707SThomas Wood void drm_debugfs_connector_remove(struct drm_connector *connector)
71030f65707SThomas Wood {
71130f65707SThomas Wood 	if (!connector->debugfs_entry)
71230f65707SThomas Wood 		return;
71330f65707SThomas Wood 
71430f65707SThomas Wood 	debugfs_remove_recursive(connector->debugfs_entry);
71530f65707SThomas Wood 
71630f65707SThomas Wood 	connector->debugfs_entry = NULL;
71730f65707SThomas Wood }
71830f65707SThomas Wood 
drm_debugfs_crtc_add(struct drm_crtc * crtc)719b792e640SGreg Kroah-Hartman void drm_debugfs_crtc_add(struct drm_crtc *crtc)
7209edbf1faSTomeu Vizoso {
7210b30d57aSChristian König 	struct drm_device *dev = crtc->dev;
7229edbf1faSTomeu Vizoso 	struct dentry *root;
7239edbf1faSTomeu Vizoso 	char *name;
72428a62277SBen Gamari 
7259edbf1faSTomeu Vizoso 	name = kasprintf(GFP_KERNEL, "crtc-%d", crtc->index);
7269edbf1faSTomeu Vizoso 	if (!name)
727b792e640SGreg Kroah-Hartman 		return;
7289edbf1faSTomeu Vizoso 
7290b30d57aSChristian König 	root = debugfs_create_dir(name, dev->debugfs_root);
7309edbf1faSTomeu Vizoso 	kfree(name);
7319edbf1faSTomeu Vizoso 
7329edbf1faSTomeu Vizoso 	crtc->debugfs_entry = root;
7339edbf1faSTomeu Vizoso 
734b792e640SGreg Kroah-Hartman 	drm_debugfs_crtc_crc_add(crtc);
7359edbf1faSTomeu Vizoso }
7369edbf1faSTomeu Vizoso 
drm_debugfs_crtc_remove(struct drm_crtc * crtc)7379edbf1faSTomeu Vizoso void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
7389edbf1faSTomeu Vizoso {
7399edbf1faSTomeu Vizoso 	debugfs_remove_recursive(crtc->debugfs_entry);
7409edbf1faSTomeu Vizoso 	crtc->debugfs_entry = NULL;
7419edbf1faSTomeu Vizoso }
7429edbf1faSTomeu Vizoso 
bridges_show(struct seq_file * m,void * data)743d0b3c318SDmitry Baryshkov static int bridges_show(struct seq_file *m, void *data)
744d0b3c318SDmitry Baryshkov {
745d0b3c318SDmitry Baryshkov 	struct drm_encoder *encoder = m->private;
746d0b3c318SDmitry Baryshkov 	struct drm_printer p = drm_seq_file_printer(m);
747d0b3c318SDmitry Baryshkov 	struct drm_bridge *bridge;
748d0b3c318SDmitry Baryshkov 	unsigned int idx = 0;
749d0b3c318SDmitry Baryshkov 
750d0b3c318SDmitry Baryshkov 	drm_for_each_bridge_in_chain(encoder, bridge) {
751*72443c73SLuca Ceresoli 		drm_printf(&p, "bridge[%u]: %ps\n", idx++, bridge->funcs);
752d0b3c318SDmitry Baryshkov 		drm_printf(&p, "\ttype: [%d] %s\n",
753d0b3c318SDmitry Baryshkov 			   bridge->type,
754d0b3c318SDmitry Baryshkov 			   drm_get_connector_type_name(bridge->type));
755235e6065SSui Jingfeng 
756d0b3c318SDmitry Baryshkov 		if (bridge->of_node)
757d0b3c318SDmitry Baryshkov 			drm_printf(&p, "\tOF: %pOFfc\n", bridge->of_node);
758235e6065SSui Jingfeng 
759d0b3c318SDmitry Baryshkov 		drm_printf(&p, "\tops: [0x%x]", bridge->ops);
760d0b3c318SDmitry Baryshkov 		if (bridge->ops & DRM_BRIDGE_OP_DETECT)
761d0b3c318SDmitry Baryshkov 			drm_puts(&p, " detect");
762d0b3c318SDmitry Baryshkov 		if (bridge->ops & DRM_BRIDGE_OP_EDID)
763d0b3c318SDmitry Baryshkov 			drm_puts(&p, " edid");
764d0b3c318SDmitry Baryshkov 		if (bridge->ops & DRM_BRIDGE_OP_HPD)
765d0b3c318SDmitry Baryshkov 			drm_puts(&p, " hpd");
766d0b3c318SDmitry Baryshkov 		if (bridge->ops & DRM_BRIDGE_OP_MODES)
767d0b3c318SDmitry Baryshkov 			drm_puts(&p, " modes");
7686b4468b0SDmitry Baryshkov 		if (bridge->ops & DRM_BRIDGE_OP_HDMI)
7696b4468b0SDmitry Baryshkov 			drm_puts(&p, " hdmi");
770d0b3c318SDmitry Baryshkov 		drm_puts(&p, "\n");
771d0b3c318SDmitry Baryshkov 	}
772d0b3c318SDmitry Baryshkov 
773d0b3c318SDmitry Baryshkov 	return 0;
774d0b3c318SDmitry Baryshkov }
775d0b3c318SDmitry Baryshkov DEFINE_SHOW_ATTRIBUTE(bridges);
776d0b3c318SDmitry Baryshkov 
drm_debugfs_encoder_add(struct drm_encoder * encoder)777caf525edSDmitry Baryshkov void drm_debugfs_encoder_add(struct drm_encoder *encoder)
778caf525edSDmitry Baryshkov {
779caf525edSDmitry Baryshkov 	struct drm_minor *minor = encoder->dev->primary;
780caf525edSDmitry Baryshkov 	struct dentry *root;
781caf525edSDmitry Baryshkov 	char *name;
782caf525edSDmitry Baryshkov 
783caf525edSDmitry Baryshkov 	name = kasprintf(GFP_KERNEL, "encoder-%d", encoder->index);
784caf525edSDmitry Baryshkov 	if (!name)
785caf525edSDmitry Baryshkov 		return;
786caf525edSDmitry Baryshkov 
787caf525edSDmitry Baryshkov 	root = debugfs_create_dir(name, minor->debugfs_root);
788caf525edSDmitry Baryshkov 	kfree(name);
789caf525edSDmitry Baryshkov 
790caf525edSDmitry Baryshkov 	encoder->debugfs_entry = root;
791caf525edSDmitry Baryshkov 
792d0b3c318SDmitry Baryshkov 	/* bridges list */
793d0b3c318SDmitry Baryshkov 	debugfs_create_file("bridges", 0444, root, encoder,
794d0b3c318SDmitry Baryshkov 			    &bridges_fops);
795d0b3c318SDmitry Baryshkov 
79676385d49SMarek Szyprowski 	if (encoder->funcs && encoder->funcs->debugfs_init)
797caf525edSDmitry Baryshkov 		encoder->funcs->debugfs_init(encoder, root);
798caf525edSDmitry Baryshkov }
799caf525edSDmitry Baryshkov 
drm_debugfs_encoder_remove(struct drm_encoder * encoder)800caf525edSDmitry Baryshkov void drm_debugfs_encoder_remove(struct drm_encoder *encoder)
801caf525edSDmitry Baryshkov {
802caf525edSDmitry Baryshkov 	debugfs_remove_recursive(encoder->debugfs_entry);
803caf525edSDmitry Baryshkov 	encoder->debugfs_entry = NULL;
804caf525edSDmitry Baryshkov }
805