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 /** 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 */ 61 void 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 } 89 EXPORT_SYMBOL(drm_client_dev_hotplug); 90 91 void 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 112 static 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 129 void 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 } 140 EXPORT_SYMBOL(drm_client_dev_suspend); 141 142 static 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 159 void 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 } 170 EXPORT_SYMBOL(drm_client_dev_resume); 171 172 #ifdef CONFIG_DEBUG_FS 173 static 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 188 static const struct drm_debugfs_info drm_client_debugfs_list[] = { 189 { "internal_clients", drm_client_debugfs_internal_clients, 0 }, 190 }; 191 192 void 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 198