X-Git-Url: http://plrg.eecs.uci.edu/git/?p=firefly-linux-kernel-4.4.55.git;a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Fdrm_crtc.c;h=7febc7dc6e68ddd724706a5632296d8b42560848;hp=8328e7059205d266a456710613628c64b097f787;hb=234718be61d6b0dbf94ac977af23e61ea3e5bfb2;hpb=f6f18a261c1748973e9aac529ed81bb4abd9f120 diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8328e7059205..7febc7dc6e68 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -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,