On Mon, Jul 02, 2018 at 03:54:25PM +0200, Noralf Trønnes wrote: > This the beginning of an API for in-kernel clients. > First out is a way to get a framebuffer backed by a dumb buffer. > > Only GEM drivers are supported. > The original idea of using an exported dma-buf was dropped because it > also creates an anonomous file descriptor which doesn't work when the > buffer is created from a kernel thread. The easy way out is to use > drm_driver.gem_prime_vmap to get the virtual address, which requires a > GEM object. This excludes the vmwgfx driver which is the only non-GEM > driver apart from the legacy ones. A solution for vmwgfx will have to be > worked out later if it wants to support the client API which it probably > will when we have a bootsplash client. > > Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> > Reviewed-by: Daniel Vetter <daniel.vetter@xxxxxxxx> > --- > Documentation/gpu/drm-client.rst | 12 ++ > Documentation/gpu/index.rst | 1 + > drivers/gpu/drm/Makefile | 2 +- > drivers/gpu/drm/drm_client.c | 289 +++++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/drm_drv.c | 1 + > include/drm/drm_client.h | 76 ++++++++++ > include/drm/drm_device.h | 7 + > 7 files changed, 387 insertions(+), 1 deletion(-) > create mode 100644 Documentation/gpu/drm-client.rst > create mode 100644 drivers/gpu/drm/drm_client.c > create mode 100644 include/drm/drm_client.h > > diff --git a/Documentation/gpu/drm-client.rst b/Documentation/gpu/drm-client.rst > new file mode 100644 > index 000000000000..7e672063e7eb > --- /dev/null > +++ b/Documentation/gpu/drm-client.rst > @@ -0,0 +1,12 @@ > +================= > +Kernel clients > +================= > + > +.. kernel-doc:: drivers/gpu/drm/drm_client.c > + :doc: overview > + > +.. kernel-doc:: include/drm/drm_client.h > + :internal: > + > +.. kernel-doc:: drivers/gpu/drm/drm_client.c > + :export: > diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst > index 00288f34c5a6..1fcf8e851e15 100644 > --- a/Documentation/gpu/index.rst > +++ b/Documentation/gpu/index.rst > @@ -10,6 +10,7 @@ Linux GPU Driver Developer's Guide > drm-kms > drm-kms-helpers > drm-uapi > + drm-client > drivers > vga-switcheroo > vgaarbiter > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 69c13517ea3a..d6657a61d037 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ > drm_encoder.o drm_mode_object.o drm_property.o \ > drm_plane.o drm_color_mgmt.o drm_print.o \ > drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ > - drm_syncobj.o drm_lease.o drm_writeback.o > + drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o > > drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o > drm-$(CONFIG_DRM_VM) += drm_vm.o > diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c > new file mode 100644 > index 000000000000..9c9b8ac7aea3 > --- /dev/null > +++ b/drivers/gpu/drm/drm_client.c > @@ -0,0 +1,289 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright 2018 Noralf Trønnes > + */ > + > +#include <linux/list.h> > +#include <linux/mutex.h> > +#include <linux/seq_file.h> > +#include <linux/slab.h> > + > +#include <drm/drm_client.h> > +#include <drm/drm_debugfs.h> > +#include <drm/drm_device.h> > +#include <drm/drm_drv.h> > +#include <drm/drm_file.h> > +#include <drm/drm_fourcc.h> > +#include <drm/drm_gem.h> > +#include <drm/drm_mode.h> > +#include <drm/drm_print.h> > +#include <drm/drmP.h> > + > +#include "drm_crtc_internal.h" > +#include "drm_internal.h" > + > +/** > + * DOC: overview > + * > + * This library provides support for clients running in the kernel like fbdev and bootsplash. > + * Currently it's only partially implemented, just enough to support fbdev. > + * > + * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported. > + */ > + > +static int drm_client_open(struct drm_client_dev *client) > +{ > + struct drm_device *dev = client->dev; > + struct drm_file *file; > + > + file = drm_file_alloc(dev->primary); > + if (IS_ERR(file)) > + return PTR_ERR(file); > + > + mutex_lock(&dev->filelist_mutex); > + list_add(&file->lhead, &dev->filelist_internal); > + mutex_unlock(&dev->filelist_mutex); > + > + client->file = file; > + > + return 0; > +} > + > +static void drm_client_close(struct drm_client_dev *client) > +{ > + struct drm_device *dev = client->dev; > + > + mutex_lock(&dev->filelist_mutex); > + list_del(&client->file->lhead); > + mutex_unlock(&dev->filelist_mutex); > + > + drm_file_free(client->file); > +} > +EXPORT_SYMBOL(drm_client_close); > + > +/** > + * drm_client_new - Create a DRM client > + * @dev: DRM device > + * @client: DRM client > + * @name: Client name > + * > + * Use drm_client_put() to free the client. s/_put/_release/ ... spotted while reviewing the 2nd client library patch. -Daniel > + * > + * Returns: > + * Zero on success or negative error code on failure. > + */ > +int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, > + const char *name) > +{ > + int ret; > + > + if (!drm_core_check_feature(dev, DRIVER_MODESET) || > + !dev->driver->dumb_create || !dev->driver->gem_prime_vmap) > + return -ENOTSUPP; > + > + client->dev = dev; > + client->name = name; > + > + ret = drm_client_open(client); > + if (ret) > + return ret; > + > + drm_dev_get(dev); > + > + return 0; > +} > +EXPORT_SYMBOL(drm_client_new); > + > +/** > + * drm_client_release - Release DRM client resources > + * @client: DRM client > + * > + * Releases resources by closing the &drm_file that was opened by drm_client_new(). > + * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set. > + * > + * This function should only be called from the unregister callback. An exception > + * is fbdev which cannot free the buffer if userspace has open file descriptors. > + * > + * Note: > + * Clients cannot initiate a release by themselves. This is done to keep the code simple. > + * The driver has to be unloaded before the client can be unloaded. > + */ > +void drm_client_release(struct drm_client_dev *client) > +{ > + struct drm_device *dev = client->dev; > + > + DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name); > + > + drm_client_close(client); > + drm_dev_put(dev); > +} > +EXPORT_SYMBOL(drm_client_release); > + > +static void drm_client_buffer_delete(struct drm_client_buffer *buffer) > +{ > + struct drm_device *dev = buffer->client->dev; > + > + if (buffer->vaddr && dev->driver->gem_prime_vunmap) > + dev->driver->gem_prime_vunmap(buffer->gem, buffer->vaddr); > + > + if (buffer->gem) > + drm_gem_object_put_unlocked(buffer->gem); > + > + drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file); > + kfree(buffer); > +} > + > +static struct drm_client_buffer * > +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) > +{ > + struct drm_mode_create_dumb dumb_args = { }; > + struct drm_device *dev = client->dev; > + struct drm_client_buffer *buffer; > + struct drm_gem_object *obj; > + void *vaddr; > + int ret; > + > + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); > + if (!buffer) > + return ERR_PTR(-ENOMEM); > + > + buffer->client = client; > + > + dumb_args.width = width; > + dumb_args.height = height; > + dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8; > + ret = drm_mode_create_dumb(dev, &dumb_args, client->file); > + if (ret) > + goto err_free; > + > + buffer->handle = dumb_args.handle; > + buffer->pitch = dumb_args.pitch; > + > + obj = drm_gem_object_lookup(client->file, dumb_args.handle); > + if (!obj) { > + ret = -ENOENT; > + goto err_delete; > + } > + > + buffer->gem = obj; > + > + /* > + * FIXME: The dependency on GEM here isn't required, we could > + * convert the driver handle to a dma-buf instead and use the > + * backend-agnostic dma-buf vmap support instead. This would > + * require that the handle2fd prime ioctl is reworked to pull the > + * fd_install step out of the driver backend hooks, to make that > + * final step optional for internal users. > + */ > + vaddr = dev->driver->gem_prime_vmap(obj); > + if (!vaddr) { > + ret = -ENOMEM; > + goto err_delete; > + } > + > + buffer->vaddr = vaddr; > + > + return buffer; > + > +err_delete: > + drm_client_buffer_delete(buffer); > +err_free: > + kfree(buffer); > + > + return ERR_PTR(ret); > +} > + > +static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer) > +{ > + int ret; > + > + if (!buffer->fb) > + return; > + > + ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file); > + if (ret) > + DRM_DEV_ERROR(buffer->client->dev->dev, > + "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret); > + > + buffer->fb = NULL; > +} > + > +static int drm_client_buffer_addfb(struct drm_client_buffer *buffer, > + u32 width, u32 height, u32 format) > +{ > + struct drm_client_dev *client = buffer->client; > + struct drm_mode_fb_cmd fb_req = { }; > + const struct drm_format_info *info; > + int ret; > + > + info = drm_format_info(format); > + fb_req.bpp = info->cpp[0] * 8; > + fb_req.depth = info->depth; > + fb_req.width = width; > + fb_req.height = height; > + fb_req.handle = buffer->handle; > + fb_req.pitch = buffer->pitch; > + > + ret = drm_mode_addfb(client->dev, &fb_req, client->file); > + if (ret) > + return ret; > + > + buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id); > + if (WARN_ON(!buffer->fb)) > + return -ENOENT; > + > + /* drop the reference we picked up in framebuffer lookup */ > + drm_framebuffer_put(buffer->fb); > + > + strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN); > + > + return 0; > +} > + > +/** > + * drm_client_framebuffer_create - Create a client framebuffer > + * @client: DRM client > + * @width: Framebuffer width > + * @height: Framebuffer height > + * @format: Buffer format > + * > + * This function creates a &drm_client_buffer which consists of a > + * &drm_framebuffer backed by a dumb buffer. > + * Call drm_client_framebuffer_delete() to free the buffer. > + * > + * Returns: > + * Pointer to a client buffer or an error pointer on failure. > + */ > +struct drm_client_buffer * > +drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) > +{ > + struct drm_client_buffer *buffer; > + int ret; > + > + buffer = drm_client_buffer_create(client, width, height, format); > + if (IS_ERR(buffer)) > + return buffer; > + > + ret = drm_client_buffer_addfb(buffer, width, height, format); > + if (ret) { > + drm_client_buffer_delete(buffer); > + return ERR_PTR(ret); > + } > + > + return buffer; > +} > +EXPORT_SYMBOL(drm_client_framebuffer_create); > + > +/** > + * drm_client_framebuffer_delete - Delete a client framebuffer > + * @buffer: DRM client buffer (can be NULL) > + */ > +void drm_client_framebuffer_delete(struct drm_client_buffer *buffer) > +{ > + if (!buffer) > + return; > + > + drm_client_buffer_rmfb(buffer); > + drm_client_buffer_delete(buffer); > +} > +EXPORT_SYMBOL(drm_client_framebuffer_delete); > diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c > index 7af748ed1c58..e7ff0b03328b 100644 > --- a/drivers/gpu/drm/drm_drv.c > +++ b/drivers/gpu/drm/drm_drv.c > @@ -505,6 +505,7 @@ int drm_dev_init(struct drm_device *dev, > dev->driver = driver; > > INIT_LIST_HEAD(&dev->filelist); > + INIT_LIST_HEAD(&dev->filelist_internal); > INIT_LIST_HEAD(&dev->ctxlist); > INIT_LIST_HEAD(&dev->vmalist); > INIT_LIST_HEAD(&dev->maplist); > diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h > new file mode 100644 > index 000000000000..e366f95d4414 > --- /dev/null > +++ b/include/drm/drm_client.h > @@ -0,0 +1,76 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > + > +#ifndef _DRM_CLIENT_H_ > +#define _DRM_CLIENT_H_ > + > +#include <linux/types.h> > + > +struct drm_device; > +struct drm_file; > +struct drm_framebuffer; > +struct drm_gem_object; > + > +/** > + * struct drm_client_dev - DRM client instance > + */ > +struct drm_client_dev { > + /** > + * @dev: DRM device > + */ > + struct drm_device *dev; > + > + /** > + * @name: Name of the client. > + */ > + const char *name; > + > + /** > + * @file: DRM file > + */ > + struct drm_file *file; > +}; > + > +int drm_client_new(struct drm_device *dev, struct drm_client_dev *client, > + const char *name); > +void drm_client_release(struct drm_client_dev *client); > + > +/** > + * struct drm_client_buffer - DRM client buffer > + */ > +struct drm_client_buffer { > + /** > + * @client: DRM client > + */ > + struct drm_client_dev *client; > + > + /** > + * @handle: Buffer handle > + */ > + u32 handle; > + > + /** > + * @pitch: Buffer pitch > + */ > + u32 pitch; > + > + /** > + * @gem: GEM object backing this buffer > + */ > + struct drm_gem_object *gem; > + > + /** > + * @vaddr: Virtual address for the buffer > + */ > + void *vaddr; > + > + /** > + * @fb: DRM framebuffer > + */ > + struct drm_framebuffer *fb; > +}; > + > +struct drm_client_buffer * > +drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format); > +void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); > + > +#endif > diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h > index 858ba19a3e29..9e29976d4e98 100644 > --- a/include/drm/drm_device.h > +++ b/include/drm/drm_device.h > @@ -74,6 +74,13 @@ struct drm_device { > struct mutex filelist_mutex; > struct list_head filelist; > > + /** > + * @filelist_internal: > + * > + * List of open DRM files for in-kernel clients. Protected by @filelist_mutex. > + */ > + struct list_head filelist_internal; > + > /** \name Memory management */ > /*@{ */ > struct list_head maplist; /**< Linked list of regions */ > -- > 2.15.1 > > _______________________________________________ > dri-devel mailing list > dri-devel@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/dri-devel -- Daniel Vetter Software Engineer, Intel Corporation http://blog.ffwll.ch _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel