Another month, another massive atomic patchset.
I managed to clean up warts left over from the modeset-rework rebase, but other than that I haven't really found the time to touch this too much since the last time I posted patches from this set.
Seeing as my schedule isn't getting any less busy in the forseeable future, it would be nice to get this monster merged sooner rather than never.
Ever since the code started to resemble something sane, I've tried to avoid squashing patches, just in case someone was actually trying to follow what's changed. But clearly some of the patches can be squashed, and that would probably allow some of the earlier ones to be dropped entirely.
What's clearly needed is a test tool that will stresss the mode setting side of the code more. My glplane test just changes the modes in the beginning, and then just does flips and other plane stuff. I'll try to get something done on that front before Christmas.
Repo is here: https://gitorious.org/vsyrjala/linux/commits/drm_atomic_23
The repo also contains 64bit get_user() implementation for x86-32, which I sent to lkml and x86 people for review. So this should now be testable even on a 32bit system.
From: Ville Syrjälä ville.syrjala@linux.intel.com
struct drm_region represents a two dimensional region. The utility functions are there to help driver writers.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 155 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 24 +++++++ 2 files changed, 179 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d6d0072..f64e572 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3923,3 +3923,158 @@ int drm_format_vert_chroma_subsampling(uint32_t format) } } EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); + +/** + * drm_region_adjust_size - adjust the size of the region + * @r: region to be adjusted + * @x: horizontal adjustment + * @y: vertical adjustment + * + * Change the size of region @r by @x in the horizontal direction, + * and by @y in the vertical direction, while keeping the center + * of @r stationary. + * + * Positive @x and @y increase the size, negative values decrease it. + */ +void drm_region_adjust_size(struct drm_region *r, int x, int y) +{ + r->x1 -= x >> 1; + r->y1 -= y >> 1; + r->x2 += (x + 1) >> 1; + r->y2 += (y + 1) >> 1; +} +EXPORT_SYMBOL(drm_region_adjust_size); + +/** + * drm_region_translate - translate the region + * @r: region to be tranlated + * @x: horizontal translation + * @y: vertical translation + * + * Move region @r by @x in the horizontal direction, + * and by @y in the vertical direction. + */ +void drm_region_translate(struct drm_region *r, int x, int y) +{ + r->x1 += x; + r->y1 += y; + r->x2 += x; + r->y2 += y; +} +EXPORT_SYMBOL(drm_region_translate); + +/** + * drm_region_subsample - subsample a region + * @r: region to be subsampled + * @hsub: horizontal subsampling factor + * @vsub: vertical subsampling factor + * + * Divide the coordinates of region @r by @hsub and @vsub. + */ +void drm_region_subsample(struct drm_region *r, int hsub, int vsub) +{ + r->x1 /= hsub; + r->y1 /= vsub; + r->x2 /= hsub; + r->y2 /= vsub; +} +EXPORT_SYMBOL(drm_region_subsample); + +/** + * drm_region_width - determine the region width + * @r: region whose width is returned + * + * RETURNS: + * The width of the region. + */ +int drm_region_width(const struct drm_region *r) +{ + return r->x2 - r->x1; +} +EXPORT_SYMBOL(drm_region_width); + +/** + * drm_region_height - determine the region height + * @r: region whose height is returned + * + * RETURNS: + * The height of the region. + */ +int drm_region_height(const struct drm_region *r) +{ + return r->y2 - r->y1; +} +EXPORT_SYMBOL(drm_region_height); + +/** + * drm_region_visible - determine if the the region is visible + * @r: region whose visibility is returned + * + * RETURNS: + * @true if the region is visible, @false otherwise. + */ +bool drm_region_visible(const struct drm_region *r) +{ + return drm_region_width(r) > 0 && drm_region_height(r) > 0; +} +EXPORT_SYMBOL(drm_region_visible); + +/** + * drm_region_clip - clip one region by another region + * @r: region to be clipped + * @clip: clip region + * + * Clip region @r by region @clip. + * + * RETURNS: + * @true if the region is still visible after being clipped, + * @false otherwise. + */ +bool drm_region_clip(struct drm_region *r, const struct drm_region *clip) +{ + r->x1 = max(r->x1, clip->x1); + r->y1 = max(r->y1, clip->y1); + r->x2 = min(r->x2, clip->x2); + r->y2 = min(r->y2, clip->y2); + + return drm_region_visible(r); +} +EXPORT_SYMBOL(drm_region_clip); + +/** + * drm_region_clip_scaled - perform a scaled clip operation + * @src: source window region + * @dst: destination window region + * @clip: clip region + * @hscale: horizontal scaling factor + * @vscale: vertical scaling factor + * + * Clip region @dst by region @clip. Clip region @src by the same + * amounts multiplied by @hscale and @vscale. + * + * RETUTRNS: + * @true if region @dst is still visible after being clipped, + * @false otherwise + */ +bool drm_region_clip_scaled(struct drm_region *src, struct drm_region *dst, + const struct drm_region *clip, + int hscale, int vscale) +{ + int diff; + + diff = clip->x1 - dst->x1; + if (diff > 0) + src->x1 += diff * hscale; + diff = clip->y1 - dst->y1; + if (diff > 0) + src->y1 += diff * vscale; + diff = dst->x2 - clip->x2; + if (diff > 0) + src->x2 -= diff * hscale; + diff = dst->y2 - clip->y2; + if (diff > 0) + src->y2 -= diff * vscale; + + return drm_region_clip(dst, clip); +} +EXPORT_SYMBOL(drm_region_clip_scaled); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3538eda..0ac6d83 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1082,4 +1082,28 @@ extern int drm_format_plane_cpp(uint32_t format, int plane); extern int drm_format_horz_chroma_subsampling(uint32_t format); extern int drm_format_vert_chroma_subsampling(uint32_t format);
+/** + * drm_region - two dimensional region + * @x1: horizontal starting coordinate (inclusive) + * @x2: horizontal ending coordinate (exclusive) + * @y1: vertical starting coordinate (inclusive) + * @y2: vertical ending coordinate (exclusive) + */ +struct drm_region { + int x1, y1, x2, y2; +}; + +extern void drm_region_adjust_size(struct drm_region *r, int x, int y); +extern void drm_region_translate(struct drm_region *r, int x, int y); +extern void drm_region_subsample(struct drm_region *r, int hsub, int vsub); +extern int drm_region_width(const struct drm_region *r); +extern int drm_region_height(const struct drm_region *r); +extern bool drm_region_visible(const struct drm_region *r); +extern bool drm_region_clip(struct drm_region *r, + const struct drm_region *clip); +extern bool drm_region_clip_scaled(struct drm_region *src, + struct drm_region *dst, + const struct drm_region *clip, + int hscale, int vscale); + #endif /* __DRM_CRTC_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 102 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 4 ++ 2 files changed, 106 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index f64e572..8918179 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4078,3 +4078,105 @@ bool drm_region_clip_scaled(struct drm_region *src, struct drm_region *dst, return drm_region_clip(dst, clip); } EXPORT_SYMBOL(drm_region_clip_scaled); + +/** + * drm_calc_hscale - calculate the horizontal scaling factor + * @src: source window region + * @dst: destination window region + * @min_hscale: minimum allowed horizontal scaling factor + * @max_hscale: maximum allowed horizontal scaling factor + * + * Calculate the horizontal scaling factor as + * (@src width) / (@dst width). + * + * If the calculated scaling factor is below @min_hscale, + * decrease the width of region @dst to compensate. + * + * If the calculcated scaling factor is above @max_hscale, + * decrease the width of region @src to compensate. + * + * RETURNS: + * The horizontal scaling factor. + */ +int drm_calc_hscale(struct drm_region *src, struct drm_region *dst, + int min_hscale, int max_hscale) +{ + int src_w = drm_region_width(src); + int dst_w = drm_region_width(dst); + int hscale; + + if (dst_w <= 0) + return 0; + + hscale = src_w / dst_w; + + if (hscale < min_hscale) { + int max_dst_w = src_w / min_hscale; + + drm_region_adjust_size(dst, max_dst_w - dst_w, 0); + + return min_hscale; + } + + if (hscale > max_hscale) { + int max_src_w = dst_w * max_hscale; + + drm_region_adjust_size(src, max_src_w - src_w, 0); + + return max_hscale; + } + + return hscale; +} +EXPORT_SYMBOL(drm_calc_hscale); + +/** + * drm_calc_vscale - calculate the vertical scaling factor + * @src: source window region + * @dst: destination window region + * @min_vscale: minimum allowed vertical scaling factor + * @max_vscale: maximum allowed vertical scaling factor + * + * Calculate the vertical scaling factor as + * (@src height) / (@dst height). + * + * If the calculated scaling factor is below @min_vscale, + * decrease the height of region @dst to compensate. + * + * If the calculcated scaling factor is above @max_vscale, + * decrease the height of region @src to compensate. + * + * RETURNS: + * The vertical scaling factor. + */ +int drm_calc_vscale(struct drm_region *src, struct drm_region *dst, + int min_vscale, int max_vscale) +{ + int src_h = drm_region_height(src); + int dst_h = drm_region_height(dst); + int vscale; + + if (dst_h <= 0) + return 0; + + vscale = src_h / dst_h; + + if (vscale < min_vscale) { + int max_dst_h = src_h / min_vscale; + + drm_region_adjust_size(dst, 0, max_dst_h - dst_h); + + return min_vscale; + } + + if (vscale > max_vscale) { + int max_src_h = dst_h * max_vscale; + + drm_region_adjust_size(src, 0, max_src_h - src_h); + + return max_vscale; + } + + return vscale; +} +EXPORT_SYMBOL(drm_calc_vscale); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 0ac6d83..a80a346 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1105,5 +1105,9 @@ extern bool drm_region_clip_scaled(struct drm_region *src, struct drm_region *dst, const struct drm_region *clip, int hscale, int vscale); +extern int drm_calc_hscale(struct drm_region *src, struct drm_region *dst, + int min_hscale, int max_hscale); +extern int drm_calc_vscale(struct drm_region *src, struct drm_region *dst, + int min_vscale, int max_vscale);
#endif /* __DRM_CRTC_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
If the update_plane() operation succeeds, make a copy of the requested src and crtc coordinates, so that the the plane may be reclipped if the display mode changed later.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 8 ++++++++ include/drm/drm_crtc.h | 4 ++++ 2 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 8918179..3164131 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1852,6 +1852,14 @@ int drm_mode_setplane(struct drm_device *dev, void *data, if (!ret) { plane->crtc = crtc; plane->fb = fb; + plane->crtc_x = plane_req->crtc_x; + plane->crtc_y = plane_req->crtc_y; + plane->crtc_w = plane_req->crtc_w; + plane->crtc_h = plane_req->crtc_h; + plane->src_x = plane_req->src_x; + plane->src_y = plane_req->src_y; + plane->src_w = plane_req->src_w; + plane->src_h = plane_req->src_h; }
out: diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index a80a346..6b7d809 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -672,6 +672,10 @@ struct drm_plane { void *helper_private;
struct drm_object_properties properties; + + uint32_t src_x, src_y, src_w, src_h; + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; };
/**
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add an optional driver specific restore_fbdev_mode() hook to drm_fb_helper. If the driver doesn't provide the hook, drm_fb_helper_restore_fbdev_mode() is called directly as before.
In this hook the driver can disable additional planes, cursors etc. that shouldn't be visible while fbdev is in control.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_fb_helper.c | 5 ++++- include/drm/drm_fb_helper.h | 1 + 2 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 05e623a..6902097 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -265,7 +265,10 @@ static bool drm_fb_helper_force_kernel_mode(void) if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) continue;
- ret = drm_fb_helper_restore_fbdev_mode(helper); + if (helper->funcs->restore_fbdev_mode) + ret = helper->funcs->restore_fbdev_mode(helper); + else + ret = drm_fb_helper_restore_fbdev_mode(helper); if (ret) error = true; } diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5120b01..7f76e9c 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -56,6 +56,7 @@ struct drm_fb_helper_funcs {
int (*fb_probe)(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes); + int (*restore_fbdev_mode)(struct drm_fb_helper *helper); };
struct drm_fb_helper_connector {
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 8 +++++--- include/drm/drm_crtc.h | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3164131..38b8ce3 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3112,8 +3112,8 @@ done: return ret; }
-static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, - void *data) +struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, + void *data) { struct drm_property_blob *blob; int ret; @@ -3138,14 +3138,16 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev list_add_tail(&blob->head, &dev->mode_config.property_blob_list); return blob; } +EXPORT_SYMBOL(drm_property_create_blob);
-static void drm_property_destroy_blob(struct drm_device *dev, +void drm_property_destroy_blob(struct drm_device *dev, struct drm_property_blob *blob) { drm_mode_object_put(dev, &blob->base); list_del(&blob->head); kfree(blob); } +EXPORT_SYMBOL(drm_property_destroy_blob);
int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 6b7d809..27812e0 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -970,6 +970,10 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags const char *name, uint64_t min, uint64_t max); extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); +extern struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, + int length, void *data); +extern void drm_property_destroy_blob(struct drm_device *dev, + struct drm_property_blob *blob); extern int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name); extern int drm_mode_create_dvi_i_properties(struct drm_device *dev);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Treat a range property as signed when the unsigned minimum value is larger than the unsigned maximum value.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 17 ++++++++++++++--- 1 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 38b8ce3..7badd2a 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3213,14 +3213,25 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
-static bool drm_property_change_is_valid(struct drm_property *property, +static bool range_property_is_signed(const struct drm_property *property) +{ + return property->values[0] > property->values[1]; +} + +static bool drm_property_change_is_valid(const struct drm_property *property, uint64_t value) { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; if (property->flags & DRM_MODE_PROP_RANGE) { - if (value < property->values[0] || value > property->values[1]) - return false; + if (range_property_is_signed(property)) { + if ((int64_t)value < (int64_t)property->values[0] || + (int64_t)value > (int64_t)property->values[1]) + return false; + } else { + if (value < property->values[0] || value > property->values[1]) + return false; + } return true; } else if (property->flags & DRM_MODE_PROP_BITMASK) { int i;
From: Ville Syrjälä ville.syrjala@linux.intel.com
To avoid having to pass object types from userspace for atomic mode setting ioctl, allow drm_mode_object_find() to look up an object of any type. This will only work as long as the all object types share the ID space.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 3 ++- include/drm/drm_crtc.h | 1 + 2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 7badd2a..2dd3649 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -267,7 +267,8 @@ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
mutex_lock(&dev->mode_config.idr_mutex); obj = idr_find(&dev->mode_config.crtc_idr, id); - if (!obj || (obj->type != type) || (obj->id != id)) + if (!obj || (type != DRM_MODE_OBJECT_ANY && obj->type != type) || + (obj->id != id)) obj = NULL; mutex_unlock(&dev->mode_config.idr_mutex);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 27812e0..28a972a 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -48,6 +48,7 @@ struct drm_object_properties; #define DRM_MODE_OBJECT_FB 0xfbfbfbfb #define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb #define DRM_MODE_OBJECT_PLANE 0xeeeeeeee +#define DRM_MODE_OBJECT_ANY 0
struct drm_mode_object { uint32_t id;
From: Ville Syrjälä ville.syrjala@linux.intel.com
--- drivers/gpu/drm/drm_crtc_helper.c | 5 +++-- include/drm/drm_crtc_helper.h | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 7b2d378..0d62c94 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -312,8 +312,8 @@ EXPORT_SYMBOL(drm_helper_disable_unused_functions); * * Return false if @encoder can't be driven by @crtc, true otherwise. */ -static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, - struct drm_crtc *crtc) +bool drm_encoder_crtc_ok(struct drm_encoder *encoder, + struct drm_crtc *crtc) { struct drm_device *dev; struct drm_crtc *tmp; @@ -333,6 +333,7 @@ static bool drm_encoder_crtc_ok(struct drm_encoder *encoder, return true; return false; } +EXPORT_SYMBOL(drm_encoder_crtc_ok);
/* * Check the CRTC we're going to map each output to vs. its current diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index f43d556..5abae74 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -169,4 +169,7 @@ extern void drm_kms_helper_hotplug_event(struct drm_device *dev); extern void drm_kms_helper_poll_disable(struct drm_device *dev); extern void drm_kms_helper_poll_enable(struct drm_device *dev);
+extern bool drm_encoder_crtc_ok(struct drm_encoder *encoder, + struct drm_crtc *crtc); + #endif
From: Ville Syrjälä ville.syrjala@linux.intel.com
--- drivers/gpu/drm/drm_crtc_helper.c | 3 ++- include/drm/drm_crtc_helper.h | 1 + 2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 0d62c94..30eb557 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -340,7 +340,7 @@ EXPORT_SYMBOL(drm_encoder_crtc_ok); * CRTC. If they don't match, we have to disable the output and the CRTC * since the driver will have to re-route things. */ -static void +void drm_crtc_prepare_encoders(struct drm_device *dev) { struct drm_encoder_helper_funcs *encoder_funcs; @@ -357,6 +357,7 @@ drm_crtc_prepare_encoders(struct drm_device *dev) drm_encoder_disable(encoder); } } +EXPORT_SYMBOL(drm_crtc_prepare_encoders);
/** * drm_crtc_helper_set_mode - internal helper to set a mode diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 5abae74..7702a3a 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -171,5 +171,6 @@ extern void drm_kms_helper_poll_enable(struct drm_device *dev);
extern bool drm_encoder_crtc_ok(struct drm_encoder *encoder, struct drm_crtc *crtc); +extern void drm_crtc_prepare_encoders(struct drm_device *dev);
#endif
From: Ville Syrjälä ville.syrjala@linux.intel.com
Refactor the code to check whether an object has a specific property to a new function.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 20 ++++++++++++++------ 1 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 2dd3649..0a9a7cb 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3374,6 +3374,19 @@ out: return ret; }
+static bool object_has_prop(const struct drm_mode_object *obj, u32 prop_id) +{ + int i; + + if (!obj->properties) + return false; + + for (i = 0; i < obj->properties->count; i++) + if (obj->properties->ids[i] == prop_id) + return true; + return false; +} + int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3382,7 +3395,6 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_mode_object *prop_obj; struct drm_property *property; int ret = -EINVAL; - int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -3395,11 +3407,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, if (!arg_obj->properties) goto out;
- for (i = 0; i < arg_obj->properties->count; i++) - if (arg_obj->properties->ids[i] == arg->prop_id) - break; - - if (i == arg_obj->properties->count) + if (!object_has_prop(arg_obj, arg->prop_id)) goto out;
prop_obj = drm_mode_object_find(dev, arg->prop_id,
From: Ville Syrjälä ville.syrjala@linux.intel.com
Export drm_crtc_convert_to_umode() and drm_crtc_convert_umode().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 10 ++++++---- include/drm/drm_crtc.h | 4 ++++ 2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 0a9a7cb..3709f8c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1119,8 +1119,8 @@ EXPORT_SYMBOL(drm_mode_config_cleanup); * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to * the user. */ -static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, - const struct drm_display_mode *in) +void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, + const struct drm_display_mode *in) { WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || @@ -1146,6 +1146,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); out->name[DRM_DISPLAY_MODE_LEN-1] = 0; } +EXPORT_SYMBOL(drm_crtc_convert_to_umode);
/** * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode @@ -1161,8 +1162,8 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * RETURNS: * Zero on success, errno on failure. */ -static int drm_crtc_convert_umode(struct drm_display_mode *out, - const struct drm_mode_modeinfo *in) +int drm_crtc_convert_umode(struct drm_display_mode *out, + const struct drm_mode_modeinfo *in) { if (in->clock > INT_MAX || in->vrefresh > INT_MAX) return -ERANGE; @@ -1186,6 +1187,7 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out,
return 0; } +EXPORT_SYMBOL(drm_crtc_convert_umode);
/** * drm_mode_getresources - get graphics configuration diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 28a972a..23f2f37 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1090,6 +1090,10 @@ extern int drm_format_num_planes(uint32_t format); extern int drm_format_plane_cpp(uint32_t format, int plane); extern int drm_format_horz_chroma_subsampling(uint32_t format); extern int drm_format_vert_chroma_subsampling(uint32_t format); +extern void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, + const struct drm_display_mode *in); +extern int drm_crtc_convert_umode(struct drm_display_mode *out, + const struct drm_mode_modeinfo *in);
/** * drm_region - two dimensional region
From: Ville Syrjälä ville.syrjala@linux.intel.com
When first allocated blobs can be given a maximum size for which memory is allocated. Later the data inside the blob can be replaced, assuming that the maximum size is not exceeded.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 45 ++++++++++++++++++++++++++++++++++++++----- include/drm/drm_crtc.h | 8 ++++++- 2 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3709f8c..aeda9f4 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2926,6 +2926,9 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) { struct drm_property_enum *prop_enum, *pt;
+ if (!property) + return; + list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { list_del(&prop_enum->head); kfree(prop_enum); @@ -3115,16 +3118,24 @@ done: return ret; }
-struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, - void *data) +struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, + unsigned int length, + unsigned int max_length, + const void *data) { struct drm_property_blob *blob; int ret;
- if (!length || !data) + if (!!length != !!data) return NULL;
- blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); + if (max_length < length) + max_length = length; + + if (max_length == 0) + return NULL; + + blob = kzalloc(sizeof(struct drm_property_blob)+max_length, GFP_KERNEL); if (!blob) return NULL;
@@ -3134,18 +3145,40 @@ struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int l return NULL; }
+ blob->max_length = max_length; blob->length = length;
- memcpy(blob->data, data, length); + if (length) + memcpy(blob->data, data, length);
list_add_tail(&blob->head, &dev->mode_config.property_blob_list); return blob; } EXPORT_SYMBOL(drm_property_create_blob);
+int drm_property_blob_replace_data(struct drm_property_blob *blob, + unsigned int length, const void *data) +{ + if (!!length != !!data) + return -EINVAL; + + if (length > blob->max_length) + return -ENOSPC; + + blob->length = length; + if (length) + memcpy(blob->data, data, length); + + return 0; +} +EXPORT_SYMBOL(drm_property_blob_replace_data); + void drm_property_destroy_blob(struct drm_device *dev, struct drm_property_blob *blob) { + if (!blob) + return; + drm_mode_object_put(dev, &blob->base); list_del(&blob->head); kfree(blob); @@ -3204,7 +3237,7 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
size = EDID_LENGTH * (1 + edid->extensions); connector->edid_blob_ptr = drm_property_create_blob(connector->dev, - size, edid); + size, 0, edid); if (!connector->edid_blob_ptr) return -EINVAL;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 23f2f37..5b8b1b7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -276,6 +276,7 @@ struct drm_property_blob { struct drm_mode_object base; struct list_head head; unsigned int length; + unsigned int max_length; unsigned char data[]; };
@@ -972,7 +973,12 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags uint64_t min, uint64_t max); extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property); extern struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, - int length, void *data); + unsigned int level, + unsigned int max_length, + const void *data); +extern int drm_property_blob_replace_data(struct drm_property_blob *blob, + unsigned int length, + const void *data); extern void drm_property_destroy_blob(struct drm_device *dev, struct drm_property_blob *blob); extern int drm_property_add_enum(struct drm_property *property, int index,
From: Ville Syrjälä ville.syrjala@linux.intel.com
The drm_flip mechanism can be used to implement robust page flipping support, and also to synchronize the flips on multiple hardware scanout engines (eg. CRTCs and overlays).
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_flip.c | 376 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_flip.h | 244 ++++++++++++++++++++++++++++ 3 files changed, 621 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/drm_flip.c create mode 100644 include/drm/drm_flip.h
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 6f58c81..f2965de 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -12,7 +12,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ - drm_trace_points.o drm_global.o drm_prime.o + drm_trace_points.o drm_global.o drm_prime.o drm_flip.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o diff --git a/drivers/gpu/drm/drm_flip.c b/drivers/gpu/drm/drm_flip.c new file mode 100644 index 0000000..6ccc3f8 --- /dev/null +++ b/drivers/gpu/drm/drm_flip.c @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Ville Syrjälä ville.syrjala@linux.intel.com + */ + +#include <drm/drm_flip.h> + +static void drm_flip_driver_cleanup(struct work_struct *work) +{ + struct drm_flip *flip, *next; + struct drm_flip_driver *driver = + container_of(work, struct drm_flip_driver, cleanup_work); + LIST_HEAD(list); + + spin_lock_irq(&driver->lock); + + list_cut_position(&list, + &driver->cleanup_list, + driver->cleanup_list.prev); + + spin_unlock_irq(&driver->lock); + + if (list_empty(&list)) + return; + + list_for_each_entry_safe(flip, next, &list, list) { + struct drm_flip_helper *helper = flip->helper; + + WARN_ON(!flip->finished); + + helper->funcs->cleanup(flip); + } +} + +static void drm_flip_driver_finish(struct work_struct *work) +{ + struct drm_flip *flip, *next; + struct drm_flip_driver *driver = + container_of(work, struct drm_flip_driver, finish_work); + LIST_HEAD(list); + bool need_cleanup = false; + + spin_lock_irq(&driver->lock); + + list_cut_position(&list, + &driver->finish_list, + driver->finish_list.prev); + + spin_unlock_irq(&driver->lock); + + if (list_empty(&list)) + return; + + list_for_each_entry_safe(flip, next, &list, list) { + struct drm_flip_helper *helper = flip->helper; + + helper->funcs->finish(flip); + + spin_lock_irq(&driver->lock); + + flip->finished = true; + + /* + * It's possible that drm_flip_set_scanout() was called after we + * pulled this flip from finish_list, in which case the flip + * could be in need of cleanup, but not on cleanup_list. + */ + if (flip == helper->scanout_flip) { + list_del_init(&flip->list); + } else { + need_cleanup = true; + list_move_tail(&flip->list, &driver->cleanup_list); + } + + spin_unlock_irq(&driver->lock); + } + + if (need_cleanup) + queue_work(driver->wq, &driver->cleanup_work); +} + +static bool drm_flip_set_scanout(struct drm_flip_helper *helper, + struct drm_flip *flip) +{ + struct drm_flip_driver *driver = helper->driver; + struct drm_flip *old = helper->scanout_flip; + + helper->scanout_flip = flip; + + if (old && old->finished) + list_move_tail(&old->list, &driver->cleanup_list); + + return old != NULL; +} + +static bool drm_flip_complete(struct drm_flip *flip) +{ + struct drm_flip_helper *helper = flip->helper; + struct drm_flip_driver *driver = helper->driver; + bool need_cleanup = false; + + helper->funcs->complete(flip); + + if (flip->flipped) { + if (drm_flip_set_scanout(helper, flip)) + need_cleanup = true; + } + + list_add_tail(&flip->list, &driver->finish_list); + + return need_cleanup; +} + +void drm_flip_helper_init(struct drm_flip_helper *helper, + struct drm_flip_driver *driver, + const struct drm_flip_helper_funcs *funcs) +{ + helper->pending_flip = NULL; + helper->scanout_flip = NULL; + helper->driver = driver; + helper->funcs = funcs; +} + +void drm_flip_helper_clear(struct drm_flip_helper *helper) +{ + unsigned long flags; + struct drm_flip_driver *driver = helper->driver; + struct drm_flip *pending_flip; + bool need_finish = false; + bool need_cleanup = false; + + spin_lock_irqsave(&driver->lock, flags); + + pending_flip = helper->pending_flip; + + if (pending_flip) { + BUG_ON(pending_flip->helper != helper); + + need_finish = true; + + if (drm_flip_complete(pending_flip)) + need_cleanup = true; + + helper->pending_flip = NULL; + } + + if (drm_flip_set_scanout(helper, NULL)) + need_cleanup = true; + + spin_unlock_irqrestore(&driver->lock, flags); + + if (need_finish) + queue_work(driver->wq, &driver->finish_work); + + if (need_cleanup) + queue_work(driver->wq, &driver->cleanup_work); +} + +void drm_flip_helper_fini(struct drm_flip_helper *helper) +{ + struct drm_flip_driver *driver = helper->driver; + + drm_flip_helper_clear(helper); + + flush_work_sync(&driver->finish_work); + flush_work_sync(&driver->cleanup_work); +} + +void drm_flip_helper_vblank(struct drm_flip_helper *helper) +{ + struct drm_flip_driver *driver = helper->driver; + struct drm_flip *pending_flip; + unsigned long flags; + bool need_finish = false; + bool need_cleanup = false; + + spin_lock_irqsave(&driver->lock, flags); + + pending_flip = helper->pending_flip; + + if (pending_flip) { + BUG_ON(pending_flip->helper != helper); + + if (helper->funcs->vblank(pending_flip)) + pending_flip->flipped = true; + + if (pending_flip->flipped) { + need_finish = true; + + if (drm_flip_complete(pending_flip)) + need_cleanup = true; + + helper->pending_flip = NULL; + } + } + + spin_unlock_irqrestore(&driver->lock, flags); + + if (need_finish) + queue_work(driver->wq, &driver->finish_work); + + if (need_cleanup) + queue_work(driver->wq, &driver->cleanup_work); +} + +void drm_flip_driver_init(struct drm_flip_driver *driver, + const struct drm_flip_driver_funcs *funcs) +{ + spin_lock_init(&driver->lock); + + INIT_LIST_HEAD(&driver->finish_list); + INIT_LIST_HEAD(&driver->cleanup_list); + + INIT_WORK(&driver->finish_work, drm_flip_driver_finish); + INIT_WORK(&driver->cleanup_work, drm_flip_driver_cleanup); + + driver->funcs = funcs; + + driver->wq = create_singlethread_workqueue("drm_flip"); +} + +void drm_flip_driver_fini(struct drm_flip_driver *driver) +{ + destroy_workqueue(driver->wq); + + /* All the scheduled flips should be cleaned up by now. */ + WARN_ON(!list_empty(&driver->finish_list)); + WARN_ON(!list_empty(&driver->cleanup_list)); +} + +void drm_flip_driver_schedule_flips(struct drm_flip_driver *driver, + struct list_head *flips) +{ + unsigned long flags; + struct drm_flip *flip, *next; + bool need_finish = false; + bool need_cleanup = false; + + spin_lock_irqsave(&driver->lock, flags); + + list_for_each_entry(flip, flips, list) { + struct drm_flip_helper *helper = flip->helper; + struct drm_flip *pending_flip = helper->pending_flip; + + if (helper->funcs->flip(flip, pending_flip)) + pending_flip->flipped = true; + } + + if (driver->funcs->flush) + driver->funcs->flush(driver); + + /* Complete all flips that got overridden */ + list_for_each_entry_safe(flip, next, flips, list) { + struct drm_flip_helper *helper = flip->helper; + struct drm_flip *pending_flip = helper->pending_flip; + + BUG_ON(helper->driver != driver); + + if (pending_flip) { + BUG_ON(pending_flip->helper != helper); + + need_finish = true; + + if (drm_flip_complete(pending_flip)) + need_cleanup = true; + } + + list_del_init(&flip->list); + helper->pending_flip = flip; + } + + spin_unlock_irqrestore(&driver->lock, flags); + + if (need_finish) + queue_work(driver->wq, &driver->finish_work); + + if (need_cleanup) + queue_work(driver->wq, &driver->cleanup_work); +} + +void drm_flip_driver_prepare_flips(struct drm_flip_driver *driver, + struct list_head *flips) +{ + struct drm_flip *flip; + + list_for_each_entry(flip, flips, list) { + struct drm_flip_helper *helper = flip->helper; + + if (helper->funcs->prepare) + helper->funcs->prepare(flip); + } +} + +void drm_flip_driver_complete_flips(struct drm_flip_driver *driver, + struct list_head *flips) +{ + unsigned long flags; + struct drm_flip *flip, *next; + bool need_finish = false; + bool need_cleanup = false; + + spin_lock_irqsave(&driver->lock, flags); + + /* first complete all pending flips */ + list_for_each_entry(flip, flips, list) { + struct drm_flip_helper *helper = flip->helper; + struct drm_flip *pending_flip = helper->pending_flip; + + BUG_ON(helper->driver != driver); + + if (pending_flip) { + BUG_ON(pending_flip->helper != helper); + + need_finish = true; + + if (drm_flip_complete(pending_flip)) + need_cleanup = true; + + helper->pending_flip = NULL; + } + } + + /* then complete all new flips as well */ + list_for_each_entry_safe(flip, next, flips, list) { + list_del_init(&flip->list); + + /* + * This is the flip that gets scanned out + * next time the hardware is fired up. + */ + flip->flipped = true; + + need_finish = true; + + if (drm_flip_complete(flip)) + need_cleanup = true; + } + + spin_unlock_irqrestore(&driver->lock, flags); + + if (need_finish) + queue_work(driver->wq, &driver->finish_work); + + if (need_cleanup) + queue_work(driver->wq, &driver->cleanup_work); +} + +void drm_flip_init(struct drm_flip *flip, + struct drm_flip_helper *helper) +{ + flip->helper = helper; + flip->flipped = false; + flip->finished = false; + INIT_LIST_HEAD(&flip->list); +} diff --git a/include/drm/drm_flip.h b/include/drm/drm_flip.h new file mode 100644 index 0000000..4172d6e --- /dev/null +++ b/include/drm/drm_flip.h @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and iated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Ville Syrjälä ville.syrjala@linux.intel.com + */ + +#ifndef DRM_FLIP_H +#define DRM_FLIP_H + +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> + +struct drm_flip; +struct drm_flip_helper; +struct drm_flip_driver; + +/* Driver callbacks for drm_flip_driver */ +struct drm_flip_driver_funcs { + /* + * Optional callback, called after drm_flip_driver_schedule_flips() + * has called drm_flip_helper::flip() for all the provided flips. + * Can be used to: + * - commit the flips atomically to the hardware, if the + * hardware provides some mechanism to do that. + * - flush posted writes to make sure all the flips have reached + * the hardware + * Called with drm_flip_driver::lock held. + */ + void (*flush)(struct drm_flip_driver *driver); +}; + +/* + * The driver needs one drm_flip_driver to + * coordinates the drm_flip mechanism. + */ +struct drm_flip_driver { + /* protects drm_flip_driver, drm_flip_helper, and drm_flip internals. */ + spinlock_t lock; + + /* list of drm_flips waiting to be finished, protected by 'lock' */ + struct list_head finish_list; + + /* list of drm_flips waiting to be cleaned up, protected by 'lock' */ + struct list_head cleanup_list; + + /* work used to finish the drm_flips */ + struct work_struct finish_work; + + /* work used to clean up the drm_flips */ + struct work_struct cleanup_work; + + /* driver provided callback functions */ + const struct drm_flip_driver_funcs *funcs; + + /* work queue for finish_work and cleanup_work */ + struct workqueue_struct *wq; +}; + +/* Driver callbacks for drm_flip_helper */ +struct drm_flip_helper_funcs { + /* + * Optional function to perform heavy but non-timing + * critial preparations for the flip. + * Called from drm_flip_driver_prepare_flips() with + * no extra locks being held. + */ + void (*prepare)(struct drm_flip *flip); + /* + * Instruct the hardware to flip on the next vblank. + * Must return true, iff pending_flip exists, and has + * actually flipped (ie. now being scanned out). + * Otherwise must return false. + * Called with drm_flip_driver::lock held. + */ + bool (*flip)(struct drm_flip *flip, + struct drm_flip *pending_flip); + /* + * Called from drm_flip_helper_vblank() if + * pending_flip exists. Must return true, iff + * pending_flip has actually flipped (ie. now + * being scanned out). Otherwise must return false. + * Called with drm_flip_driver::lock held. + */ + bool (*vblank)(struct drm_flip *pending_flip); + + /* + * The flip has just occured, or it got overwritten + * by a more recent flip. If the flip occured, it is + * now being scanned out, otherwise it is scheduled + * for cleanup. + * Can be called from drm_flip_driver_schedule_flips(), + * drm_flip_driver_complete_flips(), or from + * drm_flip_helper_vblank(). + * Called with drm_flip_driver::lock held. + */ + void (*complete)(struct drm_flip *flip); + + /* + * Perform finishing steps on the flip. Called from a workqueue + * soon after the flip has completed. The flip's buffer may be + * actively scanned out. + * Called with no locks being held. + */ + void (*finish)(struct drm_flip *flip); + + /* + * Perform final cleanup on the flip. Called from a workqueue + * after the flip's buffer is no longer being scanned out. + * Called with no locks being held. + */ + void (*cleanup)(struct drm_flip *flip); + +}; + +/* + * The driver needs one drm_flip_helper for each scanout engine it + * wants to operate through the drm_flip mechanism. + */ +struct drm_flip_helper { + /* drm_flip from the previous drm_flip_schedule() call */ + struct drm_flip *pending_flip; + /* drm_flip whose buffer is being scanned out */ + struct drm_flip *scanout_flip; + /* associated drm_flip_driver */ + struct drm_flip_driver *driver; + /* driver provided callback functions */ + const struct drm_flip_helper_funcs *funcs; +}; + +/* + * This structure represents a single page flip operation. + */ +struct drm_flip { + /* associated drm_flip_helper */ + struct drm_flip_helper *helper; + /* has this flip occured? */ + bool flipped; + /* has the finish work been executed for this flip? */ + bool finished; + /* used to keep this flip on various lists */ + struct list_head list; +}; + +/* + * Initialize the flip driver. + */ +void drm_flip_driver_init(struct drm_flip_driver *driver, + const struct drm_flip_driver_funcs *funcs); + +/* + * Finalize the flip driver. This will block until all the + * pending finish and cleanup work has been completed. + */ +void drm_flip_driver_fini(struct drm_flip_driver *driver); + +/* + * Initialize flip helper. + */ +void drm_flip_helper_init(struct drm_flip_helper *helper, + struct drm_flip_driver *driver, + const struct drm_flip_helper_funcs *funcs); + +/* + * Clear flip helper state. This will forcefully complete the + * helper's pending flip (if any). + */ +void drm_flip_helper_clear(struct drm_flip_helper *helper); + +/* + * Finalize the flip helper. This will forcefully complete the + * helper's pending flip (if any), and wait for the finish and + * cleanup works to finish. + */ +void drm_flip_helper_fini(struct drm_flip_helper *helper); + +/* + * Call this from the driver's vblank handler for the scanout engine + * associated with this helper. + */ +void drm_flip_helper_vblank(struct drm_flip_helper *helper); + +/* + * This will call drm_flip_helper::prepare() (if provided) for all the + * drm_flips on the list. The purpose is to perform any non-timing critical + * preparation steps for the flips before taking locks or disabling interrupts. + */ +void drm_flip_driver_prepare_flips(struct drm_flip_driver *driver, + struct list_head *flips); + +/* + * Schedule the flips on the list to occur on the next vblank. + * + * This will call drm_flip_helper::flip() for all the drm_flips on the list. + * It will then call drm_flip_driver::flush(), after which it will complete + * any pending_flip that got overridden by the new flips. + * + * Unless the hardware provides some mechanism to synchronize the flips, the + * time spent until drm_flip_driver::flush() is timing critical and the driver + * must somehow make sure it can complete the operation in a seemingly atomic + * fashion. + */ +void drm_flip_driver_schedule_flips(struct drm_flip_driver *driver, + struct list_head *flips); + +/* + * This will complete any pending_flip and also all the flips + * on the provided list (in that order). + * + * Call this instead of drm_flip_driver_schedule_flips() + * eg. if the hardware powered down, and you just want to keep + * the drm_flip mechanim's state consistent w/o waking up the + * hardware. + */ +void drm_flip_driver_complete_flips(struct drm_flip_driver *driver, + struct list_head *flips); + +/* + * Initialize the flip structure members. + */ +void drm_flip_init(struct drm_flip *flip, + struct drm_flip_helper *helper); + +#endif
From: Ville Syrjälä ville.syrjala@linux.intel.com
These will be ued by standard properties MODE and CONNECTOR_IDS.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- include/drm/drm_crtc.h | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 5b8b1b7..d05d302 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -423,6 +423,9 @@ struct drm_crtc { void *helper_private;
struct drm_object_properties properties; + + struct drm_property_blob *mode_blob; + struct drm_property_blob *connector_ids_blob; };
From: Ville Syrjälä ville.syrjala@linux.intel.com
This new ioctl can be used to update an arbitrary set of object properties in one operation.
The ioctl simply takes a list of object IDs and property IDs and their values. For setting values of blob properties, the property value indicates the length of the data, and the actual data is passed via another blob pointer.
Three flags are currently supported to alter the behaviour of the ioctl: - DRM_MODE_ATOMIC_TEST_ONLY indicates that the new state chould only be checked for validity, but not hardware state is to be modified. - DRM_MODE_ATOMIC_EVENT requests that asynchronous completion events be sent back to user space. A new event type is added for this purpose. - DRM_MODE_ATOMIC_NONBLOCK indicates that the operation must be completed asynchronously without blocking the the caller for significant amounts of time.
Internally the driver must implement the functions pointers in struct drm_atomic_funcs to support the new ioctl.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 146 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_drv.c | 1 + include/drm/drmP.h | 8 +++ include/drm/drm_crtc.h | 13 ++++ include/uapi/drm/drm.h | 12 ++++ include/uapi/drm/drm_mode.h | 17 +++++ 6 files changed, 197 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index aeda9f4..897ff46 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4245,3 +4245,149 @@ int drm_calc_vscale(struct drm_region *src, struct drm_region *dst, return vscale; } EXPORT_SYMBOL(drm_calc_vscale); + +int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv) +{ + struct drm_mode_atomic *arg = data; + uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr); + uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr); + uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); + uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr); + uint64_t __user *blob_values_ptr = (uint64_t __user *)(unsigned long)(arg->blob_values_ptr); + unsigned int copied_objs = 0; + unsigned int copied_props = 0; + unsigned int copied_blobs = 0; + void *state; + int ret = 0; + unsigned int i, j; + + if (!dev->driver->atomic_funcs || + !dev->driver->atomic_funcs->begin || + !dev->driver->atomic_funcs->set || + !dev->driver->atomic_funcs->check || + !dev->driver->atomic_funcs->commit || + !dev->driver->atomic_funcs->end) + return -ENOSYS; + + if (arg->flags & ~(DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_EVENT | DRM_MODE_ATOMIC_NONBLOCK)) + return -EINVAL; + + /* can't test and expect an event at the same time. */ + if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY && arg->flags & DRM_MODE_ATOMIC_EVENT) + return -EINVAL; + + mutex_lock(&dev->mode_config.mutex); + + state = dev->driver->atomic_funcs->begin(dev, file_priv, arg->flags, arg->user_data); + if (IS_ERR(state)) { + ret = PTR_ERR(state); + goto unlock; + } + + for (i = 0; i < arg->count_objs; i++) { + uint32_t obj_id, count_props; + struct drm_mode_object *obj; + + if (get_user(obj_id, objs_ptr + copied_objs)) { + ret = -EFAULT; + goto out; + } + + obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); + if (!obj || !obj->properties) { + ret = -ENOENT; + goto out; + } + + if (get_user(count_props, count_props_ptr + copied_objs)) { + ret = -EFAULT; + goto out; + } + + copied_objs++; + + for (j = 0; j < count_props; j++) { + uint32_t prop_id; + uint64_t prop_value; + struct drm_mode_object *prop_obj; + struct drm_property *prop; + void *blob_data = NULL; + + if (get_user(prop_id, props_ptr + copied_props)) { + ret = -EFAULT; + goto out; + } + + if (!object_has_prop(obj, prop_id)) { + ret = -EINVAL; + goto out; + } + + prop_obj = drm_mode_object_find(dev, prop_id, DRM_MODE_OBJECT_PROPERTY); + if (!prop_obj) { + ret = -ENOENT; + goto out; + } + prop = obj_to_property(prop_obj); + + if (get_user(prop_value, prop_values_ptr + copied_props)) { + ret = -EFAULT; + goto out; + } + + if (!drm_property_change_is_valid(prop, prop_value)) { + ret = -EINVAL; + goto out; + } + + if (prop->flags & DRM_MODE_PROP_BLOB && prop_value) { + uint64_t blob_ptr; + + if (get_user(blob_ptr, blob_values_ptr + copied_blobs)) { + ret = -EFAULT; + goto out; + } + + blob_data = kmalloc(prop_value, GFP_KERNEL); + if (!blob_data) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(blob_data, (void __user *)(unsigned long)blob_ptr, prop_value)) { + kfree(blob_data); + ret = -EFAULT; + goto out; + } + } + + /* User space sends the blob pointer even if we don't use it (length==0). */ + if (prop->flags & DRM_MODE_PROP_BLOB) + copied_blobs++; + + /* The driver will be in charge of blob_data from now on. */ + ret = dev->driver->atomic_funcs->set(dev, state, obj, prop, prop_value, blob_data); + if (ret) + goto out; + + copied_props++; + } + } + + ret = dev->driver->atomic_funcs->check(dev, state); + if (ret) + goto out; + + if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) + goto out; + + ret = dev->driver->atomic_funcs->commit(dev, state); + + out: + dev->driver->atomic_funcs->end(dev, state); + unlock: + mutex_unlock(&dev->mode_config.mutex); + + return ret; +} diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index be174ca..dbef305 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -166,6 +166,7 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), };
#define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fad21c9..1773812 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -958,6 +958,8 @@ struct drm_driver {
/* List of devices hanging off this driver */ struct list_head device_list; + + const struct drm_atomic_funcs *atomic_funcs; };
#define DRM_MINOR_UNASSIGNED 0 @@ -1051,6 +1053,12 @@ struct drm_pending_vblank_event { struct drm_event_vblank event; };
+struct drm_pending_atomic_event { + struct drm_pending_event base; + int pipe; + struct drm_event_atomic event; +}; + /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index d05d302..77f08a3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1092,6 +1092,8 @@ extern int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int drm_mode_atomic_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv);
extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp); @@ -1132,4 +1134,15 @@ extern int drm_calc_hscale(struct drm_region *src, struct drm_region *dst, extern int drm_calc_vscale(struct drm_region *src, struct drm_region *dst, int min_vscale, int max_vscale);
+struct drm_atomic_funcs { + void *(*begin)(struct drm_device *dev, struct drm_file *file, + uint32_t flags, uint64_t user_data); + int (*set)(struct drm_device *dev, void *state, + struct drm_mode_object *obj, struct drm_property *prop, + uint64_t value, void *blob_data); + int (*check)(struct drm_device *dev, void *state); + int (*commit)(struct drm_device *dev, void *state); + void (*end)(struct drm_device *dev, void *state); +}; + #endif /* __DRM_CRTC_H__ */ diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 8d1e2bb..7b057ca 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -732,6 +732,7 @@ struct drm_prime_handle { #define DRM_IOCTL_MODE_ADDFB2 DRM_IOWR(0xB8, struct drm_mode_fb_cmd2) #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES DRM_IOWR(0xB9, struct drm_mode_obj_get_properties) #define DRM_IOCTL_MODE_OBJ_SETPROPERTY DRM_IOWR(0xBA, struct drm_mode_obj_set_property) +#define DRM_IOCTL_MODE_ATOMIC DRM_IOWR(0xBB, struct drm_mode_atomic)
/** * Device specific ioctls should only be in their respective headers @@ -763,6 +764,7 @@ struct drm_event {
#define DRM_EVENT_VBLANK 0x01 #define DRM_EVENT_FLIP_COMPLETE 0x02 +#define DRM_EVENT_ATOMIC_COMPLETE 0x03
struct drm_event_vblank { struct drm_event base; @@ -773,6 +775,16 @@ struct drm_event_vblank { __u32 reserved; };
+struct drm_event_atomic { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 obj_id; + __u32 old_fb_id; +}; + #define DRM_CAP_DUMB_BUFFER 0x1 #define DRM_CAP_VBLANK_HIGH_CRTC 0x2 #define DRM_CAP_DUMB_PREFERRED_DEPTH 0x3 diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 3d6301b..71f5e0d 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -460,4 +460,21 @@ struct drm_mode_destroy_dumb { uint32_t handle; };
+ +#define DRM_MODE_ATOMIC_TEST_ONLY (1<<0) +#define DRM_MODE_ATOMIC_EVENT (1<<1) +#define DRM_MODE_ATOMIC_NONBLOCK (1<<2) + +/* FIXME come up with some sane error reporting mechanism? */ +struct drm_mode_atomic { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 blob_values_ptr; + __u64 user_data; +}; + #endif
From: Ville Syrjälä ville.syrjala@linux.intel.com
bits_per_pixel may not be populated for all pixel formats, so let's just use drm_format_plane_cpp().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 21dd4db..a4e71e5 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1991,6 +1991,7 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, unsigned long linear_offset; u32 dspcntr; u32 reg; + unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
switch (plane) { case 0: @@ -2049,13 +2050,12 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
I915_WRITE(reg, dspcntr);
- linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * cpp;
if (INTEL_INFO(dev)->gen >= 4) { intel_crtc->dspaddr_offset = intel_gen4_compute_offset_xtiled(&x, &y, - fb->bits_per_pixel / 8, - fb->pitches[0]); + cpp, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; } else { intel_crtc->dspaddr_offset = linear_offset; @@ -2088,6 +2088,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc, unsigned long linear_offset; u32 dspcntr; u32 reg; + unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
switch (plane) { case 0: @@ -2144,11 +2145,10 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
I915_WRITE(reg, dspcntr);
- linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * cpp; intel_crtc->dspaddr_offset = intel_gen4_compute_offset_xtiled(&x, &y, - fb->bits_per_pixel / 8, - fb->pitches[0]); + cpp, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset;
DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n",
From: Ville Syrjälä ville.syrjala@linux.intel.com
Properly clip the source when the destination gets clipped by the pipe dimensions.
Sadly the video sprite hardware is rather limited so it can't do proper sub-pixel postitioning. Resort to a best effort approach, where the source coordinates are rounded to the nearest (macro)pixel boundary.
Also do some additional checking against various hardware limits.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_sprite.c | 171 +++++++++++++++++++++++------------ 1 files changed, 111 insertions(+), 60 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 827dcd4..77ca0da 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -120,11 +120,10 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x);
- linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; sprsurf_offset = intel_gen4_compute_offset_xtiled(&x, &y, - fb->bits_per_pixel / 8, - fb->pitches[0]); + pixel_size, fb->pitches[0]); linear_offset -= sprsurf_offset;
/* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET @@ -286,11 +285,10 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
- linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + linear_offset = y * fb->pitches[0] + x * pixel_size; dvssurf_offset = intel_gen4_compute_offset_xtiled(&x, &y, - fb->bits_per_pixel / 8, - fb->pitches[0]); + pixel_size, fb->pitches[0]); linear_offset -= dvssurf_offset;
if (obj->tiling_mode != I915_TILING_NONE) @@ -409,6 +407,20 @@ ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) key->flags = I915_SET_COLORKEY_NONE; }
+static bool +format_is_yuv(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YVYU: + return true; + default: + return false; + } +} + static int intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, @@ -420,35 +432,68 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane = to_intel_plane(plane); - struct intel_framebuffer *intel_fb; - struct drm_i915_gem_object *obj, *old_obj; + struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + struct drm_i915_gem_object *obj = intel_fb->obj; + struct drm_i915_gem_object *old_obj = intel_plane->obj; int pipe = intel_plane->pipe; enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); int ret = 0; - int x = src_x >> 16, y = src_y >> 16; int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay; bool disable_primary = false; - - intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; - - old_obj = intel_plane->obj; - - src_w = src_w >> 16; - src_h = src_h >> 16; - - /* Pipe must be running... */ - if (!(I915_READ(PIPECONF(cpu_transcoder)) & PIPECONF_ENABLE)) - return -EINVAL; - - if (crtc_x >= primary_w || crtc_y >= primary_h) - return -EINVAL; + bool visible; + int hscale, vscale; + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + struct drm_region src = { + .x1 = src_x, + .x2 = src_x + src_w, + .y1 = src_y, + .y2 = src_y + src_h, + }; + struct drm_region dst = { + .x1 = crtc_x, + .x2 = crtc_x + crtc_w, + .y1 = crtc_y, + .y2 = crtc_y + crtc_h, + }; + const struct drm_region clip = { + .x2 = crtc->mode.hdisplay, + .y2 = crtc->mode.vdisplay, + };
/* Don't modify another pipe's plane */ if (intel_plane->pipe != intel_crtc->pipe) return -EINVAL;
+ if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) + return -EINVAL; + + /* + * We may not have a scaler, eg. HSW does not have it any more + */ + if (!intel_plane->can_scale && (((uint64_t) crtc_w << 16) != src_w || + ((uint64_t) crtc_h << 16) != src_h)) + return -EINVAL; + + /* + * FIXME the following code does a bunch of fuzzy adjustments to the + * coordinates and sizes. We probably need some way to decide whether + * strict checking should be done instead. + */ + hscale = drm_calc_hscale(&src, &dst, 1, intel_plane->max_downscale << 16); + vscale = drm_calc_vscale(&src, &dst, 1, intel_plane->max_downscale << 16); + + visible = drm_region_clip_scaled(&src, &dst, &clip, hscale, vscale); + + crtc_x = dst.x1; + crtc_y = dst.y1; + crtc_w = drm_region_width(&dst); + crtc_h = drm_region_height(&dst); + + /* HW doesn't seem to like smaller sprite, even when scaling */ + if (crtc_w < 3 || crtc_h < 3) + visible = false; + /* Sprite planes can be linear or x-tiled surfaces */ switch (obj->tiling_mode) { case I915_TILING_NONE: @@ -459,43 +504,47 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, }
/* - * Clamp the width & height into the visible area. Note we don't - * try to scale the source if part of the visible region is offscreen. - * The caller must handle that by adjusting source offset and size. + * Hardware doesn't handle subpixel coordinates. + * Round to nearest (macro)pixel boundary. */ - if ((crtc_x < 0) && ((crtc_x + crtc_w) > 0)) { - crtc_w += crtc_x; - crtc_x = 0; + if (format_is_yuv(fb->pixel_format)) { + src_x = ((src.x1 + 0x10000) >> 17) << 1; + src_w = (((src.x2 + 0x10000) >> 17) << 1) - src_x; + } else { + src_x = (src.x1 + 0x8000) >> 16; + src_w = ((src.x2 + 0x8000) >> 16) - src_x; } - if ((crtc_x + crtc_w) <= 0) /* Nothing to display */ - goto out; - if ((crtc_x + crtc_w) > primary_w) - crtc_w = primary_w - crtc_x; + src_y = (src.y1 + 0x8000) >> 16; + src_h = ((src.y2 + 0x8000) >> 16) - src_y;
- if ((crtc_y < 0) && ((crtc_y + crtc_h) > 0)) { - crtc_h += crtc_y; - crtc_y = 0; - } - if ((crtc_y + crtc_h) <= 0) /* Nothing to display */ - goto out; - if (crtc_y + crtc_h > primary_h) - crtc_h = primary_h - crtc_y; + /* Account for minimum source size when scaling */ + if (visible && (src_w != crtc_w || src_h != crtc_h)) { + unsigned int min_w = format_is_yuv(fb->pixel_format) ? 4 : 3;
- if (!crtc_w || !crtc_h) /* Again, nothing to display */ - goto out; + if (src_w < min_w) { + src_w = min_w; + if (src_x > fb->width - src_w) + src_x = fb->width - src_w; + }
- /* - * We may not have a scaler, eg. HSW does not have it any more - */ - if (!intel_plane->can_scale && (crtc_w != src_w || crtc_h != src_h)) - return -EINVAL; + /* FIXME interlacing */ + if (src_h < 3) { + src_h = 3; + if (src_y > fb->height - src_h) + src_y = fb->height - src_h; + } + }
- /* - * We can take a larger source and scale it down, but - * only so much... 16x is the max on SNB. - */ - if (((src_w * src_h) / (crtc_w * crtc_h)) > intel_plane->max_downscale) - return -EINVAL; + /* Check size restrictions when scaling */ + if (visible && (src_w != crtc_w || src_h != crtc_h)) { + if (src_w > 2048 || src_h > 2048 || + src_w * cpp > 4096 - 64 || fb->pitches[0] > 4096) + return -EINVAL; + } + + /* Pipe must be running... */ + if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE)) + return 0;
/* * If the sprite is completely covering the primary plane, @@ -520,11 +569,14 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (!disable_primary) intel_enable_primary(crtc);
- intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y, - crtc_w, crtc_h, x, y, src_w, src_h); + if (visible) { + intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y, + crtc_w, crtc_h, src_x, src_y, src_w, src_h);
- if (disable_primary) - intel_disable_primary(crtc); + if (disable_primary) + intel_disable_primary(crtc); + } else + intel_plane->disable_plane(plane);
/* Unpin old obj after new one is active to avoid ugliness */ if (old_obj) { @@ -544,7 +596,6 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
out_unlock: mutex_unlock(&dev->struct_mutex); -out: return ret; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
Convert intel_fb_restore_mode to be useable as the drm_fb_helper.restore_fbdev_mode hook. This will cause all planes to be disabled when swithing back to fbcon.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_dma.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 2 +- drivers/gpu/drm/i915/intel_fb.c | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 6fec5d4..392c9a1 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1790,7 +1790,7 @@ void i915_driver_lastclose(struct drm_device * dev) return;
if (drm_core_check_feature(dev, DRIVER_MODESET)) { - intel_fb_restore_mode(dev); + intel_fb_restore_mode(&dev_priv->fbdev->helper); vga_switcheroo_process_delayed_switch(); return; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6724be8..16770cb 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -606,7 +606,7 @@ extern int intel_overlay_attrs(struct drm_device *dev, void *data, struct drm_file *file_priv);
extern void intel_fb_output_poll_changed(struct drm_device *dev); -extern void intel_fb_restore_mode(struct drm_device *dev); +extern int intel_fb_restore_mode(struct drm_fb_helper *helper);
extern void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state); diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index b7773e5..3ab7967 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -195,6 +195,7 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = { .gamma_set = intel_crtc_fb_gamma_set, .gamma_get = intel_crtc_fb_gamma_get, .fb_probe = intel_fb_find_or_create_single, + .restore_fbdev_mode = intel_fb_restore_mode, };
static void intel_fbdev_destroy(struct drm_device *dev, @@ -275,22 +276,27 @@ void intel_fb_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); }
-void intel_fb_restore_mode(struct drm_device *dev) +int intel_fb_restore_mode(struct drm_fb_helper *helper) { + struct drm_device *dev = helper->dev; int ret; - drm_i915_private_t *dev_priv = dev->dev_private; struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane;
mutex_lock(&dev->mode_config.mutex);
- ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper); - if (ret) + ret = drm_fb_helper_restore_fbdev_mode(helper); + if (ret) { DRM_DEBUG("failed to restore crtc mode\n"); + goto out; + }
/* Be sure to shut off any planes that may be active */ list_for_each_entry(plane, &config->plane_list, head) plane->funcs->disable_plane(plane);
+out: mutex_unlock(&dev->mode_config.mutex); + + return ret; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
Split the update_plane() codepath into two separate steps. The first step checkis and clips the plane, and the second step actually commits the changes to the hardware. This allows the atomic modesetting code to perform all checks before clobering hardware state.
The update_plane() hook is reduced to a thin wrapper calling both check and commit functions.
Buffer (un)pinning is still being performed in the commit step. This needs to be changed as well, so that the atomic modesetting code can try to pin all new buffers before touching the hardware.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_drv.h | 15 +- drivers/gpu/drm/i915/intel_sprite.c | 411 ++++++++++++++++++++++------------- 2 files changed, 266 insertions(+), 160 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 16770cb..0d7c2fc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -231,6 +231,15 @@ struct intel_crtc { uint32_t ddi_pll_sel; };
+struct intel_plane_coords { + /* disabled or fully clipped? */ + bool visible; + /* coordinates clipped against pipe dimensions */ + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + uint32_t src_x, src_y, src_w, src_h; +}; + struct intel_plane { struct drm_plane base; enum pipe pipe; @@ -240,11 +249,7 @@ struct intel_plane { u32 lut_r[1024], lut_g[1024], lut_b[1024]; void (*update_plane)(struct drm_plane *plane, struct drm_framebuffer *fb, - struct drm_i915_gem_object *obj, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t x, uint32_t y, - uint32_t src_w, uint32_t src_h); + const struct intel_plane_coords *clip); void (*disable_plane)(struct drm_plane *plane); int (*update_colorkey)(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key); diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 77ca0da..d64cefd 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -36,16 +36,189 @@ #include <drm/i915_drm.h> #include "i915_drv.h"
+static bool +format_is_yuv(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_VYUY: + case DRM_FORMAT_YVYU: + return true; + default: + return false; + } +} + +static void intel_clip_plane(const struct drm_plane *plane, + const struct drm_crtc *crtc, + const struct drm_framebuffer *fb, + struct intel_plane_coords *coords) +{ + const struct intel_plane *intel_plane = to_intel_plane(plane); + const struct drm_display_mode *mode = &crtc->mode; + int hscale, vscale; + struct drm_region src = { + .x1 = coords->src_x, + .x2 = coords->src_x + coords->src_w, + .y1 = coords->src_y, + .y2 = coords->src_y + coords->src_h, + }; + struct drm_region dst = { + .x1 = coords->crtc_x, + .x2 = coords->crtc_x + coords->crtc_w, + .y1 = coords->crtc_y, + .y2 = coords->crtc_y + coords->crtc_h, + }; + const struct drm_region clip = { + .x2 = mode->hdisplay, + .y2 = mode->vdisplay, + }; + + hscale = drm_calc_hscale(&src, &dst, 1, intel_plane->max_downscale << 16); + vscale = drm_calc_vscale(&src, &dst, 1, intel_plane->max_downscale << 16); + + coords->visible = drm_region_clip_scaled(&src, &dst, &clip, hscale, vscale); + + coords->crtc_x = dst.x1; + coords->crtc_y = dst.y1; + coords->crtc_w = drm_region_width(&dst); + coords->crtc_h = drm_region_height(&dst); + + /* HW doesn't seem to like smaller sprite, even when scaling */ + /* FIXME return an error instead? */ + if (coords->crtc_w < 3 || coords->crtc_h < 3) + coords->visible = false; + + /* + * Hardware doesn't handle subpixel coordinates. + * Round to nearest (macro)pixel boundary. + */ + if (format_is_yuv(fb->pixel_format)) { + coords->src_x = ((src.x1 + 0x10000) >> 17) << 1; + coords->src_w = (((src.x2 + 0x10000) >> 17) << 1) - coords->src_x; + } else { + coords->src_x = (src.x1 + 0x8000) >> 16; + coords->src_w = ((src.x2 + 0x8000) >> 16) - coords->src_x; + } + coords->src_y = (src.y1 + 0x8000) >> 16; + coords->src_h = ((src.y2 + 0x8000) >> 16) - coords->src_y; + + /* Account for minimum source size when scaling */ + if (coords->visible && (coords->src_w != coords->crtc_w || coords->src_h != coords->crtc_h)) { + unsigned int min_w = format_is_yuv(fb->pixel_format) ? 4 : 3; + + if (coords->src_w < min_w) { + coords->src_w = min_w; + if (coords->src_x > fb->width - coords->src_w) + coords->src_x = fb->width - coords->src_w; + } + + /* FIXME interlacing */ + if (coords->src_h < 3) { + coords->src_h = 3; + if (coords->src_y > fb->height - coords->src_h) + coords->src_y = fb->height - coords->src_h; + } + } +} + +int intel_check_plane(const struct drm_plane *plane, + const struct drm_crtc *crtc, + const struct drm_framebuffer *fb, + struct intel_plane_coords *coords) +{ + const struct intel_plane *intel_plane = to_intel_plane(plane); + + if (fb) { + /* FIXME copy-pasted. refactor common code in drm_crtc.c */ + uint32_t fb_width = fb->width << 16; + uint32_t fb_height = fb->height << 16; + int i; + + for (i = 0; i < plane->format_count; i++) { + if (plane->format_types[i] == fb->pixel_format) + break; + } + if (i == plane->format_count) + return -EINVAL; + + if (coords->src_w > fb_width || + coords->src_x > fb_width - coords->src_w || + coords->src_h > fb_height || + coords->src_y > fb_height - coords->src_h) + return -ENOSPC; + + if (coords->crtc_w > INT_MAX || + coords->crtc_x > INT_MAX - (int32_t) coords->crtc_w || + coords->crtc_h > INT_MAX || + coords->crtc_y > INT_MAX - (int32_t) coords->crtc_h) + return -ERANGE; + + if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) + return -EINVAL; + + /* Sprite planes can be linear or x-tiled surfaces */ + switch (to_intel_framebuffer(fb)->obj->tiling_mode) { + case I915_TILING_NONE: + case I915_TILING_X: + break; + default: + return -EINVAL; + } + } + + if (crtc) { + const struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + /* Don't modify another pipe's plane */ + if (intel_plane->pipe != intel_crtc->pipe) + return -EINVAL; + } + + if (!fb || !crtc || !crtc->enabled) { + coords->visible = false; + return 0; + } + + /* + * We may not have a scaler, eg. HSW does not have it any more + */ + if (!intel_plane->can_scale && (((uint64_t) coords->crtc_w << 16) != coords->src_w || + ((uint64_t) coords->crtc_h << 16) != coords->src_h)) + return -EINVAL; + + intel_clip_plane(plane, crtc, fb, coords); + + /* Check size restrictions when scaling */ + if (coords->visible && (coords->src_w != coords->crtc_w || coords->src_h != coords->crtc_h)) { + unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + + if (coords->src_w > 2048 || coords->src_h > 2048 || + coords->src_w * cpp > 4096 - 64 || fb->pitches[0] > 4096) + return -EINVAL; + } + + return 0; +} + static void -ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, - struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t x, uint32_t y, - uint32_t src_w, uint32_t src_h) +ivb_update_plane(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); + const struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + int crtc_x = coords->crtc_x; + int crtc_y = coords->crtc_y; + unsigned int crtc_w = coords->crtc_w; + unsigned int crtc_h = coords->crtc_h; + uint32_t x = coords->src_x; + uint32_t y = coords->src_y; + uint32_t src_w = coords->src_w; + uint32_t src_h = coords->src_h; int pipe = intel_plane->pipe; u32 sprctl, sprscale = 0; unsigned long sprsurf_offset, linear_offset; @@ -218,15 +391,22 @@ ivb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) }
static void -ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, - struct drm_i915_gem_object *obj, int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t x, uint32_t y, - uint32_t src_w, uint32_t src_h) +ilk_update_plane(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); + const struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + int crtc_x = coords->crtc_x; + int crtc_y = coords->crtc_y; + unsigned int crtc_w = coords->crtc_w; + unsigned int crtc_h = coords->crtc_h; + uint32_t x = coords->src_x; + uint32_t y = coords->src_y; + uint32_t src_w = coords->src_w; + uint32_t src_h = coords->src_h; int pipe = intel_plane->pipe; unsigned long dvssurf_offset, linear_offset; u32 dvscntr, dvsscale; @@ -407,151 +587,68 @@ ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) key->flags = I915_SET_COLORKEY_NONE; }
-static bool -format_is_yuv(uint32_t format) -{ - switch (format) { - case DRM_FORMAT_YUYV: - case DRM_FORMAT_UYVY: - case DRM_FORMAT_VYUY: - case DRM_FORMAT_YVYU: - return true; - default: - return false; - } -} - static int -intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) +intel_disable_plane(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct intel_plane *intel_plane = to_intel_plane(plane); + int ret = 0; + + if (plane->crtc) + intel_enable_primary(plane->crtc); + + intel_plane->disable_plane(plane); + + if (!intel_plane->obj) + goto out; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(intel_plane->obj); + intel_plane->obj = NULL; + mutex_unlock(&dev->struct_mutex); +out: + + return ret; +} + +int +intel_commit_plane(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane = to_intel_plane(plane); - struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); - struct drm_i915_gem_object *obj = intel_fb->obj; + struct intel_framebuffer *intel_fb; + struct drm_i915_gem_object *obj; struct drm_i915_gem_object *old_obj = intel_plane->obj; int pipe = intel_plane->pipe; - enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, - pipe); - int ret = 0; - int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay; + int ret; + int primary_w, primary_h; bool disable_primary = false; - bool visible; - int hscale, vscale; - int cpp = drm_format_plane_cpp(fb->pixel_format, 0); - struct drm_region src = { - .x1 = src_x, - .x2 = src_x + src_w, - .y1 = src_y, - .y2 = src_y + src_h, - }; - struct drm_region dst = { - .x1 = crtc_x, - .x2 = crtc_x + crtc_w, - .y1 = crtc_y, - .y2 = crtc_y + crtc_h, - }; - const struct drm_region clip = { - .x2 = crtc->mode.hdisplay, - .y2 = crtc->mode.vdisplay, - };
- /* Don't modify another pipe's plane */ - if (intel_plane->pipe != intel_crtc->pipe) - return -EINVAL; - - if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) - return -EINVAL; - - /* - * We may not have a scaler, eg. HSW does not have it any more - */ - if (!intel_plane->can_scale && (((uint64_t) crtc_w << 16) != src_w || - ((uint64_t) crtc_h << 16) != src_h)) - return -EINVAL; - - /* - * FIXME the following code does a bunch of fuzzy adjustments to the - * coordinates and sizes. We probably need some way to decide whether - * strict checking should be done instead. - */ - hscale = drm_calc_hscale(&src, &dst, 1, intel_plane->max_downscale << 16); - vscale = drm_calc_vscale(&src, &dst, 1, intel_plane->max_downscale << 16); - - visible = drm_region_clip_scaled(&src, &dst, &clip, hscale, vscale); - - crtc_x = dst.x1; - crtc_y = dst.y1; - crtc_w = drm_region_width(&dst); - crtc_h = drm_region_height(&dst); - - /* HW doesn't seem to like smaller sprite, even when scaling */ - if (crtc_w < 3 || crtc_h < 3) - visible = false; - - /* Sprite planes can be linear or x-tiled surfaces */ - switch (obj->tiling_mode) { - case I915_TILING_NONE: - case I915_TILING_X: - break; - default: - return -EINVAL; - } - - /* - * Hardware doesn't handle subpixel coordinates. - * Round to nearest (macro)pixel boundary. - */ - if (format_is_yuv(fb->pixel_format)) { - src_x = ((src.x1 + 0x10000) >> 17) << 1; - src_w = (((src.x2 + 0x10000) >> 17) << 1) - src_x; - } else { - src_x = (src.x1 + 0x8000) >> 16; - src_w = ((src.x2 + 0x8000) >> 16) - src_x; - } - src_y = (src.y1 + 0x8000) >> 16; - src_h = ((src.y2 + 0x8000) >> 16) - src_y; - - /* Account for minimum source size when scaling */ - if (visible && (src_w != crtc_w || src_h != crtc_h)) { - unsigned int min_w = format_is_yuv(fb->pixel_format) ? 4 : 3; - - if (src_w < min_w) { - src_w = min_w; - if (src_x > fb->width - src_w) - src_x = fb->width - src_w; - } - - /* FIXME interlacing */ - if (src_h < 3) { - src_h = 3; - if (src_y > fb->height - src_h) - src_y = fb->height - src_h; - } - } - - /* Check size restrictions when scaling */ - if (visible && (src_w != crtc_w || src_h != crtc_h)) { - if (src_w > 2048 || src_h > 2048 || - src_w * cpp > 4096 - 64 || fb->pitches[0] > 4096) - return -EINVAL; + if (!coords->visible) { + intel_disable_plane(plane); + return 0; }
+ /* FIXME this should happen anymore I suppose */ /* Pipe must be running... */ if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE)) return 0;
+ intel_fb = to_intel_framebuffer(fb); + obj = intel_fb->obj; + primary_w = crtc->mode.hdisplay; + primary_h = crtc->mode.vdisplay; + /* * If the sprite is completely covering the primary plane, * we can disable the primary and save power. */ - if ((crtc_x == 0) && (crtc_y == 0) && - (crtc_w == primary_w) && (crtc_h == primary_h)) + if ((coords->crtc_x == 0) && (coords->crtc_y == 0) && + (coords->crtc_w == primary_w) && (coords->crtc_h == primary_h)) disable_primary = true;
mutex_lock(&dev->struct_mutex); @@ -569,9 +666,8 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, if (!disable_primary) intel_enable_primary(crtc);
- if (visible) { - intel_plane->update_plane(plane, fb, obj, crtc_x, crtc_y, - crtc_w, crtc_h, src_x, src_y, src_w, src_h); + if (coords->visible) { + intel_plane->update_plane(plane, fb, coords);
if (disable_primary) intel_disable_primary(crtc); @@ -600,26 +696,31 @@ out_unlock: }
static int -intel_disable_plane(struct drm_plane *plane) +intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h) { - struct drm_device *dev = plane->dev; - struct intel_plane *intel_plane = to_intel_plane(plane); - int ret = 0; + int ret; + struct intel_plane_coords coords = { + .crtc_x = crtc_x, + .crtc_y = crtc_y, + .crtc_w = crtc_w, + .crtc_h = crtc_h, + .src_x = src_x, + .src_y = src_y, + .src_w = src_w, + .src_h = src_h, + };
- if (plane->crtc) - intel_enable_primary(plane->crtc); - intel_plane->disable_plane(plane); + ret = intel_check_plane(plane, crtc, fb, &coords); + if (ret) + return ret;
- if (!intel_plane->obj) - goto out; + intel_commit_plane(plane, crtc, fb, &coords);
- mutex_lock(&dev->struct_mutex); - intel_unpin_fb_obj(intel_plane->obj); - intel_plane->obj = NULL; - mutex_unlock(&dev->struct_mutex); -out: - - return ret; + return 0; }
static void intel_destroy_plane(struct drm_plane *plane)
From: Ville Syrjälä ville.syrjala@linux.intel.com
Split the clock stuff out.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 128 ++++++++++++++++++++-------------- 1 files changed, 75 insertions(+), 53 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a4e71e5..0fa9c2a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4544,6 +4544,76 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); }
+static bool i9xx_compute_clocks(struct drm_crtc *crtc, + struct drm_display_mode *adjusted_mode, + intel_clock_t *clock, + bool *has_reduced_clock, + intel_clock_t *reduced_clock, + int *refclk, int *num_connectors, bool *is_dp) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + const intel_limit_t *limit; + bool ret, is_sdvo = false, is_tv = false, is_lvds = false; + + *num_connectors = 0; + + for_each_encoder_on_crtc(dev, crtc, encoder) { + switch (encoder->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_SDVO: + case INTEL_OUTPUT_HDMI: + is_sdvo = true; + if (encoder->needs_tv_clock) + is_tv = true; + break; + case INTEL_OUTPUT_TVOUT: + is_tv = true; + break; + case INTEL_OUTPUT_DISPLAYPORT: + *is_dp = true; + break; + } + + (*num_connectors)++; + } + + *refclk = i9xx_get_refclk(crtc, *num_connectors); + + /* + * Returns a set of divisors for the desired target clock with the given + * refclk, or FALSE. The returned values represent the clock equation: + * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. + */ + limit = intel_limit(crtc, *refclk); + ret = limit->find_pll(limit, crtc, adjusted_mode->clock, *refclk, NULL, + clock); + if (!ret) + return false; + + if (is_lvds && dev_priv->lvds_downclock_avail) { + /* + * Ensure we match the reduced clock's P to the target clock. + * If the clocks don't match, we can't switch the display clock + * by using the FP0/FP1. In such case we will disable the LVDS + * downclock feature. + */ + *has_reduced_clock = limit->find_pll(limit, crtc, + dev_priv->lvds_downclock, + *refclk, + clock, + reduced_clock); + } + + if (is_sdvo && is_tv) + i9xx_adjust_sdvo_tv_clock(adjusted_mode, clock); + + return true; +} + static int i9xx_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -4558,44 +4628,13 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, int refclk, num_connectors = 0; intel_clock_t clock, reduced_clock; u32 dspcntr, pipeconf; - bool ok, has_reduced_clock = false, is_sdvo = false; - bool is_lvds = false, is_tv = false, is_dp = false; - struct intel_encoder *encoder; - const intel_limit_t *limit; + bool ok, has_reduced_clock = false; + bool is_dp = false; int ret;
- for_each_encoder_on_crtc(dev, crtc, encoder) { - switch (encoder->type) { - case INTEL_OUTPUT_LVDS: - is_lvds = true; - break; - case INTEL_OUTPUT_SDVO: - case INTEL_OUTPUT_HDMI: - is_sdvo = true; - if (encoder->needs_tv_clock) - is_tv = true; - break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; - case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; - break; - } - - num_connectors++; - } - - refclk = i9xx_get_refclk(crtc, num_connectors); - - /* - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - */ - limit = intel_limit(crtc, refclk); - ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL, - &clock); + ok = i9xx_compute_clocks(crtc, adjusted_mode, &clock, + &has_reduced_clock, &reduced_clock, + &refclk, &num_connectors, &is_dp); if (!ok) { DRM_ERROR("Couldn't find PLL settings for mode!\n"); return -EINVAL; @@ -4604,23 +4643,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, /* Ensure that the cursor is valid for the new mode before changing... */ intel_crtc_update_cursor(crtc, true);
- if (is_lvds && dev_priv->lvds_downclock_avail) { - /* - * Ensure we match the reduced clock's P to the target clock. - * If the clocks don't match, we can't switch the display clock - * by using the FP0/FP1. In such case we will disable the LVDS - * downclock feature. - */ - has_reduced_clock = limit->find_pll(limit, crtc, - dev_priv->lvds_downclock, - refclk, - &clock, - &reduced_clock); - } - - if (is_sdvo && is_tv) - i9xx_adjust_sdvo_tv_clock(adjusted_mode, &clock); - if (IS_GEN2(dev)) i8xx_update_pll(crtc, adjusted_mode, &clock, has_reduced_clock ? &reduced_clock : NULL,
From: Ville Syrjälä ville.syrjala@linux.intel.com
i9xx_adjust_sdvo_tv_clock(), i9xx_compute_clocks() and ironlake_compute_clocks() do not modify the adjusted_mode passed in, so pass it as const.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0fa9c2a..2a2c664 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4159,7 +4159,7 @@ static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors) return refclk; }
-static void i9xx_adjust_sdvo_tv_clock(struct drm_display_mode *adjusted_mode, +static void i9xx_adjust_sdvo_tv_clock(const struct drm_display_mode *adjusted_mode, intel_clock_t *clock) { /* SDVO TV has fixed PLL values depend on its clock range, @@ -4545,7 +4545,7 @@ static void intel_set_pipe_timings(struct intel_crtc *intel_crtc, }
static bool i9xx_compute_clocks(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode, + const struct drm_display_mode *adjusted_mode, intel_clock_t *clock, bool *has_reduced_clock, intel_clock_t *reduced_clock, @@ -5135,7 +5135,7 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc, }
static bool ironlake_compute_clocks(struct drm_crtc *crtc, - struct drm_display_mode *adjusted_mode, + const struct drm_display_mode *adjusted_mode, intel_clock_t *clock, bool *has_reduced_clock, intel_clock_t *reduced_clock)
From: Ville Syrjälä ville.syrjala@linux.intel.com
intel_check_clock() can be used to check clock validity w/o modifying hardware state.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_display.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ab0bd29..c41982e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1719,6 +1719,8 @@ extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern void intel_init_pch_refclk(struct drm_device *dev); +extern int intel_check_clock(struct drm_crtc *crtc, + const struct drm_display_mode *adjusted_mode); extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void intel_detect_pch(struct drm_device *dev); extern int intel_trans_dp_port_sel(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2a2c664..dcd61fb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5686,6 +5686,40 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, return ret; }
+int intel_check_clock(struct drm_crtc *crtc, + const struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + intel_clock_t clock, reduced_clock; + bool has_reduced_clock = false; + bool ok; + + if (IS_HASWELL(dev)) { + if (!intel_ddi_pll_mode_set(crtc, adjusted_mode->clock)) + return -EINVAL; + + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) { + ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, + &has_reduced_clock, + &reduced_clock); + } else + ok = true; + } else if (HAS_PCH_SPLIT(dev)) { + ok = ironlake_compute_clocks(crtc, adjusted_mode, &clock, + &has_reduced_clock, &reduced_clock); + } else { + int num_connectors = 0; + bool is_dp = false; + int refclk; + + ok = i9xx_compute_clocks(crtc, adjusted_mode, &clock, + &has_reduced_clock, &reduced_clock, + &refclk, &num_connectors, &is_dp); + } + + return ok ? 0 : -EINVAL; +} + static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode,
From: Ander Conselvan de Oliveira conselvan2@gmail.com
This way it is possible to check if the cursor changed without doing any setup. Will be useful for the atomic modesetting api. --- drivers/gpu/drm/i915/intel_display.c | 1 + drivers/gpu/drm/i915/intel_drv.h | 2 +- 2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dcd61fb..2a32cb0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6260,6 +6260,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex);
intel_crtc->cursor_addr = addr; + intel_crtc->cursor_handle = handle; intel_crtc->cursor_bo = obj; intel_crtc->cursor_width = width; intel_crtc->cursor_height = height; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 0d7c2fc..e77202c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -220,7 +220,7 @@ struct intel_crtc { unsigned long dspaddr_offset;
struct drm_i915_gem_object *cursor_bo; - uint32_t cursor_addr; + uint32_t cursor_addr, cursor_handle; int16_t cursor_x, cursor_y; int16_t cursor_width, cursor_height; bool cursor_visible;
From: Ander Conselvan de Oliveira conselvan2@gmail.com
The atomic mode setting API will need to pin the cursor bo without making changes to the current setup. Only on a later stage the cursor registers can be written and the previous bo released.
This patch splits intel_crtc_cursor_set() into three parts: prepare, commit and unref. intel_crtc_cursor_prepare() will pin the cursor bo and return a gem object and the address to be written to the cursor registers. intel_crtc_cursor_commit() takes that object and address and actually changes the cursor. intel_crtc_cursor_unref() is used to release the previous cursor bo. --- drivers/gpu/drm/i915/intel_display.c | 90 +++++++++++++++++++++++++-------- 1 files changed, 68 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2a32cb0..0f25a07 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6173,10 +6173,12 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, } }
-static int intel_crtc_cursor_set(struct drm_crtc *crtc, +static int intel_crtc_cursor_prepare(struct drm_crtc *crtc, struct drm_file *file, uint32_t handle, - uint32_t width, uint32_t height) + uint32_t width, uint32_t height, + struct drm_i915_gem_object **obj_ret, + uint32_t *addr_ret) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -6188,10 +6190,9 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, /* if we want to turn off the cursor ignore width and height */ if (!handle) { DRM_DEBUG_KMS("cursor off\n"); - addr = 0; - obj = NULL; - mutex_lock(&dev->struct_mutex); - goto finish; + *addr_ret = 0; + *obj_ret = NULL; + return 0; }
/* Currently we only support 64x64 cursors */ @@ -6247,17 +6248,46 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, if (IS_GEN2(dev)) I915_WRITE(CURSIZE, (height << 12) | width);
- finish: - if (intel_crtc->cursor_bo) { - if (dev_priv->info->cursor_needs_physical) { - if (intel_crtc->cursor_bo != obj) - i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo); - } else - i915_gem_object_unpin(intel_crtc->cursor_bo); - drm_gem_object_unreference(&intel_crtc->cursor_bo->base); - } + mutex_unlock(&dev->struct_mutex); + + *obj_ret = obj; + *addr_ret = addr; + + return 0; +fail_unpin: + i915_gem_object_unpin(obj); +fail_locked: + mutex_unlock(&dev->struct_mutex); +fail: + drm_gem_object_unreference_unlocked(&obj->base); + return ret; +} + +static void intel_crtc_cursor_bo_unref(struct drm_crtc *crtc, + struct drm_i915_gem_object *obj) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + mutex_lock(&dev->struct_mutex); + + if (dev_priv->info->cursor_needs_physical) { + if (obj != intel_crtc->cursor_bo) + i915_gem_detach_phys_object(dev, obj); + } else + i915_gem_object_unpin(obj); + drm_gem_object_unreference(&obj->base);
mutex_unlock(&dev->struct_mutex); +} + +static void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, + uint32_t width, uint32_t height, + struct drm_i915_gem_object *obj, + uint32_t addr) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
intel_crtc->cursor_addr = addr; intel_crtc->cursor_handle = handle; @@ -6266,15 +6296,31 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, intel_crtc->cursor_height = height;
intel_crtc_update_cursor(crtc, true); +} + +static int intel_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int ret; + struct drm_i915_gem_object *obj, *old_obj; + uint32_t addr; + + ret = intel_crtc_cursor_prepare(crtc, file, handle, width, height, + &obj, &addr); + if (ret) + return ret; + + old_obj = intel_crtc->cursor_bo; + + intel_crtc_cursor_commit(crtc, handle, width, height, obj, addr); + + if (old_obj) + intel_crtc_cursor_bo_unref(crtc, old_obj);
return 0; -fail_unpin: - i915_gem_object_unpin(obj); -fail_locked: - mutex_unlock(&dev->struct_mutex); -fail: - drm_gem_object_unreference_unlocked(&obj->base); - return ret; }
static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
From: Ander Conselvan de Oliveira conselvan2@gmail.com
--- drivers/gpu/drm/i915/intel_display.c | 24 ++++++++++++------------ drivers/gpu/drm/i915/intel_drv.h | 13 +++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0f25a07..2f72881 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6173,12 +6173,12 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, } }
-static int intel_crtc_cursor_prepare(struct drm_crtc *crtc, - struct drm_file *file, - uint32_t handle, - uint32_t width, uint32_t height, - struct drm_i915_gem_object **obj_ret, - uint32_t *addr_ret) +int intel_crtc_cursor_prepare(struct drm_crtc *crtc, + struct drm_file *file, + uint32_t handle, + uint32_t width, uint32_t height, + struct drm_i915_gem_object **obj_ret, + uint32_t *addr_ret) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -6263,8 +6263,8 @@ fail: return ret; }
-static void intel_crtc_cursor_bo_unref(struct drm_crtc *crtc, - struct drm_i915_gem_object *obj) +void intel_crtc_cursor_bo_unref(struct drm_crtc *crtc, + struct drm_i915_gem_object *obj) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -6282,10 +6282,10 @@ static void intel_crtc_cursor_bo_unref(struct drm_crtc *crtc, mutex_unlock(&dev->struct_mutex); }
-static void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, - uint32_t width, uint32_t height, - struct drm_i915_gem_object *obj, - uint32_t addr) +void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, + uint32_t width, uint32_t height, + struct drm_i915_gem_object *obj, + uint32_t addr) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e77202c..785fa04 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -679,4 +679,17 @@ extern bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector); extern void intel_ddi_fdi_disable(struct drm_crtc *crtc);
+extern int intel_crtc_cursor_prepare(struct drm_crtc *crtc, + struct drm_file *file, + uint32_t handle, + uint32_t width, uint32_t height, + struct drm_i915_gem_object **obj_ret, + uint32_t *addr_ret); +extern void intel_crtc_cursor_bo_unref(struct drm_crtc *crtc, + struct drm_i915_gem_object *obj); +extern void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, + uint32_t width, uint32_t height, + struct drm_i915_gem_object *obj, + uint32_t addr); + #endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
intel_finish_fb() will be used by the atomic modeset code, so make it non-static.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2f72881..a184b70 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2182,7 +2182,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, return dev_priv->display.update_plane(crtc, fb, x, y); }
-static int +int intel_finish_fb(struct drm_framebuffer *old_fb) { struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 785fa04..59ca0c3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -590,6 +590,7 @@ extern int intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_i915_gem_object *obj, struct intel_ring_buffer *pipelined); extern void intel_unpin_fb_obj(struct drm_i915_gem_object *obj); +extern int intel_finish_fb(struct drm_framebuffer *old_fb);
extern int intel_framebuffer_init(struct drm_device *dev, struct intel_framebuffer *ifb,
From: Ville Syrjälä ville.syrjala@linux.intel.com
intel_pipe_set_base() (un)pins the buffers, so it can't be called from the atomic modeset paths. Pull the intel_pipe_set_base() and watermark modifications out of i9xx_crtc_mode_set() and ironlake_crtc_mode_set() into intel_crtc_mode_set(), so that the former two can be used from the atomic modeset paths.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 35 ++++++++++++--------------------- 1 files changed, 13 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a184b70..6522e1e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4630,7 +4630,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, u32 dspcntr, pipeconf; bool ok, has_reduced_clock = false; bool is_dp = false; - int ret;
ok = i9xx_compute_clocks(crtc, adjusted_mode, &clock, &has_reduced_clock, &reduced_clock, @@ -4738,11 +4737,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane));
- ret = intel_pipe_set_base(crtc, x, y, fb); - - intel_update_watermarks(dev); - - return ret; + return 0; }
static void ironlake_init_pch_refclk(struct drm_device *dev) @@ -5479,7 +5474,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool ok, has_reduced_clock = false; bool is_lvds = false, is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - int ret; bool dither, fdi_config_ok;
for_each_encoder_on_crtc(dev, crtc, encoder) { @@ -5590,13 +5584,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); POSTING_READ(DSPCNTR(plane));
- ret = intel_pipe_set_base(crtc, x, y, fb); - - intel_update_watermarks(dev); - - intel_update_linetime_watermarks(dev, pipe, adjusted_mode); - - return fdi_config_ok ? ret : -EINVAL; + return fdi_config_ok ? 0 : -EINVAL; }
static int haswell_crtc_mode_set(struct drm_crtc *crtc, @@ -5613,7 +5601,6 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, int num_connectors = 0; bool is_dp = false, is_cpu_edp = false; struct intel_encoder *encoder; - int ret; bool dither;
for_each_encoder_on_crtc(dev, crtc, encoder) { @@ -5677,13 +5664,7 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), DISPPLANE_GAMMA_ENABLE); POSTING_READ(DSPCNTR(plane));
- ret = intel_pipe_set_base(crtc, x, y, fb); - - intel_update_watermarks(dev); - - intel_update_linetime_watermarks(dev, pipe, adjusted_mode); - - return ret; + return 0; }
int intel_check_clock(struct drm_crtc *crtc, @@ -5738,6 +5719,16 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, x, y, fb); + + if (!ret) { + ret = intel_pipe_set_base(crtc, x, y, fb); + + intel_update_watermarks(dev); + + if (HAS_PCH_SPLIT(dev)) + intel_update_linetime_watermarks(dev, pipe, adjusted_mode); + } + drm_vblank_post_modeset(dev, pipe);
if (ret != 0)
From: Ville Syrjälä ville.syrjala@linux.intel.com
Make intel_crtc_update_sarea() available for the atomic mode setting code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_drv.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6522e1e..9918dcb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3652,8 +3652,8 @@ static void i9xx_crtc_off(struct drm_crtc *crtc) { }
-static void intel_crtc_update_sarea(struct drm_crtc *crtc, - bool enabled) +void intel_crtc_update_sarea(struct drm_crtc *crtc, + bool enabled) { struct drm_device *dev = crtc->dev; struct drm_i915_master_private *master_priv; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 59ca0c3..c1fee22 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -693,4 +693,6 @@ extern void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, struct drm_i915_gem_object *obj, uint32_t addr);
+extern void intel_crtc_update_sarea(struct drm_crtc *crtc, bool enabled); + #endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
Make intel_crtc_update_sarea_pos() available to the atomic mode setting code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9918dcb..f7190d2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2209,7 +2209,7 @@ intel_finish_fb(struct drm_framebuffer *old_fb) return ret; }
-static void intel_crtc_update_sarea_pos(struct drm_crtc *crtc, int x, int y) +void intel_crtc_update_sarea_pos(struct drm_crtc *crtc, int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_master_private *master_priv; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c1fee22..a7d8df8 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -694,5 +694,6 @@ extern void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, uint32_t addr);
extern void intel_crtc_update_sarea(struct drm_crtc *crtc, bool enabled); +extern void intel_crtc_update_sarea_pos(struct drm_crtc *crtc, int x, int y);
#endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
intel_modeset_adjusted_mode() doesn't modify the passed display mode. So pass it as const.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f7190d2..dac80c4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7401,7 +7401,7 @@ static void intel_modeset_commit_output_state(struct drm_device *dev)
static struct drm_display_mode * intel_modeset_adjusted_mode(struct drm_crtc *crtc, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct drm_device *dev = crtc->dev; struct drm_display_mode *adjusted_mode;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Make intel_crtc_mode_fixup() available for the upcoming atomic modesetting code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 6 +++--- drivers/gpu/drm/i915/intel_drv.h | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dac80c4..2c2fcda 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3851,9 +3851,9 @@ bool intel_connector_get_hw_state(struct intel_connector *connector) return encoder->get_hw_state(encoder, &pipe); }
-static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +bool intel_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a7d8df8..ee6c85f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -696,4 +696,8 @@ extern void intel_crtc_cursor_commit(struct drm_crtc *crtc, uint32_t handle, extern void intel_crtc_update_sarea(struct drm_crtc *crtc, bool enabled); extern void intel_crtc_update_sarea_pos(struct drm_crtc *crtc, int x, int y);
+extern bool intel_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + #endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
intel_plane_regs can be used to shadow all the typical plane registers.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_drv.h | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ee6c85f..b11510a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -194,6 +194,20 @@ struct intel_connector { struct edid *edid; };
+struct intel_plane_regs { + u32 cntr; + u32 linoff; + u32 stride; + u32 pos; + u32 size; + u32 keyval; + u32 keymsk; + u32 surf; + u32 keymaxval; + u32 tileoff; + u32 scale; +}; + struct intel_crtc { struct drm_crtc base; enum pipe pipe;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Separate the part that calculates the register values from the part that writes the registers. This will be useful in the atomic page flip code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 3 + drivers/gpu/drm/i915/intel_display.c | 154 ++++++++++++++++++++++------------ drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 105 insertions(+), 53 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c41982e..66b3b64 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -297,6 +297,9 @@ struct drm_i915_display_funcs { struct drm_i915_gem_object *obj); int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); + int (*calc_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y); + void (*commit_plane)(struct drm_crtc *crtc); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2c2fcda..c0db749 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1979,8 +1979,30 @@ unsigned long intel_gen4_compute_offset_xtiled(int *x, int *y, return tile_rows * pitch * 8 + tiles * 4096; }
-static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y) +static void intel_commit_plane(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int plane = intel_crtc->plane; + const struct intel_plane_regs *regs = &intel_crtc->primary_regs; + + I915_WRITE(DSPCNTR(plane), regs->cntr); + I915_WRITE(DSPSTRIDE(plane), regs->stride); + + if (IS_HASWELL(dev)) { + I915_WRITE(DSPOFFSET(plane), regs->tileoff); + I915_WRITE(DSPSURF(plane), regs->surf); + } else if (INTEL_INFO(dev)->gen >= 4) { + I915_WRITE(DSPTILEOFF(plane), regs->tileoff); + I915_WRITE(DSPLINOFF(plane), regs->linoff); + I915_WRITE(DSPSURF(plane), regs->surf); + } else + I915_WRITE(DSPADDR(plane), regs->linoff); +} + +static int i9xx_calc_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -1989,9 +2011,8 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_i915_gem_object *obj; int plane = intel_crtc->plane; unsigned long linear_offset; - u32 dspcntr; - u32 reg; unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + struct intel_plane_regs *regs = &intel_crtc->primary_regs;
switch (plane) { case 0: @@ -2005,36 +2026,35 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, intel_fb = to_intel_framebuffer(fb); obj = intel_fb->obj;
- reg = DSPCNTR(plane); - dspcntr = I915_READ(reg); + regs->cntr = I915_READ(DSPCNTR(plane)); /* Mask out pixel format bits in case we change it */ - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + regs->cntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (fb->pixel_format) { case DRM_FORMAT_C8: - dspcntr |= DISPPLANE_8BPP; + regs->cntr |= DISPPLANE_8BPP; break; case DRM_FORMAT_XRGB1555: case DRM_FORMAT_ARGB1555: - dspcntr |= DISPPLANE_BGRX555; + regs->cntr |= DISPPLANE_BGRX555; break; case DRM_FORMAT_RGB565: - dspcntr |= DISPPLANE_BGRX565; + regs->cntr |= DISPPLANE_BGRX565; break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: - dspcntr |= DISPPLANE_BGRX888; + regs->cntr |= DISPPLANE_BGRX888; break; case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: - dspcntr |= DISPPLANE_RGBX888; + regs->cntr |= DISPPLANE_RGBX888; break; case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_ARGB2101010: - dspcntr |= DISPPLANE_BGRX101010; + regs->cntr |= DISPPLANE_BGRX101010; break; case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: - dspcntr |= DISPPLANE_RGBX101010; + regs->cntr |= DISPPLANE_RGBX101010; break; default: DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format); @@ -2043,13 +2063,11 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
if (INTEL_INFO(dev)->gen >= 4) { if (obj->tiling_mode != I915_TILING_NONE) - dspcntr |= DISPPLANE_TILED; + regs->cntr |= DISPPLANE_TILED; else - dspcntr &= ~DISPPLANE_TILED; + regs->cntr &= ~DISPPLANE_TILED; }
- I915_WRITE(reg, dspcntr); - linear_offset = y * fb->pitches[0] + x * cpp;
if (INTEL_INFO(dev)->gen >= 4) { @@ -2063,21 +2081,37 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n", obj->gtt_offset, linear_offset, x, y, fb->pitches[0]); - I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); + regs->stride = fb->pitches[0]; if (INTEL_INFO(dev)->gen >= 4) { - I915_MODIFY_DISPBASE(DSPSURF(plane), - obj->gtt_offset + intel_crtc->dspaddr_offset); - I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); - I915_WRITE(DSPLINOFF(plane), linear_offset); + regs->surf = I915_LO_DISPBASE(I915_READ(DSPSURF(plane))) | + (obj->gtt_offset + intel_crtc->dspaddr_offset); + regs->tileoff = (y << 16) | x; + regs->linoff = linear_offset; } else - I915_WRITE(DSPADDR(plane), obj->gtt_offset + linear_offset); - POSTING_READ(reg); + regs->linoff = obj->gtt_offset + linear_offset;
return 0; }
-static int ironlake_update_plane(struct drm_crtc *crtc, - struct drm_framebuffer *fb, int x, int y) +static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + int ret; + + ret = i9xx_calc_plane(crtc, fb, x, y); + if (ret) + return ret; + + intel_commit_plane(crtc); + + POSTING_READ(DSPCNTR(to_intel_crtc(crtc)->plane)); + + return 0; +} + +static int ironlake_calc_plane(struct drm_crtc *crtc, + struct drm_framebuffer *fb, int x, int y) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -2086,9 +2120,8 @@ static int ironlake_update_plane(struct drm_crtc *crtc, struct drm_i915_gem_object *obj; int plane = intel_crtc->plane; unsigned long linear_offset; - u32 dspcntr; - u32 reg; unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + struct intel_plane_regs *regs = &intel_crtc->primary_regs;
switch (plane) { case 0: @@ -2103,32 +2136,31 @@ static int ironlake_update_plane(struct drm_crtc *crtc, intel_fb = to_intel_framebuffer(fb); obj = intel_fb->obj;
- reg = DSPCNTR(plane); - dspcntr = I915_READ(reg); + regs->cntr = I915_READ(DSPCNTR(plane)); /* Mask out pixel format bits in case we change it */ - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + regs->cntr &= ~DISPPLANE_PIXFORMAT_MASK; switch (fb->pixel_format) { case DRM_FORMAT_C8: - dspcntr |= DISPPLANE_8BPP; + regs->cntr |= DISPPLANE_8BPP; break; case DRM_FORMAT_RGB565: - dspcntr |= DISPPLANE_BGRX565; + regs->cntr |= DISPPLANE_BGRX565; break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: - dspcntr |= DISPPLANE_BGRX888; + regs->cntr |= DISPPLANE_BGRX888; break; case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ABGR8888: - dspcntr |= DISPPLANE_RGBX888; + regs->cntr |= DISPPLANE_RGBX888; break; case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_ARGB2101010: - dspcntr |= DISPPLANE_BGRX101010; + regs->cntr |= DISPPLANE_BGRX101010; break; case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_ABGR2101010: - dspcntr |= DISPPLANE_RGBX101010; + regs->cntr |= DISPPLANE_RGBX101010; break; default: DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format); @@ -2136,14 +2168,12 @@ static int ironlake_update_plane(struct drm_crtc *crtc, }
if (obj->tiling_mode != I915_TILING_NONE) - dspcntr |= DISPPLANE_TILED; + regs->cntr |= DISPPLANE_TILED; else - dspcntr &= ~DISPPLANE_TILED; + regs->cntr &= ~DISPPLANE_TILED;
/* must disable */ - dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - - I915_WRITE(reg, dspcntr); + regs->cntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
linear_offset = y * fb->pitches[0] + x * cpp; intel_crtc->dspaddr_offset = @@ -2153,16 +2183,28 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
DRM_DEBUG_KMS("Writing base %08X %08lX %d %d %d\n", obj->gtt_offset, linear_offset, x, y, fb->pitches[0]); - I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]); - I915_MODIFY_DISPBASE(DSPSURF(plane), - obj->gtt_offset + intel_crtc->dspaddr_offset); - if (IS_HASWELL(dev)) { - I915_WRITE(DSPOFFSET(plane), (y << 16) | x); - } else { - I915_WRITE(DSPTILEOFF(plane), (y << 16) | x); - I915_WRITE(DSPLINOFF(plane), linear_offset); - } - POSTING_READ(reg); + regs->stride = fb->pitches[0]; + regs->surf = I915_LO_DISPBASE(I915_READ(DSPSURF(plane))) | + (obj->gtt_offset + intel_crtc->dspaddr_offset); + regs->tileoff = (y << 16) | x; + regs->linoff = linear_offset; + + return 0; +} + +static int ironlake_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, + int x, int y) +{ + struct drm_i915_private *dev_priv = crtc->dev->dev_private; + int ret; + + ret = ironlake_calc_plane(crtc, fb, x, y); + if (ret) + return ret; + + intel_commit_plane(crtc); + + POSTING_READ(DSPCNTR(to_intel_crtc(crtc)->plane));
return 0; } @@ -8498,18 +8540,24 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.crtc_disable = haswell_crtc_disable; dev_priv->display.off = haswell_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; + dev_priv->display.calc_plane = ironlake_calc_plane; + dev_priv->display.commit_plane = intel_commit_plane; } else if (HAS_PCH_SPLIT(dev)) { dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; dev_priv->display.crtc_enable = ironlake_crtc_enable; dev_priv->display.crtc_disable = ironlake_crtc_disable; dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; + dev_priv->display.calc_plane = ironlake_calc_plane; + dev_priv->display.commit_plane = intel_commit_plane; } else { dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_enable = i9xx_crtc_enable; dev_priv->display.crtc_disable = i9xx_crtc_disable; dev_priv->display.off = i9xx_crtc_off; dev_priv->display.update_plane = i9xx_update_plane; + dev_priv->display.calc_plane = i9xx_calc_plane; + dev_priv->display.commit_plane = intel_commit_plane; }
/* Returns the core display clock speed */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index b11510a..4798f54 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -243,6 +243,7 @@ struct intel_crtc { /* We can share PLLs across outputs if the timings match */ struct intel_pch_pll *pch_pll; uint32_t ddi_pll_sel; + struct intel_plane_regs primary_regs; };
struct intel_plane_coords {
From: Ville Syrjälä ville.syrjala@linux.intel.com
Separate the part that calculates the register values from the part that writes the registers. This will be useful in the atomic page flip code. Also move the watermark magic into a prepare function that can be performed outside the critical parts of the atomic page flip code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_drv.h | 5 + drivers/gpu/drm/i915/intel_sprite.c | 416 ++++++++++++++++++++++------------- 2 files changed, 273 insertions(+), 148 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4798f54..05afdd1 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -270,6 +270,11 @@ struct intel_plane { struct drm_intel_sprite_colorkey *key); void (*get_colorkey)(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key); + void (*calc)(struct drm_plane *plane, struct drm_framebuffer *fb, + const struct intel_plane_coords *clip); + void (*prepare)(struct drm_plane *plane); + void (*commit)(struct drm_plane *plane); + struct intel_plane_regs regs; };
struct intel_watermark_params { diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index d64cefd..88644df 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -203,14 +203,13 @@ int intel_check_plane(const struct drm_plane *plane, }
static void -ivb_update_plane(struct drm_plane *plane, - struct drm_framebuffer *fb, - const struct intel_plane_coords *coords) +ivb_calc_plane(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) { struct drm_device *dev = plane->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); - const struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + const struct drm_i915_gem_object *obj; int crtc_x = coords->crtc_x; int crtc_y = coords->crtc_y; unsigned int crtc_w = coords->crtc_w; @@ -219,48 +218,56 @@ ivb_update_plane(struct drm_plane *plane, uint32_t y = coords->src_y; uint32_t src_w = coords->src_w; uint32_t src_h = coords->src_h; - int pipe = intel_plane->pipe; - u32 sprctl, sprscale = 0; unsigned long sprsurf_offset, linear_offset; - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + int pixel_size; + struct intel_plane_regs *regs = &intel_plane->regs;
- sprctl = I915_READ(SPRCTL(pipe)); + if (!coords->visible) { + regs->cntr &= ~SPRITE_ENABLE; + /* Disable the scaler */ + regs->scale = 0; + return; + } + + obj = to_intel_framebuffer(fb)->obj; + pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
/* Mask out pixel format bits in case we change it */ - sprctl &= ~SPRITE_PIXFORMAT_MASK; - sprctl &= ~SPRITE_RGB_ORDER_RGBX; - sprctl &= ~SPRITE_YUV_BYTE_ORDER_MASK; - sprctl &= ~SPRITE_TILED; + regs->cntr &= ~(SPRITE_PIXFORMAT_MASK | + SPRITE_RGB_ORDER_RGBX | + SPRITE_YUV_BYTE_ORDER_MASK | + SPRITE_TILED | + SPRITE_ENABLE);
switch (fb->pixel_format) { case DRM_FORMAT_XBGR8888: - sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX; + regs->cntr |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX; break; case DRM_FORMAT_XRGB8888: - sprctl |= SPRITE_FORMAT_RGBX888; + regs->cntr |= SPRITE_FORMAT_RGBX888; break; case DRM_FORMAT_YUYV: - sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV; + regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV; break; case DRM_FORMAT_YVYU: - sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU; + regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU; break; case DRM_FORMAT_UYVY: - sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY; + regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY; break; case DRM_FORMAT_VYUY: - sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY; + regs->cntr |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY; break; default: BUG(); }
if (obj->tiling_mode != I915_TILING_NONE) - sprctl |= SPRITE_TILED; + regs->cntr |= SPRITE_TILED;
/* must disable */ - sprctl |= SPRITE_TRICKLE_FEED_DISABLE; - sprctl |= SPRITE_ENABLE; + regs->cntr |= SPRITE_TRICKLE_FEED_DISABLE; + regs->cntr |= SPRITE_ENABLE;
/* Sizes are 0 based */ src_w--; @@ -268,20 +275,80 @@ ivb_update_plane(struct drm_plane *plane, crtc_w--; crtc_h--;
- intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size); + regs->scale = 0; + if (crtc_w != src_w || crtc_h != src_h) + regs->scale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; + + regs->stride = fb->pitches[0]; + regs->pos = (crtc_y << 16) | crtc_x; + + linear_offset = y * fb->pitches[0] + x * pixel_size; + sprsurf_offset = + intel_gen4_compute_offset_xtiled(&x, &y, + pixel_size, fb->pitches[0]); + linear_offset -= sprsurf_offset; + + /* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET + * register */ + if (IS_HASWELL(dev) || obj->tiling_mode != I915_TILING_NONE) + regs->tileoff = (y << 16) | x; + else + regs->linoff = linear_offset; + + regs->size = (crtc_h << 16) | crtc_w; + regs->surf = I915_LO_DISPBASE(regs->surf) | (obj->gtt_offset + sprsurf_offset); +} + +static void +ivb_commit_plane(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(plane); + int pipe = intel_plane->pipe; + const struct intel_plane_regs *regs = &intel_plane->regs; + + I915_WRITE(SPRKEYVAL(pipe), regs->keyval); + I915_WRITE(SPRKEYMAX(pipe), regs->keymaxval); + I915_WRITE(SPRKEYMSK(pipe), regs->keymsk); + I915_WRITE(SPRSTRIDE(pipe), regs->stride); + I915_WRITE(SPRPOS(pipe), regs->pos); + if (IS_HASWELL(dev)) { + I915_WRITE(SPROFFSET(pipe), regs->tileoff); + } else { + I915_WRITE(SPRTILEOFF(pipe), regs->tileoff); + I915_WRITE(SPRLINOFF(pipe), regs->linoff); + } + I915_WRITE(SPRSIZE(pipe), regs->size); + if (intel_plane->can_scale) + I915_WRITE(SPRSCALE(pipe), regs->scale); + I915_WRITE(SPRCTL(pipe), regs->cntr); + I915_WRITE(SPRSURF(pipe), regs->surf); +} + +static void +ivb_prepare_plane(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(plane); + int pipe = intel_plane->pipe; + int pixel_size = plane->fb ? drm_format_plane_cpp(plane->fb->pixel_format, 0) : 0; + const struct intel_plane_regs *regs = &intel_plane->regs; + + intel_update_sprite_watermarks(dev, pipe, regs->size & 0xffff, pixel_size);
/* * IVB workaround: must disable low power watermarks for at least * one frame before enabling scaling. LP watermarks can be re-enabled * when scaling is disabled. */ - if (crtc_w != src_w || crtc_h != src_h) { + if (regs->scale & SPRITE_SCALE_ENABLE) { if (!dev_priv->sprite_scaling_enabled) { dev_priv->sprite_scaling_enabled = true; intel_update_watermarks(dev); intel_wait_for_vblank(dev, pipe); } - sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; } else { if (dev_priv->sprite_scaling_enabled) { dev_priv->sprite_scaling_enabled = false; @@ -289,30 +356,21 @@ ivb_update_plane(struct drm_plane *plane, intel_update_watermarks(dev); } } - - I915_WRITE(SPRSTRIDE(pipe), fb->pitches[0]); - I915_WRITE(SPRPOS(pipe), (crtc_y << 16) | crtc_x); - - linear_offset = y * fb->pitches[0] + x * pixel_size; - sprsurf_offset = - intel_gen4_compute_offset_xtiled(&x, &y, - pixel_size, fb->pitches[0]); - linear_offset -= sprsurf_offset; - - /* HSW consolidates SPRTILEOFF and SPRLINOFF into a single SPROFFSET - * register */ - if (IS_HASWELL(dev)) - I915_WRITE(SPROFFSET(pipe), (y << 16) | x); - else if (obj->tiling_mode != I915_TILING_NONE) - I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); - else - I915_WRITE(SPRLINOFF(pipe), linear_offset); - - I915_WRITE(SPRSIZE(pipe), (crtc_h << 16) | crtc_w); - if (intel_plane->can_scale) - I915_WRITE(SPRSCALE(pipe), sprscale); - I915_WRITE(SPRCTL(pipe), sprctl); - I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset + sprsurf_offset); +} + +static void +ivb_update_plane(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) +{ + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(plane); + int pipe = intel_plane->pipe; + + ivb_calc_plane(plane, fb, coords); + ivb_prepare_plane(plane); + ivb_commit_plane(plane); POSTING_READ(SPRSURF(pipe)); }
@@ -323,13 +381,12 @@ ivb_disable_plane(struct drm_plane *plane) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; + struct intel_plane_regs *regs = &intel_plane->regs;
- I915_WRITE(SPRCTL(pipe), I915_READ(SPRCTL(pipe)) & ~SPRITE_ENABLE); + regs->cntr &= ~SPRITE_ENABLE; /* Can't leave the scaler enabled... */ - if (intel_plane->can_scale) - I915_WRITE(SPRSCALE(pipe), 0); - /* Activate double buffered register update */ - I915_MODIFY_DISPBASE(SPRSURF(pipe), 0); + regs->scale = 0; + ivb_commit_plane(plane); POSTING_READ(SPRSURF(pipe));
dev_priv->sprite_scaling_enabled = false; @@ -342,63 +399,52 @@ ivb_update_colorkey(struct drm_plane *plane, { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_plane *intel_plane; - u32 sprctl; - int ret = 0; + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_plane_regs *regs = &intel_plane->regs;
- intel_plane = to_intel_plane(plane); + regs->keyval = key->min_value; + regs->keymaxval = key->max_value; + regs->keymsk = key->channel_mask;
- I915_WRITE(SPRKEYVAL(intel_plane->pipe), key->min_value); - I915_WRITE(SPRKEYMAX(intel_plane->pipe), key->max_value); - I915_WRITE(SPRKEYMSK(intel_plane->pipe), key->channel_mask); - - sprctl = I915_READ(SPRCTL(intel_plane->pipe)); - sprctl &= ~(SPRITE_SOURCE_KEY | SPRITE_DEST_KEY); + regs->cntr &= ~(SPRITE_SOURCE_KEY | SPRITE_DEST_KEY); if (key->flags & I915_SET_COLORKEY_DESTINATION) - sprctl |= SPRITE_DEST_KEY; + regs->cntr |= SPRITE_DEST_KEY; else if (key->flags & I915_SET_COLORKEY_SOURCE) - sprctl |= SPRITE_SOURCE_KEY; - I915_WRITE(SPRCTL(intel_plane->pipe), sprctl); + regs->cntr |= SPRITE_SOURCE_KEY;
+ ivb_commit_plane(plane); POSTING_READ(SPRKEYMSK(intel_plane->pipe));
- return ret; + return 0; }
static void ivb_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) { - struct drm_device *dev = plane->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_plane *intel_plane; - u32 sprctl; + struct intel_plane *intel_plane = to_intel_plane(plane); + const struct intel_plane_regs *regs = &intel_plane->regs;
- intel_plane = to_intel_plane(plane); - - key->min_value = I915_READ(SPRKEYVAL(intel_plane->pipe)); - key->max_value = I915_READ(SPRKEYMAX(intel_plane->pipe)); - key->channel_mask = I915_READ(SPRKEYMSK(intel_plane->pipe)); + key->min_value = regs->keyval; + key->max_value = regs->keymaxval; + key->channel_mask = regs->keymsk; key->flags = 0;
- sprctl = I915_READ(SPRCTL(intel_plane->pipe)); - - if (sprctl & SPRITE_DEST_KEY) + if (regs->cntr & SPRITE_DEST_KEY) key->flags = I915_SET_COLORKEY_DESTINATION; - else if (sprctl & SPRITE_SOURCE_KEY) + else if (regs->cntr & SPRITE_SOURCE_KEY) key->flags = I915_SET_COLORKEY_SOURCE; else key->flags = I915_SET_COLORKEY_NONE; }
static void -ilk_update_plane(struct drm_plane *plane, - struct drm_framebuffer *fb, - const struct intel_plane_coords *coords) +ilk_calc_plane(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) { struct drm_device *dev = plane->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); - const struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + const struct drm_i915_gem_object *obj; int crtc_x = coords->crtc_x; int crtc_y = coords->crtc_y; unsigned int crtc_w = coords->crtc_w; @@ -407,48 +453,56 @@ ilk_update_plane(struct drm_plane *plane, uint32_t y = coords->src_y; uint32_t src_w = coords->src_w; uint32_t src_h = coords->src_h; - int pipe = intel_plane->pipe; unsigned long dvssurf_offset, linear_offset; - u32 dvscntr, dvsscale; - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + int pixel_size; + struct intel_plane_regs *regs = &intel_plane->regs;
- dvscntr = I915_READ(DVSCNTR(pipe)); + if (!coords->visible) { + regs->cntr &= ~DVS_ENABLE; + /* Disable the scaler */ + regs->scale = 0; + return; + } + + obj = to_intel_framebuffer(fb)->obj; + pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
/* Mask out pixel format bits in case we change it */ - dvscntr &= ~DVS_PIXFORMAT_MASK; - dvscntr &= ~DVS_RGB_ORDER_XBGR; - dvscntr &= ~DVS_YUV_BYTE_ORDER_MASK; - dvscntr &= ~DVS_TILED; + regs->cntr &= ~(DVS_PIXFORMAT_MASK | + DVS_RGB_ORDER_XBGR | + DVS_YUV_BYTE_ORDER_MASK | + DVS_TILED | + DVS_ENABLE);
switch (fb->pixel_format) { case DRM_FORMAT_XBGR8888: - dvscntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR; + regs->cntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR; break; case DRM_FORMAT_XRGB8888: - dvscntr |= DVS_FORMAT_RGBX888; + regs->cntr |= DVS_FORMAT_RGBX888; break; case DRM_FORMAT_YUYV: - dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV; + regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV; break; case DRM_FORMAT_YVYU: - dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU; + regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU; break; case DRM_FORMAT_UYVY: - dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY; + regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY; break; case DRM_FORMAT_VYUY: - dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY; + regs->cntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY; break; default: BUG(); }
if (obj->tiling_mode != I915_TILING_NONE) - dvscntr |= DVS_TILED; + regs->cntr |= DVS_TILED;
if (IS_GEN6(dev)) - dvscntr |= DVS_TRICKLE_FEED_DISABLE; /* must disable */ - dvscntr |= DVS_ENABLE; + regs->cntr |= DVS_TRICKLE_FEED_DISABLE; /* must disable */ + regs->cntr |= DVS_ENABLE;
/* Sizes are 0 based */ src_w--; @@ -456,14 +510,12 @@ ilk_update_plane(struct drm_plane *plane, crtc_w--; crtc_h--;
- intel_update_sprite_watermarks(dev, pipe, crtc_w, pixel_size); - - dvsscale = 0; + regs->scale = 0; if (IS_GEN5(dev) || crtc_w != src_w || crtc_h != src_h) - dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h; + regs->scale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
- I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]); - I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x); + regs->stride = fb->pitches[0]; + regs->pos = (crtc_y << 16) | crtc_x;
linear_offset = y * fb->pitches[0] + x * pixel_size; dvssurf_offset = @@ -472,14 +524,61 @@ ilk_update_plane(struct drm_plane *plane, linear_offset -= dvssurf_offset;
if (obj->tiling_mode != I915_TILING_NONE) - I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x); + regs->tileoff = (y << 16) | x; else - I915_WRITE(DVSLINOFF(pipe), linear_offset); + regs->linoff = linear_offset;
- I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w); - I915_WRITE(DVSSCALE(pipe), dvsscale); - I915_WRITE(DVSCNTR(pipe), dvscntr); - I915_MODIFY_DISPBASE(DVSSURF(pipe), obj->gtt_offset + dvssurf_offset); + regs->size = (crtc_h << 16) | crtc_w; + regs->surf = I915_LO_DISPBASE(regs->surf) | (obj->gtt_offset + dvssurf_offset); +} + +static void +ilk_prepare_plane(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct intel_plane *intel_plane = to_intel_plane(plane); + int pipe = intel_plane->pipe; + int pixel_size = plane->fb ? drm_format_plane_cpp(plane->fb->pixel_format, 0) : 0; + const struct intel_plane_regs *regs = &intel_plane->regs; + + intel_update_sprite_watermarks(dev, pipe, regs->size & 0xffff, pixel_size); +} + +static void +ilk_commit_plane(struct drm_plane *plane) +{ + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(plane); + int pipe = intel_plane->pipe; + const struct intel_plane_regs *regs = &intel_plane->regs; + + I915_WRITE(DVSKEYVAL(pipe), regs->keyval); + I915_WRITE(DVSKEYMAX(pipe), regs->keymaxval); + I915_WRITE(DVSKEYMSK(pipe), regs->keymsk); + I915_WRITE(DVSSTRIDE(pipe), regs->stride); + I915_WRITE(DVSPOS(pipe), regs->pos); + I915_WRITE(DVSTILEOFF(pipe), regs->tileoff); + I915_WRITE(DVSLINOFF(pipe), regs->linoff); + I915_WRITE(DVSSIZE(pipe), regs->size); + I915_WRITE(DVSSCALE(pipe), regs->scale); + I915_WRITE(DVSCNTR(pipe), regs->cntr); + I915_WRITE(DVSSURF(pipe), regs->surf); +} + +static void +ilk_update_plane(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) +{ + struct drm_device *dev = plane->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_plane *intel_plane = to_intel_plane(plane); + int pipe = intel_plane->pipe; + + ilk_calc_plane(plane, fb, coords); + ilk_prepare_plane(plane); + ilk_commit_plane(plane); POSTING_READ(DVSSURF(pipe)); }
@@ -490,21 +589,34 @@ ilk_disable_plane(struct drm_plane *plane) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; + struct intel_plane_regs *regs = &intel_plane->regs;
- I915_WRITE(DVSCNTR(pipe), I915_READ(DVSCNTR(pipe)) & ~DVS_ENABLE); + regs->cntr &= ~DVS_ENABLE; /* Disable the scaler */ - I915_WRITE(DVSSCALE(pipe), 0); - /* Flush double buffered register updates */ - I915_MODIFY_DISPBASE(DVSSURF(pipe), 0); + regs->scale = 0; + ilk_commit_plane(plane); POSTING_READ(DVSSURF(pipe)); }
+void intel_calc_sprite(struct drm_plane *plane, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords) +{ + struct drm_device *dev = plane->dev; + + if (INTEL_INFO(dev)->gen >= 7) + ivb_calc_plane(plane, fb, coords); + else + ilk_calc_plane(plane, fb, coords); +} + static void intel_enable_primary(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane_regs *regs = &intel_crtc->primary_regs; int reg = DSPCNTR(intel_crtc->plane);
if (!intel_crtc->primary_disabled) @@ -513,7 +625,8 @@ intel_enable_primary(struct drm_crtc *crtc) intel_crtc->primary_disabled = false; intel_update_fbc(dev);
- I915_WRITE(reg, I915_READ(reg) | DISPLAY_PLANE_ENABLE); + regs->cntr = I915_READ(reg) | DISPLAY_PLANE_ENABLE; + dev_priv->display.commit_plane(crtc); }
static void @@ -522,12 +635,14 @@ intel_disable_primary(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane_regs *regs = &intel_crtc->primary_regs; int reg = DSPCNTR(intel_crtc->plane);
if (intel_crtc->primary_disabled) return;
- I915_WRITE(reg, I915_READ(reg) & ~DISPLAY_PLANE_ENABLE); + regs->cntr = I915_READ(reg) & ~DISPLAY_PLANE_ENABLE; + dev_priv->display.commit_plane(crtc);
intel_crtc->primary_disabled = true; intel_update_fbc(dev); @@ -539,49 +654,39 @@ ilk_update_colorkey(struct drm_plane *plane, { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_plane *intel_plane; - u32 dvscntr; - int ret = 0; + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_plane_regs *regs = &intel_plane->regs;
- intel_plane = to_intel_plane(plane); + regs->keyval = key->min_value; + regs->keymaxval = key->max_value; + regs->keymsk = key->channel_mask;
- I915_WRITE(DVSKEYVAL(intel_plane->pipe), key->min_value); - I915_WRITE(DVSKEYMAX(intel_plane->pipe), key->max_value); - I915_WRITE(DVSKEYMSK(intel_plane->pipe), key->channel_mask); - - dvscntr = I915_READ(DVSCNTR(intel_plane->pipe)); - dvscntr &= ~(DVS_SOURCE_KEY | DVS_DEST_KEY); + regs->cntr &= ~(DVS_SOURCE_KEY | DVS_DEST_KEY); if (key->flags & I915_SET_COLORKEY_DESTINATION) - dvscntr |= DVS_DEST_KEY; + regs->cntr |= DVS_DEST_KEY; else if (key->flags & I915_SET_COLORKEY_SOURCE) - dvscntr |= DVS_SOURCE_KEY; - I915_WRITE(DVSCNTR(intel_plane->pipe), dvscntr); + regs->cntr |= DVS_SOURCE_KEY;
+ ilk_commit_plane(plane); POSTING_READ(DVSKEYMSK(intel_plane->pipe));
- return ret; + return 0; }
static void ilk_get_colorkey(struct drm_plane *plane, struct drm_intel_sprite_colorkey *key) { - struct drm_device *dev = plane->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_plane *intel_plane; - u32 dvscntr; + struct intel_plane *intel_plane = to_intel_plane(plane); + const struct intel_plane_regs *regs = &intel_plane->regs;
- intel_plane = to_intel_plane(plane); - - key->min_value = I915_READ(DVSKEYVAL(intel_plane->pipe)); - key->max_value = I915_READ(DVSKEYMAX(intel_plane->pipe)); - key->channel_mask = I915_READ(DVSKEYMSK(intel_plane->pipe)); + key->min_value = regs->keyval; + key->max_value = regs->keymaxval; + key->channel_mask = regs->keymsk; key->flags = 0;
- dvscntr = I915_READ(DVSCNTR(intel_plane->pipe)); - - if (dvscntr & DVS_DEST_KEY) + if (regs->cntr & DVS_DEST_KEY) key->flags = I915_SET_COLORKEY_DESTINATION; - else if (dvscntr & DVS_SOURCE_KEY) + else if (regs->cntr & DVS_SOURCE_KEY) key->flags = I915_SET_COLORKEY_SOURCE; else key->flags = I915_SET_COLORKEY_NONE; @@ -819,6 +924,7 @@ static uint32_t snb_plane_formats[] = { int intel_plane_init(struct drm_device *dev, enum pipe pipe) { + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane; unsigned long possible_crtcs; const uint32_t *plane_formats; @@ -842,6 +948,13 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) intel_plane->update_colorkey = ilk_update_colorkey; intel_plane->get_colorkey = ilk_get_colorkey;
+ intel_plane->calc = ilk_calc_plane; + intel_plane->prepare = ilk_prepare_plane; + intel_plane->commit = ilk_commit_plane; + + intel_plane->regs.cntr = I915_READ(DVSCNTR(pipe)); + intel_plane->regs.surf = I915_READ(DVSSURF(pipe)); + if (IS_GEN6(dev)) { plane_formats = snb_plane_formats; num_plane_formats = ARRAY_SIZE(snb_plane_formats); @@ -862,6 +975,13 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe) intel_plane->update_colorkey = ivb_update_colorkey; intel_plane->get_colorkey = ivb_get_colorkey;
+ intel_plane->calc = ivb_calc_plane; + intel_plane->prepare = ivb_prepare_plane; + intel_plane->commit = ivb_commit_plane; + + intel_plane->regs.cntr = I915_READ(SPRCTL(pipe)); + intel_plane->regs.surf = I915_READ(SPRSURF(pipe)); + plane_formats = snb_plane_formats; num_plane_formats = ARRAY_SIZE(snb_plane_formats); break;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Implement the mandatory hooks for the atomic modeset ioctl.
The code first makes a backup of the current state, then proceeds to modify the state as properties are modified. After all the properties have been handled the new state is checked, and if everything checks out, the new state is commited to hardware. Finally we clean up any temporary storage.
In theory everything is checked before the hardware state is clobbered, so there should be no need for rollback. But as the current modesetting code can still return errors from some of of the operation, the rollback code is included.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/intel_atomic.c | 1625 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 5 + drivers/gpu/drm/i915/intel_drv.h | 3 + include/drm/drm_crtc.h | 2 + 5 files changed, 1636 insertions(+), 0 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_atomic.c
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 0f2c549..cca14e4 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -16,6 +16,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ i915_gem_tiling.o \ i915_sysfs.o \ i915_trace_points.o \ + intel_atomic.o \ intel_display.o \ intel_crt.o \ intel_lvds.o \ diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c new file mode 100644 index 0000000..4899f8c --- /dev/null +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -0,0 +1,1625 @@ +/* + * Copyright (C) 2011-2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Authors: + * Ville Syrjälä ville.syrjala@linux.intel.com + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> + +#include "intel_drv.h" + +static struct drm_property *prop_src_x; +static struct drm_property *prop_src_y; +static struct drm_property *prop_src_w; +static struct drm_property *prop_src_h; +static struct drm_property *prop_crtc_x; +static struct drm_property *prop_crtc_y; +static struct drm_property *prop_crtc_w; +static struct drm_property *prop_crtc_h; +static struct drm_property *prop_fb_id; +static struct drm_property *prop_crtc_id; +static struct drm_property *prop_mode; +static struct drm_property *prop_connector_ids; +static struct drm_property *prop_cursor_id; +static struct drm_property *prop_cursor_x; +static struct drm_property *prop_cursor_y; +static struct drm_property *prop_cursor_w; +static struct drm_property *prop_cursor_h; + +struct intel_plane_state { + struct drm_plane *plane; + struct intel_plane_coords coords; + bool dirty; + bool pinned; + bool changed; + + struct { + struct drm_crtc *crtc; + struct drm_framebuffer *fb; + uint32_t src_x, src_y, src_w, src_h; + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + } old; +}; + +struct intel_crtc_state { + struct drm_crtc *crtc; + bool mode_dirty; + bool fb_dirty; + bool cursor_dirty; + bool active_dirty; + bool pinned; + bool cursor_pinned; + unsigned long connectors_bitmask; + unsigned long encoders_bitmask; + bool changed; + + struct { + bool enabled; + struct drm_display_mode mode; + struct drm_framebuffer *fb; + int x, y; + unsigned long connectors_bitmask; + unsigned long encoders_bitmask; + + struct drm_i915_gem_object *cursor_bo; + uint32_t cursor_handle; + int16_t cursor_x, cursor_y; + int16_t cursor_width, cursor_height; + bool cursor_visible; + } old; +}; + +struct intel_atomic_state { + struct drm_file *file; + struct intel_plane_state *plane; + struct intel_crtc_state *crtc; + bool dirty; + bool restore_state; + bool restore_hw; + unsigned int flags; + uint64_t user_data; + struct drm_plane *saved_planes; + struct intel_crtc *saved_crtcs; + struct drm_connector *saved_connectors; + struct drm_encoder *saved_encoders; +}; + +static void update_connectors_bitmask(struct drm_crtc *crtc, + unsigned long *connectors_bitmask) +{ + struct drm_device *dev = crtc->dev; + struct drm_connector *connector; + unsigned int i = 0; + + *connectors_bitmask = 0; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && connector->encoder->crtc == crtc) + __set_bit(i, connectors_bitmask); + + i++; + } +} + +static void update_encoders_bitmask(struct drm_crtc *crtc, + unsigned long *encoders_bitmask) +{ + struct drm_device *dev = crtc->dev; + struct drm_encoder *encoder; + unsigned int i = 0; + + *encoders_bitmask = 0; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc) + __set_bit(i, encoders_bitmask); + + i++; + } +} + +static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, int count_ids) +{ + struct drm_crtc *crtc = s->crtc; + struct drm_device *dev = crtc->dev; + struct drm_connector *connectors[count_ids]; + struct drm_connector *connector; + struct drm_encoder *encoder; + int i; + + for (i = 0; i < count_ids; i++) { + struct drm_encoder *encoder; + const struct drm_connector_helper_funcs *connector_funcs; + struct drm_mode_object *obj; + int j; + + /* don't accept duplicates */ + for (j = i + 1; j < count_ids; j++) + if (ids[i] == ids[j]) + return -EINVAL; + + obj = drm_mode_object_find(dev, ids[i], DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + DRM_DEBUG_KMS("Unknown connector ID %u\n", ids[i]); + return -ENOENT; + } + + connector = obj_to_connector(obj); + connector_funcs = connector->helper_private; + + encoder = connector_funcs->best_encoder(connector); + + if (!drm_encoder_crtc_ok(encoder, crtc)) + return -EINVAL; + + connectors[i] = connector; + } + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + const struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + + for (i = 0; i < count_ids; i++) { + if (connector == connectors[i]) + break; + } + + /* this connector isn't in the set */ + if (i == count_ids) { + /* remove the link to the encoder if this crtc was set to drive it */ + if (connector->encoder && connector->encoder->crtc == crtc) + connector->encoder = NULL; + continue; + } + + encoder = connector_funcs->best_encoder(connector); + + connector->encoder = encoder; + encoder->crtc = crtc; + } + + /* prune dangling encoder->crtc links pointing to this crtc */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc == crtc && !drm_helper_encoder_in_use(encoder)) + encoder->crtc = NULL; + } + + update_connectors_bitmask(s->crtc, &s->connectors_bitmask); + update_encoders_bitmask(s->crtc, &s->encoders_bitmask); + + return 0; +} + +static size_t intel_atomic_state_size(const struct drm_device *dev) +{ + struct intel_atomic_state *state; + unsigned int num_connector = dev->mode_config.num_connector; + unsigned int num_encoder = dev->mode_config.num_encoder; + unsigned int num_crtc = dev->mode_config.num_crtc; + unsigned int num_plane = dev->mode_config.num_plane; + + return sizeof *state + + num_crtc * sizeof state->crtc[0] + + num_plane * sizeof state->plane[0] + + num_connector * sizeof state->saved_connectors[0] + + num_encoder * sizeof state->saved_encoders[0] + + num_crtc * sizeof state->saved_crtcs[0] + + num_plane * sizeof state->saved_planes[0]; +} + + +static void populate_old(struct drm_device *dev) +{ + struct drm_encoder *encoder; + struct drm_connector *connector; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + encoder->old_crtc = encoder->crtc; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + connector->old_encoder = connector->encoder; +} + +static void *intel_atomic_begin(struct drm_device *dev, struct drm_file *file, + uint32_t flags, uint64_t user_data) +{ + struct intel_atomic_state *state; + struct drm_plane *plane; + struct drm_crtc *crtc; + struct drm_connector *connector; + struct drm_encoder *encoder; + unsigned int num_connector = dev->mode_config.num_connector; + unsigned int num_encoder = dev->mode_config.num_encoder; + unsigned int num_crtc = dev->mode_config.num_crtc; + unsigned int num_plane = dev->mode_config.num_plane; + int i; + + state = kzalloc(intel_atomic_state_size(dev), GFP_KERNEL); + if (!state) + return ERR_PTR(-ENOMEM); + + state->flags = flags; + state->file = file; + state->user_data = user_data; + + state->crtc = (struct intel_crtc_state *)(state + 1); + state->plane = (struct intel_plane_state *)(state->crtc + num_crtc); + + state->saved_connectors = (struct drm_connector *)(state->plane + num_plane); + state->saved_encoders = (struct drm_encoder *)(state->saved_connectors + num_connector); + state->saved_crtcs = (struct intel_crtc *)(state->saved_encoders + num_encoder); + state->saved_planes = (struct drm_plane *)(state->saved_crtcs + num_crtc); + + populate_old(dev); + + i = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc_state *s = &state->crtc[i++]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + s->crtc = crtc; + s->old.cursor_bo = intel_crtc->cursor_bo; + s->old.cursor_x = intel_crtc->cursor_x; + s->old.cursor_y = intel_crtc->cursor_y; + s->old.cursor_width = intel_crtc->cursor_width; + s->old.cursor_height = intel_crtc->cursor_height; + s->old.cursor_visible = intel_crtc->cursor_visible; + + /* save current config */ + s->old.enabled = crtc->enabled; + s->old.mode = crtc->mode; + s->old.fb = crtc->fb; + s->old.x = crtc->x; + s->old.y = crtc->y; + + update_connectors_bitmask(crtc, &s->connectors_bitmask); + update_encoders_bitmask(crtc, &s->encoders_bitmask); + + s->old.connectors_bitmask = s->connectors_bitmask; + s->old.encoders_bitmask = s->encoders_bitmask; + } + + i = 0; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + struct intel_plane_state *s = &state->plane[i++]; + + s->plane = plane; + + /* save current config */ + s->old.crtc = plane->crtc; + s->old.fb = plane->fb; + s->old.src_x = plane->src_x; + s->old.src_y = plane->src_y; + s->old.src_w = plane->src_w; + s->old.src_h = plane->src_h; + s->old.crtc_x = plane->crtc_x; + s->old.crtc_y = plane->crtc_y; + s->old.crtc_w = plane->crtc_w; + s->old.crtc_h = plane->crtc_h; + } + + i = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + state->saved_connectors[i++] = *connector; + i = 0; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + state->saved_encoders[i++] = *encoder; + i = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + state->saved_crtcs[i++] = *intel_crtc; + } + i = 0; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) + state->saved_planes[i++] = *plane; + + state->file = file; + + return state; +} + +static int plane_set(struct intel_atomic_state *s, + struct intel_plane_state *state, + struct drm_property *prop, + uint64_t value) +{ + struct drm_plane *plane = state->plane; + struct drm_mode_object *obj; + + state->changed = true; + + if (prop == prop_src_x) { + plane->src_x = value; + } else if (prop == prop_src_y) { + plane->src_y = value; + } else if (prop == prop_src_w) { + plane->src_w = value; + } else if (prop == prop_src_h) { + plane->src_h = value; + } else if (prop == prop_crtc_x) { + plane->crtc_x = value; + } else if (prop == prop_crtc_y) { + plane->crtc_y = value; + } else if (prop == prop_crtc_w) { + plane->crtc_w = value; + } else if (prop == prop_crtc_h) { + plane->crtc_h = value; + } else if (prop == prop_crtc_id) { + if (value) { + obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_CRTC); + if (!obj) { + DRM_DEBUG_KMS("Unknown CRTC ID %llu\n", value); + return -ENOENT; + } + plane->crtc = obj_to_crtc(obj); + } else + plane->crtc = NULL; + } else if (prop == prop_fb_id) { + if (value) { + obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_DEBUG_KMS("Unknown framebuffer ID %llu\n", value); + return -ENOENT; + } + plane->fb = obj_to_fb(obj); + } else + plane->fb = NULL; + } else + return -ENOENT; + + s->restore_state = true; + + return 0; +} + +static int crtc_set(struct intel_atomic_state *s, + struct intel_crtc_state *state, + struct drm_property *prop, + uint64_t value, const void *blob_data) +{ + struct drm_crtc *crtc = state->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_mode_object *obj; + + state->changed = true; + + if (prop == prop_src_x) { + crtc->x = value; + } else if (prop == prop_src_y) { + crtc->y = value; + } else if (prop == prop_mode) { + const struct drm_mode_modeinfo *umode = blob_data; + + if (value != 0 && value != sizeof *umode) { + DRM_DEBUG_KMS("Invalid mode length\n"); + return -EINVAL; + } + + if (value) { + struct drm_display_mode *mode; + int ret; + + mode = drm_mode_create(crtc->dev); + if (!mode) + return -ENOMEM; + + ret = drm_crtc_convert_umode(mode, umode); + if (ret) { + DRM_DEBUG_KMS("Invalid mode\n"); + drm_mode_debug_printmodeline(mode); + drm_mode_destroy(crtc->dev, mode); + return ret; + } + + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + + crtc->mode = *mode; + crtc->enabled = true; + drm_mode_destroy(crtc->dev, mode); + } else + crtc->enabled = false; + } else if (prop == prop_fb_id) { + if (value) { + obj = drm_mode_object_find(crtc->dev, value, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_DEBUG_KMS("Unknown framebuffer ID %llu\n", value); + return -ENOENT; + } + crtc->fb = obj_to_fb(obj); + } else + crtc->fb = NULL; + } else if (prop == prop_connector_ids) { + const uint32_t *ids = blob_data; + uint64_t count_ids = value / sizeof(uint32_t); + int ret; + + if (value & 3) { + DRM_DEBUG_KMS("Invalid connectors length\n"); + return -EINVAL; + } + + if (count_ids > crtc->dev->mode_config.num_connector) { + DRM_DEBUG_KMS("Too many connectors specified\n"); + return -ERANGE; + } + + ret = process_connectors(state, ids, count_ids); + if (ret) + return ret; + } else if (prop == prop_cursor_id) { + intel_crtc->cursor_handle = value; + } else if (prop == prop_cursor_x) { + intel_crtc->cursor_x = value; + } else if (prop == prop_cursor_y) { + intel_crtc->cursor_y = value; + } else if (prop == prop_cursor_w) { + if (value != 0 && value != 64) { + DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n"); + return -EINVAL; + } + intel_crtc->cursor_width = value; + } else if (prop == prop_cursor_h) { + if (value != 0 && value != 64) { + DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n"); + return -EINVAL; + } + intel_crtc->cursor_height = value; + } else + return -ENOENT; + + s->restore_state = true; + + return 0; +} + +static void crtc_compute_dirty(struct intel_atomic_state *s, + struct intel_crtc_state *state) +{ + struct drm_crtc *crtc = state->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + /* if no properties were specified nothing is dirty */ + if (!state->changed) + return; + + /* fb/pan changed? */ + if (crtc->x != state->old.x || + crtc->y != state->old.y || + crtc->fb != state->old.fb) { + /* FIXME do we need a full modeset sometimes? */ + state->fb_dirty = true; + } + + /* enabled <-> disabled? */ + if (crtc->enabled != state->old.enabled) { + state->mode_dirty = true; + state->active_dirty = true; + } + + /* mode changed? */ + if (crtc->enabled && state->old.enabled && + !drm_mode_equal(&crtc->mode, &state->old.mode)) { + state->mode_dirty = true; + + if (crtc->mode.hdisplay != state->old.mode.hdisplay || + crtc->mode.vdisplay != state->old.mode.vdisplay) + state->active_dirty = true; + } + + /* connectors changed? */ + if (state->connectors_bitmask != state->old.connectors_bitmask || + state->encoders_bitmask != state->old.encoders_bitmask) + state->mode_dirty = true; + + /* cursor changed? */ + if (intel_crtc->cursor_handle != state->old.cursor_handle || + intel_crtc->cursor_x != state->old.cursor_x || + intel_crtc->cursor_y != state->old.cursor_y || + intel_crtc->cursor_width != state->old.cursor_width || + intel_crtc->cursor_height != state->old.cursor_height) + state->cursor_dirty = true; + + if (state->fb_dirty || state->mode_dirty || state->cursor_dirty) + s->dirty = true; +} + +static void plane_compute_dirty(struct intel_atomic_state *s, + struct intel_plane_state *state) +{ + struct drm_plane *plane = state->plane; + + /* if no properties were specified nothing is dirty */ + if (!state->changed) + return; + + if (plane->src_x != state->old.src_x || + plane->src_y != state->old.src_x || + plane->src_w != state->old.src_x || + plane->src_h != state->old.src_x || + plane->crtc_x != state->old.crtc_x || + plane->crtc_y != state->old.crtc_y || + plane->crtc_w != state->old.crtc_w || + plane->crtc_h != state->old.crtc_h || + plane->crtc != state->old.crtc || + plane->fb != state->old.fb) + state->dirty = true; + + if (state->dirty) + s->dirty = true; +} + +static struct intel_plane_state *get_plane_state(const struct drm_device *dev, + struct intel_atomic_state *state, + const struct drm_plane *plane) +{ + int i; + + for (i = 0; i < dev->mode_config.num_plane; i++) + if (plane == state->plane[i].plane) + return &state->plane[i]; + + return NULL; +} + +static struct intel_crtc_state *get_crtc_state(const struct drm_device *dev, + struct intel_atomic_state *state, + const struct drm_crtc *crtc) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) + if (crtc == state->crtc[i].crtc) + return &state->crtc[i]; + + return NULL; +} + +static void crtc_prepare(struct intel_crtc_state *st, + struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder; + + dev_priv->display.crtc_disable(&intel_crtc->base); + + /* FIXME need to check where this stuff is used really */ + list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) { + if (intel_encoder->base.crtc == crtc) + intel_encoder->connectors_active = false; + } +} + +static int crtc_set_base(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return dev_priv->display.update_plane(crtc, crtc->fb, crtc->x, crtc->y); +} + +static int crtc_mode_set(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_encoder *encoder; + int pipe = to_intel_crtc(crtc)->pipe; + int ret; + + if (!crtc->enabled) { + dev_priv->display.off(crtc); + return 0; + } + + drm_vblank_pre_modeset(dev, pipe); + + ret = dev_priv->display.crtc_mode_set(crtc, &crtc->mode, &crtc->hwmode, + crtc->x, crtc->y, crtc->fb); + if (!ret) + ret = dev_priv->display.update_plane(crtc, crtc->fb, crtc->x, crtc->y); + + if (!ret) { + intel_update_watermarks(dev); + + if (HAS_PCH_SPLIT(dev)) + intel_update_linetime_watermarks(dev, pipe, &crtc->hwmode); + } + + drm_vblank_post_modeset(dev, pipe); + + if (ret) + return ret; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + + if (encoder->crtc != crtc) + continue; + + encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->hwmode); + } + + return 0; +} + +static void crtc_commit(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder; + + if (!crtc->enabled) + return; + + dev_priv->display.crtc_enable(&intel_crtc->base); + + /* FIXME need to check where this stuff is used really */ + /* FIXME need to update DPMS state somewhere */ + list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) { + if (intel_encoder->base.crtc == crtc) + intel_encoder->connectors_active = true; + } +} + +static void unpin_cursors(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (!st->cursor_pinned) + continue; + + intel_crtc_cursor_bo_unref(crtc, intel_crtc->cursor_bo); + + st->cursor_pinned = false; + } +} + +static int pin_cursors(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i, ret; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (!st->cursor_dirty) + continue; + + ret = intel_crtc_cursor_prepare(crtc, s->file, + intel_crtc->cursor_handle, + intel_crtc->cursor_width, + intel_crtc->cursor_height, + &intel_crtc->cursor_bo, + &intel_crtc->cursor_addr); + if (ret) + goto unpin; + + st->cursor_pinned = true; + } + + return 0; + +unpin: + unpin_cursors(dev, s); + + return ret; +} + +static void unpin_old_cursors(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + + if (!st->cursor_dirty) + continue; + + if (!st->old.cursor_bo) + continue; + + intel_crtc_cursor_bo_unref(crtc, st->old.cursor_bo); + } +} + +static void unpin_fbs(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = dev->mode_config.num_plane - 1; i >= 0; i--) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_plane *plane = st->plane; + struct drm_i915_gem_object *obj; + + if (!st->pinned) + continue; + + obj = to_intel_framebuffer(plane->fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + + st->pinned = false; + } + + for (i = dev->mode_config.num_crtc - 1; i >= 0; i--) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct drm_i915_gem_object *obj; + + if (!st->pinned) + continue; + + obj = to_intel_framebuffer(crtc->fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + + st->pinned = false; + } +} + +static int pin_fbs(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i, ret; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct drm_i915_gem_object *obj; + + if (!st->fb_dirty) + continue; + + if (!crtc->fb) + continue; + + obj = to_intel_framebuffer(crtc->fb)->obj; + + mutex_lock(&dev->struct_mutex); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + mutex_unlock(&dev->struct_mutex); + + if (ret) + goto unpin; + + st->pinned = true; + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_plane *plane = st->plane; + struct drm_i915_gem_object *obj; + + if (!st->dirty) + continue; + + if (!plane->fb) + continue; + + obj = to_intel_framebuffer(plane->fb)->obj; + + mutex_lock(&dev->struct_mutex); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + mutex_unlock(&dev->struct_mutex); + + if (ret) + goto unpin; + + st->pinned = true; + } + + return 0; + + unpin: + unpin_fbs(dev, s); + + return ret; +} + +static void unpin_old_fbs(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_i915_gem_object *obj; + + if (!st->fb_dirty) + continue; + + if (!st->old.fb) + continue; + + obj = to_intel_framebuffer(st->old.fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_i915_gem_object *obj; + + if (!st->dirty) + continue; + + if (!st->old.fb) + continue; + + obj = to_intel_framebuffer(st->old.fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + } +} + +static void update_plane_obj(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_plane *plane = st->plane; + struct intel_plane *intel_plane = to_intel_plane(plane); + + if (!st->dirty) + continue; + + if (plane->fb) + intel_plane->obj = to_intel_framebuffer(plane->fb)->obj; + else + intel_plane->obj = NULL; + } +} + +/* FIXME need to refactor the sprite code some more */ +int intel_disable_plane_nopin(struct drm_plane *plane); +int intel_commit_plane_nopin(struct drm_plane *plane, + struct drm_crtc *crtc, + struct drm_framebuffer *fb, + const struct intel_plane_coords *coords); + +static void swap_old_new(struct drm_device *dev, + struct intel_atomic_state *s) +{ + struct drm_encoder *encoder; + struct drm_connector *connector; + int i; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + swap(encoder->crtc, encoder->old_crtc); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + swap(connector->encoder, connector->old_encoder); + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + + swap(crtc->enabled, st->old.enabled); + } +} + +static int apply_config(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i, ret; + + /* + * FIXME +` * Hackish way to make crtc_disable() see the current + * state (actually just some select pieces of it). + */ + swap_old_new(dev, s); + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + mutex_lock(&dev->struct_mutex); + + if (st->mode_dirty) { + /* wait for pending MI_WAIT_FOR_EVENTs */ + if (st->old.fb) + intel_finish_fb(st->old.fb); + } + + mutex_unlock(&dev->struct_mutex); + + if (!st->mode_dirty) + continue; + + crtc_prepare(st, st->crtc); + } + + /* Undo the hack above. */ + swap_old_new(dev, s); + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + if (!st->mode_dirty) + continue; + + ret = crtc_mode_set(st->crtc); + if (ret) + return ret; + } + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + int j; + + if (st->mode_dirty) + crtc_commit(crtc); + else if (st->fb_dirty) { + ret = crtc_set_base(st->crtc); + if (ret) + return ret; + } + + /* + * FIXME these should happen alongside the primary plane setup + * which occurs inside the crtc_enable() hook. + */ + + if (st->cursor_dirty) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + intel_crtc_cursor_commit(crtc, + intel_crtc->cursor_handle, + intel_crtc->cursor_width, + intel_crtc->cursor_height, + intel_crtc->cursor_bo, + intel_crtc->cursor_addr); + } + + for (j = 0; j < dev->mode_config.num_plane; j++) { + struct intel_plane_state *pst = &s->plane[j]; + struct drm_plane *plane = pst->plane; + struct intel_plane *intel_plane = to_intel_plane(plane); + + if (!pst->dirty) + continue; + + if (pst->coords.visible && plane->crtc == crtc) + intel_plane->update_plane(plane, plane->fb, &pst->coords); + else if (!pst->coords.visible && pst->old.crtc == crtc) + intel_plane->disable_plane(plane); + } + } + + /* don't restore the old state in end() */ + s->dirty = false; + s->restore_state = false; + + return 0; +} + +static void restore_state(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + struct drm_plane *plane; + + i = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + *connector = s->saved_connectors[i++]; + i = 0; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + *encoder = s->saved_encoders[i++]; + i = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + intel_crtc->base = s->saved_crtcs[i].base; + intel_crtc->cursor_bo = s->saved_crtcs[i].cursor_bo; + intel_crtc->cursor_addr = s->saved_crtcs[i].cursor_addr; + intel_crtc->cursor_handle = s->saved_crtcs[i].cursor_handle; + intel_crtc->cursor_x = s->saved_crtcs[i].cursor_x; + intel_crtc->cursor_y = s->saved_crtcs[i].cursor_y; + intel_crtc->cursor_width = s->saved_crtcs[i].cursor_width; + intel_crtc->cursor_height = s->saved_crtcs[i].cursor_height; + intel_crtc->cursor_visible = s->saved_crtcs[i].cursor_visible; + + i++; + } + i = 0; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) + *plane = s->saved_planes[i++]; + + /* FIXME props etc. */ + + /* was the hardware state clobbered? */ + if (s->restore_hw) + apply_config(dev, s); +} + +static int intel_atomic_set(struct drm_device *dev, void *state, + struct drm_mode_object *obj, + struct drm_property *prop, + uint64_t value, void *blob_data) +{ + struct intel_atomic_state *s = state; + int ret = -EINVAL; + + switch (obj->type) { + case DRM_MODE_OBJECT_PLANE: + ret = plane_set(s, get_plane_state(dev, s, obj_to_plane(obj)), prop, value); + break; + case DRM_MODE_OBJECT_CRTC: + ret = crtc_set(s, get_crtc_state(dev, s, obj_to_crtc(obj)), prop, value, blob_data); + break; + default: + break; + } + + kfree(blob_data); + + return ret; +} + +int intel_check_plane(const struct drm_plane *plane, + const struct drm_crtc *crtc, + const struct drm_framebuffer *fb, + struct intel_plane_coords *st); + +static void dirty_planes(const struct drm_device *dev, + struct intel_atomic_state *state, + const struct drm_crtc *crtc) +{ + int i; + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *s = &state->plane[i]; + + if (s->plane->crtc == crtc) + s->dirty = true; + } +} + +static int check_crtc(struct intel_crtc_state *s) +{ + struct drm_crtc *crtc = s->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_encoder *encoder; + struct drm_framebuffer *fb = crtc->fb; + int ret; + + /* must have a fb and connectors if we have a mode, and vice versa */ + if (crtc->enabled) { + if (!fb) + return -EINVAL; + if (!drm_helper_crtc_in_use(crtc)) + return -EINVAL; + } else { + if (fb) + return -EINVAL; + if (drm_helper_crtc_in_use(crtc)) + return -EINVAL; + } + + if (crtc->enabled) { + if (crtc->mode.hdisplay > fb->width || + crtc->mode.vdisplay > fb->height || + crtc->x > fb->width - crtc->mode.hdisplay || + crtc->y > fb->height - crtc->mode.vdisplay) + return -ENOSPC; + } + + if (fb) { + /* FIXME refactor and check */ + switch (fb->pixel_format) { + case DRM_FORMAT_C8: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: + break; + default: + return -EINVAL; + } + } + + if (intel_crtc->cursor_visible && + (intel_crtc->cursor_width != 64 || + intel_crtc->cursor_height != 64)) { + return -EINVAL; + } + + if (!crtc->enabled || !s->mode_dirty) + return 0; + + crtc->hwmode = crtc->mode; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + + if (encoder->crtc != crtc) + continue; + + if (!encoder_funcs->mode_fixup(encoder, &crtc->mode, &crtc->hwmode)) + return -EINVAL; + } + + if (!intel_crtc_mode_fixup(crtc, &crtc->mode, &crtc->hwmode)) + return -EINVAL; + + ret = intel_check_clock(crtc, &crtc->hwmode); + if (ret) + return ret; + + return 0; +} + +static int intel_atomic_check(struct drm_device *dev, void *state) +{ + struct intel_atomic_state *s = state; + int ret; + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + crtc_compute_dirty(s, st); + } + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_plane_state *st = &s->plane[i]; + + plane_compute_dirty(s, st); + } + + if (!s->dirty) + return 0; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + if (!st->fb_dirty && !st->mode_dirty && !st->cursor_dirty) + continue; + + if (st->mode_dirty && s->flags & DRM_MODE_ATOMIC_NONBLOCK) + return -EAGAIN; + + ret = check_crtc(st); + if (ret) + return ret; + + /* + * Mark all planes on this CRTC as dirty if the active video + * area changed so that the planes will get reclipped correctly. + * + * Also any modesetting will disable+enable the pipe, so the + * plane needs to be re-enabled afterwards too. + * TODO: there's no need to redo the clipping in such cases + * if the computed values were cached, the could be commited + * directly. + */ + if (st->active_dirty || st->mode_dirty) + dirty_planes(dev, s, st->crtc); + } + + /* check for conflicts in encoder/connector assignment */ + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + int j; + + for (j = i + 1; j < dev->mode_config.num_crtc; j++) { + struct intel_crtc_state *st2 = &s->crtc[j]; + + if (st->connectors_bitmask & st2->connectors_bitmask) + return -EINVAL; + + if (st->encoders_bitmask & st2->encoders_bitmask) + return -EINVAL; + } + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + const struct drm_plane *plane = st->plane; + + if (!st->dirty) + continue; + + st->coords.crtc_x = plane->crtc_x; + st->coords.crtc_y = plane->crtc_y; + st->coords.crtc_w = plane->crtc_w; + st->coords.crtc_h = plane->crtc_h; + + st->coords.src_x = plane->src_x; + st->coords.src_y = plane->src_y; + st->coords.src_w = plane->src_w; + st->coords.src_h = plane->src_h; + + ret = intel_check_plane(plane, plane->crtc, plane->fb, &st->coords); + if (ret) + return ret; + } + + return 0; +} + +static void update_plane_props(struct drm_plane *plane) +{ + struct drm_mode_object *obj = &plane->base; + + drm_object_property_set_value(obj, prop_src_x, plane->src_x); + drm_object_property_set_value(obj, prop_src_y, plane->src_y); + drm_object_property_set_value(obj, prop_src_w, plane->src_w); + drm_object_property_set_value(obj, prop_src_h, plane->src_h); + + drm_object_property_set_value(obj, prop_crtc_x, plane->crtc_x); + drm_object_property_set_value(obj, prop_crtc_y, plane->crtc_y); + drm_object_property_set_value(obj, prop_crtc_w, plane->crtc_w); + drm_object_property_set_value(obj, prop_crtc_h, plane->crtc_h); + + drm_object_property_set_value(obj, prop_fb_id, plane->fb ? plane->fb->base.id : 0); + drm_object_property_set_value(obj, prop_crtc_id, plane->crtc ? plane->crtc->base.id : 0); +} + +static int update_prop_connector_ids(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_connector *connector; + uint64_t value = 0; + int i = 0; + uint32_t connector_ids[dev->mode_config.num_connector]; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && connector->encoder->crtc == crtc) + connector_ids[i++] = connector->base.id; + } + + if (i) { + drm_property_blob_replace_data(crtc->connector_ids_blob, + i * sizeof connector_ids[0], connector_ids); + value = crtc->connector_ids_blob->base.id; + } else + drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, prop_connector_ids, value); + + return 0; +} + +static int update_prop_mode(struct drm_crtc *crtc) +{ + uint64_t value = 0; + + if (crtc->enabled) { + struct drm_mode_modeinfo umode; + + drm_crtc_convert_to_umode(&umode, &crtc->mode); + drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, &umode); + value = crtc->mode_blob->base.id; + } else + drm_property_blob_replace_data(crtc->mode_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, prop_mode, value); + + return 0; +} + +static void update_crtc_props(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + drm_object_property_set_value(obj, prop_src_x, crtc->x); + drm_object_property_set_value(obj, prop_src_y, crtc->y); + + drm_object_property_set_value(obj, prop_fb_id, crtc->fb ? crtc->fb->base.id : 0); + + drm_object_property_set_value(obj, prop_cursor_id, intel_crtc->cursor_handle); + drm_object_property_set_value(obj, prop_cursor_x, intel_crtc->cursor_x); + drm_object_property_set_value(obj, prop_cursor_y, intel_crtc->cursor_y); + drm_object_property_set_value(obj, prop_cursor_w, intel_crtc->cursor_width); + drm_object_property_set_value(obj, prop_cursor_h, intel_crtc->cursor_height); + + update_prop_mode(crtc); + update_prop_connector_ids(crtc); +} + +static void update_props(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + if (!st->fb_dirty && !st->mode_dirty) + continue; + + update_crtc_props(st->crtc); + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + + if (!st->dirty) + continue; + + update_plane_props(st->plane); + } +} + +static void update_crtc(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + + /* FIXME is this OK? */ + if (st->fb_dirty && !st->mode_dirty) { + mutex_lock(&dev->struct_mutex); + intel_update_fbc(dev); + mutex_unlock(&dev->struct_mutex); + } + + if (st->mode_dirty) { + drm_calc_timestamping_constants(crtc); + intel_crtc_update_sarea(crtc, crtc->enabled); + } + + if (st->fb_dirty) + intel_crtc_update_sarea_pos(crtc, crtc->x, crtc->y); + } +} + +static int intel_atomic_commit(struct drm_device *dev, void *state) +{ + struct intel_atomic_state *s = state; + int ret; + + if (s->flags & DRM_MODE_ATOMIC_NONBLOCK) + return -ENOSYS; + + if (s->flags & DRM_MODE_ATOMIC_EVENT) + return -ENOSYS; + + if (!s->dirty) + return 0; + + ret = pin_fbs(dev, s); + if (ret) + return ret; + + ret = pin_cursors(dev, s); + if (ret) + return ret; + + /* apply in a blocking manner */ + ret = apply_config(dev, s); + if (ret) { + unpin_cursors(dev, s); + unpin_fbs(dev, s); + s->restore_hw = true; + return ret; + } + + unpin_old_cursors(dev, s); + unpin_old_fbs(dev, s); + + update_plane_obj(dev, s); + + update_crtc(dev, s); + + update_props(dev, s); + + return 0; +} + +static void intel_atomic_end(struct drm_device *dev, void *state) +{ + struct intel_atomic_state *s = state; + + /* restore the state of all objects */ + if (s->restore_state) + restore_state(dev, state); + + kfree(state); +} + +static const struct drm_atomic_funcs intel_atomic_funcs = { + .begin = intel_atomic_begin, + .set = intel_atomic_set, + .check = intel_atomic_check, + .commit = intel_atomic_commit, + .end = intel_atomic_end, +}; + +static struct { + struct drm_property **prop; + const char *name; + uint64_t min; + uint64_t max; +} props[] = { + { &prop_src_x, "SRC_X", 0, UINT_MAX }, + { &prop_src_y, "SRC_Y", 0, UINT_MAX }, + { &prop_src_w, "SRC_W", 0, UINT_MAX }, + { &prop_src_h, "SRC_H", 0, UINT_MAX }, + + { &prop_crtc_x, "CRTC_X", INT_MIN, INT_MAX }, + { &prop_crtc_y, "CRTC_Y", INT_MIN, INT_MAX }, + { &prop_crtc_w, "CRTC_W", 0, INT_MAX }, + { &prop_crtc_h, "CRTC_H", 0, INT_MAX }, + + { &prop_fb_id, "FB_ID", 0, UINT_MAX }, + { &prop_crtc_id, "CRTC_ID", 0, UINT_MAX }, + + { &prop_cursor_id, "CURSOR_ID", 0, UINT_MAX }, + { &prop_cursor_w, "CURSOR_W", 0, UINT_MAX }, + { &prop_cursor_h, "CURSOR_H", 0, UINT_MAX }, + { &prop_cursor_x, "CURSOR_X", INT_MIN, INT_MAX }, + { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX }, +}; + +int intel_atomic_init(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_plane *plane; + int ret = -ENOMEM; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + *props[i].prop = + drm_property_create_range(dev, 0, props[i].name, + props[i].min, props[i].max); + if (!*props[i].prop) + goto out; + } + + /* FIXME create special object ID list property type? */ + prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + if (!prop_connector_ids) + goto out; + + prop_mode = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!prop_mode) + goto out; + + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + struct drm_mode_object *obj = &plane->base; + + drm_object_attach_property(obj, prop_src_x, 0); + drm_object_attach_property(obj, prop_src_y, 0); + drm_object_attach_property(obj, prop_src_w, 0); + drm_object_attach_property(obj, prop_src_h, 0); + + drm_object_attach_property(obj, prop_crtc_x, 0); + drm_object_attach_property(obj, prop_crtc_y, 0); + drm_object_attach_property(obj, prop_crtc_w, 0); + drm_object_attach_property(obj, prop_crtc_h, 0); + + drm_object_attach_property(obj, prop_fb_id, 0); + drm_object_attach_property(obj, prop_crtc_id, 0); + + update_plane_props(plane); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_mode_object *obj = &crtc->base; + + drm_object_attach_property(obj, prop_src_x, 0); + drm_object_attach_property(obj, prop_src_y, 0); + + drm_object_attach_property(obj, prop_fb_id, 0); + drm_object_attach_property(obj, prop_mode, 0); + drm_object_attach_property(obj, prop_connector_ids, 0); + + drm_object_attach_property(obj, prop_cursor_id, 0); + drm_object_attach_property(obj, prop_cursor_x, 0); + drm_object_attach_property(obj, prop_cursor_y, 0); + drm_object_attach_property(obj, prop_cursor_w, 0); + drm_object_attach_property(obj, prop_cursor_h, 0); + + crtc->mode_blob = drm_property_create_blob(dev, 0, sizeof(struct drm_mode_modeinfo), NULL); + if (!crtc->mode_blob) + goto out; + + crtc->connector_ids_blob = drm_property_create_blob(dev, 0, + dev->mode_config.num_connector * sizeof(uint32_t), NULL); + if (!crtc->connector_ids_blob) + goto out; + + update_crtc_props(crtc); + } + + dev->driver->atomic_funcs = &intel_atomic_funcs; + + return 0; + + out: + drm_property_destroy(dev, prop_mode); + drm_property_destroy(dev, prop_connector_ids); + + while (i--) + drm_property_destroy(dev, *props[i].prop); + + return ret; +} + +void intel_atomic_fini(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + drm_property_destroy_blob(dev, crtc->mode_blob); + drm_property_destroy_blob(dev, crtc->connector_ids_blob); + } + + drm_property_destroy(dev, prop_connector_ids); + drm_property_destroy(dev, prop_mode); + drm_property_destroy(dev, prop_crtc_id); + drm_property_destroy(dev, prop_fb_id); + + drm_property_destroy(dev, prop_crtc_h); + drm_property_destroy(dev, prop_crtc_w); + drm_property_destroy(dev, prop_crtc_y); + drm_property_destroy(dev, prop_crtc_x); + + drm_property_destroy(dev, prop_src_h); + drm_property_destroy(dev, prop_src_w); + drm_property_destroy(dev, prop_src_y); + drm_property_destroy(dev, prop_src_x); +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c0db749..62e2a56 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -8409,6 +8409,9 @@ static void intel_setup_outputs(struct drm_device *dev) intel_encoder_clones(encoder); }
+ /* FIXME error handling */ + intel_atomic_init(dev); + intel_init_pch_refclk(dev);
drm_helper_move_panel_connectors_to_head(dev); @@ -9187,6 +9190,8 @@ void intel_modeset_cleanup(struct drm_device *dev) /* flush any delayed tasks or pending work */ flush_scheduled_work();
+ intel_atomic_fini(dev); + drm_mode_config_cleanup(dev); }
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 05afdd1..103d104 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -720,4 +720,7 @@ extern bool intel_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
+extern int intel_atomic_init(struct drm_device *dev); +extern void intel_atomic_fini(struct drm_device *dev); + #endif /* __INTEL_DRV_H__ */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 77f08a3..897c110 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -509,6 +509,7 @@ struct drm_encoder { struct drm_crtc *crtc; const struct drm_encoder_funcs *funcs; void *helper_private; + struct drm_crtc *old_crtc; };
enum drm_connector_force { @@ -614,6 +615,7 @@ struct drm_connector { int audio_latency[2]; int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ unsigned bad_edid_counter; + struct drm_encoder *old_encoder; };
/**
From: Ville Syrjälä ville.syrjala@linux.intel.com
Send completion events when the atomic modesetting operations has finished succesfully.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 195 ++++++++++++++++++++++++++++++++++- 1 files changed, 192 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 4899f8c..3adb140 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -53,6 +53,7 @@ struct intel_plane_state { bool dirty; bool pinned; bool changed; + struct drm_pending_atomic_event *event;
struct { struct drm_crtc *crtc; @@ -74,6 +75,7 @@ struct intel_crtc_state { unsigned long connectors_bitmask; unsigned long encoders_bitmask; bool changed; + struct drm_pending_atomic_event *event;
struct { bool enabled; @@ -922,6 +924,111 @@ int intel_commit_plane_nopin(struct drm_plane *plane, struct drm_framebuffer *fb, const struct intel_plane_coords *coords);
+static struct drm_pending_atomic_event *alloc_event(struct drm_device *dev, + struct drm_file *file_priv, + uint64_t user_data) +{ + struct drm_pending_atomic_event *e; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (file_priv->event_space < sizeof e->event) { + spin_unlock_irqrestore(&dev->event_lock, flags); + return ERR_PTR(-ENOSPC); + } + + file_priv->event_space -= sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + e = kzalloc(sizeof *e, GFP_KERNEL); + if (!e) { + spin_lock_irqsave(&dev->event_lock, flags); + file_priv->event_space += sizeof e->event; + spin_unlock_irqrestore(&dev->event_lock, flags); + + return ERR_PTR(-ENOMEM); + } + + e->event.base.type = DRM_EVENT_ATOMIC_COMPLETE; + e->event.base.length = sizeof e->event; + e->event.user_data = user_data; + e->base.event = &e->event.base; + e->base.file_priv = file_priv; + e->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + + return e; +} + +static void free_event(struct drm_pending_atomic_event *e) +{ + e->base.file_priv->event_space += sizeof e->event; + kfree(e); +} + +static void queue_event(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_pending_atomic_event *e) +{ + struct timeval tvbl; + + if (crtc) { + int pipe = to_intel_crtc(crtc)->pipe; + + /* FIXME this is wrong for flips that are completed not at vblank */ + e->event.sequence = drm_vblank_count_and_time(dev, pipe, &tvbl); + e->event.tv_sec = tvbl.tv_sec; + e->event.tv_usec = tvbl.tv_usec; + } else { + e->event.sequence = 0; + e->event.tv_sec = 0; + e->event.tv_usec = 0; + } + + list_add_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); +} + +static void queue_remaining_events(struct drm_device *dev, struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + if (st->event) { + if (st->old.fb) + st->event->event.old_fb_id = st->old.fb->base.id; + + spin_lock_irq(&dev->event_lock); + queue_event(dev, st->crtc, st->event); + spin_unlock_irq(&dev->event_lock); + + st->event = NULL; + } + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_crtc *crtc; + + if (!st->event) + continue; + + crtc = st->plane->crtc; + if (!crtc) + crtc = st->old.crtc; + + if (st->old.fb) + st->event->event.old_fb_id = st->old.fb->base.id; + + spin_lock_irq(&dev->event_lock); + queue_event(dev, crtc, st->event); + spin_unlock_irq(&dev->event_lock); + + st->event = NULL; + } +} + static void swap_old_new(struct drm_device *dev, struct intel_atomic_state *s) { @@ -1426,6 +1533,73 @@ static void update_crtc(struct drm_device *dev, } }
+static int alloc_flip_data(struct drm_device *dev, struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + if (st->changed && s->flags & DRM_MODE_ATOMIC_EVENT) { + struct drm_pending_atomic_event *e; + + e = alloc_event(dev, s->file, s->user_data); + if (IS_ERR(e)) + return PTR_ERR(e); + + e->event.obj_id = st->crtc->base.id; + + st->event = e; + } + } + + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + + if (st->changed && s->flags & DRM_MODE_ATOMIC_EVENT) { + struct drm_pending_atomic_event *e; + + e = alloc_event(dev, s->file, s->user_data); + if (IS_ERR(e)) + return PTR_ERR(e); + + e->event.obj_id = st->plane->base.id; + + st->event = e; + } + } + + return 0; +} + +static void free_flip_data(struct drm_device *dev, struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + + if (st->event) { + spin_lock_irq(&dev->event_lock); + free_event(st->event); + spin_unlock_irq(&dev->event_lock); + st->event = NULL; + } + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + + if (st->event) { + spin_lock_irq(&dev->event_lock); + free_event(st->event); + spin_unlock_irq(&dev->event_lock); + st->event = NULL; + } + } +} + static int intel_atomic_commit(struct drm_device *dev, void *state) { struct intel_atomic_state *s = state; @@ -1434,12 +1608,13 @@ static int intel_atomic_commit(struct drm_device *dev, void *state) if (s->flags & DRM_MODE_ATOMIC_NONBLOCK) return -ENOSYS;
- if (s->flags & DRM_MODE_ATOMIC_EVENT) - return -ENOSYS; - if (!s->dirty) return 0;
+ ret = alloc_flip_data(dev, s); + if (ret) + return ret; + ret = pin_fbs(dev, s); if (ret) return ret; @@ -1460,6 +1635,17 @@ static int intel_atomic_commit(struct drm_device *dev, void *state) unpin_old_cursors(dev, s); unpin_old_fbs(dev, s);
+ /* + * Either we took the blocking code path, or perhaps the state of + * some objects didn't actually change? Nonetheless the user wanted + * events for all objects he touched, so queue up any events that + * are still pending. + * + * FIXME this needs more work. If the previous flip is still pending + * we shouldn't send this event until that flip completes. + */ + queue_remaining_events(dev, s); + update_plane_obj(dev, s);
update_crtc(dev, s); @@ -1473,6 +1659,9 @@ static void intel_atomic_end(struct drm_device *dev, void *state) { struct intel_atomic_state *s = state;
+ /* don't send events when restoring old state */ + free_flip_data(dev, state); + /* restore the state of all objects */ if (s->restore_state) restore_state(dev, state);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add support for the DRM_MODE_ATOMIC_NONBLOCK flag.
The drm_flip helper provides the necessary logic to track the progress of the flips. drm_flip is driven by a few extra calls from the interrupt handling and crtc_disable code paths.
Since the hardware doesn't provide inter-plane synchronization, some extra software magic is required to avoid flips for multiple planes ending up on the wrong sides of the vblank leading edge.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_dma.c | 3 + drivers/gpu/drm/i915/i915_drv.h | 4 + drivers/gpu/drm/i915/i915_irq.c | 16 +- drivers/gpu/drm/i915/intel_atomic.c | 622 +++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_display.c | 2 + drivers/gpu/drm/i915/intel_drv.h | 7 + 6 files changed, 637 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 392c9a1..8501bf3 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1764,6 +1764,8 @@ int i915_driver_open(struct drm_device *dev, struct drm_file *file)
idr_init(&file_priv->context_idr);
+ INIT_LIST_HEAD(&file_priv->pending_flips); + return 0; }
@@ -1804,6 +1806,7 @@ void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) { i915_gem_context_close(dev, file_priv); i915_gem_release(dev, file_priv); + intel_atomic_free_events(dev, file_priv); }
void i915_driver_postclose(struct drm_device *dev, struct drm_file *file) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 66b3b64..0bafe7f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -38,6 +38,7 @@ #include <linux/io-mapping.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> +#include <drm/drm_flip.h> #include <drm/intel-gtt.h> #include <linux/backlight.h> #include <linux/intel-iommu.h> @@ -957,6 +958,8 @@ typedef struct drm_i915_private { /* Old dri1 support infrastructure, beware the dragons ya fools entering * here! */ struct i915_dri1_state dri1; + + struct drm_flip_driver flip_driver; } drm_i915_private_t;
/* Iterate over initialised rings */ @@ -1179,6 +1182,7 @@ struct drm_i915_file_private { struct list_head request_list; } mm; struct idr context_idr; + struct list_head pending_flips; };
#define INTEL_INFO(dev) (((struct drm_i915_private *) (dev)->dev_private)->info) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d02e022..430f201 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -581,8 +581,10 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg) spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
for_each_pipe(pipe) { - if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) + if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS) { drm_handle_vblank(dev, pipe); + intel_atomic_handle_vblank(dev, pipe); + }
if (pipe_stats[pipe] & PLANE_FLIPDONE_INT_STATUS_VLV) { intel_prepare_page_flip(dev, pipe); @@ -727,8 +729,10 @@ static irqreturn_t ivybridge_irq_handler(int irq, void *arg) intel_opregion_gse_intr(dev);
for (i = 0; i < 3; i++) { - if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) + if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) { drm_handle_vblank(dev, i); + intel_atomic_handle_vblank(dev, i); + } if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) { intel_prepare_page_flip(dev, i); intel_finish_page_flip_plane(dev, i); @@ -807,11 +811,15 @@ static irqreturn_t ironlake_irq_handler(int irq, void *arg) if (de_iir & DE_GSE) intel_opregion_gse_intr(dev);
- if (de_iir & DE_PIPEA_VBLANK) + if (de_iir & DE_PIPEA_VBLANK) { drm_handle_vblank(dev, 0); + intel_atomic_handle_vblank(dev, 0); + }
- if (de_iir & DE_PIPEB_VBLANK) + if (de_iir & DE_PIPEB_VBLANK) { drm_handle_vblank(dev, 1); + intel_atomic_handle_vblank(dev, 1); + }
if (de_iir & DE_PLANEA_FLIP_DONE) { intel_prepare_page_flip(dev, 0); diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 3adb140..238a843 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -26,6 +26,7 @@
#include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <drm/drm_flip.h>
#include "intel_drv.h"
@@ -47,6 +48,20 @@ static struct drm_property *prop_cursor_y; static struct drm_property *prop_cursor_w; static struct drm_property *prop_cursor_h;
+struct intel_flip { + struct drm_flip base; + u32 vbl_count; + bool vblank_ref; + bool has_cursor; + struct drm_crtc *crtc; + struct drm_plane *plane; + struct drm_i915_gem_object *old_bo; + struct drm_i915_gem_object *old_cursor_bo; + struct drm_pending_atomic_event *event; + uint32_t old_fb_id; + struct list_head pending_head; +}; + struct intel_plane_state { struct drm_plane *plane; struct intel_plane_coords coords; @@ -54,6 +69,7 @@ struct intel_plane_state { bool pinned; bool changed; struct drm_pending_atomic_event *event; + struct intel_flip *flip;
struct { struct drm_crtc *crtc; @@ -76,6 +92,7 @@ struct intel_crtc_state { unsigned long encoders_bitmask; bool changed; struct drm_pending_atomic_event *event; + struct intel_flip *flip;
struct { bool enabled; @@ -966,6 +983,22 @@ static void free_event(struct drm_pending_atomic_event *e) kfree(e); }
+void intel_atomic_free_events(struct drm_device *dev, struct drm_file *file) +{ + struct drm_i915_file_private *file_priv = file->driver_priv; + struct intel_flip *intel_flip, *next; + + spin_lock_irq(&dev->event_lock); + + list_for_each_entry_safe(intel_flip, next, &file_priv->pending_flips, pending_head) { + free_event(intel_flip->event); + intel_flip->event = NULL; + list_del_init(&intel_flip->pending_head); + } + + spin_unlock_irq(&dev->event_lock); +} + static void queue_event(struct drm_device *dev, struct drm_crtc *crtc, struct drm_pending_atomic_event *e) { @@ -1533,6 +1566,60 @@ static void update_crtc(struct drm_device *dev, } }
+static void atomic_pipe_commit(struct drm_device *dev, + struct intel_atomic_state *state, + int pipe); + +static int apply_nonblocking(struct drm_device *dev, struct intel_atomic_state *s) +{ + struct intel_crtc *intel_crtc; + int i; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct intel_crtc *intel_crtc = to_intel_crtc(st->crtc); + struct drm_i915_gem_object *obj; + + if (!st->old.fb) + continue; + + obj = to_intel_framebuffer(st->old.fb)->obj; + + /* Only one bit per plane in pending_flips */ + if (atomic_read(&obj->pending_flip) & (1 << intel_crtc->plane)) + return -EBUSY; + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct intel_plane *intel_plane = to_intel_plane(st->plane); + struct drm_i915_gem_object *obj; + + if (!st->old.fb) + continue; + + obj = to_intel_framebuffer(st->old.fb)->obj; + + if (!st->old.fb) + continue; + + obj = to_intel_framebuffer(st->old.fb)->obj; + + /* Only one bit per plane in pending_flips */ + if (atomic_read(&obj->pending_flip) & (1 << (16 + intel_plane->pipe))) + return -EBUSY; + } + + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) + atomic_pipe_commit(dev, s, intel_crtc->pipe); + + /* don't restore the old state in end() */ + s->dirty = false; + s->restore_state = false; + + return 0; +} + static int alloc_flip_data(struct drm_device *dev, struct intel_atomic_state *s) { int i; @@ -1551,6 +1638,13 @@ static int alloc_flip_data(struct drm_device *dev, struct intel_atomic_state *s)
st->event = e; } + + if (!st->fb_dirty && !st->mode_dirty && !st->cursor_dirty) + continue; + + st->flip = kzalloc(sizeof *st->flip, GFP_KERNEL); + if (!st->flip) + return -ENOMEM; }
@@ -1568,6 +1662,13 @@ static int alloc_flip_data(struct drm_device *dev, struct intel_atomic_state *s)
st->event = e; } + + if (!st->dirty) + continue; + + st->flip = kzalloc(sizeof *st->flip, GFP_KERNEL); + if (!st->flip) + return -ENOMEM; }
return 0; @@ -1586,6 +1687,9 @@ static void free_flip_data(struct drm_device *dev, struct intel_atomic_state *s) spin_unlock_irq(&dev->event_lock); st->event = NULL; } + + kfree(st->flip); + st->flip = NULL; }
for (i = 0; i < dev->mode_config.num_plane; i++) { @@ -1597,6 +1701,9 @@ static void free_flip_data(struct drm_device *dev, struct intel_atomic_state *s) spin_unlock_irq(&dev->event_lock); st->event = NULL; } + + kfree(st->flip); + st->flip = NULL; } }
@@ -1605,9 +1712,6 @@ static int intel_atomic_commit(struct drm_device *dev, void *state) struct intel_atomic_state *s = state; int ret;
- if (s->flags & DRM_MODE_ATOMIC_NONBLOCK) - return -ENOSYS; - if (!s->dirty) return 0;
@@ -1623,17 +1727,27 @@ static int intel_atomic_commit(struct drm_device *dev, void *state) if (ret) return ret;
- /* apply in a blocking manner */ - ret = apply_config(dev, s); - if (ret) { - unpin_cursors(dev, s); - unpin_fbs(dev, s); - s->restore_hw = true; - return ret; - } + /* try to apply in a non blocking manner */ + if (s->flags & DRM_MODE_ATOMIC_NONBLOCK) { + ret = apply_nonblocking(dev, s); + if (ret) { + unpin_cursors(dev, s); + unpin_fbs(dev, s); + return ret; + } + } else { + /* apply in a blocking manner */ + ret = apply_config(dev, s); + if (ret) { + unpin_cursors(dev, s); + unpin_fbs(dev, s); + s->restore_hw = true; + return ret; + }
- unpin_old_cursors(dev, s); - unpin_old_fbs(dev, s); + unpin_old_cursors(dev, s); + unpin_old_fbs(dev, s); + }
/* * Either we took the blocking code path, or perhaps the state of @@ -1703,6 +1817,9 @@ static struct { { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX }, };
+static void intel_flip_init(struct drm_device *dev); +static void intel_flip_fini(struct drm_device *dev); + int intel_atomic_init(struct drm_device *dev) { struct drm_crtc *crtc; @@ -1776,6 +1893,8 @@ int intel_atomic_init(struct drm_device *dev)
dev->driver->atomic_funcs = &intel_atomic_funcs;
+ intel_flip_init(dev); + return 0;
out: @@ -1792,6 +1911,8 @@ void intel_atomic_fini(struct drm_device *dev) { struct drm_crtc *crtc;
+ intel_flip_fini(dev); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { drm_property_destroy_blob(dev, crtc->mode_blob); drm_property_destroy_blob(dev, crtc->connector_ids_blob); @@ -1812,3 +1933,478 @@ void intel_atomic_fini(struct drm_device *dev) drm_property_destroy(dev, prop_src_y); drm_property_destroy(dev, prop_src_x); } + +enum { + /* somwehat arbitrary value */ + INTEL_VBL_CNT_TIMEOUT = 5, +}; + +static void intel_flip_complete(struct drm_flip *flip) +{ + struct intel_flip *intel_flip = + container_of(flip, struct intel_flip, base); + struct drm_device *dev = intel_flip->crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = intel_flip->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + unsigned long flags; + + spin_lock_irqsave(&dev->event_lock, flags); + + if (intel_flip->event) { + list_del_init(&intel_flip->pending_head); + intel_flip->event->event.old_fb_id = intel_flip->old_fb_id; + queue_event(dev, crtc, intel_flip->event); + } + + spin_unlock_irqrestore(&dev->event_lock, flags); + + if (intel_flip->vblank_ref) + drm_vblank_put(dev, pipe); + + /* Possibly allow rendering to old_bo again */ + if (intel_flip->old_bo) { + if (intel_flip->plane) { + struct intel_plane *intel_plane = to_intel_plane(intel_flip->plane); + atomic_clear_mask(1 << (16 + intel_plane->pipe), &intel_flip->old_bo->pending_flip.counter); + } else + atomic_clear_mask(1 << intel_crtc->plane, &intel_flip->old_bo->pending_flip.counter); + + if (atomic_read(&intel_flip->old_bo->pending_flip) == 0) + wake_up(&dev_priv->pending_flip_queue); + } +} + +static void intel_flip_finish(struct drm_flip *flip) +{ + struct intel_flip *intel_flip = + container_of(flip, struct intel_flip, base); + struct drm_device *dev = intel_flip->crtc->dev; + + if (intel_flip->old_bo) { + mutex_lock(&dev->struct_mutex); + + intel_unpin_fb_obj(intel_flip->old_bo); + + drm_gem_object_unreference(&intel_flip->old_bo->base); + + mutex_unlock(&dev->struct_mutex); + } + + if (intel_flip->old_cursor_bo) + intel_crtc_cursor_bo_unref(intel_flip->crtc, intel_flip->old_cursor_bo); +} + +static void intel_flip_cleanup(struct drm_flip *flip) +{ + struct intel_flip *intel_flip = + container_of(flip, struct intel_flip, base); + + kfree(intel_flip); +} + +static void intel_flip_driver_flush(struct drm_flip_driver *driver) +{ + struct drm_i915_private *dev_priv = + container_of(driver, struct drm_i915_private, flip_driver); + + /* Flush posted writes */ + I915_READ(PIPEDSL(PIPE_A)); +} + +static bool intel_have_new_frmcount(struct drm_device *dev) +{ + return IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5; +} + +static u32 get_vbl_count(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + + if (intel_have_new_frmcount(dev)) { + return I915_READ(PIPE_FRMCOUNT_GM45(pipe)); + } else { + u32 high, low1, low2, dsl; + unsigned int timeout = 0; + + /* + * FIXME check where the frame counter increments, and if + * it happens in the middle of some line, take appropriate + * measures to get a sensible reading. + */ + + /* All reads must be satisfied during the same frame */ + do { + low1 = I915_READ(PIPEFRAMEPIXEL(pipe)) >> PIPE_FRAME_LOW_SHIFT; + high = I915_READ(PIPEFRAME(pipe)) << 8; + dsl = I915_READ(PIPEDSL(pipe)); + low2 = I915_READ(PIPEFRAMEPIXEL(pipe)) >> PIPE_FRAME_LOW_SHIFT; + } while (low1 != low2 && timeout++ < INTEL_VBL_CNT_TIMEOUT); + + if (timeout >= INTEL_VBL_CNT_TIMEOUT) + dev_warn(dev->dev, + "Timed out while determining VBL count for pipe %d\n", pipe); + + return ((high | low2) + + ((dsl >= crtc->hwmode.crtc_vdisplay) && + (dsl < crtc->hwmode.crtc_vtotal - 1))) & 0xffffff; + } +} + +static unsigned int usecs_to_scanlines(struct drm_crtc *crtc, + unsigned int usecs) +{ + /* paranoia */ + if (!crtc->hwmode.crtc_htotal) + return 1; + + return DIV_ROUND_UP(usecs * crtc->hwmode.clock, + 1000 * crtc->hwmode.crtc_htotal); +} + +static void intel_pipe_vblank_evade(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + /* FIXME needs to be calibrated sensibly */ + u32 min = crtc->hwmode.crtc_vdisplay - usecs_to_scanlines(crtc, 50); + u32 max = crtc->hwmode.crtc_vdisplay - 1; + long timeout = msecs_to_jiffies(3); + u32 val; + + bool vblank_ref = drm_vblank_get(dev, pipe) == 0; + + intel_crtc->vbl_received = false; + + val = I915_READ(PIPEDSL(pipe)); + + while (val >= min && val <= max && timeout > 0) { + local_irq_enable(); + + timeout = wait_event_timeout(intel_crtc->vbl_wait, + intel_crtc->vbl_received, + timeout); + + local_irq_disable(); + + intel_crtc->vbl_received = false; + + val = I915_READ(PIPEDSL(pipe)); + } + + if (vblank_ref) + drm_vblank_put(dev, pipe); + + if (val >= min && val <= max) + dev_warn(dev->dev, + "Page flipping close to vblank start (DSL=%u, VBL=%u)\n", + val, crtc->hwmode.crtc_vdisplay); +} + +static bool vbl_count_after_eq_new(u32 a, u32 b) +{ + return !((a - b) & 0x80000000); +} + +static bool vbl_count_after_eq(u32 a, u32 b) +{ + return !((a - b) & 0x800000); +} + +static bool intel_vbl_check(struct drm_flip *pending_flip, u32 vbl_count) +{ + struct intel_flip *old_intel_flip = + container_of(pending_flip, struct intel_flip, base); + struct drm_device *dev = old_intel_flip->crtc->dev; + + if (intel_have_new_frmcount(dev)) + return vbl_count_after_eq_new(vbl_count, old_intel_flip->vbl_count); + else + return vbl_count_after_eq(vbl_count, old_intel_flip->vbl_count); +} + +static void intel_flip_prepare(struct drm_flip *flip) +{ + struct intel_flip *intel_flip = + container_of(flip, struct intel_flip, base); + + if (intel_flip->plane) { + struct drm_plane *plane = intel_flip->plane; + struct intel_plane *intel_plane = to_intel_plane(plane); + + intel_plane->prepare(plane); + } +} + +static bool intel_flip_flip(struct drm_flip *flip, + struct drm_flip *pending_flip) +{ + struct intel_flip *intel_flip = container_of(flip, struct intel_flip, base); + struct drm_crtc *crtc = intel_flip->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_device *dev = crtc->dev; + int pipe = intel_crtc->pipe; + u32 vbl_count; + + intel_flip->vblank_ref = drm_vblank_get(dev, pipe) == 0; + + vbl_count = get_vbl_count(crtc); + + /* arm all the double buffer registers */ + if (intel_flip->plane) { + struct drm_plane *plane = intel_flip->plane; + struct intel_plane *intel_plane = to_intel_plane(plane); + + intel_plane->commit(plane); + } else { + struct drm_i915_private *dev_priv = dev->dev_private; + + dev_priv->display.commit_plane(crtc); + } + + if (intel_flip->has_cursor) + intel_crtc_cursor_commit(crtc, + intel_crtc->cursor_handle, + intel_crtc->cursor_width, + intel_crtc->cursor_height, + intel_crtc->cursor_bo, + intel_crtc->cursor_addr); + + /* This flip will happen on the next vblank */ + if (intel_have_new_frmcount(dev)) + intel_flip->vbl_count = vbl_count + 1; + else + intel_flip->vbl_count = (vbl_count + 1) & 0xffffff; + + if (pending_flip) { + struct intel_flip *old_intel_flip = + container_of(pending_flip, struct intel_flip, base); + bool flipped = intel_vbl_check(pending_flip, vbl_count); + + if (!flipped) { + swap(intel_flip->old_fb_id, old_intel_flip->old_fb_id); + swap(intel_flip->old_bo, old_intel_flip->old_bo); + swap(intel_flip->old_cursor_bo, old_intel_flip->old_cursor_bo); + } + + return flipped; + } + + return false; +} + +static bool intel_flip_vblank(struct drm_flip *pending_flip) +{ + struct intel_flip *old_intel_flip = + container_of(pending_flip, struct intel_flip, base); + u32 vbl_count = get_vbl_count(old_intel_flip->crtc); + + return intel_vbl_check(pending_flip, vbl_count); +} + +static const struct drm_flip_helper_funcs intel_flip_funcs = { + .prepare = intel_flip_prepare, + .flip = intel_flip_flip, + .vblank = intel_flip_vblank, + .complete = intel_flip_complete, + .finish = intel_flip_finish, + .cleanup = intel_flip_cleanup, +}; + +static const struct drm_flip_driver_funcs intel_flip_driver_funcs = { + .flush = intel_flip_driver_flush, +}; + +static void intel_flip_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc; + struct intel_plane *intel_plane; + + drm_flip_driver_init(&dev_priv->flip_driver, &intel_flip_driver_funcs); + + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) { + init_waitqueue_head(&intel_crtc->vbl_wait); + + drm_flip_helper_init(&intel_crtc->flip_helper, + &dev_priv->flip_driver, &intel_flip_funcs); + } + + list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) + drm_flip_helper_init(&intel_plane->flip_helper, + &dev_priv->flip_driver, &intel_flip_funcs); +} + +static void intel_flip_fini(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc; + struct intel_plane *intel_plane; + + list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) + drm_flip_helper_fini(&intel_plane->flip_helper); + + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) + drm_flip_helper_fini(&intel_crtc->flip_helper); + + drm_flip_driver_fini(&dev_priv->flip_driver); +} + +static void atomic_pipe_commit(struct drm_device *dev, + struct intel_atomic_state *state, + int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_file_private *file_priv = state->file->driver_priv; + LIST_HEAD(flips); + int i; + bool pipe_enabled = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe))->active; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &state->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_flip *intel_flip; + + if (!st->fb_dirty && !st->cursor_dirty) + continue; + + if (intel_crtc->pipe != pipe) + continue; + + intel_flip = st->flip; + st->flip = NULL; + + drm_flip_init(&intel_flip->base, &intel_crtc->flip_helper); + + if (st->event) { + intel_flip->event = st->event; + st->event = NULL; + /* need to keep track of it in case process exits */ + spin_lock_irq(&dev->event_lock); + list_add_tail(&intel_flip->pending_head, + &file_priv->pending_flips); + spin_unlock_irq(&dev->event_lock); + } + + intel_flip->crtc = crtc; + + /* should already be checked so can't fail */ + /* FIXME refactor the failing parts? */ + dev_priv->display.calc_plane(crtc, crtc->fb, crtc->x, crtc->y); + + if (st->cursor_dirty) { + intel_flip->has_cursor = true; + intel_flip->old_cursor_bo = st->old.cursor_bo; + } + + if (st->old.fb) { + intel_flip->old_fb_id = st->old.fb->base.id; + intel_flip->old_bo = to_intel_framebuffer(st->old.fb)->obj; + + mutex_lock(&dev->struct_mutex); + drm_gem_object_reference(&intel_flip->old_bo->base); + mutex_unlock(&dev->struct_mutex); + + /* + * Block clients from rendering to the new back buffer until + * the flip occurs and the object is no longer visible. + */ + atomic_set_mask(1 << intel_crtc->plane, + &intel_flip->old_bo->pending_flip.counter); + } + + list_add_tail(&intel_flip->base.list, &flips); + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &state->plane[i]; + struct drm_plane *plane = st->plane; + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_flip *intel_flip; + + if (!st->dirty) + continue; + + if (intel_plane->pipe != pipe) + continue; + + intel_flip = st->flip; + st->flip = NULL; + + drm_flip_init(&intel_flip->base, &intel_plane->flip_helper); + + if (st->event) { + intel_flip->event = st->event; + st->event = NULL; + /* need to keep track of it in case process exits */ + spin_lock_irq(&dev->event_lock); + list_add_tail(&intel_flip->pending_head, + &file_priv->pending_flips); + spin_unlock_irq(&dev->event_lock); + } + + intel_flip->crtc = intel_get_crtc_for_pipe(dev, pipe); + intel_flip->plane = plane; + + intel_plane->calc(plane, plane->fb, &st->coords); + + if (st->old.fb) { + intel_flip->old_fb_id = st->old.fb->base.id; + intel_flip->old_bo = to_intel_framebuffer(st->old.fb)->obj; + + mutex_lock(&dev->struct_mutex); + drm_gem_object_reference(&intel_flip->old_bo->base); + mutex_unlock(&dev->struct_mutex); + + /* + * Block clients from rendering to the new back buffer until + * the flip occurs and the object is no longer visible. + */ + atomic_set_mask(1 << (16 + intel_plane->pipe), + &intel_flip->old_bo->pending_flip.counter); + } + + list_add_tail(&intel_flip->base.list, &flips); + } + + if (list_empty(&flips)) + return; + + if (!pipe_enabled) { + drm_flip_driver_complete_flips(&dev_priv->flip_driver, &flips); + return; + } + + drm_flip_driver_prepare_flips(&dev_priv->flip_driver, &flips); + + local_irq_disable(); + + intel_pipe_vblank_evade(intel_get_crtc_for_pipe(dev, pipe)); + + drm_flip_driver_schedule_flips(&dev_priv->flip_driver, &flips); + + local_irq_enable(); +} + +void intel_atomic_handle_vblank(struct drm_device *dev, int pipe) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); + struct intel_plane *intel_plane; + + intel_crtc->vbl_received = true; + wake_up(&intel_crtc->vbl_wait); + + drm_flip_helper_vblank(&intel_crtc->flip_helper); + + list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) { + if (intel_plane->pipe == pipe) + drm_flip_helper_vblank(&intel_plane->flip_helper); + } +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 62e2a56..7394aca 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3554,6 +3554,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) encoder->disable(encoder);
intel_crtc_wait_for_pending_flips(crtc); + drm_flip_helper_clear(&intel_crtc->flip_helper); drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false);
@@ -3674,6 +3675,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
/* Give the overlay scaler a chance to disable if it's on this pipe */ intel_crtc_wait_for_pending_flips(crtc); + drm_flip_helper_clear(&intel_crtc->flip_helper); drm_vblank_off(dev, pipe); intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 103d104..cd37428 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -32,6 +32,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_dp_helper.h> +#include <drm/drm_flip.h>
#define _wait_for(COND, MS, W) ({ \ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ @@ -244,6 +245,9 @@ struct intel_crtc { struct intel_pch_pll *pch_pll; uint32_t ddi_pll_sel; struct intel_plane_regs primary_regs; + struct drm_flip_helper flip_helper; + wait_queue_head_t vbl_wait; + bool vbl_received; };
struct intel_plane_coords { @@ -275,6 +279,7 @@ struct intel_plane { void (*prepare)(struct drm_plane *plane); void (*commit)(struct drm_plane *plane); struct intel_plane_regs regs; + struct drm_flip_helper flip_helper; };
struct intel_watermark_params { @@ -722,5 +727,7 @@ extern bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
extern int intel_atomic_init(struct drm_device *dev); extern void intel_atomic_fini(struct drm_device *dev); +extern void intel_atomic_free_events(struct drm_device *dev, struct drm_file *file); +extern void intel_atomic_handle_vblank(struct drm_device *dev, int pipe);
#endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
intel_enable_primary() and intel_disable_primary() are needed in the atomic mode setting code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_drv.h | 3 +++ drivers/gpu/drm/i915/intel_sprite.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index cd37428..92a9518 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -730,4 +730,7 @@ extern void intel_atomic_fini(struct drm_device *dev); extern void intel_atomic_free_events(struct drm_device *dev, struct drm_file *file); extern void intel_atomic_handle_vblank(struct drm_device *dev, int pipe);
+extern void intel_enable_primary(struct drm_crtc *crtc); +extern void intel_disable_primary(struct drm_crtc *crtc); + #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 88644df..693b265 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -610,7 +610,7 @@ void intel_calc_sprite(struct drm_plane *plane, ilk_calc_plane(plane, fb, coords); }
-static void +void intel_enable_primary(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -629,7 +629,7 @@ intel_enable_primary(struct drm_crtc *crtc) dev_priv->display.commit_plane(crtc); }
-static void +void intel_disable_primary(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Check primary_disabled state before enabling the primary plane in crtc_enable() hooks.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7394aca..957907c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3349,7 +3349,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_crtc_load_lut(crtc);
intel_enable_pipe(dev_priv, pipe, is_pch_port); - intel_enable_plane(dev_priv, plane, pipe); + if (!intel_crtc->primary_disabled) + intel_enable_plane(dev_priv, plane, pipe);
if (is_pch_port) ironlake_pch_enable(crtc); @@ -3429,7 +3430,8 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_ddi_enable_pipe_func(crtc);
intel_enable_pipe(dev_priv, pipe, is_pch_port); - intel_enable_plane(dev_priv, plane, pipe); + if (!intel_crtc->primary_disabled) + intel_enable_plane(dev_priv, plane, pipe);
if (is_pch_port) lpt_pch_enable(crtc); @@ -3644,7 +3646,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
intel_enable_pll(dev_priv, pipe); intel_enable_pipe(dev_priv, pipe, false); - intel_enable_plane(dev_priv, plane, pipe); + if (!intel_crtc->primary_disabled) + intel_enable_plane(dev_priv, plane, pipe);
intel_crtc_load_lut(crtc); intel_update_fbc(dev);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Check the active and primary_disabled flags and set the DISPLAY_PLANE_ENABLE bit accordingly in calc_plane() hook for the primary plane.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 957907c..9903456 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2068,6 +2068,11 @@ static int i9xx_calc_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, regs->cntr &= ~DISPPLANE_TILED; }
+ if (intel_crtc->active && !intel_crtc->primary_disabled) + regs->cntr |= DISPLAY_PLANE_ENABLE; + else + regs->cntr &= ~DISPLAY_PLANE_ENABLE; + linear_offset = y * fb->pitches[0] + x * cpp;
if (INTEL_INFO(dev)->gen >= 4) { @@ -2175,6 +2180,11 @@ static int ironlake_calc_plane(struct drm_crtc *crtc, /* must disable */ regs->cntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+ if (intel_crtc->active && !intel_crtc->primary_disabled) + regs->cntr |= DISPLAY_PLANE_ENABLE; + else + regs->cntr &= ~DISPLAY_PLANE_ENABLE; + linear_offset = y * fb->pitches[0] + x * cpp; intel_crtc->dspaddr_offset = intel_gen4_compute_offset_xtiled(&x, &y,
From: Ville Syrjälä ville.syrjala@linux.intel.com
Enable/disable the primary plane accordingly when the sprite plane coverage changes.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 41 +++++++++++++++++++++++++++++++++++ 1 files changed, 41 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 238a843..41885fa 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -93,6 +93,7 @@ struct intel_crtc_state { bool changed; struct drm_pending_atomic_event *event; struct intel_flip *flip; + bool primary_disabled;
struct { bool enabled; @@ -318,6 +319,8 @@ static void *intel_atomic_begin(struct drm_device *dev, struct drm_file *file,
s->old.connectors_bitmask = s->connectors_bitmask; s->old.encoders_bitmask = s->encoders_bitmask; + + s->primary_disabled = intel_crtc->primary_disabled; }
i = 0; @@ -1157,6 +1160,9 @@ static int apply_config(struct drm_device *dev, intel_crtc->cursor_addr); }
+ if (!st->primary_disabled) + intel_enable_primary(crtc); + for (j = 0; j < dev->mode_config.num_plane; j++) { struct intel_plane_state *pst = &s->plane[j]; struct drm_plane *plane = pst->plane; @@ -1170,6 +1176,9 @@ static int apply_config(struct drm_device *dev, else if (!pst->coords.visible && pst->old.crtc == crtc) intel_plane->disable_plane(plane); } + + if (st->primary_disabled) + intel_disable_primary(crtc); }
/* don't restore the old state in end() */ @@ -1207,6 +1216,7 @@ static void restore_state(struct drm_device *dev, intel_crtc->cursor_width = s->saved_crtcs[i].cursor_width; intel_crtc->cursor_height = s->saved_crtcs[i].cursor_height; intel_crtc->cursor_visible = s->saved_crtcs[i].cursor_visible; + intel_crtc->primary_disabled = s->saved_crtcs[i].primary_disabled;
i++; } @@ -1346,6 +1356,28 @@ static int check_crtc(struct intel_crtc_state *s) return 0; }
+static void update_primary_visibility(struct drm_device *dev, + struct intel_atomic_state *s, + const struct drm_crtc *crtc, + const struct drm_plane *plane, + const struct intel_plane_coords *coords) +{ + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + bool primary_disabled = + coords->visible && + coords->crtc_x == 0 && + coords->crtc_y == 0 && + coords->crtc_w == crtc->mode.hdisplay && + coords->crtc_h == crtc->mode.vdisplay; + + if (primary_disabled != intel_crtc->primary_disabled) { + struct intel_crtc_state *st = get_crtc_state(dev, s, crtc); + st->fb_dirty = true; + st->primary_disabled = primary_disabled; + s->dirty = true; + } +} + static int intel_atomic_check(struct drm_device *dev, void *state) { struct intel_atomic_state *s = state; @@ -1430,6 +1462,12 @@ static int intel_atomic_check(struct drm_device *dev, void *state) ret = intel_check_plane(plane, plane->crtc, plane->fb, &st->coords); if (ret) return ret; + + /* FIXME doesn't correctly handle cases where plane moves between crtcs */ + if (plane->crtc) + update_primary_visibility(dev, s, plane->crtc, plane, &st->coords); + else if (st->old.crtc) + update_primary_visibility(dev, s, st->old.crtc, plane, &st->coords); }
return 0; @@ -2295,6 +2333,9 @@ static void atomic_pipe_commit(struct drm_device *dev,
intel_flip->crtc = crtc;
+ /* update primary_disabled befoer calc_plane() */ + intel_crtc->primary_disabled = st->primary_disabled; + /* should already be checked so can't fail */ /* FIXME refactor the failing parts? */ dev_priv->display.calc_plane(crtc, crtc->fb, crtc->x, crtc->y);
From: Ander Conselvan de Oliveira ander.conselvan.de.oliveira@intel.com
--- drivers/gpu/drm/drm_flip.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_flip.c b/drivers/gpu/drm/drm_flip.c index 6ccc3f8..a20e6a4 100644 --- a/drivers/gpu/drm/drm_flip.c +++ b/drivers/gpu/drm/drm_flip.c @@ -24,6 +24,7 @@ * Ville Syrjälä ville.syrjala@linux.intel.com */
+#include <linux/export.h> #include <drm/drm_flip.h>
static void drm_flip_driver_cleanup(struct work_struct *work) @@ -141,6 +142,7 @@ void drm_flip_helper_init(struct drm_flip_helper *helper, helper->driver = driver; helper->funcs = funcs; } +EXPORT_SYMBOL(drm_flip_helper_init);
void drm_flip_helper_clear(struct drm_flip_helper *helper) { @@ -176,6 +178,7 @@ void drm_flip_helper_clear(struct drm_flip_helper *helper) if (need_cleanup) queue_work(driver->wq, &driver->cleanup_work); } +EXPORT_SYMBOL(drm_flip_helper_clear);
void drm_flip_helper_fini(struct drm_flip_helper *helper) { @@ -186,6 +189,7 @@ void drm_flip_helper_fini(struct drm_flip_helper *helper) flush_work_sync(&driver->finish_work); flush_work_sync(&driver->cleanup_work); } +EXPORT_SYMBOL(drm_flip_helper_fini);
void drm_flip_helper_vblank(struct drm_flip_helper *helper) { @@ -223,6 +227,7 @@ void drm_flip_helper_vblank(struct drm_flip_helper *helper) if (need_cleanup) queue_work(driver->wq, &driver->cleanup_work); } +EXPORT_SYMBOL(drm_flip_helper_vblank);
void drm_flip_driver_init(struct drm_flip_driver *driver, const struct drm_flip_driver_funcs *funcs) @@ -239,6 +244,7 @@ void drm_flip_driver_init(struct drm_flip_driver *driver,
driver->wq = create_singlethread_workqueue("drm_flip"); } +EXPORT_SYMBOL(drm_flip_driver_init);
void drm_flip_driver_fini(struct drm_flip_driver *driver) { @@ -248,6 +254,7 @@ void drm_flip_driver_fini(struct drm_flip_driver *driver) WARN_ON(!list_empty(&driver->finish_list)); WARN_ON(!list_empty(&driver->cleanup_list)); } +EXPORT_SYMBOL(drm_flip_driver_fini);
void drm_flip_driver_schedule_flips(struct drm_flip_driver *driver, struct list_head *flips) @@ -298,6 +305,7 @@ void drm_flip_driver_schedule_flips(struct drm_flip_driver *driver, if (need_cleanup) queue_work(driver->wq, &driver->cleanup_work); } +EXPORT_SYMBOL(drm_flip_driver_schedule_flips);
void drm_flip_driver_prepare_flips(struct drm_flip_driver *driver, struct list_head *flips) @@ -311,6 +319,7 @@ void drm_flip_driver_prepare_flips(struct drm_flip_driver *driver, helper->funcs->prepare(flip); } } +EXPORT_SYMBOL(drm_flip_driver_prepare_flips);
void drm_flip_driver_complete_flips(struct drm_flip_driver *driver, struct list_head *flips) @@ -365,6 +374,7 @@ void drm_flip_driver_complete_flips(struct drm_flip_driver *driver, if (need_cleanup) queue_work(driver->wq, &driver->cleanup_work); } +EXPORT_SYMBOL(drm_flip_driver_complete_flips);
void drm_flip_init(struct drm_flip *flip, struct drm_flip_helper *helper) @@ -374,3 +384,4 @@ void drm_flip_init(struct drm_flip *flip, flip->finished = false; INIT_LIST_HEAD(&flip->list); } +EXPORT_SYMBOL(drm_flip_init);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Don't leave stale flips hanging around the sprites' flip helpers when the crtc is being disabled.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 15 +++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 4 ++-- drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 41885fa..41fd0d5 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2449,3 +2449,18 @@ void intel_atomic_handle_vblank(struct drm_device *dev, int pipe) drm_flip_helper_vblank(&intel_plane->flip_helper); } } + +void intel_atomic_clear_flips(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_plane *intel_plane; + int pipe = intel_crtc->pipe; + + drm_flip_helper_clear(&intel_crtc->flip_helper); + + list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) { + if (intel_plane->pipe == pipe) + drm_flip_helper_clear(&intel_plane->flip_helper); + } +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9903456..566bf29 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3566,7 +3566,7 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) encoder->disable(encoder);
intel_crtc_wait_for_pending_flips(crtc); - drm_flip_helper_clear(&intel_crtc->flip_helper); + intel_atomic_clear_flips(crtc); drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false);
@@ -3688,7 +3688,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
/* Give the overlay scaler a chance to disable if it's on this pipe */ intel_crtc_wait_for_pending_flips(crtc); - drm_flip_helper_clear(&intel_crtc->flip_helper); + intel_atomic_clear_flips(crtc); drm_vblank_off(dev, pipe); intel_crtc_dpms_overlay(intel_crtc, false); intel_crtc_update_cursor(crtc, false); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 92a9518..5b0789f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -729,6 +729,7 @@ extern int intel_atomic_init(struct drm_device *dev); extern void intel_atomic_fini(struct drm_device *dev); extern void intel_atomic_free_events(struct drm_device *dev, struct drm_file *file); extern void intel_atomic_handle_vblank(struct drm_device *dev, int pipe); +extern void intel_atomic_clear_flips(struct drm_crtc *crtc);
extern void intel_enable_primary(struct drm_crtc *crtc); extern void intel_disable_primary(struct drm_crtc *crtc);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Move the property code around a bit to make it easier to move it out to the drm core later.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 423 ++++++++++++++++++++--------------- drivers/gpu/drm/i915/intel_drv.h | 3 + include/drm/drm_crtc.h | 12 + 3 files changed, 252 insertions(+), 186 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 41fd0d5..0e37cf5 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1473,87 +1473,6 @@ static int intel_atomic_check(struct drm_device *dev, void *state) return 0; }
-static void update_plane_props(struct drm_plane *plane) -{ - struct drm_mode_object *obj = &plane->base; - - drm_object_property_set_value(obj, prop_src_x, plane->src_x); - drm_object_property_set_value(obj, prop_src_y, plane->src_y); - drm_object_property_set_value(obj, prop_src_w, plane->src_w); - drm_object_property_set_value(obj, prop_src_h, plane->src_h); - - drm_object_property_set_value(obj, prop_crtc_x, plane->crtc_x); - drm_object_property_set_value(obj, prop_crtc_y, plane->crtc_y); - drm_object_property_set_value(obj, prop_crtc_w, plane->crtc_w); - drm_object_property_set_value(obj, prop_crtc_h, plane->crtc_h); - - drm_object_property_set_value(obj, prop_fb_id, plane->fb ? plane->fb->base.id : 0); - drm_object_property_set_value(obj, prop_crtc_id, plane->crtc ? plane->crtc->base.id : 0); -} - -static int update_prop_connector_ids(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_connector *connector; - uint64_t value = 0; - int i = 0; - uint32_t connector_ids[dev->mode_config.num_connector]; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder && connector->encoder->crtc == crtc) - connector_ids[i++] = connector->base.id; - } - - if (i) { - drm_property_blob_replace_data(crtc->connector_ids_blob, - i * sizeof connector_ids[0], connector_ids); - value = crtc->connector_ids_blob->base.id; - } else - drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL); - - drm_object_property_set_value(&crtc->base, prop_connector_ids, value); - - return 0; -} - -static int update_prop_mode(struct drm_crtc *crtc) -{ - uint64_t value = 0; - - if (crtc->enabled) { - struct drm_mode_modeinfo umode; - - drm_crtc_convert_to_umode(&umode, &crtc->mode); - drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, &umode); - value = crtc->mode_blob->base.id; - } else - drm_property_blob_replace_data(crtc->mode_blob, 0, NULL); - - drm_object_property_set_value(&crtc->base, prop_mode, value); - - return 0; -} - -static void update_crtc_props(struct drm_crtc *crtc) -{ - struct drm_mode_object *obj = &crtc->base; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - drm_object_property_set_value(obj, prop_src_x, crtc->x); - drm_object_property_set_value(obj, prop_src_y, crtc->y); - - drm_object_property_set_value(obj, prop_fb_id, crtc->fb ? crtc->fb->base.id : 0); - - drm_object_property_set_value(obj, prop_cursor_id, intel_crtc->cursor_handle); - drm_object_property_set_value(obj, prop_cursor_x, intel_crtc->cursor_x); - drm_object_property_set_value(obj, prop_cursor_y, intel_crtc->cursor_y); - drm_object_property_set_value(obj, prop_cursor_w, intel_crtc->cursor_width); - drm_object_property_set_value(obj, prop_cursor_h, intel_crtc->cursor_height); - - update_prop_mode(crtc); - update_prop_connector_ids(crtc); -} - static void update_props(struct drm_device *dev, struct intel_atomic_state *s) { @@ -1565,7 +1484,7 @@ static void update_props(struct drm_device *dev, if (!st->fb_dirty && !st->mode_dirty) continue;
- update_crtc_props(st->crtc); + intel_crtc_update_properties(st->crtc); }
for (i = 0; i < dev->mode_config.num_plane; i++) { @@ -1574,7 +1493,7 @@ static void update_props(struct drm_device *dev, if (!st->dirty) continue;
- update_plane_props(st->plane); + drm_plane_update_properties(st->plane); } }
@@ -1829,32 +1748,6 @@ static const struct drm_atomic_funcs intel_atomic_funcs = { .end = intel_atomic_end, };
-static struct { - struct drm_property **prop; - const char *name; - uint64_t min; - uint64_t max; -} props[] = { - { &prop_src_x, "SRC_X", 0, UINT_MAX }, - { &prop_src_y, "SRC_Y", 0, UINT_MAX }, - { &prop_src_w, "SRC_W", 0, UINT_MAX }, - { &prop_src_h, "SRC_H", 0, UINT_MAX }, - - { &prop_crtc_x, "CRTC_X", INT_MIN, INT_MAX }, - { &prop_crtc_y, "CRTC_Y", INT_MIN, INT_MAX }, - { &prop_crtc_w, "CRTC_W", 0, INT_MAX }, - { &prop_crtc_h, "CRTC_H", 0, INT_MAX }, - - { &prop_fb_id, "FB_ID", 0, UINT_MAX }, - { &prop_crtc_id, "CRTC_ID", 0, UINT_MAX }, - - { &prop_cursor_id, "CURSOR_ID", 0, UINT_MAX }, - { &prop_cursor_w, "CURSOR_W", 0, UINT_MAX }, - { &prop_cursor_h, "CURSOR_H", 0, UINT_MAX }, - { &prop_cursor_x, "CURSOR_X", INT_MIN, INT_MAX }, - { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX }, -}; - static void intel_flip_init(struct drm_device *dev); static void intel_flip_fini(struct drm_device *dev);
@@ -1862,71 +1755,25 @@ int intel_atomic_init(struct drm_device *dev) { struct drm_crtc *crtc; struct drm_plane *plane; - int ret = -ENOMEM; - unsigned int i; + int ret;
- for (i = 0; i < ARRAY_SIZE(props); i++) { - *props[i].prop = - drm_property_create_range(dev, 0, props[i].name, - props[i].min, props[i].max); - if (!*props[i].prop) - goto out; - } - - /* FIXME create special object ID list property type? */ - prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); - if (!prop_connector_ids) - goto out; - - prop_mode = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); - if (!prop_mode) + ret = drm_mode_create_properties(dev); + if (ret) goto out;
list_for_each_entry(plane, &dev->mode_config.plane_list, head) { - struct drm_mode_object *obj = &plane->base; - - drm_object_attach_property(obj, prop_src_x, 0); - drm_object_attach_property(obj, prop_src_y, 0); - drm_object_attach_property(obj, prop_src_w, 0); - drm_object_attach_property(obj, prop_src_h, 0); - - drm_object_attach_property(obj, prop_crtc_x, 0); - drm_object_attach_property(obj, prop_crtc_y, 0); - drm_object_attach_property(obj, prop_crtc_w, 0); - drm_object_attach_property(obj, prop_crtc_h, 0); - - drm_object_attach_property(obj, prop_fb_id, 0); - drm_object_attach_property(obj, prop_crtc_id, 0); - - update_plane_props(plane); + drm_plane_attach_properties(plane); + drm_plane_update_properties(plane); }
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct drm_mode_object *obj = &crtc->base; + intel_crtc_attach_properties(crtc);
- drm_object_attach_property(obj, prop_src_x, 0); - drm_object_attach_property(obj, prop_src_y, 0); + ret = drm_crtc_create_blobs(crtc); + if (ret) + goto destroy_props;
- drm_object_attach_property(obj, prop_fb_id, 0); - drm_object_attach_property(obj, prop_mode, 0); - drm_object_attach_property(obj, prop_connector_ids, 0); - - drm_object_attach_property(obj, prop_cursor_id, 0); - drm_object_attach_property(obj, prop_cursor_x, 0); - drm_object_attach_property(obj, prop_cursor_y, 0); - drm_object_attach_property(obj, prop_cursor_w, 0); - drm_object_attach_property(obj, prop_cursor_h, 0); - - crtc->mode_blob = drm_property_create_blob(dev, 0, sizeof(struct drm_mode_modeinfo), NULL); - if (!crtc->mode_blob) - goto out; - - crtc->connector_ids_blob = drm_property_create_blob(dev, 0, - dev->mode_config.num_connector * sizeof(uint32_t), NULL); - if (!crtc->connector_ids_blob) - goto out; - - update_crtc_props(crtc); + intel_crtc_update_properties(crtc); }
dev->driver->atomic_funcs = &intel_atomic_funcs; @@ -1935,12 +1782,9 @@ int intel_atomic_init(struct drm_device *dev)
return 0;
+ destroy_props: + drm_mode_destroy_properties(dev); out: - drm_property_destroy(dev, prop_mode); - drm_property_destroy(dev, prop_connector_ids); - - while (i--) - drm_property_destroy(dev, *props[i].prop);
return ret; } @@ -1952,24 +1796,10 @@ void intel_atomic_fini(struct drm_device *dev) intel_flip_fini(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - drm_property_destroy_blob(dev, crtc->mode_blob); - drm_property_destroy_blob(dev, crtc->connector_ids_blob); + drm_crtc_destroy_blobs(crtc); }
- drm_property_destroy(dev, prop_connector_ids); - drm_property_destroy(dev, prop_mode); - drm_property_destroy(dev, prop_crtc_id); - drm_property_destroy(dev, prop_fb_id); - - drm_property_destroy(dev, prop_crtc_h); - drm_property_destroy(dev, prop_crtc_w); - drm_property_destroy(dev, prop_crtc_y); - drm_property_destroy(dev, prop_crtc_x); - - drm_property_destroy(dev, prop_src_h); - drm_property_destroy(dev, prop_src_w); - drm_property_destroy(dev, prop_src_y); - drm_property_destroy(dev, prop_src_x); + drm_mode_destroy_properties(dev); }
enum { @@ -2464,3 +2294,224 @@ void intel_atomic_clear_flips(struct drm_crtc *crtc) drm_flip_helper_clear(&intel_plane->flip_helper); } } + +static void drm_crtc_update_connector_ids_property(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_connector *connector; + uint64_t value = 0; + int i = 0; + uint32_t connector_ids[dev->mode_config.num_connector]; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && connector->encoder->crtc == crtc) + connector_ids[i++] = connector->base.id; + } + + if (i) { + drm_property_blob_replace_data(crtc->connector_ids_blob, + i * sizeof connector_ids[0], connector_ids); + value = crtc->connector_ids_blob->base.id; + } else + drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, prop_connector_ids, value); +} + +static void drm_crtc_update_mode_property(struct drm_crtc *crtc) +{ + uint64_t value = 0; + + if (crtc->enabled) { + struct drm_mode_modeinfo umode; + + drm_crtc_convert_to_umode(&umode, &crtc->mode); + drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, &umode); + value = crtc->mode_blob->base.id; + } else + drm_property_blob_replace_data(crtc->mode_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, prop_mode, value); +} + +void drm_crtc_update_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + + drm_object_property_set_value(obj, prop_src_x, crtc->x); + drm_object_property_set_value(obj, prop_src_y, crtc->y); + drm_object_property_set_value(obj, prop_fb_id, crtc->fb ? crtc->fb->base.id : 0); + + drm_crtc_update_mode_property(crtc); + drm_crtc_update_connector_ids_property(crtc); +} + +void intel_crtc_update_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + drm_crtc_update_properties(crtc); + + drm_object_property_set_value(obj, prop_cursor_id, intel_crtc->cursor_handle); + drm_object_property_set_value(obj, prop_cursor_x, intel_crtc->cursor_x); + drm_object_property_set_value(obj, prop_cursor_y, intel_crtc->cursor_y); + drm_object_property_set_value(obj, prop_cursor_w, intel_crtc->cursor_width); + drm_object_property_set_value(obj, prop_cursor_h, intel_crtc->cursor_height); +} + +void drm_plane_update_properties(struct drm_plane *plane) +{ + struct drm_mode_object *obj = &plane->base; + + drm_object_property_set_value(obj, prop_src_x, plane->src_x); + drm_object_property_set_value(obj, prop_src_y, plane->src_y); + drm_object_property_set_value(obj, prop_src_w, plane->src_w); + drm_object_property_set_value(obj, prop_src_h, plane->src_h); + + drm_object_property_set_value(obj, prop_crtc_x, plane->crtc_x); + drm_object_property_set_value(obj, prop_crtc_y, plane->crtc_y); + drm_object_property_set_value(obj, prop_crtc_w, plane->crtc_w); + drm_object_property_set_value(obj, prop_crtc_h, plane->crtc_h); + + drm_object_property_set_value(obj, prop_fb_id, plane->fb ? plane->fb->base.id : 0); + drm_object_property_set_value(obj, prop_crtc_id, plane->crtc ? plane->crtc->base.id : 0); +} + +int drm_crtc_create_blobs(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + crtc->mode_blob = drm_property_create_blob(dev, 0, sizeof(struct drm_mode_modeinfo), NULL); + if (!crtc->mode_blob) + return -ENOMEM; + + crtc->connector_ids_blob = drm_property_create_blob(dev, 0, dev->mode_config.num_connector * sizeof(uint32_t), NULL); + if (!crtc->connector_ids_blob) + return -ENOMEM; + + return 0; +} + +void drm_crtc_destroy_blobs(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + drm_property_destroy_blob(dev, crtc->mode_blob); + drm_property_destroy_blob(dev, crtc->connector_ids_blob); +} + +void drm_mode_destroy_properties(struct drm_device *dev) +{ + drm_property_destroy(dev, prop_connector_ids); + drm_property_destroy(dev, prop_mode); + drm_property_destroy(dev, prop_crtc_id); + drm_property_destroy(dev, prop_fb_id); + + drm_property_destroy(dev, prop_crtc_h); + drm_property_destroy(dev, prop_crtc_w); + drm_property_destroy(dev, prop_crtc_y); + drm_property_destroy(dev, prop_crtc_x); + + drm_property_destroy(dev, prop_src_h); + drm_property_destroy(dev, prop_src_w); + drm_property_destroy(dev, prop_src_y); + drm_property_destroy(dev, prop_src_x); +} + +static struct { + struct drm_property **prop; + const char *name; + uint64_t min; + uint64_t max; +} props[] = { + { &prop_src_x, "SRC_X", 0, UINT_MAX }, + { &prop_src_y, "SRC_Y", 0, UINT_MAX }, + { &prop_src_w, "SRC_W", 0, UINT_MAX }, + { &prop_src_h, "SRC_H", 0, UINT_MAX }, + + { &prop_crtc_x, "CRTC_X", INT_MIN, INT_MAX }, + { &prop_crtc_y, "CRTC_Y", INT_MIN, INT_MAX }, + { &prop_crtc_w, "CRTC_W", 0, INT_MAX }, + { &prop_crtc_h, "CRTC_H", 0, INT_MAX }, + + { &prop_fb_id, "FB_ID", 0, UINT_MAX }, + { &prop_crtc_id, "CRTC_ID", 0, UINT_MAX }, + + { &prop_cursor_id, "CURSOR_ID", 0, UINT_MAX }, + { &prop_cursor_w, "CURSOR_W", 0, UINT_MAX }, + { &prop_cursor_h, "CURSOR_H", 0, UINT_MAX }, + { &prop_cursor_x, "CURSOR_X", INT_MIN, INT_MAX }, + { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX }, +}; + +int drm_mode_create_properties(struct drm_device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + *props[i].prop = drm_property_create_range(dev, 0, props[i].name, props[i].min, props[i].max); + if (!*props[i].prop) + goto out; + } + + /* FIXME create special object ID list property type? */ + prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + if (!prop_connector_ids) + goto out; + + prop_mode = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!prop_mode) + goto out; + + return 0; + + out: + drm_property_destroy(dev, prop_mode); + drm_property_destroy(dev, prop_connector_ids); + + while (i--) + drm_property_destroy(dev, *props[i].prop); + + return -ENOMEM; /* FIXME? */ +} + +void drm_plane_attach_properties(struct drm_plane *plane) +{ + struct drm_mode_object *obj = &plane->base; + + drm_object_attach_property(obj, prop_src_x, 0); + drm_object_attach_property(obj, prop_src_y, 0); + drm_object_attach_property(obj, prop_src_w, 0); + drm_object_attach_property(obj, prop_src_h, 0); + drm_object_attach_property(obj, prop_crtc_x, 0); + drm_object_attach_property(obj, prop_crtc_y, 0); + drm_object_attach_property(obj, prop_crtc_w, 0); + drm_object_attach_property(obj, prop_crtc_h, 0); + drm_object_attach_property(obj, prop_fb_id, 0); + drm_object_attach_property(obj, prop_crtc_id, 0); +} + +void drm_crtc_attach_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + + drm_object_attach_property(obj, prop_src_x, 0); + drm_object_attach_property(obj, prop_src_y, 0); + drm_object_attach_property(obj, prop_fb_id, 0); + drm_object_attach_property(obj, prop_mode, 0); + drm_object_attach_property(obj, prop_connector_ids, 0); +} + +void intel_crtc_attach_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + + drm_crtc_attach_properties(crtc); + + drm_object_attach_property(obj, prop_cursor_id, 0); + drm_object_attach_property(obj, prop_cursor_x, 0); + drm_object_attach_property(obj, prop_cursor_y, 0); + drm_object_attach_property(obj, prop_cursor_w, 0); + drm_object_attach_property(obj, prop_cursor_h, 0); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5b0789f..1230c1b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -734,4 +734,7 @@ extern void intel_atomic_clear_flips(struct drm_crtc *crtc); extern void intel_enable_primary(struct drm_crtc *crtc); extern void intel_disable_primary(struct drm_crtc *crtc);
+extern void intel_crtc_attach_properties(struct drm_crtc *crtc); +extern void intel_crtc_update_properties(struct drm_crtc *crtc); + #endif /* __INTEL_DRV_H__ */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 897c110..74fb5a8 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1147,4 +1147,16 @@ struct drm_atomic_funcs { void (*end)(struct drm_device *dev, void *state); };
+extern int drm_mode_create_properties(struct drm_device *dev); +extern void drm_mode_destroy_properties(struct drm_device *dev); + +extern int drm_crtc_create_blobs(struct drm_crtc *crtc); +extern void drm_crtc_destroy_blobs(struct drm_crtc *crtc); + +extern void drm_crtc_attach_properties(struct drm_crtc *crtc); +extern void drm_plane_attach_properties(struct drm_plane *plane); + +extern void drm_crtc_update_properties(struct drm_crtc *crtc); +extern void drm_plane_update_properties(struct drm_plane *plane); + #endif /* __DRM_CRTC_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
Standard connector properties are kept in the mode_config structure. Move the CRTC and plane properties there as well.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 293 +++++++++++++++++++---------------- include/drm/drm_crtc.h | 19 +++ 2 files changed, 175 insertions(+), 137 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 0e37cf5..0def947 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -30,24 +30,6 @@
#include "intel_drv.h"
-static struct drm_property *prop_src_x; -static struct drm_property *prop_src_y; -static struct drm_property *prop_src_w; -static struct drm_property *prop_src_h; -static struct drm_property *prop_crtc_x; -static struct drm_property *prop_crtc_y; -static struct drm_property *prop_crtc_w; -static struct drm_property *prop_crtc_h; -static struct drm_property *prop_fb_id; -static struct drm_property *prop_crtc_id; -static struct drm_property *prop_mode; -static struct drm_property *prop_connector_ids; -static struct drm_property *prop_cursor_id; -static struct drm_property *prop_cursor_x; -static struct drm_property *prop_cursor_y; -static struct drm_property *prop_cursor_w; -static struct drm_property *prop_cursor_h; - struct intel_flip { struct drm_flip base; u32 vbl_count; @@ -368,27 +350,28 @@ static int plane_set(struct intel_atomic_state *s, uint64_t value) { struct drm_plane *plane = state->plane; + struct drm_mode_config *config = &plane->dev->mode_config; struct drm_mode_object *obj;
state->changed = true;
- if (prop == prop_src_x) { + if (prop == config->src_x_prop) { plane->src_x = value; - } else if (prop == prop_src_y) { + } else if (prop == config->src_y_prop) { plane->src_y = value; - } else if (prop == prop_src_w) { + } else if (prop == config->src_w_prop) { plane->src_w = value; - } else if (prop == prop_src_h) { + } else if (prop == config->src_h_prop) { plane->src_h = value; - } else if (prop == prop_crtc_x) { + } else if (prop == config->crtc_x_prop) { plane->crtc_x = value; - } else if (prop == prop_crtc_y) { + } else if (prop == config->crtc_y_prop) { plane->crtc_y = value; - } else if (prop == prop_crtc_w) { + } else if (prop == config->crtc_w_prop) { plane->crtc_w = value; - } else if (prop == prop_crtc_h) { + } else if (prop == config->crtc_h_prop) { plane->crtc_h = value; - } else if (prop == prop_crtc_id) { + } else if (prop == config->crtc_id_prop) { if (value) { obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_CRTC); if (!obj) { @@ -398,7 +381,7 @@ static int plane_set(struct intel_atomic_state *s, plane->crtc = obj_to_crtc(obj); } else plane->crtc = NULL; - } else if (prop == prop_fb_id) { + } else if (prop == config->fb_id_prop) { if (value) { obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_FB); if (!obj) { @@ -422,16 +405,17 @@ static int crtc_set(struct intel_atomic_state *s, uint64_t value, const void *blob_data) { struct drm_crtc *crtc = state->crtc; + struct drm_mode_config *config = &crtc->dev->mode_config; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_mode_object *obj;
state->changed = true;
- if (prop == prop_src_x) { + if (prop == config->src_x_prop) { crtc->x = value; - } else if (prop == prop_src_y) { + } else if (prop == config->src_y_prop) { crtc->y = value; - } else if (prop == prop_mode) { + } else if (prop == config->mode_prop) { const struct drm_mode_modeinfo *umode = blob_data;
if (value != 0 && value != sizeof *umode) { @@ -462,7 +446,7 @@ static int crtc_set(struct intel_atomic_state *s, drm_mode_destroy(crtc->dev, mode); } else crtc->enabled = false; - } else if (prop == prop_fb_id) { + } else if (prop == config->fb_id_prop) { if (value) { obj = drm_mode_object_find(crtc->dev, value, DRM_MODE_OBJECT_FB); if (!obj) { @@ -472,7 +456,7 @@ static int crtc_set(struct intel_atomic_state *s, crtc->fb = obj_to_fb(obj); } else crtc->fb = NULL; - } else if (prop == prop_connector_ids) { + } else if (prop == config->connector_ids_prop) { const uint32_t *ids = blob_data; uint64_t count_ids = value / sizeof(uint32_t); int ret; @@ -482,7 +466,7 @@ static int crtc_set(struct intel_atomic_state *s, return -EINVAL; }
- if (count_ids > crtc->dev->mode_config.num_connector) { + if (count_ids > config->num_connector) { DRM_DEBUG_KMS("Too many connectors specified\n"); return -ERANGE; } @@ -490,19 +474,19 @@ static int crtc_set(struct intel_atomic_state *s, ret = process_connectors(state, ids, count_ids); if (ret) return ret; - } else if (prop == prop_cursor_id) { + } else if (prop == config->cursor_id_prop) { intel_crtc->cursor_handle = value; - } else if (prop == prop_cursor_x) { + } else if (prop == config->cursor_x_prop) { intel_crtc->cursor_x = value; - } else if (prop == prop_cursor_y) { + } else if (prop == config->cursor_y_prop) { intel_crtc->cursor_y = value; - } else if (prop == prop_cursor_w) { + } else if (prop == config->cursor_w_prop) { if (value != 0 && value != 64) { DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n"); return -EINVAL; } intel_crtc->cursor_width = value; - } else if (prop == prop_cursor_h) { + } else if (prop == config->cursor_h_prop) { if (value != 0 && value != 64) { DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n"); return -EINVAL; @@ -2297,13 +2281,13 @@ void intel_atomic_clear_flips(struct drm_crtc *crtc)
static void drm_crtc_update_connector_ids_property(struct drm_crtc *crtc) { - struct drm_device *dev = crtc->dev; + struct drm_mode_config *config = &crtc->dev->mode_config; struct drm_connector *connector; uint64_t value = 0; int i = 0; - uint32_t connector_ids[dev->mode_config.num_connector]; + uint32_t connector_ids[config->num_connector];
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + list_for_each_entry(connector, &config->connector_list, head) { if (connector->encoder && connector->encoder->crtc == crtc) connector_ids[i++] = connector->base.id; } @@ -2315,11 +2299,12 @@ static void drm_crtc_update_connector_ids_property(struct drm_crtc *crtc) } else drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL);
- drm_object_property_set_value(&crtc->base, prop_connector_ids, value); + drm_object_property_set_value(&crtc->base, config->connector_ids_prop, value); }
static void drm_crtc_update_mode_property(struct drm_crtc *crtc) { + struct drm_mode_config *config = &crtc->dev->mode_config; uint64_t value = 0;
if (crtc->enabled) { @@ -2331,16 +2316,17 @@ static void drm_crtc_update_mode_property(struct drm_crtc *crtc) } else drm_property_blob_replace_data(crtc->mode_blob, 0, NULL);
- drm_object_property_set_value(&crtc->base, prop_mode, value); + drm_object_property_set_value(&crtc->base, config->mode_prop, value); }
void drm_crtc_update_properties(struct drm_crtc *crtc) { struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config;
- drm_object_property_set_value(obj, prop_src_x, crtc->x); - drm_object_property_set_value(obj, prop_src_y, crtc->y); - drm_object_property_set_value(obj, prop_fb_id, crtc->fb ? crtc->fb->base.id : 0); + drm_object_property_set_value(obj, config->src_x_prop, crtc->x); + drm_object_property_set_value(obj, config->src_y_prop, crtc->y); + drm_object_property_set_value(obj, config->fb_id_prop, crtc->fb ? crtc->fb->base.id : 0);
drm_crtc_update_mode_property(crtc); drm_crtc_update_connector_ids_property(crtc); @@ -2349,33 +2335,35 @@ void drm_crtc_update_properties(struct drm_crtc *crtc) void intel_crtc_update_properties(struct drm_crtc *crtc) { struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config; struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
drm_crtc_update_properties(crtc);
- drm_object_property_set_value(obj, prop_cursor_id, intel_crtc->cursor_handle); - drm_object_property_set_value(obj, prop_cursor_x, intel_crtc->cursor_x); - drm_object_property_set_value(obj, prop_cursor_y, intel_crtc->cursor_y); - drm_object_property_set_value(obj, prop_cursor_w, intel_crtc->cursor_width); - drm_object_property_set_value(obj, prop_cursor_h, intel_crtc->cursor_height); + drm_object_property_set_value(obj, config->cursor_id_prop, intel_crtc->cursor_handle); + drm_object_property_set_value(obj, config->cursor_x_prop, intel_crtc->cursor_x); + drm_object_property_set_value(obj, config->cursor_y_prop, intel_crtc->cursor_y); + drm_object_property_set_value(obj, config->cursor_w_prop, intel_crtc->cursor_width); + drm_object_property_set_value(obj, config->cursor_h_prop, intel_crtc->cursor_height); }
void drm_plane_update_properties(struct drm_plane *plane) { struct drm_mode_object *obj = &plane->base; + struct drm_mode_config *config = &plane->dev->mode_config;
- drm_object_property_set_value(obj, prop_src_x, plane->src_x); - drm_object_property_set_value(obj, prop_src_y, plane->src_y); - drm_object_property_set_value(obj, prop_src_w, plane->src_w); - drm_object_property_set_value(obj, prop_src_h, plane->src_h); + drm_object_property_set_value(obj, config->src_x_prop, plane->src_x); + drm_object_property_set_value(obj, config->src_y_prop, plane->src_y); + drm_object_property_set_value(obj, config->src_w_prop, plane->src_w); + drm_object_property_set_value(obj, config->src_h_prop, plane->src_h);
- drm_object_property_set_value(obj, prop_crtc_x, plane->crtc_x); - drm_object_property_set_value(obj, prop_crtc_y, plane->crtc_y); - drm_object_property_set_value(obj, prop_crtc_w, plane->crtc_w); - drm_object_property_set_value(obj, prop_crtc_h, plane->crtc_h); + drm_object_property_set_value(obj, config->crtc_x_prop, plane->crtc_x); + drm_object_property_set_value(obj, config->crtc_y_prop, plane->crtc_y); + drm_object_property_set_value(obj, config->crtc_w_prop, plane->crtc_w); + drm_object_property_set_value(obj, config->crtc_h_prop, plane->crtc_h);
- drm_object_property_set_value(obj, prop_fb_id, plane->fb ? plane->fb->base.id : 0); - drm_object_property_set_value(obj, prop_crtc_id, plane->crtc ? plane->crtc->base.id : 0); + drm_object_property_set_value(obj, config->fb_id_prop, plane->fb ? plane->fb->base.id : 0); + drm_object_property_set_value(obj, config->crtc_id_prop, plane->crtc ? plane->crtc->base.id : 0); }
int drm_crtc_create_blobs(struct drm_crtc *crtc) @@ -2401,77 +2389,105 @@ void drm_crtc_destroy_blobs(struct drm_crtc *crtc) drm_property_destroy_blob(dev, crtc->connector_ids_blob); }
+void drm_property_destroy_null(struct drm_device *dev, struct drm_property **prop) +{ + drm_property_destroy(dev, *prop); + *prop = NULL; +} + void drm_mode_destroy_properties(struct drm_device *dev) { - drm_property_destroy(dev, prop_connector_ids); - drm_property_destroy(dev, prop_mode); - drm_property_destroy(dev, prop_crtc_id); - drm_property_destroy(dev, prop_fb_id); - - drm_property_destroy(dev, prop_crtc_h); - drm_property_destroy(dev, prop_crtc_w); - drm_property_destroy(dev, prop_crtc_y); - drm_property_destroy(dev, prop_crtc_x); - - drm_property_destroy(dev, prop_src_h); - drm_property_destroy(dev, prop_src_w); - drm_property_destroy(dev, prop_src_y); - drm_property_destroy(dev, prop_src_x); + struct drm_mode_config *config = &dev->mode_config; + + drm_property_destroy_null(dev, &config->cursor_h_prop); + drm_property_destroy_null(dev, &config->cursor_w_prop); + drm_property_destroy_null(dev, &config->cursor_y_prop); + drm_property_destroy_null(dev, &config->cursor_x_prop); + drm_property_destroy_null(dev, &config->cursor_id_prop); + + drm_property_destroy_null(dev, &config->connector_ids_prop); + drm_property_destroy_null(dev, &config->mode_prop); + + drm_property_destroy_null(dev, &config->crtc_id_prop); + drm_property_destroy_null(dev, &config->fb_id_prop); + + drm_property_destroy_null(dev, &config->crtc_h_prop); + drm_property_destroy_null(dev, &config->crtc_w_prop); + drm_property_destroy_null(dev, &config->crtc_y_prop); + drm_property_destroy_null(dev, &config->crtc_x_prop); + + drm_property_destroy_null(dev, &config->src_h_prop); + drm_property_destroy_null(dev, &config->src_w_prop); + drm_property_destroy_null(dev, &config->src_y_prop); + drm_property_destroy_null(dev, &config->src_x_prop); }
-static struct { - struct drm_property **prop; - const char *name; - uint64_t min; - uint64_t max; -} props[] = { - { &prop_src_x, "SRC_X", 0, UINT_MAX }, - { &prop_src_y, "SRC_Y", 0, UINT_MAX }, - { &prop_src_w, "SRC_W", 0, UINT_MAX }, - { &prop_src_h, "SRC_H", 0, UINT_MAX }, - - { &prop_crtc_x, "CRTC_X", INT_MIN, INT_MAX }, - { &prop_crtc_y, "CRTC_Y", INT_MIN, INT_MAX }, - { &prop_crtc_w, "CRTC_W", 0, INT_MAX }, - { &prop_crtc_h, "CRTC_H", 0, INT_MAX }, - - { &prop_fb_id, "FB_ID", 0, UINT_MAX }, - { &prop_crtc_id, "CRTC_ID", 0, UINT_MAX }, - - { &prop_cursor_id, "CURSOR_ID", 0, UINT_MAX }, - { &prop_cursor_w, "CURSOR_W", 0, UINT_MAX }, - { &prop_cursor_h, "CURSOR_H", 0, UINT_MAX }, - { &prop_cursor_x, "CURSOR_X", INT_MIN, INT_MAX }, - { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX }, -}; - int drm_mode_create_properties(struct drm_device *dev) { - int i; + struct drm_mode_config *config = &dev->mode_config;
- for (i = 0; i < ARRAY_SIZE(props); i++) { - *props[i].prop = drm_property_create_range(dev, 0, props[i].name, props[i].min, props[i].max); - if (!*props[i].prop) - goto out; - } + config->src_x_prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); + if (!config->src_x_prop) + goto out; + config->src_y_prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX); + if (!config->src_y_prop) + goto out; + config->src_w_prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); + if (!config->src_w_prop) + goto out; + config->src_h_prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); + if (!config->src_h_prop) + goto out; + + config->crtc_x_prop = drm_property_create_range(dev, 0, "CRTC_X", INT_MIN, INT_MAX); + if (!config->crtc_x_prop) + goto out; + config->crtc_y_prop = drm_property_create_range(dev, 0, "CRTC_Y", INT_MIN, INT_MAX); + if (!config->crtc_y_prop) + goto out; + config->crtc_w_prop = drm_property_create_range(dev, 0, "CRTC_W", 0, UINT_MAX); + if (!config->crtc_w_prop) + goto out; + config->crtc_h_prop = drm_property_create_range(dev, 0, "CRTC_H", 0, UINT_MAX); + if (!config->crtc_h_prop) + goto out; + + config->fb_id_prop = drm_property_create_range(dev, 0, "FB_ID", 0, UINT_MAX); + if (!config->fb_id_prop) + goto out; + config->crtc_id_prop = drm_property_create_range(dev, 0, "CRTC_ID", 0, UINT_MAX); + if (!config->crtc_id_prop) + goto out; + + config->cursor_id_prop = drm_property_create_range(dev, 0, "CURSOR_ID", 0, UINT_MAX); + if (!config->cursor_id_prop) + goto out; + config->cursor_x_prop = drm_property_create_range(dev, 0, "CURSOR_X", INT_MIN, INT_MAX); + if (!config->cursor_x_prop) + goto out; + config->cursor_y_prop = drm_property_create_range(dev, 0, "CURSOR_Y", INT_MIN, INT_MAX); + if (!config->cursor_y_prop) + goto out; + config->cursor_w_prop = drm_property_create_range(dev, 0, "CURSOR_W", 0, UINT_MAX); + if (!config->cursor_w_prop) + goto out; + config->cursor_h_prop = drm_property_create_range(dev, 0, "CURSOR_H", 0, UINT_MAX); + if (!config->cursor_h_prop) + goto out;
/* FIXME create special object ID list property type? */ - prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); - if (!prop_connector_ids) + config->connector_ids_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + if (!config->connector_ids_prop) goto out;
- prop_mode = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); - if (!prop_mode) + config->mode_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!config->mode_prop) goto out;
return 0;
out: - drm_property_destroy(dev, prop_mode); - drm_property_destroy(dev, prop_connector_ids); - - while (i--) - drm_property_destroy(dev, *props[i].prop); + drm_mode_destroy_properties(dev);
return -ENOMEM; /* FIXME? */ } @@ -2479,39 +2495,42 @@ int drm_mode_create_properties(struct drm_device *dev) void drm_plane_attach_properties(struct drm_plane *plane) { struct drm_mode_object *obj = &plane->base; + struct drm_mode_config *config = &plane->dev->mode_config;
- drm_object_attach_property(obj, prop_src_x, 0); - drm_object_attach_property(obj, prop_src_y, 0); - drm_object_attach_property(obj, prop_src_w, 0); - drm_object_attach_property(obj, prop_src_h, 0); - drm_object_attach_property(obj, prop_crtc_x, 0); - drm_object_attach_property(obj, prop_crtc_y, 0); - drm_object_attach_property(obj, prop_crtc_w, 0); - drm_object_attach_property(obj, prop_crtc_h, 0); - drm_object_attach_property(obj, prop_fb_id, 0); - drm_object_attach_property(obj, prop_crtc_id, 0); + drm_object_attach_property(obj, config->src_x_prop, 0); + drm_object_attach_property(obj, config->src_y_prop, 0); + drm_object_attach_property(obj, config->src_w_prop, 0); + drm_object_attach_property(obj, config->src_h_prop, 0); + drm_object_attach_property(obj, config->crtc_x_prop, 0); + drm_object_attach_property(obj, config->crtc_y_prop, 0); + drm_object_attach_property(obj, config->crtc_w_prop, 0); + drm_object_attach_property(obj, config->crtc_h_prop, 0); + drm_object_attach_property(obj, config->fb_id_prop, 0); + drm_object_attach_property(obj, config->crtc_id_prop, 0); }
void drm_crtc_attach_properties(struct drm_crtc *crtc) { struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config;
- drm_object_attach_property(obj, prop_src_x, 0); - drm_object_attach_property(obj, prop_src_y, 0); - drm_object_attach_property(obj, prop_fb_id, 0); - drm_object_attach_property(obj, prop_mode, 0); - drm_object_attach_property(obj, prop_connector_ids, 0); + drm_object_attach_property(obj, config->src_x_prop, 0); + drm_object_attach_property(obj, config->src_y_prop, 0); + drm_object_attach_property(obj, config->fb_id_prop, 0); + drm_object_attach_property(obj, config->mode_prop, 0); + drm_object_attach_property(obj, config->connector_ids_prop, 0); }
void intel_crtc_attach_properties(struct drm_crtc *crtc) { struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config;
drm_crtc_attach_properties(crtc);
- drm_object_attach_property(obj, prop_cursor_id, 0); - drm_object_attach_property(obj, prop_cursor_x, 0); - drm_object_attach_property(obj, prop_cursor_y, 0); - drm_object_attach_property(obj, prop_cursor_w, 0); - drm_object_attach_property(obj, prop_cursor_h, 0); + drm_object_attach_property(obj, config->cursor_id_prop, 0); + drm_object_attach_property(obj, config->cursor_x_prop, 0); + drm_object_attach_property(obj, config->cursor_y_prop, 0); + drm_object_attach_property(obj, config->cursor_w_prop, 0); + drm_object_attach_property(obj, config->cursor_h_prop, 0); } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 74fb5a8..c8cea87 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -835,6 +835,25 @@ struct drm_mode_config { struct drm_property *dithering_mode_property; struct drm_property *dirty_info_property;
+ /* CRTC/plane properties */ + struct drm_property *src_x_prop; + struct drm_property *src_y_prop; + struct drm_property *src_w_prop; + struct drm_property *src_h_prop; + struct drm_property *crtc_x_prop; + struct drm_property *crtc_y_prop; + struct drm_property *crtc_w_prop; + struct drm_property *crtc_h_prop; + struct drm_property *fb_id_prop; + struct drm_property *crtc_id_prop; + struct drm_property *mode_prop; + struct drm_property *connector_ids_prop; + struct drm_property *cursor_id_prop; + struct drm_property *cursor_x_prop; + struct drm_property *cursor_y_prop; + struct drm_property *cursor_w_prop; + struct drm_property *cursor_h_prop; + /* dumb ioctl parameters */ uint32_t preferred_depth, prefer_shadow; };
From: Ville Syrjälä ville.syrjala@linux.intel.com
Keep the new CRTC properties when doing modeset through the legacy code paths.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc_helper.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 30eb557..19444cd 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -782,6 +782,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) kfree(save_connectors); kfree(save_encoders); kfree(save_crtcs); + + /* changes in one CRTC can affect the others */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + drm_crtc_update_properties(crtc); + return 0;
fail: @@ -810,6 +815,11 @@ fail: kfree(save_connectors); kfree(save_encoders); kfree(save_crtcs); + + /* changes in one CRTC can affect the others */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + drm_crtc_update_properties(crtc); + return ret; } EXPORT_SYMBOL(drm_crtc_helper_set_config);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Keep the new plane properties when doing operations through the legacy code paths.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 897ff46..9d8738f 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -407,6 +407,8 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) /* disconnect the plane from the fb and crtc: */ plane->fb = NULL; plane->crtc = NULL; + + drm_plane_update_properties(plane); } }
@@ -1780,6 +1782,8 @@ int drm_mode_setplane(struct drm_device *dev, void *data, plane->funcs->disable_plane(plane); plane->crtc = NULL; plane->fb = NULL; + + drm_plane_update_properties(plane); goto out; }
@@ -1863,6 +1867,8 @@ int drm_mode_setplane(struct drm_device *dev, void *data, plane->src_y = plane_req->src_y; plane->src_w = plane_req->src_w; plane->src_h = plane_req->src_h; + + drm_plane_update_properties(plane); }
out:
From: Ville Syrjälä ville.syrjala@linux.intel.com
Update cursor related CRTC properties after a modeset. The cursor properties aren't handled by the drm core since not all CRTCs have cursors.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 566bf29..321c840 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6368,6 +6368,8 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, if (old_obj) intel_crtc_cursor_bo_unref(crtc, old_obj);
+ intel_crtc_update_properties(crtc); + return 0; }
@@ -6380,6 +6382,8 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
intel_crtc_update_cursor(crtc, true);
+ intel_crtc_update_properties(crtc); + return 0; }
@@ -7341,6 +7345,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
trace_i915_flip_request(intel_crtc->plane, obj);
+ intel_crtc_update_properties(crtc); + return 0;
cleanup_pending: @@ -8067,6 +8073,7 @@ next_encoder: static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; + struct drm_crtc *crtc; struct drm_mode_set save_set; struct intel_set_config *config; int ret; @@ -8140,6 +8147,10 @@ static int intel_crtc_set_config(struct drm_mode_set *set)
intel_set_config_free(config);
+ /* changes in one CRTC can affect the others */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + intel_crtc_update_properties(crtc); + return 0;
fail: @@ -8151,6 +8162,10 @@ fail: save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n");
+ /* changes in one CRTC can affect the others */ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + intel_crtc_update_properties(crtc); + out_config: intel_set_config_free(config); return ret;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Move some of the property code to drm_crtc.c since it should be shared by everyone.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 235 +++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_atomic.c | 256 ---------------------------------- drivers/gpu/drm/i915/intel_display.c | 29 ++++ 3 files changed, 264 insertions(+), 256 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 9d8738f..527b35b 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4397,3 +4397,238 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
return ret; } + +static void drm_crtc_update_connector_ids_property(struct drm_crtc *crtc) +{ + struct drm_mode_config *config = &crtc->dev->mode_config; + struct drm_connector *connector; + uint64_t value = 0; + int i = 0; + uint32_t connector_ids[config->num_connector]; + + list_for_each_entry(connector, &config->connector_list, head) { + if (connector->encoder && connector->encoder->crtc == crtc) + connector_ids[i++] = connector->base.id; + } + + if (i) { + drm_property_blob_replace_data(crtc->connector_ids_blob, + i * sizeof connector_ids[0], connector_ids); + value = crtc->connector_ids_blob->base.id; + } else + drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, config->connector_ids_prop, value); +} + +static void drm_crtc_update_mode_property(struct drm_crtc *crtc) +{ + struct drm_mode_config *config = &crtc->dev->mode_config; + uint64_t value = 0; + + if (crtc->enabled) { + struct drm_mode_modeinfo umode; + + drm_crtc_convert_to_umode(&umode, &crtc->mode); + drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, &umode); + value = crtc->mode_blob->base.id; + } else + drm_property_blob_replace_data(crtc->mode_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, config->mode_prop, value); +} + +void drm_crtc_update_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config; + + drm_object_property_set_value(obj, config->src_x_prop, crtc->x); + drm_object_property_set_value(obj, config->src_y_prop, crtc->y); + drm_object_property_set_value(obj, config->fb_id_prop, crtc->fb ? crtc->fb->base.id : 0); + + drm_crtc_update_mode_property(crtc); + drm_crtc_update_connector_ids_property(crtc); +} +EXPORT_SYMBOL(drm_crtc_update_properties); + +void drm_plane_update_properties(struct drm_plane *plane) +{ + struct drm_mode_object *obj = &plane->base; + struct drm_mode_config *config = &plane->dev->mode_config; + + drm_object_property_set_value(obj, config->src_x_prop, plane->src_x); + drm_object_property_set_value(obj, config->src_y_prop, plane->src_y); + drm_object_property_set_value(obj, config->src_w_prop, plane->src_w); + drm_object_property_set_value(obj, config->src_h_prop, plane->src_h); + + drm_object_property_set_value(obj, config->crtc_x_prop, plane->crtc_x); + drm_object_property_set_value(obj, config->crtc_y_prop, plane->crtc_y); + drm_object_property_set_value(obj, config->crtc_w_prop, plane->crtc_w); + drm_object_property_set_value(obj, config->crtc_h_prop, plane->crtc_h); + + drm_object_property_set_value(obj, config->fb_id_prop, plane->fb ? plane->fb->base.id : 0); + drm_object_property_set_value(obj, config->crtc_id_prop, plane->crtc ? plane->crtc->base.id : 0); +} +EXPORT_SYMBOL(drm_plane_update_properties); + +int drm_crtc_create_blobs(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + crtc->mode_blob = drm_property_create_blob(dev, 0, sizeof(struct drm_mode_modeinfo), NULL); + if (!crtc->mode_blob) + return -ENOMEM; + + crtc->connector_ids_blob = drm_property_create_blob(dev, 0, dev->mode_config.num_connector * sizeof(uint32_t), NULL); + if (!crtc->connector_ids_blob) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(drm_crtc_create_blobs); + +void drm_crtc_destroy_blobs(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + drm_property_destroy_blob(dev, crtc->mode_blob); + drm_property_destroy_blob(dev, crtc->connector_ids_blob); +} +EXPORT_SYMBOL(drm_crtc_destroy_blobs); + +static void drm_property_destroy_null(struct drm_device *dev, struct drm_property **prop) +{ + drm_property_destroy(dev, *prop); + *prop = NULL; +} + +void drm_mode_destroy_properties(struct drm_device *dev) +{ + struct drm_mode_config *config = &dev->mode_config; + + drm_property_destroy_null(dev, &config->cursor_h_prop); + drm_property_destroy_null(dev, &config->cursor_w_prop); + drm_property_destroy_null(dev, &config->cursor_y_prop); + drm_property_destroy_null(dev, &config->cursor_x_prop); + drm_property_destroy_null(dev, &config->cursor_id_prop); + + drm_property_destroy_null(dev, &config->connector_ids_prop); + drm_property_destroy_null(dev, &config->mode_prop); + + drm_property_destroy_null(dev, &config->crtc_id_prop); + drm_property_destroy_null(dev, &config->fb_id_prop); + + drm_property_destroy_null(dev, &config->crtc_h_prop); + drm_property_destroy_null(dev, &config->crtc_w_prop); + drm_property_destroy_null(dev, &config->crtc_y_prop); + drm_property_destroy_null(dev, &config->crtc_x_prop); + + drm_property_destroy_null(dev, &config->src_h_prop); + drm_property_destroy_null(dev, &config->src_w_prop); + drm_property_destroy_null(dev, &config->src_y_prop); + drm_property_destroy_null(dev, &config->src_x_prop); +} +EXPORT_SYMBOL(drm_mode_destroy_properties); + +int drm_mode_create_properties(struct drm_device *dev) +{ + struct drm_mode_config *config = &dev->mode_config; + + config->src_x_prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); + if (!config->src_x_prop) + goto out; + config->src_y_prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX); + if (!config->src_y_prop) + goto out; + config->src_w_prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); + if (!config->src_w_prop) + goto out; + config->src_h_prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); + if (!config->src_h_prop) + goto out; + + config->crtc_x_prop = drm_property_create_range(dev, 0, "CRTC_X", INT_MIN, INT_MAX); + if (!config->crtc_x_prop) + goto out; + config->crtc_y_prop = drm_property_create_range(dev, 0, "CRTC_Y", INT_MIN, INT_MAX); + if (!config->crtc_y_prop) + goto out; + config->crtc_w_prop = drm_property_create_range(dev, 0, "CRTC_W", 0, UINT_MAX); + if (!config->crtc_w_prop) + goto out; + config->crtc_h_prop = drm_property_create_range(dev, 0, "CRTC_H", 0, UINT_MAX); + if (!config->crtc_h_prop) + goto out; + + config->fb_id_prop = drm_property_create_range(dev, 0, "FB_ID", 0, UINT_MAX); + if (!config->fb_id_prop) + goto out; + config->crtc_id_prop = drm_property_create_range(dev, 0, "CRTC_ID", 0, UINT_MAX); + if (!config->crtc_id_prop) + goto out; + + config->cursor_id_prop = drm_property_create_range(dev, 0, "CURSOR_ID", 0, UINT_MAX); + if (!config->cursor_id_prop) + goto out; + config->cursor_x_prop = drm_property_create_range(dev, 0, "CURSOR_X", INT_MIN, INT_MAX); + if (!config->cursor_x_prop) + goto out; + config->cursor_y_prop = drm_property_create_range(dev, 0, "CURSOR_Y", INT_MIN, INT_MAX); + if (!config->cursor_y_prop) + goto out; + config->cursor_w_prop = drm_property_create_range(dev, 0, "CURSOR_W", 0, UINT_MAX); + if (!config->cursor_w_prop) + goto out; + config->cursor_h_prop = drm_property_create_range(dev, 0, "CURSOR_H", 0, UINT_MAX); + if (!config->cursor_h_prop) + goto out; + + /* FIXME create special object ID list property type? */ + config->connector_ids_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + if (!config->connector_ids_prop) + goto out; + + config->mode_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!config->mode_prop) + goto out; + + return 0; + + out: + drm_mode_destroy_properties(dev); + + return -ENOMEM; /* FIXME? */ +} +EXPORT_SYMBOL(drm_mode_create_properties); + +void drm_plane_attach_properties(struct drm_plane *plane) +{ + struct drm_mode_object *obj = &plane->base; + struct drm_mode_config *config = &plane->dev->mode_config; + + drm_object_attach_property(obj, config->src_x_prop, 0); + drm_object_attach_property(obj, config->src_y_prop, 0); + drm_object_attach_property(obj, config->src_w_prop, 0); + drm_object_attach_property(obj, config->src_h_prop, 0); + drm_object_attach_property(obj, config->crtc_x_prop, 0); + drm_object_attach_property(obj, config->crtc_y_prop, 0); + drm_object_attach_property(obj, config->crtc_w_prop, 0); + drm_object_attach_property(obj, config->crtc_h_prop, 0); + drm_object_attach_property(obj, config->fb_id_prop, 0); + drm_object_attach_property(obj, config->crtc_id_prop, 0); +} +EXPORT_SYMBOL(drm_plane_attach_properties); + +void drm_crtc_attach_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config; + + drm_object_attach_property(obj, config->src_x_prop, 0); + drm_object_attach_property(obj, config->src_y_prop, 0); + drm_object_attach_property(obj, config->fb_id_prop, 0); + drm_object_attach_property(obj, config->mode_prop, 0); + drm_object_attach_property(obj, config->connector_ids_prop, 0); +} +EXPORT_SYMBOL(drm_crtc_attach_properties); diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 0def947..6dd07fb 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2278,259 +2278,3 @@ void intel_atomic_clear_flips(struct drm_crtc *crtc) drm_flip_helper_clear(&intel_plane->flip_helper); } } - -static void drm_crtc_update_connector_ids_property(struct drm_crtc *crtc) -{ - struct drm_mode_config *config = &crtc->dev->mode_config; - struct drm_connector *connector; - uint64_t value = 0; - int i = 0; - uint32_t connector_ids[config->num_connector]; - - list_for_each_entry(connector, &config->connector_list, head) { - if (connector->encoder && connector->encoder->crtc == crtc) - connector_ids[i++] = connector->base.id; - } - - if (i) { - drm_property_blob_replace_data(crtc->connector_ids_blob, - i * sizeof connector_ids[0], connector_ids); - value = crtc->connector_ids_blob->base.id; - } else - drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL); - - drm_object_property_set_value(&crtc->base, config->connector_ids_prop, value); -} - -static void drm_crtc_update_mode_property(struct drm_crtc *crtc) -{ - struct drm_mode_config *config = &crtc->dev->mode_config; - uint64_t value = 0; - - if (crtc->enabled) { - struct drm_mode_modeinfo umode; - - drm_crtc_convert_to_umode(&umode, &crtc->mode); - drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, &umode); - value = crtc->mode_blob->base.id; - } else - drm_property_blob_replace_data(crtc->mode_blob, 0, NULL); - - drm_object_property_set_value(&crtc->base, config->mode_prop, value); -} - -void drm_crtc_update_properties(struct drm_crtc *crtc) -{ - struct drm_mode_object *obj = &crtc->base; - struct drm_mode_config *config = &crtc->dev->mode_config; - - drm_object_property_set_value(obj, config->src_x_prop, crtc->x); - drm_object_property_set_value(obj, config->src_y_prop, crtc->y); - drm_object_property_set_value(obj, config->fb_id_prop, crtc->fb ? crtc->fb->base.id : 0); - - drm_crtc_update_mode_property(crtc); - drm_crtc_update_connector_ids_property(crtc); -} - -void intel_crtc_update_properties(struct drm_crtc *crtc) -{ - struct drm_mode_object *obj = &crtc->base; - struct drm_mode_config *config = &crtc->dev->mode_config; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - drm_crtc_update_properties(crtc); - - drm_object_property_set_value(obj, config->cursor_id_prop, intel_crtc->cursor_handle); - drm_object_property_set_value(obj, config->cursor_x_prop, intel_crtc->cursor_x); - drm_object_property_set_value(obj, config->cursor_y_prop, intel_crtc->cursor_y); - drm_object_property_set_value(obj, config->cursor_w_prop, intel_crtc->cursor_width); - drm_object_property_set_value(obj, config->cursor_h_prop, intel_crtc->cursor_height); -} - -void drm_plane_update_properties(struct drm_plane *plane) -{ - struct drm_mode_object *obj = &plane->base; - struct drm_mode_config *config = &plane->dev->mode_config; - - drm_object_property_set_value(obj, config->src_x_prop, plane->src_x); - drm_object_property_set_value(obj, config->src_y_prop, plane->src_y); - drm_object_property_set_value(obj, config->src_w_prop, plane->src_w); - drm_object_property_set_value(obj, config->src_h_prop, plane->src_h); - - drm_object_property_set_value(obj, config->crtc_x_prop, plane->crtc_x); - drm_object_property_set_value(obj, config->crtc_y_prop, plane->crtc_y); - drm_object_property_set_value(obj, config->crtc_w_prop, plane->crtc_w); - drm_object_property_set_value(obj, config->crtc_h_prop, plane->crtc_h); - - drm_object_property_set_value(obj, config->fb_id_prop, plane->fb ? plane->fb->base.id : 0); - drm_object_property_set_value(obj, config->crtc_id_prop, plane->crtc ? plane->crtc->base.id : 0); -} - -int drm_crtc_create_blobs(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - - crtc->mode_blob = drm_property_create_blob(dev, 0, sizeof(struct drm_mode_modeinfo), NULL); - if (!crtc->mode_blob) - return -ENOMEM; - - crtc->connector_ids_blob = drm_property_create_blob(dev, 0, dev->mode_config.num_connector * sizeof(uint32_t), NULL); - if (!crtc->connector_ids_blob) - return -ENOMEM; - - return 0; -} - -void drm_crtc_destroy_blobs(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - - drm_property_destroy_blob(dev, crtc->mode_blob); - drm_property_destroy_blob(dev, crtc->connector_ids_blob); -} - -void drm_property_destroy_null(struct drm_device *dev, struct drm_property **prop) -{ - drm_property_destroy(dev, *prop); - *prop = NULL; -} - -void drm_mode_destroy_properties(struct drm_device *dev) -{ - struct drm_mode_config *config = &dev->mode_config; - - drm_property_destroy_null(dev, &config->cursor_h_prop); - drm_property_destroy_null(dev, &config->cursor_w_prop); - drm_property_destroy_null(dev, &config->cursor_y_prop); - drm_property_destroy_null(dev, &config->cursor_x_prop); - drm_property_destroy_null(dev, &config->cursor_id_prop); - - drm_property_destroy_null(dev, &config->connector_ids_prop); - drm_property_destroy_null(dev, &config->mode_prop); - - drm_property_destroy_null(dev, &config->crtc_id_prop); - drm_property_destroy_null(dev, &config->fb_id_prop); - - drm_property_destroy_null(dev, &config->crtc_h_prop); - drm_property_destroy_null(dev, &config->crtc_w_prop); - drm_property_destroy_null(dev, &config->crtc_y_prop); - drm_property_destroy_null(dev, &config->crtc_x_prop); - - drm_property_destroy_null(dev, &config->src_h_prop); - drm_property_destroy_null(dev, &config->src_w_prop); - drm_property_destroy_null(dev, &config->src_y_prop); - drm_property_destroy_null(dev, &config->src_x_prop); -} - -int drm_mode_create_properties(struct drm_device *dev) -{ - struct drm_mode_config *config = &dev->mode_config; - - config->src_x_prop = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); - if (!config->src_x_prop) - goto out; - config->src_y_prop = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX); - if (!config->src_y_prop) - goto out; - config->src_w_prop = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); - if (!config->src_w_prop) - goto out; - config->src_h_prop = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); - if (!config->src_h_prop) - goto out; - - config->crtc_x_prop = drm_property_create_range(dev, 0, "CRTC_X", INT_MIN, INT_MAX); - if (!config->crtc_x_prop) - goto out; - config->crtc_y_prop = drm_property_create_range(dev, 0, "CRTC_Y", INT_MIN, INT_MAX); - if (!config->crtc_y_prop) - goto out; - config->crtc_w_prop = drm_property_create_range(dev, 0, "CRTC_W", 0, UINT_MAX); - if (!config->crtc_w_prop) - goto out; - config->crtc_h_prop = drm_property_create_range(dev, 0, "CRTC_H", 0, UINT_MAX); - if (!config->crtc_h_prop) - goto out; - - config->fb_id_prop = drm_property_create_range(dev, 0, "FB_ID", 0, UINT_MAX); - if (!config->fb_id_prop) - goto out; - config->crtc_id_prop = drm_property_create_range(dev, 0, "CRTC_ID", 0, UINT_MAX); - if (!config->crtc_id_prop) - goto out; - - config->cursor_id_prop = drm_property_create_range(dev, 0, "CURSOR_ID", 0, UINT_MAX); - if (!config->cursor_id_prop) - goto out; - config->cursor_x_prop = drm_property_create_range(dev, 0, "CURSOR_X", INT_MIN, INT_MAX); - if (!config->cursor_x_prop) - goto out; - config->cursor_y_prop = drm_property_create_range(dev, 0, "CURSOR_Y", INT_MIN, INT_MAX); - if (!config->cursor_y_prop) - goto out; - config->cursor_w_prop = drm_property_create_range(dev, 0, "CURSOR_W", 0, UINT_MAX); - if (!config->cursor_w_prop) - goto out; - config->cursor_h_prop = drm_property_create_range(dev, 0, "CURSOR_H", 0, UINT_MAX); - if (!config->cursor_h_prop) - goto out; - - /* FIXME create special object ID list property type? */ - config->connector_ids_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); - if (!config->connector_ids_prop) - goto out; - - config->mode_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); - if (!config->mode_prop) - goto out; - - return 0; - - out: - drm_mode_destroy_properties(dev); - - return -ENOMEM; /* FIXME? */ -} - -void drm_plane_attach_properties(struct drm_plane *plane) -{ - struct drm_mode_object *obj = &plane->base; - struct drm_mode_config *config = &plane->dev->mode_config; - - drm_object_attach_property(obj, config->src_x_prop, 0); - drm_object_attach_property(obj, config->src_y_prop, 0); - drm_object_attach_property(obj, config->src_w_prop, 0); - drm_object_attach_property(obj, config->src_h_prop, 0); - drm_object_attach_property(obj, config->crtc_x_prop, 0); - drm_object_attach_property(obj, config->crtc_y_prop, 0); - drm_object_attach_property(obj, config->crtc_w_prop, 0); - drm_object_attach_property(obj, config->crtc_h_prop, 0); - drm_object_attach_property(obj, config->fb_id_prop, 0); - drm_object_attach_property(obj, config->crtc_id_prop, 0); -} - -void drm_crtc_attach_properties(struct drm_crtc *crtc) -{ - struct drm_mode_object *obj = &crtc->base; - struct drm_mode_config *config = &crtc->dev->mode_config; - - drm_object_attach_property(obj, config->src_x_prop, 0); - drm_object_attach_property(obj, config->src_y_prop, 0); - drm_object_attach_property(obj, config->fb_id_prop, 0); - drm_object_attach_property(obj, config->mode_prop, 0); - drm_object_attach_property(obj, config->connector_ids_prop, 0); -} - -void intel_crtc_attach_properties(struct drm_crtc *crtc) -{ - struct drm_mode_object *obj = &crtc->base; - struct drm_mode_config *config = &crtc->dev->mode_config; - - drm_crtc_attach_properties(crtc); - - drm_object_attach_property(obj, config->cursor_id_prop, 0); - drm_object_attach_property(obj, config->cursor_x_prop, 0); - drm_object_attach_property(obj, config->cursor_y_prop, 0); - drm_object_attach_property(obj, config->cursor_w_prop, 0); - drm_object_attach_property(obj, config->cursor_h_prop, 0); -} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 321c840..451cc3d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9372,3 +9372,32 @@ intel_display_print_error_state(struct seq_file *m, } } #endif + +void intel_crtc_update_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + drm_crtc_update_properties(crtc); + + drm_object_property_set_value(obj, config->cursor_id_prop, intel_crtc->cursor_handle); + drm_object_property_set_value(obj, config->cursor_x_prop, intel_crtc->cursor_x); + drm_object_property_set_value(obj, config->cursor_y_prop, intel_crtc->cursor_y); + drm_object_property_set_value(obj, config->cursor_w_prop, intel_crtc->cursor_width); + drm_object_property_set_value(obj, config->cursor_h_prop, intel_crtc->cursor_height); +} + +void intel_crtc_attach_properties(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct drm_mode_config *config = &crtc->dev->mode_config; + + drm_crtc_attach_properties(crtc); + + drm_object_attach_property(obj, config->cursor_id_prop, 0); + drm_object_attach_property(obj, config->cursor_x_prop, 0); + drm_object_attach_property(obj, config->cursor_y_prop, 0); + drm_object_attach_property(obj, config->cursor_w_prop, 0); + drm_object_attach_property(obj, config->cursor_h_prop, 0); +}
From: Ville Syrjälä ville.syrjala@linux.intel.com
All connectors use intel_best_encoder() as their best_encoder() func, so just call it directly w/o the indirection.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 10 ++-------- 1 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 6dd07fb..e58ec3f 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -152,8 +152,6 @@ static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, i int i;
for (i = 0; i < count_ids; i++) { - struct drm_encoder *encoder; - const struct drm_connector_helper_funcs *connector_funcs; struct drm_mode_object *obj; int j;
@@ -169,9 +167,8 @@ static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, i }
connector = obj_to_connector(obj); - connector_funcs = connector->helper_private;
- encoder = connector_funcs->best_encoder(connector); + encoder = intel_best_encoder(connector);
if (!drm_encoder_crtc_ok(encoder, crtc)) return -EINVAL; @@ -180,9 +177,6 @@ static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, i }
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - const struct drm_connector_helper_funcs *connector_funcs = - connector->helper_private; - for (i = 0; i < count_ids; i++) { if (connector == connectors[i]) break; @@ -196,7 +190,7 @@ static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, i continue; }
- encoder = connector_funcs->best_encoder(connector); + encoder = intel_best_encoder(connector);
connector->encoder = encoder; encoder->crtc = crtc;
From: Ville Syrjälä ville.syrjala@linux.intel.com
The atomic code needs intel_modeset_update_staged_output_state().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 451cc3d..c84a839 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7423,7 +7423,7 @@ static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, * Updates the staged output configuration state, e.g. after we've read out the * current hw state. */ -static void intel_modeset_update_staged_output_state(struct drm_device *dev) +void intel_modeset_update_staged_output_state(struct drm_device *dev) { struct intel_encoder *encoder; struct intel_connector *connector; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1230c1b..24dc192 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -737,4 +737,6 @@ extern void intel_disable_primary(struct drm_crtc *crtc); extern void intel_crtc_attach_properties(struct drm_crtc *crtc); extern void intel_crtc_update_properties(struct drm_crtc *crtc);
+extern void intel_modeset_update_staged_output_state(struct drm_device *dev); + #endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
The i915 modeset logic requires that these be kept in sync with the other crtc and encoder pointers.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index e58ec3f..c5558cc 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1701,6 +1701,8 @@ static int intel_atomic_commit(struct drm_device *dev, void *state)
update_props(dev, s);
+ intel_modeset_update_staged_output_state(dev); + return 0; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
Update the connector DPMS state after atomic modeset operations.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 31 +++++++++++++++++++++++++++++++ 1 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index c5558cc..1fce359 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1475,6 +1475,36 @@ static void update_props(struct drm_device *dev, } }
+/* + * FIXME + * Perhaps atomic modeset shouldn't actually change the DPMS state, + * unless explicitly asked to do so. That's the way we treat everything + * else, so it makes sense. Although the dpms property is already a bit + * special in the legacy codepaths, so maybe we should follow the same + * pattern. Ie. a modeset forces DPMS to on (which is what we do here). + */ +static void update_connector_dpms(struct drm_device *dev, struct drm_crtc *crtc) +{ + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + int dpms = connector->dpms; + + if (connector->encoder && connector->encoder->crtc == crtc) + dpms = DRM_MODE_DPMS_ON; + else if (!connector->encoder || !connector->encoder->crtc) + dpms = DRM_MODE_DPMS_OFF; + + if (connector->dpms == dpms) + continue; + + connector->dpms = dpms; + drm_connector_property_set_value(connector, + dev->mode_config.dpms_property, + dpms); + } +} + static void update_crtc(struct drm_device *dev, struct intel_atomic_state *s) { @@ -1494,6 +1524,7 @@ static void update_crtc(struct drm_device *dev, if (st->mode_dirty) { drm_calc_timestamping_constants(crtc); intel_crtc_update_sarea(crtc, crtc->enabled); + update_connector_dpms(dev, crtc); }
if (st->fb_dirty)
From: Ville Syrjälä ville.syrjala@linux.intel.com
The pending_flip counter is probably going to be remove entirely, along with i915_gem_execbuffer_wait_for_flips() so don't even try to use it.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 74 +---------------------------------- 1 files changed, 2 insertions(+), 72 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 1fce359..ac7fe88 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1536,45 +1536,9 @@ static void atomic_pipe_commit(struct drm_device *dev, struct intel_atomic_state *state, int pipe);
-static int apply_nonblocking(struct drm_device *dev, struct intel_atomic_state *s) +static void apply_nonblocking(struct drm_device *dev, struct intel_atomic_state *s) { struct intel_crtc *intel_crtc; - int i; - - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct intel_crtc_state *st = &s->crtc[i]; - struct intel_crtc *intel_crtc = to_intel_crtc(st->crtc); - struct drm_i915_gem_object *obj; - - if (!st->old.fb) - continue; - - obj = to_intel_framebuffer(st->old.fb)->obj; - - /* Only one bit per plane in pending_flips */ - if (atomic_read(&obj->pending_flip) & (1 << intel_crtc->plane)) - return -EBUSY; - } - - for (i = 0; i < dev->mode_config.num_plane; i++) { - struct intel_plane_state *st = &s->plane[i]; - struct intel_plane *intel_plane = to_intel_plane(st->plane); - struct drm_i915_gem_object *obj; - - if (!st->old.fb) - continue; - - obj = to_intel_framebuffer(st->old.fb)->obj; - - if (!st->old.fb) - continue; - - obj = to_intel_framebuffer(st->old.fb)->obj; - - /* Only one bit per plane in pending_flips */ - if (atomic_read(&obj->pending_flip) & (1 << (16 + intel_plane->pipe))) - return -EBUSY; - }
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, base.head) atomic_pipe_commit(dev, s, intel_crtc->pipe); @@ -1582,8 +1546,6 @@ static int apply_nonblocking(struct drm_device *dev, struct intel_atomic_state * /* don't restore the old state in end() */ s->dirty = false; s->restore_state = false; - - return 0; }
static int alloc_flip_data(struct drm_device *dev, struct intel_atomic_state *s) @@ -1695,12 +1657,7 @@ static int intel_atomic_commit(struct drm_device *dev, void *state)
/* try to apply in a non blocking manner */ if (s->flags & DRM_MODE_ATOMIC_NONBLOCK) { - ret = apply_nonblocking(dev, s); - if (ret) { - unpin_cursors(dev, s); - unpin_fbs(dev, s); - return ret; - } + apply_nonblocking(dev, s); } else { /* apply in a blocking manner */ ret = apply_config(dev, s); @@ -1823,7 +1780,6 @@ static void intel_flip_complete(struct drm_flip *flip) struct intel_flip *intel_flip = container_of(flip, struct intel_flip, base); struct drm_device *dev = intel_flip->crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = intel_flip->crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; @@ -1841,18 +1797,6 @@ static void intel_flip_complete(struct drm_flip *flip)
if (intel_flip->vblank_ref) drm_vblank_put(dev, pipe); - - /* Possibly allow rendering to old_bo again */ - if (intel_flip->old_bo) { - if (intel_flip->plane) { - struct intel_plane *intel_plane = to_intel_plane(intel_flip->plane); - atomic_clear_mask(1 << (16 + intel_plane->pipe), &intel_flip->old_bo->pending_flip.counter); - } else - atomic_clear_mask(1 << intel_crtc->plane, &intel_flip->old_bo->pending_flip.counter); - - if (atomic_read(&intel_flip->old_bo->pending_flip) == 0) - wake_up(&dev_priv->pending_flip_queue); - } }
static void intel_flip_finish(struct drm_flip *flip) @@ -2193,13 +2137,6 @@ static void atomic_pipe_commit(struct drm_device *dev, mutex_lock(&dev->struct_mutex); drm_gem_object_reference(&intel_flip->old_bo->base); mutex_unlock(&dev->struct_mutex); - - /* - * Block clients from rendering to the new back buffer until - * the flip occurs and the object is no longer visible. - */ - atomic_set_mask(1 << intel_crtc->plane, - &intel_flip->old_bo->pending_flip.counter); }
list_add_tail(&intel_flip->base.list, &flips); @@ -2244,13 +2181,6 @@ static void atomic_pipe_commit(struct drm_device *dev, mutex_lock(&dev->struct_mutex); drm_gem_object_reference(&intel_flip->old_bo->base); mutex_unlock(&dev->struct_mutex); - - /* - * Block clients from rendering to the new back buffer until - * the flip occurs and the object is no longer visible. - */ - atomic_set_mask(1 << (16 + intel_plane->pipe), - &intel_flip->old_bo->pending_flip.counter); }
list_add_tail(&intel_flip->base.list, &flips);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Avoids a NULL pointer dereference if the atomic modeset fails.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index ac7fe88..98af0ae 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -726,7 +726,8 @@ static int pin_cursors(struct drm_device *dev, if (ret) goto unpin;
- st->cursor_pinned = true; + if (intel_crtc->cursor_bo) + st->cursor_pinned = true; }
return 0;
From: Ville Syrjälä ville.syrjala@linux.intel.com
We have more than one sprite, so a boolean simply won't cut it. Turn sprite_scaling_enabled into a bitmask and track the state of sprite scaler for each sprite independently.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 2 +- drivers/gpu/drm/i915/intel_sprite.c | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0bafe7f..6f865bf 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -729,7 +729,7 @@ typedef struct drm_i915_private {
/* overlay */ struct intel_overlay *overlay; - bool sprite_scaling_enabled; + unsigned int sprite_scaling_enabled;
/* LVDS info */ int backlight_level; /* restore backlight to this value */ diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 693b265..ce3b950 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -345,15 +345,16 @@ ivb_prepare_plane(struct drm_plane *plane) */ if (regs->scale & SPRITE_SCALE_ENABLE) { if (!dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = true; + dev_priv->sprite_scaling_enabled |= 1 << pipe; intel_update_watermarks(dev); intel_wait_for_vblank(dev, pipe); } } else { - if (dev_priv->sprite_scaling_enabled) { - dev_priv->sprite_scaling_enabled = false; + if (dev_priv->sprite_scaling_enabled & (1 << pipe)) { + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); /* potentially re-enable LP watermarks */ - intel_update_watermarks(dev); + if (!dev_priv->sprite_scaling_enabled) + intel_update_watermarks(dev); } } } @@ -389,7 +390,7 @@ ivb_disable_plane(struct drm_plane *plane) ivb_commit_plane(plane); POSTING_READ(SPRSURF(pipe));
- dev_priv->sprite_scaling_enabled = false; + dev_priv->sprite_scaling_enabled &= ~(1 << pipe); intel_update_watermarks(dev); }
From: Ander Conselvan de Oliveira ander.conselvan.de.oliveira@intel.com
intel_crtc->cursor_visible is only changed on the commit phase, so the check for visibility was considering the previous state. Change this to intel_crtc->cursor_handle instead. --- drivers/gpu/drm/i915/intel_atomic.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 98af0ae..c4cec40 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1304,7 +1304,7 @@ static int check_crtc(struct intel_crtc_state *s) } }
- if (intel_crtc->cursor_visible && + if (intel_crtc->cursor_handle && (intel_crtc->cursor_width != 64 || intel_crtc->cursor_height != 64)) { return -EINVAL;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index c4cec40..bfc0563 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -555,9 +555,9 @@ static void plane_compute_dirty(struct intel_atomic_state *s, return;
if (plane->src_x != state->old.src_x || - plane->src_y != state->old.src_x || - plane->src_w != state->old.src_x || - plane->src_h != state->old.src_x || + plane->src_y != state->old.src_y || + plane->src_w != state->old.src_w || + plane->src_h != state->old.src_h || plane->crtc_x != state->old.crtc_x || plane->crtc_y != state->old.crtc_y || plane->crtc_w != state->old.crtc_w ||
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index bfc0563..a119896 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1641,13 +1641,15 @@ static int intel_atomic_commit(struct drm_device *dev, void *state) struct intel_atomic_state *s = state; int ret;
- if (!s->dirty) - return 0; - ret = alloc_flip_data(dev, s); if (ret) return ret;
+ if (!s->dirty) { + queue_remaining_events(dev, s); + return 0; + } + ret = pin_fbs(dev, s); if (ret) return ret;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Don't unpin the old fb after flips, unless a new fb was pinned, or we're disabling the plane/crtc.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index a119896..b795cb2 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2133,8 +2133,10 @@ static void atomic_pipe_commit(struct drm_device *dev, intel_flip->old_cursor_bo = st->old.cursor_bo; }
- if (st->old.fb) { + if (st->old.fb) intel_flip->old_fb_id = st->old.fb->base.id; + + if (st->fb_dirty && st->old.fb) { intel_flip->old_bo = to_intel_framebuffer(st->old.fb)->obj;
mutex_lock(&dev->struct_mutex); @@ -2177,8 +2179,10 @@ static void atomic_pipe_commit(struct drm_device *dev,
intel_plane->calc(plane, plane->fb, &st->coords);
- if (st->old.fb) { + if (st->old.fb) intel_flip->old_fb_id = st->old.fb->base.id; + + if (st->dirty && st->old.fb) { intel_flip->old_bo = to_intel_framebuffer(st->old.fb)->obj;
mutex_lock(&dev->struct_mutex);
From: Ville Syrjälä ville.syrjala@linux.intel.com
These function no longer exist. --- drivers/gpu/drm/i915/intel_atomic.c | 7 ------- 1 files changed, 0 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index b795cb2..7a7695b 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -916,13 +916,6 @@ static void update_plane_obj(struct drm_device *dev, } }
-/* FIXME need to refactor the sprite code some more */ -int intel_disable_plane_nopin(struct drm_plane *plane); -int intel_commit_plane_nopin(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, - const struct intel_plane_coords *coords); - static struct drm_pending_atomic_event *alloc_event(struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
From: Ville Syrjälä ville.syrjala@linux.intel.com
Atomic code might need i915_gem_check_olr().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_gem.c | 2 +- 2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6f865bf..6bb8b3f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1520,6 +1520,7 @@ void i915_gem_retire_requests(struct drm_device *dev); void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring); int __must_check i915_gem_check_wedge(struct drm_i915_private *dev_priv, bool interruptible); +int i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno);
void i915_gem_reset(struct drm_device *dev); void i915_gem_clflush_object(struct drm_i915_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 9530592..f2f63ba 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -969,7 +969,7 @@ i915_gem_check_wedge(struct drm_i915_private *dev_priv, * Compare seqno against outstanding lazy request. Emit a request if they are * equal. */ -static int +int i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) { int ret;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Collect the part which takes care of issuing the flips to a new function. This makes the following patch nicer to look at.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 37 ++++++++++++++++++++-------------- 1 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 7a7695b..33357ec 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2075,15 +2075,35 @@ static void intel_flip_fini(struct drm_device *dev) drm_flip_driver_fini(&dev_priv->flip_driver); }
+static void intel_atomic_schedule_flips(struct drm_i915_private *dev_priv, + struct intel_crtc *intel_crtc, + struct list_head *flips) +{ + if (!intel_crtc->active) { + drm_flip_driver_complete_flips(&dev_priv->flip_driver, flips); + return; + } + + drm_flip_driver_prepare_flips(&dev_priv->flip_driver, flips); + + local_irq_disable(); + + intel_pipe_vblank_evade(&intel_crtc->base); + + drm_flip_driver_schedule_flips(&dev_priv->flip_driver, flips); + + local_irq_enable(); +} + static void atomic_pipe_commit(struct drm_device *dev, struct intel_atomic_state *state, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_file_private *file_priv = state->file->driver_priv; + struct intel_crtc *intel_crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); LIST_HEAD(flips); int i; - bool pipe_enabled = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe))->active;
for (i = 0; i < dev->mode_config.num_crtc; i++) { struct intel_crtc_state *st = &state->crtc[i]; @@ -2189,20 +2209,7 @@ static void atomic_pipe_commit(struct drm_device *dev, if (list_empty(&flips)) return;
- if (!pipe_enabled) { - drm_flip_driver_complete_flips(&dev_priv->flip_driver, &flips); - return; - } - - drm_flip_driver_prepare_flips(&dev_priv->flip_driver, &flips); - - local_irq_disable(); - - intel_pipe_vblank_evade(intel_get_crtc_for_pipe(dev, pipe)); - - drm_flip_driver_schedule_flips(&dev_priv->flip_driver, &flips); - - local_irq_enable(); + intel_atomic_schedule_flips(dev_priv, intel_crtc, &flips); }
void intel_atomic_handle_vblank(struct drm_device *dev, int pipe)
From: Ville Syrjälä ville.syrjala@linux.intel.com
After the atomic flip has been split up into individual flip requests for each scanout engine, put each such request into a FIFO. Then for each flip request add new request to the ring(s) in order to get an interrupt once the GPU has finished whatever it was doing with the new front buffer. Once the flip requests associated with the same atomic flip and the same pipe at the head of the queue are ready, pull them from the queue and issue the flips to the hardware.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 8 ++ drivers/gpu/drm/i915/i915_irq.c | 2 + drivers/gpu/drm/i915/intel_atomic.c | 215 ++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 1 + 4 files changed, 222 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6bb8b3f..80b4b30 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -960,6 +960,14 @@ typedef struct drm_i915_private { struct i915_dri1_state dri1;
struct drm_flip_driver flip_driver; + + struct { + struct list_head list; + spinlock_t lock; + struct work_struct work; + struct workqueue_struct *wq; + unsigned int next_flip_seq; + } flip; } drm_i915_private_t;
/* Iterate over initialised rings */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 430f201..2f0dce2 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -349,6 +349,8 @@ static void notify_ring(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private;
+ intel_atomic_notify_ring(dev, ring); + if (ring->obj == NULL) return;
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 33357ec..2ebceec 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -30,6 +30,8 @@
#include "intel_drv.h"
+#define USE_WRITE_SEQNO + struct intel_flip { struct drm_flip base; u32 vbl_count; @@ -42,6 +44,9 @@ struct intel_flip { struct drm_pending_atomic_event *event; uint32_t old_fb_id; struct list_head pending_head; + struct intel_ring_buffer *ring; + u32 seqno; + unsigned int flip_seq; };
struct intel_plane_state { @@ -801,6 +806,7 @@ static int pin_fbs(struct drm_device *dev, struct intel_atomic_state *s) { int i, ret; + bool nonblock = s->flags & DRM_MODE_ATOMIC_NONBLOCK;
for (i = 0; i < dev->mode_config.num_crtc; i++) { struct intel_crtc_state *st = &s->crtc[i]; @@ -816,7 +822,7 @@ static int pin_fbs(struct drm_device *dev, obj = to_intel_framebuffer(crtc->fb)->obj;
mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + ret = intel_pin_and_fence_fb_obj(dev, obj, nonblock ? obj->ring : NULL); mutex_unlock(&dev->struct_mutex);
if (ret) @@ -839,7 +845,7 @@ static int pin_fbs(struct drm_device *dev, obj = to_intel_framebuffer(plane->fb)->obj;
mutex_lock(&dev->struct_mutex); - ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + ret = intel_pin_and_fence_fb_obj(dev, obj, nonblock ? obj->ring : NULL); mutex_unlock(&dev->struct_mutex);
if (ret) @@ -2040,6 +2046,8 @@ static const struct drm_flip_driver_funcs intel_flip_driver_funcs = { .flush = intel_flip_driver_flush, };
+static void intel_atomic_process_flips_work(struct work_struct *work); + static void intel_flip_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2058,6 +2066,11 @@ static void intel_flip_init(struct drm_device *dev) list_for_each_entry(intel_plane, &dev->mode_config.plane_list, base.head) drm_flip_helper_init(&intel_plane->flip_helper, &dev_priv->flip_driver, &intel_flip_funcs); + + INIT_LIST_HEAD(&dev_priv->flip.list); + spin_lock_init(&dev_priv->flip.lock); + INIT_WORK(&dev_priv->flip.work, intel_atomic_process_flips_work); + dev_priv->flip.wq = create_singlethread_workqueue("intel_flip"); }
static void intel_flip_fini(struct drm_device *dev) @@ -2075,6 +2088,30 @@ static void intel_flip_fini(struct drm_device *dev) drm_flip_driver_fini(&dev_priv->flip_driver); }
+static bool intel_atomic_postpone_flip(struct intel_flip *intel_flip) +{ + struct intel_ring_buffer *ring = intel_flip->ring; + int ret; + + ret = i915_gem_check_olr(ring, intel_flip->seqno); + if (WARN_ON(ret)) { + intel_flip->ring = NULL; + return false; + } + + if (i915_seqno_passed(ring->get_seqno(ring, true), intel_flip->seqno)) { + intel_flip->ring = NULL; + return false; + } + + if (WARN_ON(!ring->irq_get(ring))) { + intel_flip->ring = NULL; + return false; + } + + return true; +} + static void intel_atomic_schedule_flips(struct drm_i915_private *dev_priv, struct intel_crtc *intel_crtc, struct list_head *flips) @@ -2095,15 +2132,120 @@ static void intel_atomic_schedule_flips(struct drm_i915_private *dev_priv, local_irq_enable(); }
+static bool intel_atomic_flips_ready(struct drm_device *dev, unsigned int flip_seq) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_flip *intel_flip; + + /* check if all flips w/ same flip_seq are ready */ + list_for_each_entry(intel_flip, &dev_priv->flip.list, base.list) { + if (intel_flip->flip_seq != flip_seq) + break; + + if (intel_flip->ring) + return false; + } + + return true; +} + +static void intel_atomic_process_flips_work(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = container_of(work, struct drm_i915_private, flip.work); + struct drm_device *dev = dev_priv->dev; + + for (;;) { + struct intel_flip *intel_flip, *next; + unsigned int flip_seq; + struct intel_crtc *intel_crtc; + LIST_HEAD(flips); + unsigned long flags; + + if (list_empty(&dev_priv->flip.list)) + return; + + spin_lock_irqsave(&dev_priv->flip.lock, flags); + + intel_flip = list_first_entry(&dev_priv->flip.list, struct intel_flip, base.list); + flip_seq = intel_flip->flip_seq; + intel_crtc = to_intel_crtc(intel_flip->crtc); + + if (intel_atomic_flips_ready(dev, flip_seq)) { + list_for_each_entry_safe(intel_flip, next, &dev_priv->flip.list, base.list) { + if (intel_flip->flip_seq != flip_seq) + break; + list_move_tail(&intel_flip->base.list, &flips); + } + } + + spin_unlock_irqrestore(&dev_priv->flip.lock, flags); + + if (list_empty(&flips)) + return; + + mutex_lock(&dev->mode_config.mutex); + intel_atomic_schedule_flips(dev_priv, intel_crtc, &flips); + mutex_unlock(&dev->mode_config.mutex); + } +} + +static void intel_atomic_check_flips_ready(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_flip *intel_flip; + + if (list_empty(&dev_priv->flip.list)) + return; + + intel_flip = list_first_entry(&dev_priv->flip.list, struct intel_flip, base.list); + if (intel_atomic_flips_ready(dev, intel_flip->flip_seq)) + queue_work(dev_priv->flip.wq, &dev_priv->flip.work); +} + +void intel_atomic_notify_ring(struct drm_device *dev, + struct intel_ring_buffer *ring) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_flip *intel_flip; + unsigned long flags; + u32 seqno; + + if (list_empty(&dev_priv->flip.list)) + return; + + seqno = ring->get_seqno(ring, false); + + spin_lock_irqsave(&dev_priv->flip.lock, flags); + + list_for_each_entry(intel_flip, &dev_priv->flip.list, base.list) { + if (ring != intel_flip->ring) + continue; + + if (i915_seqno_passed(seqno, intel_flip->seqno)) { + intel_flip->ring = NULL; + ring->irq_put(ring); + } + } + + intel_atomic_check_flips_ready(dev); + + spin_unlock_irqrestore(&dev_priv->flip.lock, flags); +} + static void atomic_pipe_commit(struct drm_device *dev, struct intel_atomic_state *state, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_file_private *file_priv = state->file->driver_priv; - struct intel_crtc *intel_crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); LIST_HEAD(flips); int i; + /* FIXME treat flips for all pipes as one set for GPU sync */ + unsigned int flip_seq = dev_priv->flip.next_flip_seq++; + struct intel_flip *intel_flip, *next; + unsigned long flags; + struct intel_ring_buffer *ring; + unsigned int rings_mask = 0;
for (i = 0; i < dev->mode_config.num_crtc; i++) { struct intel_crtc_state *st = &state->crtc[i]; @@ -2122,6 +2264,8 @@ static void atomic_pipe_commit(struct drm_device *dev,
drm_flip_init(&intel_flip->base, &intel_crtc->flip_helper);
+ intel_flip->flip_seq = flip_seq; + if (st->event) { intel_flip->event = st->event; st->event = NULL; @@ -2134,6 +2278,19 @@ static void atomic_pipe_commit(struct drm_device *dev,
intel_flip->crtc = crtc;
+ if (crtc->fb) { + struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj; + + mutex_lock(&dev->struct_mutex); +#ifdef USE_WRITE_SEQNO + intel_flip->seqno = obj->last_write_seqno; +#else + intel_flip->seqno = obj->last_read_seqno; +#endif + intel_flip->ring = obj->ring; + mutex_unlock(&dev->struct_mutex); + } + /* update primary_disabled befoer calc_plane() */ intel_crtc->primary_disabled = st->primary_disabled;
@@ -2177,6 +2334,8 @@ static void atomic_pipe_commit(struct drm_device *dev,
drm_flip_init(&intel_flip->base, &intel_plane->flip_helper);
+ intel_flip->flip_seq = flip_seq; + if (st->event) { intel_flip->event = st->event; st->event = NULL; @@ -2190,6 +2349,19 @@ static void atomic_pipe_commit(struct drm_device *dev, intel_flip->crtc = intel_get_crtc_for_pipe(dev, pipe); intel_flip->plane = plane;
+ if (plane->fb) { + struct drm_i915_gem_object *obj = to_intel_framebuffer(plane->fb)->obj; + + mutex_lock(&dev->struct_mutex); +#ifdef USE_WRITE_SEQNO + intel_flip->seqno = obj->last_write_seqno; +#else + intel_flip->seqno = obj->last_read_seqno; +#endif + intel_flip->ring = obj->ring; + mutex_unlock(&dev->struct_mutex); + } + intel_plane->calc(plane, plane->fb, &st->coords);
if (st->old.fb) @@ -2209,7 +2381,42 @@ static void atomic_pipe_commit(struct drm_device *dev, if (list_empty(&flips)) return;
- intel_atomic_schedule_flips(dev_priv, intel_crtc, &flips); + mutex_lock(&dev->struct_mutex); + + list_for_each_entry(intel_flip, &flips, base.list) { + struct intel_ring_buffer *ring = intel_flip->ring; + + if (!ring) + continue; + + if (intel_atomic_postpone_flip(intel_flip)) + rings_mask |= intel_ring_flag(ring); + } + + spin_lock_irqsave(&dev_priv->flip.lock, flags); + + list_for_each_entry_safe(intel_flip, next, &flips, base.list) + list_move_tail(&intel_flip->base.list, &dev_priv->flip.list); + + /* if no rings are involved, we can avoid checking seqnos */ + if (rings_mask == 0) + intel_atomic_check_flips_ready(dev); + + spin_unlock_irqrestore(&dev_priv->flip.lock, flags); + + mutex_unlock(&dev->struct_mutex); + + if (rings_mask == 0) + return; + + /* + * Double check to catch cases where the irq + * fired before the flip was placed onto flip.list. + */ + for_each_ring(ring, dev_priv, i) { + if (rings_mask & intel_ring_flag(ring)) + intel_atomic_notify_ring(dev, ring); + } }
void intel_atomic_handle_vblank(struct drm_device *dev, int pipe) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 24dc192..446fa1c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -730,6 +730,7 @@ extern void intel_atomic_fini(struct drm_device *dev); extern void intel_atomic_free_events(struct drm_device *dev, struct drm_file *file); extern void intel_atomic_handle_vblank(struct drm_device *dev, int pipe); extern void intel_atomic_clear_flips(struct drm_crtc *crtc); +extern void intel_atomic_notify_ring(struct drm_device *dev, struct intel_ring_buffer *ring);
extern void intel_enable_primary(struct drm_crtc *crtc); extern void intel_disable_primary(struct drm_crtc *crtc);
From: Ville Syrjälä ville.syrjala@linux.intel.com
If the GPU hangs, release all pending atomic flips from the queue.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_irq.c | 1 + drivers/gpu/drm/i915/intel_atomic.c | 29 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2f0dce2..47d6067 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1486,6 +1486,7 @@ void i915_handle_error(struct drm_device *dev, bool wedged) */ for_each_ring(ring, dev_priv, i) wake_up_all(&ring->irq_queue); + intel_atomic_wedged(dev); }
queue_work(dev_priv->wq, &dev_priv->error_work); diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 2ebceec..f59be7f 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2232,6 +2232,30 @@ void intel_atomic_notify_ring(struct drm_device *dev, spin_unlock_irqrestore(&dev_priv->flip.lock, flags); }
+void intel_atomic_wedged(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_flip *intel_flip; + unsigned long flags; + + spin_lock_irqsave(&dev_priv->flip.lock, flags); + + list_for_each_entry(intel_flip, &dev_priv->flip.list, base.list) { + struct intel_ring_buffer *ring = intel_flip->ring; + + if (ring) { + intel_flip->ring = NULL; + ring->irq_put(ring); + } + } + + /* all flips are "ready" so no need to check with intel_atomic_flips_ready() */ + if (!list_empty(&dev_priv->flip.list)) + queue_work(dev_priv->flip.wq, &dev_priv->flip.work); + + spin_unlock_irqrestore(&dev_priv->flip.lock, flags); +} + static void atomic_pipe_commit(struct drm_device *dev, struct intel_atomic_state *state, int pipe) @@ -2409,6 +2433,11 @@ static void atomic_pipe_commit(struct drm_device *dev, if (rings_mask == 0) return;
+ if (atomic_read(&dev_priv->mm.wedged)) { + intel_atomic_wedged(dev); + return; + } + /* * Double check to catch cases where the irq * fired before the flip was placed onto flip.list. diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 446fa1c..33fac47 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -731,6 +731,7 @@ extern void intel_atomic_free_events(struct drm_device *dev, struct drm_file *fi extern void intel_atomic_handle_vblank(struct drm_device *dev, int pipe); extern void intel_atomic_clear_flips(struct drm_crtc *crtc); extern void intel_atomic_notify_ring(struct drm_device *dev, struct intel_ring_buffer *ring); +extern void intel_atomic_wedged(struct drm_device *dev);
extern void intel_enable_primary(struct drm_crtc *crtc); extern void intel_disable_primary(struct drm_crtc *crtc);
From: Ville Syrjälä ville.syrjala@linux.intel.com
The register values are computed when the flip ioctl is issued, and they're used only after we've waited for the GPU to finish rendering. The computed values are store in the intel_crtc and intel_plane structs, so issuing another flip before the previous one has been fully completed would clobber those stored registers. Fix the problem by making a copy of the calculated register values inside the intel_flip structure. The copy is then used when it's time to commit the registers to the hardware.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 4 +++- drivers/gpu/drm/i915/intel_atomic.c | 8 ++++++-- drivers/gpu/drm/i915/intel_display.c | 14 ++++++++------ drivers/gpu/drm/i915/intel_drv.h | 2 +- drivers/gpu/drm/i915/intel_sprite.c | 24 ++++++++++++------------ 5 files changed, 30 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 80b4b30..d8e8a4f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -269,6 +269,8 @@ struct drm_i915_error_state { struct intel_display_error_state *display; };
+struct intel_plane_regs; + struct drm_i915_display_funcs { bool (*fbc_enabled)(struct drm_device *dev); void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval); @@ -300,7 +302,7 @@ struct drm_i915_display_funcs { int x, int y); int (*calc_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y); - void (*commit_plane)(struct drm_crtc *crtc); + void (*commit_plane)(struct drm_crtc *crtc, const struct intel_plane_regs *regs); /* clock updates for mode set */ /* cursor updates */ /* render clock increase/decrease */ diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index f59be7f..e52d92a 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -47,6 +47,8 @@ struct intel_flip { struct intel_ring_buffer *ring; u32 seqno; unsigned int flip_seq; + /* FIXME need cursor regs too */ + struct intel_plane_regs regs; };
struct intel_plane_state { @@ -1986,11 +1988,11 @@ static bool intel_flip_flip(struct drm_flip *flip, struct drm_plane *plane = intel_flip->plane; struct intel_plane *intel_plane = to_intel_plane(plane);
- intel_plane->commit(plane); + intel_plane->commit(plane, &intel_flip->regs); } else { struct drm_i915_private *dev_priv = dev->dev_private;
- dev_priv->display.commit_plane(crtc); + dev_priv->display.commit_plane(crtc, &intel_flip->regs); }
if (intel_flip->has_cursor) @@ -2321,6 +2323,7 @@ static void atomic_pipe_commit(struct drm_device *dev, /* should already be checked so can't fail */ /* FIXME refactor the failing parts? */ dev_priv->display.calc_plane(crtc, crtc->fb, crtc->x, crtc->y); + intel_flip->regs = intel_crtc->primary_regs;
if (st->cursor_dirty) { intel_flip->has_cursor = true; @@ -2387,6 +2390,7 @@ static void atomic_pipe_commit(struct drm_device *dev, }
intel_plane->calc(plane, plane->fb, &st->coords); + intel_flip->regs = intel_plane->regs;
if (st->old.fb) intel_flip->old_fb_id = st->old.fb->base.id; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c84a839..390251d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1979,13 +1979,13 @@ unsigned long intel_gen4_compute_offset_xtiled(int *x, int *y, return tile_rows * pitch * 8 + tiles * 4096; }
-static void intel_commit_plane(struct drm_crtc *crtc) +static void intel_commit_plane(struct drm_crtc *crtc, + const struct intel_plane_regs *regs) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int plane = intel_crtc->plane; - const struct intel_plane_regs *regs = &intel_crtc->primary_regs;
I915_WRITE(DSPCNTR(plane), regs->cntr); I915_WRITE(DSPSTRIDE(plane), regs->stride); @@ -2102,15 +2102,16 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int ret;
ret = i9xx_calc_plane(crtc, fb, x, y); if (ret) return ret;
- intel_commit_plane(crtc); + intel_commit_plane(crtc, &intel_crtc->primary_regs);
- POSTING_READ(DSPCNTR(to_intel_crtc(crtc)->plane)); + POSTING_READ(DSPCNTR(intel_crtc->plane));
return 0; } @@ -2206,15 +2207,16 @@ static int ironlake_update_plane(struct drm_crtc *crtc, struct drm_framebuffer * int x, int y) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int ret;
ret = ironlake_calc_plane(crtc, fb, x, y); if (ret) return ret;
- intel_commit_plane(crtc); + intel_commit_plane(crtc, &intel_crtc->primary_regs);
- POSTING_READ(DSPCNTR(to_intel_crtc(crtc)->plane)); + POSTING_READ(DSPCNTR(intel_crtc->plane));
return 0; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 33fac47..e39a10b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -277,7 +277,7 @@ struct intel_plane { void (*calc)(struct drm_plane *plane, struct drm_framebuffer *fb, const struct intel_plane_coords *clip); void (*prepare)(struct drm_plane *plane); - void (*commit)(struct drm_plane *plane); + void (*commit)(struct drm_plane *plane, const struct intel_plane_regs *regs); struct intel_plane_regs regs; struct drm_flip_helper flip_helper; }; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index ce3b950..d960e19 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -300,13 +300,13 @@ ivb_calc_plane(struct drm_plane *plane, }
static void -ivb_commit_plane(struct drm_plane *plane) +ivb_commit_plane(struct drm_plane *plane, + const struct intel_plane_regs *regs) { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; - const struct intel_plane_regs *regs = &intel_plane->regs;
I915_WRITE(SPRKEYVAL(pipe), regs->keyval); I915_WRITE(SPRKEYMAX(pipe), regs->keymaxval); @@ -371,7 +371,7 @@ ivb_update_plane(struct drm_plane *plane,
ivb_calc_plane(plane, fb, coords); ivb_prepare_plane(plane); - ivb_commit_plane(plane); + ivb_commit_plane(plane, &intel_plane->regs); POSTING_READ(SPRSURF(pipe)); }
@@ -387,7 +387,7 @@ ivb_disable_plane(struct drm_plane *plane) regs->cntr &= ~SPRITE_ENABLE; /* Can't leave the scaler enabled... */ regs->scale = 0; - ivb_commit_plane(plane); + ivb_commit_plane(plane, regs); POSTING_READ(SPRSURF(pipe));
dev_priv->sprite_scaling_enabled &= ~(1 << pipe); @@ -413,7 +413,7 @@ ivb_update_colorkey(struct drm_plane *plane, else if (key->flags & I915_SET_COLORKEY_SOURCE) regs->cntr |= SPRITE_SOURCE_KEY;
- ivb_commit_plane(plane); + ivb_commit_plane(plane, regs); POSTING_READ(SPRKEYMSK(intel_plane->pipe));
return 0; @@ -546,13 +546,13 @@ ilk_prepare_plane(struct drm_plane *plane) }
static void -ilk_commit_plane(struct drm_plane *plane) +ilk_commit_plane(struct drm_plane *plane, + const struct intel_plane_regs *regs) { struct drm_device *dev = plane->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; - const struct intel_plane_regs *regs = &intel_plane->regs;
I915_WRITE(DVSKEYVAL(pipe), regs->keyval); I915_WRITE(DVSKEYMAX(pipe), regs->keymaxval); @@ -579,7 +579,7 @@ ilk_update_plane(struct drm_plane *plane,
ilk_calc_plane(plane, fb, coords); ilk_prepare_plane(plane); - ilk_commit_plane(plane); + ilk_commit_plane(plane, &intel_plane->regs); POSTING_READ(DVSSURF(pipe)); }
@@ -595,7 +595,7 @@ ilk_disable_plane(struct drm_plane *plane) regs->cntr &= ~DVS_ENABLE; /* Disable the scaler */ regs->scale = 0; - ilk_commit_plane(plane); + ilk_commit_plane(plane, regs); POSTING_READ(DVSSURF(pipe)); }
@@ -627,7 +627,7 @@ intel_enable_primary(struct drm_crtc *crtc) intel_update_fbc(dev);
regs->cntr = I915_READ(reg) | DISPLAY_PLANE_ENABLE; - dev_priv->display.commit_plane(crtc); + dev_priv->display.commit_plane(crtc, regs); }
void @@ -643,7 +643,7 @@ intel_disable_primary(struct drm_crtc *crtc) return;
regs->cntr = I915_READ(reg) & ~DISPLAY_PLANE_ENABLE; - dev_priv->display.commit_plane(crtc); + dev_priv->display.commit_plane(crtc, regs);
intel_crtc->primary_disabled = true; intel_update_fbc(dev); @@ -668,7 +668,7 @@ ilk_update_colorkey(struct drm_plane *plane, else if (key->flags & I915_SET_COLORKEY_SOURCE) regs->cntr |= DVS_SOURCE_KEY;
- ilk_commit_plane(plane); + ilk_commit_plane(plane, regs); POSTING_READ(DVSKEYMSK(intel_plane->pipe));
return 0;
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 390251d..2f518be 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3486,6 +3486,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) encoder->disable(encoder);
intel_crtc_wait_for_pending_flips(crtc); + intel_atomic_clear_flips(crtc); drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 36 +++++++++++++++++++++++++++++++++++ 1 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index e52d92a..36446d1 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2471,9 +2471,23 @@ void intel_atomic_handle_vblank(struct drm_device *dev, int pipe) void intel_atomic_clear_flips(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_plane *intel_plane; + struct intel_flip *intel_flip, *next; int pipe = intel_crtc->pipe; + unsigned long flags; + LIST_HEAD(flips); + + /* + * If there are flips still waiting for the GPU, remove them + * from the list, so that they won't be able to move over to + * drm_flip_helpers' possession after we've called + * drm_flip_helper_clear(). + */ + spin_lock_irqsave(&dev_priv->flip.lock, flags); + list_cut_position(&flips, &dev_priv->flip.list, dev_priv->flip.list.prev); + spin_unlock_irqrestore(&dev_priv->flip.lock, flags);
drm_flip_helper_clear(&intel_crtc->flip_helper);
@@ -2481,4 +2495,26 @@ void intel_atomic_clear_flips(struct drm_crtc *crtc) if (intel_plane->pipe == pipe) drm_flip_helper_clear(&intel_plane->flip_helper); } + + /* + * Drop all non-ready flips. Doing this after calling + * drm_flip_helper_clear() maintaines the correct order + * of completion events. + */ + list_for_each_entry_safe(intel_flip, next, &flips, base.list) { + struct intel_ring_buffer *ring = intel_flip->ring; + + if (ring) { + intel_flip->ring = NULL; + ring->irq_put(ring); + } + + intel_flip_complete(&intel_flip->base); + /* + * FIXME drm_flip_helper calls the following functions + * from a workqueue. Perhaps we should do the same here? + */ + intel_flip_finish(&intel_flip->base); + intel_flip_cleanup(&intel_flip->base); + } }
From: Ville Syrjälä ville.syrjala@linux.intel.com
Most of the code for preparing the 'struct intel_flip' instances was identical betwen the CRTC and plane codepaths. Refactor the common parts into a single function.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 122 +++++++++++++++-------------------- 1 files changed, 52 insertions(+), 70 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 36446d1..3717e89 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2258,6 +2258,50 @@ void intel_atomic_wedged(struct drm_device *dev) spin_unlock_irqrestore(&dev_priv->flip.lock, flags); }
+static void intel_atomic_flip_init(struct intel_flip *intel_flip, + struct drm_device *dev, + u32 flip_seq, + struct drm_i915_file_private *file_priv, + struct drm_pending_atomic_event *event, + struct drm_framebuffer *fb, + bool unpin_old_fb, struct drm_framebuffer *old_fb) +{ + intel_flip->flip_seq = flip_seq; + + if (event) { + intel_flip->event = event; + + /* need to keep track of it in case process exits */ + spin_lock_irq(&dev->event_lock); + list_add_tail(&intel_flip->pending_head, &file_priv->pending_flips); + spin_unlock_irq(&dev->event_lock); + } + + if (fb) { + struct drm_i915_gem_object *obj = to_intel_framebuffer(fb)->obj; + + mutex_lock(&dev->struct_mutex); +#ifdef USE_WRITE_SEQNO + intel_flip->seqno = obj->last_write_seqno; +#else + intel_flip->seqno = obj->last_read_seqno; +#endif + intel_flip->ring = obj->ring; + mutex_unlock(&dev->struct_mutex); + } + + if (old_fb) + intel_flip->old_fb_id = old_fb->base.id; + + if (unpin_old_fb && old_fb) { + intel_flip->old_bo = to_intel_framebuffer(old_fb)->obj; + + mutex_lock(&dev->struct_mutex); + drm_gem_object_reference(&intel_flip->old_bo->base); + mutex_unlock(&dev->struct_mutex); + } +} + static void atomic_pipe_commit(struct drm_device *dev, struct intel_atomic_state *state, int pipe) @@ -2290,33 +2334,13 @@ static void atomic_pipe_commit(struct drm_device *dev,
drm_flip_init(&intel_flip->base, &intel_crtc->flip_helper);
- intel_flip->flip_seq = flip_seq; - - if (st->event) { - intel_flip->event = st->event; - st->event = NULL; - /* need to keep track of it in case process exits */ - spin_lock_irq(&dev->event_lock); - list_add_tail(&intel_flip->pending_head, - &file_priv->pending_flips); - spin_unlock_irq(&dev->event_lock); - } + intel_atomic_flip_init(intel_flip, dev, flip_seq, + file_priv, st->event, crtc->fb, + st->fb_dirty, st->old.fb); + st->event = NULL;
intel_flip->crtc = crtc;
- if (crtc->fb) { - struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj; - - mutex_lock(&dev->struct_mutex); -#ifdef USE_WRITE_SEQNO - intel_flip->seqno = obj->last_write_seqno; -#else - intel_flip->seqno = obj->last_read_seqno; -#endif - intel_flip->ring = obj->ring; - mutex_unlock(&dev->struct_mutex); - } - /* update primary_disabled befoer calc_plane() */ intel_crtc->primary_disabled = st->primary_disabled;
@@ -2330,17 +2354,6 @@ static void atomic_pipe_commit(struct drm_device *dev, intel_flip->old_cursor_bo = st->old.cursor_bo; }
- if (st->old.fb) - intel_flip->old_fb_id = st->old.fb->base.id; - - if (st->fb_dirty && st->old.fb) { - intel_flip->old_bo = to_intel_framebuffer(st->old.fb)->obj; - - mutex_lock(&dev->struct_mutex); - drm_gem_object_reference(&intel_flip->old_bo->base); - mutex_unlock(&dev->struct_mutex); - } - list_add_tail(&intel_flip->base.list, &flips); }
@@ -2361,48 +2374,17 @@ static void atomic_pipe_commit(struct drm_device *dev,
drm_flip_init(&intel_flip->base, &intel_plane->flip_helper);
- intel_flip->flip_seq = flip_seq; - - if (st->event) { - intel_flip->event = st->event; - st->event = NULL; - /* need to keep track of it in case process exits */ - spin_lock_irq(&dev->event_lock); - list_add_tail(&intel_flip->pending_head, - &file_priv->pending_flips); - spin_unlock_irq(&dev->event_lock); - } + intel_atomic_flip_init(intel_flip, dev, flip_seq, + file_priv, st->event, plane->fb, + st->dirty, st->old.fb); + st->event = NULL;
intel_flip->crtc = intel_get_crtc_for_pipe(dev, pipe); intel_flip->plane = plane;
- if (plane->fb) { - struct drm_i915_gem_object *obj = to_intel_framebuffer(plane->fb)->obj; - - mutex_lock(&dev->struct_mutex); -#ifdef USE_WRITE_SEQNO - intel_flip->seqno = obj->last_write_seqno; -#else - intel_flip->seqno = obj->last_read_seqno; -#endif - intel_flip->ring = obj->ring; - mutex_unlock(&dev->struct_mutex); - } - intel_plane->calc(plane, plane->fb, &st->coords); intel_flip->regs = intel_plane->regs;
- if (st->old.fb) - intel_flip->old_fb_id = st->old.fb->base.id; - - if (st->dirty && st->old.fb) { - intel_flip->old_bo = to_intel_framebuffer(st->old.fb)->obj; - - mutex_lock(&dev->struct_mutex); - drm_gem_object_reference(&intel_flip->old_bo->base); - mutex_unlock(&dev->struct_mutex); - } - list_add_tail(&intel_flip->base.list, &flips); }
From: Ville Syrjälä ville.syrjala@linux.intel.com
The atomic code will want to call intel_modeset_commit_output_state() too.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 2 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2f518be..d950939 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7449,7 +7449,7 @@ void intel_modeset_update_staged_output_state(struct drm_device *dev) * * This function copies the stage display pipe configuration to the real one. */ -static void intel_modeset_commit_output_state(struct drm_device *dev) +void intel_modeset_commit_output_state(struct drm_device *dev) { struct intel_encoder *encoder; struct intel_connector *connector; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e39a10b..d0666d8 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -740,5 +740,6 @@ extern void intel_crtc_attach_properties(struct drm_crtc *crtc); extern void intel_crtc_update_properties(struct drm_crtc *crtc);
extern void intel_modeset_update_staged_output_state(struct drm_device *dev); +extern void intel_modeset_commit_output_state(struct drm_device *dev);
#endif /* __INTEL_DRV_H__ */
From: Ville Syrjälä ville.syrjala@linux.intel.com
Use the new_crtc and new_encoder pointes inside the intel_encoder and intel_connector structures instead of swapping the crtc and encoder pointers in the drm base structures around the disable calls.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 115 ++++++++++++++-------------------- 1 files changed, 47 insertions(+), 68 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 3717e89..3f093d6 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -115,43 +115,57 @@ struct intel_atomic_state { struct drm_encoder *saved_encoders; };
-static void update_connectors_bitmask(struct drm_crtc *crtc, +static void update_connectors_bitmask(struct intel_crtc *intel_crtc, unsigned long *connectors_bitmask) { - struct drm_device *dev = crtc->dev; - struct drm_connector *connector; + struct drm_device *dev = intel_crtc->base.dev; + struct intel_connector *intel_connector; unsigned int i = 0;
*connectors_bitmask = 0;
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder && connector->encoder->crtc == crtc) + list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) { + if (intel_connector->new_encoder && + intel_connector->new_encoder->new_crtc == intel_crtc) __set_bit(i, connectors_bitmask);
i++; } }
-static void update_encoders_bitmask(struct drm_crtc *crtc, +static void update_encoders_bitmask(struct intel_crtc *intel_crtc, unsigned long *encoders_bitmask) { - struct drm_device *dev = crtc->dev; - struct drm_encoder *encoder; + struct drm_device *dev = intel_crtc->base.dev; + struct intel_encoder *intel_encoder; unsigned int i = 0;
*encoders_bitmask = 0;
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc == crtc) + list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) { + if (intel_encoder->new_crtc == intel_crtc) __set_bit(i, encoders_bitmask);
i++; } }
+static bool intel_encoder_in_use(struct intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct intel_connector *intel_connector; + + list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) + if (intel_connector->new_encoder == intel_encoder) + return true; + + return false; +} + static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, int count_ids) { struct drm_crtc *crtc = s->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_device *dev = crtc->dev; struct drm_connector *connectors[count_ids]; struct drm_connector *connector; @@ -184,6 +198,9 @@ static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, i }
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct intel_connector *intel_connector = to_intel_connector(connector); + struct intel_encoder *intel_encoder; + for (i = 0; i < count_ids; i++) { if (connector == connectors[i]) break; @@ -192,25 +209,28 @@ static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, i /* this connector isn't in the set */ if (i == count_ids) { /* remove the link to the encoder if this crtc was set to drive it */ - if (connector->encoder && connector->encoder->crtc == crtc) - connector->encoder = NULL; + if (intel_connector->new_encoder && + intel_connector->new_encoder->new_crtc == intel_crtc) + intel_connector->new_encoder = NULL; continue; }
- encoder = intel_best_encoder(connector); + intel_encoder = to_intel_encoder(intel_best_encoder(connector));
- connector->encoder = encoder; - encoder->crtc = crtc; + intel_connector->new_encoder = intel_encoder; + intel_encoder->new_crtc = intel_crtc; }
/* prune dangling encoder->crtc links pointing to this crtc */ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc == crtc && !drm_helper_encoder_in_use(encoder)) - encoder->crtc = NULL; + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + + if (intel_encoder->new_crtc == intel_crtc && !intel_encoder_in_use(intel_encoder)) + intel_encoder->new_crtc = NULL; }
- update_connectors_bitmask(s->crtc, &s->connectors_bitmask); - update_encoders_bitmask(s->crtc, &s->encoders_bitmask); + update_connectors_bitmask(intel_crtc, &s->connectors_bitmask); + update_encoders_bitmask(intel_crtc, &s->encoders_bitmask);
return 0; } @@ -232,19 +252,6 @@ static size_t intel_atomic_state_size(const struct drm_device *dev) num_plane * sizeof state->saved_planes[0]; }
- -static void populate_old(struct drm_device *dev) -{ - struct drm_encoder *encoder; - struct drm_connector *connector; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) - encoder->old_crtc = encoder->crtc; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->old_encoder = connector->encoder; -} - static void *intel_atomic_begin(struct drm_device *dev, struct drm_file *file, uint32_t flags, uint64_t user_data) { @@ -275,8 +282,6 @@ static void *intel_atomic_begin(struct drm_device *dev, struct drm_file *file, state->saved_crtcs = (struct intel_crtc *)(state->saved_encoders + num_encoder); state->saved_planes = (struct drm_plane *)(state->saved_crtcs + num_crtc);
- populate_old(dev); - i = 0; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct intel_crtc_state *s = &state->crtc[i++]; @@ -297,8 +302,8 @@ static void *intel_atomic_begin(struct drm_device *dev, struct drm_file *file, s->old.x = crtc->x; s->old.y = crtc->y;
- update_connectors_bitmask(crtc, &s->connectors_bitmask); - update_encoders_bitmask(crtc, &s->encoders_bitmask); + update_connectors_bitmask(intel_crtc, &s->connectors_bitmask); + update_encoders_bitmask(intel_crtc, &s->encoders_bitmask);
s->old.connectors_bitmask = s->connectors_bitmask; s->old.encoders_bitmask = s->encoders_bitmask; @@ -1045,39 +1050,11 @@ static void queue_remaining_events(struct drm_device *dev, struct intel_atomic_s } }
-static void swap_old_new(struct drm_device *dev, - struct intel_atomic_state *s) -{ - struct drm_encoder *encoder; - struct drm_connector *connector; - int i; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) - swap(encoder->crtc, encoder->old_crtc); - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) - swap(connector->encoder, connector->old_encoder); - - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct intel_crtc_state *st = &s->crtc[i]; - struct drm_crtc *crtc = st->crtc; - - swap(crtc->enabled, st->old.enabled); - } -} - static int apply_config(struct drm_device *dev, struct intel_atomic_state *s) { int i, ret;
- /* - * FIXME -` * Hackish way to make crtc_disable() see the current - * state (actually just some select pieces of it). - */ - swap_old_new(dev, s); - for (i = 0; i < dev->mode_config.num_crtc; i++) { struct intel_crtc_state *st = &s->crtc[i];
@@ -1097,8 +1074,7 @@ static int apply_config(struct drm_device *dev, crtc_prepare(st, st->crtc); }
- /* Undo the hack above. */ - swap_old_new(dev, s); + intel_modeset_commit_output_state(dev);
for (i = 0; i < dev->mode_config.num_crtc; i++) { struct intel_crtc_state *st = &s->crtc[i]; @@ -1204,6 +1180,9 @@ static void restore_state(struct drm_device *dev, list_for_each_entry(plane, &dev->mode_config.plane_list, head) *plane = s->saved_planes[i++];
+ /* must restore the new_crtc and new_encoder pointers as well */ + intel_modeset_update_staged_output_state(dev); + /* FIXME props etc. */
/* was the hardware state clobbered? */ @@ -1318,8 +1297,9 @@ static int check_crtc(struct intel_crtc_state *s)
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
- if (encoder->crtc != crtc) + if (intel_encoder->new_crtc != intel_crtc) continue;
if (!encoder_funcs->mode_fixup(encoder, &crtc->mode, &crtc->hwmode)) @@ -1693,7 +1673,6 @@ static int intel_atomic_commit(struct drm_device *dev, void *state)
update_props(dev, s);
- intel_modeset_update_staged_output_state(dev);
return 0; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
These are not needed anymore.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- include/drm/drm_crtc.h | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c8cea87..da9abb6 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -509,7 +509,6 @@ struct drm_encoder { struct drm_crtc *crtc; const struct drm_encoder_funcs *funcs; void *helper_private; - struct drm_crtc *old_crtc; };
enum drm_connector_force { @@ -615,7 +614,6 @@ struct drm_connector { int audio_latency[2]; int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ unsigned bad_edid_counter; - struct drm_encoder *old_encoder; };
/**
From: Ville Syrjälä ville.syrjala@linux.intel.com
The atomic check() hook is interested in the staged new configuration, so it must use the intel_encoder::new_crtc pointer when checking if a CRTC is in use or not.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 16 ++++++++++++++-- 1 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 3f093d6..ec79354 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -150,6 +150,18 @@ static void update_encoders_bitmask(struct intel_crtc *intel_crtc, } }
+static bool intel_crtc_in_use(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct intel_encoder *intel_encoder; + + list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) + if (intel_encoder->new_crtc == intel_crtc) + return true; + + return false; +} + static bool intel_encoder_in_use(struct intel_encoder *intel_encoder) { struct drm_device *dev = intel_encoder->base.dev; @@ -1246,12 +1258,12 @@ static int check_crtc(struct intel_crtc_state *s) if (crtc->enabled) { if (!fb) return -EINVAL; - if (!drm_helper_crtc_in_use(crtc)) + if (!intel_crtc_in_use(intel_crtc)) return -EINVAL; } else { if (fb) return -EINVAL; - if (drm_helper_crtc_in_use(crtc)) + if (intel_crtc_in_use(intel_crtc)) return -EINVAL; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add a comment that outlines some of the missign/incomplete parts of the atomic code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index ec79354..415cd72 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -24,6 +24,21 @@ * Ville Syrjälä ville.syrjala@linux.intel.com */
+/* + * TODO: + * - check crtc.enabled old vs. new state handling + * - generate a correct timestamp for override flips + * - send flip events at correct time when previous flip is pending, + * and nothing changed in the later flip + * - flip_seq should be the same for all flips issued at the same time + * - make GPU reset handling robust + * - old style frame counter still has possible issues + * - move primary plane scanout handling into a drm_plane + * - cursor register handling needs work + * - should drm_plane be used for cursors too? + * - more refactoring and cleanups + */ + #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_flip.h>
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_atomic.c | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 415cd72..efdaff3 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -717,7 +717,6 @@ static void crtc_commit(struct drm_crtc *crtc) dev_priv->display.crtc_enable(&intel_crtc->base);
/* FIXME need to check where this stuff is used really */ - /* FIXME need to update DPMS state somewhere */ list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, base.head) { if (intel_encoder->base.crtc == crtc) intel_encoder->connectors_active = true; @@ -1210,8 +1209,6 @@ static void restore_state(struct drm_device *dev, /* must restore the new_crtc and new_encoder pointers as well */ intel_modeset_update_staged_output_state(dev);
- /* FIXME props etc. */ - /* was the hardware state clobbered? */ if (s->restore_hw) apply_config(dev, s); @@ -1523,7 +1520,6 @@ static void update_crtc(struct drm_device *dev, struct intel_crtc_state *st = &s->crtc[i]; struct drm_crtc *crtc = st->crtc;
- /* FIXME is this OK? */ if (st->fb_dirty && !st->mode_dirty) { mutex_lock(&dev->struct_mutex); intel_update_fbc(dev);
From: Ville Syrjälä ville.syrjala@linux.intel.com
A new trace point for tracking changes to gem object pin count.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_gem.c | 6 ++++++ drivers/gpu/drm/i915/i915_trace.h | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f2f63ba..d4b76d9 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3482,6 +3482,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, if (!obj->has_global_gtt_mapping && map_and_fenceable) i915_gem_gtt_bind_object(obj, obj->cache_level);
+ trace_i915_gem_object_pin_count(obj, obj->pin_count, obj->pin_count + 1); + obj->pin_count++; obj->pin_mappable |= map_and_fenceable;
@@ -3491,6 +3493,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, void i915_gem_object_unpin(struct drm_i915_gem_object *obj) { + trace_i915_gem_object_pin_count(obj, obj->pin_count, obj->pin_count - 1); + BUG_ON(obj->pin_count == 0); BUG_ON(obj->gtt_space == NULL);
@@ -3769,6 +3773,8 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) if (obj->phys_obj) i915_gem_detach_phys_object(dev, obj);
+ trace_i915_gem_object_pin_count(obj, obj->pin_count, 0); + obj->pin_count = 0; if (WARN_ON(i915_gem_object_unbind(obj) == -ERESTARTSYS)) { bool was_interruptible; diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 3db4a68..44f8d40 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -16,6 +16,25 @@
/* object tracking */
+TRACE_EVENT(i915_gem_object_pin_count, + TP_PROTO(struct drm_i915_gem_object *obj, u32 pin_count_pre, u32 pin_count_post), + TP_ARGS(obj, pin_count_pre, pin_count_post), + + TP_STRUCT__entry( + __field(struct drm_i915_gem_object *, obj) + __field(u32, pin_count_pre) + __field(u32, pin_count_post) + ), + + TP_fast_assign( + __entry->obj = obj; + __entry->pin_count_pre = pin_count_pre; + __entry->pin_count_post = pin_count_post; + ), + + TP_printk("obj=%p, pin_count=%u->%u", __entry->obj, __entry->pin_count_pre, __entry->pin_count_post) +); + TRACE_EVENT(i915_gem_object_create, TP_PROTO(struct drm_i915_gem_object *obj), TP_ARGS(obj),
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_trace.h | 15 +++++++++++++++ drivers/gpu/drm/i915/intel_atomic.c | 9 ++++++++- 3 files changed, 24 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index d8e8a4f..53998f41 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -969,6 +969,7 @@ typedef struct drm_i915_private { struct work_struct work; struct workqueue_struct *wq; unsigned int next_flip_seq; + unsigned int queue_len; } flip; } drm_i915_private_t;
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 44f8d40..f71e1b3 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -16,6 +16,21 @@
/* object tracking */
+TRACE_EVENT(i915_flip_queue_len, + TP_PROTO(unsigned int queue_len), + TP_ARGS(queue_len), + + TP_STRUCT__entry( + __field(u32, queue_len) + ), + + TP_fast_assign( + __entry->queue_len = queue_len; + ), + + TP_printk("queue_len=%u", __entry->queue_len) +); + TRACE_EVENT(i915_gem_object_pin_count, TP_PROTO(struct drm_i915_gem_object *obj, u32 pin_count_pre, u32 pin_count_post), TP_ARGS(obj, pin_count_pre, pin_count_post), diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index efdaff3..ef5e352 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -2179,9 +2179,12 @@ static void intel_atomic_process_flips_work(struct work_struct *work) if (intel_flip->flip_seq != flip_seq) break; list_move_tail(&intel_flip->base.list, &flips); + dev_priv->flip.queue_len--; } }
+ trace_i915_flip_queue_len(dev_priv->flip.queue_len); + spin_unlock_irqrestore(&dev_priv->flip.lock, flags);
if (list_empty(&flips)) @@ -2407,8 +2410,12 @@ static void atomic_pipe_commit(struct drm_device *dev,
spin_lock_irqsave(&dev_priv->flip.lock, flags);
- list_for_each_entry_safe(intel_flip, next, &flips, base.list) + list_for_each_entry_safe(intel_flip, next, &flips, base.list) { list_move_tail(&intel_flip->base.list, &dev_priv->flip.list); + dev_priv->flip.queue_len++; + } + + trace_i915_flip_queue_len(dev_priv->flip.queue_len);
/* if no rings are involved, we can avoid checking seqnos */ if (rings_mask == 0)
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add a module parameter that allows one to easily change between blocking and non-blocking GPU synchronization with atomic page flips.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_stub.c | 5 ++ drivers/gpu/drm/i915/i915_trace.h | 49 ++++++++++++++ drivers/gpu/drm/i915/intel_atomic.c | 119 ++++++++++++++++++++++++++++++++++- 3 files changed, 172 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 200e104..b73fde8 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -52,6 +52,9 @@ EXPORT_SYMBOL(drm_timestamp_precision); */ unsigned int drm_timestamp_monotonic = 1;
+unsigned int drm_async_gpu = 1; /* 1 to enable async gpu wait */ +EXPORT_SYMBOL(drm_async_gpu); + MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); @@ -59,11 +62,13 @@ MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); +MODULE_PARM_DESC(async_gpu, "Async GPU wait");
module_param_named(debug, drm_debug, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); +module_param_named(async_gpu, drm_async_gpu, int, 0600);
struct idr drm_minors_idr;
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index f71e1b3..754a204 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -16,6 +16,55 @@
/* object tracking */
+TRACE_EVENT(i915_atomic_flip, + TP_PROTO(bool sprite, int pipe, int action, u32 commit_surf, u32 commit_surflive, u32 surf, u32 surflive, u32 iir, u32 commit_dsl, u32 dsl, u32 flip_vbl_count, u32 vbl_count), + TP_ARGS(sprite, pipe, action, commit_surf, commit_surflive, surf, surflive, iir, commit_dsl, dsl, flip_vbl_count, vbl_count), + + TP_STRUCT__entry( + __field(bool, sprite) + __field(int, pipe) + __field(int, action) + __field(u32, commit_surf) + __field(u32, commit_surflive) + __field(u32, surf) + __field(u32, surflive) + __field(u32, iir) + __field(u32, commit_dsl) + __field(u32, dsl) + __field(u32, flip_vbl_count) + __field(u32, vbl_count) + ), + + TP_fast_assign( + __entry->sprite = sprite; + __entry->pipe = pipe; + __entry->action = action; + __entry->commit_surf = commit_surf; + __entry->commit_surflive = commit_surflive; + __entry->surf = surf; + __entry->surflive = surflive; + __entry->iir = iir; + __entry->commit_dsl = commit_dsl; + __entry->dsl = dsl; + __entry->flip_vbl_count = flip_vbl_count; + __entry->vbl_count = vbl_count; + ), + + TP_printk( + "%s/%d %s commit_surf=%x commit_surflive=%x surf=%x surflive=%x iir=%x commit_dsl=%u dsl=%u flip_vbl_count=%u vbl_count=%u", + __entry->sprite ? "SPR" : "DSP", __entry->pipe, + __entry->action == 0 ? "new" : + __entry->action == 1 ? "flipped" : + __entry->action == 2 ? "not flipped" : + __entry->action == 3 ? "missed flipped" : "?", + __entry->commit_surf, __entry->commit_surflive, + __entry->surf, __entry->surflive, + __entry->iir, + __entry->commit_dsl, __entry->dsl, + __entry->flip_vbl_count, __entry->vbl_count + ) +); + TRACE_EVENT(i915_flip_queue_len, TP_PROTO(unsigned int queue_len), TP_ARGS(queue_len), diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index ef5e352..c964b64a 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -47,6 +47,8 @@
#define USE_WRITE_SEQNO
+//#define SURFLIVE_DEBUG + struct intel_flip { struct drm_flip base; u32 vbl_count; @@ -64,6 +66,11 @@ struct intel_flip { unsigned int flip_seq; /* FIXME need cursor regs too */ struct intel_plane_regs regs; +#ifdef SURFLIVE_DEBUG + u32 commit_dsl; + u32 commit_surf; + u32 commit_surflive; +#endif };
struct intel_plane_state { @@ -835,11 +842,13 @@ static void unpin_fbs(struct drm_device *dev, } }
+extern unsigned int drm_async_gpu; + static int pin_fbs(struct drm_device *dev, struct intel_atomic_state *s) { int i, ret; - bool nonblock = s->flags & DRM_MODE_ATOMIC_NONBLOCK; + bool nonblock = drm_async_gpu && (s->flags & DRM_MODE_ATOMIC_NONBLOCK);
for (i = 0; i < dev->mode_config.num_crtc; i++) { struct intel_crtc_state *st = &s->crtc[i]; @@ -1971,6 +1980,48 @@ static void intel_flip_prepare(struct drm_flip *flip) } }
+#ifdef SURFLIVE_DEBUG +enum flip_action { + _NEW, + _FLIPPED, + _NOT_FLIPPED, + _MISSED_FLIPPED, +}; + +static void trace_flip(struct intel_flip *intel_flip, enum flip_action action) +{ + struct drm_crtc *crtc = intel_flip->crtc; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = to_intel_crtc(crtc)->pipe; + u32 surf; + u32 surflive; + u32 dsl; + u32 iir; + u32 vbl_count; + + if (intel_flip->plane) { + surf = I915_READ(SPRSURF(pipe)); + surflive = I915_READ(SPRSURFLIVE(pipe)); + } else { + surf = I915_READ(DSPSURF(pipe)); + surflive = I915_READ(DSPSURFLIVE(pipe)); + } + dsl = I915_READ(PIPEDSL(pipe)); + iir = I915_READ(DEIIR); + vbl_count = get_vbl_count(crtc); + + trace_i915_atomic_flip(intel_flip->plane != NULL, pipe, action, + intel_flip->commit_surf, intel_flip->commit_surflive, + surf, surflive, iir, intel_flip->commit_dsl, dsl, + intel_flip->vbl_count, vbl_count); +} +#endif + +#ifdef SURFLIVE_DEBUG +static unsigned int missed_flips; +#endif + static bool intel_flip_flip(struct drm_flip *flip, struct drm_flip *pending_flip) { @@ -1980,6 +2031,9 @@ static bool intel_flip_flip(struct drm_flip *flip, struct drm_device *dev = crtc->dev; int pipe = intel_crtc->pipe; u32 vbl_count; +#ifdef SURFLIVE_DEBUG + struct drm_i915_private *dev_priv = dev->dev_private; +#endif
intel_flip->vblank_ref = drm_vblank_get(dev, pipe) == 0;
@@ -1991,10 +2045,26 @@ static bool intel_flip_flip(struct drm_flip *flip, struct intel_plane *intel_plane = to_intel_plane(plane);
intel_plane->commit(plane, &intel_flip->regs); + +#ifdef SURFLIVE_DEBUG + intel_flip->commit_dsl = I915_READ(PIPEDSL(pipe)); + intel_flip->commit_surf = I915_READ(SPRSURF(pipe)); + intel_flip->commit_surflive = I915_READ(SPRSURFLIVE(pipe)); + if (intel_flip->commit_surf != intel_flip->regs.surf) + pr_err("SPRITE SURF MISMATCH\n"); +#endif } else { struct drm_i915_private *dev_priv = dev->dev_private;
dev_priv->display.commit_plane(crtc, &intel_flip->regs); + +#ifdef SURFLIVE_DEBUG + intel_flip->commit_dsl = I915_READ(PIPEDSL(pipe)); + intel_flip->commit_surf = I915_READ(DSPSURF(pipe)); + intel_flip->commit_surflive = I915_READ(DSPSURFLIVE(pipe)); + if (intel_flip->commit_surf != intel_flip->regs.surf) + pr_err("PRIMARY PLANE SURF MISMATCH\n"); +#endif }
if (intel_flip->has_cursor) @@ -2011,16 +2081,38 @@ static bool intel_flip_flip(struct drm_flip *flip, else intel_flip->vbl_count = (vbl_count + 1) & 0xffffff;
+#ifdef SURFLIVE_DEBUG + trace_flip(intel_flip, _NEW); +#endif + if (pending_flip) { struct intel_flip *old_intel_flip = container_of(pending_flip, struct intel_flip, base); bool flipped = intel_vbl_check(pending_flip, vbl_count);
if (!flipped) { +#ifdef SURFLIVE_DEBUG + u32 surflive = I915_READ(old_intel_flip->plane ? SPRSURFLIVE(pipe) : DSPSURFLIVE(pipe)); + if (old_intel_flip->commit_surflive != surflive) + trace_flip(old_intel_flip, _NOT_FLIPPED); +#endif swap(intel_flip->old_fb_id, old_intel_flip->old_fb_id); swap(intel_flip->old_bo, old_intel_flip->old_bo); swap(intel_flip->old_cursor_bo, old_intel_flip->old_cursor_bo); } +#ifdef SURFLIVE_DEBUG + else { + u32 surflive = I915_READ(old_intel_flip->plane ? SPRSURFLIVE(pipe) : DSPSURFLIVE(pipe)); + if (old_intel_flip->commit_surf != surflive) { + trace_flip(old_intel_flip, _FLIPPED); + missed_flips++; + return false; + } + if (missed_flips) + trace_flip(old_intel_flip, _MISSED_FLIPPED); + missed_flips = 0; + } +#endif
return flipped; } @@ -2034,7 +2126,26 @@ static bool intel_flip_vblank(struct drm_flip *pending_flip) container_of(pending_flip, struct intel_flip, base); u32 vbl_count = get_vbl_count(old_intel_flip->crtc);
+#ifdef SURFLIVE_DEBUG + struct drm_i915_private *dev_priv = old_intel_flip->crtc->dev->dev_private; + int pipe = to_intel_crtc(old_intel_flip->crtc)->pipe; + bool flipped; + flipped = intel_vbl_check(pending_flip, vbl_count); + if (flipped) { + u32 surflive = I915_READ(old_intel_flip->plane ? SPRSURFLIVE(pipe) : DSPSURFLIVE(pipe)); + if (old_intel_flip->commit_surf != surflive) { + trace_flip(old_intel_flip, _FLIPPED); + missed_flips++; + return false; + } + if (missed_flips) + trace_flip(old_intel_flip, _MISSED_FLIPPED); + missed_flips = 0; + } + return flipped; +#else return intel_vbl_check(pending_flip, vbl_count); +#endif }
static const struct drm_flip_helper_funcs intel_flip_funcs = { @@ -2396,6 +2507,12 @@ static void atomic_pipe_commit(struct drm_device *dev, if (list_empty(&flips)) return;
+ if (!drm_async_gpu) { + struct intel_crtc *intel_crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); + intel_atomic_schedule_flips(dev_priv, intel_crtc, &flips); + return; + } + mutex_lock(&dev->struct_mutex);
list_for_each_entry(intel_flip, &flips, base.list) {
From: Ville Syrjälä ville.syrjala@linux.intel.com
--- drivers/gpu/drm/i915/intel_atomic.c | 15 +++++++++++++++ 1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index c964b64a..43767c2 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -48,6 +48,7 @@ #define USE_WRITE_SEQNO
//#define SURFLIVE_DEBUG +//#define FLIP_TIME_DEBUG
struct intel_flip { struct drm_flip base; @@ -1842,6 +1843,10 @@ static void intel_flip_cleanup(struct drm_flip *flip) kfree(intel_flip); }
+#ifdef FLIP_TIME_DEBUG +static ktime_t tstart, tend, tdiff; +#endif + static void intel_flip_driver_flush(struct drm_flip_driver *driver) { struct drm_i915_private *dev_priv = @@ -1849,6 +1854,13 @@ static void intel_flip_driver_flush(struct drm_flip_driver *driver)
/* Flush posted writes */ I915_READ(PIPEDSL(PIPE_A)); + +#ifdef FLIP_TIME_DEBUG + tend = ktime_get(); + tdiff = ktime_sub(tend, tstart); + if (ktime_to_ns(tdiff) >= 10000ULL) + DRM_DEBUG_KMS("**** flip took %llu nsec ****\n", ktime_to_ns(tdiff)); +#endif }
static bool intel_have_new_frmcount(struct drm_device *dev) @@ -2242,6 +2254,9 @@ static void intel_atomic_schedule_flips(struct drm_i915_private *dev_priv,
intel_pipe_vblank_evade(&intel_crtc->base);
+#ifdef FLIP_TIME_DEBUG + tstart = ktime_get(); +#endif drm_flip_driver_schedule_flips(&dev_priv->flip_driver, flips);
local_irq_enable();
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_trace.h | 19 +++++++++++++++++++ drivers/gpu/drm/i915/intel_atomic.c | 4 ++++ 2 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 754a204..fb35edd 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -16,6 +16,25 @@
/* object tracking */
+TRACE_EVENT(i915_atomic_flush, + TP_PROTO(u32 dsl0, u32 dsl1, u32 dsl2), + TP_ARGS(dsl0, dsl1, dsl2), + + TP_STRUCT__entry( + __field(u32, dsl0) + __field(u32, dsl1) + __field(u32, dsl2) + ), + TP_fast_assign( + __entry->dsl0 = dsl0; + __entry->dsl1 = dsl1; + __entry->dsl2 = dsl2; + ), + + TP_printk("dsl(0)=%u dsl(1)=%u dsl(2)=%u", __entry->dsl0, __entry->dsl1, __entry->dsl2) +); + + TRACE_EVENT(i915_atomic_flip, TP_PROTO(bool sprite, int pipe, int action, u32 commit_surf, u32 commit_surflive, u32 surf, u32 surflive, u32 iir, u32 commit_dsl, u32 dsl, u32 flip_vbl_count, u32 vbl_count), TP_ARGS(sprite, pipe, action, commit_surf, commit_surflive, surf, surflive, iir, commit_dsl, dsl, flip_vbl_count, vbl_count), diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 43767c2..0270d7e 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1855,6 +1855,10 @@ static void intel_flip_driver_flush(struct drm_flip_driver *driver) /* Flush posted writes */ I915_READ(PIPEDSL(PIPE_A));
+#ifdef SURFLIVE_DEBUG + trace_i915_atomic_flush(I915_READ(PIPEDSL(0)), I915_READ(PIPEDSL(1)), I915_READ(PIPEDSL(2))); +#endif + #ifdef FLIP_TIME_DEBUG tend = ktime_get(); tdiff = ktime_sub(tend, tstart);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_trace.h | 18 ++++++++++++++++++ drivers/gpu/drm/i915/intel_atomic.c | 2 ++ 2 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index fb35edd..2b529f2 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -16,6 +16,24 @@
/* object tracking */
+TRACE_EVENT(i915_atomic_evade, + TP_PROTO(u32 dsl_min, u32 dsl_max, u32 dsl), + TP_ARGS(dsl_min, dsl_max, dsl), + + TP_STRUCT__entry( + __field(u32, dsl_min) + __field(u32, dsl_max) + __field(u32, dsl) + ), + TP_fast_assign( + __entry->dsl_min = dsl_min; + __entry->dsl_max = dsl_max; + __entry->dsl = dsl; + ), + + TP_printk("dsl_min=%u dsl_max=%u dsl=%u", __entry->dsl_min, __entry->dsl_max, __entry->dsl) +); + TRACE_EVENT(i915_atomic_flush, TP_PROTO(u32 dsl0, u32 dsl1, u32 dsl2), TP_ARGS(dsl0, dsl1, dsl2), diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 0270d7e..993a7dc 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -1950,6 +1950,8 @@ static void intel_pipe_vblank_evade(struct drm_crtc *crtc) intel_crtc->vbl_received = false;
val = I915_READ(PIPEDSL(pipe)); + + trace_i915_atomic_evade(min, max, val); }
if (vblank_ref)
On Wed, Dec 12, 2012 at 06:15:27PM +0200, ville.syrjala@linux.intel.com wrote:
Another month, another massive atomic patchset.
I managed to clean up warts left over from the modeset-rework rebase, but other than that I haven't really found the time to touch this too much since the last time I posted patches from this set.
Seeing as my schedule isn't getting any less busy in the forseeable future, it would be nice to get this monster merged sooner rather than never.
Ever since the code started to resemble something sane, I've tried to avoid squashing patches, just in case someone was actually trying to follow what's changed. But clearly some of the patches can be squashed, and that would probably allow some of the earlier ones to be dropped entirely.
What's clearly needed is a test tool that will stresss the mode setting side of the code more. My glplane test just changes the modes in the beginning, and then just does flips and other plane stuff. I'll try to get something done on that front before Christmas.
Repo is here: https://gitorious.org/vsyrjala/linux/commits/drm_atomic_23
The repo also contains 64bit get_user() implementation for x86-32, which I sent to lkml and x86 people for review. So this should now be testable even on a 32bit system.
Can you please upload your testcase somewhere so that I can play around with this without too much hassle.
Thanks, Daniel
On Thu, Dec 13, 2012 at 03:17:38PM +0100, Daniel Vetter wrote:
On Wed, Dec 12, 2012 at 06:15:27PM +0200, ville.syrjala@linux.intel.com wrote:
Another month, another massive atomic patchset.
I managed to clean up warts left over from the modeset-rework rebase, but other than that I haven't really found the time to touch this too much since the last time I posted patches from this set.
Seeing as my schedule isn't getting any less busy in the forseeable future, it would be nice to get this monster merged sooner rather than never.
Ever since the code started to resemble something sane, I've tried to avoid squashing patches, just in case someone was actually trying to follow what's changed. But clearly some of the patches can be squashed, and that would probably allow some of the earlier ones to be dropped entirely.
What's clearly needed is a test tool that will stresss the mode setting side of the code more. My glplane test just changes the modes in the beginning, and then just does flips and other plane stuff. I'll try to get something done on that front before Christmas.
Repo is here: https://gitorious.org/vsyrjala/linux/commits/drm_atomic_23
The repo also contains 64bit get_user() implementation for x86-32, which I sent to lkml and x86 people for review. So this should now be testable even on a 32bit system.
Can you please upload your testcase somewhere so that I can play around with this without too much hassle.
It's here: https://gitorious.org/vsyrjala/glplane
It responds to a bunch of keys in some way. Look near the end of main() in plane.c to see which keys do what.
You'll need libdrm too: https://gitorious.org/vsyrjala/drm/commits/drm_atomic_7
And there's an older test here, but I'm not sure it works anymore: https://gitorious.org/vsyrjala/plane
On Wed, Dec 12, 2012 at 06:15:27PM +0200, ville.syrjala@linux.intel.com wrote: ...
Ever since the code started to resemble something sane, I've tried to avoid squashing patches, just in case someone was actually trying to follow what's changed. But clearly some of the patches can be squashed, and that would probably allow some of the earlier ones to be dropped entirely.
FYI I now squashed this down to 56 patches, with 6 being pure debug stuff, ~15 trivial unstatic/export stuff, and the rest are of varying complexity. So we're down to ~35 patches with substance.
Next week I can see about pulling some of the intel specific plane stuff out and send that separately. Those would involve a few framework type patches the core as well. By my quick calculation that could bring the final count down by another 15 or so.
There are also some core drm patches that could be merged at any point, but most of those wouldn't have any use until the atomic stuff starts taking advantage or them (signed range props, resizeable blobs, etc.).
The current set is here: https://gitorious.org/vsyrjala/linux/commits/drm_atomic_24
dri-devel@lists.freedesktop.org