diff options
Diffstat (limited to 'drivers/gpu/drm/drm_debugfs.c')
-rw-r--r-- | drivers/gpu/drm/drm_debugfs.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b4b51d46f339..13bd42923dd4 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/slab.h> | 35 | #include <linux/slab.h> |
36 | #include <linux/export.h> | 36 | #include <linux/export.h> |
37 | #include <drm/drmP.h> | 37 | #include <drm/drmP.h> |
38 | #include <drm/drm_edid.h> | ||
38 | 39 | ||
39 | #if defined(CONFIG_DEBUG_FS) | 40 | #if defined(CONFIG_DEBUG_FS) |
40 | 41 | ||
@@ -237,5 +238,186 @@ int drm_debugfs_cleanup(struct drm_minor *minor) | |||
237 | return 0; | 238 | return 0; |
238 | } | 239 | } |
239 | 240 | ||
241 | static int connector_show(struct seq_file *m, void *data) | ||
242 | { | ||
243 | struct drm_connector *connector = m->private; | ||
244 | const char *status; | ||
245 | |||
246 | switch (connector->force) { | ||
247 | case DRM_FORCE_ON: | ||
248 | status = "on\n"; | ||
249 | break; | ||
250 | |||
251 | case DRM_FORCE_ON_DIGITAL: | ||
252 | status = "digital\n"; | ||
253 | break; | ||
254 | |||
255 | case DRM_FORCE_OFF: | ||
256 | status = "off\n"; | ||
257 | break; | ||
258 | |||
259 | case DRM_FORCE_UNSPECIFIED: | ||
260 | status = "unspecified\n"; | ||
261 | break; | ||
262 | |||
263 | default: | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | seq_puts(m, status); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int connector_open(struct inode *inode, struct file *file) | ||
273 | { | ||
274 | struct drm_connector *dev = inode->i_private; | ||
275 | |||
276 | return single_open(file, connector_show, dev); | ||
277 | } | ||
278 | |||
279 | static ssize_t connector_write(struct file *file, const char __user *ubuf, | ||
280 | size_t len, loff_t *offp) | ||
281 | { | ||
282 | struct seq_file *m = file->private_data; | ||
283 | struct drm_connector *connector = m->private; | ||
284 | char buf[12]; | ||
285 | |||
286 | if (len > sizeof(buf) - 1) | ||
287 | return -EINVAL; | ||
288 | |||
289 | if (copy_from_user(buf, ubuf, len)) | ||
290 | return -EFAULT; | ||
291 | |||
292 | buf[len] = '\0'; | ||
293 | |||
294 | if (!strcmp(buf, "on")) | ||
295 | connector->force = DRM_FORCE_ON; | ||
296 | else if (!strcmp(buf, "digital")) | ||
297 | connector->force = DRM_FORCE_ON_DIGITAL; | ||
298 | else if (!strcmp(buf, "off")) | ||
299 | connector->force = DRM_FORCE_OFF; | ||
300 | else if (!strcmp(buf, "unspecified")) | ||
301 | connector->force = DRM_FORCE_UNSPECIFIED; | ||
302 | else | ||
303 | return -EINVAL; | ||
304 | |||
305 | return len; | ||
306 | } | ||
307 | |||
308 | static int edid_show(struct seq_file *m, void *data) | ||
309 | { | ||
310 | struct drm_connector *connector = m->private; | ||
311 | struct drm_property_blob *edid = connector->edid_blob_ptr; | ||
312 | |||
313 | if (connector->override_edid && edid) | ||
314 | seq_write(m, edid->data, edid->length); | ||
315 | |||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static int edid_open(struct inode *inode, struct file *file) | ||
320 | { | ||
321 | struct drm_connector *dev = inode->i_private; | ||
322 | |||
323 | return single_open(file, edid_show, dev); | ||
324 | } | ||
325 | |||
326 | static ssize_t edid_write(struct file *file, const char __user *ubuf, | ||
327 | size_t len, loff_t *offp) | ||
328 | { | ||
329 | struct seq_file *m = file->private_data; | ||
330 | struct drm_connector *connector = m->private; | ||
331 | char *buf; | ||
332 | struct edid *edid; | ||
333 | int ret; | ||
334 | |||
335 | buf = memdup_user(ubuf, len); | ||
336 | if (IS_ERR(buf)) | ||
337 | return PTR_ERR(buf); | ||
338 | |||
339 | edid = (struct edid *) buf; | ||
340 | |||
341 | if (len == 5 && !strncmp(buf, "reset", 5)) { | ||
342 | connector->override_edid = false; | ||
343 | ret = drm_mode_connector_update_edid_property(connector, NULL); | ||
344 | } else if (len < EDID_LENGTH || | ||
345 | EDID_LENGTH * (1 + edid->extensions) > len) | ||
346 | ret = -EINVAL; | ||
347 | else { | ||
348 | connector->override_edid = false; | ||
349 | ret = drm_mode_connector_update_edid_property(connector, edid); | ||
350 | if (!ret) | ||
351 | connector->override_edid = true; | ||
352 | } | ||
353 | |||
354 | kfree(buf); | ||
355 | |||
356 | return (ret) ? ret : len; | ||
357 | } | ||
358 | |||
359 | static const struct file_operations drm_edid_fops = { | ||
360 | .owner = THIS_MODULE, | ||
361 | .open = edid_open, | ||
362 | .read = seq_read, | ||
363 | .llseek = seq_lseek, | ||
364 | .release = single_release, | ||
365 | .write = edid_write | ||
366 | }; | ||
367 | |||
368 | |||
369 | static const struct file_operations drm_connector_fops = { | ||
370 | .owner = THIS_MODULE, | ||
371 | .open = connector_open, | ||
372 | .read = seq_read, | ||
373 | .llseek = seq_lseek, | ||
374 | .release = single_release, | ||
375 | .write = connector_write | ||
376 | }; | ||
377 | |||
378 | int drm_debugfs_connector_add(struct drm_connector *connector) | ||
379 | { | ||
380 | struct drm_minor *minor = connector->dev->primary; | ||
381 | struct dentry *root, *ent; | ||
382 | |||
383 | if (!minor->debugfs_root) | ||
384 | return -1; | ||
385 | |||
386 | root = debugfs_create_dir(connector->name, minor->debugfs_root); | ||
387 | if (!root) | ||
388 | return -ENOMEM; | ||
389 | |||
390 | connector->debugfs_entry = root; | ||
391 | |||
392 | /* force */ | ||
393 | ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector, | ||
394 | &drm_connector_fops); | ||
395 | if (!ent) | ||
396 | goto error; | ||
397 | |||
398 | /* edid */ | ||
399 | ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, | ||
400 | connector, &drm_edid_fops); | ||
401 | if (!ent) | ||
402 | goto error; | ||
403 | |||
404 | return 0; | ||
405 | |||
406 | error: | ||
407 | debugfs_remove_recursive(connector->debugfs_entry); | ||
408 | connector->debugfs_entry = NULL; | ||
409 | return -ENOMEM; | ||
410 | } | ||
411 | |||
412 | void drm_debugfs_connector_remove(struct drm_connector *connector) | ||
413 | { | ||
414 | if (!connector->debugfs_entry) | ||
415 | return; | ||
416 | |||
417 | debugfs_remove_recursive(connector->debugfs_entry); | ||
418 | |||
419 | connector->debugfs_entry = NULL; | ||
420 | } | ||
421 | |||
240 | #endif /* CONFIG_DEBUG_FS */ | 422 | #endif /* CONFIG_DEBUG_FS */ |
241 | 423 | ||