All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Noralf Trønnes" <noralf@tronnes.org>
To: dri-devel@lists.freedesktop.org
Cc: intel-gfx@lists.freedesktop.org,
	"Noralf Trønnes" <noralf@tronnes.org>,
	laurent.pinchart@ideasonboard.com
Subject: [PATCH v2 04/12] drm: Begin an API for in-kernel clients
Date: Mon, 18 Jun 2018 16:17:31 +0200	[thread overview]
Message-ID: <20180618141739.48151-5-noralf@tronnes.org> (raw)
In-Reply-To: <20180618141739.48151-1-noralf@tronnes.org>

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@tronnes.org>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
---

Changes since version 1:
- Make it possible to embed struct drm_client_dev and drop the private
  pointer
- Use kref reference counting to control client release since both the
  client and the driver can release.
- Add comment about using dma-buf as a possibility with some rework
- Move buffer NULL check to drm_client_framebuffer_delete()
- Move client name to struct drm_client_dev
- Move up drm_dev_get/put calls to make them more visible
- Move drm_client_dev.list definition to later patch that makes use of it


 Documentation/gpu/drm-client.rst |  12 ++
 Documentation/gpu/index.rst      |   1 +
 drivers/gpu/drm/Makefile         |   2 +-
 drivers/gpu/drm/drm_client.c     | 316 +++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_drv.c        |   1 +
 include/drm/drm_client.h         |  87 +++++++++++
 include/drm/drm_device.h         |   7 +
 7 files changed, 425 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 ef9f3dab287f..8c8045147416 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_syncobj.o drm_lease.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..98dda40f5416
--- /dev/null
+++ b/drivers/gpu/drm/drm_client.c
@@ -0,0 +1,316 @@
+// 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;
+}
+
+/**
+ * drm_client_close - Close the client DRM file
+ * @client: DRM client
+ *
+ * Close the &drm_file that was opened by drm_client_new(). It is called
+ * automatically if the &drm_client_funcs.release callback is not set.
+ * This function should only be called from the release callback.
+ */
+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.
+ *
+ * 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;
+	kref_init(&client->ref);
+
+	ret = drm_client_open(client);
+	if (ret)
+		return ret;
+
+	drm_dev_get(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_client_new);
+
+static void drm_client_release(struct kref *ref)
+{
+	struct drm_client_dev *client = container_of(ref, struct drm_client_dev, ref);
+	struct drm_device *dev = client->dev;
+
+	DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name);
+
+	drm_client_close(client);
+	kfree(client);
+
+	drm_dev_put(dev);
+}
+
+/**
+ * drm_client_get - Take a reference of a DRM client
+ * @client: DRM client (can be NULL)
+ *
+ * This increases the ref-count of @client by one. You *must* already own a
+ * reference when calling this. Use drm_client_put() to drop this reference
+ * again.
+ */
+void drm_client_get(struct drm_client_dev *client)
+{
+	if (client)
+		kref_get(&client->ref);
+}
+EXPORT_SYMBOL(drm_client_get);
+
+/**
+ * drm_client_put - Drop reference of a DRM client
+ * @client: DRM client (can be NULL)
+ *
+ * This decreases the ref-count of @client by one. The client resources are
+ * released if the ref-count drops to zero.
+ */
+void drm_client_put(struct drm_client_dev *client)
+{
+	if (client)
+		kref_put(&client->ref, drm_client_release);
+}
+EXPORT_SYMBOL(drm_client_put);
+
+static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
+{
+	struct drm_device *dev;
+
+	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 f6910ebe4d0e..67ac793a7108 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..12ac2615b17d
--- /dev/null
+++ b/include/drm/drm_client.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DRM_CLIENT_H_
+#define _DRM_CLIENT_H_
+
+#include <linux/kref.h>
+#include <linux/types.h>
+
+struct drm_device;
+struct drm_framebuffer;
+struct drm_gem_object;
+struct drm_minor;
+
+/**
+ * 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;
+
+	/**
+	 * @ref:
+	 *
+	 * Reference counting. The client resources are released when this drops
+	 * to zero.
+	 */
+	struct kref ref;
+
+	/**
+	 * @file: DRM file
+	 */
+	struct drm_file *file;
+};
+
+void drm_client_close(struct drm_client_dev *client);
+int drm_client_new(struct drm_device *dev, struct drm_client_dev *client,
+		   const char *name);
+void drm_client_get(struct drm_client_dev *client);
+void drm_client_put(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

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

  parent reply	other threads:[~2018-06-18 14:17 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-06-18 14:17 [PATCH v2 00/12] drm: Add generic fbdev emulation Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 01/12] drm: provide management functions for drm_file Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 02/12] drm/file: Don't set master on in-kernel clients Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 03/12] drm: Make ioctls available for " Noralf Trønnes
2018-06-18 14:17 ` Noralf Trønnes [this message]
2018-06-18 14:17 ` [PATCH v2 05/12] drm/fb-helper: Add generic fbdev emulation .fb_probe function Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 06/12] drm/pl111: Set .gem_prime_vmap and .gem_prime_mmap Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 07/12] drm/cma-helper: Use the generic fbdev emulation Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 08/12] drm/client: Add client callbacks Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 09/12] drm/debugfs: Add internal client debugfs file Noralf Trønnes
2018-06-20  9:23   ` [Intel-gfx] " Daniel Vetter
2018-06-18 14:17 ` [PATCH v2 10/12] drm/fb-helper: Finish the generic fbdev emulation Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 11/12] drm/tinydrm: Use drm_fbdev_generic_setup() Noralf Trønnes
2018-06-18 14:17 ` [PATCH v2 12/12] drm/cma-helper: Remove drm_fb_cma_fbdev_init_with_funcs() Noralf Trønnes
2018-06-18 15:05 ` ✗ Fi.CI.CHECKPATCH: warning for drm: Add generic fbdev emulation Patchwork
2018-06-18 15:09 ` ✗ Fi.CI.SPARSE: " Patchwork
2018-06-18 15:20 ` ✓ Fi.CI.BAT: success " Patchwork
2018-06-18 20:02 ` ✓ Fi.CI.IGT: " Patchwork
2018-06-20  9:34 ` [PATCH v2 00/12] " Daniel Vetter
2018-06-20 13:52   ` [Intel-gfx] " Noralf Trønnes
2018-06-20 15:28     ` Noralf Trønnes
2018-06-21  7:14       ` Daniel Vetter
2018-06-21 17:19         ` Noralf Trønnes
2018-06-26 13:41           ` [Intel-gfx] " Noralf Trønnes
2018-06-26 13:42             ` Daniel Vetter
2018-06-25 14:28 ` Noralf Trønnes

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180618141739.48151-5-noralf@tronnes.org \
    --to=noralf@tronnes.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=laurent.pinchart@ideasonboard.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.