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