aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_connector.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_connector.c')
-rw-r--r--drivers/gpu/drm/drm_connector.c235
1 files changed, 231 insertions, 4 deletions
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 3bc782b630b9..ec3973e8963c 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;
@@ -543,6 +549,10 @@ int drm_connector_register(struct drm_connector *connector)
/* Let userspace know we have a new connector */
drm_sysfs_hotplug_event(connector->dev);
+ 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);
mutex_unlock(&connector_list_lock);
@@ -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);
@@ -625,6 +640,8 @@ int drm_connector_register_all(struct drm_device *dev)
*
* In contrast to the other drm_get_*_name functions this one here returns a
* const pointer and hence is threadsafe.
+ *
+ * Returns: connector status string
*/
const char *drm_get_connector_status_name(enum drm_connector_status status)
{
@@ -707,7 +724,7 @@ __drm_connector_put_safe(struct drm_connector *conn)
* drm_connector_list_iter_next - return next connector
* @iter: connector_list iterator
*
- * Returns the next connector for @iter, or NULL when the list walk has
+ * Returns: the next connector for @iter, or NULL when the list walk has
* completed.
*/
struct drm_connector *
@@ -780,6 +797,8 @@ static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
*
* Note you could abuse this and return something out of bounds, but that
* would be a caller error. No unscrubbed user data should make it here.
+ *
+ * Returns: string describing an enumerated subpixel property
*/
const char *drm_get_subpixel_order_name(enum subpixel_order order)
{
@@ -809,6 +828,9 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = {
* Store the supported bus formats in display info structure.
* See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
* a full list of available formats.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
*/
int drm_display_info_set_bus_formats(struct drm_display_info *info,
const u32 *formats,
@@ -1264,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)
@@ -1326,6 +1388,8 @@ int drm_connector_create_standard_properties(struct drm_device *dev)
* @dev: DRM device
*
* Called by a driver the first time a DVI-I connector is made.
+ *
+ * Returns: %0
*/
int drm_mode_create_dvi_i_properties(struct drm_device *dev)
{
@@ -1397,6 +1461,8 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property);
* Game:
* Content type is game
*
+ * The meaning of each content type is defined in CTA-861-G table 15.
+ *
* Drivers can set up this property by calling
* drm_connector_attach_content_type_property(). Decoding to
* infoframe values is done through drm_hdmi_avi_infoframe_content_type().
@@ -1407,6 +1473,8 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property);
* @connector: connector to attach content type property on.
*
* Called by a driver the first time a HDMI connector is made.
+ *
+ * Returns: %0
*/
int drm_connector_attach_content_type_property(struct drm_connector *connector)
{
@@ -1487,6 +1555,9 @@ EXPORT_SYMBOL(drm_connector_attach_tv_margin_properties);
* creates the TV margin properties for a given device. No need to call this
* function for an SDTV connector, it's already called from
* drm_mode_create_tv_properties().
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
*/
int drm_mode_create_tv_margin_properties(struct drm_device *dev)
{
@@ -1527,6 +1598,9 @@ EXPORT_SYMBOL(drm_mode_create_tv_margin_properties);
* the TV specific connector properties for a given device. Caller is
* responsible for allocating a list of format names and passing them to
* this routine.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
*/
int drm_mode_create_tv_properties(struct drm_device *dev,
unsigned int num_modes,
@@ -1622,6 +1696,8 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
* Atomic drivers should use drm_connector_attach_scaling_mode_property()
* instead to correctly assign &drm_connector_state.scaling_mode
* in the atomic state.
+ *
+ * Returns: %0
*/
int drm_mode_create_scaling_mode_property(struct drm_device *dev)
{
@@ -1939,6 +2015,9 @@ EXPORT_SYMBOL(drm_mode_create_content_type_property);
* @dev: DRM device
*
* Create the suggested x/y offset property for connectors.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
*/
int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
{
@@ -2312,8 +2391,8 @@ int drm_connector_set_panel_orientation(
EXPORT_SYMBOL(drm_connector_set_panel_orientation);
/**
- * drm_connector_set_panel_orientation_with_quirk -
- * set the connector's panel_orientation after checking for quirks
+ * drm_connector_set_panel_orientation_with_quirk - set the
+ * connector's panel_orientation after checking for quirks
* @connector: connector for which to init the panel-orientation property.
* @panel_orientation: drm_panel_orientation value to set
* @width: width in pixels of the panel, used for panel quirk detection
@@ -2341,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)
@@ -2597,7 +2824,7 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
/**
* drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
- * @connector: connector to report the event on
+ * @connector_fwnode: fwnode_handle to report the event on
*
* On some hardware a hotplug event notification may come from outside the display
* driver / device. An example of this is some USB Type-C setups where the hardware