LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_client.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 162 0.0 %
Date: 2023-08-24 13:40:31 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             :  * Registering a client generates a hotplug event that allows the client
     127             :  * to set up its display from pre-existing outputs. The client must have
     128             :  * initialized its state to able to handle the hotplug event successfully.
     129             :  */
     130           0 : void drm_client_register(struct drm_client_dev *client)
     131             : {
     132           0 :         struct drm_device *dev = client->dev;
     133             :         int ret;
     134             : 
     135           0 :         mutex_lock(&dev->clientlist_mutex);
     136           0 :         list_add(&client->list, &dev->clientlist);
     137             : 
     138           0 :         if (client->funcs && client->funcs->hotplug) {
     139             :                 /*
     140             :                  * Perform an initial hotplug event to pick up the
     141             :                  * display configuration for the client. This step
     142             :                  * has to be performed *after* registering the client
     143             :                  * in the list of clients, or a concurrent hotplug
     144             :                  * event might be lost; leaving the display off.
     145             :                  *
     146             :                  * Hold the clientlist_mutex as for a regular hotplug
     147             :                  * event.
     148             :                  */
     149           0 :                 ret = client->funcs->hotplug(client);
     150           0 :                 if (ret)
     151           0 :                         drm_dbg_kms(dev, "client hotplug ret=%d\n", ret);
     152             :         }
     153           0 :         mutex_unlock(&dev->clientlist_mutex);
     154           0 : }
     155             : EXPORT_SYMBOL(drm_client_register);
     156             : 
     157             : /**
     158             :  * drm_client_release - Release DRM client resources
     159             :  * @client: DRM client
     160             :  *
     161             :  * Releases resources by closing the &drm_file that was opened by drm_client_init().
     162             :  * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.
     163             :  *
     164             :  * This function should only be called from the unregister callback. An exception
     165             :  * is fbdev which cannot free the buffer if userspace has open file descriptors.
     166             :  *
     167             :  * Note:
     168             :  * Clients cannot initiate a release by themselves. This is done to keep the code simple.
     169             :  * The driver has to be unloaded before the client can be unloaded.
     170             :  */
     171           0 : void drm_client_release(struct drm_client_dev *client)
     172             : {
     173           0 :         struct drm_device *dev = client->dev;
     174             : 
     175           0 :         drm_dbg_kms(dev, "%s\n", client->name);
     176             : 
     177           0 :         drm_client_modeset_free(client);
     178           0 :         drm_client_close(client);
     179           0 :         drm_dev_put(dev);
     180           0 :         if (client->funcs)
     181             :                 module_put(client->funcs->owner);
     182           0 : }
     183             : EXPORT_SYMBOL(drm_client_release);
     184             : 
     185           0 : void drm_client_dev_unregister(struct drm_device *dev)
     186             : {
     187             :         struct drm_client_dev *client, *tmp;
     188             : 
     189           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET))
     190             :                 return;
     191             : 
     192           0 :         mutex_lock(&dev->clientlist_mutex);
     193           0 :         list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
     194           0 :                 list_del(&client->list);
     195           0 :                 if (client->funcs && client->funcs->unregister) {
     196           0 :                         client->funcs->unregister(client);
     197             :                 } else {
     198           0 :                         drm_client_release(client);
     199           0 :                         kfree(client);
     200             :                 }
     201             :         }
     202           0 :         mutex_unlock(&dev->clientlist_mutex);
     203             : }
     204             : 
     205             : /**
     206             :  * drm_client_dev_hotplug - Send hotplug event to clients
     207             :  * @dev: DRM device
     208             :  *
     209             :  * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
     210             :  *
     211             :  * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
     212             :  * don't need to call this function themselves.
     213             :  */
     214           0 : void drm_client_dev_hotplug(struct drm_device *dev)
     215             : {
     216             :         struct drm_client_dev *client;
     217             :         int ret;
     218             : 
     219           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET))
     220             :                 return;
     221             : 
     222           0 :         if (!dev->mode_config.num_connector) {
     223           0 :                 drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n");
     224           0 :                 return;
     225             :         }
     226             : 
     227           0 :         mutex_lock(&dev->clientlist_mutex);
     228           0 :         list_for_each_entry(client, &dev->clientlist, list) {
     229           0 :                 if (!client->funcs || !client->funcs->hotplug)
     230           0 :                         continue;
     231             : 
     232           0 :                 if (client->hotplug_failed)
     233           0 :                         continue;
     234             : 
     235           0 :                 ret = client->funcs->hotplug(client);
     236           0 :                 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
     237           0 :                 if (ret)
     238           0 :                         client->hotplug_failed = true;
     239             :         }
     240           0 :         mutex_unlock(&dev->clientlist_mutex);
     241             : }
     242             : EXPORT_SYMBOL(drm_client_dev_hotplug);
     243             : 
     244           0 : void drm_client_dev_restore(struct drm_device *dev)
     245             : {
     246             :         struct drm_client_dev *client;
     247             :         int ret;
     248             : 
     249           0 :         if (!drm_core_check_feature(dev, DRIVER_MODESET))
     250             :                 return;
     251             : 
     252           0 :         mutex_lock(&dev->clientlist_mutex);
     253           0 :         list_for_each_entry(client, &dev->clientlist, list) {
     254           0 :                 if (!client->funcs || !client->funcs->restore)
     255           0 :                         continue;
     256             : 
     257           0 :                 ret = client->funcs->restore(client);
     258           0 :                 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
     259           0 :                 if (!ret) /* The first one to return zero gets the privilege to restore */
     260             :                         break;
     261             :         }
     262           0 :         mutex_unlock(&dev->clientlist_mutex);
     263             : }
     264             : 
     265           0 : static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
     266             : {
     267           0 :         if (buffer->gem) {
     268           0 :                 drm_gem_vunmap_unlocked(buffer->gem, &buffer->map);
     269           0 :                 drm_gem_object_put(buffer->gem);
     270             :         }
     271             : 
     272           0 :         kfree(buffer);
     273           0 : }
     274             : 
     275             : static struct drm_client_buffer *
     276           0 : drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
     277             :                          u32 format, u32 *handle)
     278             : {
     279           0 :         const struct drm_format_info *info = drm_format_info(format);
     280           0 :         struct drm_mode_create_dumb dumb_args = { };
     281           0 :         struct drm_device *dev = client->dev;
     282             :         struct drm_client_buffer *buffer;
     283             :         struct drm_gem_object *obj;
     284             :         int ret;
     285             : 
     286           0 :         buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
     287           0 :         if (!buffer)
     288             :                 return ERR_PTR(-ENOMEM);
     289             : 
     290           0 :         buffer->client = client;
     291             : 
     292           0 :         dumb_args.width = width;
     293           0 :         dumb_args.height = height;
     294           0 :         dumb_args.bpp = drm_format_info_bpp(info, 0);
     295           0 :         ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
     296           0 :         if (ret)
     297             :                 goto err_delete;
     298             : 
     299           0 :         obj = drm_gem_object_lookup(client->file, dumb_args.handle);
     300           0 :         if (!obj)  {
     301             :                 ret = -ENOENT;
     302             :                 goto err_delete;
     303             :         }
     304             : 
     305           0 :         buffer->pitch = dumb_args.pitch;
     306           0 :         buffer->gem = obj;
     307           0 :         *handle = dumb_args.handle;
     308             : 
     309           0 :         return buffer;
     310             : 
     311             : err_delete:
     312           0 :         drm_client_buffer_delete(buffer);
     313             : 
     314           0 :         return ERR_PTR(ret);
     315             : }
     316             : 
     317             : /**
     318             :  * drm_client_buffer_vmap - Map DRM client buffer into address space
     319             :  * @buffer: DRM client buffer
     320             :  * @map_copy: Returns the mapped memory's address
     321             :  *
     322             :  * This function maps a client buffer into kernel address space. If the
     323             :  * buffer is already mapped, it returns the existing mapping's address.
     324             :  *
     325             :  * Client buffer mappings are not ref'counted. Each call to
     326             :  * drm_client_buffer_vmap() should be followed by a call to
     327             :  * drm_client_buffer_vunmap(); or the client buffer should be mapped
     328             :  * throughout its lifetime.
     329             :  *
     330             :  * The returned address is a copy of the internal value. In contrast to
     331             :  * other vmap interfaces, you don't need it for the client's vunmap
     332             :  * function. So you can modify it at will during blit and draw operations.
     333             :  *
     334             :  * Returns:
     335             :  *      0 on success, or a negative errno code otherwise.
     336             :  */
     337             : int
     338           0 : drm_client_buffer_vmap(struct drm_client_buffer *buffer,
     339             :                        struct iosys_map *map_copy)
     340             : {
     341           0 :         struct iosys_map *map = &buffer->map;
     342             :         int ret;
     343             : 
     344             :         /*
     345             :          * FIXME: The dependency on GEM here isn't required, we could
     346             :          * convert the driver handle to a dma-buf instead and use the
     347             :          * backend-agnostic dma-buf vmap support instead. This would
     348             :          * require that the handle2fd prime ioctl is reworked to pull the
     349             :          * fd_install step out of the driver backend hooks, to make that
     350             :          * final step optional for internal users.
     351             :          */
     352           0 :         ret = drm_gem_vmap_unlocked(buffer->gem, map);
     353           0 :         if (ret)
     354             :                 return ret;
     355             : 
     356           0 :         *map_copy = *map;
     357             : 
     358           0 :         return 0;
     359             : }
     360             : EXPORT_SYMBOL(drm_client_buffer_vmap);
     361             : 
     362             : /**
     363             :  * drm_client_buffer_vunmap - Unmap DRM client buffer
     364             :  * @buffer: DRM client buffer
     365             :  *
     366             :  * This function removes a client buffer's memory mapping. Calling this
     367             :  * function is only required by clients that manage their buffer mappings
     368             :  * by themselves.
     369             :  */
     370           0 : void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
     371             : {
     372           0 :         struct iosys_map *map = &buffer->map;
     373             : 
     374           0 :         drm_gem_vunmap_unlocked(buffer->gem, map);
     375           0 : }
     376             : EXPORT_SYMBOL(drm_client_buffer_vunmap);
     377             : 
     378           0 : static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
     379             : {
     380             :         int ret;
     381             : 
     382           0 :         if (!buffer->fb)
     383             :                 return;
     384             : 
     385           0 :         ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
     386           0 :         if (ret)
     387           0 :                 drm_err(buffer->client->dev,
     388             :                         "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
     389             : 
     390           0 :         buffer->fb = NULL;
     391             : }
     392             : 
     393           0 : static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
     394             :                                    u32 width, u32 height, u32 format,
     395             :                                    u32 handle)
     396             : {
     397           0 :         struct drm_client_dev *client = buffer->client;
     398           0 :         struct drm_mode_fb_cmd fb_req = { };
     399             :         const struct drm_format_info *info;
     400             :         int ret;
     401             : 
     402           0 :         info = drm_format_info(format);
     403           0 :         fb_req.bpp = drm_format_info_bpp(info, 0);
     404           0 :         fb_req.depth = info->depth;
     405           0 :         fb_req.width = width;
     406           0 :         fb_req.height = height;
     407           0 :         fb_req.handle = handle;
     408           0 :         fb_req.pitch = buffer->pitch;
     409             : 
     410           0 :         ret = drm_mode_addfb(client->dev, &fb_req, client->file);
     411           0 :         if (ret)
     412             :                 return ret;
     413             : 
     414           0 :         buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
     415           0 :         if (WARN_ON(!buffer->fb))
     416             :                 return -ENOENT;
     417             : 
     418             :         /* drop the reference we picked up in framebuffer lookup */
     419           0 :         drm_framebuffer_put(buffer->fb);
     420             : 
     421           0 :         strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
     422             : 
     423             :         return 0;
     424             : }
     425             : 
     426             : /**
     427             :  * drm_client_framebuffer_create - Create a client framebuffer
     428             :  * @client: DRM client
     429             :  * @width: Framebuffer width
     430             :  * @height: Framebuffer height
     431             :  * @format: Buffer format
     432             :  *
     433             :  * This function creates a &drm_client_buffer which consists of a
     434             :  * &drm_framebuffer backed by a dumb buffer.
     435             :  * Call drm_client_framebuffer_delete() to free the buffer.
     436             :  *
     437             :  * Returns:
     438             :  * Pointer to a client buffer or an error pointer on failure.
     439             :  */
     440             : struct drm_client_buffer *
     441           0 : drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
     442             : {
     443             :         struct drm_client_buffer *buffer;
     444             :         u32 handle;
     445             :         int ret;
     446             : 
     447           0 :         buffer = drm_client_buffer_create(client, width, height, format,
     448             :                                           &handle);
     449           0 :         if (IS_ERR(buffer))
     450             :                 return buffer;
     451             : 
     452           0 :         ret = drm_client_buffer_addfb(buffer, width, height, format, handle);
     453             : 
     454             :         /*
     455             :          * The handle is only needed for creating the framebuffer, destroy it
     456             :          * again to solve a circular dependency should anybody export the GEM
     457             :          * object as DMA-buf. The framebuffer and our buffer structure are still
     458             :          * holding references to the GEM object to prevent its destruction.
     459             :          */
     460           0 :         drm_mode_destroy_dumb(client->dev, handle, client->file);
     461             : 
     462           0 :         if (ret) {
     463           0 :                 drm_client_buffer_delete(buffer);
     464           0 :                 return ERR_PTR(ret);
     465             :         }
     466             : 
     467             :         return buffer;
     468             : }
     469             : EXPORT_SYMBOL(drm_client_framebuffer_create);
     470             : 
     471             : /**
     472             :  * drm_client_framebuffer_delete - Delete a client framebuffer
     473             :  * @buffer: DRM client buffer (can be NULL)
     474             :  */
     475           0 : void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
     476             : {
     477           0 :         if (!buffer)
     478             :                 return;
     479             : 
     480           0 :         drm_client_buffer_rmfb(buffer);
     481           0 :         drm_client_buffer_delete(buffer);
     482             : }
     483             : EXPORT_SYMBOL(drm_client_framebuffer_delete);
     484             : 
     485             : /**
     486             :  * drm_client_framebuffer_flush - Manually flush client framebuffer
     487             :  * @buffer: DRM client buffer (can be NULL)
     488             :  * @rect: Damage rectangle (if NULL flushes all)
     489             :  *
     490             :  * This calls &drm_framebuffer_funcs->dirty (if present) to flush buffer changes
     491             :  * for drivers that need it.
     492             :  *
     493             :  * Returns:
     494             :  * Zero on success or negative error code on failure.
     495             :  */
     496           0 : int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect)
     497             : {
     498           0 :         if (!buffer || !buffer->fb || !buffer->fb->funcs->dirty)
     499             :                 return 0;
     500             : 
     501           0 :         if (rect) {
     502           0 :                 struct drm_clip_rect clip = {
     503           0 :                         .x1 = rect->x1,
     504           0 :                         .y1 = rect->y1,
     505           0 :                         .x2 = rect->x2,
     506           0 :                         .y2 = rect->y2,
     507             :                 };
     508             : 
     509           0 :                 return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
     510             :                                                 0, 0, &clip, 1);
     511             :         }
     512             : 
     513           0 :         return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
     514             :                                         0, 0, NULL, 0);
     515             : }
     516             : EXPORT_SYMBOL(drm_client_framebuffer_flush);
     517             : 
     518             : #ifdef CONFIG_DEBUG_FS
     519             : static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
     520             : {
     521             :         struct drm_debugfs_entry *entry = m->private;
     522             :         struct drm_device *dev = entry->dev;
     523             :         struct drm_printer p = drm_seq_file_printer(m);
     524             :         struct drm_client_dev *client;
     525             : 
     526             :         mutex_lock(&dev->clientlist_mutex);
     527             :         list_for_each_entry(client, &dev->clientlist, list)
     528             :                 drm_printf(&p, "%s\n", client->name);
     529             :         mutex_unlock(&dev->clientlist_mutex);
     530             : 
     531             :         return 0;
     532             : }
     533             : 
     534             : static const struct drm_debugfs_info drm_client_debugfs_list[] = {
     535             :         { "internal_clients", drm_client_debugfs_internal_clients, 0 },
     536             : };
     537             : 
     538             : void drm_client_debugfs_init(struct drm_minor *minor)
     539             : {
     540             :         drm_debugfs_add_files(minor->dev, drm_client_debugfs_list,
     541             :                               ARRAY_SIZE(drm_client_debugfs_list));
     542             : }
     543             : #endif

Generated by: LCOV version 1.14