| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. |
|---|
| 3 | 4 | * Author: Liviu Dudau <Liviu.Dudau@arm.com> |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software and is provided to you under the terms of the |
|---|
| 6 | | - * GNU General Public License version 2 as published by the Free Software |
|---|
| 7 | | - * Foundation, and any use by you of this program is subject to the terms |
|---|
| 8 | | - * of such GNU licence. |
|---|
| 9 | 5 | * |
|---|
| 10 | 6 | * ARM Mali DP500/DP550/DP650 KMS/DRM driver |
|---|
| 11 | 7 | */ |
|---|
| .. | .. |
|---|
| 19 | 15 | #include <linux/pm_runtime.h> |
|---|
| 20 | 16 | #include <linux/debugfs.h> |
|---|
| 21 | 17 | |
|---|
| 22 | | -#include <drm/drmP.h> |
|---|
| 23 | 18 | #include <drm/drm_atomic.h> |
|---|
| 24 | 19 | #include <drm/drm_atomic_helper.h> |
|---|
| 25 | 20 | #include <drm/drm_crtc.h> |
|---|
| 26 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 27 | | -#include <drm/drm_fb_helper.h> |
|---|
| 21 | +#include <drm/drm_drv.h> |
|---|
| 28 | 22 | #include <drm/drm_fb_cma_helper.h> |
|---|
| 23 | +#include <drm/drm_fb_helper.h> |
|---|
| 24 | +#include <drm/drm_fourcc.h> |
|---|
| 29 | 25 | #include <drm/drm_gem_cma_helper.h> |
|---|
| 30 | 26 | #include <drm/drm_gem_framebuffer_helper.h> |
|---|
| 31 | 27 | #include <drm/drm_modeset_helper.h> |
|---|
| 32 | 28 | #include <drm/drm_of.h> |
|---|
| 29 | +#include <drm/drm_probe_helper.h> |
|---|
| 30 | +#include <drm/drm_vblank.h> |
|---|
| 33 | 31 | |
|---|
| 34 | 32 | #include "malidp_drv.h" |
|---|
| 35 | 33 | #include "malidp_mw.h" |
|---|
| .. | .. |
|---|
| 37 | 35 | #include "malidp_hw.h" |
|---|
| 38 | 36 | |
|---|
| 39 | 37 | #define MALIDP_CONF_VALID_TIMEOUT 250 |
|---|
| 38 | +#define AFBC_HEADER_SIZE 16 |
|---|
| 39 | +#define AFBC_SUPERBLK_ALIGNMENT 128 |
|---|
| 40 | 40 | |
|---|
| 41 | 41 | static void malidp_write_gamma_table(struct malidp_hw_device *hwdev, |
|---|
| 42 | 42 | u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) |
|---|
| .. | .. |
|---|
| 269 | 269 | .atomic_commit_tail = malidp_atomic_commit_tail, |
|---|
| 270 | 270 | }; |
|---|
| 271 | 271 | |
|---|
| 272 | +static bool |
|---|
| 273 | +malidp_verify_afbc_framebuffer_caps(struct drm_device *dev, |
|---|
| 274 | + const struct drm_mode_fb_cmd2 *mode_cmd) |
|---|
| 275 | +{ |
|---|
| 276 | + if (malidp_format_mod_supported(dev, mode_cmd->pixel_format, |
|---|
| 277 | + mode_cmd->modifier[0]) == false) |
|---|
| 278 | + return false; |
|---|
| 279 | + |
|---|
| 280 | + if (mode_cmd->offsets[0] != 0) { |
|---|
| 281 | + DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n"); |
|---|
| 282 | + return false; |
|---|
| 283 | + } |
|---|
| 284 | + |
|---|
| 285 | + switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { |
|---|
| 286 | + case AFBC_SIZE_16X16: |
|---|
| 287 | + if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) { |
|---|
| 288 | + DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n"); |
|---|
| 289 | + return false; |
|---|
| 290 | + } |
|---|
| 291 | + break; |
|---|
| 292 | + default: |
|---|
| 293 | + DRM_DEBUG_KMS("Unsupported AFBC block size\n"); |
|---|
| 294 | + return false; |
|---|
| 295 | + } |
|---|
| 296 | + |
|---|
| 297 | + return true; |
|---|
| 298 | +} |
|---|
| 299 | + |
|---|
| 300 | +static bool |
|---|
| 301 | +malidp_verify_afbc_framebuffer_size(struct drm_device *dev, |
|---|
| 302 | + struct drm_file *file, |
|---|
| 303 | + const struct drm_mode_fb_cmd2 *mode_cmd) |
|---|
| 304 | +{ |
|---|
| 305 | + int n_superblocks = 0; |
|---|
| 306 | + const struct drm_format_info *info; |
|---|
| 307 | + struct drm_gem_object *objs = NULL; |
|---|
| 308 | + u32 afbc_superblock_size = 0, afbc_superblock_height = 0; |
|---|
| 309 | + u32 afbc_superblock_width = 0, afbc_size = 0; |
|---|
| 310 | + int bpp = 0; |
|---|
| 311 | + |
|---|
| 312 | + switch (mode_cmd->modifier[0] & AFBC_SIZE_MASK) { |
|---|
| 313 | + case AFBC_SIZE_16X16: |
|---|
| 314 | + afbc_superblock_height = 16; |
|---|
| 315 | + afbc_superblock_width = 16; |
|---|
| 316 | + break; |
|---|
| 317 | + default: |
|---|
| 318 | + DRM_DEBUG_KMS("AFBC superblock size is not supported\n"); |
|---|
| 319 | + return false; |
|---|
| 320 | + } |
|---|
| 321 | + |
|---|
| 322 | + info = drm_get_format_info(dev, mode_cmd); |
|---|
| 323 | + |
|---|
| 324 | + n_superblocks = (mode_cmd->width / afbc_superblock_width) * |
|---|
| 325 | + (mode_cmd->height / afbc_superblock_height); |
|---|
| 326 | + |
|---|
| 327 | + bpp = malidp_format_get_bpp(info->format); |
|---|
| 328 | + |
|---|
| 329 | + afbc_superblock_size = (bpp * afbc_superblock_width * afbc_superblock_height) |
|---|
| 330 | + / BITS_PER_BYTE; |
|---|
| 331 | + |
|---|
| 332 | + afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT); |
|---|
| 333 | + afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT); |
|---|
| 334 | + |
|---|
| 335 | + if ((mode_cmd->width * bpp) != (mode_cmd->pitches[0] * BITS_PER_BYTE)) { |
|---|
| 336 | + DRM_DEBUG_KMS("Invalid value of (pitch * BITS_PER_BYTE) (=%u) " |
|---|
| 337 | + "should be same as width (=%u) * bpp (=%u)\n", |
|---|
| 338 | + (mode_cmd->pitches[0] * BITS_PER_BYTE), |
|---|
| 339 | + mode_cmd->width, bpp); |
|---|
| 340 | + return false; |
|---|
| 341 | + } |
|---|
| 342 | + |
|---|
| 343 | + objs = drm_gem_object_lookup(file, mode_cmd->handles[0]); |
|---|
| 344 | + if (!objs) { |
|---|
| 345 | + DRM_DEBUG_KMS("Failed to lookup GEM object\n"); |
|---|
| 346 | + return false; |
|---|
| 347 | + } |
|---|
| 348 | + |
|---|
| 349 | + if (objs->size < afbc_size) { |
|---|
| 350 | + DRM_DEBUG_KMS("buffer size (%zu) too small for AFBC buffer size = %u\n", |
|---|
| 351 | + objs->size, afbc_size); |
|---|
| 352 | + drm_gem_object_put(objs); |
|---|
| 353 | + return false; |
|---|
| 354 | + } |
|---|
| 355 | + |
|---|
| 356 | + drm_gem_object_put(objs); |
|---|
| 357 | + |
|---|
| 358 | + return true; |
|---|
| 359 | +} |
|---|
| 360 | + |
|---|
| 361 | +static bool |
|---|
| 362 | +malidp_verify_afbc_framebuffer(struct drm_device *dev, struct drm_file *file, |
|---|
| 363 | + const struct drm_mode_fb_cmd2 *mode_cmd) |
|---|
| 364 | +{ |
|---|
| 365 | + if (malidp_verify_afbc_framebuffer_caps(dev, mode_cmd)) |
|---|
| 366 | + return malidp_verify_afbc_framebuffer_size(dev, file, mode_cmd); |
|---|
| 367 | + |
|---|
| 368 | + return false; |
|---|
| 369 | +} |
|---|
| 370 | + |
|---|
| 371 | +static struct drm_framebuffer * |
|---|
| 372 | +malidp_fb_create(struct drm_device *dev, struct drm_file *file, |
|---|
| 373 | + const struct drm_mode_fb_cmd2 *mode_cmd) |
|---|
| 374 | +{ |
|---|
| 375 | + if (mode_cmd->modifier[0]) { |
|---|
| 376 | + if (!malidp_verify_afbc_framebuffer(dev, file, mode_cmd)) |
|---|
| 377 | + return ERR_PTR(-EINVAL); |
|---|
| 378 | + } |
|---|
| 379 | + |
|---|
| 380 | + return drm_gem_fb_create(dev, file, mode_cmd); |
|---|
| 381 | +} |
|---|
| 382 | + |
|---|
| 272 | 383 | static const struct drm_mode_config_funcs malidp_mode_config_funcs = { |
|---|
| 273 | | - .fb_create = drm_gem_fb_create, |
|---|
| 274 | | - .output_poll_changed = drm_fb_helper_output_poll_changed, |
|---|
| 384 | + .fb_create = malidp_fb_create, |
|---|
| 275 | 385 | .atomic_check = drm_atomic_helper_check, |
|---|
| 276 | 386 | .atomic_commit = drm_atomic_helper_commit, |
|---|
| 277 | 387 | }; |
|---|
| .. | .. |
|---|
| 290 | 400 | drm->mode_config.max_height = hwdev->max_line_size; |
|---|
| 291 | 401 | drm->mode_config.funcs = &malidp_mode_config_funcs; |
|---|
| 292 | 402 | drm->mode_config.helper_private = &malidp_mode_config_helpers; |
|---|
| 403 | + drm->mode_config.allow_fb_modifiers = true; |
|---|
| 293 | 404 | |
|---|
| 294 | 405 | ret = malidp_crtc_init(drm); |
|---|
| 295 | 406 | if (ret) |
|---|
| .. | .. |
|---|
| 380 | 491 | spin_unlock_irqrestore(&malidp->errors_lock, irqflags); |
|---|
| 381 | 492 | } |
|---|
| 382 | 493 | |
|---|
| 383 | | -void malidp_error_stats_dump(const char *prefix, |
|---|
| 384 | | - struct malidp_error_stats error_stats, |
|---|
| 385 | | - struct seq_file *m) |
|---|
| 494 | +static void malidp_error_stats_dump(const char *prefix, |
|---|
| 495 | + struct malidp_error_stats error_stats, |
|---|
| 496 | + struct seq_file *m) |
|---|
| 386 | 497 | { |
|---|
| 387 | 498 | seq_printf(m, "[%s] num_errors : %d\n", prefix, |
|---|
| 388 | 499 | error_stats.num_errors); |
|---|
| .. | .. |
|---|
| 437 | 548 | .release = single_release, |
|---|
| 438 | 549 | }; |
|---|
| 439 | 550 | |
|---|
| 440 | | -static int malidp_debugfs_init(struct drm_minor *minor) |
|---|
| 551 | +static void malidp_debugfs_init(struct drm_minor *minor) |
|---|
| 441 | 552 | { |
|---|
| 442 | 553 | struct malidp_drm *malidp = minor->dev->dev_private; |
|---|
| 443 | | - struct dentry *dentry = NULL; |
|---|
| 444 | 554 | |
|---|
| 445 | 555 | malidp_error_stats_init(&malidp->de_errors); |
|---|
| 446 | 556 | malidp_error_stats_init(&malidp->se_errors); |
|---|
| 447 | 557 | spin_lock_init(&malidp->errors_lock); |
|---|
| 448 | | - dentry = debugfs_create_file("debug", |
|---|
| 449 | | - S_IRUGO | S_IWUSR, |
|---|
| 450 | | - minor->debugfs_root, minor->dev, |
|---|
| 451 | | - &malidp_debugfs_fops); |
|---|
| 452 | | - if (!dentry) { |
|---|
| 453 | | - DRM_ERROR("Cannot create debug file\n"); |
|---|
| 454 | | - return -ENOMEM; |
|---|
| 455 | | - } |
|---|
| 456 | | - return 0; |
|---|
| 558 | + debugfs_create_file("debug", S_IRUGO | S_IWUSR, minor->debugfs_root, |
|---|
| 559 | + minor->dev, &malidp_debugfs_fops); |
|---|
| 457 | 560 | } |
|---|
| 458 | 561 | |
|---|
| 459 | 562 | #endif //CONFIG_DEBUG_FS |
|---|
| 460 | 563 | |
|---|
| 461 | 564 | static struct drm_driver malidp_driver = { |
|---|
| 462 | | - .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | |
|---|
| 463 | | - DRIVER_PRIME, |
|---|
| 464 | | - .lastclose = drm_fb_helper_lastclose, |
|---|
| 465 | | - .gem_free_object_unlocked = drm_gem_cma_free_object, |
|---|
| 466 | | - .gem_vm_ops = &drm_gem_cma_vm_ops, |
|---|
| 467 | | - .dumb_create = malidp_dumb_create, |
|---|
| 468 | | - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
|---|
| 469 | | - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
|---|
| 470 | | - .gem_prime_export = drm_gem_prime_export, |
|---|
| 471 | | - .gem_prime_import = drm_gem_prime_import, |
|---|
| 472 | | - .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, |
|---|
| 473 | | - .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, |
|---|
| 474 | | - .gem_prime_vmap = drm_gem_cma_prime_vmap, |
|---|
| 475 | | - .gem_prime_vunmap = drm_gem_cma_prime_vunmap, |
|---|
| 476 | | - .gem_prime_mmap = drm_gem_cma_prime_mmap, |
|---|
| 565 | + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
|---|
| 566 | + DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(malidp_dumb_create), |
|---|
| 477 | 567 | #ifdef CONFIG_DEBUG_FS |
|---|
| 478 | 568 | .debugfs_init = malidp_debugfs_init, |
|---|
| 479 | 569 | #endif |
|---|
| .. | .. |
|---|
| 565 | 655 | return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id); |
|---|
| 566 | 656 | } |
|---|
| 567 | 657 | |
|---|
| 568 | | -DEVICE_ATTR_RO(core_id); |
|---|
| 658 | +static DEVICE_ATTR_RO(core_id); |
|---|
| 569 | 659 | |
|---|
| 570 | | -static int malidp_init_sysfs(struct device *dev) |
|---|
| 571 | | -{ |
|---|
| 572 | | - int ret = device_create_file(dev, &dev_attr_core_id); |
|---|
| 573 | | - |
|---|
| 574 | | - if (ret) |
|---|
| 575 | | - DRM_ERROR("failed to create device file for core_id\n"); |
|---|
| 576 | | - |
|---|
| 577 | | - return ret; |
|---|
| 578 | | -} |
|---|
| 579 | | - |
|---|
| 580 | | -static void malidp_fini_sysfs(struct device *dev) |
|---|
| 581 | | -{ |
|---|
| 582 | | - device_remove_file(dev, &dev_attr_core_id); |
|---|
| 583 | | -} |
|---|
| 660 | +static struct attribute *mali_dp_attrs[] = { |
|---|
| 661 | + &dev_attr_core_id.attr, |
|---|
| 662 | + NULL, |
|---|
| 663 | +}; |
|---|
| 664 | +ATTRIBUTE_GROUPS(mali_dp); |
|---|
| 584 | 665 | |
|---|
| 585 | 666 | #define MAX_OUTPUT_CHANNELS 3 |
|---|
| 586 | 667 | |
|---|
| .. | .. |
|---|
| 717 | 798 | |
|---|
| 718 | 799 | malidp->core_id = version; |
|---|
| 719 | 800 | |
|---|
| 801 | + ret = of_property_read_u32(dev->of_node, |
|---|
| 802 | + "arm,malidp-arqos-value", |
|---|
| 803 | + &hwdev->arqos_value); |
|---|
| 804 | + if (ret) |
|---|
| 805 | + hwdev->arqos_value = 0x0; |
|---|
| 806 | + |
|---|
| 720 | 807 | /* set the number of lines used for output of RGB data */ |
|---|
| 721 | 808 | ret = of_property_read_u8_array(dev->of_node, |
|---|
| 722 | 809 | "arm,malidp-output-port-lines", |
|---|
| .. | .. |
|---|
| 735 | 822 | ret = malidp_init(drm); |
|---|
| 736 | 823 | if (ret < 0) |
|---|
| 737 | 824 | goto query_hw_fail; |
|---|
| 738 | | - |
|---|
| 739 | | - ret = malidp_init_sysfs(dev); |
|---|
| 740 | | - if (ret) |
|---|
| 741 | | - goto init_fail; |
|---|
| 742 | 825 | |
|---|
| 743 | 826 | /* Set the CRTC's port so that the encoder component can find it */ |
|---|
| 744 | 827 | malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); |
|---|
| .. | .. |
|---|
| 765 | 848 | drm->irq_enabled = true; |
|---|
| 766 | 849 | |
|---|
| 767 | 850 | ret = drm_vblank_init(drm, drm->mode_config.num_crtc); |
|---|
| 768 | | - drm_crtc_vblank_reset(&malidp->crtc); |
|---|
| 769 | 851 | if (ret < 0) { |
|---|
| 770 | 852 | DRM_ERROR("failed to initialise vblank\n"); |
|---|
| 771 | 853 | goto vblank_fail; |
|---|
| .. | .. |
|---|
| 774 | 856 | |
|---|
| 775 | 857 | drm_mode_config_reset(drm); |
|---|
| 776 | 858 | |
|---|
| 777 | | - ret = drm_fb_cma_fbdev_init(drm, 32, 0); |
|---|
| 778 | | - if (ret) |
|---|
| 779 | | - goto fbdev_fail; |
|---|
| 780 | | - |
|---|
| 781 | 859 | drm_kms_helper_poll_init(drm); |
|---|
| 782 | 860 | |
|---|
| 783 | 861 | ret = drm_dev_register(drm, 0); |
|---|
| 784 | 862 | if (ret) |
|---|
| 785 | 863 | goto register_fail; |
|---|
| 786 | 864 | |
|---|
| 865 | + drm_fbdev_generic_setup(drm, 32); |
|---|
| 866 | + |
|---|
| 787 | 867 | return 0; |
|---|
| 788 | 868 | |
|---|
| 789 | 869 | register_fail: |
|---|
| 790 | | - drm_fb_cma_fbdev_fini(drm); |
|---|
| 791 | 870 | drm_kms_helper_poll_fini(drm); |
|---|
| 792 | | -fbdev_fail: |
|---|
| 793 | 871 | pm_runtime_get_sync(dev); |
|---|
| 794 | 872 | vblank_fail: |
|---|
| 795 | 873 | malidp_se_irq_fini(hwdev); |
|---|
| .. | .. |
|---|
| 801 | 879 | bind_fail: |
|---|
| 802 | 880 | of_node_put(malidp->crtc.port); |
|---|
| 803 | 881 | malidp->crtc.port = NULL; |
|---|
| 804 | | -init_fail: |
|---|
| 805 | | - malidp_fini_sysfs(dev); |
|---|
| 806 | 882 | malidp_fini(drm); |
|---|
| 807 | 883 | query_hw_fail: |
|---|
| 808 | 884 | pm_runtime_put(dev); |
|---|
| .. | .. |
|---|
| 826 | 902 | struct malidp_hw_device *hwdev = malidp->dev; |
|---|
| 827 | 903 | |
|---|
| 828 | 904 | drm_dev_unregister(drm); |
|---|
| 829 | | - drm_fb_cma_fbdev_fini(drm); |
|---|
| 830 | 905 | drm_kms_helper_poll_fini(drm); |
|---|
| 831 | 906 | pm_runtime_get_sync(dev); |
|---|
| 832 | | - drm_crtc_vblank_off(&malidp->crtc); |
|---|
| 907 | + drm_atomic_helper_shutdown(drm); |
|---|
| 833 | 908 | malidp_se_irq_fini(hwdev); |
|---|
| 834 | 909 | malidp_de_irq_fini(hwdev); |
|---|
| 835 | 910 | drm->irq_enabled = false; |
|---|
| 836 | | - drm_atomic_helper_shutdown(drm); |
|---|
| 837 | 911 | component_unbind_all(dev, drm); |
|---|
| 838 | 912 | of_node_put(malidp->crtc.port); |
|---|
| 839 | 913 | malidp->crtc.port = NULL; |
|---|
| 840 | | - malidp_fini_sysfs(dev); |
|---|
| 841 | 914 | malidp_fini(drm); |
|---|
| 842 | 915 | pm_runtime_put(dev); |
|---|
| 843 | 916 | if (pm_runtime_enabled(dev)) |
|---|
| .. | .. |
|---|
| 933 | 1006 | .name = "mali-dp", |
|---|
| 934 | 1007 | .pm = &malidp_pm_ops, |
|---|
| 935 | 1008 | .of_match_table = malidp_drm_of_match, |
|---|
| 1009 | + .dev_groups = mali_dp_groups, |
|---|
| 936 | 1010 | }, |
|---|
| 937 | 1011 | }; |
|---|
| 938 | 1012 | |
|---|