]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/blob - drivers/gpu/drm/drm_debugfs.c
Merge tag 'devicetree-fixes-for-4.19-3' of git://git.kernel.org/pub/scm/linux/kernel...
[rpmsg/rpmsg.git] / drivers / gpu / drm / drm_debugfs.c
1 /*
2  * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com
3  *
4  * Copyright 2008 Ben Gamari <bgamari@gmail.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23  * OTHER DEALINGS IN THE SOFTWARE.
24  */
26 #include <linux/debugfs.h>
27 #include <linux/seq_file.h>
28 #include <linux/slab.h>
29 #include <linux/export.h>
31 #include <drm/drm_client.h>
32 #include <drm/drm_debugfs.h>
33 #include <drm/drm_edid.h>
34 #include <drm/drm_atomic.h>
35 #include <drm/drmP.h>
37 #include "drm_internal.h"
38 #include "drm_crtc_internal.h"
40 #if defined(CONFIG_DEBUG_FS)
42 /***************************************************
43  * Initialization, etc.
44  **************************************************/
46 static const struct drm_info_list drm_debugfs_list[] = {
47         {"name", drm_name_info, 0},
48         {"clients", drm_clients_info, 0},
49         {"gem_names", drm_gem_name_info, DRIVER_GEM},
50 };
51 #define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
54 static int drm_debugfs_open(struct inode *inode, struct file *file)
55 {
56         struct drm_info_node *node = inode->i_private;
58         return single_open(file, node->info_ent->show, node);
59 }
62 static const struct file_operations drm_debugfs_fops = {
63         .owner = THIS_MODULE,
64         .open = drm_debugfs_open,
65         .read = seq_read,
66         .llseek = seq_lseek,
67         .release = single_release,
68 };
71 /**
72  * drm_debugfs_create_files - Initialize a given set of debugfs files for DRM
73  *                      minor
74  * @files: The array of files to create
75  * @count: The number of files given
76  * @root: DRI debugfs dir entry.
77  * @minor: device minor number
78  *
79  * Create a given set of debugfs files represented by an array of
80  * &struct drm_info_list in the given root directory. These files will be removed
81  * automatically on drm_debugfs_cleanup().
82  */
83 int drm_debugfs_create_files(const struct drm_info_list *files, int count,
84                              struct dentry *root, struct drm_minor *minor)
85 {
86         struct drm_device *dev = minor->dev;
87         struct dentry *ent;
88         struct drm_info_node *tmp;
89         int i, ret;
91         for (i = 0; i < count; i++) {
92                 u32 features = files[i].driver_features;
94                 if (features != 0 &&
95                     (dev->driver->driver_features & features) != features)
96                         continue;
98                 tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
99                 if (tmp == NULL) {
100                         ret = -1;
101                         goto fail;
102                 }
103                 ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
104                                           root, tmp, &drm_debugfs_fops);
105                 if (!ent) {
106                         DRM_ERROR("Cannot create /sys/kernel/debug/dri/%pd/%s\n",
107                                   root, files[i].name);
108                         kfree(tmp);
109                         ret = -1;
110                         goto fail;
111                 }
113                 tmp->minor = minor;
114                 tmp->dent = ent;
115                 tmp->info_ent = &files[i];
117                 mutex_lock(&minor->debugfs_lock);
118                 list_add(&tmp->list, &minor->debugfs_list);
119                 mutex_unlock(&minor->debugfs_lock);
120         }
121         return 0;
123 fail:
124         drm_debugfs_remove_files(files, count, minor);
125         return ret;
127 EXPORT_SYMBOL(drm_debugfs_create_files);
129 int drm_debugfs_init(struct drm_minor *minor, int minor_id,
130                      struct dentry *root)
132         struct drm_device *dev = minor->dev;
133         char name[64];
134         int ret;
136         INIT_LIST_HEAD(&minor->debugfs_list);
137         mutex_init(&minor->debugfs_lock);
138         sprintf(name, "%d", minor_id);
139         minor->debugfs_root = debugfs_create_dir(name, root);
140         if (!minor->debugfs_root) {
141                 DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s\n", name);
142                 return -1;
143         }
145         ret = drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES,
146                                        minor->debugfs_root, minor);
147         if (ret) {
148                 debugfs_remove(minor->debugfs_root);
149                 minor->debugfs_root = NULL;
150                 DRM_ERROR("Failed to create core drm debugfs files\n");
151                 return ret;
152         }
154         if (drm_drv_uses_atomic_modeset(dev)) {
155                 ret = drm_atomic_debugfs_init(minor);
156                 if (ret) {
157                         DRM_ERROR("Failed to create atomic debugfs files\n");
158                         return ret;
159                 }
160         }
162         if (drm_core_check_feature(dev, DRIVER_MODESET)) {
163                 ret = drm_framebuffer_debugfs_init(minor);
164                 if (ret) {
165                         DRM_ERROR("Failed to create framebuffer debugfs file\n");
166                         return ret;
167                 }
169                 ret = drm_client_debugfs_init(minor);
170                 if (ret) {
171                         DRM_ERROR("Failed to create client debugfs file\n");
172                         return ret;
173                 }
174         }
176         if (dev->driver->debugfs_init) {
177                 ret = dev->driver->debugfs_init(minor);
178                 if (ret) {
179                         DRM_ERROR("DRM: Driver failed to initialize "
180                                   "/sys/kernel/debug/dri.\n");
181                         return ret;
182                 }
183         }
184         return 0;
188 int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
189                              struct drm_minor *minor)
191         struct list_head *pos, *q;
192         struct drm_info_node *tmp;
193         int i;
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;
209 EXPORT_SYMBOL(drm_debugfs_remove_files);
211 static void drm_debugfs_remove_all_files(struct drm_minor *minor)
213         struct drm_info_node *node, *tmp;
215         mutex_lock(&minor->debugfs_lock);
216         list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) {
217                 debugfs_remove(node->dent);
218                 list_del(&node->list);
219                 kfree(node);
220         }
221         mutex_unlock(&minor->debugfs_lock);
224 int drm_debugfs_cleanup(struct drm_minor *minor)
226         if (!minor->debugfs_root)
227                 return 0;
229         drm_debugfs_remove_all_files(minor);
231         debugfs_remove_recursive(minor->debugfs_root);
232         minor->debugfs_root = NULL;
234         return 0;
237 static int connector_show(struct seq_file *m, void *data)
239         struct drm_connector *connector = m->private;
241         seq_printf(m, "%s\n", drm_get_connector_force_name(connector->force));
243         return 0;
246 static int connector_open(struct inode *inode, struct file *file)
248         struct drm_connector *dev = inode->i_private;
250         return single_open(file, connector_show, dev);
253 static ssize_t connector_write(struct file *file, const char __user *ubuf,
254                                size_t len, loff_t *offp)
256         struct seq_file *m = file->private_data;
257         struct drm_connector *connector = m->private;
258         char buf[12];
260         if (len > sizeof(buf) - 1)
261                 return -EINVAL;
263         if (copy_from_user(buf, ubuf, len))
264                 return -EFAULT;
266         buf[len] = '\0';
268         if (!strcmp(buf, "on"))
269                 connector->force = DRM_FORCE_ON;
270         else if (!strcmp(buf, "digital"))
271                 connector->force = DRM_FORCE_ON_DIGITAL;
272         else if (!strcmp(buf, "off"))
273                 connector->force = DRM_FORCE_OFF;
274         else if (!strcmp(buf, "unspecified"))
275                 connector->force = DRM_FORCE_UNSPECIFIED;
276         else
277                 return -EINVAL;
279         return len;
282 static int edid_show(struct seq_file *m, void *data)
284         struct drm_connector *connector = m->private;
285         struct drm_property_blob *edid = connector->edid_blob_ptr;
287         if (connector->override_edid && edid)
288                 seq_write(m, edid->data, edid->length);
290         return 0;
293 static int edid_open(struct inode *inode, struct file *file)
295         struct drm_connector *dev = inode->i_private;
297         return single_open(file, edid_show, dev);
300 static ssize_t edid_write(struct file *file, const char __user *ubuf,
301                           size_t len, loff_t *offp)
303         struct seq_file *m = file->private_data;
304         struct drm_connector *connector = m->private;
305         char *buf;
306         struct edid *edid;
307         int ret;
309         buf = memdup_user(ubuf, len);
310         if (IS_ERR(buf))
311                 return PTR_ERR(buf);
313         edid = (struct edid *) buf;
315         if (len == 5 && !strncmp(buf, "reset", 5)) {
316                 connector->override_edid = false;
317                 ret = drm_connector_update_edid_property(connector, NULL);
318         } else if (len < EDID_LENGTH ||
319                    EDID_LENGTH * (1 + edid->extensions) > len)
320                 ret = -EINVAL;
321         else {
322                 connector->override_edid = false;
323                 ret = drm_connector_update_edid_property(connector, edid);
324                 if (!ret)
325                         connector->override_edid = true;
326         }
328         kfree(buf);
330         return (ret) ? ret : len;
333 static const struct file_operations drm_edid_fops = {
334         .owner = THIS_MODULE,
335         .open = edid_open,
336         .read = seq_read,
337         .llseek = seq_lseek,
338         .release = single_release,
339         .write = edid_write
340 };
343 static const struct file_operations drm_connector_fops = {
344         .owner = THIS_MODULE,
345         .open = connector_open,
346         .read = seq_read,
347         .llseek = seq_lseek,
348         .release = single_release,
349         .write = connector_write
350 };
352 int drm_debugfs_connector_add(struct drm_connector *connector)
354         struct drm_minor *minor = connector->dev->primary;
355         struct dentry *root, *ent;
357         if (!minor->debugfs_root)
358                 return -1;
360         root = debugfs_create_dir(connector->name, minor->debugfs_root);
361         if (!root)
362                 return -ENOMEM;
364         connector->debugfs_entry = root;
366         /* force */
367         ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector,
368                                   &drm_connector_fops);
369         if (!ent)
370                 goto error;
372         /* edid */
373         ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root,
374                                   connector, &drm_edid_fops);
375         if (!ent)
376                 goto error;
378         return 0;
380 error:
381         debugfs_remove_recursive(connector->debugfs_entry);
382         connector->debugfs_entry = NULL;
383         return -ENOMEM;
386 void drm_debugfs_connector_remove(struct drm_connector *connector)
388         if (!connector->debugfs_entry)
389                 return;
391         debugfs_remove_recursive(connector->debugfs_entry);
393         connector->debugfs_entry = NULL;
396 int drm_debugfs_crtc_add(struct drm_crtc *crtc)
398         struct drm_minor *minor = crtc->dev->primary;
399         struct dentry *root;
400         char *name;
402         name = kasprintf(GFP_KERNEL, "crtc-%d", crtc->index);
403         if (!name)
404                 return -ENOMEM;
406         root = debugfs_create_dir(name, minor->debugfs_root);
407         kfree(name);
408         if (!root)
409                 return -ENOMEM;
411         crtc->debugfs_entry = root;
413         if (drm_debugfs_crtc_crc_add(crtc))
414                 goto error;
416         return 0;
418 error:
419         drm_debugfs_crtc_remove(crtc);
420         return -ENOMEM;
423 void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
425         debugfs_remove_recursive(crtc->debugfs_entry);
426         crtc->debugfs_entry = NULL;
429 #endif /* CONFIG_DEBUG_FS */