1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2013 Red Hat 4 * Author: Rob Clark <robdclark@gmail.com> 5 */ 6 7 #include <linux/fb.h> 8 9 #include <drm/drm_drv.h> 10 #include <drm/drm_crtc_helper.h> 11 #include <drm/drm_fb_helper.h> 12 #include <drm/drm_file.h> 13 #include <drm/drm_fourcc.h> 14 #include <drm/drm_framebuffer.h> 15 #include <drm/drm_prime.h> 16 17 #include "msm_drv.h" 18 #include "msm_gem.h" 19 #include "msm_kms.h" 20 21 static bool fbdev = true; 22 MODULE_PARM_DESC(fbdev, "Enable fbdev compat layer"); 23 module_param(fbdev, bool, 0600); 24 25 /* 26 * fbdev funcs, to implement legacy fbdev interface on top of drm driver 27 */ 28 29 FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(msm_fbdev, 30 drm_fb_helper_damage_range, 31 drm_fb_helper_damage_area) 32 33 static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) 34 { 35 struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par; 36 struct drm_gem_object *bo = msm_framebuffer_bo(helper->fb, 0); 37 38 return drm_gem_prime_mmap(bo, vma); 39 } 40 41 static void msm_fbdev_fb_destroy(struct fb_info *info) 42 { 43 struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par; 44 struct drm_gem_object *bo = msm_framebuffer_bo(helper->fb, 0); 45 46 DBG(); 47 48 drm_fb_helper_fini(helper); 49 50 msm_gem_put_vaddr(bo); 51 52 drm_client_buffer_delete(helper->buffer); 53 drm_client_release(&helper->client); 54 } 55 56 static const struct fb_ops msm_fb_ops = { 57 .owner = THIS_MODULE, 58 __FB_DEFAULT_DEFERRED_OPS_RDWR(msm_fbdev), 59 DRM_FB_HELPER_DEFAULT_OPS, 60 __FB_DEFAULT_DEFERRED_OPS_DRAW(msm_fbdev), 61 .fb_mmap = msm_fbdev_mmap, 62 .fb_destroy = msm_fbdev_fb_destroy, 63 }; 64 65 static int msm_fbdev_fb_dirty(struct drm_fb_helper *helper, 66 struct drm_clip_rect *clip) 67 { 68 struct drm_device *dev = helper->dev; 69 int ret; 70 71 /* Call damage handlers only if necessary */ 72 if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2)) 73 return 0; 74 75 if (helper->fb->funcs->dirty) { 76 ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1); 77 if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret)) 78 return ret; 79 } 80 81 return 0; 82 } 83 84 static const struct drm_fb_helper_funcs msm_fbdev_helper_funcs = { 85 .fb_dirty = msm_fbdev_fb_dirty, 86 }; 87 88 int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper, 89 struct drm_fb_helper_surface_size *sizes) 90 { 91 struct drm_client_dev *client = &helper->client; 92 struct drm_device *dev = client->dev; 93 struct drm_file *file = client->file; 94 struct msm_drm_private *priv = dev->dev_private; 95 struct fb_info *fbi = helper->info; 96 const struct drm_format_info *format; 97 u32 fourcc, pitch, handle; 98 u64 size; 99 struct drm_gem_object *bo; 100 struct drm_client_buffer *buffer; 101 uint64_t paddr; 102 int ret; 103 104 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width, 105 sizes->surface_height, sizes->surface_bpp, 106 sizes->fb_width, sizes->fb_height); 107 108 fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); 109 format = drm_get_format_info(dev, fourcc, DRM_FORMAT_MOD_LINEAR); 110 /* adreno needs pitch aligned to 32 pixels: */ 111 pitch = drm_format_info_min_pitch(format, 0, ALIGN(sizes->surface_width, 32)); 112 size = ALIGN(pitch * sizes->surface_height, PAGE_SIZE); 113 114 /* allocate backing bo */ 115 DBG("allocating %llu bytes for fb %d", size, dev->primary->index); 116 bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN, NULL); 117 if (IS_ERR(bo)) { 118 drm_warn(dev, "could not allocate stolen bo\n"); 119 /* try regular bo: */ 120 bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC, NULL); 121 if (IS_ERR(bo)) { 122 drm_err(dev, "failed to allocate buffer object\n"); 123 return PTR_ERR(bo); 124 } 125 } 126 127 msm_gem_object_set_name(bo, "stolenfb"); 128 129 ret = drm_gem_handle_create(file, bo, &handle); 130 if (ret) 131 goto err_drm_gem_object_put; 132 133 buffer = drm_client_buffer_create(client, sizes->surface_width, sizes->surface_height, 134 fourcc, handle, pitch); 135 if (IS_ERR(buffer)) { 136 ret = PTR_ERR(buffer); 137 goto err_drm_gem_handle_delete; 138 } 139 140 /* 141 * NOTE: if we can be guaranteed to be able to map buffer 142 * in panic (ie. lock-safe, etc) we could avoid pinning the 143 * buffer now: 144 */ 145 ret = msm_gem_get_and_pin_iova(bo, priv->kms->vm, &paddr); 146 if (ret) { 147 drm_err(dev, "failed to get buffer obj iova: %d\n", ret); 148 goto err_drm_client_buffer_delete; 149 } 150 151 DBG("fbi=%p, dev=%p", fbi, dev); 152 153 helper->funcs = &msm_fbdev_helper_funcs; 154 helper->buffer = buffer; 155 helper->fb = buffer->fb; 156 157 fbi->fbops = &msm_fb_ops; 158 159 drm_fb_helper_fill_info(fbi, helper, sizes); 160 161 fbi->screen_buffer = msm_gem_get_vaddr(bo); 162 if (IS_ERR(fbi->screen_buffer)) { 163 ret = PTR_ERR(fbi->screen_buffer); 164 goto err_msm_gem_unpin; 165 } 166 fbi->screen_size = bo->size; 167 fbi->fix.smem_start = paddr; 168 fbi->fix.smem_len = bo->size; 169 170 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); 171 DBG("allocated %dx%d fb", buffer->fb->width, buffer->fb->height); 172 173 /* The handle is only needed for creating the framebuffer. */ 174 drm_gem_handle_delete(file, handle); 175 176 /* The framebuffer still holds a reference on the GEM object. */ 177 drm_gem_object_put(bo); 178 179 return 0; 180 181 err_msm_gem_unpin: 182 msm_gem_unpin_iova(bo, priv->kms->vm); 183 msm_gem_vma_put(bo); 184 err_drm_client_buffer_delete: 185 drm_client_buffer_delete(buffer); 186 err_drm_gem_handle_delete: 187 drm_gem_handle_delete(file, handle); 188 err_drm_gem_object_put: 189 drm_gem_object_put(bo); 190 return ret; 191 } 192