fb.c (9414B)
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 CMA 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_gem_framebuffer_helper.h> 14#include <drm/drm_modeset_helper.h> 15 16#include "drm.h" 17#include "gem.h" 18 19#ifdef CONFIG_DRM_FBDEV_EMULATION 20static inline struct tegra_fbdev *to_tegra_fbdev(struct drm_fb_helper *helper) 21{ 22 return container_of(helper, struct tegra_fbdev, base); 23} 24#endif 25 26struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, 27 unsigned int index) 28{ 29 return to_tegra_bo(drm_gem_fb_get_obj(framebuffer, index)); 30} 31 32bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer) 33{ 34 struct tegra_bo *bo = tegra_fb_get_plane(framebuffer, 0); 35 36 if (bo->flags & TEGRA_BO_BOTTOM_UP) 37 return true; 38 39 return false; 40} 41 42int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, 43 struct tegra_bo_tiling *tiling) 44{ 45 uint64_t modifier = framebuffer->modifier; 46 47 if (fourcc_mod_is_vendor(modifier, NVIDIA)) { 48 if ((modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) == 0) 49 tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_TEGRA; 50 else 51 tiling->sector_layout = TEGRA_BO_SECTOR_LAYOUT_GPU; 52 53 modifier &= ~DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT; 54 } 55 56 switch (modifier) { 57 case DRM_FORMAT_MOD_LINEAR: 58 tiling->mode = TEGRA_BO_TILING_MODE_PITCH; 59 tiling->value = 0; 60 break; 61 62 case DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED: 63 tiling->mode = TEGRA_BO_TILING_MODE_TILED; 64 tiling->value = 0; 65 break; 66 67 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0): 68 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 69 tiling->value = 0; 70 break; 71 72 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1): 73 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 74 tiling->value = 1; 75 break; 76 77 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2): 78 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 79 tiling->value = 2; 80 break; 81 82 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3): 83 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 84 tiling->value = 3; 85 break; 86 87 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4): 88 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 89 tiling->value = 4; 90 break; 91 92 case DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5): 93 tiling->mode = TEGRA_BO_TILING_MODE_BLOCK; 94 tiling->value = 5; 95 break; 96 97 default: 98 DRM_DEBUG_KMS("unknown format modifier: %llx\n", modifier); 99 return -EINVAL; 100 } 101 102 return 0; 103} 104 105static const struct drm_framebuffer_funcs tegra_fb_funcs = { 106 .destroy = drm_gem_fb_destroy, 107 .create_handle = drm_gem_fb_create_handle, 108}; 109 110static struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm, 111 const struct drm_mode_fb_cmd2 *mode_cmd, 112 struct tegra_bo **planes, 113 unsigned int num_planes) 114{ 115 struct drm_framebuffer *fb; 116 unsigned int i; 117 int err; 118 119 fb = kzalloc(sizeof(*fb), GFP_KERNEL); 120 if (!fb) 121 return ERR_PTR(-ENOMEM); 122 123 drm_helper_mode_fill_fb_struct(drm, fb, mode_cmd); 124 125 for (i = 0; i < fb->format->num_planes; i++) 126 fb->obj[i] = &planes[i]->gem; 127 128 err = drm_framebuffer_init(drm, fb, &tegra_fb_funcs); 129 if (err < 0) { 130 dev_err(drm->dev, "failed to initialize framebuffer: %d\n", 131 err); 132 kfree(fb); 133 return ERR_PTR(err); 134 } 135 136 return fb; 137} 138 139struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, 140 struct drm_file *file, 141 const struct drm_mode_fb_cmd2 *cmd) 142{ 143 const struct drm_format_info *info = drm_get_format_info(drm, cmd); 144 struct tegra_bo *planes[4]; 145 struct drm_gem_object *gem; 146 struct drm_framebuffer *fb; 147 unsigned int i; 148 int err; 149 150 for (i = 0; i < info->num_planes; i++) { 151 unsigned int width = cmd->width / (i ? info->hsub : 1); 152 unsigned int height = cmd->height / (i ? info->vsub : 1); 153 unsigned int size, bpp; 154 155 gem = drm_gem_object_lookup(file, cmd->handles[i]); 156 if (!gem) { 157 err = -ENXIO; 158 goto unreference; 159 } 160 161 bpp = info->cpp[i]; 162 163 size = (height - 1) * cmd->pitches[i] + 164 width * bpp + cmd->offsets[i]; 165 166 if (gem->size < size) { 167 err = -EINVAL; 168 goto unreference; 169 } 170 171 planes[i] = to_tegra_bo(gem); 172 } 173 174 fb = tegra_fb_alloc(drm, cmd, planes, i); 175 if (IS_ERR(fb)) { 176 err = PTR_ERR(fb); 177 goto unreference; 178 } 179 180 return fb; 181 182unreference: 183 while (i--) 184 drm_gem_object_put(&planes[i]->gem); 185 186 return ERR_PTR(err); 187} 188 189#ifdef CONFIG_DRM_FBDEV_EMULATION 190static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 191{ 192 struct drm_fb_helper *helper = info->par; 193 struct tegra_bo *bo; 194 int err; 195 196 bo = tegra_fb_get_plane(helper->fb, 0); 197 198 err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma); 199 if (err < 0) 200 return err; 201 202 return __tegra_gem_mmap(&bo->gem, vma); 203} 204 205static const struct fb_ops tegra_fb_ops = { 206 .owner = THIS_MODULE, 207 DRM_FB_HELPER_DEFAULT_OPS, 208 .fb_fillrect = drm_fb_helper_sys_fillrect, 209 .fb_copyarea = drm_fb_helper_sys_copyarea, 210 .fb_imageblit = drm_fb_helper_sys_imageblit, 211 .fb_mmap = tegra_fb_mmap, 212}; 213 214static int tegra_fbdev_probe(struct drm_fb_helper *helper, 215 struct drm_fb_helper_surface_size *sizes) 216{ 217 struct tegra_fbdev *fbdev = to_tegra_fbdev(helper); 218 struct tegra_drm *tegra = helper->dev->dev_private; 219 struct drm_device *drm = helper->dev; 220 struct drm_mode_fb_cmd2 cmd = { 0 }; 221 unsigned int bytes_per_pixel; 222 struct drm_framebuffer *fb; 223 unsigned long offset; 224 struct fb_info *info; 225 struct tegra_bo *bo; 226 size_t size; 227 int err; 228 229 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 230 231 cmd.width = sizes->surface_width; 232 cmd.height = sizes->surface_height; 233 cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, 234 tegra->pitch_align); 235 236 cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 237 sizes->surface_depth); 238 239 size = cmd.pitches[0] * cmd.height; 240 241 bo = tegra_bo_create(drm, size, 0); 242 if (IS_ERR(bo)) 243 return PTR_ERR(bo); 244 245 info = drm_fb_helper_alloc_fbi(helper); 246 if (IS_ERR(info)) { 247 dev_err(drm->dev, "failed to allocate framebuffer info\n"); 248 drm_gem_object_put(&bo->gem); 249 return PTR_ERR(info); 250 } 251 252 fbdev->fb = tegra_fb_alloc(drm, &cmd, &bo, 1); 253 if (IS_ERR(fbdev->fb)) { 254 err = PTR_ERR(fbdev->fb); 255 dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n", 256 err); 257 drm_gem_object_put(&bo->gem); 258 return PTR_ERR(fbdev->fb); 259 } 260 261 fb = fbdev->fb; 262 helper->fb = fb; 263 helper->fbdev = info; 264 265 info->fbops = &tegra_fb_ops; 266 267 drm_fb_helper_fill_info(info, helper, sizes); 268 269 offset = info->var.xoffset * bytes_per_pixel + 270 info->var.yoffset * fb->pitches[0]; 271 272 if (bo->pages) { 273 bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP, 274 pgprot_writecombine(PAGE_KERNEL)); 275 if (!bo->vaddr) { 276 dev_err(drm->dev, "failed to vmap() framebuffer\n"); 277 err = -ENOMEM; 278 goto destroy; 279 } 280 } 281 282 drm->mode_config.fb_base = (resource_size_t)bo->iova; 283 info->screen_base = (void __iomem *)bo->vaddr + offset; 284 info->screen_size = size; 285 info->fix.smem_start = (unsigned long)(bo->iova + offset); 286 info->fix.smem_len = size; 287 288 return 0; 289 290destroy: 291 drm_framebuffer_remove(fb); 292 return err; 293} 294 295static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = { 296 .fb_probe = tegra_fbdev_probe, 297}; 298 299static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm) 300{ 301 struct tegra_fbdev *fbdev; 302 303 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); 304 if (!fbdev) { 305 dev_err(drm->dev, "failed to allocate DRM fbdev\n"); 306 return ERR_PTR(-ENOMEM); 307 } 308 309 drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); 310 311 return fbdev; 312} 313 314static void tegra_fbdev_free(struct tegra_fbdev *fbdev) 315{ 316 kfree(fbdev); 317} 318 319static int tegra_fbdev_init(struct tegra_fbdev *fbdev, 320 unsigned int preferred_bpp, 321 unsigned int num_crtc, 322 unsigned int max_connectors) 323{ 324 struct drm_device *drm = fbdev->base.dev; 325 int err; 326 327 err = drm_fb_helper_init(drm, &fbdev->base); 328 if (err < 0) { 329 dev_err(drm->dev, "failed to initialize DRM FB helper: %d\n", 330 err); 331 return err; 332 } 333 334 err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp); 335 if (err < 0) { 336 dev_err(drm->dev, "failed to set initial configuration: %d\n", 337 err); 338 goto fini; 339 } 340 341 return 0; 342 343fini: 344 drm_fb_helper_fini(&fbdev->base); 345 return err; 346} 347 348static void tegra_fbdev_exit(struct tegra_fbdev *fbdev) 349{ 350 drm_fb_helper_unregister_fbi(&fbdev->base); 351 352 if (fbdev->fb) { 353 struct tegra_bo *bo = tegra_fb_get_plane(fbdev->fb, 0); 354 355 /* Undo the special mapping we made in fbdev probe. */ 356 if (bo && bo->pages) { 357 vunmap(bo->vaddr); 358 bo->vaddr = NULL; 359 } 360 361 drm_framebuffer_remove(fbdev->fb); 362 } 363 364 drm_fb_helper_fini(&fbdev->base); 365 tegra_fbdev_free(fbdev); 366} 367#endif 368 369int tegra_drm_fb_prepare(struct drm_device *drm) 370{ 371#ifdef CONFIG_DRM_FBDEV_EMULATION 372 struct tegra_drm *tegra = drm->dev_private; 373 374 tegra->fbdev = tegra_fbdev_create(drm); 375 if (IS_ERR(tegra->fbdev)) 376 return PTR_ERR(tegra->fbdev); 377#endif 378 379 return 0; 380} 381 382void tegra_drm_fb_free(struct drm_device *drm) 383{ 384#ifdef CONFIG_DRM_FBDEV_EMULATION 385 struct tegra_drm *tegra = drm->dev_private; 386 387 tegra_fbdev_free(tegra->fbdev); 388#endif 389} 390 391int tegra_drm_fb_init(struct drm_device *drm) 392{ 393#ifdef CONFIG_DRM_FBDEV_EMULATION 394 struct tegra_drm *tegra = drm->dev_private; 395 int err; 396 397 err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc, 398 drm->mode_config.num_connector); 399 if (err < 0) 400 return err; 401#endif 402 403 return 0; 404} 405 406void tegra_drm_fb_exit(struct drm_device *drm) 407{ 408#ifdef CONFIG_DRM_FBDEV_EMULATION 409 struct tegra_drm *tegra = drm->dev_private; 410 411 tegra_fbdev_exit(tegra->fbdev); 412#endif 413}