| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2012 Avionic Design GmbH |
|---|
| 3 | 4 | * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 7 | | - * published by the Free Software Foundation. |
|---|
| 8 | 5 | */ |
|---|
| 9 | 6 | |
|---|
| 10 | 7 | #include <linux/clk.h> |
|---|
| 11 | 8 | #include <linux/debugfs.h> |
|---|
| 9 | +#include <linux/delay.h> |
|---|
| 12 | 10 | #include <linux/iommu.h> |
|---|
| 11 | +#include <linux/module.h> |
|---|
| 13 | 12 | #include <linux/of_device.h> |
|---|
| 14 | 13 | #include <linux/pm_runtime.h> |
|---|
| 15 | 14 | #include <linux/reset.h> |
|---|
| 16 | 15 | |
|---|
| 17 | 16 | #include <soc/tegra/pmc.h> |
|---|
| 17 | + |
|---|
| 18 | +#include <drm/drm_atomic.h> |
|---|
| 19 | +#include <drm/drm_atomic_helper.h> |
|---|
| 20 | +#include <drm/drm_debugfs.h> |
|---|
| 21 | +#include <drm/drm_fourcc.h> |
|---|
| 22 | +#include <drm/drm_plane_helper.h> |
|---|
| 23 | +#include <drm/drm_vblank.h> |
|---|
| 18 | 24 | |
|---|
| 19 | 25 | #include "dc.h" |
|---|
| 20 | 26 | #include "drm.h" |
|---|
| .. | .. |
|---|
| 22 | 28 | #include "hub.h" |
|---|
| 23 | 29 | #include "plane.h" |
|---|
| 24 | 30 | |
|---|
| 25 | | -#include <drm/drm_atomic.h> |
|---|
| 26 | | -#include <drm/drm_atomic_helper.h> |
|---|
| 27 | | -#include <drm/drm_plane_helper.h> |
|---|
| 31 | +static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, |
|---|
| 32 | + struct drm_crtc_state *state); |
|---|
| 28 | 33 | |
|---|
| 29 | 34 | static void tegra_dc_stats_reset(struct tegra_dc_stats *stats) |
|---|
| 30 | 35 | { |
|---|
| .. | .. |
|---|
| 130 | 135 | |
|---|
| 131 | 136 | default: |
|---|
| 132 | 137 | WARN_ON_ONCE(1); |
|---|
| 133 | | - /* fallthrough */ |
|---|
| 138 | + fallthrough; |
|---|
| 134 | 139 | case 4: |
|---|
| 135 | 140 | max = 4; |
|---|
| 136 | 141 | break; |
|---|
| .. | .. |
|---|
| 363 | 368 | h_size = window->src.w * bpp; |
|---|
| 364 | 369 | v_size = window->src.h; |
|---|
| 365 | 370 | |
|---|
| 371 | + if (window->reflect_x) |
|---|
| 372 | + h_offset += (window->src.w - 1) * bpp; |
|---|
| 373 | + |
|---|
| 374 | + if (window->reflect_y) |
|---|
| 375 | + v_offset += window->src.h - 1; |
|---|
| 376 | + |
|---|
| 366 | 377 | value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size); |
|---|
| 367 | 378 | tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE); |
|---|
| 368 | 379 | |
|---|
| .. | .. |
|---|
| 398 | 409 | } else { |
|---|
| 399 | 410 | tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE); |
|---|
| 400 | 411 | } |
|---|
| 401 | | - |
|---|
| 402 | | - if (window->bottom_up) |
|---|
| 403 | | - v_offset += window->src.h - 1; |
|---|
| 404 | 412 | |
|---|
| 405 | 413 | tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET); |
|---|
| 406 | 414 | tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET); |
|---|
| .. | .. |
|---|
| 465 | 473 | value |= COLOR_EXPAND; |
|---|
| 466 | 474 | } |
|---|
| 467 | 475 | |
|---|
| 468 | | - if (window->bottom_up) |
|---|
| 476 | + if (window->reflect_x) |
|---|
| 477 | + value |= H_DIRECTION; |
|---|
| 478 | + |
|---|
| 479 | + if (window->reflect_y) |
|---|
| 469 | 480 | value |= V_DIRECTION; |
|---|
| 470 | 481 | |
|---|
| 471 | 482 | if (tegra_plane_use_horizontal_filtering(plane, window)) { |
|---|
| .. | .. |
|---|
| 596 | 607 | struct drm_plane_state *state) |
|---|
| 597 | 608 | { |
|---|
| 598 | 609 | struct tegra_plane_state *plane_state = to_tegra_plane_state(state); |
|---|
| 599 | | - unsigned int rotation = DRM_MODE_ROTATE_0 | DRM_MODE_REFLECT_Y; |
|---|
| 610 | + unsigned int supported_rotation = DRM_MODE_ROTATE_0 | |
|---|
| 611 | + DRM_MODE_REFLECT_X | |
|---|
| 612 | + DRM_MODE_REFLECT_Y; |
|---|
| 613 | + unsigned int rotation = state->rotation; |
|---|
| 600 | 614 | struct tegra_bo_tiling *tiling = &plane_state->tiling; |
|---|
| 601 | 615 | struct tegra_plane *tegra = to_tegra_plane(plane); |
|---|
| 602 | 616 | struct tegra_dc *dc = to_tegra_dc(state->crtc); |
|---|
| .. | .. |
|---|
| 634 | 648 | return -EINVAL; |
|---|
| 635 | 649 | } |
|---|
| 636 | 650 | |
|---|
| 637 | | - rotation = drm_rotation_simplify(state->rotation, rotation); |
|---|
| 651 | + /* |
|---|
| 652 | + * Older userspace used custom BO flag in order to specify the Y |
|---|
| 653 | + * reflection, while modern userspace uses the generic DRM rotation |
|---|
| 654 | + * property in order to achieve the same result. The legacy BO flag |
|---|
| 655 | + * duplicates the DRM rotation property when both are set. |
|---|
| 656 | + */ |
|---|
| 657 | + if (tegra_fb_is_bottom_up(state->fb)) |
|---|
| 658 | + rotation |= DRM_MODE_REFLECT_Y; |
|---|
| 659 | + |
|---|
| 660 | + rotation = drm_rotation_simplify(rotation, supported_rotation); |
|---|
| 661 | + |
|---|
| 662 | + if (rotation & DRM_MODE_REFLECT_X) |
|---|
| 663 | + plane_state->reflect_x = true; |
|---|
| 664 | + else |
|---|
| 665 | + plane_state->reflect_x = false; |
|---|
| 638 | 666 | |
|---|
| 639 | 667 | if (rotation & DRM_MODE_REFLECT_Y) |
|---|
| 640 | | - plane_state->bottom_up = true; |
|---|
| 668 | + plane_state->reflect_y = true; |
|---|
| 641 | 669 | else |
|---|
| 642 | | - plane_state->bottom_up = false; |
|---|
| 670 | + plane_state->reflect_y = false; |
|---|
| 643 | 671 | |
|---|
| 644 | 672 | /* |
|---|
| 645 | 673 | * Tegra doesn't support different strides for U and V planes so we |
|---|
| .. | .. |
|---|
| 701 | 729 | window.dst.w = drm_rect_width(&plane->state->dst); |
|---|
| 702 | 730 | window.dst.h = drm_rect_height(&plane->state->dst); |
|---|
| 703 | 731 | window.bits_per_pixel = fb->format->cpp[0] * 8; |
|---|
| 704 | | - window.bottom_up = tegra_fb_is_bottom_up(fb) || state->bottom_up; |
|---|
| 732 | + window.reflect_x = state->reflect_x; |
|---|
| 733 | + window.reflect_y = state->reflect_y; |
|---|
| 705 | 734 | |
|---|
| 706 | 735 | /* copy from state */ |
|---|
| 707 | 736 | window.zpos = plane->state->normalized_zpos; |
|---|
| .. | .. |
|---|
| 710 | 739 | window.swap = state->swap; |
|---|
| 711 | 740 | |
|---|
| 712 | 741 | for (i = 0; i < fb->format->num_planes; i++) { |
|---|
| 713 | | - struct tegra_bo *bo = tegra_fb_get_plane(fb, i); |
|---|
| 714 | | - |
|---|
| 715 | | - window.base[i] = bo->paddr + fb->offsets[i]; |
|---|
| 742 | + window.base[i] = state->iova[i] + fb->offsets[i]; |
|---|
| 716 | 743 | |
|---|
| 717 | 744 | /* |
|---|
| 718 | 745 | * Tegra uses a shared stride for UV planes. Framebuffers are |
|---|
| .. | .. |
|---|
| 727 | 754 | } |
|---|
| 728 | 755 | |
|---|
| 729 | 756 | static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { |
|---|
| 757 | + .prepare_fb = tegra_plane_prepare_fb, |
|---|
| 758 | + .cleanup_fb = tegra_plane_cleanup_fb, |
|---|
| 730 | 759 | .atomic_check = tegra_plane_atomic_check, |
|---|
| 731 | 760 | .atomic_disable = tegra_plane_atomic_disable, |
|---|
| 732 | 761 | .atomic_update = tegra_plane_atomic_update, |
|---|
| .. | .. |
|---|
| 787 | 816 | err = drm_plane_create_rotation_property(&plane->base, |
|---|
| 788 | 817 | DRM_MODE_ROTATE_0, |
|---|
| 789 | 818 | DRM_MODE_ROTATE_0 | |
|---|
| 819 | + DRM_MODE_ROTATE_180 | |
|---|
| 820 | + DRM_MODE_REFLECT_X | |
|---|
| 790 | 821 | DRM_MODE_REFLECT_Y); |
|---|
| 791 | 822 | if (err < 0) |
|---|
| 792 | 823 | dev_err(dc->dev, "failed to create rotation property: %d\n", |
|---|
| .. | .. |
|---|
| 832 | 863 | static void tegra_cursor_atomic_update(struct drm_plane *plane, |
|---|
| 833 | 864 | struct drm_plane_state *old_state) |
|---|
| 834 | 865 | { |
|---|
| 835 | | - struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0); |
|---|
| 866 | + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); |
|---|
| 836 | 867 | struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); |
|---|
| 837 | | - struct drm_plane_state *state = plane->state; |
|---|
| 838 | 868 | u32 value = CURSOR_CLIP_DISPLAY; |
|---|
| 839 | 869 | |
|---|
| 840 | 870 | /* rien ne va plus */ |
|---|
| 841 | 871 | if (!plane->state->crtc || !plane->state->fb) |
|---|
| 842 | 872 | return; |
|---|
| 843 | 873 | |
|---|
| 844 | | - switch (state->crtc_w) { |
|---|
| 874 | + switch (plane->state->crtc_w) { |
|---|
| 845 | 875 | case 32: |
|---|
| 846 | 876 | value |= CURSOR_SIZE_32x32; |
|---|
| 847 | 877 | break; |
|---|
| .. | .. |
|---|
| 859 | 889 | break; |
|---|
| 860 | 890 | |
|---|
| 861 | 891 | default: |
|---|
| 862 | | - WARN(1, "cursor size %ux%u not supported\n", state->crtc_w, |
|---|
| 863 | | - state->crtc_h); |
|---|
| 892 | + WARN(1, "cursor size %ux%u not supported\n", |
|---|
| 893 | + plane->state->crtc_w, plane->state->crtc_h); |
|---|
| 864 | 894 | return; |
|---|
| 865 | 895 | } |
|---|
| 866 | 896 | |
|---|
| 867 | | - value |= (bo->paddr >> 10) & 0x3fffff; |
|---|
| 897 | + value |= (state->iova[0] >> 10) & 0x3fffff; |
|---|
| 868 | 898 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR); |
|---|
| 869 | 899 | |
|---|
| 870 | 900 | #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT |
|---|
| 871 | | - value = (bo->paddr >> 32) & 0x3; |
|---|
| 901 | + value = (state->iova[0] >> 32) & 0x3; |
|---|
| 872 | 902 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI); |
|---|
| 873 | 903 | #endif |
|---|
| 874 | 904 | |
|---|
| .. | .. |
|---|
| 887 | 917 | tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); |
|---|
| 888 | 918 | |
|---|
| 889 | 919 | /* position the cursor */ |
|---|
| 890 | | - value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff); |
|---|
| 920 | + value = (plane->state->crtc_y & 0x3fff) << 16 | |
|---|
| 921 | + (plane->state->crtc_x & 0x3fff); |
|---|
| 891 | 922 | tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); |
|---|
| 892 | 923 | } |
|---|
| 893 | 924 | |
|---|
| .. | .. |
|---|
| 909 | 940 | } |
|---|
| 910 | 941 | |
|---|
| 911 | 942 | static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { |
|---|
| 943 | + .prepare_fb = tegra_plane_prepare_fb, |
|---|
| 944 | + .cleanup_fb = tegra_plane_cleanup_fb, |
|---|
| 912 | 945 | .atomic_check = tegra_cursor_atomic_check, |
|---|
| 913 | 946 | .atomic_update = tegra_cursor_atomic_update, |
|---|
| 914 | 947 | .atomic_disable = tegra_cursor_atomic_disable, |
|---|
| 948 | +}; |
|---|
| 949 | + |
|---|
| 950 | +static const uint64_t linear_modifiers[] = { |
|---|
| 951 | + DRM_FORMAT_MOD_LINEAR, |
|---|
| 952 | + DRM_FORMAT_MOD_INVALID |
|---|
| 915 | 953 | }; |
|---|
| 916 | 954 | |
|---|
| 917 | 955 | static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, |
|---|
| .. | .. |
|---|
| 942 | 980 | |
|---|
| 943 | 981 | err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, |
|---|
| 944 | 982 | &tegra_plane_funcs, formats, |
|---|
| 945 | | - num_formats, NULL, |
|---|
| 983 | + num_formats, linear_modifiers, |
|---|
| 946 | 984 | DRM_PLANE_TYPE_CURSOR, NULL); |
|---|
| 947 | 985 | if (err < 0) { |
|---|
| 948 | 986 | kfree(plane); |
|---|
| .. | .. |
|---|
| 950 | 988 | } |
|---|
| 951 | 989 | |
|---|
| 952 | 990 | drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); |
|---|
| 991 | + drm_plane_create_zpos_immutable_property(&plane->base, 255); |
|---|
| 953 | 992 | |
|---|
| 954 | 993 | return &plane->base; |
|---|
| 955 | 994 | } |
|---|
| .. | .. |
|---|
| 1060 | 1099 | |
|---|
| 1061 | 1100 | err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, |
|---|
| 1062 | 1101 | &tegra_plane_funcs, formats, |
|---|
| 1063 | | - num_formats, NULL, type, NULL); |
|---|
| 1102 | + num_formats, linear_modifiers, |
|---|
| 1103 | + type, NULL); |
|---|
| 1064 | 1104 | if (err < 0) { |
|---|
| 1065 | 1105 | kfree(plane); |
|---|
| 1066 | 1106 | return ERR_PTR(err); |
|---|
| .. | .. |
|---|
| 1072 | 1112 | err = drm_plane_create_rotation_property(&plane->base, |
|---|
| 1073 | 1113 | DRM_MODE_ROTATE_0, |
|---|
| 1074 | 1114 | DRM_MODE_ROTATE_0 | |
|---|
| 1115 | + DRM_MODE_ROTATE_180 | |
|---|
| 1116 | + DRM_MODE_REFLECT_X | |
|---|
| 1075 | 1117 | DRM_MODE_REFLECT_Y); |
|---|
| 1076 | 1118 | if (err < 0) |
|---|
| 1077 | 1119 | dev_err(dc->dev, "failed to create rotation property: %d\n", |
|---|
| .. | .. |
|---|
| 1155 | 1197 | |
|---|
| 1156 | 1198 | static void tegra_crtc_reset(struct drm_crtc *crtc) |
|---|
| 1157 | 1199 | { |
|---|
| 1158 | | - struct tegra_dc_state *state; |
|---|
| 1200 | + struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL); |
|---|
| 1159 | 1201 | |
|---|
| 1160 | 1202 | if (crtc->state) |
|---|
| 1161 | | - __drm_atomic_helper_crtc_destroy_state(crtc->state); |
|---|
| 1203 | + tegra_crtc_atomic_destroy_state(crtc, crtc->state); |
|---|
| 1162 | 1204 | |
|---|
| 1163 | | - kfree(crtc->state); |
|---|
| 1164 | | - crtc->state = NULL; |
|---|
| 1165 | | - |
|---|
| 1166 | | - state = kzalloc(sizeof(*state), GFP_KERNEL); |
|---|
| 1167 | | - if (state) { |
|---|
| 1168 | | - crtc->state = &state->base; |
|---|
| 1169 | | - crtc->state->crtc = crtc; |
|---|
| 1170 | | - } |
|---|
| 1171 | | - |
|---|
| 1172 | | - drm_crtc_vblank_reset(crtc); |
|---|
| 1205 | + __drm_atomic_helper_crtc_reset(crtc, &state->base); |
|---|
| 1173 | 1206 | } |
|---|
| 1174 | 1207 | |
|---|
| 1175 | 1208 | static struct drm_crtc_state * |
|---|
| .. | .. |
|---|
| 1497 | 1530 | struct drm_minor *minor = crtc->dev->primary; |
|---|
| 1498 | 1531 | struct dentry *root; |
|---|
| 1499 | 1532 | struct tegra_dc *dc = to_tegra_dc(crtc); |
|---|
| 1500 | | - int err; |
|---|
| 1501 | 1533 | |
|---|
| 1502 | 1534 | #ifdef CONFIG_DEBUG_FS |
|---|
| 1503 | 1535 | root = crtc->debugfs_entry; |
|---|
| .. | .. |
|---|
| 1513 | 1545 | for (i = 0; i < count; i++) |
|---|
| 1514 | 1546 | dc->debugfs_files[i].data = dc; |
|---|
| 1515 | 1547 | |
|---|
| 1516 | | - err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor); |
|---|
| 1517 | | - if (err < 0) |
|---|
| 1518 | | - goto free; |
|---|
| 1548 | + drm_debugfs_create_files(dc->debugfs_files, count, root, minor); |
|---|
| 1519 | 1549 | |
|---|
| 1520 | 1550 | return 0; |
|---|
| 1521 | | - |
|---|
| 1522 | | -free: |
|---|
| 1523 | | - kfree(dc->debugfs_files); |
|---|
| 1524 | | - dc->debugfs_files = NULL; |
|---|
| 1525 | | - |
|---|
| 1526 | | - return err; |
|---|
| 1527 | 1551 | } |
|---|
| 1528 | 1552 | |
|---|
| 1529 | 1553 | static void tegra_dc_early_unregister(struct drm_crtc *crtc) |
|---|
| .. | .. |
|---|
| 1728 | 1752 | { |
|---|
| 1729 | 1753 | struct tegra_dc *dc = to_tegra_dc(crtc); |
|---|
| 1730 | 1754 | u32 value; |
|---|
| 1755 | + int err; |
|---|
| 1731 | 1756 | |
|---|
| 1732 | 1757 | if (!tegra_dc_idle(dc)) { |
|---|
| 1733 | 1758 | tegra_dc_stop(dc); |
|---|
| .. | .. |
|---|
| 1774 | 1799 | |
|---|
| 1775 | 1800 | spin_unlock_irq(&crtc->dev->event_lock); |
|---|
| 1776 | 1801 | |
|---|
| 1777 | | - pm_runtime_put_sync(dc->dev); |
|---|
| 1802 | + err = host1x_client_suspend(&dc->client); |
|---|
| 1803 | + if (err < 0) |
|---|
| 1804 | + dev_err(dc->dev, "failed to suspend: %d\n", err); |
|---|
| 1778 | 1805 | } |
|---|
| 1779 | 1806 | |
|---|
| 1780 | 1807 | static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, |
|---|
| .. | .. |
|---|
| 1784 | 1811 | struct tegra_dc_state *state = to_dc_state(crtc->state); |
|---|
| 1785 | 1812 | struct tegra_dc *dc = to_tegra_dc(crtc); |
|---|
| 1786 | 1813 | u32 value; |
|---|
| 1814 | + int err; |
|---|
| 1787 | 1815 | |
|---|
| 1788 | | - pm_runtime_get_sync(dc->dev); |
|---|
| 1816 | + err = host1x_client_resume(&dc->client); |
|---|
| 1817 | + if (err < 0) { |
|---|
| 1818 | + dev_err(dc->dev, "failed to resume: %d\n", err); |
|---|
| 1819 | + return; |
|---|
| 1820 | + } |
|---|
| 1789 | 1821 | |
|---|
| 1790 | 1822 | /* initialize display controller */ |
|---|
| 1791 | 1823 | if (dc->syncpt) { |
|---|
| .. | .. |
|---|
| 1978 | 2010 | return IRQ_HANDLED; |
|---|
| 1979 | 2011 | } |
|---|
| 1980 | 2012 | |
|---|
| 2013 | +static bool tegra_dc_has_window_groups(struct tegra_dc *dc) |
|---|
| 2014 | +{ |
|---|
| 2015 | + unsigned int i; |
|---|
| 2016 | + |
|---|
| 2017 | + if (!dc->soc->wgrps) |
|---|
| 2018 | + return true; |
|---|
| 2019 | + |
|---|
| 2020 | + for (i = 0; i < dc->soc->num_wgrps; i++) { |
|---|
| 2021 | + const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i]; |
|---|
| 2022 | + |
|---|
| 2023 | + if (wgrp->dc == dc->pipe && wgrp->num_windows > 0) |
|---|
| 2024 | + return true; |
|---|
| 2025 | + } |
|---|
| 2026 | + |
|---|
| 2027 | + return false; |
|---|
| 2028 | +} |
|---|
| 2029 | + |
|---|
| 1981 | 2030 | static int tegra_dc_init(struct host1x_client *client) |
|---|
| 1982 | 2031 | { |
|---|
| 1983 | | - struct drm_device *drm = dev_get_drvdata(client->parent); |
|---|
| 2032 | + struct drm_device *drm = dev_get_drvdata(client->host); |
|---|
| 1984 | 2033 | unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED; |
|---|
| 1985 | 2034 | struct tegra_dc *dc = host1x_client_to_dc(client); |
|---|
| 1986 | 2035 | struct tegra_drm *tegra = drm->dev_private; |
|---|
| .. | .. |
|---|
| 1988 | 2037 | struct drm_plane *cursor = NULL; |
|---|
| 1989 | 2038 | int err; |
|---|
| 1990 | 2039 | |
|---|
| 2040 | + /* |
|---|
| 2041 | + * XXX do not register DCs with no window groups because we cannot |
|---|
| 2042 | + * assign a primary plane to them, which in turn will cause KMS to |
|---|
| 2043 | + * crash. |
|---|
| 2044 | + */ |
|---|
| 2045 | + if (!tegra_dc_has_window_groups(dc)) |
|---|
| 2046 | + return 0; |
|---|
| 2047 | + |
|---|
| 2048 | + /* |
|---|
| 2049 | + * Set the display hub as the host1x client parent for the display |
|---|
| 2050 | + * controller. This is needed for the runtime reference counting that |
|---|
| 2051 | + * ensures the display hub is always powered when any of the display |
|---|
| 2052 | + * controllers are. |
|---|
| 2053 | + */ |
|---|
| 2054 | + if (dc->soc->has_nvdisplay) |
|---|
| 2055 | + client->parent = &tegra->hub->client; |
|---|
| 2056 | + |
|---|
| 1991 | 2057 | dc->syncpt = host1x_syncpt_request(client, flags); |
|---|
| 1992 | 2058 | if (!dc->syncpt) |
|---|
| 1993 | 2059 | dev_warn(dc->dev, "failed to allocate syncpoint\n"); |
|---|
| 1994 | 2060 | |
|---|
| 1995 | | - dc->group = host1x_client_iommu_attach(client, true); |
|---|
| 1996 | | - if (IS_ERR(dc->group)) { |
|---|
| 1997 | | - err = PTR_ERR(dc->group); |
|---|
| 2061 | + err = host1x_client_iommu_attach(client); |
|---|
| 2062 | + if (err < 0 && err != -ENODEV) { |
|---|
| 1998 | 2063 | dev_err(client->dev, "failed to attach to domain: %d\n", err); |
|---|
| 1999 | 2064 | return err; |
|---|
| 2000 | 2065 | } |
|---|
| .. | .. |
|---|
| 2052 | 2117 | goto cleanup; |
|---|
| 2053 | 2118 | } |
|---|
| 2054 | 2119 | |
|---|
| 2120 | + /* |
|---|
| 2121 | + * Inherit the DMA parameters (such as maximum segment size) from the |
|---|
| 2122 | + * parent host1x device. |
|---|
| 2123 | + */ |
|---|
| 2124 | + client->dev->dma_parms = client->host->dma_parms; |
|---|
| 2125 | + |
|---|
| 2055 | 2126 | return 0; |
|---|
| 2056 | 2127 | |
|---|
| 2057 | 2128 | cleanup: |
|---|
| .. | .. |
|---|
| 2061 | 2132 | if (!IS_ERR(primary)) |
|---|
| 2062 | 2133 | drm_plane_cleanup(primary); |
|---|
| 2063 | 2134 | |
|---|
| 2064 | | - host1x_client_iommu_detach(client, dc->group); |
|---|
| 2135 | + host1x_client_iommu_detach(client); |
|---|
| 2065 | 2136 | host1x_syncpt_free(dc->syncpt); |
|---|
| 2066 | 2137 | |
|---|
| 2067 | 2138 | return err; |
|---|
| .. | .. |
|---|
| 2072 | 2143 | struct tegra_dc *dc = host1x_client_to_dc(client); |
|---|
| 2073 | 2144 | int err; |
|---|
| 2074 | 2145 | |
|---|
| 2146 | + if (!tegra_dc_has_window_groups(dc)) |
|---|
| 2147 | + return 0; |
|---|
| 2148 | + |
|---|
| 2149 | + /* avoid a dangling pointer just in case this disappears */ |
|---|
| 2150 | + client->dev->dma_parms = NULL; |
|---|
| 2151 | + |
|---|
| 2075 | 2152 | devm_free_irq(dc->dev, dc->irq, dc); |
|---|
| 2076 | 2153 | |
|---|
| 2077 | 2154 | err = tegra_dc_rgb_exit(dc); |
|---|
| .. | .. |
|---|
| 2080 | 2157 | return err; |
|---|
| 2081 | 2158 | } |
|---|
| 2082 | 2159 | |
|---|
| 2083 | | - host1x_client_iommu_detach(client, dc->group); |
|---|
| 2160 | + host1x_client_iommu_detach(client); |
|---|
| 2084 | 2161 | host1x_syncpt_free(dc->syncpt); |
|---|
| 2085 | 2162 | |
|---|
| 2086 | 2163 | return 0; |
|---|
| 2087 | 2164 | } |
|---|
| 2088 | 2165 | |
|---|
| 2166 | +static int tegra_dc_runtime_suspend(struct host1x_client *client) |
|---|
| 2167 | +{ |
|---|
| 2168 | + struct tegra_dc *dc = host1x_client_to_dc(client); |
|---|
| 2169 | + struct device *dev = client->dev; |
|---|
| 2170 | + int err; |
|---|
| 2171 | + |
|---|
| 2172 | + err = reset_control_assert(dc->rst); |
|---|
| 2173 | + if (err < 0) { |
|---|
| 2174 | + dev_err(dev, "failed to assert reset: %d\n", err); |
|---|
| 2175 | + return err; |
|---|
| 2176 | + } |
|---|
| 2177 | + |
|---|
| 2178 | + if (dc->soc->has_powergate) |
|---|
| 2179 | + tegra_powergate_power_off(dc->powergate); |
|---|
| 2180 | + |
|---|
| 2181 | + clk_disable_unprepare(dc->clk); |
|---|
| 2182 | + pm_runtime_put_sync(dev); |
|---|
| 2183 | + |
|---|
| 2184 | + return 0; |
|---|
| 2185 | +} |
|---|
| 2186 | + |
|---|
| 2187 | +static int tegra_dc_runtime_resume(struct host1x_client *client) |
|---|
| 2188 | +{ |
|---|
| 2189 | + struct tegra_dc *dc = host1x_client_to_dc(client); |
|---|
| 2190 | + struct device *dev = client->dev; |
|---|
| 2191 | + int err; |
|---|
| 2192 | + |
|---|
| 2193 | + err = pm_runtime_resume_and_get(dev); |
|---|
| 2194 | + if (err < 0) { |
|---|
| 2195 | + dev_err(dev, "failed to get runtime PM: %d\n", err); |
|---|
| 2196 | + return err; |
|---|
| 2197 | + } |
|---|
| 2198 | + |
|---|
| 2199 | + if (dc->soc->has_powergate) { |
|---|
| 2200 | + err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, |
|---|
| 2201 | + dc->rst); |
|---|
| 2202 | + if (err < 0) { |
|---|
| 2203 | + dev_err(dev, "failed to power partition: %d\n", err); |
|---|
| 2204 | + goto put_rpm; |
|---|
| 2205 | + } |
|---|
| 2206 | + } else { |
|---|
| 2207 | + err = clk_prepare_enable(dc->clk); |
|---|
| 2208 | + if (err < 0) { |
|---|
| 2209 | + dev_err(dev, "failed to enable clock: %d\n", err); |
|---|
| 2210 | + goto put_rpm; |
|---|
| 2211 | + } |
|---|
| 2212 | + |
|---|
| 2213 | + err = reset_control_deassert(dc->rst); |
|---|
| 2214 | + if (err < 0) { |
|---|
| 2215 | + dev_err(dev, "failed to deassert reset: %d\n", err); |
|---|
| 2216 | + goto disable_clk; |
|---|
| 2217 | + } |
|---|
| 2218 | + } |
|---|
| 2219 | + |
|---|
| 2220 | + return 0; |
|---|
| 2221 | + |
|---|
| 2222 | +disable_clk: |
|---|
| 2223 | + clk_disable_unprepare(dc->clk); |
|---|
| 2224 | +put_rpm: |
|---|
| 2225 | + pm_runtime_put_sync(dev); |
|---|
| 2226 | + return err; |
|---|
| 2227 | +} |
|---|
| 2228 | + |
|---|
| 2089 | 2229 | static const struct host1x_client_ops dc_client_ops = { |
|---|
| 2090 | 2230 | .init = tegra_dc_init, |
|---|
| 2091 | 2231 | .exit = tegra_dc_exit, |
|---|
| 2232 | + .suspend = tegra_dc_runtime_suspend, |
|---|
| 2233 | + .resume = tegra_dc_runtime_resume, |
|---|
| 2092 | 2234 | }; |
|---|
| 2093 | 2235 | |
|---|
| 2094 | 2236 | static const struct tegra_dc_soc_info tegra20_dc_soc_info = { |
|---|
| .. | .. |
|---|
| 2234 | 2376 | .num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps), |
|---|
| 2235 | 2377 | }; |
|---|
| 2236 | 2378 | |
|---|
| 2379 | +static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = { |
|---|
| 2380 | + { |
|---|
| 2381 | + .index = 0, |
|---|
| 2382 | + .dc = 0, |
|---|
| 2383 | + .windows = (const unsigned int[]) { 0 }, |
|---|
| 2384 | + .num_windows = 1, |
|---|
| 2385 | + }, { |
|---|
| 2386 | + .index = 1, |
|---|
| 2387 | + .dc = 1, |
|---|
| 2388 | + .windows = (const unsigned int[]) { 1 }, |
|---|
| 2389 | + .num_windows = 1, |
|---|
| 2390 | + }, { |
|---|
| 2391 | + .index = 2, |
|---|
| 2392 | + .dc = 1, |
|---|
| 2393 | + .windows = (const unsigned int[]) { 2 }, |
|---|
| 2394 | + .num_windows = 1, |
|---|
| 2395 | + }, { |
|---|
| 2396 | + .index = 3, |
|---|
| 2397 | + .dc = 2, |
|---|
| 2398 | + .windows = (const unsigned int[]) { 3 }, |
|---|
| 2399 | + .num_windows = 1, |
|---|
| 2400 | + }, { |
|---|
| 2401 | + .index = 4, |
|---|
| 2402 | + .dc = 2, |
|---|
| 2403 | + .windows = (const unsigned int[]) { 4 }, |
|---|
| 2404 | + .num_windows = 1, |
|---|
| 2405 | + }, { |
|---|
| 2406 | + .index = 5, |
|---|
| 2407 | + .dc = 2, |
|---|
| 2408 | + .windows = (const unsigned int[]) { 5 }, |
|---|
| 2409 | + .num_windows = 1, |
|---|
| 2410 | + }, |
|---|
| 2411 | +}; |
|---|
| 2412 | + |
|---|
| 2413 | +static const struct tegra_dc_soc_info tegra194_dc_soc_info = { |
|---|
| 2414 | + .supports_background_color = true, |
|---|
| 2415 | + .supports_interlacing = true, |
|---|
| 2416 | + .supports_cursor = true, |
|---|
| 2417 | + .supports_block_linear = true, |
|---|
| 2418 | + .has_legacy_blending = false, |
|---|
| 2419 | + .pitch_align = 64, |
|---|
| 2420 | + .has_powergate = false, |
|---|
| 2421 | + .coupled_pm = false, |
|---|
| 2422 | + .has_nvdisplay = true, |
|---|
| 2423 | + .wgrps = tegra194_dc_wgrps, |
|---|
| 2424 | + .num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps), |
|---|
| 2425 | +}; |
|---|
| 2426 | + |
|---|
| 2237 | 2427 | static const struct of_device_id tegra_dc_of_match[] = { |
|---|
| 2238 | 2428 | { |
|---|
| 2429 | + .compatible = "nvidia,tegra194-dc", |
|---|
| 2430 | + .data = &tegra194_dc_soc_info, |
|---|
| 2431 | + }, { |
|---|
| 2239 | 2432 | .compatible = "nvidia,tegra186-dc", |
|---|
| 2240 | 2433 | .data = &tegra186_dc_soc_info, |
|---|
| 2241 | 2434 | }, { |
|---|
| .. | .. |
|---|
| 2296 | 2489 | return 0; |
|---|
| 2297 | 2490 | } |
|---|
| 2298 | 2491 | |
|---|
| 2299 | | -static int tegra_dc_match_by_pipe(struct device *dev, void *data) |
|---|
| 2492 | +static int tegra_dc_match_by_pipe(struct device *dev, const void *data) |
|---|
| 2300 | 2493 | { |
|---|
| 2301 | 2494 | struct tegra_dc *dc = dev_get_drvdata(dev); |
|---|
| 2302 | | - unsigned int pipe = (unsigned long)data; |
|---|
| 2495 | + unsigned int pipe = (unsigned long)(void *)data; |
|---|
| 2303 | 2496 | |
|---|
| 2304 | 2497 | return dc->pipe == pipe; |
|---|
| 2305 | 2498 | } |
|---|
| .. | .. |
|---|
| 2312 | 2505 | * POWER_CONTROL registers during CRTC enabling. |
|---|
| 2313 | 2506 | */ |
|---|
| 2314 | 2507 | if (dc->soc->coupled_pm && dc->pipe == 1) { |
|---|
| 2315 | | - u32 flags = DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_CONSUMER; |
|---|
| 2316 | | - struct device_link *link; |
|---|
| 2317 | | - struct device *partner; |
|---|
| 2508 | + struct device *companion; |
|---|
| 2509 | + struct tegra_dc *parent; |
|---|
| 2318 | 2510 | |
|---|
| 2319 | | - partner = driver_find_device(dc->dev->driver, NULL, NULL, |
|---|
| 2320 | | - tegra_dc_match_by_pipe); |
|---|
| 2321 | | - if (!partner) |
|---|
| 2511 | + companion = driver_find_device(dc->dev->driver, NULL, (const void *)0, |
|---|
| 2512 | + tegra_dc_match_by_pipe); |
|---|
| 2513 | + if (!companion) |
|---|
| 2322 | 2514 | return -EPROBE_DEFER; |
|---|
| 2323 | 2515 | |
|---|
| 2324 | | - link = device_link_add(dc->dev, partner, flags); |
|---|
| 2325 | | - if (!link) { |
|---|
| 2326 | | - dev_err(dc->dev, "failed to link controllers\n"); |
|---|
| 2327 | | - return -EINVAL; |
|---|
| 2328 | | - } |
|---|
| 2516 | + parent = dev_get_drvdata(companion); |
|---|
| 2517 | + dc->client.parent = &parent->client; |
|---|
| 2329 | 2518 | |
|---|
| 2330 | | - dev_dbg(dc->dev, "coupled to %s\n", dev_name(partner)); |
|---|
| 2519 | + dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion)); |
|---|
| 2331 | 2520 | } |
|---|
| 2332 | 2521 | |
|---|
| 2333 | 2522 | return 0; |
|---|
| .. | .. |
|---|
| 2335 | 2524 | |
|---|
| 2336 | 2525 | static int tegra_dc_probe(struct platform_device *pdev) |
|---|
| 2337 | 2526 | { |
|---|
| 2338 | | - struct resource *regs; |
|---|
| 2339 | 2527 | struct tegra_dc *dc; |
|---|
| 2340 | 2528 | int err; |
|---|
| 2341 | 2529 | |
|---|
| .. | .. |
|---|
| 2392 | 2580 | tegra_powergate_power_off(dc->powergate); |
|---|
| 2393 | 2581 | } |
|---|
| 2394 | 2582 | |
|---|
| 2395 | | - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 2396 | | - dc->regs = devm_ioremap_resource(&pdev->dev, regs); |
|---|
| 2583 | + dc->regs = devm_platform_ioremap_resource(pdev, 0); |
|---|
| 2397 | 2584 | if (IS_ERR(dc->regs)) |
|---|
| 2398 | 2585 | return PTR_ERR(dc->regs); |
|---|
| 2399 | 2586 | |
|---|
| 2400 | 2587 | dc->irq = platform_get_irq(pdev, 0); |
|---|
| 2401 | | - if (dc->irq < 0) { |
|---|
| 2402 | | - dev_err(&pdev->dev, "failed to get IRQ\n"); |
|---|
| 2588 | + if (dc->irq < 0) |
|---|
| 2403 | 2589 | return -ENXIO; |
|---|
| 2404 | | - } |
|---|
| 2405 | 2590 | |
|---|
| 2406 | 2591 | err = tegra_dc_rgb_probe(dc); |
|---|
| 2407 | 2592 | if (err < 0 && err != -ENODEV) { |
|---|
| 2408 | | - dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); |
|---|
| 2593 | + const char *level = KERN_ERR; |
|---|
| 2594 | + |
|---|
| 2595 | + if (err == -EPROBE_DEFER) |
|---|
| 2596 | + level = KERN_DEBUG; |
|---|
| 2597 | + |
|---|
| 2598 | + dev_printk(level, dc->dev, "failed to probe RGB output: %d\n", |
|---|
| 2599 | + err); |
|---|
| 2409 | 2600 | return err; |
|---|
| 2410 | 2601 | } |
|---|
| 2411 | 2602 | |
|---|
| .. | .. |
|---|
| 2420 | 2611 | if (err < 0) { |
|---|
| 2421 | 2612 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", |
|---|
| 2422 | 2613 | err); |
|---|
| 2423 | | - return err; |
|---|
| 2614 | + goto disable_pm; |
|---|
| 2424 | 2615 | } |
|---|
| 2425 | 2616 | |
|---|
| 2426 | 2617 | return 0; |
|---|
| 2618 | + |
|---|
| 2619 | +disable_pm: |
|---|
| 2620 | + pm_runtime_disable(&pdev->dev); |
|---|
| 2621 | + tegra_dc_rgb_remove(dc); |
|---|
| 2622 | + |
|---|
| 2623 | + return err; |
|---|
| 2427 | 2624 | } |
|---|
| 2428 | 2625 | |
|---|
| 2429 | 2626 | static int tegra_dc_remove(struct platform_device *pdev) |
|---|
| .. | .. |
|---|
| 2449 | 2646 | return 0; |
|---|
| 2450 | 2647 | } |
|---|
| 2451 | 2648 | |
|---|
| 2452 | | -#ifdef CONFIG_PM |
|---|
| 2453 | | -static int tegra_dc_suspend(struct device *dev) |
|---|
| 2454 | | -{ |
|---|
| 2455 | | - struct tegra_dc *dc = dev_get_drvdata(dev); |
|---|
| 2456 | | - int err; |
|---|
| 2457 | | - |
|---|
| 2458 | | - err = reset_control_assert(dc->rst); |
|---|
| 2459 | | - if (err < 0) { |
|---|
| 2460 | | - dev_err(dev, "failed to assert reset: %d\n", err); |
|---|
| 2461 | | - return err; |
|---|
| 2462 | | - } |
|---|
| 2463 | | - |
|---|
| 2464 | | - if (dc->soc->has_powergate) |
|---|
| 2465 | | - tegra_powergate_power_off(dc->powergate); |
|---|
| 2466 | | - |
|---|
| 2467 | | - clk_disable_unprepare(dc->clk); |
|---|
| 2468 | | - |
|---|
| 2469 | | - return 0; |
|---|
| 2470 | | -} |
|---|
| 2471 | | - |
|---|
| 2472 | | -static int tegra_dc_resume(struct device *dev) |
|---|
| 2473 | | -{ |
|---|
| 2474 | | - struct tegra_dc *dc = dev_get_drvdata(dev); |
|---|
| 2475 | | - int err; |
|---|
| 2476 | | - |
|---|
| 2477 | | - if (dc->soc->has_powergate) { |
|---|
| 2478 | | - err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk, |
|---|
| 2479 | | - dc->rst); |
|---|
| 2480 | | - if (err < 0) { |
|---|
| 2481 | | - dev_err(dev, "failed to power partition: %d\n", err); |
|---|
| 2482 | | - return err; |
|---|
| 2483 | | - } |
|---|
| 2484 | | - } else { |
|---|
| 2485 | | - err = clk_prepare_enable(dc->clk); |
|---|
| 2486 | | - if (err < 0) { |
|---|
| 2487 | | - dev_err(dev, "failed to enable clock: %d\n", err); |
|---|
| 2488 | | - return err; |
|---|
| 2489 | | - } |
|---|
| 2490 | | - |
|---|
| 2491 | | - err = reset_control_deassert(dc->rst); |
|---|
| 2492 | | - if (err < 0) { |
|---|
| 2493 | | - dev_err(dev, "failed to deassert reset: %d\n", err); |
|---|
| 2494 | | - return err; |
|---|
| 2495 | | - } |
|---|
| 2496 | | - } |
|---|
| 2497 | | - |
|---|
| 2498 | | - return 0; |
|---|
| 2499 | | -} |
|---|
| 2500 | | -#endif |
|---|
| 2501 | | - |
|---|
| 2502 | | -static const struct dev_pm_ops tegra_dc_pm_ops = { |
|---|
| 2503 | | - SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL) |
|---|
| 2504 | | -}; |
|---|
| 2505 | | - |
|---|
| 2506 | 2649 | struct platform_driver tegra_dc_driver = { |
|---|
| 2507 | 2650 | .driver = { |
|---|
| 2508 | 2651 | .name = "tegra-dc", |
|---|
| 2509 | 2652 | .of_match_table = tegra_dc_of_match, |
|---|
| 2510 | | - .pm = &tegra_dc_pm_ops, |
|---|
| 2511 | 2653 | }, |
|---|
| 2512 | 2654 | .probe = tegra_dc_probe, |
|---|
| 2513 | 2655 | .remove = tegra_dc_remove, |
|---|