drm_fbdev_ttm.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // SPDX-License-Identifier: MIT
  2. #include <linux/export.h>
  3. #include <linux/moduleparam.h>
  4. #include <linux/vmalloc.h>
  5. #include <drm/drm_crtc_helper.h>
  6. #include <drm/drm_drv.h>
  7. #include <drm/drm_fb_helper.h>
  8. #include <drm/drm_framebuffer.h>
  9. #include <drm/drm_gem.h>
  10. #include <drm/drm_print.h>
  11. #include <drm/drm_fbdev_ttm.h>
  12. /* @user: 1=userspace, 0=fbcon */
  13. static int drm_fbdev_ttm_fb_open(struct fb_info *info, int user)
  14. {
  15. struct drm_fb_helper *fb_helper = info->par;
  16. /* No need to take a ref for fbcon because it unbinds on unregister */
  17. if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
  18. return -ENODEV;
  19. return 0;
  20. }
  21. static int drm_fbdev_ttm_fb_release(struct fb_info *info, int user)
  22. {
  23. struct drm_fb_helper *fb_helper = info->par;
  24. if (user)
  25. module_put(fb_helper->dev->driver->fops->owner);
  26. return 0;
  27. }
  28. FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_ttm,
  29. drm_fb_helper_damage_range,
  30. drm_fb_helper_damage_area);
  31. static void drm_fbdev_ttm_fb_destroy(struct fb_info *info)
  32. {
  33. struct drm_fb_helper *fb_helper = info->par;
  34. void *shadow = info->screen_buffer;
  35. if (!fb_helper->dev)
  36. return;
  37. fb_deferred_io_cleanup(info);
  38. drm_fb_helper_fini(fb_helper);
  39. vfree(shadow);
  40. drm_client_buffer_delete(fb_helper->buffer);
  41. drm_client_release(&fb_helper->client);
  42. }
  43. static const struct fb_ops drm_fbdev_ttm_fb_ops = {
  44. .owner = THIS_MODULE,
  45. .fb_open = drm_fbdev_ttm_fb_open,
  46. .fb_release = drm_fbdev_ttm_fb_release,
  47. FB_DEFAULT_DEFERRED_OPS(drm_fbdev_ttm),
  48. DRM_FB_HELPER_DEFAULT_OPS,
  49. .fb_destroy = drm_fbdev_ttm_fb_destroy,
  50. };
  51. static void drm_fbdev_ttm_damage_blit_real(struct drm_fb_helper *fb_helper,
  52. struct drm_clip_rect *clip,
  53. struct iosys_map *dst)
  54. {
  55. struct drm_framebuffer *fb = fb_helper->fb;
  56. size_t offset = clip->y1 * fb->pitches[0];
  57. size_t len = clip->x2 - clip->x1;
  58. unsigned int y;
  59. void *src;
  60. switch (drm_format_info_bpp(fb->format, 0)) {
  61. case 1:
  62. offset += clip->x1 / 8;
  63. len = DIV_ROUND_UP(len + clip->x1 % 8, 8);
  64. break;
  65. case 2:
  66. offset += clip->x1 / 4;
  67. len = DIV_ROUND_UP(len + clip->x1 % 4, 4);
  68. break;
  69. case 4:
  70. offset += clip->x1 / 2;
  71. len = DIV_ROUND_UP(len + clip->x1 % 2, 2);
  72. break;
  73. default:
  74. offset += clip->x1 * fb->format->cpp[0];
  75. len *= fb->format->cpp[0];
  76. break;
  77. }
  78. src = fb_helper->info->screen_buffer + offset;
  79. iosys_map_incr(dst, offset); /* go to first pixel within clip rect */
  80. for (y = clip->y1; y < clip->y2; y++) {
  81. iosys_map_memcpy_to(dst, 0, src, len);
  82. iosys_map_incr(dst, fb->pitches[0]);
  83. src += fb->pitches[0];
  84. }
  85. }
  86. static int drm_fbdev_ttm_damage_blit(struct drm_fb_helper *fb_helper,
  87. struct drm_clip_rect *clip)
  88. {
  89. struct drm_client_buffer *buffer = fb_helper->buffer;
  90. struct iosys_map map, dst;
  91. int ret;
  92. /*
  93. * We have to pin the client buffer to its current location while
  94. * flushing the shadow buffer. In the general case, concurrent
  95. * modesetting operations could try to move the buffer and would
  96. * fail. The modeset has to be serialized by acquiring the reservation
  97. * object of the underlying BO here.
  98. *
  99. * For fbdev emulation, we only have to protect against fbdev modeset
  100. * operations. Nothing else will involve the client buffer's BO. So it
  101. * is sufficient to acquire struct drm_fb_helper.lock here.
  102. */
  103. mutex_lock(&fb_helper->lock);
  104. ret = drm_client_buffer_vmap_local(buffer, &map);
  105. if (ret)
  106. goto out;
  107. dst = map;
  108. drm_fbdev_ttm_damage_blit_real(fb_helper, clip, &dst);
  109. drm_client_buffer_vunmap_local(buffer);
  110. out:
  111. mutex_unlock(&fb_helper->lock);
  112. return ret;
  113. }
  114. static int drm_fbdev_ttm_helper_fb_dirty(struct drm_fb_helper *helper,
  115. struct drm_clip_rect *clip)
  116. {
  117. struct drm_device *dev = helper->dev;
  118. int ret;
  119. /* Call damage handlers only if necessary */
  120. if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))
  121. return 0;
  122. ret = drm_fbdev_ttm_damage_blit(helper, clip);
  123. if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret))
  124. return ret;
  125. if (helper->fb->funcs->dirty) {
  126. ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
  127. if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
  128. return ret;
  129. }
  130. return 0;
  131. }
  132. static const struct drm_fb_helper_funcs drm_fbdev_ttm_helper_funcs = {
  133. .fb_dirty = drm_fbdev_ttm_helper_fb_dirty,
  134. };
  135. /*
  136. * struct drm_driver
  137. */
  138. int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
  139. struct drm_fb_helper_surface_size *sizes)
  140. {
  141. struct drm_client_dev *client = &fb_helper->client;
  142. struct drm_device *dev = fb_helper->dev;
  143. struct fb_info *info = fb_helper->info;
  144. struct drm_client_buffer *buffer;
  145. size_t screen_size;
  146. void *screen_buffer;
  147. u32 format;
  148. int ret;
  149. drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
  150. sizes->surface_width, sizes->surface_height,
  151. sizes->surface_bpp);
  152. format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp,
  153. sizes->surface_depth);
  154. buffer = drm_client_buffer_create_dumb(client, sizes->surface_width,
  155. sizes->surface_height, format);
  156. if (IS_ERR(buffer))
  157. return PTR_ERR(buffer);
  158. fb_helper->funcs = &drm_fbdev_ttm_helper_funcs;
  159. fb_helper->buffer = buffer;
  160. fb_helper->fb = buffer->fb;
  161. screen_size = buffer->gem->size;
  162. screen_buffer = vzalloc(screen_size);
  163. if (!screen_buffer) {
  164. ret = -ENOMEM;
  165. goto err_drm_client_buffer_delete;
  166. }
  167. drm_fb_helper_fill_info(info, fb_helper, sizes);
  168. info->fbops = &drm_fbdev_ttm_fb_ops;
  169. /* screen */
  170. info->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
  171. info->screen_buffer = screen_buffer;
  172. info->fix.smem_len = screen_size;
  173. /* deferred I/O */
  174. fb_helper->fbdefio.delay = HZ / 20;
  175. fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
  176. info->fbdefio = &fb_helper->fbdefio;
  177. ret = fb_deferred_io_init(info);
  178. if (ret)
  179. goto err_vfree;
  180. return 0;
  181. err_vfree:
  182. vfree(screen_buffer);
  183. err_drm_client_buffer_delete:
  184. fb_helper->fb = NULL;
  185. fb_helper->buffer = NULL;
  186. drm_client_buffer_delete(buffer);
  187. return ret;
  188. }
  189. EXPORT_SYMBOL(drm_fbdev_ttm_driver_fbdev_probe);