On some hardware a hotplug event notification may come from outside the display driver / device. One example of this is laptops with hybrid graphics where one or more outputs are connected to the discrete GPU only. In this case if the discrete GPU is fully powered down to save power, a hotplug to one of these outputs will not be noticed through normal means. These laptops have another mechanism to detect the hotplug which will typically raise an event on the ACPI video bus. Another example of this is some USB Type-C setups where the hardware muxes the DisplayPort data and aux-lines but does not pass the altmode HPD status bit to the GPUs DP HPD pin. This commit adds a loose coupling to the drm subsys allowing event-sources to notify drm-drivers of such events without needing a reference to a drm-device or a drm-connector. This loose coupling is implemented through a blocking notifier. drm-drivers interested in oob hotplug events can register themselves to receive notifations and event-sources call drm_kms_call_oob_hotplug_notifier_chain to let any listeners know about events. An earlier attempt has been done to implement this functionality with a tight coupling, where the event-source would somehow figure out the right drm-connector to deliver the event to and then send it directly to that drm-connector. Such a tight coupling approach has several issues with lifetime management of the coupled objects as well as introducing several probe-ordering issues. Therefor the tight coupling approach has been abandoned. Note for now drm_kms_call_oob_hotplug_notifier_chain's event parameter can only have 1 value: DRM_OOB_HOTPLUG_TYPE_C_DP. The ACPI videobus hotplug event case is currently already supported in the nouveau driver by registering a generic acpi event notifier. Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> --- Documentation/gpu/drm-kms-helpers.rst | 1 + drivers/gpu/drm/drm_probe_helper.c | 67 +++++++++++++++++++++++++++ include/drm/drm_probe_helper.h | 12 +++++ 3 files changed, 80 insertions(+) diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index b422eb8edf16..f144d68f8e7a 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -249,6 +249,7 @@ Output Probing Helper Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c :doc: output probing helper overview + :doc: out-of-band hotplug event helper overview .. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c :export: diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 6fd08e04b323..4f0b421514ef 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -31,6 +31,7 @@ #include <linux/export.h> #include <linux/moduleparam.h> +#include <linux/notifier.h> #include <drm/drmP.h> #include <drm/drm_client.h> @@ -792,3 +793,69 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) return changed; } EXPORT_SYMBOL(drm_helper_hpd_irq_event); + +/** + * DOC: out-of-band hotplug event helper overview + * + * On some hardware a hotplug event notification may come from outside + * the display driver / device. + * + * One example of this is laptops with hybrid graphics where one or more + * outputs are connected to the discrete GPU only. In this case if the discrete + * GPU is fully powered down to save power, a hotplug to one of these outputs + * will not be noticed through normal means. These laptops have another + * mechanism to detect the hotplug which will typically raise an event on the + * ACPI video bus. + * + * Another example of this is some USB Type-C setups where the hardware + * muxes the DisplayPort data and aux-lines but does not pass the altmode + * HPD status bit to the GPUs DP HPD pin. + * + * The oob hotplug helper functions allow a drm display driver to listen + * for such oob events and allow other subsystems to notify listeners of + * these events. + */ + +static BLOCKING_NOTIFIER_HEAD(drm_kms_oob_hotplug_notifier_head); + +/** + * drm_kms_register_oob_hotplug_notifier - register an oob hotplug notifier + * @nb: notifier_block to register + * + * Drivers can use this helper function to register a notifier for + * out-of-band hotplug events. + */ +int drm_kms_register_oob_hotplug_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register( + &drm_kms_oob_hotplug_notifier_head, nb); +} +EXPORT_SYMBOL(drm_kms_register_oob_hotplug_notifier); + +/** + * drm_kms_unregister_oob_hotplug_notifier - unregister an oob hotplug notifier + * @nb: notifier_block to register + * + * Drivers can use this helper function to unregister a notifier for + * out-of-band hotplug events. + */ +int drm_kms_unregister_oob_hotplug_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister( + &drm_kms_oob_hotplug_notifier_head, nb); +} +EXPORT_SYMBOL(drm_kms_unregister_oob_hotplug_notifier); + +/** + * drm_kms_call_oob_hotplug_notifier_chain - notify about an oob hotplug event + * @event: enum drm_kms_oob_hotplug_event value describing the event + * + * Out-of-band hotplug event sources can call this helper function to notify + * kms-drivers about an oob hotplug event. + */ +int drm_kms_call_oob_hotplug_notifier_chain(unsigned long event) +{ + return blocking_notifier_call_chain( + &drm_kms_oob_hotplug_notifier_head, event, NULL); +} +EXPORT_SYMBOL(drm_kms_call_oob_hotplug_notifier_chain); diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h index 8d3ed2834d34..68cfce47d35f 100644 --- a/include/drm/drm_probe_helper.h +++ b/include/drm/drm_probe_helper.h @@ -24,4 +24,16 @@ void drm_kms_helper_poll_disable(struct drm_device *dev); void drm_kms_helper_poll_enable(struct drm_device *dev); bool drm_kms_helper_is_poll_worker(void); +/** + * enum drm_kms_oob_hotplug_event - out-of-band hotplug events + * @DRM_OOB_HOTPLUG_TYPE_C_DP: DisplayPort over Type-C hotplug event + */ +enum drm_kms_oob_hotplug_event { + DRM_OOB_HOTPLUG_TYPE_C_DP = 0, +}; + +int drm_kms_register_oob_hotplug_notifier(struct notifier_block *nb); +int drm_kms_unregister_oob_hotplug_notifier(struct notifier_block *nb); +int drm_kms_call_oob_hotplug_notifier_chain(unsigned long event); + #endif -- 2.20.1