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