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 #include <linux/fb.h> 12 #include <linux/vmalloc.h> 13 14 #include <drm/drm_drv.h> 15 #include <drm/drm_crtc_helper.h> 16 #include <drm/drm_fb_helper.h> 17 #include <drm/drm_fourcc.h> 18 #include <drm/drm_framebuffer.h> 19 #include <drm/drm_gem_framebuffer_helper.h> 20 #include <drm/drm_modeset_helper.h> 21 22 #include "drm.h" 23 #include "gem.h" 24 25 static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 26 { 27 struct drm_fb_helper *helper = info->par; 28 struct tegra_bo *bo; 29 int err; 30 31 bo = tegra_fb_get_plane(helper->fb, 0); 32 33 err = drm_gem_mmap_obj(&bo->gem, bo->gem.size, vma); 34 if (err < 0) 35 return err; 36 37 return __tegra_gem_mmap(&bo->gem, vma); 38 } 39 40 static void tegra_fbdev_fb_destroy(struct fb_info *info) 41 { 42 struct drm_fb_helper *helper = info->par; 43 struct drm_framebuffer *fb = helper->fb; 44 struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); 45 46 drm_fb_helper_fini(helper); 47 48 /* Undo the special mapping we made in fbdev probe. */ 49 if (bo->pages) { 50 vunmap(bo->vaddr); 51 bo->vaddr = NULL; 52 } 53 drm_framebuffer_remove(fb); 54 55 drm_client_release(&helper->client); 56 } 57 58 static const struct fb_ops tegra_fb_ops = { 59 .owner = THIS_MODULE, 60 __FB_DEFAULT_DMAMEM_OPS_RDWR, 61 DRM_FB_HELPER_DEFAULT_OPS, 62 __FB_DEFAULT_DMAMEM_OPS_DRAW, 63 .fb_mmap = tegra_fb_mmap, 64 .fb_destroy = tegra_fbdev_fb_destroy, 65 }; 66 67 static const struct drm_fb_helper_funcs tegra_fbdev_helper_funcs = { 68 }; 69 70 int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, 71 struct drm_fb_helper_surface_size *sizes) 72 { 73 struct tegra_drm *tegra = helper->dev->dev_private; 74 struct drm_device *drm = helper->dev; 75 struct drm_mode_fb_cmd2 cmd = { 0 }; 76 struct fb_info *info = helper->info; 77 unsigned int bytes_per_pixel; 78 struct drm_framebuffer *fb; 79 unsigned long offset; 80 struct tegra_bo *bo; 81 size_t size; 82 int err; 83 84 bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); 85 86 cmd.width = sizes->surface_width; 87 cmd.height = sizes->surface_height; 88 cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel, 89 tegra->pitch_align); 90 91 cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 92 sizes->surface_depth); 93 94 size = cmd.pitches[0] * cmd.height; 95 96 bo = tegra_bo_create(drm, size, 0); 97 if (IS_ERR(bo)) 98 return PTR_ERR(bo); 99 100 fb = tegra_fb_alloc(drm, 101 drm_get_format_info(drm, cmd.pixel_format, cmd.modifier[0]), 102 &cmd, &bo, 1); 103 if (IS_ERR(fb)) { 104 err = PTR_ERR(fb); 105 dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n", 106 err); 107 drm_gem_object_put(&bo->gem); 108 return PTR_ERR(fb); 109 } 110 111 helper->funcs = &tegra_fbdev_helper_funcs; 112 helper->fb = fb; 113 helper->info = info; 114 115 info->fbops = &tegra_fb_ops; 116 117 drm_fb_helper_fill_info(info, helper, sizes); 118 119 offset = info->var.xoffset * bytes_per_pixel + 120 info->var.yoffset * fb->pitches[0]; 121 122 if (bo->pages) { 123 bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP, 124 pgprot_writecombine(PAGE_KERNEL)); 125 if (!bo->vaddr) { 126 dev_err(drm->dev, "failed to vmap() framebuffer\n"); 127 err = -ENOMEM; 128 goto destroy; 129 } 130 } 131 132 info->flags |= FBINFO_VIRTFB; 133 info->screen_buffer = bo->vaddr + offset; 134 info->screen_size = size; 135 info->fix.smem_start = (unsigned long)(bo->iova + offset); 136 info->fix.smem_len = size; 137 138 return 0; 139 140 destroy: 141 drm_framebuffer_remove(fb); 142 return err; 143 } 144