.. | .. |
---|
| 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 | |
---|
.. | .. |
---|
2376 | 2564 | usleep_range(2000, 4000); |
---|
2377 | 2565 | |
---|
2378 | 2566 | err = reset_control_assert(dc->rst); |
---|
2379 | | - if (err < 0) |
---|
| 2567 | + if (err < 0) { |
---|
| 2568 | + clk_disable_unprepare(dc->clk); |
---|
2380 | 2569 | return err; |
---|
| 2570 | + } |
---|
2381 | 2571 | |
---|
2382 | 2572 | usleep_range(2000, 4000); |
---|
2383 | 2573 | |
---|
.. | .. |
---|
2392 | 2582 | tegra_powergate_power_off(dc->powergate); |
---|
2393 | 2583 | } |
---|
2394 | 2584 | |
---|
2395 | | - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
---|
2396 | | - dc->regs = devm_ioremap_resource(&pdev->dev, regs); |
---|
| 2585 | + dc->regs = devm_platform_ioremap_resource(pdev, 0); |
---|
2397 | 2586 | if (IS_ERR(dc->regs)) |
---|
2398 | 2587 | return PTR_ERR(dc->regs); |
---|
2399 | 2588 | |
---|
2400 | 2589 | dc->irq = platform_get_irq(pdev, 0); |
---|
2401 | | - if (dc->irq < 0) { |
---|
2402 | | - dev_err(&pdev->dev, "failed to get IRQ\n"); |
---|
| 2590 | + if (dc->irq < 0) |
---|
2403 | 2591 | return -ENXIO; |
---|
2404 | | - } |
---|
2405 | 2592 | |
---|
2406 | 2593 | err = tegra_dc_rgb_probe(dc); |
---|
2407 | 2594 | if (err < 0 && err != -ENODEV) { |
---|
2408 | | - dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err); |
---|
| 2595 | + const char *level = KERN_ERR; |
---|
| 2596 | + |
---|
| 2597 | + if (err == -EPROBE_DEFER) |
---|
| 2598 | + level = KERN_DEBUG; |
---|
| 2599 | + |
---|
| 2600 | + dev_printk(level, dc->dev, "failed to probe RGB output: %d\n", |
---|
| 2601 | + err); |
---|
2409 | 2602 | return err; |
---|
2410 | 2603 | } |
---|
2411 | 2604 | |
---|
.. | .. |
---|
2420 | 2613 | if (err < 0) { |
---|
2421 | 2614 | dev_err(&pdev->dev, "failed to register host1x client: %d\n", |
---|
2422 | 2615 | err); |
---|
2423 | | - return err; |
---|
| 2616 | + goto disable_pm; |
---|
2424 | 2617 | } |
---|
2425 | 2618 | |
---|
2426 | 2619 | return 0; |
---|
| 2620 | + |
---|
| 2621 | +disable_pm: |
---|
| 2622 | + pm_runtime_disable(&pdev->dev); |
---|
| 2623 | + tegra_dc_rgb_remove(dc); |
---|
| 2624 | + |
---|
| 2625 | + return err; |
---|
2427 | 2626 | } |
---|
2428 | 2627 | |
---|
2429 | 2628 | static int tegra_dc_remove(struct platform_device *pdev) |
---|
.. | .. |
---|
2449 | 2648 | return 0; |
---|
2450 | 2649 | } |
---|
2451 | 2650 | |
---|
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 | 2651 | struct platform_driver tegra_dc_driver = { |
---|
2507 | 2652 | .driver = { |
---|
2508 | 2653 | .name = "tegra-dc", |
---|
2509 | 2654 | .of_match_table = tegra_dc_of_match, |
---|
2510 | | - .pm = &tegra_dc_pm_ops, |
---|
2511 | 2655 | }, |
---|
2512 | 2656 | .probe = tegra_dc_probe, |
---|
2513 | 2657 | .remove = tegra_dc_remove, |
---|