diff options
Diffstat (limited to 'drivers/gpu/drm/drm_connector.c')
| -rw-r--r-- | drivers/gpu/drm/drm_connector.c | 205 | 
1 files changed, 204 insertions, 1 deletions
| diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 52e20c68813b..a50c82bc2b2f 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -28,6 +28,7 @@  #include <drm/drm_print.h>  #include <drm/drm_drv.h>  #include <drm/drm_file.h> +#include <drm/drm_privacy_screen_consumer.h>  #include <drm/drm_sysfs.h>  #include <linux/uaccess.h> @@ -462,6 +463,11 @@ void drm_connector_cleanup(struct drm_connector *connector)  		    DRM_CONNECTOR_REGISTERED))  		drm_connector_unregister(connector); +	if (connector->privacy_screen) { +		drm_privacy_screen_put(connector->privacy_screen); +		connector->privacy_screen = NULL; +	} +  	if (connector->tile_group) {  		drm_mode_put_tile_group(dev, connector->tile_group);  		connector->tile_group = NULL; @@ -541,7 +547,11 @@ int drm_connector_register(struct drm_connector *connector)  	connector->registration_state = DRM_CONNECTOR_REGISTERED;  	/* Let userspace know we have a new connector */ -	drm_sysfs_hotplug_event(connector->dev); +	drm_sysfs_connector_hotplug_event(connector); + +	if (connector->privacy_screen) +		drm_privacy_screen_register_notifier(connector->privacy_screen, +					   &connector->privacy_screen_notifier);  	mutex_lock(&connector_list_lock);  	list_add_tail(&connector->global_connector_list_entry, &connector_list); @@ -578,6 +588,11 @@ void drm_connector_unregister(struct drm_connector *connector)  	list_del_init(&connector->global_connector_list_entry);  	mutex_unlock(&connector_list_lock); +	if (connector->privacy_screen) +		drm_privacy_screen_unregister_notifier( +					connector->privacy_screen, +					&connector->privacy_screen_notifier); +  	if (connector->funcs->early_unregister)  		connector->funcs->early_unregister(connector); @@ -1271,6 +1286,46 @@ static const struct drm_prop_enum_list dp_colorspaces[] = {   *	For DVI-I and TVout there is also a matching property "select subconnector"   *	allowing to switch between signal types.   *	DP subconnector corresponds to a downstream port. + * + * privacy-screen sw-state, privacy-screen hw-state: + *	These 2 optional properties can be used to query the state of the + *	electronic privacy screen that is available on some displays; and in + *	some cases also control the state. If a driver implements these + *	properties then both properties must be present. + * + *	"privacy-screen hw-state" is read-only and reflects the actual state + *	of the privacy-screen, possible values: "Enabled", "Disabled, + *	"Enabled-locked", "Disabled-locked". The locked states indicate + *	that the state cannot be changed through the DRM API. E.g. there + *	might be devices where the firmware-setup options, or a hardware + *	slider-switch, offer always on / off modes. + * + *	"privacy-screen sw-state" can be set to change the privacy-screen state + *	when not locked. In this case the driver must update the hw-state + *	property to reflect the new state on completion of the commit of the + *	sw-state property. Setting the sw-state property when the hw-state is + *	locked must be interpreted by the driver as a request to change the + *	state to the set state when the hw-state becomes unlocked. E.g. if + *	"privacy-screen hw-state" is "Enabled-locked" and the sw-state + *	gets set to "Disabled" followed by the user unlocking the state by + *	changing the slider-switch position, then the driver must set the + *	state to "Disabled" upon receiving the unlock event. + * + *	In some cases the privacy-screen's actual state might change outside of + *	control of the DRM code. E.g. there might be a firmware handled hotkey + *	which toggles the actual state, or the actual state might be changed + *	through another userspace API such as writing /proc/acpi/ibm/lcdshadow. + *	In this case the driver must update both the hw-state and the sw-state + *	to reflect the new value, overwriting any pending state requests in the + *	sw-state. Any pending sw-state requests are thus discarded. + * + *	Note that the ability for the state to change outside of control of + *	the DRM master process means that userspace must not cache the value + *	of the sw-state. Caching the sw-state value and including it in later + *	atomic commits may lead to overriding a state change done through e.g. + *	a firmware handled hotkey. Therefor userspace must not include the + *	privacy-screen sw-state in an atomic commit unless it wants to change + *	its value.   */  int drm_connector_create_standard_properties(struct drm_device *dev) @@ -2365,6 +2420,154 @@ int drm_connector_set_panel_orientation_with_quirk(  }  EXPORT_SYMBOL(drm_connector_set_panel_orientation_with_quirk); +static const struct drm_prop_enum_list privacy_screen_enum[] = { +	{ PRIVACY_SCREEN_DISABLED,		"Disabled" }, +	{ PRIVACY_SCREEN_ENABLED,		"Enabled" }, +	{ PRIVACY_SCREEN_DISABLED_LOCKED,	"Disabled-locked" }, +	{ PRIVACY_SCREEN_ENABLED_LOCKED,	"Enabled-locked" }, +}; + +/** + * drm_connector_create_privacy_screen_properties - create the drm connecter's + *    privacy-screen properties. + * @connector: connector for which to create the privacy-screen properties + * + * This function creates the "privacy-screen sw-state" and "privacy-screen + * hw-state" properties for the connector. They are not attached. + */ +void +drm_connector_create_privacy_screen_properties(struct drm_connector *connector) +{ +	if (connector->privacy_screen_sw_state_property) +		return; + +	/* Note sw-state only supports the first 2 values of the enum */ +	connector->privacy_screen_sw_state_property = +		drm_property_create_enum(connector->dev, DRM_MODE_PROP_ENUM, +				"privacy-screen sw-state", +				privacy_screen_enum, 2); + +	connector->privacy_screen_hw_state_property = +		drm_property_create_enum(connector->dev, +				DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_ENUM, +				"privacy-screen hw-state", +				privacy_screen_enum, +				ARRAY_SIZE(privacy_screen_enum)); +} +EXPORT_SYMBOL(drm_connector_create_privacy_screen_properties); + +/** + * drm_connector_attach_privacy_screen_properties - attach the drm connecter's + *    privacy-screen properties. + * @connector: connector on which to attach the privacy-screen properties + * + * This function attaches the "privacy-screen sw-state" and "privacy-screen + * hw-state" properties to the connector. The initial state of both is set + * to "Disabled". + */ +void +drm_connector_attach_privacy_screen_properties(struct drm_connector *connector) +{ +	if (!connector->privacy_screen_sw_state_property) +		return; + +	drm_object_attach_property(&connector->base, +				   connector->privacy_screen_sw_state_property, +				   PRIVACY_SCREEN_DISABLED); + +	drm_object_attach_property(&connector->base, +				   connector->privacy_screen_hw_state_property, +				   PRIVACY_SCREEN_DISABLED); +} +EXPORT_SYMBOL(drm_connector_attach_privacy_screen_properties); + +static void drm_connector_update_privacy_screen_properties( +	struct drm_connector *connector, bool set_sw_state) +{ +	enum drm_privacy_screen_status sw_state, hw_state; + +	drm_privacy_screen_get_state(connector->privacy_screen, +				     &sw_state, &hw_state); + +	if (set_sw_state) +		connector->state->privacy_screen_sw_state = sw_state; +	drm_object_property_set_value(&connector->base, +			connector->privacy_screen_hw_state_property, hw_state); +} + +static int drm_connector_privacy_screen_notifier( +	struct notifier_block *nb, unsigned long action, void *data) +{ +	struct drm_connector *connector = +		container_of(nb, struct drm_connector, privacy_screen_notifier); +	struct drm_device *dev = connector->dev; + +	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); +	drm_connector_update_privacy_screen_properties(connector, true); +	drm_modeset_unlock(&dev->mode_config.connection_mutex); + +	drm_sysfs_connector_status_event(connector, +				connector->privacy_screen_sw_state_property); +	drm_sysfs_connector_status_event(connector, +				connector->privacy_screen_hw_state_property); + +	return NOTIFY_DONE; +} + +/** + * drm_connector_attach_privacy_screen_provider - attach a privacy-screen to + *    the connector + * @connector: connector to attach the privacy-screen to + * @priv: drm_privacy_screen to attach + * + * Create and attach the standard privacy-screen properties and register + * a generic notifier for generating sysfs-connector-status-events + * on external changes to the privacy-screen status. + * This function takes ownership of the passed in drm_privacy_screen and will + * call drm_privacy_screen_put() on it when the connector is destroyed. + */ +void drm_connector_attach_privacy_screen_provider( +	struct drm_connector *connector, struct drm_privacy_screen *priv) +{ +	connector->privacy_screen = priv; +	connector->privacy_screen_notifier.notifier_call = +		drm_connector_privacy_screen_notifier; + +	drm_connector_create_privacy_screen_properties(connector); +	drm_connector_update_privacy_screen_properties(connector, true); +	drm_connector_attach_privacy_screen_properties(connector); +} +EXPORT_SYMBOL(drm_connector_attach_privacy_screen_provider); + +/** + * drm_connector_update_privacy_screen - update connector's privacy-screen sw-state + * @connector_state: connector-state to update the privacy-screen for + * + * This function calls drm_privacy_screen_set_sw_state() on the connector's + * privacy-screen. + * + * If the connector has no privacy-screen, then this is a no-op. + */ +void drm_connector_update_privacy_screen(const struct drm_connector_state *connector_state) +{ +	struct drm_connector *connector = connector_state->connector; +	int ret; + +	if (!connector->privacy_screen) +		return; + +	ret = drm_privacy_screen_set_sw_state(connector->privacy_screen, +					      connector_state->privacy_screen_sw_state); +	if (ret) { +		drm_err(connector->dev, "Error updating privacy-screen sw_state\n"); +		return; +	} + +	/* The hw_state property value may have changed, update it. */ +	drm_connector_update_privacy_screen_properties(connector, false); +} +EXPORT_SYMBOL(drm_connector_update_privacy_screen); +  int drm_connector_set_obj_prop(struct drm_mode_object *obj,  				    struct drm_property *property,  				    uint64_t value) |