@@ -74,6 +74,7 @@ EXPORT_SYMBOL(drm_client_close);
* @dev: DRM device
* @client: DRM client
* @name: Client name
+ * @funcs: DRM client functions (optional)
*
* Use drm_client_put() to free the client.
*
@@ -81,8 +82,9 @@ EXPORT_SYMBOL(drm_client_close);
* 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)
+ const char *name, const struct drm_client_funcs *funcs)
{
+ bool registered;
int ret;
if (!drm_core_check_feature(dev, DRIVER_MODESET) ||
@@ -91,12 +93,23 @@ int drm_client_new(struct drm_device *dev, struct drm_client_dev *client,
client->dev = dev;
client->name = name;
+ client->funcs = funcs;
kref_init(&client->ref);
ret = drm_client_open(client);
if (ret)
return ret;
+ mutex_lock(&dev->clientlist_mutex);
+ registered = dev->registered;
+ if (registered)
+ list_add(&client->list, &dev->clientlist);
+ mutex_unlock(&dev->clientlist_mutex);
+ if (!registered) {
+ drm_client_close(client);
+ return -ENODEV;
+ }
+
drm_dev_get(dev);
return 0;
@@ -110,8 +123,17 @@ static void drm_client_release(struct kref *ref)
DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name);
- drm_client_close(client);
- kfree(client);
+ mutex_lock(&dev->clientlist_mutex);
+ if (!list_empty(&client->list))
+ list_del(&client->list);
+ mutex_unlock(&dev->clientlist_mutex);
+
+ if (client->funcs && client->funcs->release) {
+ client->funcs->release(client);
+ } else {
+ drm_client_close(client);
+ kfree(client);
+ }
drm_dev_put(dev);
}
@@ -145,6 +167,75 @@ void drm_client_put(struct drm_client_dev *client)
}
EXPORT_SYMBOL(drm_client_put);
+void drm_client_dev_unregister(struct drm_device *dev)
+{
+ struct drm_client_dev *client, *iter;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ do {
+ client = NULL;
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(iter, &dev->clientlist, list) {
+ list_del_init(&iter->list);
+ if (iter->funcs && iter->funcs->unregister) {
+ /* Make sure a release has not begun */
+ if (kref_get_unless_zero(&iter->ref)) {
+ client = iter;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+ if (client) {
+ client->funcs->unregister(client);
+ drm_client_put(client);
+ }
+ } while (client);
+}
+
+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 || !client->funcs->hotplug)
+ continue;
+
+ ret = client->funcs->hotplug(client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_hotplug);
+
+void drm_client_dev_restore(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 || !client->funcs->restore)
+ continue;
+
+ ret = client->funcs->restore(client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
+ if (!ret) /* The first one to return zero gets the privilege to restore */
+ break;
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
{
struct drm_device *dev;
@@ -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>
@@ -506,6 +507,7 @@ int drm_dev_init(struct drm_device *dev,
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);
@@ -515,6 +517,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);
@@ -570,6 +573,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;
@@ -604,6 +608,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);
@@ -859,6 +864,8 @@ void drm_dev_unregister(struct drm_device *dev)
dev->registered = false;
+ drm_client_dev_unregister(dev);
+
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_modeset_unregister_all(dev);
@@ -182,7 +182,7 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
fb_helper = &fbdev_cma->fb_helper;
- ret = drm_client_new(dev, &fb_helper->client, "fbdev");
+ ret = drm_client_new(dev, &fb_helper->client, "fbdev", NULL);
if (ret)
goto err_free;
@@ -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_restore(dev);
}
/**
@@ -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);
@@ -6,11 +6,61 @@
#include <linux/kref.h>
#include <linux/types.h>
+struct drm_client_dev;
struct drm_device;
struct drm_framebuffer;
struct drm_gem_object;
struct drm_minor;
+/**
+ * struct drm_client_funcs - DRM client callbacks
+ */
+struct drm_client_funcs {
+ /**
+ * @release:
+ *
+ * Called when the reference count is dropped to zero. Users of this
+ * callback is responsible for calling drm_client_close() and freeing
+ * the client structure.
+ *
+ * This callback is optional.
+ */
+ void (*release)(struct drm_client_dev *client);
+
+ /**
+ * @unregister:
+ *
+ * Called when &drm_device is unregistered. The client should respond by
+ * releasing it's resources using drm_client_put(). If it can't do so
+ * due to resoruces being tied up, like fbdev with open file handles,
+ * it should release it's resources as soon as possible.
+ *
+ * This callback is optional.
+ */
+ void (*unregister)(struct drm_client_dev *client);
+
+ /**
+ * @restore:
+ *
+ * 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 not called after @unregister has been called.
+ *
+ * This callback is optional.
+ */
+ int (*restore)(struct drm_client_dev *client);
+
+ /**
+ * @hotplug:
+ *
+ * Called on drm_kms_helper_hotplug_event().
+ * This callback is not called after @unregister has been called.
+ *
+ * This callback is optional.
+ */
+ int (*hotplug)(struct drm_client_dev *client);
+};
+
/**
* struct drm_client_dev - DRM client instance
*/
@@ -33,6 +83,19 @@ struct drm_client_dev {
*/
struct kref ref;
+ /**
+ * @list:
+ *
+ * List of all clients of a DRM device, linked into
+ * &drm_device.clientlist. Protected by &drm_device.clientlist_mutex.
+ */
+ struct list_head list;
+
+ /**
+ * @funcs: DRM client functions (optional)
+ */
+ const struct drm_client_funcs *funcs;
+
/**
* @file: DRM file
*/
@@ -41,10 +104,14 @@ struct drm_client_dev {
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);
+ const char *name, const struct drm_client_funcs *funcs);
void drm_client_get(struct drm_client_dev *client);
void drm_client_put(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_restore(struct drm_device *dev);
+
/**
* struct drm_client_buffer - DRM client buffer
*/
@@ -81,6 +81,20 @@ struct drm_device {
*/
struct list_head filelist_internal;
+ /**
+ * @clientlist_mutex:
+ *
+ * Protects @clientlist access.
+ */
+ struct mutex clientlist_mutex;
+
+ /**
+ * @clientlist:
+ *
+ * List of in-kernel clients. Protected by @clientlist_mutex.
+ */
+ struct list_head clientlist;
+
/** \name Memory management */
/*@{ */
struct list_head maplist; /**< Linked list of regions */
Add client callbacks and hook them up. Add a list of clients per drm_device. Signed-off-by: Noralf Trønnes <noralf@tronnes.org> --- Changes since version 1: - Remove unused functions - Change name drm_client_funcs.lastclose -> .restore - Change name drm_client_funcs.remove -> .unregister - Rework unregister code drivers/gpu/drm/drm_client.c | 97 +++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/drm_drv.c | 7 +++ drivers/gpu/drm/drm_fb_cma_helper.c | 2 +- drivers/gpu/drm/drm_file.c | 3 ++ drivers/gpu/drm/drm_probe_helper.c | 3 ++ include/drm/drm_client.h | 69 +++++++++++++++++++++++++- include/drm/drm_device.h | 14 ++++++ 7 files changed, 190 insertions(+), 5 deletions(-)