// SPDX-License-Identifier: GPL-2.0 or MIT1/*2* Copyright 2018 Noralf Trønnes3*/45#include <linux/export.h>6#include <linux/iosys-map.h>7#include <linux/list.h>8#include <linux/mutex.h>9#include <linux/seq_file.h>10#include <linux/slab.h>1112#include <drm/drm_client.h>13#include <drm/drm_device.h>14#include <drm/drm_drv.h>15#include <drm/drm_file.h>16#include <drm/drm_fourcc.h>17#include <drm/drm_framebuffer.h>18#include <drm/drm_gem.h>19#include <drm/drm_mode.h>20#include <drm/drm_print.h>2122#include "drm_crtc_internal.h"23#include "drm_internal.h"2425/**26* DOC: overview27*28* This library provides support for clients running in the kernel like fbdev and bootsplash.29*30* GEM drivers which provide a GEM based dumb buffer with a virtual address are supported.31*/3233static int drm_client_open(struct drm_client_dev *client)34{35struct drm_device *dev = client->dev;36struct drm_file *file;3738file = drm_file_alloc(dev->primary);39if (IS_ERR(file))40return PTR_ERR(file);4142mutex_lock(&dev->filelist_mutex);43list_add(&file->lhead, &dev->filelist_internal);44mutex_unlock(&dev->filelist_mutex);4546client->file = file;4748return 0;49}5051static void drm_client_close(struct drm_client_dev *client)52{53struct drm_device *dev = client->dev;5455mutex_lock(&dev->filelist_mutex);56list_del(&client->file->lhead);57mutex_unlock(&dev->filelist_mutex);5859drm_file_free(client->file);60}6162/**63* drm_client_init - Initialise a DRM client64* @dev: DRM device65* @client: DRM client66* @name: Client name67* @funcs: DRM client functions (optional)68*69* This initialises the client and opens a &drm_file.70* Use drm_client_register() to complete the process.71* The caller needs to hold a reference on @dev before calling this function.72* The client is freed when the &drm_device is unregistered. See drm_client_release().73*74* Returns:75* Zero on success or negative error code on failure.76*/77int drm_client_init(struct drm_device *dev, struct drm_client_dev *client,78const char *name, const struct drm_client_funcs *funcs)79{80int ret;8182if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create)83return -EOPNOTSUPP;8485client->dev = dev;86client->name = name;87client->funcs = funcs;8889ret = drm_client_modeset_create(client);90if (ret)91return ret;9293ret = drm_client_open(client);94if (ret)95goto err_free;9697drm_dev_get(dev);9899return 0;100101err_free:102drm_client_modeset_free(client);103return ret;104}105EXPORT_SYMBOL(drm_client_init);106107/**108* drm_client_register - Register client109* @client: DRM client110*111* Add the client to the &drm_device client list to activate its callbacks.112* @client must be initialized by a call to drm_client_init(). After113* drm_client_register() it is no longer permissible to call drm_client_release()114* directly (outside the unregister callback), instead cleanup will happen115* automatically on driver unload.116*117* Registering a client generates a hotplug event that allows the client118* to set up its display from pre-existing outputs. The client must have119* initialized its state to able to handle the hotplug event successfully.120*/121void drm_client_register(struct drm_client_dev *client)122{123struct drm_device *dev = client->dev;124int ret;125126mutex_lock(&dev->clientlist_mutex);127list_add(&client->list, &dev->clientlist);128129if (client->funcs && client->funcs->hotplug) {130/*131* Perform an initial hotplug event to pick up the132* display configuration for the client. This step133* has to be performed *after* registering the client134* in the list of clients, or a concurrent hotplug135* event might be lost; leaving the display off.136*137* Hold the clientlist_mutex as for a regular hotplug138* event.139*/140ret = client->funcs->hotplug(client);141if (ret)142drm_dbg_kms(dev, "client hotplug ret=%d\n", ret);143}144mutex_unlock(&dev->clientlist_mutex);145}146EXPORT_SYMBOL(drm_client_register);147148/**149* drm_client_release - Release DRM client resources150* @client: DRM client151*152* Releases resources by closing the &drm_file that was opened by drm_client_init().153* It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.154*155* This function should only be called from the unregister callback. An exception156* is fbdev which cannot free the buffer if userspace has open file descriptors.157*158* Note:159* Clients cannot initiate a release by themselves. This is done to keep the code simple.160* The driver has to be unloaded before the client can be unloaded.161*/162void drm_client_release(struct drm_client_dev *client)163{164struct drm_device *dev = client->dev;165166drm_dbg_kms(dev, "%s\n", client->name);167168drm_client_modeset_free(client);169drm_client_close(client);170drm_dev_put(dev);171}172EXPORT_SYMBOL(drm_client_release);173174static void drm_client_buffer_delete(struct drm_client_buffer *buffer)175{176if (buffer->gem) {177drm_gem_vunmap(buffer->gem, &buffer->map);178drm_gem_object_put(buffer->gem);179}180181kfree(buffer);182}183184static struct drm_client_buffer *185drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,186u32 format, u32 *handle)187{188const struct drm_format_info *info = drm_format_info(format);189struct drm_mode_create_dumb dumb_args = { };190struct drm_device *dev = client->dev;191struct drm_client_buffer *buffer;192struct drm_gem_object *obj;193int ret;194195buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);196if (!buffer)197return ERR_PTR(-ENOMEM);198199buffer->client = client;200201dumb_args.width = width;202dumb_args.height = height;203dumb_args.bpp = drm_format_info_bpp(info, 0);204ret = drm_mode_create_dumb(dev, &dumb_args, client->file);205if (ret)206goto err_delete;207208obj = drm_gem_object_lookup(client->file, dumb_args.handle);209if (!obj) {210ret = -ENOENT;211goto err_delete;212}213214buffer->pitch = dumb_args.pitch;215buffer->gem = obj;216*handle = dumb_args.handle;217218return buffer;219220err_delete:221drm_client_buffer_delete(buffer);222223return ERR_PTR(ret);224}225226/**227* drm_client_buffer_vmap_local - Map DRM client buffer into address space228* @buffer: DRM client buffer229* @map_copy: Returns the mapped memory's address230*231* This function maps a client buffer into kernel address space. If the232* buffer is already mapped, it returns the existing mapping's address.233*234* Client buffer mappings are not ref'counted. Each call to235* drm_client_buffer_vmap_local() should be closely followed by a call to236* drm_client_buffer_vunmap_local(). See drm_client_buffer_vmap() for237* long-term mappings.238*239* The returned address is a copy of the internal value. In contrast to240* other vmap interfaces, you don't need it for the client's vunmap241* function. So you can modify it at will during blit and draw operations.242*243* Returns:244* 0 on success, or a negative errno code otherwise.245*/246int drm_client_buffer_vmap_local(struct drm_client_buffer *buffer,247struct iosys_map *map_copy)248{249struct drm_gem_object *gem = buffer->gem;250struct iosys_map *map = &buffer->map;251int ret;252253drm_gem_lock(gem);254255ret = drm_gem_vmap_locked(gem, map);256if (ret)257goto err_drm_gem_vmap_unlocked;258*map_copy = *map;259260return 0;261262err_drm_gem_vmap_unlocked:263drm_gem_unlock(gem);264return ret;265}266EXPORT_SYMBOL(drm_client_buffer_vmap_local);267268/**269* drm_client_buffer_vunmap_local - Unmap DRM client buffer270* @buffer: DRM client buffer271*272* This function removes a client buffer's memory mapping established273* with drm_client_buffer_vunmap_local(). Calling this function is only274* required by clients that manage their buffer mappings by themselves.275*/276void drm_client_buffer_vunmap_local(struct drm_client_buffer *buffer)277{278struct drm_gem_object *gem = buffer->gem;279struct iosys_map *map = &buffer->map;280281drm_gem_vunmap_locked(gem, map);282drm_gem_unlock(gem);283}284EXPORT_SYMBOL(drm_client_buffer_vunmap_local);285286/**287* drm_client_buffer_vmap - Map DRM client buffer into address space288* @buffer: DRM client buffer289* @map_copy: Returns the mapped memory's address290*291* This function maps a client buffer into kernel address space. If the292* buffer is already mapped, it returns the existing mapping's address.293*294* Client buffer mappings are not ref'counted. Each call to295* drm_client_buffer_vmap() should be followed by a call to296* drm_client_buffer_vunmap(); or the client buffer should be mapped297* throughout its lifetime.298*299* The returned address is a copy of the internal value. In contrast to300* other vmap interfaces, you don't need it for the client's vunmap301* function. So you can modify it at will during blit and draw operations.302*303* Returns:304* 0 on success, or a negative errno code otherwise.305*/306int drm_client_buffer_vmap(struct drm_client_buffer *buffer,307struct iosys_map *map_copy)308{309int ret;310311ret = drm_gem_vmap(buffer->gem, &buffer->map);312if (ret)313return ret;314*map_copy = buffer->map;315316return 0;317}318EXPORT_SYMBOL(drm_client_buffer_vmap);319320/**321* drm_client_buffer_vunmap - Unmap DRM client buffer322* @buffer: DRM client buffer323*324* This function removes a client buffer's memory mapping. Calling this325* function is only required by clients that manage their buffer mappings326* by themselves.327*/328void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)329{330drm_gem_vunmap(buffer->gem, &buffer->map);331}332EXPORT_SYMBOL(drm_client_buffer_vunmap);333334static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)335{336int ret;337338if (!buffer->fb)339return;340341ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);342if (ret)343drm_err(buffer->client->dev,344"Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);345346buffer->fb = NULL;347}348349static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,350u32 width, u32 height, u32 format,351u32 handle)352{353struct drm_client_dev *client = buffer->client;354struct drm_mode_fb_cmd2 fb_req = { };355int ret;356357fb_req.width = width;358fb_req.height = height;359fb_req.pixel_format = format;360fb_req.handles[0] = handle;361fb_req.pitches[0] = buffer->pitch;362363ret = drm_mode_addfb2(client->dev, &fb_req, client->file);364if (ret)365return ret;366367buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);368if (WARN_ON(!buffer->fb))369return -ENOENT;370371/* drop the reference we picked up in framebuffer lookup */372drm_framebuffer_put(buffer->fb);373374strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);375376return 0;377}378379/**380* drm_client_framebuffer_create - Create a client framebuffer381* @client: DRM client382* @width: Framebuffer width383* @height: Framebuffer height384* @format: Buffer format385*386* This function creates a &drm_client_buffer which consists of a387* &drm_framebuffer backed by a dumb buffer.388* Call drm_client_framebuffer_delete() to free the buffer.389*390* Returns:391* Pointer to a client buffer or an error pointer on failure.392*/393struct drm_client_buffer *394drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)395{396struct drm_client_buffer *buffer;397u32 handle;398int ret;399400buffer = drm_client_buffer_create(client, width, height, format,401&handle);402if (IS_ERR(buffer))403return buffer;404405ret = drm_client_buffer_addfb(buffer, width, height, format, handle);406407/*408* The handle is only needed for creating the framebuffer, destroy it409* again to solve a circular dependency should anybody export the GEM410* object as DMA-buf. The framebuffer and our buffer structure are still411* holding references to the GEM object to prevent its destruction.412*/413drm_mode_destroy_dumb(client->dev, handle, client->file);414415if (ret) {416drm_client_buffer_delete(buffer);417return ERR_PTR(ret);418}419420return buffer;421}422EXPORT_SYMBOL(drm_client_framebuffer_create);423424/**425* drm_client_framebuffer_delete - Delete a client framebuffer426* @buffer: DRM client buffer (can be NULL)427*/428void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)429{430if (!buffer)431return;432433drm_client_buffer_rmfb(buffer);434drm_client_buffer_delete(buffer);435}436EXPORT_SYMBOL(drm_client_framebuffer_delete);437438/**439* drm_client_framebuffer_flush - Manually flush client framebuffer440* @buffer: DRM client buffer (can be NULL)441* @rect: Damage rectangle (if NULL flushes all)442*443* This calls &drm_framebuffer_funcs->dirty (if present) to flush buffer changes444* for drivers that need it.445*446* Returns:447* Zero on success or negative error code on failure.448*/449int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect)450{451if (!buffer || !buffer->fb || !buffer->fb->funcs->dirty)452return 0;453454if (rect) {455struct drm_clip_rect clip = {456.x1 = rect->x1,457.y1 = rect->y1,458.x2 = rect->x2,459.y2 = rect->y2,460};461462return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,4630, 0, &clip, 1);464}465466return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,4670, 0, NULL, 0);468}469EXPORT_SYMBOL(drm_client_framebuffer_flush);470471472