18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2008 Ben Gamari <bgamari@gmail.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation 98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next 148c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 158c2ecf20Sopenharmony_ci * Software. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 188c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 198c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 208c2ecf20Sopenharmony_ci * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 218c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 228c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 238c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 278c2ecf20Sopenharmony_ci#include <linux/export.h> 288c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_auth.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_client.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_debugfs.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 378c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 388c2ecf20Sopenharmony_ci#include <drm/drm_edid.h> 398c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 408c2ecf20Sopenharmony_ci#include <drm/drm_gem.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include "drm_crtc_internal.h" 438c2ecf20Sopenharmony_ci#include "drm_internal.h" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/*************************************************** 488c2ecf20Sopenharmony_ci * Initialization, etc. 498c2ecf20Sopenharmony_ci **************************************************/ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic int drm_name_info(struct seq_file *m, void *data) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *) m->private; 548c2ecf20Sopenharmony_ci struct drm_minor *minor = node->minor; 558c2ecf20Sopenharmony_ci struct drm_device *dev = minor->dev; 568c2ecf20Sopenharmony_ci struct drm_master *master; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci mutex_lock(&dev->master_mutex); 598c2ecf20Sopenharmony_ci master = dev->master; 608c2ecf20Sopenharmony_ci seq_printf(m, "%s", dev->driver->name); 618c2ecf20Sopenharmony_ci if (dev->dev) 628c2ecf20Sopenharmony_ci seq_printf(m, " dev=%s", dev_name(dev->dev)); 638c2ecf20Sopenharmony_ci if (master && master->unique) 648c2ecf20Sopenharmony_ci seq_printf(m, " master=%s", master->unique); 658c2ecf20Sopenharmony_ci if (dev->unique) 668c2ecf20Sopenharmony_ci seq_printf(m, " unique=%s", dev->unique); 678c2ecf20Sopenharmony_ci seq_printf(m, "\n"); 688c2ecf20Sopenharmony_ci mutex_unlock(&dev->master_mutex); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int drm_clients_info(struct seq_file *m, void *data) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *) m->private; 768c2ecf20Sopenharmony_ci struct drm_device *dev = node->minor->dev; 778c2ecf20Sopenharmony_ci struct drm_file *priv; 788c2ecf20Sopenharmony_ci kuid_t uid; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci seq_printf(m, 818c2ecf20Sopenharmony_ci "%20s %5s %3s master a %5s %10s\n", 828c2ecf20Sopenharmony_ci "command", 838c2ecf20Sopenharmony_ci "pid", 848c2ecf20Sopenharmony_ci "dev", 858c2ecf20Sopenharmony_ci "uid", 868c2ecf20Sopenharmony_ci "magic"); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* dev->filelist is sorted youngest first, but we want to present 898c2ecf20Sopenharmony_ci * oldest first (i.e. kernel, servers, clients), so walk backwardss. 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ci mutex_lock(&dev->filelist_mutex); 928c2ecf20Sopenharmony_ci list_for_each_entry_reverse(priv, &dev->filelist, lhead) { 938c2ecf20Sopenharmony_ci struct task_struct *task; 948c2ecf20Sopenharmony_ci bool is_current_master = drm_is_current_master(priv); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci rcu_read_lock(); /* locks pid_task()->comm */ 978c2ecf20Sopenharmony_ci task = pid_task(priv->pid, PIDTYPE_PID); 988c2ecf20Sopenharmony_ci uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; 998c2ecf20Sopenharmony_ci seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", 1008c2ecf20Sopenharmony_ci task ? task->comm : "<unknown>", 1018c2ecf20Sopenharmony_ci pid_vnr(priv->pid), 1028c2ecf20Sopenharmony_ci priv->minor->index, 1038c2ecf20Sopenharmony_ci is_current_master ? 'y' : 'n', 1048c2ecf20Sopenharmony_ci priv->authenticated ? 'y' : 'n', 1058c2ecf20Sopenharmony_ci from_kuid_munged(seq_user_ns(m), uid), 1068c2ecf20Sopenharmony_ci priv->magic); 1078c2ecf20Sopenharmony_ci rcu_read_unlock(); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci mutex_unlock(&dev->filelist_mutex); 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int drm_gem_one_name_info(int id, void *ptr, void *data) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct drm_gem_object *obj = ptr; 1168c2ecf20Sopenharmony_ci struct seq_file *m = data; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci seq_printf(m, "%6d %8zd %7d %8d\n", 1198c2ecf20Sopenharmony_ci obj->name, obj->size, 1208c2ecf20Sopenharmony_ci obj->handle_count, 1218c2ecf20Sopenharmony_ci kref_read(&obj->refcount)); 1228c2ecf20Sopenharmony_ci return 0; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int drm_gem_name_info(struct seq_file *m, void *data) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *) m->private; 1288c2ecf20Sopenharmony_ci struct drm_device *dev = node->minor->dev; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci seq_printf(m, " name size handles refcount\n"); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci mutex_lock(&dev->object_name_lock); 1338c2ecf20Sopenharmony_ci idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m); 1348c2ecf20Sopenharmony_ci mutex_unlock(&dev->object_name_lock); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic const struct drm_info_list drm_debugfs_list[] = { 1408c2ecf20Sopenharmony_ci {"name", drm_name_info, 0}, 1418c2ecf20Sopenharmony_ci {"clients", drm_clients_info, 0}, 1428c2ecf20Sopenharmony_ci {"gem_names", drm_gem_name_info, DRIVER_GEM}, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list) 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int drm_debugfs_open(struct inode *inode, struct file *file) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct drm_info_node *node = inode->i_private; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return single_open(file, node->info_ent->show, node); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic const struct file_operations drm_debugfs_fops = { 1568c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1578c2ecf20Sopenharmony_ci .open = drm_debugfs_open, 1588c2ecf20Sopenharmony_ci .read = seq_read, 1598c2ecf20Sopenharmony_ci .llseek = seq_lseek, 1608c2ecf20Sopenharmony_ci .release = single_release, 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/** 1658c2ecf20Sopenharmony_ci * drm_debugfs_create_files - Initialize a given set of debugfs files for DRM 1668c2ecf20Sopenharmony_ci * minor 1678c2ecf20Sopenharmony_ci * @files: The array of files to create 1688c2ecf20Sopenharmony_ci * @count: The number of files given 1698c2ecf20Sopenharmony_ci * @root: DRI debugfs dir entry. 1708c2ecf20Sopenharmony_ci * @minor: device minor number 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * Create a given set of debugfs files represented by an array of 1738c2ecf20Sopenharmony_ci * &struct drm_info_list in the given root directory. These files will be removed 1748c2ecf20Sopenharmony_ci * automatically on drm_debugfs_cleanup(). 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_civoid drm_debugfs_create_files(const struct drm_info_list *files, int count, 1778c2ecf20Sopenharmony_ci struct dentry *root, struct drm_minor *minor) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct drm_device *dev = minor->dev; 1808c2ecf20Sopenharmony_ci struct drm_info_node *tmp; 1818c2ecf20Sopenharmony_ci int i; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 1848c2ecf20Sopenharmony_ci u32 features = files[i].driver_features; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (features && !drm_core_check_all_features(dev, features)) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); 1908c2ecf20Sopenharmony_ci if (tmp == NULL) 1918c2ecf20Sopenharmony_ci continue; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci tmp->minor = minor; 1948c2ecf20Sopenharmony_ci tmp->dent = debugfs_create_file(files[i].name, 1958c2ecf20Sopenharmony_ci S_IFREG | S_IRUGO, root, tmp, 1968c2ecf20Sopenharmony_ci &drm_debugfs_fops); 1978c2ecf20Sopenharmony_ci tmp->info_ent = &files[i]; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci mutex_lock(&minor->debugfs_lock); 2008c2ecf20Sopenharmony_ci list_add(&tmp->list, &minor->debugfs_list); 2018c2ecf20Sopenharmony_ci mutex_unlock(&minor->debugfs_lock); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_debugfs_create_files); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciint drm_debugfs_init(struct drm_minor *minor, int minor_id, 2078c2ecf20Sopenharmony_ci struct dentry *root) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct drm_device *dev = minor->dev; 2108c2ecf20Sopenharmony_ci char name[64]; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&minor->debugfs_list); 2138c2ecf20Sopenharmony_ci mutex_init(&minor->debugfs_lock); 2148c2ecf20Sopenharmony_ci sprintf(name, "%d", minor_id); 2158c2ecf20Sopenharmony_ci minor->debugfs_root = debugfs_create_dir(name, root); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, 2188c2ecf20Sopenharmony_ci minor->debugfs_root, minor); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) { 2218c2ecf20Sopenharmony_ci drm_atomic_debugfs_init(minor); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (drm_core_check_feature(dev, DRIVER_MODESET)) { 2258c2ecf20Sopenharmony_ci drm_framebuffer_debugfs_init(minor); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci drm_client_debugfs_init(minor); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (dev->driver->debugfs_init) 2318c2ecf20Sopenharmony_ci dev->driver->debugfs_init(minor); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciint drm_debugfs_remove_files(const struct drm_info_list *files, int count, 2388c2ecf20Sopenharmony_ci struct drm_minor *minor) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct list_head *pos, *q; 2418c2ecf20Sopenharmony_ci struct drm_info_node *tmp; 2428c2ecf20Sopenharmony_ci int i; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci mutex_lock(&minor->debugfs_lock); 2458c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 2468c2ecf20Sopenharmony_ci list_for_each_safe(pos, q, &minor->debugfs_list) { 2478c2ecf20Sopenharmony_ci tmp = list_entry(pos, struct drm_info_node, list); 2488c2ecf20Sopenharmony_ci if (tmp->info_ent == &files[i]) { 2498c2ecf20Sopenharmony_ci debugfs_remove(tmp->dent); 2508c2ecf20Sopenharmony_ci list_del(pos); 2518c2ecf20Sopenharmony_ci kfree(tmp); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci mutex_unlock(&minor->debugfs_lock); 2568c2ecf20Sopenharmony_ci return 0; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_debugfs_remove_files); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void drm_debugfs_remove_all_files(struct drm_minor *minor) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct drm_info_node *node, *tmp; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_lock(&minor->debugfs_lock); 2658c2ecf20Sopenharmony_ci list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) { 2668c2ecf20Sopenharmony_ci debugfs_remove(node->dent); 2678c2ecf20Sopenharmony_ci list_del(&node->list); 2688c2ecf20Sopenharmony_ci kfree(node); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci mutex_unlock(&minor->debugfs_lock); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_civoid drm_debugfs_cleanup(struct drm_minor *minor) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci if (!minor->debugfs_root) 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci drm_debugfs_remove_all_files(minor); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci debugfs_remove_recursive(minor->debugfs_root); 2818c2ecf20Sopenharmony_ci minor->debugfs_root = NULL; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic int connector_show(struct seq_file *m, void *data) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct drm_connector *connector = m->private; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci seq_printf(m, "%s\n", drm_get_connector_force_name(connector->force)); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int connector_open(struct inode *inode, struct file *file) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct drm_connector *dev = inode->i_private; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci return single_open(file, connector_show, dev); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic ssize_t connector_write(struct file *file, const char __user *ubuf, 3018c2ecf20Sopenharmony_ci size_t len, loff_t *offp) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci struct seq_file *m = file->private_data; 3048c2ecf20Sopenharmony_ci struct drm_connector *connector = m->private; 3058c2ecf20Sopenharmony_ci char buf[12]; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (len > sizeof(buf) - 1) 3088c2ecf20Sopenharmony_ci return -EINVAL; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (copy_from_user(buf, ubuf, len)) 3118c2ecf20Sopenharmony_ci return -EFAULT; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci buf[len] = '\0'; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (sysfs_streq(buf, "on")) 3168c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_ON; 3178c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "digital")) 3188c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_ON_DIGITAL; 3198c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "off")) 3208c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_OFF; 3218c2ecf20Sopenharmony_ci else if (sysfs_streq(buf, "unspecified")) 3228c2ecf20Sopenharmony_ci connector->force = DRM_FORCE_UNSPECIFIED; 3238c2ecf20Sopenharmony_ci else 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return len; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int edid_show(struct seq_file *m, void *data) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct drm_connector *connector = m->private; 3328c2ecf20Sopenharmony_ci struct drm_property_blob *edid = connector->edid_blob_ptr; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (connector->override_edid && edid) 3358c2ecf20Sopenharmony_ci seq_write(m, edid->data, edid->length); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int edid_open(struct inode *inode, struct file *file) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct drm_connector *dev = inode->i_private; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return single_open(file, edid_show, dev); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic ssize_t edid_write(struct file *file, const char __user *ubuf, 3488c2ecf20Sopenharmony_ci size_t len, loff_t *offp) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct seq_file *m = file->private_data; 3518c2ecf20Sopenharmony_ci struct drm_connector *connector = m->private; 3528c2ecf20Sopenharmony_ci char *buf; 3538c2ecf20Sopenharmony_ci struct edid *edid; 3548c2ecf20Sopenharmony_ci int ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci buf = memdup_user(ubuf, len); 3578c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 3588c2ecf20Sopenharmony_ci return PTR_ERR(buf); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci edid = (struct edid *) buf; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (len == 5 && !strncmp(buf, "reset", 5)) { 3638c2ecf20Sopenharmony_ci connector->override_edid = false; 3648c2ecf20Sopenharmony_ci ret = drm_connector_update_edid_property(connector, NULL); 3658c2ecf20Sopenharmony_ci } else if (len < EDID_LENGTH || 3668c2ecf20Sopenharmony_ci EDID_LENGTH * (1 + edid->extensions) > len) 3678c2ecf20Sopenharmony_ci ret = -EINVAL; 3688c2ecf20Sopenharmony_ci else { 3698c2ecf20Sopenharmony_ci connector->override_edid = false; 3708c2ecf20Sopenharmony_ci ret = drm_connector_update_edid_property(connector, edid); 3718c2ecf20Sopenharmony_ci if (!ret) 3728c2ecf20Sopenharmony_ci connector->override_edid = true; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci kfree(buf); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return (ret) ? ret : len; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* 3818c2ecf20Sopenharmony_ci * Returns the min and max vrr vfreq through the connector's debugfs file. 3828c2ecf20Sopenharmony_ci * Example usage: cat /sys/kernel/debug/dri/0/DP-1/vrr_range 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_cistatic int vrr_range_show(struct seq_file *m, void *data) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct drm_connector *connector = m->private; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (connector->status != connector_status_connected) 3898c2ecf20Sopenharmony_ci return -ENODEV; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci seq_printf(m, "Min: %u\n", (u8)connector->display_info.monitor_range.min_vfreq); 3928c2ecf20Sopenharmony_ci seq_printf(m, "Max: %u\n", (u8)connector->display_info.monitor_range.max_vfreq); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(vrr_range); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic const struct file_operations drm_edid_fops = { 3998c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4008c2ecf20Sopenharmony_ci .open = edid_open, 4018c2ecf20Sopenharmony_ci .read = seq_read, 4028c2ecf20Sopenharmony_ci .llseek = seq_lseek, 4038c2ecf20Sopenharmony_ci .release = single_release, 4048c2ecf20Sopenharmony_ci .write = edid_write 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic const struct file_operations drm_connector_fops = { 4098c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4108c2ecf20Sopenharmony_ci .open = connector_open, 4118c2ecf20Sopenharmony_ci .read = seq_read, 4128c2ecf20Sopenharmony_ci .llseek = seq_lseek, 4138c2ecf20Sopenharmony_ci .release = single_release, 4148c2ecf20Sopenharmony_ci .write = connector_write 4158c2ecf20Sopenharmony_ci}; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_civoid drm_debugfs_connector_add(struct drm_connector *connector) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct drm_minor *minor = connector->dev->primary; 4208c2ecf20Sopenharmony_ci struct dentry *root; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (!minor->debugfs_root) 4238c2ecf20Sopenharmony_ci return; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci root = debugfs_create_dir(connector->name, minor->debugfs_root); 4268c2ecf20Sopenharmony_ci connector->debugfs_entry = root; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* force */ 4298c2ecf20Sopenharmony_ci debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector, 4308c2ecf20Sopenharmony_ci &drm_connector_fops); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* edid */ 4338c2ecf20Sopenharmony_ci debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, connector, 4348c2ecf20Sopenharmony_ci &drm_edid_fops); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* vrr range */ 4378c2ecf20Sopenharmony_ci debugfs_create_file("vrr_range", S_IRUGO, root, connector, 4388c2ecf20Sopenharmony_ci &vrr_range_fops); 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_civoid drm_debugfs_connector_remove(struct drm_connector *connector) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci if (!connector->debugfs_entry) 4448c2ecf20Sopenharmony_ci return; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci debugfs_remove_recursive(connector->debugfs_entry); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci connector->debugfs_entry = NULL; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_civoid drm_debugfs_crtc_add(struct drm_crtc *crtc) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct drm_minor *minor = crtc->dev->primary; 4548c2ecf20Sopenharmony_ci struct dentry *root; 4558c2ecf20Sopenharmony_ci char *name; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "crtc-%d", crtc->index); 4588c2ecf20Sopenharmony_ci if (!name) 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci root = debugfs_create_dir(name, minor->debugfs_root); 4628c2ecf20Sopenharmony_ci kfree(name); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci crtc->debugfs_entry = root; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci drm_debugfs_crtc_crc_add(crtc); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_civoid drm_debugfs_crtc_remove(struct drm_crtc *crtc) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci debugfs_remove_recursive(crtc->debugfs_entry); 4728c2ecf20Sopenharmony_ci crtc->debugfs_entry = NULL; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 476