Merge tag 'lsk-v4.4-16.06-android'
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / drm_crtc.c
index 8328e7059205d266a456710613628c64b097f787..7febc7dc6e68ddd724706a5632296d8b42560848 100644 (file)
@@ -306,8 +306,7 @@ static int drm_mode_object_get_reg(struct drm_device *dev,
  * reference counted modeset objects like framebuffers.
  *
  * Returns:
- * New unique (relative to other objects in @dev) integer identifier for the
- * object.
+ * Zero on success, error code on failure.
  */
 int drm_mode_object_get(struct drm_device *dev,
                        struct drm_mode_object *obj, uint32_t obj_type)
@@ -423,7 +422,7 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
 out:
        mutex_unlock(&dev->mode_config.fb_lock);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL(drm_framebuffer_init);
 
@@ -538,7 +537,12 @@ EXPORT_SYMBOL(drm_framebuffer_reference);
  */
 void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->dev;
+       struct drm_device *dev;
+
+       if (!fb)
+               return;
+
+       dev = fb->dev;
 
        mutex_lock(&dev->mode_config.fb_lock);
        /* Mark fb as reaped and drop idr ref. */
@@ -589,12 +593,17 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
  */
 void drm_framebuffer_remove(struct drm_framebuffer *fb)
 {
-       struct drm_device *dev = fb->dev;
+       struct drm_device *dev;
        struct drm_crtc *crtc;
        struct drm_plane *plane;
        struct drm_mode_set set;
        int ret;
 
+       if (!fb)
+               return;
+
+       dev = fb->dev;
+
        WARN_ON(!list_empty(&fb->filp_head));
 
        /*
@@ -648,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.
  *
@@ -657,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;
@@ -667,7 +678,6 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 
        crtc->dev = dev;
        crtc->funcs = funcs;
-       crtc->invert_dimensions = false;
 
        drm_modeset_lock_init(&crtc->mutex);
        ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
@@ -1067,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.
@@ -1077,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;
 
@@ -1142,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.
  *
@@ -1152,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;
@@ -1232,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
@@ -1258,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);
@@ -1389,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)
@@ -1509,7 +1629,7 @@ EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
  */
 int drm_mode_create_tv_properties(struct drm_device *dev,
                                  unsigned int num_modes,
-                                 char *modes[])
+                                 const char * const modes[])
 {
        struct drm_property *tv_selector;
        struct drm_property *tv_subconnector;
@@ -1525,6 +1645,9 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
                                          "select subconnector",
                                          drm_tv_select_enum_list,
                                          ARRAY_SIZE(drm_tv_select_enum_list));
+       if (!tv_selector)
+               goto nomem;
+
        dev->mode_config.tv_select_subconnector_property = tv_selector;
 
        tv_subconnector =
@@ -1532,6 +1655,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
                                    "subconnector",
                                    drm_tv_subconnector_enum_list,
                                    ARRAY_SIZE(drm_tv_subconnector_enum_list));
+       if (!tv_subconnector)
+               goto nomem;
        dev->mode_config.tv_subconnector_property = tv_subconnector;
 
        /*
@@ -1539,42 +1664,67 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
         */
        dev->mode_config.tv_left_margin_property =
                drm_property_create_range(dev, 0, "left margin", 0, 100);
+       if (!dev->mode_config.tv_left_margin_property)
+               goto nomem;
 
        dev->mode_config.tv_right_margin_property =
                drm_property_create_range(dev, 0, "right margin", 0, 100);
+       if (!dev->mode_config.tv_right_margin_property)
+               goto nomem;
 
        dev->mode_config.tv_top_margin_property =
                drm_property_create_range(dev, 0, "top margin", 0, 100);
+       if (!dev->mode_config.tv_top_margin_property)
+               goto nomem;
 
        dev->mode_config.tv_bottom_margin_property =
                drm_property_create_range(dev, 0, "bottom margin", 0, 100);
+       if (!dev->mode_config.tv_bottom_margin_property)
+               goto nomem;
 
        dev->mode_config.tv_mode_property =
                drm_property_create(dev, DRM_MODE_PROP_ENUM,
                                    "mode", num_modes);
+       if (!dev->mode_config.tv_mode_property)
+               goto nomem;
+
        for (i = 0; i < num_modes; i++)
                drm_property_add_enum(dev->mode_config.tv_mode_property, i,
                                      i, modes[i]);
 
        dev->mode_config.tv_brightness_property =
                drm_property_create_range(dev, 0, "brightness", 0, 100);
+       if (!dev->mode_config.tv_brightness_property)
+               goto nomem;
 
        dev->mode_config.tv_contrast_property =
                drm_property_create_range(dev, 0, "contrast", 0, 100);
+       if (!dev->mode_config.tv_contrast_property)
+               goto nomem;
 
        dev->mode_config.tv_flicker_reduction_property =
                drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
+       if (!dev->mode_config.tv_flicker_reduction_property)
+               goto nomem;
 
        dev->mode_config.tv_overscan_property =
                drm_property_create_range(dev, 0, "overscan", 0, 100);
+       if (!dev->mode_config.tv_overscan_property)
+               goto nomem;
 
        dev->mode_config.tv_saturation_property =
                drm_property_create_range(dev, 0, "saturation", 0, 100);
+       if (!dev->mode_config.tv_saturation_property)
+               goto nomem;
 
        dev->mode_config.tv_hue_property =
                drm_property_create_range(dev, 0, "hue", 0, 100);
+       if (!dev->mode_config.tv_hue_property)
+               goto nomem;
 
        return 0;
+nomem:
+       return -ENOMEM;
 }
 EXPORT_SYMBOL(drm_mode_create_tv_properties);
 
@@ -2165,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
@@ -2183,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;
@@ -2276,6 +2434,32 @@ int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
        return -EINVAL;
 }
 
+static int check_src_coords(uint32_t src_x, uint32_t src_y,
+                           uint32_t src_w, uint32_t src_h,
+                           const struct drm_framebuffer *fb)
+{
+       unsigned int fb_width, fb_height;
+
+       fb_width = fb->width << 16;
+       fb_height = fb->height << 16;
+
+       /* Make sure source coordinates are inside the fb. */
+       if (src_w > fb_width ||
+           src_x > fb_width - src_w ||
+           src_h > fb_height ||
+           src_y > fb_height - src_h) {
+               DRM_DEBUG_KMS("Invalid source coordinates "
+                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+                             src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
+                             src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
+                             src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
+                             src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
 /*
  * setplane_internal - setplane handler for internal callers
  *
@@ -2295,7 +2479,6 @@ static int __setplane_internal(struct drm_plane *plane,
                               uint32_t src_w, uint32_t src_h)
 {
        int ret = 0;
-       unsigned int fb_width, fb_height;
 
        /* No fb means shut it down */
        if (!fb) {
@@ -2332,27 +2515,13 @@ static int __setplane_internal(struct drm_plane *plane,
            crtc_y > INT_MAX - (int32_t) crtc_h) {
                DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
                              crtc_w, crtc_h, crtc_x, crtc_y);
-               return -ERANGE;
+               ret = -ERANGE;
+               goto out;
        }
 
-
-       fb_width = fb->width << 16;
-       fb_height = fb->height << 16;
-
-       /* Make sure source coordinates are inside the fb. */
-       if (src_w > fb_width ||
-           src_x > fb_width - src_w ||
-           src_h > fb_height ||
-           src_y > fb_height - src_h) {
-               DRM_DEBUG_KMS("Invalid source coordinates "
-                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
-                             src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
-                             src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
-                             src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
-                             src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
-               ret = -ENOSPC;
+       ret = check_src_coords(src_x, src_y, src_w, src_h, fb);
+       if (ret)
                goto out;
-       }
 
        plane->old_fb = plane->fb;
        ret = plane->funcs->update_plane(plane, crtc, fb,
@@ -2543,20 +2712,13 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
 
        drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
 
-       if (crtc->invert_dimensions)
+       if (crtc->state &&
+           crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) |
+                                             BIT(DRM_ROTATE_270)))
                swap(hdisplay, vdisplay);
 
-       if (hdisplay > fb->width ||
-           vdisplay > fb->height ||
-           x > fb->width - hdisplay ||
-           y > fb->height - vdisplay) {
-               DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
-                             fb->width, fb->height, hdisplay, vdisplay, x, y,
-                             crtc->invert_dimensions ? " (inverted)" : "");
-               return -ENOSPC;
-       }
-
-       return 0;
+       return check_src_coords(x << 16, y << 16,
+                               hdisplay << 16, vdisplay << 16, fb);
 }
 EXPORT_SYMBOL(drm_crtc_check_viewport);
 
@@ -3274,6 +3436,24 @@ int drm_mode_addfb2(struct drm_device *dev,
        return 0;
 }
 
+struct drm_mode_rmfb_work {
+       struct work_struct work;
+       struct list_head fbs;
+};
+
+static void drm_mode_rmfb_work_fn(struct work_struct *w)
+{
+       struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
+
+       while (!list_empty(&arg->fbs)) {
+               struct drm_framebuffer *fb =
+                       list_first_entry(&arg->fbs, typeof(*fb), filp_head);
+
+               list_del_init(&fb->filp_head);
+               drm_framebuffer_remove(fb);
+       }
+}
+
 /**
  * drm_mode_rmfb - remove an FB from the configuration
  * @dev: drm device for the ioctl
@@ -3310,14 +3490,29 @@ int drm_mode_rmfb(struct drm_device *dev,
        if (!found)
                goto fail_lookup;
 
-       /* Mark fb as reaped, we still have a ref from fpriv->fbs. */
-       __drm_framebuffer_unregister(dev, fb);
-
        list_del_init(&fb->filp_head);
        mutex_unlock(&dev->mode_config.fb_lock);
        mutex_unlock(&file_priv->fbs_lock);
 
-       drm_framebuffer_remove(fb);
+       /*
+        * we now own the reference that was stored in the fbs list
+        *
+        * drm_framebuffer_remove may fail with -EINTR on pending signals,
+        * so run this in a separate stack as there's no way to correctly
+        * handle this after the fb is already removed from the lookup table.
+        */
+       if (atomic_read(&fb->refcount.refcount) > 1) {
+               struct drm_mode_rmfb_work arg;
+
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+               INIT_LIST_HEAD(&arg.fbs);
+               list_add_tail(&fb->filp_head, &arg.fbs);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
+       } else
+               drm_framebuffer_unreference(fb);
 
        return 0;
 
@@ -3470,7 +3665,6 @@ out_err1:
        return ret;
 }
 
-
 /**
  * drm_fb_release - remove and free the FBs on this file
  * @priv: drm file for the ioctl
@@ -3484,8 +3678,10 @@ out_err1:
  */
 void drm_fb_release(struct drm_file *priv)
 {
-       struct drm_device *dev = priv->minor->dev;
        struct drm_framebuffer *fb, *tfb;
+       struct drm_mode_rmfb_work arg;
+
+       INIT_LIST_HEAD(&arg.fbs);
 
        /*
         * When the file gets released that means no one else can access the fb
@@ -3498,16 +3694,22 @@ void drm_fb_release(struct drm_file *priv)
         * at it any more.
         */
        list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+               if (atomic_read(&fb->refcount.refcount) > 1) {
+                       list_move_tail(&fb->filp_head, &arg.fbs);
+               } else {
+                       list_del_init(&fb->filp_head);
 
-               mutex_lock(&dev->mode_config.fb_lock);
-               /* Mark fb as reaped, we still have a ref from fpriv->fbs. */
-               __drm_framebuffer_unregister(dev, fb);
-               mutex_unlock(&dev->mode_config.fb_lock);
+                       /* This drops the fpriv->fbs reference. */
+                       drm_framebuffer_unreference(fb);
+               }
+       }
 
-               list_del_init(&fb->filp_head);
+       if (!list_empty(&arg.fbs)) {
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
 
-               /* This will also drop the fpriv->fbs reference. */
-               drm_framebuffer_remove(fb);
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
        }
 }
 
@@ -5181,7 +5383,14 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
                goto out;
        }
 
-       ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
+       if (crtc->state) {
+               const struct drm_plane_state *state = crtc->primary->state;
+
+               ret = check_src_coords(state->src_x, state->src_y,
+                                      state->src_w, state->src_h, fb);
+       } else {
+               ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
+       }
        if (ret)
                goto out;
 
@@ -5629,7 +5838,8 @@ unsigned int drm_rotation_simplify(unsigned int rotation,
 {
        if (rotation & ~supported_rotations) {
                rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y);
-               rotation = (rotation & ~0xf) | BIT((ffs(rotation & 0xf) + 1) % 4);
+               rotation = (rotation & DRM_REFLECT_MASK) |
+                          BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4);
        }
 
        return rotation;
@@ -5732,7 +5942,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
         */
        WARN_ON(!list_empty(&dev->mode_config.fb_list));
        list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
-               drm_framebuffer_remove(fb);
+               drm_framebuffer_free(&fb->refcount);
        }
 
        list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,