diff mbox

[RFC,v4,18/25] drm/client: Make the display modes available to clients

Message ID 20180412161237.9314-7-noralf@tronnes.org (mailing list archive)
State New, archived
Headers show

Commit Message

Noralf Trønnes April 12, 2018, 4:12 p.m. UTC
Give clients easy access to the display modes.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/drm_client.c | 159 +++++++++++++++++++++++++++++++++----------
 include/drm/drm_client.h     |  25 +++++++
 2 files changed, 148 insertions(+), 36 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 764c556630b8..bce1630a0db2 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -8,6 +8,7 @@ 
  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
  */
 
+#include <linux/list.h>
 #include <linux/slab.h>
 
 #include <drm/drm_atomic.h>
@@ -54,6 +55,7 @@  struct drm_client_display *drm_client_display_create(struct drm_device *dev)
 	}
 
 	display->dev = dev;
+	INIT_LIST_HEAD(&display->modes);
 	display->modeset_count = num_crtc;
 
 	drm_for_each_crtc(crtc, dev)
@@ -84,12 +86,16 @@  EXPORT_SYMBOL(drm_client_display_create);
  */
 void drm_client_display_free(struct drm_client_display *display)
 {
+	struct drm_display_mode *mode, *tmp;
 	struct drm_mode_set *modeset;
 	unsigned int i;
 
 	if (!display)
 		return;
 
+	list_for_each_entry_safe(mode, tmp, &display->modes, head)
+		drm_mode_destroy(display->dev, mode);
+
 	drm_client_display_for_each_modeset(modeset, display) {
 		if (modeset->mode)
 			drm_mode_destroy(display->dev, modeset->mode);
@@ -670,22 +676,70 @@  static int drm_pick_crtcs(struct drm_client_display *display,
 	return best_score;
 }
 
-/**
- * drm_client_find_display() - Find display
- * @dev: DRM device
- * @width: Maximum display mode width (optional)
- * @height: Maximum display mode height (optional)
- *
- * This function returns a display the client can use if available.
- *
- * Free resources by calling drm_client_display_free().
- *
- * Returns:
- * A &drm_client_display on success, NULL if no connectors are found
- * or error pointer on failure.
- */
-struct drm_client_display *
-drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height)
+/* Give the client a static list of display modes */
+static int drm_client_display_copy_modes(struct drm_client_display *display)
+{
+	int hdisplay = 0, vdisplay = 0, vrefresh;
+	struct drm_device *dev = display->dev;
+	struct drm_display_mode *mode, *copy;
+	struct drm_connector *connector;
+	struct drm_mode_set *modeset;
+	unsigned int count = 0;
+
+	drm_client_display_for_each_modeset(modeset, display) {
+		if (!modeset->num_connectors || !modeset->mode)
+			continue;
+
+		connector = modeset->connectors[0];
+		mode = modeset->mode;
+		count++;
+
+		if (modeset->num_connectors == 2) {
+			/* Cloned output */
+			copy = drm_mode_duplicate(dev, modeset->mode);
+			if (!copy)
+				return -ENOMEM;
+			list_add_tail(&copy->head, &display->modes);
+			display->mode = copy;
+
+			return 0;
+		}
+
+		if (!modeset->y)
+			hdisplay += modeset->mode->hdisplay;
+		if (!modeset->x)
+			vdisplay += modeset->mode->vdisplay;
+		vrefresh = modeset->mode->vrefresh;
+	}
+
+	if (!count)
+		return 0;
+
+	if (count == 1) {
+		struct drm_display_mode *iter;
+
+		list_for_each_entry(iter, &connector->modes, head) {
+			copy = drm_mode_duplicate(dev, iter);
+			if (!copy)
+				return -ENOMEM;
+			list_add_tail(&copy->head, &display->modes);
+			if (!display->mode && drm_mode_equal(iter, mode))
+				display->mode = copy;
+		}
+	} else {
+		/* Combined tile mode. Only the default one for now */
+		copy = drm_cvt_mode(dev, hdisplay, vdisplay, vrefresh, false, false, false);
+		if (!copy)
+			return -ENOMEM;
+		list_add_tail(&copy->head, &display->modes);
+		display->mode = copy;
+	}
+
+	return 0;
+}
+
+static struct drm_client_display *
+drm_client_find_display_default(struct drm_device *dev, unsigned int width, unsigned int height)
 {
 	struct drm_client_display_offset *offsets;
 	struct drm_client_display *display;
@@ -695,25 +749,6 @@  drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
 	int i, connector_count;
 	bool *enabled;
 
-	DRM_DEBUG_KMS("\n");
-
-	if (!width)
-		width = dev->mode_config.max_width;
-	if (!height)
-		height = dev->mode_config.max_height;
-
-	mutex_lock(&dev->mode_config.mutex);
-	if (!drm_client_probe_connector_modes(dev, width, height))
-		DRM_DEBUG_KMS("No connectors reported connected with modes\n");
-
-	if (dev->driver->initial_client_display) {
-		display = dev->driver->initial_client_display(dev, width, height);
-		if (display) {
-			mutex_unlock(&dev->mode_config.mutex);
-			return display;
-		}
-	}
-
 	connector_count = drm_connector_get_all(dev, &connectors);
 	if (connector_count < 1)
 		return NULL;
@@ -772,7 +807,6 @@  drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
 		}
 	}
 out:
-	mutex_unlock(&dev->mode_config.mutex);
 	drm_connector_put_all(connectors, connector_count);
 	kfree(crtcs);
 	kfree(modes);
@@ -781,4 +815,57 @@  drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
 
 	return display;
 }
+
+/**
+ * drm_client_find_display() - Find display
+ * @dev: DRM device
+ * @width: Maximum display mode width (optional)
+ * @height: Maximum display mode height (optional)
+ *
+ * This function returns a display the client can use if one is found.
+ *
+ * Free resources by calling drm_client_display_free().
+ *
+ * Returns:
+ * A &drm_client_display on success, NULL if no connectors are found
+ * or error pointer on failure.
+ */
+struct drm_client_display *
+drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height)
+{
+	struct drm_client_display *display = NULL;
+	int ret;
+
+	DRM_DEBUG_KMS("\n");
+
+	if (!width)
+		width = dev->mode_config.max_width;
+	if (!height)
+		height = dev->mode_config.max_height;
+
+	mutex_lock(&dev->mode_config.mutex);
+
+	if (!drm_client_probe_connector_modes(dev, width, height))
+		DRM_DEBUG_KMS("No connectors reported connected with modes\n");
+
+	if (dev->driver->initial_client_display)
+		display = dev->driver->initial_client_display(dev, width, height);
+
+	if (!display)
+		display = drm_client_find_display_default(dev, width, height);
+
+	if (IS_ERR_OR_NULL(display))
+		goto out_unlock;
+
+	ret = drm_client_display_copy_modes(display);
+	if (ret) {
+		drm_client_display_free(display);
+		display = ERR_PTR(ret);
+	}
+
+out_unlock:
+	mutex_unlock(&dev->mode_config.mutex);
+
+	return display;
+}
 EXPORT_SYMBOL(drm_client_find_display);
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 3befd879a0b0..524f793d6e7b 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -3,9 +3,12 @@ 
 #ifndef _DRM_CLIENT_H_
 #define _DRM_CLIENT_H_
 
+#include <linux/types.h>
+
 struct drm_connector;
 struct drm_crtc;
 struct drm_device;
+struct drm_display_mode;
 struct drm_mode_set;
 struct drm_plane;
 
@@ -33,6 +36,20 @@  struct drm_client_display {
 	 * Number of modesets
 	 */
 	unsigned int modeset_count;
+
+	/**
+	 * @modes:
+	 *
+	 * Display modes available on this display.
+	 */
+	struct list_head modes;
+
+	/**
+	 * @mode:
+	 *
+	 * The current display mode.
+	 */
+	struct drm_display_mode *mode;
 };
 
 struct drm_client_display *drm_client_display_create(struct drm_device *dev);
@@ -51,4 +68,12 @@  int drm_client_display_dpms(struct drm_client_display *display, int mode);
 struct drm_client_display *
 drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height);
 
+/**
+ * drm_client_display_for_each_mode - Iterate over the available display modes
+ * @mode: A @drm_display_mode loop cursor
+ * @display: Client display
+ */
+#define drm_client_display_for_each_mode(mode, display) \
+	list_for_each_entry(mode, &display->modes, head)
+
 #endif