LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_fbdev_generic.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 128 0.0 %
Date: 2023-08-24 13:40:31 Functions: 0 16 0.0 %

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

Generated by: LCOV version 1.14