1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ 4 * Author: Rob Clark <rob@ti.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_gem_framebuffer_helper.h> 16 #include <drm/drm_util.h> 17 18 #include "omap_drv.h" 19 #include "omap_fbdev.h" 20 21 MODULE_PARM_DESC(ywrap, "Enable ywrap scrolling (omap44xx and later, default 'y')"); 22 static bool ywrap_enabled = true; 23 module_param_named(ywrap, ywrap_enabled, bool, 0644); 24 25 /* 26 * fbdev funcs, to implement legacy fbdev interface on top of drm driver 27 */ 28 29 #define to_omap_fbdev(x) container_of(x, struct omap_fbdev, base) 30 31 struct omap_fbdev { 32 struct drm_fb_helper base; 33 bool ywrap_enabled; 34 35 /* for deferred dmm roll when getting called in atomic ctx */ 36 struct work_struct work; 37 }; 38 39 static struct drm_fb_helper *get_fb(struct fb_info *fbi); 40 41 static void pan_worker(struct work_struct *work) 42 { 43 struct omap_fbdev *fbdev = container_of(work, struct omap_fbdev, work); 44 struct drm_fb_helper *helper = &fbdev->base; 45 struct fb_info *fbi = helper->info; 46 struct drm_gem_object *bo = drm_gem_fb_get_obj(helper->fb, 0); 47 int npages; 48 49 /* DMM roll shifts in 4K pages: */ 50 npages = fbi->fix.line_length >> PAGE_SHIFT; 51 omap_gem_roll(bo, fbi->var.yoffset * npages); 52 } 53 54 FB_GEN_DEFAULT_DEFERRED_DMAMEM_OPS(omap_fbdev, 55 drm_fb_helper_damage_range, 56 drm_fb_helper_damage_area) 57 58 static int omap_fbdev_pan_display(struct fb_var_screeninfo *var, 59 struct fb_info *fbi) 60 { 61 struct drm_fb_helper *helper = get_fb(fbi); 62 struct omap_fbdev *fbdev = to_omap_fbdev(helper); 63 64 if (!helper) 65 goto fallback; 66 67 if (!fbdev->ywrap_enabled) 68 goto fallback; 69 70 if (drm_can_sleep()) { 71 pan_worker(&fbdev->work); 72 } else { 73 struct omap_drm_private *priv = helper->dev->dev_private; 74 queue_work(priv->wq, &fbdev->work); 75 } 76 77 return 0; 78 79 fallback: 80 return drm_fb_helper_pan_display(var, fbi); 81 } 82 83 static int omap_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 84 { 85 vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); 86 87 return fb_deferred_io_mmap(info, vma); 88 } 89 90 static void omap_fbdev_fb_destroy(struct fb_info *info) 91 { 92 struct drm_fb_helper *helper = info->par; 93 struct drm_framebuffer *fb = helper->fb; 94 struct drm_gem_object *bo = drm_gem_fb_get_obj(fb, 0); 95 struct omap_fbdev *fbdev = to_omap_fbdev(helper); 96 97 DBG(); 98 99 fb_deferred_io_cleanup(info); 100 drm_fb_helper_fini(helper); 101 102 omap_gem_unpin(bo); 103 drm_framebuffer_remove(fb); 104 105 drm_client_release(&helper->client); 106 drm_fb_helper_unprepare(helper); 107 kfree(fbdev); 108 } 109 110 /* 111 * For now, we cannot use FB_DEFAULT_DEFERRED_OPS and fb_deferred_io_mmap() 112 * because we use write-combine. 113 */ 114 static const struct fb_ops omap_fb_ops = { 115 .owner = THIS_MODULE, 116 __FB_DEFAULT_DEFERRED_OPS_RDWR(omap_fbdev), 117 .fb_check_var = drm_fb_helper_check_var, 118 .fb_set_par = drm_fb_helper_set_par, 119 .fb_setcmap = drm_fb_helper_setcmap, 120 .fb_blank = drm_fb_helper_blank, 121 .fb_pan_display = omap_fbdev_pan_display, 122 __FB_DEFAULT_DEFERRED_OPS_DRAW(omap_fbdev), 123 .fb_ioctl = drm_fb_helper_ioctl, 124 .fb_mmap = omap_fbdev_fb_mmap, 125 .fb_destroy = omap_fbdev_fb_destroy, 126 }; 127 128 static int omap_fbdev_create(struct drm_fb_helper *helper, 129 struct drm_fb_helper_surface_size *sizes) 130 { 131 struct omap_fbdev *fbdev = to_omap_fbdev(helper); 132 struct drm_device *dev = helper->dev; 133 struct omap_drm_private *priv = dev->dev_private; 134 struct drm_framebuffer *fb = NULL; 135 union omap_gem_size gsize; 136 struct fb_info *fbi = NULL; 137 struct drm_mode_fb_cmd2 mode_cmd = {0}; 138 struct drm_gem_object *bo; 139 dma_addr_t dma_addr; 140 int ret; 141 142 sizes->surface_bpp = 32; 143 sizes->surface_depth = 24; 144 145 DBG("create fbdev: %dx%d@%d (%dx%d)", sizes->surface_width, 146 sizes->surface_height, sizes->surface_bpp, 147 sizes->fb_width, sizes->fb_height); 148 149 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 150 sizes->surface_depth); 151 152 mode_cmd.width = sizes->surface_width; 153 mode_cmd.height = sizes->surface_height; 154 155 mode_cmd.pitches[0] = 156 DIV_ROUND_UP(mode_cmd.width * sizes->surface_bpp, 8); 157 158 fbdev->ywrap_enabled = priv->has_dmm && ywrap_enabled; 159 if (fbdev->ywrap_enabled) { 160 /* need to align pitch to page size if using DMM scrolling */ 161 mode_cmd.pitches[0] = PAGE_ALIGN(mode_cmd.pitches[0]); 162 } 163 164 /* allocate backing bo */ 165 gsize = (union omap_gem_size){ 166 .bytes = PAGE_ALIGN(mode_cmd.pitches[0] * mode_cmd.height), 167 }; 168 DBG("allocating %d bytes for fb %d", gsize.bytes, dev->primary->index); 169 bo = omap_gem_new(dev, gsize, OMAP_BO_SCANOUT | OMAP_BO_WC); 170 if (!bo) { 171 dev_err(dev->dev, "failed to allocate buffer object\n"); 172 ret = -ENOMEM; 173 goto fail; 174 } 175 176 fb = omap_framebuffer_init(dev, &mode_cmd, &bo); 177 if (IS_ERR(fb)) { 178 dev_err(dev->dev, "failed to allocate fb\n"); 179 /* note: if fb creation failed, we can't rely on fb destroy 180 * to unref the bo: 181 */ 182 drm_gem_object_put(bo); 183 ret = PTR_ERR(fb); 184 goto fail; 185 } 186 187 /* note: this keeps the bo pinned.. which is perhaps not ideal, 188 * but is needed as long as we use fb_mmap() to mmap to userspace 189 * (since this happens using fix.smem_start). Possibly we could 190 * implement our own mmap using GEM mmap support to avoid this 191 * (non-tiled buffer doesn't need to be pinned for fbcon to write 192 * to it). Then we just need to be sure that we are able to re- 193 * pin it in case of an opps. 194 */ 195 ret = omap_gem_pin(bo, &dma_addr); 196 if (ret) { 197 dev_err(dev->dev, "could not pin framebuffer\n"); 198 ret = -ENOMEM; 199 goto fail; 200 } 201 202 fbi = drm_fb_helper_alloc_info(helper); 203 if (IS_ERR(fbi)) { 204 dev_err(dev->dev, "failed to allocate fb info\n"); 205 ret = PTR_ERR(fbi); 206 goto fail; 207 } 208 209 DBG("fbi=%p, dev=%p", fbi, dev); 210 211 helper->fb = fb; 212 213 fbi->fbops = &omap_fb_ops; 214 215 drm_fb_helper_fill_info(fbi, helper, sizes); 216 217 fbi->flags |= FBINFO_VIRTFB; 218 fbi->screen_buffer = omap_gem_vaddr(bo); 219 fbi->screen_size = bo->size; 220 fbi->fix.smem_start = dma_addr; 221 fbi->fix.smem_len = bo->size; 222 223 /* deferred I/O */ 224 helper->fbdefio.delay = HZ / 20; 225 helper->fbdefio.deferred_io = drm_fb_helper_deferred_io; 226 227 fbi->fbdefio = &helper->fbdefio; 228 ret = fb_deferred_io_init(fbi); 229 if (ret) 230 goto fail; 231 232 /* if we have DMM, then we can use it for scrolling by just 233 * shuffling pages around in DMM rather than doing sw blit. 234 */ 235 if (fbdev->ywrap_enabled) { 236 DRM_INFO("Enabling DMM ywrap scrolling\n"); 237 fbi->flags |= FBINFO_HWACCEL_YWRAP | FBINFO_READS_FAST; 238 fbi->fix.ywrapstep = 1; 239 } 240 241 242 DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); 243 DBG("allocated %dx%d fb", fb->width, fb->height); 244 245 return 0; 246 247 fail: 248 249 if (ret) { 250 if (fb) 251 drm_framebuffer_remove(fb); 252 } 253 254 return ret; 255 } 256 257 static int omap_fbdev_dirty(struct drm_fb_helper *helper, struct drm_clip_rect *clip) 258 { 259 if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2)) 260 return 0; 261 262 if (helper->fb->funcs->dirty) 263 return helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1); 264 265 return 0; 266 } 267 268 static const struct drm_fb_helper_funcs omap_fb_helper_funcs = { 269 .fb_probe = omap_fbdev_create, 270 .fb_dirty = omap_fbdev_dirty, 271 }; 272 273 static struct drm_fb_helper *get_fb(struct fb_info *fbi) 274 { 275 if (!fbi || strcmp(fbi->fix.id, MODULE_NAME)) { 276 /* these are not the fb's you're looking for */ 277 return NULL; 278 } 279 return fbi->par; 280 } 281 282 /* 283 * struct drm_client 284 */ 285 286 static void omap_fbdev_client_unregister(struct drm_client_dev *client) 287 { 288 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 289 290 if (fb_helper->info) { 291 drm_fb_helper_unregister_info(fb_helper); 292 } else { 293 drm_client_release(&fb_helper->client); 294 drm_fb_helper_unprepare(fb_helper); 295 kfree(fb_helper); 296 } 297 } 298 299 static int omap_fbdev_client_restore(struct drm_client_dev *client) 300 { 301 drm_fb_helper_lastclose(client->dev); 302 303 return 0; 304 } 305 306 static int omap_fbdev_client_hotplug(struct drm_client_dev *client) 307 { 308 struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client); 309 struct drm_device *dev = client->dev; 310 int ret; 311 312 if (dev->fb_helper) 313 return drm_fb_helper_hotplug_event(dev->fb_helper); 314 315 ret = drm_fb_helper_init(dev, fb_helper); 316 if (ret) 317 goto err_drm_err; 318 319 ret = drm_fb_helper_initial_config(fb_helper); 320 if (ret) 321 goto err_drm_fb_helper_fini; 322 323 return 0; 324 325 err_drm_fb_helper_fini: 326 drm_fb_helper_fini(fb_helper); 327 err_drm_err: 328 drm_err(dev, "Failed to setup fbdev emulation (ret=%d)\n", ret); 329 return ret; 330 } 331 332 static const struct drm_client_funcs omap_fbdev_client_funcs = { 333 .owner = THIS_MODULE, 334 .unregister = omap_fbdev_client_unregister, 335 .restore = omap_fbdev_client_restore, 336 .hotplug = omap_fbdev_client_hotplug, 337 }; 338 339 void omap_fbdev_setup(struct drm_device *dev) 340 { 341 struct omap_fbdev *fbdev; 342 struct drm_fb_helper *helper; 343 int ret; 344 345 drm_WARN(dev, !dev->registered, "Device has not been registered.\n"); 346 drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n"); 347 348 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); 349 if (!fbdev) 350 return; 351 helper = &fbdev->base; 352 353 drm_fb_helper_prepare(dev, helper, 32, &omap_fb_helper_funcs); 354 355 ret = drm_client_init(dev, &helper->client, "fbdev", &omap_fbdev_client_funcs); 356 if (ret) 357 goto err_drm_client_init; 358 359 INIT_WORK(&fbdev->work, pan_worker); 360 361 drm_client_register(&helper->client); 362 363 return; 364 365 err_drm_client_init: 366 drm_fb_helper_unprepare(helper); 367 kfree(fbdev); 368 } 369