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