drm_fbdev_ttm.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // SPDX-License-Identifier: MIT
  2. #include <linux/moduleparam.h>
  3. #include <linux/vmalloc.h>
  4. #include <drm/drm_crtc_helper.h>
  5. #include <drm/drm_drv.h>
  6. #include <drm/drm_fb_helper.h>
  7. #include <drm/drm_framebuffer.h>
  8. #include <drm/drm_gem.h>
  9. #include <drm/drm_print.h>
  10. #include <drm/drm_fbdev_ttm.h>
  11. /* @user: 1=userspace, 0=fbcon */
  12. static int drm_fbdev_ttm_fb_open(struct fb_info *info, int user)
  13. {
  14. struct drm_fb_helper *fb_helper = info->par;
  15. /* No need to take a ref for fbcon because it unbinds on unregister */
  16. if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
  17. return -ENODEV;
  18. return 0;
  19. }
  20. static int drm_fbdev_ttm_fb_release(struct fb_info *info, int user)
  21. {
  22. struct drm_fb_helper *fb_helper = info->par;
  23. if (user)
  24. module_put(fb_helper->dev->driver->fops->owner);
  25. return 0;
  26. }
  27. FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_ttm,
  28. drm_fb_helper_damage_range,
  29. drm_fb_helper_damage_area);
  30. static void drm_fbdev_ttm_fb_destroy(struct fb_info *info)
  31. {
  32. struct drm_fb_helper *fb_helper = info->par;
  33. void *shadow = info->screen_buffer;
  34. if (!fb_helper->dev)
  35. return;
  36. fb_deferred_io_cleanup(info);
  37. drm_fb_helper_fini(fb_helper);
  38. vfree(shadow);
  39. drm_client_framebuffer_delete(fb_helper->buffer);
  40. drm_client_release(&fb_helper->client);
  41. drm_fb_helper_unprepare(fb_helper);
  42. kfree(fb_helper);
  43. }
  44. static const struct fb_ops drm_fbdev_ttm_fb_ops = {
  45. .owner = THIS_MODULE,
  46. .fb_open = drm_fbdev_ttm_fb_open,
  47. .fb_release = drm_fbdev_ttm_fb_release,
  48. FB_DEFAULT_DEFERRED_OPS(drm_fbdev_ttm),
  49. DRM_FB_HELPER_DEFAULT_OPS,
  50. .fb_destroy = drm_fbdev_ttm_fb_destroy,
  51. };
  52. /*
  53. * This function uses the client API to create a framebuffer backed by a dumb buffer.
  54. */
  55. static int drm_fbdev_ttm_helper_fb_probe(struct drm_fb_helper *fb_helper,
  56. struct drm_fb_helper_surface_size *sizes)
  57. {
  58. return drm_fbdev_ttm_driver_fbdev_probe(fb_helper, sizes);
  59. }
  60. static void drm_fbdev_ttm_damage_blit_real(struct drm_fb_helper *fb_helper,
  61. struct drm_clip_rect *clip,
  62. struct iosys_map *dst)
  63. {
  64. struct drm_framebuffer *fb = fb_helper->fb;
  65. size_t offset = clip->y1 * fb->pitches[0];
  66. size_t len = clip->x2 - clip->x1;
  67. unsigned int y;
  68. void *src;
  69. switch (drm_format_info_bpp(fb->format, 0)) {
  70. case 1:
  71. offset += clip->x1 / 8;
  72. len = DIV_ROUND_UP(len + clip->x1 % 8, 8);
  73. break;
  74. case 2:
  75. offset += clip->x1 / 4;
  76. len = DIV_ROUND_UP(len + clip->x1 % 4, 4);
  77. break;
  78. case 4:
  79. offset += clip->x1 / 2;
  80. len = DIV_ROUND_UP(len + clip->x1 % 2, 2);
  81. break;
  82. default:
  83. offset += clip->x1 * fb->format->cpp[0];
  84. len *= fb->format->cpp[0];
  85. break;
  86. }
  87. src = fb_helper->info->screen_buffer + offset;
  88. iosys_map_incr(dst, offset); /* go to first pixel within clip rect */
  89. for (y = clip->y1; y < clip->y2; y++) {
  90. iosys_map_memcpy_to(dst, 0, src, len);
  91. iosys_map_incr(dst, fb->pitches[0]);
  92. src += fb->pitches[0];
  93. }
  94. }
  95. static int drm_fbdev_ttm_damage_blit(struct drm_fb_helper *fb_helper,
  96. struct drm_clip_rect *clip)
  97. {
  98. struct drm_client_buffer *buffer = fb_helper->buffer;
  99. struct iosys_map map, dst;
  100. int ret;
  101. /*
  102. * We have to pin the client buffer to its current location while
  103. * flushing the shadow buffer. In the general case, concurrent
  104. * modesetting operations could try to move the buffer and would
  105. * fail. The modeset has to be serialized by acquiring the reservation
  106. * object of the underlying BO here.
  107. *
  108. * For fbdev emulation, we only have to protect against fbdev modeset
  109. * operations. Nothing else will involve the client buffer's BO. So it
  110. * is sufficient to acquire struct drm_fb_helper.lock here.
  111. */
  112. mutex_lock(&fb_helper->lock);
  113. ret = drm_client_buffer_vmap_local(buffer, &map);
  114. if (ret)
  115. goto out;
  116. dst = map;
  117. drm_fbdev_ttm_damage_blit_real(fb_helper, clip, &dst);
  118. drm_client_buffer_vunmap_local(buffer);
  119. out:
  120. mutex_unlock(&fb_helper->lock);
  121. return ret;
  122. }
  123. static int drm_fbdev_ttm_helper_fb_dirty(struct drm_fb_helper *helper,
  124. struct drm_clip_rect *clip)
  125. {
  126. struct drm_device *dev = helper->dev;
  127. int ret;
  128. /* Call damage handlers only if necessary */
  129. if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))
  130. return 0;
  131. ret = drm_fbdev_ttm_damage_blit(helper, clip);
  132. if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret))
  133. return ret;
  134. if (helper->fb->funcs->dirty) {
  135. ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
  136. if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
  137. return ret;
  138. }
  139. return 0;
  140. }
  141. static const struct drm_fb_helper_funcs drm_fbdev_ttm_helper_funcs = {
  142. .fb_probe = drm_fbdev_ttm_helper_fb_probe,
  143. .fb_dirty = drm_fbdev_ttm_helper_fb_dirty,
  144. };
  145. /*
  146. * struct drm_driver
  147. */
  148. int drm_fbdev_ttm_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
  149. struct drm_fb_helper_surface_size *sizes)
  150. {
  151. struct drm_client_dev *client = &fb_helper->client;
  152. struct drm_device *dev = fb_helper->dev;
  153. struct drm_client_buffer *buffer;
  154. struct fb_info *info;
  155. size_t screen_size;
  156. void *screen_buffer;
  157. u32 format;
  158. int ret;
  159. drm_dbg_kms(dev, "surface width(%d), height(%d) and bpp(%d)\n",
  160. sizes->surface_width, sizes->surface_height,
  161. sizes->surface_bpp);
  162. format = drm_driver_legacy_fb_format(dev, sizes->surface_bpp,
  163. sizes->surface_depth);
  164. buffer = drm_client_framebuffer_create(client, sizes->surface_width,
  165. sizes->surface_height, format);
  166. if (IS_ERR(buffer))
  167. return PTR_ERR(buffer);
  168. fb_helper->funcs = &drm_fbdev_ttm_helper_funcs;
  169. fb_helper->buffer = buffer;
  170. fb_helper->fb = buffer->fb;
  171. screen_size = buffer->gem->size;
  172. screen_buffer = vzalloc(screen_size);
  173. if (!screen_buffer) {
  174. ret = -ENOMEM;
  175. goto err_drm_client_framebuffer_delete;
  176. }
  177. info = drm_fb_helper_alloc_info(fb_helper);
  178. if (IS_ERR(info)) {
  179. ret = PTR_ERR(info);
  180. goto err_vfree;
  181. }
  182. drm_fb_helper_fill_info(info, fb_helper, sizes);
  183. info->fbops = &drm_fbdev_ttm_fb_ops;
  184. /* screen */
  185. info->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
  186. info->screen_buffer = screen_buffer;
  187. info->fix.smem_len = screen_size;
  188. /* deferred I/O */
  189. fb_helper->fbdefio.delay = HZ / 20;
  190. fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
  191. info->fbdefio = &fb_helper->fbdefio;
  192. ret = fb_deferred_io_init(info);
  193. if (ret)
  194. goto err_drm_fb_helper_release_info;
  195. return 0;
  196. err_drm_fb_helper_release_info:
  197. drm_fb_helper_release_info(fb_helper);
  198. err_vfree:
  199. vfree(screen_buffer);
  200. err_drm_client_framebuffer_delete:
  201. fb_helper->fb = NULL;
  202. fb_helper->buffer = NULL;
  203. drm_client_framebuffer_delete(buffer);
  204. return ret;
  205. }
  206. EXPORT_SYMBOL(drm_fbdev_ttm_driver_fbdev_probe);
  207. static void drm_fbdev_ttm_client_unregister(struct drm_client_dev *client)
  208. {
  209. struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
  210. if (fb_helper->info) {
  211. drm_fb_helper_unregister_info(fb_helper);
  212. } else {
  213. drm_client_release(&fb_helper->client);
  214. drm_fb_helper_unprepare(fb_helper);
  215. kfree(fb_helper);
  216. }
  217. }
  218. static int drm_fbdev_ttm_client_restore(struct drm_client_dev *client)
  219. {
  220. drm_fb_helper_lastclose(client->dev);
  221. return 0;
  222. }
  223. static int drm_fbdev_ttm_client_hotplug(struct drm_client_dev *client)
  224. {
  225. struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
  226. struct drm_device *dev = client->dev;
  227. int ret;
  228. if (dev->fb_helper)
  229. return drm_fb_helper_hotplug_event(dev->fb_helper);
  230. ret = drm_fb_helper_init(dev, fb_helper);
  231. if (ret)
  232. goto err_drm_err;
  233. if (!drm_drv_uses_atomic_modeset(dev))
  234. drm_helper_disable_unused_functions(dev);
  235. ret = drm_fb_helper_initial_config(fb_helper);
  236. if (ret)
  237. goto err_drm_fb_helper_fini;
  238. return 0;
  239. err_drm_fb_helper_fini:
  240. drm_fb_helper_fini(fb_helper);
  241. err_drm_err:
  242. drm_err(dev, "fbdev: Failed to setup emulation (ret=%d)\n", ret);
  243. return ret;
  244. }
  245. static const struct drm_client_funcs drm_fbdev_ttm_client_funcs = {
  246. .owner = THIS_MODULE,
  247. .unregister = drm_fbdev_ttm_client_unregister,
  248. .restore = drm_fbdev_ttm_client_restore,
  249. .hotplug = drm_fbdev_ttm_client_hotplug,
  250. };
  251. /**
  252. * drm_fbdev_ttm_setup() - Setup fbdev emulation for TTM-based drivers
  253. * @dev: DRM device
  254. * @preferred_bpp: Preferred bits per pixel for the device.
  255. *
  256. * This function sets up fbdev emulation for TTM-based drivers that support
  257. * dumb buffers with a virtual address and that can be mmap'ed.
  258. * drm_fbdev_ttm_setup() shall be called after the DRM driver registered
  259. * the new DRM device with drm_dev_register().
  260. *
  261. * Restore, hotplug events and teardown are all taken care of. Drivers that do
  262. * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
  263. * Simple drivers might use drm_mode_config_helper_suspend().
  264. *
  265. * In order to provide fixed mmap-able memory ranges, fbdev emulation
  266. * uses a shadow buffer in system memory. The implementation blits the shadow
  267. * fbdev buffer onto the real buffer in regular intervals.
  268. *
  269. * This function is safe to call even when there are no connectors present.
  270. * Setup will be retried on the next hotplug event.
  271. *
  272. * The fbdev is destroyed by drm_dev_unregister().
  273. */
  274. void drm_fbdev_ttm_setup(struct drm_device *dev, unsigned int preferred_bpp)
  275. {
  276. struct drm_fb_helper *fb_helper;
  277. int ret;
  278. drm_WARN(dev, !dev->registered, "Device has not been registered.\n");
  279. drm_WARN(dev, dev->fb_helper, "fb_helper is already set!\n");
  280. fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
  281. if (!fb_helper)
  282. return;
  283. drm_fb_helper_prepare(dev, fb_helper, preferred_bpp, &drm_fbdev_ttm_helper_funcs);
  284. ret = drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_ttm_client_funcs);
  285. if (ret) {
  286. drm_err(dev, "Failed to register client: %d\n", ret);
  287. goto err_drm_client_init;
  288. }
  289. drm_client_register(&fb_helper->client);
  290. return;
  291. err_drm_client_init:
  292. drm_fb_helper_unprepare(fb_helper);
  293. kfree(fb_helper);
  294. return;
  295. }
  296. EXPORT_SYMBOL(drm_fbdev_ttm_setup);