1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012-2013 Avionic Design GmbH 4 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 5 * 6 * Based on the KMS/FB DMA helpers 7 * Copyright (C) 2012 Analog Devices Inc. 8 */ 9 10 #include <linux/console.h> 11 12 #include <drm/drm_fourcc.h> 13 #include <drm/drm_framebuffer.h> 14 #include <drm/drm_gem_framebuffer_helper.h> 15 #include <drm/drm_modeset_helper.h> 16 #include <drm/drm_print.h> 17 18 #include "drm.h" 19 #include "gem.h" 20 21 struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, 22 unsigned int index) 23 { 24 return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index)); 25 } 26 27 bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer) 28 { 29 struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0); 30 31 if (bo->flags & TEGRA_BO_BOTTOM_UP) 32 return true; 33 34 return false; 35 } 36 37 int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, 38 struct tegra_bo_tiling *tiling) 39 { 40 uint64_t modifier = framebuffer->modifier; 41 42 if (fourcc_mod_is_vendor(modifier, NVIDIA)) { 43 if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0) 44 tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA; 45 else 46 tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_GPU; 47 48 modifier &= ~DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT; 49 } 50 51 switch (modifier) { 52 case DRM_FORMAT_MOD_LINEAR: 53 tiling->mode = TEGRA_BO_TILING_MODE_PITCH; 54 tiling->value = 0; 55 break; 56 57 case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: 58 tiling->mode = TEGRA_BO_TILING_MODE_TILED; 59 tiling->value = 0; 60 break; 61 62 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): 63 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 64 tiling->value = 0; 65 break; 66 67 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): 68 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 69 tiling->value = 1; 70 break; 71 72 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): 73 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 74 tiling->value = 2; 75 break; 76 77 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): 78 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 79 tiling->value = 3; 80 break; 81 82 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): 83 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 84 tiling->value = 4; 85 break; 86 87 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): 88 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 89 tiling->value = 5; 90 break; 91 92 default: 93 DRM_DEBUG_KMS("unknown format modifier: %llx\n", modifier); 94 return -EINVAL; 95 } 96 97 return 0; 98 } 99 100 static const struct drm_framebuffer_funcs tegra_fb_funcs = { 101 .destroy = drm_gem_fb_destroy, 102 .create_handle = drm_gem_fb_create_handle, 103 }; 104 105 struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm, 106 const struct drm_format_info *info, 107 const struct drm_mode_fb_cmd2 *mode_cmd, 108 struct tegra_bo **planes, 109 unsigned int num_planes) 110 { 111 struct drm_framebuffer *fb; 112 unsigned int i; 113 int err; 114 115 fb = kzalloc(sizeof(*fb), GFP_KERNEL); 116 if (!fb) 117 return ERR_PTR(-ENOMEM); 118 119 drm_helper_mode_fill_fb_struct(drm, fb, info, mode_cmd); 120 121 for (i = 0; i < fb->format->num_planes; i++) 122 fb->obj[i] = &planes[i]->gem; 123 124 err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs); 125 if (err < 0) { 126 dev_err(drm->dev, "failed to initialize framebuffer: %d\n", 127 err); 128 kfree(fb); 129 return ERR_PTR(err); 130 } 131 132 return fb; 133 } 134 135 struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, 136 struct drm_file *file, 137 const struct drm_format_info *info, 138 const struct drm_mode_fb_cmd2 *cmd) 139 { 140 struct tegra_bo *planes[4]; 141 struct drm_gem_object *gem; 142 struct drm_framebuffer *fb; 143 unsigned int i; 144 int err; 145 146 for (i = 0; i < info->num_planes; i++) { 147 unsigned int width = cmd->width / (i ? info->hsub : 1); 148 unsigned int height = cmd->height / (i ? info->vsub : 1); 149 unsigned int size, bpp; 150 151 gem = drm_gem_object_lookup(file, cmd->handles[i]); 152 if (!gem) { 153 err = -ENXIO; 154 goto unreference; 155 } 156 157 bpp = info->cpp[i]; 158 159 size = (height - 1) * cmd->pitches[i] + 160 width * bpp + cmd->offsets[i]; 161 162 if (gem->size < size) { 163 err = -EINVAL; 164 drm_gem_object_put(gem); 165 goto unreference; 166 } 167 168 planes[i] = to_tegra_bo(gem); 169 } 170 171 fb = tegra_fb_alloc(drm, info, cmd, planes, i); 172 if (IS_ERR(fb)) { 173 err = PTR_ERR(fb); 174 goto unreference; 175 } 176 177 return fb; 178 179 unreference: 180 while (i--) 181 drm_gem_object_put(&planes[i]->gem); 182 183 return ERR_PTR(err); 184 } 185