[RFC v4 19/25] drm/client: Finish the in-kernel client API
Noralf Trønnes
noralf at tronnes.org
Thu Apr 12 16:12:31 UTC 2018
The modesetting code is already present, this adds the rest of the API.
Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---
drivers/gpu/drm/drm_client.c | 573 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_debugfs.c | 7 +
drivers/gpu/drm/drm_drv.c | 11 +
drivers/gpu/drm/drm_file.c | 3 +
drivers/gpu/drm/drm_probe_helper.c | 3 +
drivers/gpu/drm/drm_sysfs.c | 20 ++
include/drm/drm_client.h | 103 +++++++
include/drm/drm_device.h | 4 +
8 files changed, 724 insertions(+)
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index bce1630a0db2..760f1795f812 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -8,7 +8,9 @@
* Copyright (c) 2007 Dave Airlie <airlied at linux.ie>
*/
+#include <linux/dma-buf.h>
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
#include <drm/drm_atomic.h>
@@ -17,14 +19,280 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
+#include <drm/drm_file.h>
#include <drm/drm_modes.h>
+#include "drm_crtc_internal.h"
#include "drm_internal.h"
struct drm_client_display_offset {
int x, y;
};
+static int drm_client_alloc_file(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);
+
+ drm_dev_get(dev);
+
+ 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_free_file(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);
+ drm_dev_put(dev);
+}
+
+struct drm_client_dev *
+drm_client_new(struct drm_device *dev, const struct drm_client_funcs *funcs)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (WARN_ON(!funcs->name))
+ return ERR_PTR(-EINVAL);
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return ERR_PTR(-ENOMEM);
+
+ client->dev = dev;
+ client->funcs = funcs;
+
+ ret = drm_client_alloc_file(client);
+ if (ret) {
+ kfree(client);
+ return ERR_PTR(ret);
+ }
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_add(&client->list, &dev->clientlist);
+ mutex_unlock(&dev->clientlist_mutex);
+
+ return client;
+}
+EXPORT_SYMBOL(drm_client_new);
+
+struct drm_client_dev *
+drm_client_new_from_id(unsigned int dev_id, const struct drm_client_funcs *funcs)
+{
+ struct drm_client_dev *client;
+ struct drm_minor *minor;
+
+ minor = drm_minor_acquire(dev_id);
+ if (IS_ERR(minor))
+ return ERR_CAST(minor);
+
+ client = drm_client_new(minor->dev, funcs);
+
+ drm_minor_release(minor);
+
+ return client;
+}
+EXPORT_SYMBOL(drm_client_new_from_id);
+
+/**
+ * drm_client_free - Free DRM client resources
+ * @client: DRM client
+ *
+ * This is called automatically on client removal unless the client returns
+ * non-zero in the &drm_client_funcs->remove callback. The fbdev client does
+ * this when it can't close &drm_file because userspace has an open fd.
+ *
+ * Note:
+ * If the client can't release it's resources on remove, it needs to hold a
+ * reference on the driver module to prevent the code from going away.
+ */
+void drm_client_free(struct drm_client_dev *client)
+{
+ DRM_DEV_DEBUG_KMS(client->dev->dev, "%s\n", client->funcs->name);
+ drm_client_free_file(client);
+ kfree(client);
+}
+EXPORT_SYMBOL(drm_client_free);
+
+static void drm_client_remove_locked(struct drm_client_dev *client)
+{
+ list_del(&client->list);
+
+ if (!client->funcs->remove || !client->funcs->remove(client))
+ drm_client_free(client);
+}
+
+static void drm_client_remove_safe(struct drm_device *dev,
+ struct drm_client_dev *client)
+{
+ struct drm_client_dev *iter;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(iter, &dev->clientlist, list) {
+ if (iter == client) {
+ drm_client_remove_locked(client);
+ break;
+ }
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
+/**
+ * drm_client_remove - Remove client
+ * @client: Client
+ *
+ * Remove a client.
+ */
+void drm_client_remove(struct drm_client_dev *client)
+{
+ struct drm_device *dev;
+
+ if (!client)
+ return;
+
+ dev = client->dev;
+ drm_dev_get(dev);
+ drm_client_remove_safe(dev, client);
+ drm_dev_put(dev);
+}
+EXPORT_SYMBOL(drm_client_remove);
+
+struct drm_client_remove_defer {
+ struct list_head list;
+ struct drm_device *dev;
+ struct drm_client_dev *client;
+};
+
+static LIST_HEAD(drm_client_remove_defer_list);
+static DEFINE_MUTEX(drm_client_remove_defer_list_lock);
+
+static void drm_client_remove_defer_work_fn(struct work_struct *work)
+{
+ struct drm_client_remove_defer *defer, *tmp;
+
+ mutex_lock(&drm_client_remove_defer_list_lock);
+ list_for_each_entry_safe(defer, tmp, &drm_client_remove_defer_list, list) {
+ drm_client_remove_safe(defer->dev, defer->client);
+ drm_dev_put(defer->dev);
+ list_del(&defer->list);
+ kfree(defer);
+ }
+ mutex_unlock(&drm_client_remove_defer_list_lock);
+}
+
+static DECLARE_WORK(drm_client_remove_defer_work, drm_client_remove_defer_work_fn);
+
+/**
+ * drm_client_remove_defer - Deferred client removal
+ * @client: Client
+ *
+ * Defer client removal to a worker. This makes it possible for a client running
+ * in a worker to remove itself.
+ *
+ * Returns:
+ * Zero on success, or -ENOMEM on allocation failure.
+ */
+int drm_client_remove_defer(struct drm_client_dev *client)
+{
+ struct drm_client_remove_defer *defer;
+
+ if (!client)
+ return 0;
+
+ defer = kzalloc(sizeof(*defer), GFP_KERNEL);
+ if (!defer)
+ return -ENOMEM;
+
+ defer->dev = client->dev;
+ defer->client = client;
+ drm_dev_get(client->dev);
+
+ mutex_lock(&drm_client_remove_defer_list_lock);
+ list_add(&defer->list, &drm_client_remove_defer_list);
+ mutex_unlock(&drm_client_remove_defer_list_lock);
+
+ schedule_work(&drm_client_remove_defer_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_client_remove_defer);
+
+void drm_client_init(void)
+{
+}
+
+void drm_client_exit(void)
+{
+ flush_work(&drm_client_remove_defer_work);
+}
+
+void drm_client_dev_unregister(struct drm_device *dev)
+{
+ struct drm_client_dev *client, *tmp;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry_safe(client, tmp, &dev->clientlist, list)
+ drm_client_remove_locked(client);
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
+void drm_client_dev_hotplug(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs->hotplug)
+ continue;
+
+ ret = client->funcs->hotplug(client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->funcs->name, ret);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_hotplug);
+
+void drm_client_dev_lastclose(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs->lastclose)
+ continue;
+
+ ret = client->funcs->lastclose(client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->funcs->name, ret);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
/**
* drm_client_display_create() - Create display structure
* @dev: DRM device
@@ -348,6 +616,90 @@ int drm_client_display_restore(struct drm_client_display *display, bool force)
}
EXPORT_SYMBOL(drm_client_display_restore);
+/**
+ * drm_client_display_commit_mode - Commit a mode/fb to the CRTC(s)
+ * @display: Client display
+ * @fb: Framebuffer (if NULL the current fb is used)
+ * @mode: Display mode (if NULL the current mode is used)
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int drm_client_display_commit(struct drm_client_display *display,
+ struct drm_framebuffer *fb, struct drm_display_mode *mode)
+{
+ struct drm_display_mode *use_mode = NULL;
+ struct drm_mode_set *modeset;
+ unsigned int count = 0;
+
+ if (mode) {
+ struct drm_display_mode *iter;
+
+ drm_client_display_for_each_mode(iter, display) {
+ if (!use_mode && drm_mode_equal(iter, mode))
+ use_mode = iter;
+ count++;
+ }
+
+ if (!use_mode)
+ return -EINVAL;
+
+ /*
+ * Don't actually set the mode in the single mode case since it
+ * might be a tiled display which consists of multiple modes.
+ * Just keep the current mode.
+ */
+ if (count == 1)
+ use_mode = NULL;
+ }
+
+ count = 0;
+ drm_client_display_for_each_modeset(modeset, display) {
+ if (!modeset->num_connectors)
+ continue;
+
+ if (fb)
+ modeset->fb = fb;
+
+ if (use_mode) {
+ if (WARN_ON(++count > 1))
+ return -EINVAL;
+
+ if (modeset->mode)
+ drm_mode_destroy(display->dev, modeset->mode);
+ modeset->mode = drm_mode_duplicate(display->dev, use_mode);
+ if (!modeset->mode)
+ return -ENOMEM;
+ }
+ }
+
+ return drm_client_display_restore(display, false);
+}
+EXPORT_SYMBOL(drm_client_display_commit);
+
+struct drm_framebuffer *drm_client_display_current_fb(struct drm_client_display *display)
+{
+ struct drm_mode_set *modeset;
+
+ drm_client_display_for_each_modeset(modeset, display) {
+ struct drm_crtc *crtc = modeset->crtc;
+ struct drm_framebuffer *fb = NULL;
+
+ drm_modeset_lock(&crtc->primary->mutex, NULL);
+ if (crtc->primary->state && crtc->primary->state->fb)
+ fb = crtc->primary->state->fb;
+ else if (!crtc->primary->state && crtc->primary->fb)
+ fb = crtc->primary->fb;
+ drm_modeset_unlock(&crtc->primary->mutex);
+
+ if (fb)
+ return fb;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(drm_client_display_current_fb);
+
static void drm_client_display_dpms_legacy(struct drm_client_display *display, int dpms_mode)
{
struct drm_device *dev = display->dev;
@@ -869,3 +1221,224 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
return display;
}
EXPORT_SYMBOL(drm_client_find_display);
+
+static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
+{
+ if (!buffer)
+ return;
+
+ if (buffer->vaddr)
+ dma_buf_vunmap(buffer->dma_buf, buffer->vaddr);
+
+ if (buffer->dma_buf)
+ dma_buf_put(buffer->dma_buf);
+
+ drm_mode_destroy_dumb(buffer->client->dev, buffer->handle, buffer->client->file);
+ kfree(buffer);
+}
+
+/* For testing __close_fd() */
+#include <linux/fdtable.h>
+
+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_prime_handle prime_args = { };
+ struct drm_client_buffer *buffer;
+ struct dma_buf *dma_buf;
+ void *vaddr;
+ int ret;
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return ERR_PTR(-ENOMEM);
+
+ buffer->client = client;
+ buffer->width = width;
+ buffer->height = height;
+ buffer->format = format;
+
+ dumb_args.width = buffer->width;
+ dumb_args.height = buffer->height;
+ dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8;
+ ret = drm_mode_create_dumb(client->dev, &dumb_args, client->file);
+ if (ret)
+ goto err_free;
+
+ buffer->handle = dumb_args.handle;
+ buffer->pitch = dumb_args.pitch;
+ buffer->size = dumb_args.size;
+
+ prime_args.handle = dumb_args.handle;
+ ret = drm_prime_handle_to_fd(client->dev, &prime_args, client->file);
+ if (ret)
+ goto err_delete;
+
+ dma_buf = dma_buf_get(prime_args.fd);
+ if (IS_ERR(dma_buf)) {
+ ret = PTR_ERR(dma_buf);
+ goto err_delete;
+ }
+
+ /*
+ * If called from a worker the dmabuf fd isn't closed and the ref
+ * doesn't drop to zero on free.
+ * If I use __close_fd() it's all fine, but that function is not exported.
+ *
+ * How do I get rid of this fd when in a worker/kernel thread?
+ * The fd isn't used beyond this function.
+ */
+// WARN_ON(__close_fd(current->files, prime_args.fd));
+
+ pr_info("%s: PF_KTHREAD=%u\n", __func__, !!(current->flags & PF_KTHREAD));
+
+ buffer->dma_buf = dma_buf;
+
+ vaddr = dma_buf_vmap(dma_buf);
+ 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 int drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
+{
+ int ret;
+
+ if (!buffer || !buffer->fb)
+ return 0;
+
+ 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;
+
+ return 0;
+}
+
+static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
+ struct drm_display_mode *mode)
+{
+ struct drm_client_dev *client = buffer->client;
+ struct drm_mode_fb_cmd2 fb_req = { };
+ int ret;
+
+ if (mode->hdisplay > buffer->width || mode->vdisplay > buffer->height)
+ return -EINVAL;
+
+ fb_req.width = mode->hdisplay;
+ fb_req.height = mode->vdisplay;
+ fb_req.pixel_format = buffer->format;
+ fb_req.handles[0] = buffer->handle;
+ fb_req.pitches[0] = buffer->pitch;
+
+ ret = drm_mode_addfb2(client->dev, &fb_req, client->file, client->funcs->name);
+ 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);
+
+ return 0;
+}
+
+/**
+ * drm_client_framebuffer_create - Create a client framebuffer
+ * @client: DRM client
+ * @mode: Display mode to create a buffer for
+ * @format: Buffer format
+ *
+ * This function creates a &drm_client_buffer which consists of a
+ * &drm_framebuffer backed by a dumb buffer. The dumb buffer is &dma_buf
+ * exported to aquire a virtual address which is stored in
+ * &drm_client_buffer->vaddr.
+ * 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,
+ struct drm_display_mode *mode, u32 format)
+{
+ struct drm_client_buffer *buffer;
+ int ret;
+
+ buffer = drm_client_buffer_create(client, mode->hdisplay,
+ mode->vdisplay, format);
+ if (IS_ERR(buffer))
+ return buffer;
+
+ ret = drm_client_buffer_addfb(buffer, mode);
+ if (ret) {
+ drm_client_buffer_delete(buffer);
+ return ERR_PTR(ret);
+ }
+
+ return buffer;
+}
+EXPORT_SYMBOL(drm_client_framebuffer_create);
+
+void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
+{
+ drm_client_buffer_rmfb(buffer);
+ drm_client_buffer_delete(buffer);
+}
+EXPORT_SYMBOL(drm_client_framebuffer_delete);
+
+int drm_client_framebuffer_flush(struct drm_client_buffer *buffer,
+ struct drm_clip_rect *rect)
+{
+ if (!buffer->fb || !buffer->fb->funcs->dirty)
+ return 0;
+
+ return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
+ 0, 0, rect, rect ? 1 : 0);
+}
+EXPORT_SYMBOL(drm_client_framebuffer_flush);
+
+#ifdef CONFIG_DEBUG_FS
+static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_client_dev *client;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list)
+ drm_printf(&p, "%s\n", client->funcs->name);
+ mutex_unlock(&dev->clientlist_mutex);
+
+ return 0;
+}
+
+static const struct drm_info_list drm_client_debugfs_list[] = {
+ { "internal_clients", drm_client_debugfs_internal_clients, 0 },
+};
+
+int drm_client_debugfs_init(struct drm_minor *minor)
+{
+ return drm_debugfs_create_files(drm_client_debugfs_list,
+ ARRAY_SIZE(drm_client_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#endif
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index b2482818fee8..50a20bfc07ea 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/export.h>
+#include <drm/drm_client.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_atomic.h>
@@ -164,6 +165,12 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
DRM_ERROR("Failed to create framebuffer debugfs file\n");
return ret;
}
+
+ ret = drm_client_debugfs_init(minor);
+ if (ret) {
+ DRM_ERROR("Failed to create client debugfs file\n");
+ return ret;
+ }
}
if (dev->driver->debugfs_init) {
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 32a83b41ab61..6f21bafb29be 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/srcu.h>
+#include <drm/drm_client.h>
#include <drm/drm_drv.h>
#include <drm/drmP.h>
@@ -507,6 +508,8 @@ 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->clientlist);
INIT_LIST_HEAD(&dev->ctxlist);
INIT_LIST_HEAD(&dev->vmalist);
INIT_LIST_HEAD(&dev->maplist);
@@ -516,6 +519,7 @@ int drm_dev_init(struct drm_device *dev,
spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->filelist_mutex);
+ mutex_init(&dev->clientlist_mutex);
mutex_init(&dev->ctxlist_mutex);
mutex_init(&dev->master_mutex);
@@ -572,6 +576,7 @@ int drm_dev_init(struct drm_device *dev,
err_free:
mutex_destroy(&dev->master_mutex);
mutex_destroy(&dev->ctxlist_mutex);
+ mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex);
return ret;
@@ -607,6 +612,7 @@ void drm_dev_fini(struct drm_device *dev)
mutex_destroy(&dev->master_mutex);
mutex_destroy(&dev->ctxlist_mutex);
+ mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex);
kfree(dev->unique);
@@ -862,6 +868,8 @@ void drm_dev_unregister(struct drm_device *dev)
{
struct drm_map_list *r_list, *list_temp;
+ drm_client_dev_unregister(dev);
+
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_lastclose(dev);
@@ -968,6 +976,7 @@ static const struct file_operations drm_stub_fops = {
static void drm_core_exit(void)
{
+ drm_client_exit();
unregister_chrdev(DRM_MAJOR, "drm");
debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
@@ -1001,6 +1010,8 @@ static int __init drm_core_init(void)
if (ret < 0)
goto error;
+ drm_client_init();
+
drm_core_init_complete = true;
DRM_DEBUG("Initialized\n");
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 55505378df47..bcc688e58776 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -35,6 +35,7 @@
#include <linux/slab.h>
#include <linux/module.h>
+#include <drm/drm_client.h>
#include <drm/drm_file.h>
#include <drm/drmP.h>
@@ -443,6 +444,8 @@ void drm_lastclose(struct drm_device * dev)
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_legacy_dev_reinit(dev);
+
+ drm_client_dev_lastclose(dev);
}
/**
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 527743394150..26be57e28a9d 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -33,6 +33,7 @@
#include <linux/moduleparam.h>
#include <drm/drmP.h>
+#include <drm/drm_client.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_crtc_helper.h>
@@ -563,6 +564,8 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev)
drm_sysfs_hotplug_event(dev);
if (dev->mode_config.funcs->output_poll_changed)
dev->mode_config.funcs->output_poll_changed(dev);
+
+ drm_client_dev_hotplug(dev);
}
EXPORT_SYMBOL(drm_kms_helper_hotplug_event);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1c5b5ce1fd7f..1fc066c41861 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/export.h>
+#include <drm/drm_client.h>
#include <drm/drm_sysfs.h>
#include <drm/drmP.h>
#include "drm_internal.h"
@@ -320,6 +321,24 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_sysfs_hotplug_event);
+static ssize_t remove_internal_clients_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct drm_minor *minor = dev_get_drvdata(dev);
+
+ drm_client_dev_unregister(minor->dev);
+
+ return len;
+}
+static DEVICE_ATTR_WO(remove_internal_clients);
+
+static struct attribute *minor_attrs[] = {
+ &dev_attr_remove_internal_clients.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(minor);
+
static void drm_sysfs_release(struct device *dev)
{
kfree(dev);
@@ -347,6 +366,7 @@ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
kdev->class = drm_class;
kdev->type = &drm_sysfs_device_minor;
kdev->parent = minor->dev->dev;
+ kdev->groups = minor_groups;
kdev->release = drm_sysfs_release;
dev_set_drvdata(kdev, minor);
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 524f793d6e7b..6fd2fcaae826 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -5,13 +5,91 @@
#include <linux/types.h>
+struct drm_clip_rect;
struct drm_connector;
struct drm_crtc;
struct drm_device;
struct drm_display_mode;
+struct drm_framebuffer;
+struct drm_minor;
struct drm_mode_set;
struct drm_plane;
+struct drm_client_dev;
+
+/**
+ * struct drm_client_funcs - DRM client callbacks
+ */
+struct drm_client_funcs {
+ /**
+ * @name:
+ *
+ * Name of the client. Mandatory.
+ */
+ const char *name;
+
+ /**
+ * @remove:
+ *
+ * Called when a &drm_device is unregistered or the client is
+ * unregistered. If zero is returned drm_client_free() is called
+ * automatically. If the client can't drop it's resources it should
+ * return non-zero and call drm_client_free() later.
+ *
+ * This callback is optional.
+ */
+ int (*remove)(struct drm_client_dev *client);
+
+ /**
+ * @lastclose:
+ *
+ * Called on drm_lastclose(). The first client instance in the list
+ * that returns zero gets the privilege to restore and no more clients
+ * are called.
+ *
+ * This callback is optional.
+ */
+ int (*lastclose)(struct drm_client_dev *client);
+
+ /**
+ * @hotplug:
+ *
+ * Called on drm_kms_helper_hotplug_event().
+ *
+ * This callback is optional.
+ */
+ int (*hotplug)(struct drm_client_dev *client);
+};
+
+/**
+ * struct drm_client_dev - DRM client instance
+ */
+struct drm_client_dev {
+ struct list_head list;
+ struct drm_device *dev;
+ const struct drm_client_funcs *funcs;
+ struct drm_file *file;
+ unsigned int file_ref_count;
+ void *private;
+};
+
+struct drm_client_dev *
+drm_client_new(struct drm_device *dev, const struct drm_client_funcs *funcs);
+struct drm_client_dev *
+drm_client_new_from_id(unsigned int dev_id, const struct drm_client_funcs *funcs);
+void drm_client_remove(struct drm_client_dev *client);
+int drm_client_remove_defer(struct drm_client_dev *client);
+void drm_client_free(struct drm_client_dev *client);
+
+void drm_client_dev_unregister(struct drm_device *dev);
+void drm_client_dev_hotplug(struct drm_device *dev);
+void drm_client_dev_lastclose(struct drm_device *dev);
+
+void drm_client_init(void);
+void drm_client_exit(void);
+
+int drm_client_debugfs_init(struct drm_minor *minor);
+
/**
* struct drm_client_display - DRM client display
*/
@@ -76,4 +154,29 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
#define drm_client_display_for_each_mode(mode, display) \
list_for_each_entry(mode, &display->modes, head)
+int drm_client_display_commit(struct drm_client_display *display,
+ struct drm_framebuffer *fb, struct drm_display_mode *mode);
+struct drm_framebuffer *drm_client_display_current_fb(struct drm_client_display *display);
+
+struct drm_client_buffer {
+ struct drm_client_dev *client;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 handle;
+ u32 pitch;
+ u64 size;
+ struct dma_buf *dma_buf;
+ void *vaddr;
+ struct drm_framebuffer *fb;
+};
+
+struct drm_client_buffer *
+drm_client_framebuffer_create(struct drm_client_dev *client,
+ struct drm_display_mode *mode, u32 format);
+void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
+
+int drm_client_framebuffer_flush(struct drm_client_buffer *buffer,
+ struct drm_clip_rect *rect);
+
#endif
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h
index 3a0eac2885b7..17edadf8b691 100644
--- a/include/drm/drm_device.h
+++ b/include/drm/drm_device.h
@@ -74,6 +74,10 @@ struct drm_device {
struct mutex filelist_mutex;
struct list_head filelist;
+ struct list_head filelist_internal;
+
+ struct mutex clientlist_mutex;
+ struct list_head clientlist;
/** \name Memory management */
/*@{ */
--
2.15.1
More information about the dri-devel
mailing list