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