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