LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_client.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 154 0.0 %
Date: 2022-12-09 01:23:36 Functions: 0 17 0.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0 or MIT
       2             : /*
       3             :  * Copyright 2018 Noralf Trønnes
       4             :  */
       5             : 
       6             : #include <linux/iosys-map.h>
       7             : #include <linux/list.h>
       8             : #include <linux/module.h>
       9             : #include <linux/mutex.h>
      10             : #include <linux/seq_file.h>
      11             : #include <linux/slab.h>
      12             : 
      13             : #include <drm/drm_client.h>
      14             : #include <drm/drm_debugfs.h>
      15             : #include <drm/drm_device.h>
      16             : #include <drm/drm_drv.h>
      17             : #include <drm/drm_file.h>
      18             : #include <drm/drm_fourcc.h>
      19             : #include <drm/drm_framebuffer.h>
      20             : #include <drm/drm_gem.h>
      21             : #include <drm/drm_mode.h>
      22             : #include <drm/drm_print.h>
      23             : 
      24             : #include "drm_crtc_internal.h"
      25             : #include "drm_internal.h"
      26             : 
      27             : /**
      28             :  * DOC: overview
      29             :  *
      30             :  * This library provides support for clients running in the kernel like fbdev and bootsplash.
      31             :  *
      32             :  * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported.
      33             :  */
      34             : 
      35           0 : static int drm_client_open(struct drm_client_dev *client)
      36             : {
      37           0 :         struct drm_device *dev = client->dev;
      38             :         struct drm_file *file;
      39             : 
      40           0 :         file = drm_file_alloc(dev->primary);
      41           0 :         if (IS_ERR(file))
      42           0 :                 return PTR_ERR(file);
      43             : 
      44           0 :         mutex_lock(&dev->filelist_mutex);
      45           0 :         list_add(&file->lhead, &dev->filelist_internal);
      46           0 :         mutex_unlock(&dev->filelist_mutex);
      47             : 
      48           0 :         client->file = file;
      49             : 
      50             :         return 0;
      51             : }
      52             : 
      53           0 : static void drm_client_close(struct drm_client_dev *client)
      54             : {
      55           0 :         struct drm_device *dev = client->dev;
      56             : 
      57           0 :         mutex_lock(&dev->filelist_mutex);
      58           0 :         list_del(&client->file->lhead);
      59           0 :         mutex_unlock(&dev->filelist_mutex);
      60             : 
      61           0 :         drm_file_free(client->file);
      62           0 : }
      63             : 
      64             : /**
      65             :  * drm_client_init - Initialise a DRM client
      66             :  * @dev: DRM device
      67             :  * @client: DRM client
      68             :  * @name: Client name
      69             :  * @funcs: DRM client functions (optional)
      70             :  *
      71             :  * This initialises the client and opens a &drm_file.
      72             :  * Use drm_client_register() to complete the process.
      73             :  * The caller needs to hold a reference on @dev before calling this function.
      74             :  * The client is freed when the &drm_device is unregistered. See drm_client_release().
      75             :  *
      76             :  * Returns:
      77             :  * Zero on success or negative error code on failure.
      78             :  */
      79           0 : int drm_client_init(struct drm_device *dev, struct drm_client_dev *client,
      80             :                     const char *name, const struct drm_client_funcs *funcs)
      81             : {
      82             :         int ret;
      83             : 
      84           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create)
      85             :                 return -EOPNOTSUPP;
      86             : 
      87             :         if (funcs && !try_module_get(funcs->owner))
      88             :                 return -ENODEV;
      89             : 
      90           0 :         client->dev = dev;
      91           0 :         client->name = name;
      92           0 :         client->funcs = funcs;
      93             : 
      94           0 :         ret = drm_client_modeset_create(client);
      95           0 :         if (ret)
      96             :                 goto err_put_module;
      97             : 
      98           0 :         ret = drm_client_open(client);
      99           0 :         if (ret)
     100             :                 goto err_free;
     101             : 
     102           0 :         drm_dev_get(dev);
     103             : 
     104           0 :         return 0;
     105             : 
     106             : err_free:
     107           0 :         drm_client_modeset_free(client);
     108             : err_put_module:
     109             :         if (funcs)
     110             :                 module_put(funcs->owner);
     111             : 
     112             :         return ret;
     113             : }
     114             : EXPORT_SYMBOL(drm_client_init);
     115             : 
     116             : /**
     117             :  * drm_client_register - Register client
     118             :  * @client: DRM client
     119             :  *
     120             :  * Add the client to the &drm_device client list to activate its callbacks.
     121             :  * @client must be initialized by a call to drm_client_init(). After
     122             :  * drm_client_register() it is no longer permissible to call drm_client_release()
     123             :  * directly (outside the unregister callback), instead cleanup will happen
     124             :  * automatically on driver unload.
     125             :  */
     126           0 : void drm_client_register(struct drm_client_dev *client)
     127             : {
     128           0 :         struct drm_device *dev = client->dev;
     129             : 
     130           0 :         mutex_lock(&dev->clientlist_mutex);
     131           0 :         list_add(&client->list, &dev->clientlist);
     132           0 :         mutex_unlock(&dev->clientlist_mutex);
     133           0 : }
     134             : EXPORT_SYMBOL(drm_client_register);
     135             : 
     136             : /**
     137             :  * drm_client_release - Release DRM client resources
     138             :  * @client: DRM client
     139             :  *
     140             :  * Releases resources by closing the &drm_file that was opened by drm_client_init().
     141             :  * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.
     142             :  *
     143             :  * This function should only be called from the unregister callback. An exception
     144             :  * is fbdev which cannot free the buffer if userspace has open file descriptors.
     145             :  *
     146             :  * Note:
     147             :  * Clients cannot initiate a release by themselves. This is done to keep the code simple.
     148             :  * The driver has to be unloaded before the client can be unloaded.
     149             :  */
     150           0 : void drm_client_release(struct drm_client_dev *client)
     151             : {
     152           0 :         struct drm_device *dev = client->dev;
     153             : 
     154           0 :         drm_dbg_kms(dev, "%s\n", client->name);
     155             : 
     156           0 :         drm_client_modeset_free(client);
     157           0 :         drm_client_close(client);
     158           0 :         drm_dev_put(dev);
     159           0 :         if (client->funcs)
     160             :                 module_put(client->funcs->owner);
     161           0 : }
     162             : EXPORT_SYMBOL(drm_client_release);
     163             : 
     164           0 : void drm_client_dev_unregister(struct drm_device *dev)
     165             : {
     166             :         struct drm_client_dev *client, *tmp;
     167             : 
     168           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET))
     169             :                 return;
     170             : 
     171           0 :         mutex_lock(&dev->clientlist_mutex);
     172           0 :         list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
     173           0 :                 list_del(&client->list);
     174           0 :                 if (client->funcs && client->funcs->unregister) {
     175           0 :                         client->funcs->unregister(client);
     176             :                 } else {
     177           0 :                         drm_client_release(client);
     178           0 :                         kfree(client);
     179             :                 }
     180             :         }
     181           0 :         mutex_unlock(&dev->clientlist_mutex);
     182             : }
     183             : 
     184             : /**
     185             :  * drm_client_dev_hotplug - Send hotplug event to clients
     186             :  * @dev: DRM device
     187             :  *
     188             :  * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
     189             :  *
     190             :  * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
     191             :  * don't need to call this function themselves.
     192             :  */
     193           0 : void drm_client_dev_hotplug(struct drm_device *dev)
     194             : {
     195             :         struct drm_client_dev *client;
     196             :         int ret;
     197             : 
     198           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET))
     199             :                 return;
     200             : 
     201           0 :         mutex_lock(&dev->clientlist_mutex);
     202           0 :         list_for_each_entry(client, &dev->clientlist, list) {
     203           0 :                 if (!client->funcs || !client->funcs->hotplug)
     204           0 :                         continue;
     205             : 
     206           0 :                 ret = client->funcs->hotplug(client);
     207           0 :                 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
     208             :         }
     209           0 :         mutex_unlock(&dev->clientlist_mutex);
     210             : }
     211             : EXPORT_SYMBOL(drm_client_dev_hotplug);
     212             : 
     213           0 : void drm_client_dev_restore(struct drm_device *dev)
     214             : {
     215             :         struct drm_client_dev *client;
     216             :         int ret;
     217             : 
     218           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET))
     219             :                 return;
     220             : 
     221           0 :         mutex_lock(&dev->clientlist_mutex);
     222           0 :         list_for_each_entry(client, &dev->clientlist, list) {
     223           0 :                 if (!client->funcs || !client->funcs->restore)
     224           0 :                         continue;
     225             : 
     226           0 :                 ret = client->funcs->restore(client);
     227           0 :                 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
     228           0 :                 if (!ret) /* The first one to return zero gets the privilege to restore */
     229             :                         break;
     230             :         }
     231           0 :         mutex_unlock(&dev->clientlist_mutex);
     232             : }
     233             : 
     234           0 : static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
     235             : {
     236           0 :         struct drm_device *dev = buffer->client->dev;
     237             : 
     238           0 :         drm_gem_vunmap(buffer->gem, &buffer->map);
     239             : 
     240           0 :         if (buffer->gem)
     241           0 :                 drm_gem_object_put(buffer->gem);
     242             : 
     243           0 :         if (buffer->handle)
     244           0 :                 drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
     245             : 
     246           0 :         kfree(buffer);
     247           0 : }
     248             : 
     249             : static struct drm_client_buffer *
     250           0 : drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
     251             : {
     252           0 :         const struct drm_format_info *info = drm_format_info(format);
     253           0 :         struct drm_mode_create_dumb dumb_args = { };
     254           0 :         struct drm_device *dev = client->dev;
     255             :         struct drm_client_buffer *buffer;
     256             :         struct drm_gem_object *obj;
     257             :         int ret;
     258             : 
     259           0 :         buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
     260           0 :         if (!buffer)
     261             :                 return ERR_PTR(-ENOMEM);
     262             : 
     263           0 :         buffer->client = client;
     264             : 
     265           0 :         dumb_args.width = width;
     266           0 :         dumb_args.height = height;
     267           0 :         dumb_args.bpp = info->cpp[0] * 8;
     268           0 :         ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
     269           0 :         if (ret)
     270             :                 goto err_delete;
     271             : 
     272           0 :         buffer->handle = dumb_args.handle;
     273           0 :         buffer->pitch = dumb_args.pitch;
     274             : 
     275           0 :         obj = drm_gem_object_lookup(client->file, dumb_args.handle);
     276           0 :         if (!obj)  {
     277             :                 ret = -ENOENT;
     278             :                 goto err_delete;
     279             :         }
     280             : 
     281           0 :         buffer->gem = obj;
     282             : 
     283           0 :         return buffer;
     284             : 
     285             : err_delete:
     286           0 :         drm_client_buffer_delete(buffer);
     287             : 
     288           0 :         return ERR_PTR(ret);
     289             : }
     290             : 
     291             : /**
     292             :  * drm_client_buffer_vmap - Map DRM client buffer into address space
     293             :  * @buffer: DRM client buffer
     294             :  * @map_copy: Returns the mapped memory's address
     295             :  *
     296             :  * This function maps a client buffer into kernel address space. If the
     297             :  * buffer is already mapped, it returns the existing mapping's address.
     298             :  *
     299             :  * Client buffer mappings are not ref'counted. Each call to
     300             :  * drm_client_buffer_vmap() should be followed by a call to
     301             :  * drm_client_buffer_vunmap(); or the client buffer should be mapped
     302             :  * throughout its lifetime.
     303             :  *
     304             :  * The returned address is a copy of the internal value. In contrast to
     305             :  * other vmap interfaces, you don't need it for the client's vunmap
     306             :  * function. So you can modify it at will during blit and draw operations.
     307             :  *
     308             :  * Returns:
     309             :  *      0 on success, or a negative errno code otherwise.
     310             :  */
     311             : int
     312           0 : drm_client_buffer_vmap(struct drm_client_buffer *buffer,
     313             :                        struct iosys_map *map_copy)
     314             : {
     315           0 :         struct iosys_map *map = &buffer->map;
     316             :         int ret;
     317             : 
     318             :         /*
     319             :          * FIXME: The dependency on GEM here isn't required, we could
     320             :          * convert the driver handle to a dma-buf instead and use the
     321             :          * backend-agnostic dma-buf vmap support instead. This would
     322             :          * require that the handle2fd prime ioctl is reworked to pull the
     323             :          * fd_install step out of the driver backend hooks, to make that
     324             :          * final step optional for internal users.
     325             :          */
     326           0 :         ret = drm_gem_vmap(buffer->gem, map);
     327           0 :         if (ret)
     328             :                 return ret;
     329             : 
     330           0 :         *map_copy = *map;
     331             : 
     332           0 :         return 0;
     333             : }
     334             : EXPORT_SYMBOL(drm_client_buffer_vmap);
     335             : 
     336             : /**
     337             :  * drm_client_buffer_vunmap - Unmap DRM client buffer
     338             :  * @buffer: DRM client buffer
     339             :  *
     340             :  * This function removes a client buffer's memory mapping. Calling this
     341             :  * function is only required by clients that manage their buffer mappings
     342             :  * by themselves.
     343             :  */
     344           0 : void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
     345             : {
     346           0 :         struct iosys_map *map = &buffer->map;
     347             : 
     348           0 :         drm_gem_vunmap(buffer->gem, map);
     349           0 : }
     350             : EXPORT_SYMBOL(drm_client_buffer_vunmap);
     351             : 
     352           0 : static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
     353             : {
     354             :         int ret;
     355             : 
     356           0 :         if (!buffer->fb)
     357             :                 return;
     358             : 
     359           0 :         ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
     360           0 :         if (ret)
     361           0 :                 drm_err(buffer->client->dev,
     362             :                         "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
     363             : 
     364           0 :         buffer->fb = NULL;
     365             : }
     366             : 
     367           0 : static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
     368             :                                    u32 width, u32 height, u32 format)
     369             : {
     370           0 :         struct drm_client_dev *client = buffer->client;
     371           0 :         struct drm_mode_fb_cmd fb_req = { };
     372             :         const struct drm_format_info *info;
     373             :         int ret;
     374             : 
     375           0 :         info = drm_format_info(format);
     376           0 :         fb_req.bpp = info->cpp[0] * 8;
     377           0 :         fb_req.depth = info->depth;
     378           0 :         fb_req.width = width;
     379           0 :         fb_req.height = height;
     380           0 :         fb_req.handle = buffer->handle;
     381           0 :         fb_req.pitch = buffer->pitch;
     382             : 
     383           0 :         ret = drm_mode_addfb(client->dev, &fb_req, client->file);
     384           0 :         if (ret)
     385             :                 return ret;
     386             : 
     387           0 :         buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
     388           0 :         if (WARN_ON(!buffer->fb))
     389             :                 return -ENOENT;
     390             : 
     391             :         /* drop the reference we picked up in framebuffer lookup */
     392           0 :         drm_framebuffer_put(buffer->fb);
     393             : 
     394           0 :         strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
     395             : 
     396           0 :         return 0;
     397             : }
     398             : 
     399             : /**
     400             :  * drm_client_framebuffer_create - Create a client framebuffer
     401             :  * @client: DRM client
     402             :  * @width: Framebuffer width
     403             :  * @height: Framebuffer height
     404             :  * @format: Buffer format
     405             :  *
     406             :  * This function creates a &drm_client_buffer which consists of a
     407             :  * &drm_framebuffer backed by a dumb buffer.
     408             :  * Call drm_client_framebuffer_delete() to free the buffer.
     409             :  *
     410             :  * Returns:
     411             :  * Pointer to a client buffer or an error pointer on failure.
     412             :  */
     413             : struct drm_client_buffer *
     414           0 : drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
     415             : {
     416             :         struct drm_client_buffer *buffer;
     417             :         int ret;
     418             : 
     419           0 :         buffer = drm_client_buffer_create(client, width, height, format);
     420           0 :         if (IS_ERR(buffer))
     421             :                 return buffer;
     422             : 
     423           0 :         ret = drm_client_buffer_addfb(buffer, width, height, format);
     424           0 :         if (ret) {
     425           0 :                 drm_client_buffer_delete(buffer);
     426           0 :                 return ERR_PTR(ret);
     427             :         }
     428             : 
     429             :         return buffer;
     430             : }
     431             : EXPORT_SYMBOL(drm_client_framebuffer_create);
     432             : 
     433             : /**
     434             :  * drm_client_framebuffer_delete - Delete a client framebuffer
     435             :  * @buffer: DRM client buffer (can be NULL)
     436             :  */
     437           0 : void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
     438             : {
     439           0 :         if (!buffer)
     440             :                 return;
     441             : 
     442           0 :         drm_client_buffer_rmfb(buffer);
     443           0 :         drm_client_buffer_delete(buffer);
     444             : }
     445             : EXPORT_SYMBOL(drm_client_framebuffer_delete);
     446             : 
     447             : /**
     448             :  * drm_client_framebuffer_flush - Manually flush client framebuffer
     449             :  * @buffer: DRM client buffer (can be NULL)
     450             :  * @rect: Damage rectangle (if NULL flushes all)
     451             :  *
     452             :  * This calls &drm_framebuffer_funcs->dirty (if present) to flush buffer changes
     453             :  * for drivers that need it.
     454             :  *
     455             :  * Returns:
     456             :  * Zero on success or negative error code on failure.
     457             :  */
     458           0 : int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect)
     459             : {
     460           0 :         if (!buffer || !buffer->fb || !buffer->fb->funcs->dirty)
     461             :                 return 0;
     462             : 
     463           0 :         if (rect) {
     464           0 :                 struct drm_clip_rect clip = {
     465           0 :                         .x1 = rect->x1,
     466           0 :                         .y1 = rect->y1,
     467           0 :                         .x2 = rect->x2,
     468           0 :                         .y2 = rect->y2,
     469             :                 };
     470             : 
     471           0 :                 return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
     472             :                                                 0, 0, &clip, 1);
     473             :         }
     474             : 
     475           0 :         return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
     476             :                                         0, 0, NULL, 0);
     477             : }
     478             : EXPORT_SYMBOL(drm_client_framebuffer_flush);
     479             : 
     480             : #ifdef CONFIG_DEBUG_FS
     481             : static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
     482             : {
     483             :         struct drm_info_node *node = m->private;
     484             :         struct drm_device *dev = node->minor->dev;
     485             :         struct drm_printer p = drm_seq_file_printer(m);
     486             :         struct drm_client_dev *client;
     487             : 
     488             :         mutex_lock(&dev->clientlist_mutex);
     489             :         list_for_each_entry(client, &dev->clientlist, list)
     490             :                 drm_printf(&p, "%s\n", client->name);
     491             :         mutex_unlock(&dev->clientlist_mutex);
     492             : 
     493             :         return 0;
     494             : }
     495             : 
     496             : static const struct drm_info_list drm_client_debugfs_list[] = {
     497             :         { "internal_clients", drm_client_debugfs_internal_clients, 0 },
     498             : };
     499             : 
     500             : void drm_client_debugfs_init(struct drm_minor *minor)
     501             : {
     502             :         drm_debugfs_create_files(drm_client_debugfs_list,
     503             :                                  ARRAY_SIZE(drm_client_debugfs_list),
     504             :                                  minor->debugfs_root, minor);
     505             : }
     506             : #endif

Generated by: LCOV version 1.14