Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/tegra: Support DMA API for display controllers

If a display controller is not attached to an explicit IOMMU domain,
which usually means that it's connected to an IOMMU domain controlled by
the DMA API, make sure to map the framebuffer to the display controller
address space. This allows us to transparently handle setups where the
display controller is attached to an IOMMU or setups where it isn't. It
also allows the driver to work with a DMA API that is backed by an
IOMMU.

Signed-off-by: Thierry Reding <treding@nvidia.com>

+120 -6
+5 -3
drivers/gpu/drm/tegra/dc.c
··· 715 715 window.swap = state->swap; 716 716 717 717 for (i = 0; i < fb->format->num_planes; i++) { 718 - struct tegra_bo *bo = tegra_fb_get_plane(fb, i); 719 - 720 - window.base[i] = bo->iova + fb->offsets[i]; 718 + window.base[i] = state->iova[i] + fb->offsets[i]; 721 719 722 720 /* 723 721 * Tegra uses a shared stride for UV planes. Framebuffers are ··· 730 732 } 731 733 732 734 static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = { 735 + .prepare_fb = tegra_plane_prepare_fb, 736 + .cleanup_fb = tegra_plane_cleanup_fb, 733 737 .atomic_check = tegra_plane_atomic_check, 734 738 .atomic_disable = tegra_plane_atomic_disable, 735 739 .atomic_update = tegra_plane_atomic_update, ··· 914 914 } 915 915 916 916 static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { 917 + .prepare_fb = tegra_plane_prepare_fb, 918 + .cleanup_fb = tegra_plane_cleanup_fb, 917 919 .atomic_check = tegra_cursor_atomic_check, 918 920 .atomic_update = tegra_cursor_atomic_update, 919 921 .atomic_disable = tegra_cursor_atomic_disable,
+3 -3
drivers/gpu/drm/tegra/hub.c
··· 413 413 unsigned int zpos = plane->state->normalized_zpos; 414 414 struct drm_framebuffer *fb = plane->state->fb; 415 415 struct tegra_plane *p = to_tegra_plane(plane); 416 - struct tegra_bo *bo; 417 416 dma_addr_t base; 418 417 u32 value; 419 418 ··· 455 456 /* disable compression */ 456 457 tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL); 457 458 458 - bo = tegra_fb_get_plane(fb, 0); 459 - base = bo->iova; 459 + base = state->iova[0] + fb->offsets[0]; 460 460 461 461 tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH); 462 462 tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS); ··· 519 521 } 520 522 521 523 static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = { 524 + .prepare_fb = tegra_plane_prepare_fb, 525 + .cleanup_fb = tegra_plane_cleanup_fb, 522 526 .atomic_check = tegra_shared_plane_atomic_check, 523 527 .atomic_update = tegra_shared_plane_atomic_update, 524 528 .atomic_disable = tegra_shared_plane_atomic_disable,
+104
drivers/gpu/drm/tegra/plane.c
··· 6 6 #include <drm/drm_atomic.h> 7 7 #include <drm/drm_atomic_helper.h> 8 8 #include <drm/drm_fourcc.h> 9 + #include <drm/drm_gem_framebuffer_helper.h> 9 10 #include <drm/drm_plane_helper.h> 10 11 11 12 #include "dc.h" ··· 24 23 { 25 24 struct tegra_plane *p = to_tegra_plane(plane); 26 25 struct tegra_plane_state *state; 26 + unsigned int i; 27 27 28 28 if (plane->state) 29 29 __drm_atomic_helper_plane_destroy_state(plane->state); ··· 38 36 plane->state->plane = plane; 39 37 plane->state->zpos = p->index; 40 38 plane->state->normalized_zpos = p->index; 39 + 40 + for (i = 0; i < 3; i++) 41 + state->iova[i] = DMA_MAPPING_ERROR; 41 42 } 42 43 } 43 44 ··· 64 59 65 60 for (i = 0; i < 2; i++) 66 61 copy->blending[i] = state->blending[i]; 62 + 63 + for (i = 0; i < 3; i++) { 64 + copy->iova[i] = DMA_MAPPING_ERROR; 65 + copy->sgt[i] = NULL; 66 + } 67 67 68 68 return &copy->base; 69 69 } ··· 104 94 .atomic_destroy_state = tegra_plane_atomic_destroy_state, 105 95 .format_mod_supported = tegra_plane_format_mod_supported, 106 96 }; 97 + 98 + static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state) 99 + { 100 + unsigned int i; 101 + int err; 102 + 103 + for (i = 0; i < state->base.fb->format->num_planes; i++) { 104 + struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i); 105 + 106 + if (!dc->client.group) { 107 + struct sg_table *sgt; 108 + 109 + sgt = host1x_bo_pin(dc->dev, &bo->base, NULL); 110 + if (IS_ERR(sgt)) { 111 + err = PTR_ERR(sgt); 112 + goto unpin; 113 + } 114 + 115 + err = dma_map_sg(dc->dev, sgt->sgl, sgt->nents, 116 + DMA_TO_DEVICE); 117 + if (err == 0) { 118 + err = -ENOMEM; 119 + goto unpin; 120 + } 121 + 122 + state->iova[i] = sg_dma_address(sgt->sgl); 123 + state->sgt[i] = sgt; 124 + } else { 125 + state->iova[i] = bo->iova; 126 + } 127 + } 128 + 129 + return 0; 130 + 131 + unpin: 132 + dev_err(dc->dev, "failed to map plane %u: %d\n", i, err); 133 + 134 + while (i--) { 135 + struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i); 136 + struct sg_table *sgt = state->sgt[i]; 137 + 138 + dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE); 139 + host1x_bo_unpin(dc->dev, &bo->base, sgt); 140 + 141 + state->iova[i] = DMA_MAPPING_ERROR; 142 + state->sgt[i] = NULL; 143 + } 144 + 145 + return err; 146 + } 147 + 148 + static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state) 149 + { 150 + unsigned int i; 151 + 152 + for (i = 0; i < state->base.fb->format->num_planes; i++) { 153 + struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i); 154 + 155 + if (!dc->client.group) { 156 + struct sg_table *sgt = state->sgt[i]; 157 + 158 + if (sgt) { 159 + dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents, 160 + DMA_TO_DEVICE); 161 + host1x_bo_unpin(dc->dev, &bo->base, sgt); 162 + } 163 + } 164 + 165 + state->iova[i] = DMA_MAPPING_ERROR; 166 + state->sgt[i] = NULL; 167 + } 168 + } 169 + 170 + int tegra_plane_prepare_fb(struct drm_plane *plane, 171 + struct drm_plane_state *state) 172 + { 173 + struct tegra_dc *dc = to_tegra_dc(state->crtc); 174 + 175 + if (!state->fb) 176 + return 0; 177 + 178 + drm_gem_fb_prepare_fb(plane, state); 179 + 180 + return tegra_dc_pin(dc, to_tegra_plane_state(state)); 181 + } 182 + 183 + void tegra_plane_cleanup_fb(struct drm_plane *plane, 184 + struct drm_plane_state *state) 185 + { 186 + struct tegra_dc *dc = to_tegra_dc(state->crtc); 187 + 188 + if (dc) 189 + tegra_dc_unpin(dc, to_tegra_plane_state(state)); 190 + } 107 191 108 192 int tegra_plane_state_add(struct tegra_plane *plane, 109 193 struct drm_plane_state *state)
+8
drivers/gpu/drm/tegra/plane.h
··· 39 39 struct tegra_plane_state { 40 40 struct drm_plane_state base; 41 41 42 + struct sg_table *sgt[3]; 43 + dma_addr_t iova[3]; 44 + 42 45 struct tegra_bo_tiling tiling; 43 46 u32 format; 44 47 u32 swap; ··· 63 60 } 64 61 65 62 extern const struct drm_plane_funcs tegra_plane_funcs; 63 + 64 + int tegra_plane_prepare_fb(struct drm_plane *plane, 65 + struct drm_plane_state *state); 66 + void tegra_plane_cleanup_fb(struct drm_plane *plane, 67 + struct drm_plane_state *state); 66 68 67 69 int tegra_plane_state_add(struct tegra_plane *plane, 68 70 struct drm_plane_state *state);