[PATCH v3 1/3] drm/connector: Add generic underscan properties

Boris Brezillon boris.brezillon at bootlin.com
Thu Nov 22 11:23:29 UTC 2018


We have 3 drivers defining the "underscan", "underscan hborder" and
"underscan vborder" properties (radeon, amd and nouveau) and we are
about to add the same kind of thing in VC4.

Define generic underscan props and add new fields to the drm_connector
state so that the property parsing logic can be shared by all DRM
drivers.

A driver can now attach underscan properties to its connector through
the drm_connector_attach_underscan_properties() helper, and can
check/apply the underscan setup based on the
drm_connector_state->underscan fields.

Signed-off-by: Boris Brezillon <boris.brezillon at bootlin.com>
---
Changes in v3:
- None

Changes in v2:
- Add a new section in the connector props doc to describe underscan
  props
- Fix description of auto mode (auto means apply underscan for HDMI
  monitors only)
- Fix description of vborder/hborder:
    right_border = left_border = hborder
    top_border = bottom_border = vborder
  not
    right_border = left_border = hborder / 2
    top_border = bottom_border = vborder / 2
- Rename drm_underscan into drm_underscan_state
---
 drivers/gpu/drm/drm_atomic_uapi.c |  12 +++
 drivers/gpu/drm/drm_connector.c   | 127 ++++++++++++++++++++++++++++++
 include/drm/drm_connector.h       |  80 +++++++++++++++++++
 3 files changed, 219 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c
index d5b7f315098c..39db6e31c565 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -740,6 +740,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
 
 		return set_out_fence_for_connector(state->state, connector,
 						   fence_ptr);
+	} else if (property == connector->underscan_mode_property) {
+		state->underscan.mode = val;
+	} else if (property == connector->underscan_hborder_property) {
+		state->underscan.hborder = val;
+	} else if (property == connector->underscan_vborder_property) {
+		state->underscan.vborder = val;
 	} else if (connector->funcs->atomic_set_property) {
 		return connector->funcs->atomic_set_property(connector,
 				state, property, val);
@@ -799,6 +805,12 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
 		*val = state->scaling_mode;
 	} else if (property == connector->content_protection_property) {
 		*val = state->content_protection;
+	} else if (property == connector->underscan_mode_property) {
+		*val = state->underscan.mode;
+	} else if (property == connector->underscan_hborder_property) {
+		*val = state->underscan.hborder;
+	} else if (property == connector->underscan_vborder_property) {
+		*val = state->underscan.vborder;
 	} else if (property == config->writeback_fb_id_property) {
 		/* Writeback framebuffer is one-shot, write and forget */
 		*val = 0;
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index c555e17ab8d7..170317248da6 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -971,6 +971,38 @@ DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
  *	can also expose this property to external outputs, in which case they
  *	must support "None", which should be the default (since external screens
  *	have a built-in scaler).
+ *
+ * Connectors for non-analog outputs may also have standardized underscan
+ * properties (drivers can set this up by calling
+ * drm_connector_attach_content_protection_property() on initialization):
+ *
+ * underscan:
+ *	This properties defines whether underscan is activated or not, and when
+ *	it is activated, how the horizontal and vertical borders are calculated:
+ *
+ *	off:
+ *		Underscan is disabled. The output image shouldn't be scaled to
+ *		take screen borders into account.
+ *	on:
+ *		Underscan is activated and horizontal and vertical borders are
+ *		specified through the "underscan hborder" and
+ *		"underscan vborder" properties.
+ *	auto:
+ *		Underscan is activated only for HDMI monitors.
+ *
+ * underscan hborder:
+ *	Horizontal border expressed in pixels. The border is symmetric, which
+ *	means you'll have a border of 'hborder' pixels on the left and on the
+ *	same border on the right.
+ *	When this value is 0 and underscan is "on" or "auto", the driver will
+ *	pick a default value (the default value is driver dependent).
+ *
+ * underscan vborder:
+ *	Vertical border expressed in pixels. The border is symmetric, which
+ *	means you'll have a border of 'vborder' pixels on the top and the same
+ *	border on the bottom.
+ *	When this value is 0 and underscan is "on" or "auto", the driver will
+ *	pick a default value (the default value is driver dependent).
  */
 
 int drm_connector_create_standard_properties(struct drm_device *dev)
@@ -1241,6 +1273,101 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_mode_create_tv_properties);
 
+static const struct drm_prop_enum_list drm_underscan_mode_enum_list[] = {
+	{ DRM_UNDERSCAN_OFF, "off" },
+	{ DRM_UNDERSCAN_ON, "on" },
+	{ DRM_UNDERSCAN_AUTO, "auto" },
+};
+
+/**
+ * drm_connector_attach_underscan_properties - attach atomic underscan
+ *					       properties
+ * @connector: connector to attach underscan mode properties on.
+ * @mode_mask: bitmask of %DRM_UNDERSCAN_XX modes encoding the supported
+ *	       underscan modes.
+ * @max_hborder: maximum size of the horizontal border expressed in pixels.
+ *		 Should be > 0.
+ * @max_vborder: maximum size of the vertical border expressed in pixels.
+ *		 Should be > 0.
+ *
+ * This is used to add support for underscan to atomic drivers.
+ * The underscan config will be set to &drm_connector_state.underscan
+ * and can be used from &drm_connector_helper_funcs->atomic_check for
+ * validation.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_underscan_properties(struct drm_connector *connector,
+					      u32 mode_mask, u64 max_hborder,
+					      u64 max_vborder)
+{
+	unsigned int i, nmodes = ARRAY_SIZE(drm_underscan_mode_enum_list);
+	struct drm_device *dev = connector->dev;
+	struct drm_property *prop;
+
+	if (!max_hborder || !max_vborder)
+		return -EINVAL;
+
+	if (!hweight32(mode_mask) || (mode_mask & ~GENMASK(nmodes - 1, 0)))
+		return -EINVAL;
+
+	prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, "underscan",
+				   hweight32(mode_mask));
+	if (!prop)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(drm_underscan_mode_enum_list); i++) {
+		const struct drm_prop_enum_list *entry;
+		int ret;
+
+		if (!(BIT(i) & mode_mask))
+			continue;
+
+		entry = &drm_underscan_mode_enum_list[i];
+		ret = drm_property_add_enum(prop, entry->type, entry->name);
+		if (ret)
+			goto err_free_mode_prop;
+	}
+
+	connector->underscan_mode_property = prop;
+
+	prop = drm_property_create_range(dev, 0, "underscan hborder", 0,
+					 max_hborder);
+	if (!prop)
+		goto err_free_mode_prop;
+
+	connector->underscan_hborder_property = prop;
+
+	prop = drm_property_create_range(dev, 0, "underscan vborder", 0,
+					 max_vborder);
+	if (!prop)
+		goto err_free_hborder_prop;
+
+	connector->underscan_vborder_property = prop;
+
+	drm_object_attach_property(&connector->base,
+				   connector->underscan_mode_property,
+				   DRM_UNDERSCAN_OFF);
+	drm_object_attach_property(&connector->base,
+				   connector->underscan_hborder_property, 0);
+	drm_object_attach_property(&connector->base,
+				   connector->underscan_vborder_property, 0);
+
+	return 0;
+
+err_free_hborder_prop:
+	drm_property_destroy(dev, connector->underscan_hborder_property);
+	connector->underscan_hborder_property = NULL;
+
+err_free_mode_prop:
+	drm_property_destroy(dev, connector->underscan_mode_property);
+	connector->underscan_mode_property = NULL;
+
+	return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_connector_attach_underscan_properties);
+
 /**
  * drm_mode_create_scaling_mode_property - create scaling mode property
  * @dev: DRM device
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 5b3cf909fd5e..14d423dc7a14 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -377,6 +377,53 @@ struct drm_tv_connector_state {
 	unsigned int hue;
 };
 
+/**
+ * enum drm_underscan_mode - Underscan mode
+ *
+ * This enum is used to track the underscan mode.
+ *
+ * @DRM_UNDERSCAN_OFF: No underscan applied, the output image will be unchanged
+ * @DRM_UNDERSCAN_ON: Underscan is enabled, and horizontal/vertical border size
+ *		      are specified through the &struct_drm_underscan->hborder
+ *		      and &struct_drm_underscan->vborder fields.
+ * @DRM_UNDERSCAN_AUTO: Underscan is enabled and &struct_drm_underscan->hborder
+ *			and &struct_drm_underscan->vborder are guessed by the
+ *			driver.
+ */
+enum drm_underscan_mode {
+	DRM_UNDERSCAN_OFF,
+	DRM_UNDERSCAN_ON,
+	DRM_UNDERSCAN_AUTO,
+};
+
+/**
+ * struct drm_underscan_state - Underscan properties attached to a connector
+ *				state
+ *
+ * This can be used to tell the CRTC how the image should be scaled/placed in
+ * order to cover fit in the display connected through this connector. Most of
+ * the time used to address situations where the display borders are hidden.
+ * Can also be used to compensate overscan done on the display side.
+ */
+struct drm_underscan_state {
+	/**
+	 * @mode: Underscan mode.
+	 */
+	enum drm_underscan_mode mode;
+
+	/**
+	 * @hborder: Horizontal border. This values encodes both the left and
+	 * right borders: left_border = right_border = hborder / 2.
+	 */
+	unsigned int hborder;
+
+	/**
+	 * @vborder: Vertical border. This values encodes both the top and
+	 * bottom borders: top_border = bottom_border = vborder / 2.
+	 */
+	unsigned int vborder;
+};
+
 /**
  * struct drm_connector_state - mutable connector state
  */
@@ -461,6 +508,13 @@ struct drm_connector_state {
 	 * drm_writeback_signal_completion()
 	 */
 	struct drm_writeback_job *writeback_job;
+
+	/**
+	 * @underscan: Underscan information. Most commonly used to adjust
+	 * image when the display has borders covering part of the image of
+	 * when it's doing overscan on its own.
+	 */
+	struct drm_underscan_state underscan;
 };
 
 /**
@@ -924,6 +978,29 @@ struct drm_connector {
 	 */
 	struct drm_property_blob *path_blob_ptr;
 
+	/**
+	 * @underscan_mode_property: Optional connector underscan mode. Used by
+	 * the driver to scale the output image and compensate an overscan done
+	 * on the display side.
+	 */
+	struct drm_property *underscan_mode_property;
+
+	/**
+	 * @underscan_hborder_property: Optional connector underscan horizontal
+	 * border (expressed in pixels). Used by the driver to adjust the
+	 * output image position and compensate an overscan done on the display
+	 * side.
+	 */
+	struct drm_property *underscan_hborder_property;
+
+	/**
+	 * @underscan_hborder_property: Optional connector underscan vertical
+	 * border (expressed in pixels). Used by the driver to adjust the
+	 * output image position and compensate an overscan done on the display
+	 * side.
+	 */
+	struct drm_property *underscan_vborder_property;
+
 #define DRM_CONNECTOR_POLL_HPD (1 << 0)
 #define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
 #define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)
@@ -1180,6 +1257,9 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev);
 int drm_mode_create_tv_properties(struct drm_device *dev,
 				  unsigned int num_modes,
 				  const char * const modes[]);
+int drm_connector_attach_underscan_properties(struct drm_connector *connector,
+					      u32 mode_mask, u64 max_hborder,
+					      u64 max_vborder);
 int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 int drm_connector_attach_content_type_property(struct drm_connector *dev);
 int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
-- 
2.17.1



More information about the amd-gfx mailing list