Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0 or MIT
  2/*
  3 * Copyright 2018 Noralf Trønnes
  4 */
  5
  6#include <linux/list.h>
  7#include <linux/mutex.h>
  8#include <linux/seq_file.h>
  9
 10#include <drm/drm_client.h>
 11#include <drm/drm_client_event.h>
 12#include <drm/drm_debugfs.h>
 13#include <drm/drm_device.h>
 14#include <drm/drm_drv.h>
 15#include <drm/drm_print.h>
 16
 17#include "drm_internal.h"
 18
 19/**
 20 * drm_client_dev_unregister - Unregister clients
 21 * @dev: DRM device
 22 *
 23 * This function releases all clients by calling each client's
 24 * &drm_client_funcs.unregister callback. The callback function
 25 * is responsibe for releaseing all resources including the client
 26 * itself.
 27 *
 28 * The helper drm_dev_unregister() calls this function. Drivers
 29 * that use it don't need to call this function themselves.
 30 */
 31void drm_client_dev_unregister(struct drm_device *dev)
 32{
 33	struct drm_client_dev *client, *tmp;
 34
 35	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 36		return;
 37
 38	mutex_lock(&dev->clientlist_mutex);
 39	list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
 40		list_del(&client->list);
 41		if (client->funcs && client->funcs->unregister) {
 42			client->funcs->unregister(client);
 43		} else {
 44			drm_client_release(client);
 45			kfree(client);
 46		}
 47	}
 48	mutex_unlock(&dev->clientlist_mutex);
 49}
 50EXPORT_SYMBOL(drm_client_dev_unregister);
 51
 52/**
 53 * drm_client_dev_hotplug - Send hotplug event to clients
 54 * @dev: DRM device
 55 *
 56 * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
 57 *
 58 * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
 59 * don't need to call this function themselves.
 60 */
 61void drm_client_dev_hotplug(struct drm_device *dev)
 62{
 63	struct drm_client_dev *client;
 64	int ret;
 65
 66	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 67		return;
 68
 69	if (!dev->mode_config.num_connector) {
 70		drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n");
 71		return;
 72	}
 73
 74	mutex_lock(&dev->clientlist_mutex);
 75	list_for_each_entry(client, &dev->clientlist, list) {
 76		if (!client->funcs || !client->funcs->hotplug)
 77			continue;
 78
 79		if (client->hotplug_failed)
 80			continue;
 81
 82		ret = client->funcs->hotplug(client);
 83		drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
 84		if (ret)
 85			client->hotplug_failed = true;
 86	}
 87	mutex_unlock(&dev->clientlist_mutex);
 88}
 89EXPORT_SYMBOL(drm_client_dev_hotplug);
 90
 91void drm_client_dev_restore(struct drm_device *dev)
 92{
 93	struct drm_client_dev *client;
 94	int ret;
 95
 96	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 97		return;
 98
 99	mutex_lock(&dev->clientlist_mutex);
100	list_for_each_entry(client, &dev->clientlist, list) {
101		if (!client->funcs || !client->funcs->restore)
102			continue;
103
104		ret = client->funcs->restore(client);
105		drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
106		if (!ret) /* The first one to return zero gets the privilege to restore */
107			break;
108	}
109	mutex_unlock(&dev->clientlist_mutex);
110}
111
112static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock)
113{
114	struct drm_device *dev = client->dev;
115	int ret = 0;
116
117	if (drm_WARN_ON_ONCE(dev, client->suspended))
118		return 0;
119
120	if (client->funcs && client->funcs->suspend)
121		ret = client->funcs->suspend(client, holds_console_lock);
122	drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
123
124	client->suspended = true;
125
126	return ret;
127}
128
129void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock)
130{
131	struct drm_client_dev *client;
132
133	mutex_lock(&dev->clientlist_mutex);
134	list_for_each_entry(client, &dev->clientlist, list) {
135		if (!client->suspended)
136			drm_client_suspend(client, holds_console_lock);
137	}
138	mutex_unlock(&dev->clientlist_mutex);
139}
140EXPORT_SYMBOL(drm_client_dev_suspend);
141
142static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock)
143{
144	struct drm_device *dev = client->dev;
145	int ret = 0;
146
147	if (drm_WARN_ON_ONCE(dev, !client->suspended))
148		return 0;
149
150	if (client->funcs && client->funcs->resume)
151		ret = client->funcs->resume(client, holds_console_lock);
152	drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
153
154	client->suspended = false;
155
156	return ret;
157}
158
159void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock)
160{
161	struct drm_client_dev *client;
162
163	mutex_lock(&dev->clientlist_mutex);
164	list_for_each_entry(client, &dev->clientlist, list) {
165		if  (client->suspended)
166			drm_client_resume(client, holds_console_lock);
167	}
168	mutex_unlock(&dev->clientlist_mutex);
169}
170EXPORT_SYMBOL(drm_client_dev_resume);
171
172#ifdef CONFIG_DEBUG_FS
173static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
174{
175	struct drm_debugfs_entry *entry = m->private;
176	struct drm_device *dev = entry->dev;
177	struct drm_printer p = drm_seq_file_printer(m);
178	struct drm_client_dev *client;
179
180	mutex_lock(&dev->clientlist_mutex);
181	list_for_each_entry(client, &dev->clientlist, list)
182		drm_printf(&p, "%s\n", client->name);
183	mutex_unlock(&dev->clientlist_mutex);
184
185	return 0;
186}
187
188static const struct drm_debugfs_info drm_client_debugfs_list[] = {
189	{ "internal_clients", drm_client_debugfs_internal_clients, 0 },
190};
191
192void drm_client_debugfs_init(struct drm_device *dev)
193{
194	drm_debugfs_add_files(dev, drm_client_debugfs_list,
195			      ARRAY_SIZE(drm_client_debugfs_list));
196}
197#endif