Merge branch 'linux-linaro-lsk-v4.4-android' of git://git.linaro.org/kernel/linux...
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_crtc.c
index 5e4bb4837bae7216cbdc8ea64b9ae2e7429646fa..29d13ef24996f87feb2f9e16d232487d189531d6 100644 (file)
@@ -657,6 +657,7 @@ DEFINE_WW_CLASS(crtc_ww_class);
  * @primary: Primary plane for CRTC
  * @cursor: Cursor plane for CRTC
  * @funcs: callbacks for the new CRTC
+ * @name: printf style format string for the CRTC name, or NULL for default name
  *
  * Inits a new object created as base part of a driver crtc object.
  *
@@ -666,7 +667,8 @@ DEFINE_WW_CLASS(crtc_ww_class);
 int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
                              struct drm_plane *primary,
                              struct drm_plane *cursor,
-                             const struct drm_crtc_funcs *funcs)
+                             const struct drm_crtc_funcs *funcs,
+                             const char *name, ...)
 {
        struct drm_mode_config *config = &dev->mode_config;
        int ret;
@@ -1075,6 +1077,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
  * @encoder: the encoder to init
  * @funcs: callbacks for this encoder
  * @encoder_type: user visible type of the encoder
+ * @name: printf style format string for the encoder name, or NULL for default name
  *
  * Initialises a preallocated encoder. Encoder should be
  * subclassed as part of driver encoder objects.
@@ -1085,7 +1088,7 @@ EXPORT_SYMBOL(drm_connector_unplug_all);
 int drm_encoder_init(struct drm_device *dev,
                      struct drm_encoder *encoder,
                      const struct drm_encoder_funcs *funcs,
-                     int encoder_type)
+                     int encoder_type, const char *name, ...)
 {
        int ret;
 
@@ -1150,6 +1153,7 @@ EXPORT_SYMBOL(drm_encoder_cleanup);
  * @formats: array of supported formats (%DRM_FORMAT_*)
  * @format_count: number of elements in @formats
  * @type: type of plane (overlay, primary, cursor)
+ * @name: printf style format string for the plane name, or NULL for default name
  *
  * Initializes a plane object of type @type.
  *
@@ -1160,7 +1164,8 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
                             unsigned long possible_crtcs,
                             const struct drm_plane_funcs *funcs,
                             const uint32_t *formats, unsigned int format_count,
-                            enum drm_plane_type type)
+                            enum drm_plane_type type,
+                            const char *name, ...)
 {
        struct drm_mode_config *config = &dev->mode_config;
        int ret;
@@ -1240,10 +1245,100 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 
        type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
        return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
-                                       formats, format_count, type);
+                                       formats, format_count, type, NULL);
 }
 EXPORT_SYMBOL(drm_plane_init);
 
+/**
+ * drm_share_plane_init - Initialize a share plane
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @parent: this plane share some resources with parent plane.
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @format_count: number of elements in @formats
+ * @type: type of plane (overlay, primary, cursor)
+ *
+ * With this API, the plane can share hardware resources with other planes.
+ *
+ *   --------------------------------------------------
+ *   |  scanout                                       |
+ *   |         ------------------                     |
+ *   |         |  parent plane  |                     |
+ *   |         | active scanout |                     |
+ *   |         |                |   ----------------- |
+ *   |         ------------------   | share plane 1 | |
+ *   |  -----------------           |active scanout | |
+ *   |  | share plane 0 |           |               | |
+ *   |  |active scanout |           ----------------- |
+ *   |  |               |                             |
+ *   |  -----------------                             |
+ *   --------------------------------------------------
+ *
+ *    parent plane
+ *        |---share plane 0
+ *        |---share plane 1
+ *        ...
+ *
+ * The plane hardware is used when the display scanout run into plane active
+ * scanout, that means we can reuse the plane hardware resources on plane
+ * non-active scanout.
+ *
+ * Because resource share, There are some limit on share plane: one group
+ * of share planes need use same zpos, can't not overlap, etc.
+ *
+ * Here assume share plane is a universal plane with some limit flags.
+ * people who use the share plane need know the limit, should call the ioctl
+ * DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+
+int drm_share_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                        struct drm_plane *parent,
+                        unsigned long possible_crtcs,
+                        const struct drm_plane_funcs *funcs,
+                        const uint32_t *formats, unsigned int format_count,
+                        enum drm_plane_type type)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       int ret;
+       int share_id;
+
+       /*
+        * TODO: only verified on ATOMIC drm driver.
+        */
+       if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
+               return -EINVAL;
+
+       ret = drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+                                      formats, format_count, type, NULL);
+       if (ret)
+               return ret;
+
+       if (parent) {
+               /*
+                * Can't support more than two level plane share.
+                */
+               WARN_ON(parent->parent);
+               share_id = parent->base.id;
+               plane->parent = parent;
+
+               config->num_share_plane++;
+               if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+                       config->num_share_overlay_plane++;
+       } else {
+               share_id = plane->base.id;
+       }
+
+       drm_object_attach_property(&plane->base,
+                                  config->prop_share_id, share_id);
+       return 0;
+}
+EXPORT_SYMBOL(drm_share_plane_init);
+
 /**
  * drm_plane_cleanup - Clean up the core plane usage
  * @plane: plane to cleanup
@@ -1266,6 +1361,11 @@ void drm_plane_cleanup(struct drm_plane *plane)
        dev->mode_config.num_total_plane--;
        if (plane->type == DRM_PLANE_TYPE_OVERLAY)
                dev->mode_config.num_overlay_plane--;
+       if (plane->parent) {
+               dev->mode_config.num_share_plane--;
+               if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+                       dev->mode_config.num_share_overlay_plane--;
+       }
        drm_modeset_unlock_all(dev);
 
        WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
@@ -1397,6 +1497,18 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
                return -ENOMEM;
        dev->mode_config.plane_type_property = prop;
 
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
+                                        "SHARE_ID", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+
+       dev->mode_config.prop_share_id = prop;
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
+                                        "SHARE_FLAGS", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_share_flags = prop;
+
        prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
                        "SRC_X", 0, UINT_MAX);
        if (!prop)
@@ -2203,6 +2315,12 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
                num_planes = config->num_total_plane;
        else
                num_planes = config->num_overlay_plane;
+       if (!file_priv->share_planes) {
+               if (file_priv->universal_planes)
+                       num_planes -= config->num_share_plane;
+               else
+                       num_planes -= config->num_share_overlay_plane;
+       }
 
        /*
         * This ioctl is called twice, once to determine how much space is
@@ -2221,6 +2339,8 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
                        if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
                            !file_priv->universal_planes)
                                continue;
+                       if (plane->parent && !file_priv->share_planes)
+                               continue;
 
                        if (put_user(plane->base.id, plane_ptr + copied))
                                return -EFAULT;
@@ -5031,6 +5151,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector,
 {
        int i;
 
+       /*
+        * In the past, drivers have attempted to model the static association
+        * of connector to encoder in simple connector/encoder devices using a
+        * direct assignment of connector->encoder = encoder. This connection
+        * is a logical one and the responsibility of the core, so drivers are
+        * expected not to mess with this.
+        *
+        * Note that the error return should've been enough here, but a large
+        * majority of drivers ignores the return value, so add in a big WARN
+        * to get people's attention.
+        */
+       if (WARN_ON(connector->encoder))
+               return -EINVAL;
+
        for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
                if (connector->encoder_ids[i] == 0) {
                        connector->encoder_ids[i] = encoder->base.id;