@@ -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(©->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(©->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(©->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);
@@ -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
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(-)