LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_client.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 159 0.0 %
Date: 2022-04-01 14:17:54 Functions: 0 15 0.0 %
Branches: 0 72 0.0 %

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

Generated by: LCOV version 1.14