drm: Add support for a panel-orientation connector property, v6
authorHans de Goede <j.w.r.degoede@gmail.com>
Sat, 25 Nov 2017 19:35:49 +0000 (20:35 +0100)
committerHans de Goede <hdegoede@redhat.com>
Mon, 4 Dec 2017 22:03:21 +0000 (23:03 +0100)
On some devices the LCD panel is mounted in the casing in such a way that
the up/top side of the panel does not match with the top side of the
device (e.g. it is mounted upside-down).

This commit adds the necessary infra for lcd-panel drm_connector-s to
have a "panel orientation" property to communicate how the panel is
orientated vs the casing.

Userspace can use this property to check for non-normal orientation and
then adjust the displayed image accordingly by rotating it to compensate.

Changes in v2:
-Store panel_orientation in drm_display_info, so that drm_fb_helper.c can
 access it easily
-Have a single drm_connector_init_panel_orientation_property rather then
 create and attach functions. The caller is expected to set
 drm_display_info.panel_orientation before calling this, then this will
 check for platform specific quirks overriding the panel_orientation and if
 the panel_orientation is set after this then it will attach the property.

Changes in v6:
-Use an enum (with kerneldoc) rather then #defines for
 DRM_MODE_PANEL_ORIENTATION_*

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20171125193553.23986-4-hdegoede@redhat.com
drivers/gpu/drm/Kconfig
drivers/gpu/drm/drm_connector.c
include/drm/drm_connector.h
include/drm/drm_mode_config.h

index 9d005ac98c2b81f167303b5deb8ef845c18b16f9..0b166e626eb6418de62b6204c280a22d965a201f 100644 (file)
@@ -7,6 +7,7 @@
 menuconfig DRM
        tristate "Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)"
        depends on (AGP || AGP=n) && !EMULATED_CMPXCHG && HAS_DMA
+       select DRM_PANEL_ORIENTATION_QUIRKS
        select HDMI
        select FB_CMDLINE
        select I2C
index 25f4b2e9a44fcdd5cef668df014001e1c1b1c90e..624edeb5c50d38ecedbed490c8ce05fca11fed99 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_utils.h>
 
 #include "drm_crtc_internal.h"
 #include "drm_internal.h"
@@ -212,6 +213,8 @@ int drm_connector_init(struct drm_device *dev,
        mutex_init(&connector->mutex);
        connector->edid_blob_ptr = NULL;
        connector->status = connector_status_unknown;
+       connector->display_info.panel_orientation =
+               DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
 
        drm_connector_get_cmdline_mode(connector);
 
@@ -668,6 +671,13 @@ static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
        { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
 };
 
+static const struct drm_prop_enum_list drm_panel_orientation_enum_list[] = {
+       { DRM_MODE_PANEL_ORIENTATION_NORMAL,    "Normal"        },
+       { DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP, "Upside Down"   },
+       { DRM_MODE_PANEL_ORIENTATION_LEFT_UP,   "Left Side Up"  },
+       { DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,  "Right Side Up" },
+};
+
 static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
        { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
        { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
@@ -776,6 +786,18 @@ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
  *
  * CRTC_ID:
  *     Mode object ID of the &drm_crtc this connector should be connected to.
+ *
+ * Connectors for LCD panels may also have one standardized property:
+ *
+ * panel orientation:
+ *     On some devices the LCD panel is mounted in the casing in such a way
+ *     that the up/top side of the panel does not match with the top side of
+ *     the device. Userspace can use this property to check for this.
+ *     Note that input coordinates from touchscreens (input devices with
+ *     INPUT_PROP_DIRECT) will still map 1:1 to the actual LCD panel
+ *     coordinates, so if userspace rotates the picture to adjust for
+ *     the orientation it must also apply the same transformation to the
+ *     touchscreen input coordinates.
  */
 
 int drm_connector_create_standard_properties(struct drm_device *dev)
@@ -1251,6 +1273,57 @@ void drm_mode_connector_set_link_status_property(struct drm_connector *connector
 }
 EXPORT_SYMBOL(drm_mode_connector_set_link_status_property);
 
+/**
+ * drm_connector_init_panel_orientation_property -
+ *     initialize the connecters panel_orientation property
+ * @connector: connector for which to init the panel-orientation property.
+ * @width: width in pixels of the panel, used for panel quirk detection
+ * @height: height in pixels of the panel, used for panel quirk detection
+ *
+ * This function should only be called for built-in panels, after setting
+ * connector->display_info.panel_orientation first (if known).
+ *
+ * This function will check for platform specific (e.g. DMI based) quirks
+ * overriding display_info.panel_orientation first, then if panel_orientation
+ * is not DRM_MODE_PANEL_ORIENTATION_UNKNOWN it will attach the
+ * "panel orientation" property to the connector.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_init_panel_orientation_property(
+       struct drm_connector *connector, int width, int height)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_info *info = &connector->display_info;
+       struct drm_property *prop;
+       int orientation_quirk;
+
+       orientation_quirk = drm_get_panel_orientation_quirk(width, height);
+       if (orientation_quirk != DRM_MODE_PANEL_ORIENTATION_UNKNOWN)
+               info->panel_orientation = orientation_quirk;
+
+       if (info->panel_orientation == DRM_MODE_PANEL_ORIENTATION_UNKNOWN)
+               return 0;
+
+       prop = dev->mode_config.panel_orientation_property;
+       if (!prop) {
+               prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                               "panel orientation",
+                               drm_panel_orientation_enum_list,
+                               ARRAY_SIZE(drm_panel_orientation_enum_list));
+               if (!prop)
+                       return -ENOMEM;
+
+               dev->mode_config.panel_orientation_property = prop;
+       }
+
+       drm_object_attach_property(&connector->base, prop,
+                                  info->panel_orientation);
+       return 0;
+}
+EXPORT_SYMBOL(drm_connector_init_panel_orientation_property);
+
 int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
                                    struct drm_property *property,
                                    uint64_t value)
index 66d6c99d15e56449c8d1693c24668bb345b004f5..f39ff52feb3be77809e83fdbf0c735cf728e58d1 100644 (file)
@@ -175,6 +175,35 @@ enum drm_link_status {
        DRM_LINK_STATUS_BAD = DRM_MODE_LINK_STATUS_BAD,
 };
 
+/**
+ * enum drm_panel_orientation - panel_orientation info for &drm_display_info
+ *
+ * This enum is used to track the (LCD) panel orientation. There are no
+ * separate #defines for the uapi!
+ *
+ * @DRM_MODE_PANEL_ORIENTATION_UNKNOWN: The drm driver has not provided any
+ *                                     panel orientation information (normal
+ *                                     for non panels) in this case the "panel
+ *                                     orientation" connector prop will not be
+ *                                     attached.
+ * @DRM_MODE_PANEL_ORIENTATION_NORMAL: The top side of the panel matches the
+ *                                     top side of the device's casing.
+ * @DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP: The top side of the panel matches the
+ *                                     bottom side of the device's casing, iow
+ *                                     the panel is mounted upside-down.
+ * @DRM_MODE_PANEL_ORIENTATION_LEFT_UP:        The left side of the panel matches the
+ *                                     top side of the device's casing.
+ * @DRM_MODE_PANEL_ORIENTATION_RIGHT_UP: The right side of the panel matches the
+ *                                     top side of the device's casing.
+ */
+enum drm_panel_orientation {
+       DRM_MODE_PANEL_ORIENTATION_UNKNOWN = -1,
+       DRM_MODE_PANEL_ORIENTATION_NORMAL = 0,
+       DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP,
+       DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
+       DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
+};
+
 /**
  * struct drm_display_info - runtime data about the connected sink
  *
@@ -222,6 +251,15 @@ struct drm_display_info {
 #define DRM_COLOR_FORMAT_YCRCB422      (1<<2)
 #define DRM_COLOR_FORMAT_YCRCB420      (1<<3)
 
+       /**
+        * @panel_orientation: Read only connector property for built-in panels,
+        * indicating the orientation of the panel vs the device's casing.
+        * drm_connector_init() sets this to DRM_MODE_PANEL_ORIENTATION_UNKNOWN.
+        * When not UNKNOWN this gets used by the drm_fb_helpers to rotate the
+        * fb to compensate and gets exported as prop to userspace.
+        */
+       int panel_orientation;
+
        /**
         * @color_formats: HDMI Color formats, selects between RGB and YCrCb
         * modes. Used DRM_COLOR_FORMAT\_ defines, which are _not_ the same ones
@@ -1035,6 +1073,8 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
                                            const struct edid *edid);
 void drm_mode_connector_set_link_status_property(struct drm_connector *connector,
                                                 uint64_t link_status);
+int drm_connector_init_panel_orientation_property(
+       struct drm_connector *connector, int width, int height);
 
 /**
  * struct drm_tile_group - Tile group metadata
index f7ef0023d68b531375ccc9d83239ee88c6709168..a0afeb591dcb1cb25e7f7005dd903ce229792cab 100644 (file)
@@ -735,6 +735,13 @@ struct drm_mode_config {
         */
        struct drm_property *non_desktop_property;
 
+       /**
+        * @panel_orientation_property: Optional connector property indicating
+        * how the lcd-panel is mounted inside the casing (e.g. normal or
+        * upside-down).
+        */
+       struct drm_property *panel_orientation_property;
+
        /* dumb ioctl parameters */
        uint32_t preferred_depth, prefer_shadow;