18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or MIT 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2018 Noralf Trønnes 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/list.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/mutex.h> 98c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <drm/drm_client.h> 138c2ecf20Sopenharmony_ci#include <drm/drm_debugfs.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_device.h> 158c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_file.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_framebuffer.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_gem.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_mode.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_print.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "drm_crtc_internal.h" 248c2ecf20Sopenharmony_ci#include "drm_internal.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/** 278c2ecf20Sopenharmony_ci * DOC: overview 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * This library provides support for clients running in the kernel like fbdev and bootsplash. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int drm_client_open(struct drm_client_dev *client) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct drm_device *dev = client->dev; 378c2ecf20Sopenharmony_ci struct drm_file *file; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci file = drm_file_alloc(dev->primary); 408c2ecf20Sopenharmony_ci if (IS_ERR(file)) 418c2ecf20Sopenharmony_ci return PTR_ERR(file); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci mutex_lock(&dev->filelist_mutex); 448c2ecf20Sopenharmony_ci list_add(&file->lhead, &dev->filelist_internal); 458c2ecf20Sopenharmony_ci mutex_unlock(&dev->filelist_mutex); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci client->file = file; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return 0; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void drm_client_close(struct drm_client_dev *client) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct drm_device *dev = client->dev; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci mutex_lock(&dev->filelist_mutex); 578c2ecf20Sopenharmony_ci list_del(&client->file->lhead); 588c2ecf20Sopenharmony_ci mutex_unlock(&dev->filelist_mutex); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci drm_file_free(client->file); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/** 648c2ecf20Sopenharmony_ci * drm_client_init - Initialise a DRM client 658c2ecf20Sopenharmony_ci * @dev: DRM device 668c2ecf20Sopenharmony_ci * @client: DRM client 678c2ecf20Sopenharmony_ci * @name: Client name 688c2ecf20Sopenharmony_ci * @funcs: DRM client functions (optional) 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * This initialises the client and opens a &drm_file. 718c2ecf20Sopenharmony_ci * Use drm_client_register() to complete the process. 728c2ecf20Sopenharmony_ci * The caller needs to hold a reference on @dev before calling this function. 738c2ecf20Sopenharmony_ci * The client is freed when the &drm_device is unregistered. See drm_client_release(). 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * Returns: 768c2ecf20Sopenharmony_ci * Zero on success or negative error code on failure. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ciint drm_client_init(struct drm_device *dev, struct drm_client_dev *client, 798c2ecf20Sopenharmony_ci const char *name, const struct drm_client_funcs *funcs) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int ret; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create) 848c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (funcs && !try_module_get(funcs->owner)) 878c2ecf20Sopenharmony_ci return -ENODEV; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci client->dev = dev; 908c2ecf20Sopenharmony_ci client->name = name; 918c2ecf20Sopenharmony_ci client->funcs = funcs; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = drm_client_modeset_create(client); 948c2ecf20Sopenharmony_ci if (ret) 958c2ecf20Sopenharmony_ci goto err_put_module; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci ret = drm_client_open(client); 988c2ecf20Sopenharmony_ci if (ret) 998c2ecf20Sopenharmony_ci goto err_free; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci drm_dev_get(dev); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cierr_free: 1068c2ecf20Sopenharmony_ci drm_client_modeset_free(client); 1078c2ecf20Sopenharmony_cierr_put_module: 1088c2ecf20Sopenharmony_ci if (funcs) 1098c2ecf20Sopenharmony_ci module_put(funcs->owner); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_init); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/** 1168c2ecf20Sopenharmony_ci * drm_client_register - Register client 1178c2ecf20Sopenharmony_ci * @client: DRM client 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Add the client to the &drm_device client list to activate its callbacks. 1208c2ecf20Sopenharmony_ci * @client must be initialized by a call to drm_client_init(). After 1218c2ecf20Sopenharmony_ci * drm_client_register() it is no longer permissible to call drm_client_release() 1228c2ecf20Sopenharmony_ci * directly (outside the unregister callback), instead cleanup will happen 1238c2ecf20Sopenharmony_ci * automatically on driver unload. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_civoid drm_client_register(struct drm_client_dev *client) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct drm_device *dev = client->dev; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci mutex_lock(&dev->clientlist_mutex); 1308c2ecf20Sopenharmony_ci list_add(&client->list, &dev->clientlist); 1318c2ecf20Sopenharmony_ci mutex_unlock(&dev->clientlist_mutex); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_register); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci/** 1368c2ecf20Sopenharmony_ci * drm_client_release - Release DRM client resources 1378c2ecf20Sopenharmony_ci * @client: DRM client 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * Releases resources by closing the &drm_file that was opened by drm_client_init(). 1408c2ecf20Sopenharmony_ci * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set. 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * This function should only be called from the unregister callback. An exception 1438c2ecf20Sopenharmony_ci * is fbdev which cannot free the buffer if userspace has open file descriptors. 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * Note: 1468c2ecf20Sopenharmony_ci * Clients cannot initiate a release by themselves. This is done to keep the code simple. 1478c2ecf20Sopenharmony_ci * The driver has to be unloaded before the client can be unloaded. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_civoid drm_client_release(struct drm_client_dev *client) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct drm_device *dev = client->dev; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "%s\n", client->name); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci drm_client_modeset_free(client); 1568c2ecf20Sopenharmony_ci drm_client_close(client); 1578c2ecf20Sopenharmony_ci drm_dev_put(dev); 1588c2ecf20Sopenharmony_ci if (client->funcs) 1598c2ecf20Sopenharmony_ci module_put(client->funcs->owner); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_release); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_civoid drm_client_dev_unregister(struct drm_device *dev) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct drm_client_dev *client, *tmp; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1688c2ecf20Sopenharmony_ci return; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci mutex_lock(&dev->clientlist_mutex); 1718c2ecf20Sopenharmony_ci list_for_each_entry_safe(client, tmp, &dev->clientlist, list) { 1728c2ecf20Sopenharmony_ci list_del(&client->list); 1738c2ecf20Sopenharmony_ci if (client->funcs && client->funcs->unregister) { 1748c2ecf20Sopenharmony_ci client->funcs->unregister(client); 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci drm_client_release(client); 1778c2ecf20Sopenharmony_ci kfree(client); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci mutex_unlock(&dev->clientlist_mutex); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci/** 1848c2ecf20Sopenharmony_ci * drm_client_dev_hotplug - Send hotplug event to clients 1858c2ecf20Sopenharmony_ci * @dev: DRM device 1868c2ecf20Sopenharmony_ci * 1878c2ecf20Sopenharmony_ci * This function calls the &drm_client_funcs.hotplug callback on the attached clients. 1888c2ecf20Sopenharmony_ci * 1898c2ecf20Sopenharmony_ci * drm_kms_helper_hotplug_event() calls this function, so drivers that use it 1908c2ecf20Sopenharmony_ci * don't need to call this function themselves. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_civoid drm_client_dev_hotplug(struct drm_device *dev) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct drm_client_dev *client; 1958c2ecf20Sopenharmony_ci int ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci mutex_lock(&dev->clientlist_mutex); 2018c2ecf20Sopenharmony_ci list_for_each_entry(client, &dev->clientlist, list) { 2028c2ecf20Sopenharmony_ci if (!client->funcs || !client->funcs->hotplug) 2038c2ecf20Sopenharmony_ci continue; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = client->funcs->hotplug(client); 2068c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci mutex_unlock(&dev->clientlist_mutex); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_dev_hotplug); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid drm_client_dev_restore(struct drm_device *dev) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct drm_client_dev *client; 2158c2ecf20Sopenharmony_ci int ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mutex_lock(&dev->clientlist_mutex); 2218c2ecf20Sopenharmony_ci list_for_each_entry(client, &dev->clientlist, list) { 2228c2ecf20Sopenharmony_ci if (!client->funcs || !client->funcs->restore) 2238c2ecf20Sopenharmony_ci continue; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = client->funcs->restore(client); 2268c2ecf20Sopenharmony_ci drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); 2278c2ecf20Sopenharmony_ci if (!ret) /* The first one to return zero gets the privilege to restore */ 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci mutex_unlock(&dev->clientlist_mutex); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic void drm_client_buffer_delete(struct drm_client_buffer *buffer) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct drm_device *dev = buffer->client->dev; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci drm_gem_vunmap(buffer->gem, buffer->vaddr); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (buffer->gem) 2408c2ecf20Sopenharmony_ci drm_gem_object_put(buffer->gem); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (buffer->handle) 2438c2ecf20Sopenharmony_ci drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci kfree(buffer); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic struct drm_client_buffer * 2498c2ecf20Sopenharmony_cidrm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci const struct drm_format_info *info = drm_format_info(format); 2528c2ecf20Sopenharmony_ci struct drm_mode_create_dumb dumb_args = { }; 2538c2ecf20Sopenharmony_ci struct drm_device *dev = client->dev; 2548c2ecf20Sopenharmony_ci struct drm_client_buffer *buffer; 2558c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); 2598c2ecf20Sopenharmony_ci if (!buffer) 2608c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci buffer->client = client; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dumb_args.width = width; 2658c2ecf20Sopenharmony_ci dumb_args.height = height; 2668c2ecf20Sopenharmony_ci dumb_args.bpp = info->cpp[0] * 8; 2678c2ecf20Sopenharmony_ci ret = drm_mode_create_dumb(dev, &dumb_args, client->file); 2688c2ecf20Sopenharmony_ci if (ret) 2698c2ecf20Sopenharmony_ci goto err_delete; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci buffer->handle = dumb_args.handle; 2728c2ecf20Sopenharmony_ci buffer->pitch = dumb_args.pitch; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci obj = drm_gem_object_lookup(client->file, dumb_args.handle); 2758c2ecf20Sopenharmony_ci if (!obj) { 2768c2ecf20Sopenharmony_ci ret = -ENOENT; 2778c2ecf20Sopenharmony_ci goto err_delete; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci buffer->gem = obj; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return buffer; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cierr_delete: 2858c2ecf20Sopenharmony_ci drm_client_buffer_delete(buffer); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/** 2918c2ecf20Sopenharmony_ci * drm_client_buffer_vmap - Map DRM client buffer into address space 2928c2ecf20Sopenharmony_ci * @buffer: DRM client buffer 2938c2ecf20Sopenharmony_ci * 2948c2ecf20Sopenharmony_ci * This function maps a client buffer into kernel address space. If the 2958c2ecf20Sopenharmony_ci * buffer is already mapped, it returns the mapping's address. 2968c2ecf20Sopenharmony_ci * 2978c2ecf20Sopenharmony_ci * Client buffer mappings are not ref'counted. Each call to 2988c2ecf20Sopenharmony_ci * drm_client_buffer_vmap() should be followed by a call to 2998c2ecf20Sopenharmony_ci * drm_client_buffer_vunmap(); or the client buffer should be mapped 3008c2ecf20Sopenharmony_ci * throughout its lifetime. 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * Returns: 3038c2ecf20Sopenharmony_ci * The mapped memory's address 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_civoid *drm_client_buffer_vmap(struct drm_client_buffer *buffer) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci void *vaddr; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (buffer->vaddr) 3108c2ecf20Sopenharmony_ci return buffer->vaddr; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * FIXME: The dependency on GEM here isn't required, we could 3148c2ecf20Sopenharmony_ci * convert the driver handle to a dma-buf instead and use the 3158c2ecf20Sopenharmony_ci * backend-agnostic dma-buf vmap support instead. This would 3168c2ecf20Sopenharmony_ci * require that the handle2fd prime ioctl is reworked to pull the 3178c2ecf20Sopenharmony_ci * fd_install step out of the driver backend hooks, to make that 3188c2ecf20Sopenharmony_ci * final step optional for internal users. 3198c2ecf20Sopenharmony_ci */ 3208c2ecf20Sopenharmony_ci vaddr = drm_gem_vmap(buffer->gem); 3218c2ecf20Sopenharmony_ci if (IS_ERR(vaddr)) 3228c2ecf20Sopenharmony_ci return vaddr; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci buffer->vaddr = vaddr; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return vaddr; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_buffer_vmap); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/** 3318c2ecf20Sopenharmony_ci * drm_client_buffer_vunmap - Unmap DRM client buffer 3328c2ecf20Sopenharmony_ci * @buffer: DRM client buffer 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * This function removes a client buffer's memory mapping. Calling this 3358c2ecf20Sopenharmony_ci * function is only required by clients that manage their buffer mappings 3368c2ecf20Sopenharmony_ci * by themselves. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_civoid drm_client_buffer_vunmap(struct drm_client_buffer *buffer) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci drm_gem_vunmap(buffer->gem, buffer->vaddr); 3418c2ecf20Sopenharmony_ci buffer->vaddr = NULL; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_buffer_vunmap); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void drm_client_buffer_rmfb(struct drm_client_buffer *buffer) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci int ret; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (!buffer->fb) 3508c2ecf20Sopenharmony_ci return; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci drm_err(buffer->client->dev, 3558c2ecf20Sopenharmony_ci "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci buffer->fb = NULL; 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_cistatic int drm_client_buffer_addfb(struct drm_client_buffer *buffer, 3618c2ecf20Sopenharmony_ci u32 width, u32 height, u32 format) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct drm_client_dev *client = buffer->client; 3648c2ecf20Sopenharmony_ci struct drm_mode_fb_cmd fb_req = { }; 3658c2ecf20Sopenharmony_ci const struct drm_format_info *info; 3668c2ecf20Sopenharmony_ci int ret; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci info = drm_format_info(format); 3698c2ecf20Sopenharmony_ci fb_req.bpp = info->cpp[0] * 8; 3708c2ecf20Sopenharmony_ci fb_req.depth = info->depth; 3718c2ecf20Sopenharmony_ci fb_req.width = width; 3728c2ecf20Sopenharmony_ci fb_req.height = height; 3738c2ecf20Sopenharmony_ci fb_req.handle = buffer->handle; 3748c2ecf20Sopenharmony_ci fb_req.pitch = buffer->pitch; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret = drm_mode_addfb(client->dev, &fb_req, client->file); 3778c2ecf20Sopenharmony_ci if (ret) 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id); 3818c2ecf20Sopenharmony_ci if (WARN_ON(!buffer->fb)) 3828c2ecf20Sopenharmony_ci return -ENOENT; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* drop the reference we picked up in framebuffer lookup */ 3858c2ecf20Sopenharmony_ci drm_framebuffer_put(buffer->fb); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci/** 3938c2ecf20Sopenharmony_ci * drm_client_framebuffer_create - Create a client framebuffer 3948c2ecf20Sopenharmony_ci * @client: DRM client 3958c2ecf20Sopenharmony_ci * @width: Framebuffer width 3968c2ecf20Sopenharmony_ci * @height: Framebuffer height 3978c2ecf20Sopenharmony_ci * @format: Buffer format 3988c2ecf20Sopenharmony_ci * 3998c2ecf20Sopenharmony_ci * This function creates a &drm_client_buffer which consists of a 4008c2ecf20Sopenharmony_ci * &drm_framebuffer backed by a dumb buffer. 4018c2ecf20Sopenharmony_ci * Call drm_client_framebuffer_delete() to free the buffer. 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci * Returns: 4048c2ecf20Sopenharmony_ci * Pointer to a client buffer or an error pointer on failure. 4058c2ecf20Sopenharmony_ci */ 4068c2ecf20Sopenharmony_cistruct drm_client_buffer * 4078c2ecf20Sopenharmony_cidrm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct drm_client_buffer *buffer; 4108c2ecf20Sopenharmony_ci int ret; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci buffer = drm_client_buffer_create(client, width, height, format); 4138c2ecf20Sopenharmony_ci if (IS_ERR(buffer)) 4148c2ecf20Sopenharmony_ci return buffer; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = drm_client_buffer_addfb(buffer, width, height, format); 4178c2ecf20Sopenharmony_ci if (ret) { 4188c2ecf20Sopenharmony_ci drm_client_buffer_delete(buffer); 4198c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return buffer; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_framebuffer_create); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci/** 4278c2ecf20Sopenharmony_ci * drm_client_framebuffer_delete - Delete a client framebuffer 4288c2ecf20Sopenharmony_ci * @buffer: DRM client buffer (can be NULL) 4298c2ecf20Sopenharmony_ci */ 4308c2ecf20Sopenharmony_civoid drm_client_framebuffer_delete(struct drm_client_buffer *buffer) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci if (!buffer) 4338c2ecf20Sopenharmony_ci return; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci drm_client_buffer_rmfb(buffer); 4368c2ecf20Sopenharmony_ci drm_client_buffer_delete(buffer); 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_framebuffer_delete); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci/** 4418c2ecf20Sopenharmony_ci * drm_client_framebuffer_flush - Manually flush client framebuffer 4428c2ecf20Sopenharmony_ci * @buffer: DRM client buffer (can be NULL) 4438c2ecf20Sopenharmony_ci * @rect: Damage rectangle (if NULL flushes all) 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * This calls &drm_framebuffer_funcs->dirty (if present) to flush buffer changes 4468c2ecf20Sopenharmony_ci * for drivers that need it. 4478c2ecf20Sopenharmony_ci * 4488c2ecf20Sopenharmony_ci * Returns: 4498c2ecf20Sopenharmony_ci * Zero on success or negative error code on failure. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ciint drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci if (!buffer || !buffer->fb || !buffer->fb->funcs->dirty) 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (rect) { 4578c2ecf20Sopenharmony_ci struct drm_clip_rect clip = { 4588c2ecf20Sopenharmony_ci .x1 = rect->x1, 4598c2ecf20Sopenharmony_ci .y1 = rect->y1, 4608c2ecf20Sopenharmony_ci .x2 = rect->x2, 4618c2ecf20Sopenharmony_ci .y2 = rect->y2, 4628c2ecf20Sopenharmony_ci }; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file, 4658c2ecf20Sopenharmony_ci 0, 0, &clip, 1); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file, 4698c2ecf20Sopenharmony_ci 0, 0, NULL, 0); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_client_framebuffer_flush); 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 4748c2ecf20Sopenharmony_cistatic int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct drm_info_node *node = m->private; 4778c2ecf20Sopenharmony_ci struct drm_device *dev = node->minor->dev; 4788c2ecf20Sopenharmony_ci struct drm_printer p = drm_seq_file_printer(m); 4798c2ecf20Sopenharmony_ci struct drm_client_dev *client; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci mutex_lock(&dev->clientlist_mutex); 4828c2ecf20Sopenharmony_ci list_for_each_entry(client, &dev->clientlist, list) 4838c2ecf20Sopenharmony_ci drm_printf(&p, "%s\n", client->name); 4848c2ecf20Sopenharmony_ci mutex_unlock(&dev->clientlist_mutex); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct drm_info_list drm_client_debugfs_list[] = { 4908c2ecf20Sopenharmony_ci { "internal_clients", drm_client_debugfs_internal_clients, 0 }, 4918c2ecf20Sopenharmony_ci}; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_civoid drm_client_debugfs_init(struct drm_minor *minor) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci drm_debugfs_create_files(drm_client_debugfs_list, 4968c2ecf20Sopenharmony_ci ARRAY_SIZE(drm_client_debugfs_list), 4978c2ecf20Sopenharmony_ci minor->debugfs_root, minor); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci#endif 500