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 */ 31 void 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 } 50 EXPORT_SYMBOL(drm_client_dev_unregister); 51 52 static void drm_client_hotplug(struct drm_client_dev *client) 53 { 54 struct drm_device *dev = client->dev; 55 int ret; 56 57 if (!client->funcs || !client->funcs->hotplug) 58 return; 59 60 if (client->hotplug_failed) 61 return; 62 63 if (client->suspended) { 64 client->hotplug_pending = true; 65 return; 66 } 67 68 client->hotplug_pending = false; 69 ret = client->funcs->hotplug(client); 70 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); 71 if (ret) 72 client->hotplug_failed = true; 73 } 74 75 /** 76 * drm_client_dev_hotplug - Send hotplug event to clients 77 * @dev: DRM device 78 * 79 * This function calls the &drm_client_funcs.hotplug callback on the attached clients. 80 * 81 * drm_kms_helper_hotplug_event() calls this function, so drivers that use it 82 * don't need to call this function themselves. 83 */ 84 void drm_client_dev_hotplug(struct drm_device *dev) 85 { 86 struct drm_client_dev *client; 87 88 if (!drm_core_check_feature(dev, DRIVER_MODESET)) 89 return; 90 91 if (!dev->mode_config.num_connector) { 92 drm_dbg_kms(dev, "No connectors found, will not send hotplug events!\n"); 93 return; 94 } 95 96 mutex_lock(&dev->clientlist_mutex); 97 list_for_each_entry(client, &dev->clientlist, list) 98 drm_client_hotplug(client); 99 mutex_unlock(&dev->clientlist_mutex); 100 } 101 EXPORT_SYMBOL(drm_client_dev_hotplug); 102 103 void drm_client_dev_restore(struct drm_device *dev) 104 { 105 struct drm_client_dev *client; 106 int ret; 107 108 if (!drm_core_check_feature(dev, DRIVER_MODESET)) 109 return; 110 111 mutex_lock(&dev->clientlist_mutex); 112 list_for_each_entry(client, &dev->clientlist, list) { 113 if (!client->funcs || !client->funcs->restore) 114 continue; 115 116 ret = client->funcs->restore(client); 117 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); 118 if (!ret) /* The first one to return zero gets the privilege to restore */ 119 break; 120 } 121 mutex_unlock(&dev->clientlist_mutex); 122 } 123 124 static int drm_client_suspend(struct drm_client_dev *client, bool holds_console_lock) 125 { 126 struct drm_device *dev = client->dev; 127 int ret = 0; 128 129 if (drm_WARN_ON_ONCE(dev, client->suspended)) 130 return 0; 131 132 if (client->funcs && client->funcs->suspend) 133 ret = client->funcs->suspend(client, holds_console_lock); 134 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); 135 136 client->suspended = true; 137 138 return ret; 139 } 140 141 void drm_client_dev_suspend(struct drm_device *dev, bool holds_console_lock) 142 { 143 struct drm_client_dev *client; 144 145 mutex_lock(&dev->clientlist_mutex); 146 list_for_each_entry(client, &dev->clientlist, list) { 147 if (!client->suspended) 148 drm_client_suspend(client, holds_console_lock); 149 } 150 mutex_unlock(&dev->clientlist_mutex); 151 } 152 EXPORT_SYMBOL(drm_client_dev_suspend); 153 154 static int drm_client_resume(struct drm_client_dev *client, bool holds_console_lock) 155 { 156 struct drm_device *dev = client->dev; 157 int ret = 0; 158 159 if (drm_WARN_ON_ONCE(dev, !client->suspended)) 160 return 0; 161 162 if (client->funcs && client->funcs->resume) 163 ret = client->funcs->resume(client, holds_console_lock); 164 drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret); 165 166 client->suspended = false; 167 168 if (client->hotplug_pending) 169 drm_client_hotplug(client); 170 171 return ret; 172 } 173 174 void drm_client_dev_resume(struct drm_device *dev, bool holds_console_lock) 175 { 176 struct drm_client_dev *client; 177 178 mutex_lock(&dev->clientlist_mutex); 179 list_for_each_entry(client, &dev->clientlist, list) { 180 if (client->suspended) 181 drm_client_resume(client, holds_console_lock); 182 } 183 mutex_unlock(&dev->clientlist_mutex); 184 } 185 EXPORT_SYMBOL(drm_client_dev_resume); 186 187 #ifdef CONFIG_DEBUG_FS 188 static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) 189 { 190 struct drm_debugfs_entry *entry = m->private; 191 struct drm_device *dev = entry->dev; 192 struct drm_printer p = drm_seq_file_printer(m); 193 struct drm_client_dev *client; 194 195 mutex_lock(&dev->clientlist_mutex); 196 list_for_each_entry(client, &dev->clientlist, list) 197 drm_printf(&p, "%s\n", client->name); 198 mutex_unlock(&dev->clientlist_mutex); 199 200 return 0; 201 } 202 203 static const struct drm_debugfs_info drm_client_debugfs_list[] = { 204 { "internal_clients", drm_client_debugfs_internal_clients, 0 }, 205 }; 206 207 void drm_client_debugfs_init(struct drm_device *dev) 208 { 209 drm_debugfs_add_files(dev, drm_client_debugfs_list, 210 ARRAY_SIZE(drm_client_debugfs_list)); 211 } 212 #endif 213