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