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 <drm/drm_drv.h> 8 #include <drm/drm_crtc_helper.h> 9 #include <drm/drm_fb_helper.h> 10 #include <drm/drm_fourcc.h> 11 #include <drm/drm_framebuffer.h> 12 #include <drm/drm_prime.h> 13 14 #include "msm_drv.h" 15 #include "msm_gem.h" 16 #include "msm_kms.h" 17 18 static bool fbdev = true; 19 MODULE_PARM_DESC(fbdev, "Enable fbdev compat layer"); 20 module_param(fbdev, bool, 0600); 21 22 /* 23 * fbdev funcs, to implement legacy fbdev interface on top of drm driver 24 */ 25 26 static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) 27 { 28 struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par; 29 struct drm_gem_object *bo = msm_framebuffer_bo(helper->fb, 0); 30 31 return drm_gem_prime_mmap(bo, vma); 32 } 33 34 static void msm_fbdev_fb_destroy(struct fb_info *info) 35 { 36 struct drm_fb_helper *helper = (struct drm_fb_helper *)info->par; 37 struct drm_framebuffer *fb = helper->fb; 38 struct drm_gem_object *bo = msm_framebuffer_bo(fb, 0); 39 40 DBG(); 41 42 drm_fb_helper_fini(helper); 43 44 /* this will free the backing object */ 45 msm_gem_put_vaddr(bo); 46 drm_framebuffer_remove(fb); 47 48 drm_client_release(&helper->client); 49 drm_fb_helper_unprepare(helper); 50 kfree(helper); 51 } 52 53 static const struct fb_ops msm_fb_ops = { 54 .owner = THIS_MODULE, 55 DRM_FB_HELPER_DEFAULT_OPS, 56 57 /* Note: to properly handle manual update displays, we wrap the 58 * basic fbdev ops which write to the framebuffer 59 */ 60 .fb_read = drm_fb_helper_sys_read, 61 .fb_write = drm_fb_helper_sys_write, 62 .fb_fillrect = drm_fb_helper_sys_fillrect, 63 .fb_copyarea = drm_fb_helper_sys_copyarea, 64 .fb_imageblit = drm_fb_helper_sys_imageblit, 65 .fb_mmap = msm_fbdev_mmap, 66 .fb_destroy = msm_fbdev_fb_destroy, 67 }; 68 69 static int msm_fbdev_create(struct drm_fb_helper *helper, 70 struct drm_fb_helper_surface_size *sizes) 71 { 72 struct drm_device *dev = helper->dev; 73 struct msm_drm_private *priv = dev->dev_private; 74 struct drm_framebuffer *fb = NULL; 75 struct drm_gem_object *bo; 76 struct fb_info *fbi = NULL; 77 uint64_t paddr; 78 uint32_t format; 79 int ret, pitch; 80 81 format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); 82 83 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width, 84 sizes->surface_height, sizes->surface_bpp, 85 sizes->fb_width, sizes->fb_height); 86 87 pitch = align_pitch(sizes->surface_width, sizes->surface_bpp); 88 fb = msm_alloc_stolen_fb(dev, sizes->surface_width, 89 sizes->surface_height, pitch, format); 90 91 if (IS_ERR(fb)) { 92 DRM_DEV_ERROR(dev->dev, "failed to allocate fb\n"); 93 return PTR_ERR(fb); 94 } 95 96 bo = msm_framebuffer_bo(fb, 0); 97 98 /* 99 * NOTE: if we can be guaranteed to be able to map buffer 100 * in panic (ie. lock-safe, etc) we could avoid pinning the 101 * buffer now: 102 */ 103 ret = msm_gem_get_and_pin_iova(bo, priv->kms->aspace, &paddr); 104 if (ret) { 105 DRM_DEV_ERROR(dev->dev, "failed to get buffer obj iova: %d\n", ret); 106 goto fail; 107 } 108 109 fbi = drm_fb_helper_alloc_info(helper); 110 if (IS_ERR(fbi)) { 111 DRM_DEV_ERROR(dev->dev, "failed to allocate fb info\n"); 112 ret = PTR_ERR(fbi); 113 goto fail; 114 } 115 116 DBG("fbi=%p, dev=%p", fbi, dev); 117 118 helper->fb = fb; 119 120 fbi->fbops = &msm_fb_ops; 121 122 drm_fb_helper_fill_info(fbi, helper, sizes); 123 124 fbi->screen_base = msm_gem_get_vaddr(bo); 125 if (IS_ERR(fbi->screen_base)) { 126 ret = PTR_ERR(fbi->screen_base); 127 goto fail; 128 } 129 fbi->screen_size = bo->size; 130 fbi->fix.smem_start = paddr; 131 fbi->fix.smem_len = bo->size; 132 133 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); 134 DBG("allocated %dx%d fb", fb->width, fb->height); 135 136 return 0; 137 138 fail: 139 drm_framebuffer_remove(fb); 140 return ret; 141 } 142 143 static const struct drm_fb_helper_funcs msm_fb_helper_funcs = { 144 .fb_probe = msm_fbdev_create, 145 }; 146 147 /* 148 * struct drm_client 149 */ 150 151 static void msm_fbdev_client_unregister(struct drm_client_dev *client) 152 { 153 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 154 155 if (fb_helper->info) { 156 drm_fb_helper_unregister_info(fb_helper); 157 } else { 158 drm_client_release(&fb_helper->client); 159 drm_fb_helper_unprepare(fb_helper); 160 kfree(fb_helper); 161 } 162 } 163 164 static int msm_fbdev_client_restore(struct drm_client_dev *client) 165 { 166 drm_fb_helper_lastclose(client->dev); 167 168 return 0; 169 } 170 171 static int msm_fbdev_client_hotplug(struct drm_client_dev *client) 172 { 173 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 174 struct drm_device *dev = client->dev; 175 int ret; 176 177 if (dev->fb_helper) 178 return drm_fb_helper_hotplug_event(dev->fb_helper); 179 180 ret = drm_fb_helper_init(dev, fb_helper); 181 if (ret) 182 goto err_drm_err; 183 184 if (!drm_drv_uses_atomic_modeset(dev)) 185 drm_helper_disable_unused_functions(dev); 186 187 ret = drm_fb_helper_initial_config(fb_helper); 188 if (ret) 189 goto err_drm_fb_helper_fini; 190 191 return 0; 192 193 err_drm_fb_helper_fini: 194 drm_fb_helper_fini(fb_helper); 195 err_drm_err: 196 drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret); 197 return ret; 198 } 199 200 static const struct drm_client_funcs msm_fbdev_client_funcs = { 201 .owner = THIS_MODULE, 202 .unregister = msm_fbdev_client_unregister, 203 .restore = msm_fbdev_client_restore, 204 .hotplug = msm_fbdev_client_hotplug, 205 }; 206 207 /* initialize fbdev helper */ 208 void msm_fbdev_setup(struct drm_device *dev) 209 { 210 struct drm_fb_helper *helper; 211 int ret; 212 213 if (!fbdev) 214 return; 215 216 drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); 217 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); 218 219 helper = kzalloc(sizeof(*helper), GFP_KERNEL); 220 if (!helper) 221 return; 222 drm_fb_helper_prepare(dev, helper, 32, &msm_fb_helper_funcs); 223 224 ret = drm_client_init(dev, &helper->client, "fbdev", &msm_fbdev_client_funcs); 225 if (ret) { 226 drm_err(dev, "Failed to register client: %d\n", ret); 227 goto err_drm_fb_helper_unprepare; 228 } 229 230 ret = msm_fbdev_client_hotplug(&helper->client); 231 if (ret) 232 drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); 233 234 drm_client_register(&helper->client); 235 236 return; 237 238 err_drm_fb_helper_unprepare: 239 drm_fb_helper_unprepare(helper); 240 kfree(helper); 241 } 242