root/drivers/gpu/drm/drm_client.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. drm_client_open
  2. drm_client_close
  3. drm_client_init
  4. drm_client_register
  5. drm_client_release
  6. drm_client_dev_unregister
  7. drm_client_dev_hotplug
  8. drm_client_dev_restore
  9. drm_client_buffer_delete
  10. drm_client_buffer_create
  11. drm_client_buffer_vmap
  12. drm_client_buffer_vunmap
  13. drm_client_buffer_rmfb
  14. drm_client_buffer_addfb
  15. drm_client_framebuffer_create
  16. drm_client_framebuffer_delete
  17. drm_client_debugfs_internal_clients
  18. drm_client_debugfs_init

   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 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         int ret;
  82 
  83         if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create)
  84                 return -EOPNOTSUPP;
  85 
  86         if (funcs && !try_module_get(funcs->owner))
  87                 return -ENODEV;
  88 
  89         client->dev = dev;
  90         client->name = name;
  91         client->funcs = funcs;
  92 
  93         ret = drm_client_modeset_create(client);
  94         if (ret)
  95                 goto err_put_module;
  96 
  97         ret = drm_client_open(client);
  98         if (ret)
  99                 goto err_free;
 100 
 101         drm_dev_get(dev);
 102 
 103         return 0;
 104 
 105 err_free:
 106         drm_client_modeset_free(client);
 107 err_put_module:
 108         if (funcs)
 109                 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 void drm_client_register(struct drm_client_dev *client)
 126 {
 127         struct drm_device *dev = client->dev;
 128 
 129         mutex_lock(&dev->clientlist_mutex);
 130         list_add(&client->list, &dev->clientlist);
 131         mutex_unlock(&dev->clientlist_mutex);
 132 }
 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 void drm_client_release(struct drm_client_dev *client)
 150 {
 151         struct drm_device *dev = client->dev;
 152 
 153         DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name);
 154 
 155         drm_client_modeset_free(client);
 156         drm_client_close(client);
 157         drm_dev_put(dev);
 158         if (client->funcs)
 159                 module_put(client->funcs->owner);
 160 }
 161 EXPORT_SYMBOL(drm_client_release);
 162 
 163 void drm_client_dev_unregister(struct drm_device *dev)
 164 {
 165         struct drm_client_dev *client, *tmp;
 166 
 167         if (!drm_core_check_feature(dev, DRIVER_MODESET))
 168                 return;
 169 
 170         mutex_lock(&dev->clientlist_mutex);
 171         list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
 172                 list_del(&client->list);
 173                 if (client->funcs && client->funcs->unregister) {
 174                         client->funcs->unregister(client);
 175                 } else {
 176                         drm_client_release(client);
 177                         kfree(client);
 178                 }
 179         }
 180         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 void drm_client_dev_hotplug(struct drm_device *dev)
 193 {
 194         struct drm_client_dev *client;
 195         int ret;
 196 
 197         if (!drm_core_check_feature(dev, DRIVER_MODESET))
 198                 return;
 199 
 200         mutex_lock(&dev->clientlist_mutex);
 201         list_for_each_entry(client, &dev->clientlist, list) {
 202                 if (!client->funcs || !client->funcs->hotplug)
 203                         continue;
 204 
 205                 ret = client->funcs->hotplug(client);
 206                 DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
 207         }
 208         mutex_unlock(&dev->clientlist_mutex);
 209 }
 210 EXPORT_SYMBOL(drm_client_dev_hotplug);
 211 
 212 void drm_client_dev_restore(struct drm_device *dev)
 213 {
 214         struct drm_client_dev *client;
 215         int ret;
 216 
 217         if (!drm_core_check_feature(dev, DRIVER_MODESET))
 218                 return;
 219 
 220         mutex_lock(&dev->clientlist_mutex);
 221         list_for_each_entry(client, &dev->clientlist, list) {
 222                 if (!client->funcs || !client->funcs->restore)
 223                         continue;
 224 
 225                 ret = client->funcs->restore(client);
 226                 DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
 227                 if (!ret) /* The first one to return zero gets the privilege to restore */
 228                         break;
 229         }
 230         mutex_unlock(&dev->clientlist_mutex);
 231 }
 232 
 233 static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
 234 {
 235         struct drm_device *dev = buffer->client->dev;
 236 
 237         drm_gem_vunmap(buffer->gem, buffer->vaddr);
 238 
 239         if (buffer->gem)
 240                 drm_gem_object_put_unlocked(buffer->gem);
 241 
 242         if (buffer->handle)
 243                 drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
 244 
 245         kfree(buffer);
 246 }
 247 
 248 static struct drm_client_buffer *
 249 drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
 250 {
 251         const struct drm_format_info *info = drm_format_info(format);
 252         struct drm_mode_create_dumb dumb_args = { };
 253         struct drm_device *dev = client->dev;
 254         struct drm_client_buffer *buffer;
 255         struct drm_gem_object *obj;
 256         int ret;
 257 
 258         buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
 259         if (!buffer)
 260                 return ERR_PTR(-ENOMEM);
 261 
 262         buffer->client = client;
 263 
 264         dumb_args.width = width;
 265         dumb_args.height = height;
 266         dumb_args.bpp = info->cpp[0] * 8;
 267         ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
 268         if (ret)
 269                 goto err_delete;
 270 
 271         buffer->handle = dumb_args.handle;
 272         buffer->pitch = dumb_args.pitch;
 273 
 274         obj = drm_gem_object_lookup(client->file, dumb_args.handle);
 275         if (!obj)  {
 276                 ret = -ENOENT;
 277                 goto err_delete;
 278         }
 279 
 280         buffer->gem = obj;
 281 
 282         return buffer;
 283 
 284 err_delete:
 285         drm_client_buffer_delete(buffer);
 286 
 287         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 void *drm_client_buffer_vmap(struct drm_client_buffer *buffer)
 306 {
 307         void *vaddr;
 308 
 309         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         vaddr = drm_gem_vmap(buffer->gem);
 321         if (IS_ERR(vaddr))
 322                 return vaddr;
 323 
 324         buffer->vaddr = vaddr;
 325 
 326         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 void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
 339 {
 340         drm_gem_vunmap(buffer->gem, buffer->vaddr);
 341         buffer->vaddr = NULL;
 342 }
 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_DEV_ERROR(buffer->client->dev->dev,
 355                               "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
 356 
 357         buffer->fb = NULL;
 358 }
 359 
 360 static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
 361                                    u32 width, u32 height, u32 format)
 362 {
 363         struct drm_client_dev *client = buffer->client;
 364         struct drm_mode_fb_cmd fb_req = { };
 365         const struct drm_format_info *info;
 366         int ret;
 367 
 368         info = drm_format_info(format);
 369         fb_req.bpp = info->cpp[0] * 8;
 370         fb_req.depth = info->depth;
 371         fb_req.width = width;
 372         fb_req.height = height;
 373         fb_req.handle = buffer->handle;
 374         fb_req.pitch = buffer->pitch;
 375 
 376         ret = drm_mode_addfb(client->dev, &fb_req, client->file);
 377         if (ret)
 378                 return ret;
 379 
 380         buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
 381         if (WARN_ON(!buffer->fb))
 382                 return -ENOENT;
 383 
 384         /* drop the reference we picked up in framebuffer lookup */
 385         drm_framebuffer_put(buffer->fb);
 386 
 387         strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
 388 
 389         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 drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
 408 {
 409         struct drm_client_buffer *buffer;
 410         int ret;
 411 
 412         buffer = drm_client_buffer_create(client, width, height, format);
 413         if (IS_ERR(buffer))
 414                 return buffer;
 415 
 416         ret = drm_client_buffer_addfb(buffer, width, height, format);
 417         if (ret) {
 418                 drm_client_buffer_delete(buffer);
 419                 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 void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
 431 {
 432         if (!buffer)
 433                 return;
 434 
 435         drm_client_buffer_rmfb(buffer);
 436         drm_client_buffer_delete(buffer);
 437 }
 438 EXPORT_SYMBOL(drm_client_framebuffer_delete);
 439 
 440 #ifdef CONFIG_DEBUG_FS
 441 static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
 442 {
 443         struct drm_info_node *node = m->private;
 444         struct drm_device *dev = node->minor->dev;
 445         struct drm_printer p = drm_seq_file_printer(m);
 446         struct drm_client_dev *client;
 447 
 448         mutex_lock(&dev->clientlist_mutex);
 449         list_for_each_entry(client, &dev->clientlist, list)
 450                 drm_printf(&p, "%s\n", client->name);
 451         mutex_unlock(&dev->clientlist_mutex);
 452 
 453         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 int drm_client_debugfs_init(struct drm_minor *minor)
 461 {
 462         return drm_debugfs_create_files(drm_client_debugfs_list,
 463                                         ARRAY_SIZE(drm_client_debugfs_list),
 464                                         minor->debugfs_root, minor);
 465 }
 466 #endif

/* [<][>][^][v][top][bottom][index][help] */