1 /* $NetBSD: drm_debugfs.c,v 1.4 2018/08/27 04:58:19 riastradh Exp $ */ 2 3 /** 4 * \file drm_debugfs.c 5 * debugfs support for DRM 6 * 7 * \author Ben Gamari <bgamari@gmail.com> 8 */ 9 10 /* 11 * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com 12 * 13 * Copyright 2008 Ben Gamari <bgamari@gmail.com> 14 * 15 * Permission is hereby granted, free of charge, to any person obtaining a 16 * copy of this software and associated documentation files (the "Software"), 17 * to deal in the Software without restriction, including without limitation 18 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 * and/or sell copies of the Software, and to permit persons to whom the 20 * Software is furnished to do so, subject to the following conditions: 21 * 22 * The above copyright notice and this permission notice (including the next 23 * paragraph) shall be included in all copies or substantial portions of the 24 * Software. 25 * 26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 29 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 30 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 31 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 * OTHER DEALINGS IN THE SOFTWARE. 33 */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: drm_debugfs.c,v 1.4 2018/08/27 04:58:19 riastradh Exp $"); 37 38 #include <linux/debugfs.h> 39 #include <linux/seq_file.h> 40 #include <linux/slab.h> 41 #include <linux/export.h> 42 #include <drm/drmP.h> 43 #include <drm/drm_edid.h> 44 #include "drm_internal.h" 45 46 #if defined(CONFIG_DEBUG_FS) 47 48 /*************************************************** 49 * Initialization, etc. 50 **************************************************/ 51 52 static const struct drm_info_list drm_debugfs_list[] = { 53 {"name", drm_name_info, 0}, 54 {"vm", drm_vm_info, 0}, 55 {"clients", drm_clients_info, 0}, 56 {"bufs", drm_bufs_info, 0}, 57 {"gem_names", drm_gem_name_info, DRIVER_GEM}, 58 {"vma", drm_vma_info, 0}, 59 }; 60 #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list) 61 62 63 static int drm_debugfs_open(struct inode *inode, struct file *file) 64 { 65 struct drm_info_node *node = inode->i_private; 66 67 return single_open(file, node->info_ent->show, node); 68 } 69 70 71 static const struct file_operations drm_debugfs_fops = { 72 .owner = THIS_MODULE, 73 .open = drm_debugfs_open, 74 .read = seq_read, 75 .llseek = seq_lseek, 76 .release = single_release, 77 }; 78 79 80 /** 81 * Initialize a given set of debugfs files for a device 82 * 83 * \param files The array of files to create 84 * \param count The number of files given 85 * \param root DRI debugfs dir entry. 86 * \param minor device minor number 87 * \return Zero on success, non-zero on failure 88 * 89 * Create a given set of debugfs files represented by an array of 90 * gdm_debugfs_lists in the given root directory. 91 */ 92 int drm_debugfs_create_files(const struct drm_info_list *files, int count, 93 struct dentry *root, struct drm_minor *minor) 94 { 95 struct drm_device *dev = minor->dev; 96 struct dentry *ent; 97 struct drm_info_node *tmp; 98 int i, ret; 99 100 for (i = 0; i < count; i++) { 101 u32 features = files[i].driver_features; 102 103 if (features != 0 && 104 (dev->driver->driver_features & features) != features) 105 continue; 106 107 tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); 108 if (tmp == NULL) { 109 ret = -1; 110 goto fail; 111 } 112 ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, 113 root, tmp, &drm_debugfs_fops); 114 if (!ent) { 115 DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n", 116 root->d_name.name, files[i].name); 117 kfree(tmp); 118 ret = -1; 119 goto fail; 120 } 121 122 tmp->minor = minor; 123 tmp->dent = ent; 124 tmp->info_ent = &files[i]; 125 126 mutex_lock(&minor->debugfs_lock); 127 list_add(&tmp->list, &minor->debugfs_list); 128 mutex_unlock(&minor->debugfs_lock); 129 } 130 return 0; 131 132 fail: 133 drm_debugfs_remove_files(files, count, minor); 134 return ret; 135 } 136 EXPORT_SYMBOL(drm_debugfs_create_files); 137 138 /** 139 * Initialize the DRI debugfs filesystem for a device 140 * 141 * \param dev DRM device 142 * \param minor device minor number 143 * \param root DRI debugfs dir entry. 144 * 145 * Create the DRI debugfs root entry "/sys/kernel/debug/dri", the device debugfs root entry 146 * "/sys/kernel/debug/dri/%minor%/", and each entry in debugfs_list as 147 * "/sys/kernel/debug/dri/%minor%/%name%". 148 */ 149 int drm_debugfs_init(struct drm_minor *minor, int minor_id, 150 struct dentry *root) 151 { 152 struct drm_device *dev = minor->dev; 153 char name[64]; 154 int ret; 155 156 INIT_LIST_HEAD(&minor->debugfs_list); 157 mutex_init(&minor->debugfs_lock); 158 snprintf(name, sizeof(name), "%d", minor_id); 159 minor->debugfs_root = debugfs_create_dir(name, root); 160 if (!minor->debugfs_root) { 161 DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s\n", name); 162 return -1; 163 } 164 165 ret = drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, 166 minor->debugfs_root, minor); 167 if (ret) { 168 debugfs_remove(minor->debugfs_root); 169 minor->debugfs_root = NULL; 170 DRM_ERROR("Failed to create core drm debugfs files\n"); 171 return ret; 172 } 173 174 if (dev->driver->debugfs_init) { 175 ret = dev->driver->debugfs_init(minor); 176 if (ret) { 177 DRM_ERROR("DRM: Driver failed to initialize " 178 "/sys/kernel/debug/dri.\n"); 179 return ret; 180 } 181 } 182 return 0; 183 } 184 185 186 /** 187 * Remove a list of debugfs files 188 * 189 * \param files The list of files 190 * \param count The number of files 191 * \param minor The minor of which we should remove the files 192 * \return always zero. 193 * 194 * Remove all debugfs entries created by debugfs_init(). 195 */ 196 int drm_debugfs_remove_files(const struct drm_info_list *files, int count, 197 struct drm_minor *minor) 198 { 199 struct list_head *pos, *q; 200 struct drm_info_node *tmp; 201 int i; 202 203 mutex_lock(&minor->debugfs_lock); 204 for (i = 0; i < count; i++) { 205 list_for_each_safe(pos, q, &minor->debugfs_list) { 206 tmp = list_entry(pos, struct drm_info_node, list); 207 if (tmp->info_ent == &files[i]) { 208 debugfs_remove(tmp->dent); 209 list_del(pos); 210 kfree(tmp); 211 } 212 } 213 } 214 mutex_unlock(&minor->debugfs_lock); 215 return 0; 216 } 217 EXPORT_SYMBOL(drm_debugfs_remove_files); 218 219 /** 220 * Cleanup the debugfs filesystem resources. 221 * 222 * \param minor device minor number. 223 * \return always zero. 224 * 225 * Remove all debugfs entries created by debugfs_init(). 226 */ 227 int drm_debugfs_cleanup(struct drm_minor *minor) 228 { 229 struct drm_device *dev = minor->dev; 230 231 if (!minor->debugfs_root) 232 return 0; 233 234 if (dev->driver->debugfs_cleanup) 235 dev->driver->debugfs_cleanup(minor); 236 237 drm_debugfs_remove_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, minor); 238 239 debugfs_remove(minor->debugfs_root); 240 minor->debugfs_root = NULL; 241 242 return 0; 243 } 244 245 static int connector_show(struct seq_file *m, void *data) 246 { 247 struct drm_connector *connector = m->private; 248 const char *status; 249 250 switch (connector->force) { 251 case DRM_FORCE_ON: 252 status = "on\n"; 253 break; 254 255 case DRM_FORCE_ON_DIGITAL: 256 status = "digital\n"; 257 break; 258 259 case DRM_FORCE_OFF: 260 status = "off\n"; 261 break; 262 263 case DRM_FORCE_UNSPECIFIED: 264 status = "unspecified\n"; 265 break; 266 267 default: 268 return 0; 269 } 270 271 seq_puts(m, status); 272 273 return 0; 274 } 275 276 static int connector_open(struct inode *inode, struct file *file) 277 { 278 struct drm_connector *dev = inode->i_private; 279 280 return single_open(file, connector_show, dev); 281 } 282 283 static ssize_t connector_write(struct file *file, const char __user *ubuf, 284 size_t len, loff_t *offp) 285 { 286 struct seq_file *m = file->private_data; 287 struct drm_connector *connector = m->private; 288 char buf[12]; 289 290 if (len > sizeof(buf) - 1) 291 return -EINVAL; 292 293 if (copy_from_user(buf, ubuf, len)) 294 return -EFAULT; 295 296 buf[len] = '\0'; 297 298 if (!strcmp(buf, "on")) 299 connector->force = DRM_FORCE_ON; 300 else if (!strcmp(buf, "digital")) 301 connector->force = DRM_FORCE_ON_DIGITAL; 302 else if (!strcmp(buf, "off")) 303 connector->force = DRM_FORCE_OFF; 304 else if (!strcmp(buf, "unspecified")) 305 connector->force = DRM_FORCE_UNSPECIFIED; 306 else 307 return -EINVAL; 308 309 return len; 310 } 311 312 static int edid_show(struct seq_file *m, void *data) 313 { 314 struct drm_connector *connector = m->private; 315 struct drm_property_blob *edid = connector->edid_blob_ptr; 316 317 if (connector->override_edid && edid) 318 seq_write(m, edid->data, edid->length); 319 320 return 0; 321 } 322 323 static int edid_open(struct inode *inode, struct file *file) 324 { 325 struct drm_connector *dev = inode->i_private; 326 327 return single_open(file, edid_show, dev); 328 } 329 330 static ssize_t edid_write(struct file *file, const char __user *ubuf, 331 size_t len, loff_t *offp) 332 { 333 struct seq_file *m = file->private_data; 334 struct drm_connector *connector = m->private; 335 char *buf; 336 struct edid *edid; 337 int ret; 338 339 buf = memdup_user(ubuf, len); 340 if (IS_ERR(buf)) 341 return PTR_ERR(buf); 342 343 edid = (struct edid *) buf; 344 345 if (len == 5 && !strncmp(buf, "reset", 5)) { 346 connector->override_edid = false; 347 ret = drm_mode_connector_update_edid_property(connector, NULL); 348 } else if (len < EDID_LENGTH || 349 EDID_LENGTH * (1 + edid->extensions) > len) 350 ret = -EINVAL; 351 else { 352 connector->override_edid = false; 353 ret = drm_mode_connector_update_edid_property(connector, edid); 354 if (!ret) 355 connector->override_edid = true; 356 } 357 358 kfree(buf); 359 360 return (ret) ? ret : len; 361 } 362 363 static const struct file_operations drm_edid_fops = { 364 .owner = THIS_MODULE, 365 .open = edid_open, 366 .read = seq_read, 367 .llseek = seq_lseek, 368 .release = single_release, 369 .write = edid_write 370 }; 371 372 373 static const struct file_operations drm_connector_fops = { 374 .owner = THIS_MODULE, 375 .open = connector_open, 376 .read = seq_read, 377 .llseek = seq_lseek, 378 .release = single_release, 379 .write = connector_write 380 }; 381 382 int drm_debugfs_connector_add(struct drm_connector *connector) 383 { 384 struct drm_minor *minor = connector->dev->primary; 385 struct dentry *root, *ent; 386 387 if (!minor->debugfs_root) 388 return -1; 389 390 root = debugfs_create_dir(connector->name, minor->debugfs_root); 391 if (!root) 392 return -ENOMEM; 393 394 connector->debugfs_entry = root; 395 396 /* force */ 397 ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector, 398 &drm_connector_fops); 399 if (!ent) 400 goto error; 401 402 /* edid */ 403 ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, 404 connector, &drm_edid_fops); 405 if (!ent) 406 goto error; 407 408 return 0; 409 410 error: 411 debugfs_remove_recursive(connector->debugfs_entry); 412 connector->debugfs_entry = NULL; 413 return -ENOMEM; 414 } 415 416 void drm_debugfs_connector_remove(struct drm_connector *connector) 417 { 418 if (!connector->debugfs_entry) 419 return; 420 421 debugfs_remove_recursive(connector->debugfs_entry); 422 423 connector->debugfs_entry = NULL; 424 } 425 426 #endif /* CONFIG_DEBUG_FS */ 427 428