Here's the latest atomic stuff. It's finally starting to look somewhat reasonable.
I rebased the work on top of drm-fixes as of today.
There are quite a few preparatory patches in the series. Some of those are just fixes/cleanups that could be pushed independently.
What's still unfinished: - some integration w/ legacy code paths is missing (eg. for the MODE and CONNECTOR_IDS props) - ioctl blocks until the GPU rendering has caught up. Need to make it fully async. - I took a shortcut w/ danvet's modeset rework new vs. old state logic - some other ugly bits are probably still left in the code, but I'm starting to get tunnel vision and some extra eyes on the code wouldn't hurt - other stuff I might be forgetting...
git repos can be found on gitorious, as usual: https://gitorious.org/vsyrjala/linux/commits/drm_atomic_15 https://gitorious.org/vsyrjala/drm/commits/drm_atomic_7
A test app of sorts can be found here: https://gitorious.org/vsyrjala/plane/commits/buffer_tracking
I also made a modifed version of the test app that renders via GL, but I need to clean that up a bit before I push it. Will do so tomorrow.
From: Ville Syrjälä ville.syrjala@linux.intel.com
Make sure 'width * cpp' and 'height * pitch + offset' don't exceed UINT_MAX.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com Reviewed-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/drm_crtc.c | 10 +++++++++- 1 files changed, 9 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index ef1b221..d9a639c 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2280,13 +2280,21 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
for (i = 0; i < num_planes; i++) { unsigned int width = r->width / (i != 0 ? hsub : 1); + unsigned int height = r->height / (i != 0 ? vsub : 1); + unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
if (!r->handles[i]) { DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); return -EINVAL; }
- if (r->pitches[i] < drm_format_plane_cpp(r->pixel_format, i) * width) { + if ((uint64_t) width * cpp > UINT_MAX) + return -ERANGE; + + if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) + return -ERANGE; + + if (r->pitches[i] < width * cpp) { DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); return -EINVAL; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
None of drm_mode_debug_printmodeline(), drm_mode_equal(), drm_mode_width() or drm_mode_height() change the mode passed in, so make the arguments const.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_modes.c | 8 ++++---- include/drm/drm_crtc.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 59450f3..d8da30e 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -46,7 +46,7 @@ * * Describe @mode using DRM_DEBUG. */ -void drm_mode_debug_printmodeline(struct drm_display_mode *mode) +void drm_mode_debug_printmodeline(const struct drm_display_mode *mode) { DRM_DEBUG_KMS("Modeline %d:"%s" %d %d %d %d %d %d %d %d %d %d " "0x%x 0x%x\n", @@ -558,7 +558,7 @@ EXPORT_SYMBOL(drm_mode_list_concat); * RETURNS: * @mode->hdisplay */ -int drm_mode_width(struct drm_display_mode *mode) +int drm_mode_width(const struct drm_display_mode *mode) { return mode->hdisplay;
@@ -579,7 +579,7 @@ EXPORT_SYMBOL(drm_mode_width); * RETURNS: * @mode->vdisplay */ -int drm_mode_height(struct drm_display_mode *mode) +int drm_mode_height(const struct drm_display_mode *mode) { return mode->vdisplay; } @@ -768,7 +768,7 @@ EXPORT_SYMBOL(drm_mode_duplicate); * RETURNS: * True if the modes are equal, false otherwise. */ -bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2) +bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) { /* do clock check convert to PICOS so fb modes get matched * the same */ diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3fa18b7..49dd8c2 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -887,14 +887,14 @@ extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_ extern void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src); extern struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, const struct drm_display_mode *mode); -extern void drm_mode_debug_printmodeline(struct drm_display_mode *mode); +extern void drm_mode_debug_printmodeline(const struct drm_display_mode *mode); extern void drm_mode_config_init(struct drm_device *dev); extern void drm_mode_config_reset(struct drm_device *dev); extern void drm_mode_config_cleanup(struct drm_device *dev); extern void drm_mode_set_name(struct drm_display_mode *mode); -extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); -extern int drm_mode_width(struct drm_display_mode *mode); -extern int drm_mode_height(struct drm_display_mode *mode); +extern bool drm_mode_equal(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2); +extern int drm_mode_width(const struct drm_display_mode *mode); +extern int drm_mode_height(const struct drm_display_mode *mode);
/* for us by fb module */ extern int drm_mode_attachmode_crtc(struct drm_device *dev,
From: Ville Syrjälä ville.syrjala@linux.intel.com
In case of a blob property drm_property_change_is_valid() can't tell whether the change is valid or not. So just return true for all blob properties, and leave it up to someone else to check it.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/drm_crtc.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d9a639c..3533609 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3212,6 +3212,9 @@ static bool drm_property_change_is_valid(struct drm_property *property, for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); + } else if (property->flags & DRM_MODE_PROP_BLOB) { + /* Only the driver knows */ + return true; } else { int i; for (i = 0; i < property->num_values; i++)
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 3533609..e60beda 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3914,3 +3914,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 49dd8c2..5252c11 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1079,4 +1079,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 e60beda..c0b064e 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4069,3 +4069,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 5252c11..bc89654 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1102,5 +1102,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 c0b064e..d5ceff0 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1853,6 +1853,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 bc89654..6a66704 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 4d58d7e..082a837 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -248,7 +248,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 d5ceff0..ddd7252 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3108,8 +3108,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; @@ -3134,14 +3134,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 6a66704..8eb04c9 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -969,6 +969,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 ddd7252..8ee7e45 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3207,14 +3207,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 8ee7e45..1a7a6aa 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 8eb04c9..45f56ce 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 1227adf..d30e0dd 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -279,8 +279,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; @@ -300,6 +300,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 e01cc80..a17285c 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -166,4 +166,7 @@ extern void drm_helper_hpd_irq_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 d30e0dd..80bbbda 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -307,7 +307,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; @@ -324,6 +324,7 @@ drm_crtc_prepare_encoders(struct drm_device *dev) drm_encoder_disable(encoder); } } +EXPORT_SYMBOL(drm_crtc_prepare_encoders);
/** * drm_crtc_set_mode - set a mode diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index a17285c..d3be5ae 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -168,5 +168,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 1a7a6aa..ad18e23 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -3368,6 +3368,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) { @@ -3376,7 +3389,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; @@ -3389,11 +3401,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 ad18e23..177bc73 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1120,8 +1120,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 || @@ -1147,6 +1147,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 @@ -1162,8 +1163,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; @@ -1187,6 +1188,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 45f56ce..4830b47 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1087,6 +1087,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 177bc73..7a6ed7d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2922,6 +2922,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); @@ -3111,16 +3114,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;
@@ -3130,18 +3141,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); @@ -3200,7 +3233,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);
ret = drm_connector_property_set_value(connector, dev->mode_config.edid_property, diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 4830b47..710f490 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[]; };
@@ -971,7 +972,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 2ff5cef..f98afd8 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 710f490..011f51b 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 7a6ed7d..4736cfe 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -4236,3 +4236,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 3fd8280..f50f63e 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 011f51b..69420e7 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -1089,6 +1089,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); @@ -1129,4 +1131,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 1e3481e..f8d3e8f 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
Fix support for all RGB/BGR pixel formats (except the 16:16:16:16 float format).
Fix intel_init_framebuffer() to match hardware and driver limitations: * RGB332 is not supported at all * CI8 is supported * XRGB1555 & co. are supported on Gen3 and earlier * XRGB210101010 & co. are supported from Gen4 onwards * BGR formats are supported from Gen4 onwards * YUV formats are supported from Gen5 onwards (driver limitation)
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_reg.h | 17 ++++-- drivers/gpu/drm/i915/intel_display.c | 94 ++++++++++++++++++++++------------ 2 files changed, 74 insertions(+), 37 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a4162dd..c2e82cc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2998,12 +2998,19 @@ #define DISPPLANE_GAMMA_ENABLE (1<<30) #define DISPPLANE_GAMMA_DISABLE 0 #define DISPPLANE_PIXFORMAT_MASK (0xf<<26) +#define DISPPLANE_YUV422 (0x0<<26) #define DISPPLANE_8BPP (0x2<<26) -#define DISPPLANE_15_16BPP (0x4<<26) -#define DISPPLANE_16BPP (0x5<<26) -#define DISPPLANE_32BPP_NO_ALPHA (0x6<<26) -#define DISPPLANE_32BPP (0x7<<26) -#define DISPPLANE_32BPP_30BIT_NO_ALPHA (0xa<<26) +#define DISPPLANE_BGRA555 (0x3<<26) +#define DISPPLANE_BGRX555 (0x4<<26) +#define DISPPLANE_BGRX565 (0x5<<26) +#define DISPPLANE_BGRX888 (0x6<<26) +#define DISPPLANE_BGRA888 (0x7<<26) +#define DISPPLANE_RGBX101010 (0x8<<26) +#define DISPPLANE_RGBA101010 (0x9<<26) +#define DISPPLANE_BGRX101010 (0xa<<26) +#define DISPPLANE_RGBX161616 (0xc<<26) +#define DISPPLANE_RGBX888 (0xe<<26) +#define DISPPLANE_RGBA888 (0xf<<26) #define DISPPLANE_STEREO_ENABLE (1<<25) #define DISPPLANE_STEREO_DISABLE 0 #define DISPPLANE_SEL_PIPE_SHIFT 24 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 461a637..2dd19f3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1969,24 +1969,38 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb, dspcntr = I915_READ(reg); /* Mask out pixel format bits in case we change it */ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - switch (fb->bits_per_pixel) { - case 8: + switch (fb->pixel_format) { + case DRM_FORMAT_C8: dspcntr |= DISPPLANE_8BPP; break; - case 16: - if (fb->depth == 15) - dspcntr |= DISPPLANE_15_16BPP; - else - dspcntr |= DISPPLANE_16BPP; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: + dspcntr |= DISPPLANE_BGRX555; break; - case 24: - case 32: - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + case DRM_FORMAT_RGB565: + dspcntr |= DISPPLANE_BGRX565; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + dspcntr |= DISPPLANE_BGRX888; + break; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + dspcntr |= DISPPLANE_RGBX888; + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + dspcntr |= DISPPLANE_BGRX101010; + break; + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + dspcntr |= DISPPLANE_RGBX101010; break; default: - DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel); + DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format); return -EINVAL; } + if (INTEL_INFO(dev)->gen >= 4) { if (obj->tiling_mode != I915_TILING_NONE) dspcntr |= DISPPLANE_TILED; @@ -2053,27 +2067,31 @@ static int ironlake_update_plane(struct drm_crtc *crtc, dspcntr = I915_READ(reg); /* Mask out pixel format bits in case we change it */ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - switch (fb->bits_per_pixel) { - case 8: + switch (fb->pixel_format) { + case DRM_FORMAT_C8: dspcntr |= DISPPLANE_8BPP; break; - case 16: - if (fb->depth != 16) - return -EINVAL; - - dspcntr |= DISPPLANE_16BPP; + case DRM_FORMAT_RGB565: + dspcntr |= DISPPLANE_BGRX565; break; - case 24: - case 32: - if (fb->depth == 24) - dspcntr |= DISPPLANE_32BPP_NO_ALPHA; - else if (fb->depth == 30) - dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA; - else - return -EINVAL; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + dspcntr |= DISPPLANE_BGRX888; + break; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + dspcntr |= DISPPLANE_RGBX888; + break; + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_ARGB2101010: + dspcntr |= DISPPLANE_BGRX101010; + break; + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + dspcntr |= DISPPLANE_RGBX101010; break; default: - DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel); + DRM_ERROR("Unknown pixel format 0x%08x\n", fb->pixel_format); return -EINVAL; }
@@ -7707,24 +7725,36 @@ int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->pitches[0] & 63) return -EINVAL;
+ /* Reject formats not supported by any plane early. */ switch (mode_cmd->pixel_format) { - case DRM_FORMAT_RGB332: + case DRM_FORMAT_C8: case DRM_FORMAT_RGB565: case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: case DRM_FORMAT_ARGB8888: + break; + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_ARGB1555: + if (INTEL_INFO(dev)->gen > 3) + return -EINVAL; + break; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_ARGB2101010: - /* RGB formats are common across chipsets */ + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_ABGR2101010: + if (INTEL_INFO(dev)->gen < 4) + return -EINVAL; break; case DRM_FORMAT_YUYV: case DRM_FORMAT_UYVY: case DRM_FORMAT_YVYU: case DRM_FORMAT_VYUY: + if (INTEL_INFO(dev)->gen < 6) + return -EINVAL; break; default: - DRM_DEBUG_KMS("unsupported pixel format %u\n", - mode_cmd->pixel_format); + DRM_DEBUG_KMS("unsupported pixel format 0x%08x\n", mode_cmd->pixel_format); 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/i915_reg.h | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index c2e82cc..5ea4570 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3031,6 +3031,7 @@ #define _DSPASIZE 0x70190 #define _DSPASURF 0x7019C /* 965+ only */ #define _DSPATILEOFF 0x701A4 /* 965+ only */ +#define _DSPASURFLIVE 0x701AC
#define DSPCNTR(plane) _PIPE(plane, _DSPACNTR, _DSPBCNTR) #define DSPADDR(plane) _PIPE(plane, _DSPAADDR, _DSPBADDR) @@ -3040,6 +3041,7 @@ #define DSPSURF(plane) _PIPE(plane, _DSPASURF, _DSPBSURF) #define DSPTILEOFF(plane) _PIPE(plane, _DSPATILEOFF, _DSPBTILEOFF) #define DSPLINOFF(plane) DSPADDR(plane) +#define DSPSURFLIVE(plane) _PIPE(plane, _DSPASURFLIVE, _DSPBSURFLIVE)
/* Display/Sprite base address macros */ #define DISP_BASEADDR_MASK (0xfffff000) @@ -3085,6 +3087,7 @@ #define _DSPBSIZE 0x71190 #define _DSPBSURF 0x7119C #define _DSPBTILEOFF 0x711A4 +#define _DSPBSURFLIVE 0x711AC
/* Sprite A control */ #define _DVSACNTR 0x72180 @@ -3150,6 +3153,7 @@ #define DVSTILEOFF(pipe) _PIPE(pipe, _DVSATILEOFF, _DVSBTILEOFF) #define DVSKEYVAL(pipe) _PIPE(pipe, _DVSAKEYVAL, _DVSBKEYVAL) #define DVSKEYMSK(pipe) _PIPE(pipe, _DVSAKEYMSK, _DVSBKEYMSK) +#define DVSSURFLIVE(pipe) _PIPE(pipe, _DVSASURFLIVE, _DVSBSURFLIVE)
#define _SPRA_CTL 0x70280 #define SPRITE_ENABLE (1<<31) @@ -3184,6 +3188,7 @@ #define _SPRA_SURF 0x7029c #define _SPRA_KEYMAX 0x702a0 #define _SPRA_TILEOFF 0x702a4 +#define _SPRA_SURFLIVE 0x702ac #define _SPRA_SCALE 0x70304 #define SPRITE_SCALE_ENABLE (1<<31) #define SPRITE_FILTER_MASK (3<<29) @@ -3204,6 +3209,7 @@ #define _SPRB_SURF 0x7129c #define _SPRB_KEYMAX 0x712a0 #define _SPRB_TILEOFF 0x712a4 +#define _SPRB_SURFLIVE 0x712ac #define _SPRB_SCALE 0x71304 #define _SPRB_GAMC 0x71400
@@ -3219,6 +3225,7 @@ #define SPRTILEOFF(pipe) _PIPE(pipe, _SPRA_TILEOFF, _SPRB_TILEOFF) #define SPRSCALE(pipe) _PIPE(pipe, _SPRA_SCALE, _SPRB_SCALE) #define SPRGAMC(pipe) _PIPE(pipe, _SPRA_GAMC, _SPRB_GAMC) +#define SPRSURFLIVE(pipe) _PIPE(pipe, _SPRA_SURFLIVE, _SPRB_SURFLIVE)
/* VBIOS regs */ #define VGACNTRL 0x71400
From: Ville Syrjälä ville.syrjala@linux.intel.com
Add the MI_WAIT_FOR_EVENT bits for sprites, and fix up the whole thing for IVB which moved most of the bits around.
Not tested at all!
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 44 +++++++++++++++++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 9 ++++++ 2 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 3eea143..cb4d9f7 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -625,10 +625,46 @@ i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) if (((flips >> plane) & 1) == 0) continue;
- if (plane) - flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; - else - flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; + if (IS_GEN7(ring->dev)) { + switch (plane) { + case 0: + flip_mask = MI_WAIT_FOR_PLANE_A_FLIP_IVB; + break; + case 1: + flip_mask = MI_WAIT_FOR_PLANE_B_FLIP_IVB; + break; + case 2: + flip_mask = MI_WAIT_FOR_PLANE_C_FLIP_IVB; + break; + case 16: + flip_mask = MI_WAIT_FOR_SPRITE_A_FLIP_IVB; + break; + case 17: + flip_mask = MI_WAIT_FOR_SPRITE_B_FLIP_IVB; + break; + case 18: + flip_mask = MI_WAIT_FOR_SPRITE_C_FLIP_IVB; + break; + } + } else { + switch (plane) { + case 0: + flip_mask = MI_WAIT_FOR_PLANE_A_FLIP; + break; + case 1: + flip_mask = MI_WAIT_FOR_PLANE_B_FLIP; + break; + case 2: + flip_mask = MI_WAIT_FOR_PLANE_C_FLIP; + break; + case 16: + flip_mask = MI_WAIT_FOR_SPRITE_A_FLIP; + break; + case 17: + flip_mask = MI_WAIT_FOR_SPRITE_B_FLIP; + break; + } + }
ret = intel_ring_begin(ring, 2); if (ret) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5ea4570..6d09411 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -188,7 +188,16 @@ #define MI_NOOP MI_INSTR(0, 0) #define MI_USER_INTERRUPT MI_INSTR(0x02, 0) #define MI_WAIT_FOR_EVENT MI_INSTR(0x03, 0) +#define MI_WAIT_FOR_SPRITE_C_FLIP_IVB (1<<20) +#define MI_WAIT_FOR_PLANE_C_FLIP_IVB (1<<15) +#define MI_WAIT_FOR_SPRITE_B_FLIP_IVB (1<<10) +#define MI_WAIT_FOR_PLANE_B_FLIP_IVB (1<<9) +#define MI_WAIT_FOR_SPRITE_A_FLIP_IVB (1<<2) +#define MI_WAIT_FOR_PLANE_A_FLIP_IVB (1<<1) #define MI_WAIT_FOR_OVERLAY_FLIP (1<<16) +#define MI_WAIT_FOR_SPRITE_B_FLIP (1<<16) +#define MI_WAIT_FOR_SPRITE_A_FLIP (1<<8) +#define MI_WAIT_FOR_PLANE_C_FLIP (1<<8) #define MI_WAIT_FOR_PLANE_B_FLIP (1<<6) #define MI_WAIT_FOR_PLANE_A_FLIP (1<<2) #define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
From: Ville Syrjälä ville.syrjala@linux.intel.com
Make sure the the framebuffer stride is smaller than the maximum accepted by any plane.
Also when using a tiled memory make sure the object stride matches the framebuffer stride.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2dd19f3..581772c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7712,6 +7712,17 @@ static const struct drm_framebuffer_funcs intel_fb_funcs = { .create_handle = intel_user_framebuffer_create_handle, };
+static unsigned int intel_max_fb_stride(const struct drm_device *dev) +{ + /* FIXME: BSpec for pre-Gen5 is a bit unclear on stride limits */ + if (INTEL_INFO(dev)->gen <= 3) + return 8192; + else if (INTEL_INFO(dev)->gen <= 4) + return 16384; + else + return 32768; +} + int intel_framebuffer_init(struct drm_device *dev, struct intel_framebuffer *intel_fb, struct drm_mode_fb_cmd2 *mode_cmd, @@ -7725,6 +7736,13 @@ int intel_framebuffer_init(struct drm_device *dev, if (mode_cmd->pitches[0] & 63) return -EINVAL;
+ if (mode_cmd->pitches[0] > intel_max_fb_stride(dev)) + return -EINVAL; + + if (obj->tiling_mode != I915_TILING_NONE && + mode_cmd->pitches[0] != obj->stride) + return -EINVAL; + /* Reject formats not supported by any plane early. */ switch (mode_cmd->pixel_format) { case DRM_FORMAT_C8:
From: Ville Syrjälä ville.syrjala@linux.intel.com
The framebuffer offset must be aligned to (macro)pixel size.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_display.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 581772c..f3fea88 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7729,6 +7729,7 @@ int intel_framebuffer_init(struct drm_device *dev, struct drm_i915_gem_object *obj) { int ret; + unsigned int align = drm_format_plane_cpp(mode_cmd->pixel_format, 0);
if (obj->tiling_mode == I915_TILING_Y) return -EINVAL; @@ -7768,6 +7769,7 @@ int intel_framebuffer_init(struct drm_device *dev, case DRM_FORMAT_UYVY: case DRM_FORMAT_YVYU: case DRM_FORMAT_VYUY: + align <<= 1; if (INTEL_INFO(dev)->gen < 6) return -EINVAL; break; @@ -7776,6 +7778,9 @@ int intel_framebuffer_init(struct drm_device *dev, return -EINVAL; }
+ if (mode_cmd->offsets[0] % align) + return -EINVAL; + ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs); if (ret) { DRM_ERROR("framebuffer init failed %d\n", ret);
From: Ville Syrjälä ville.syrjala@linux.intel.com
Take fb->offset[0] into account when calculating the linear and tile x/y offsets.
For non-tiled surfaces fb->offset[0] is simply added to the linear byte offset.
For tiled surfaces treat fb->offsets[0] as a byte offset into the linearized view of the surface. So we end up converting fb->offsets[0] into additional x and y offsets.
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 f3fea88..a4eb64f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1952,6 +1952,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: @@ -2010,13 +2011,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 = fb->offsets[0] + y * fb->pitches[0] + x * cpp;
if (INTEL_INFO(dev)->gen >= 4) { intel_crtc->dspaddr_offset = gen4_compute_dspaddr_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; @@ -2049,6 +2049,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: @@ -2105,11 +2106,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 = fb->offsets[0] + y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); intel_crtc->dspaddr_offset = gen4_compute_dspaddr_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 | 150 ++++++++++++++++++++++++----------- 1 files changed, 102 insertions(+), 48 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 82f5e5c..7d1da04 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -128,11 +128,14 @@ 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); if (obj->tiling_mode != I915_TILING_NONE) { + y += fb->offsets[0] / fb->pitches[0]; + x += fb->offsets[0] % fb->pitches[0] / pixel_size; + I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); } else { unsigned long offset;
- offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + offset = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size; I915_WRITE(SPRLINOFF(pipe), offset); } I915_WRITE(SPRSIZE(pipe), (crtc_h << 16) | crtc_w); @@ -290,11 +293,14 @@ 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); if (obj->tiling_mode != I915_TILING_NONE) { + y += fb->offsets[0] / fb->pitches[0]; + x += fb->offsets[0] % fb->pitches[0] / pixel_size; + I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x); } else { unsigned long offset;
- offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); + offset = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size; I915_WRITE(DVSLINOFF(pipe), offset); } I915_WRITE(DVSSIZE(pipe), (crtc_h << 16) | crtc_w); @@ -408,6 +414,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, @@ -419,65 +439,97 @@ 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; 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; + 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, + };
- intel_fb = to_intel_framebuffer(fb); - obj = intel_fb->obj; + /* Don't modify another pipe's plane */ + if (intel_plane->pipe != intel_crtc->pipe) + return -EINVAL;
- old_obj = intel_plane->obj; + if (fb->width < 3 || fb->height < 3 || fb->pitches[0] > 16384) + return -EINVAL;
- src_w = src_w >> 16; - src_h = src_h >> 16; + hscale = drm_calc_hscale(&src, &dst, 1, intel_plane->max_downscale << 16); + vscale = drm_calc_vscale(&src, &dst, 1, intel_plane->max_downscale << 16);
- /* Pipe must be running... */ - if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE)) - return -EINVAL; + visible = drm_region_clip_scaled(&src, &dst, &clip, hscale, vscale);
- if (crtc_x >= primary_w || crtc_y >= primary_h) - return -EINVAL; + crtc_x = dst.x1; + crtc_y = dst.y1; + crtc_w = drm_region_width(&dst); + crtc_h = drm_region_height(&dst);
- /* Don't modify another pipe's plane */ - if (intel_plane->pipe != intel_crtc->pipe) - return -EINVAL; + /* HW doesn't seem to like smaller sprite, even when scaling */ + /* FIXME return an error instead? */ + if (crtc_w < 3 || crtc_h < 3) + visible = false;
/* - * 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; + /* 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; + } } - if ((crtc_y + crtc_h) <= 0) /* Nothing to display */ - goto out; - if (crtc_y + crtc_h > primary_h) - crtc_h = primary_h - crtc_y;
- if (!crtc_w || !crtc_h) /* Again, nothing to display */ - goto out; + /* 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; + }
- /* - * 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; + /* Pipe must be running... */ + if (!(I915_READ(PIPECONF(pipe)) & PIPECONF_ENABLE)) + return 0;
/* * If the sprite is completely covering the primary plane, @@ -502,11 +554,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) { @@ -526,7 +581,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
Use drm_format_plane_cpp() to get 'pixel_size' in the sprite code.
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_sprite.c | 19 +++---------------- 1 files changed, 3 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 7d1da04..709b978 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -48,7 +48,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, struct intel_plane *intel_plane = to_intel_plane(plane); int pipe = intel_plane->pipe; u32 sprctl, sprscale = 0; - int pixel_size; + int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
sprctl = I915_READ(SPRCTL(pipe));
@@ -61,32 +61,25 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, switch (fb->pixel_format) { case DRM_FORMAT_XBGR8888: sprctl |= SPRITE_FORMAT_RGBX888 | SPRITE_RGB_ORDER_RGBX; - pixel_size = 4; break; case DRM_FORMAT_XRGB8888: sprctl |= SPRITE_FORMAT_RGBX888; - pixel_size = 4; break; case DRM_FORMAT_YUYV: sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YUYV; - pixel_size = 2; break; case DRM_FORMAT_YVYU: sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_YVYU; - pixel_size = 2; break; case DRM_FORMAT_UYVY: sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_UYVY; - pixel_size = 2; break; case DRM_FORMAT_VYUY: sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY; - pixel_size = 2; break; default: DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n"); sprctl |= SPRITE_FORMAT_RGBX888; - pixel_size = 4; break; }
@@ -228,8 +221,9 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, 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, pixel_size; + int pipe = intel_plane->pipe; u32 dvscntr, dvsscale; + int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
dvscntr = I915_READ(DVSCNTR(pipe));
@@ -242,32 +236,25 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, switch (fb->pixel_format) { case DRM_FORMAT_XBGR8888: dvscntr |= DVS_FORMAT_RGBX888 | DVS_RGB_ORDER_XBGR; - pixel_size = 4; break; case DRM_FORMAT_XRGB8888: dvscntr |= DVS_FORMAT_RGBX888; - pixel_size = 4; break; case DRM_FORMAT_YUYV: dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YUYV; - pixel_size = 2; break; case DRM_FORMAT_YVYU: dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_YVYU; - pixel_size = 2; break; case DRM_FORMAT_UYVY: dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_UYVY; - pixel_size = 2; break; case DRM_FORMAT_VYUY: dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY; - pixel_size = 2; break; default: DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n"); dvscntr |= DVS_FORMAT_RGBX888; - pixel_size = 4; break; }
From: Ville Syrjälä ville.syrjala@linux.intel.com
The framebuffer pixel format is already checked by the common code. So there's no way an invalid format could reach the driver. So instead of falling back to a default format, call BUG().
Signed-off-by: Ville Syrjälä ville.syrjala@linux.intel.com --- drivers/gpu/drm/i915/intel_sprite.c | 8 ++------ 1 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 709b978..cd6777f 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -78,9 +78,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, sprctl |= SPRITE_FORMAT_YUV422 | SPRITE_YUV_ORDER_VYUY; break; default: - DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n"); - sprctl |= SPRITE_FORMAT_RGBX888; - break; + BUG(); }
if (obj->tiling_mode != I915_TILING_NONE) @@ -253,9 +251,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb, dvscntr |= DVS_FORMAT_YUV422 | DVS_YUV_ORDER_VYUY; break; default: - DRM_DEBUG_DRIVER("bad pixel format, assuming RGBX888\n"); - dvscntr |= DVS_FORMAT_RGBX888; - break; + BUG(); }
if (obj->tiling_mode != I915_TILING_NONE)
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 c9bfd83..acc4317 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1820,7 +1820,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 fe71425..64d87c2 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -527,7 +527,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 7b30b5c..f087041 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -193,6 +193,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, @@ -273,22 +274,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 | 367 ++++++++++++++++++++++------------ 2 files changed, 247 insertions(+), 135 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 64d87c2..0660d38 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -214,6 +214,15 @@ struct intel_crtc { struct intel_pch_pll *pch_pll; };
+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; @@ -222,11 +231,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 cd6777f..fee6f17 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -36,16 +36,173 @@ #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; + } + + 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; + } + + 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; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); @@ -210,15 +367,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; u32 dvscntr, dvsscale; int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); @@ -397,129 +561,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 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 drm_i915_gem_object *old_obj = intel_plane->obj; - int pipe = intel_plane->pipe; int ret = 0; - int primary_w = crtc->mode.hdisplay, primary_h = crtc->mode.vdisplay; - 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; - - 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); + if (plane->crtc) + intel_enable_primary(plane->crtc);
- /* HW doesn't seem to like smaller sprite, even when scaling */ - /* FIXME return an error instead? */ - if (crtc_w < 3 || crtc_h < 3) - visible = false; + intel_plane->disable_plane(plane);
- /* - * 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; + if (!intel_plane->obj) + goto out;
- /* 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; + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(intel_plane->obj); + intel_plane->obj = NULL; + mutex_unlock(&dev->struct_mutex); +out:
- if (src_w < min_w) { - src_w = min_w; - if (src_x > fb->width - src_w) - src_x = fb->width - src_w; - } + return ret; +}
- /* FIXME interlacing */ - if (src_h < 3) { - src_h = 3; - if (src_y > fb->height - src_h) - src_y = fb->height - src_h; - } - } +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_plane *intel_plane = to_intel_plane(plane); + 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; + int ret; + int primary_w, primary_h; + bool disable_primary = false;
- /* 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); @@ -537,9 +640,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); @@ -568,26 +670,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; - - if (plane->crtc) - intel_enable_primary(plane->crtc); - intel_plane->disable_plane(plane); + 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 (!intel_plane->obj) - goto out; + ret = intel_check_plane(plane, crtc, fb, &coords); + if (ret) + return ret;
- mutex_lock(&dev->struct_mutex); - intel_unpin_fb_obj(intel_plane->obj); - intel_plane->obj = NULL; - mutex_unlock(&dev->struct_mutex); -out: + intel_commit_plane(plane, crtc, fb, &coords);
- 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 | 88 +++++++++++++++++++++------------- 1 files changed, 55 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a4eb64f..2a0748c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4301,25 +4301,20 @@ static void i8xx_update_pll(struct drm_crtc *crtc, I915_WRITE(DPLL(pipe), dpll); }
-static int i9xx_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *fb) +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_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; - int refclk, num_connectors = 0; - intel_clock_t clock, reduced_clock; - u32 dspcntr, pipeconf, vsyncshift; - 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; - int ret; + 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) { @@ -4336,30 +4331,25 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, is_tv = true; break; case INTEL_OUTPUT_DISPLAYPORT: - is_dp = true; + *is_dp = true; break; }
- num_connectors++; + (*num_connectors)++; }
- refclk = i9xx_get_refclk(crtc, 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); - if (!ok) { - DRM_ERROR("Couldn't find PLL settings for mode!\n"); - return -EINVAL; - } - - /* Ensure that the cursor is valid for the new mode before changing... */ - intel_crtc_update_cursor(crtc, true); + 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) { /* @@ -4368,15 +4358,47 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, * 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); + *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); + 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, + int x, int y, + struct drm_framebuffer *fb) +{ + 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; + int plane = intel_crtc->plane; + int refclk, num_connectors = 0; + intel_clock_t clock, reduced_clock; + u32 dspcntr, pipeconf, vsyncshift; + 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, + &refclk, &num_connectors, &is_dp); + if (!ok) { + DRM_ERROR("Couldn't find PLL settings for mode!\n"); + return -EINVAL; + } + + /* Ensure that the cursor is valid for the new mode before changing... */ + intel_crtc_update_cursor(crtc, true);
i9xx_update_pll_dividers(crtc, &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 2a0748c..d6b4bfe 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3963,7 +3963,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, @@ -4302,7 +4302,7 @@ static void i8xx_update_pll(struct drm_crtc *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, @@ -4716,7 +4716,7 @@ static void ironlake_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 | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f511fa2..53a35fd 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1600,6 +1600,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 ironlake_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 d6b4bfe..d4d7454 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5124,6 +5124,30 @@ static int ironlake_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 (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 d4d7454..c59985d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5674,6 +5674,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 0660d38..abe646d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -204,7 +204,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 c59985d..20539c4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5587,10 +5587,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; @@ -5602,10 +5604,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 */ @@ -5661,17 +5662,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; @@ -5680,15 +5710,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 20539c4..da59490 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5587,12 +5587,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; @@ -5677,8 +5677,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; @@ -5696,10 +5696,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 abe646d..19bef3e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -586,4 +586,17 @@ extern void intel_ddi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode);
+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 da59490..f27ac4d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2139,7 +2139,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 19bef3e..9dea410 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -511,6 +511,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 | 26 ++++++++++++-------------- 1 files changed, 12 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f27ac4d..79b6de7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4387,7 +4387,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, u32 dspcntr, pipeconf, vsyncshift; 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, @@ -4517,11 +4516,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; }
/* @@ -4795,7 +4790,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, bool ok, has_reduced_clock = false, is_sdvo = false; bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false; struct intel_encoder *encoder, *edp_encoder = NULL; - int ret; struct fdi_m_n m_n = {0}; u32 temp; int target_clock, pixel_multiplier, lane, link_bw, factor; @@ -5115,13 +5109,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 ret; + return 0; }
int intel_check_clock(struct drm_crtc *crtc, @@ -5164,6 +5152,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);
return ret;
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 79b6de7..6a5a82b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3457,8 +3457,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 9dea410..cd6ea3b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -600,4 +600,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
Refactor the code that stores the panning x/y position into the sarea. Make the new function 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 | 43 ++++++++++++++++++++++------------ drivers/gpu/drm/i915/intel_drv.h | 1 + 2 files changed, 29 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6a5a82b..ff51171 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2166,13 +2166,39 @@ intel_finish_fb(struct drm_framebuffer *old_fb) return ret; }
+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; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (!dev->primary->master) + return; + + master_priv = dev->primary->master->driver_priv; + if (!master_priv->sarea_priv) + return; + + switch (intel_crtc->pipe) { + case 0: + master_priv->sarea_priv->pipeA_x = x; + master_priv->sarea_priv->pipeA_y = y; + break; + case 1: + master_priv->sarea_priv->pipeB_x = x; + master_priv->sarea_priv->pipeB_y = y; + break; + default: + break; + } +} + static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_framebuffer *old_fb; int ret; @@ -2224,20 +2250,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, intel_update_fbc(dev); mutex_unlock(&dev->struct_mutex);
- if (!dev->primary->master) - return 0; - - master_priv = dev->primary->master->driver_priv; - if (!master_priv->sarea_priv) - return 0; - - if (intel_crtc->pipe) { - master_priv->sarea_priv->pipeB_x = x; - master_priv->sarea_priv->pipeB_y = y; - } else { - master_priv->sarea_priv->pipeA_x = x; - master_priv->sarea_priv->pipeA_y = y; - } + intel_crtc_update_sarea_pos(crtc, x, y);
return 0; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index cd6ea3b..c65c97fc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -601,5 +601,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 ff51171..683d434 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6823,7 +6823,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 683d434..ba737b8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3669,9 +3669,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 c65c97fc..2b9acba 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -603,4 +603,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 2b9acba..5dab482 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -181,6 +181,20 @@ struct intel_connector { bool (*get_hw_state)(struct intel_connector *); };
+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 | 145 ++++++++++++++++++++++------------ drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 100 insertions(+), 49 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 53a35fd..302b6e6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -269,6 +269,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 ba737b8..0fb1e09 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1940,8 +1940,27 @@ static unsigned long gen4_compute_dspaddr_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 (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; @@ -1950,9 +1969,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: @@ -1966,36 +1984,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); @@ -2004,13 +2021,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 = fb->offsets[0] + y * fb->pitches[0] + x * cpp;
if (INTEL_INFO(dev)->gen >= 4) { @@ -2024,21 +2039,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; @@ -2047,9 +2078,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: @@ -2064,32 +2094,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); @@ -2097,14 +2126,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 = fb->offsets[0] + y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); intel_crtc->dspaddr_offset = @@ -2114,12 +2141,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); - 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; } @@ -7928,12 +7971,16 @@ static void intel_init_display(struct drm_device *dev) 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 5dab482..1389eeb 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -226,6 +226,7 @@ struct intel_crtc {
/* We can share PLLs across outputs if the timings match */ struct intel_pch_pll *pch_pll; + 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 | 398 ++++++++++++++++++++++------------- 2 files changed, 259 insertions(+), 144 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1389eeb..6ab8f65 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -252,6 +252,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 fee6f17..06d62e70 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -187,14 +187,12 @@ 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; @@ -203,47 +201,55 @@ 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; - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + int pixel_size; + struct intel_plane_regs *regs = &intel_plane->regs; + + if (!coords->visible) { + regs->cntr &= ~SPRITE_ENABLE; + /* Disable the scaler */ + regs->scale = 0; + return; + }
- sprctl = I915_READ(SPRCTL(pipe)); + 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--; @@ -251,20 +257,68 @@ 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; + if (obj->tiling_mode != I915_TILING_NONE) { + y += fb->offsets[0] / fb->pitches[0]; + x += fb->offsets[0] % fb->pitches[0] / pixel_size; + + regs->tileoff = (y << 16) | x; + } else + regs->linoff = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size; + regs->size = (crtc_h << 16) | crtc_w; + regs->surf = I915_LO_DISPBASE(regs->surf) | obj->gtt_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); + I915_WRITE(SPRTILEOFF(pipe), regs->tileoff); + I915_WRITE(SPRLINOFF(pipe), regs->linoff); + I915_WRITE(SPRSIZE(pipe), regs->size); + 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; @@ -272,24 +326,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); - if (obj->tiling_mode != I915_TILING_NONE) { - y += fb->offsets[0] / fb->pitches[0]; - x += fb->offsets[0] % fb->pitches[0] / pixel_size; - - I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x); - } else { - unsigned long 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;
- offset = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size; - I915_WRITE(SPRLINOFF(pipe), offset); - } - I915_WRITE(SPRSIZE(pipe), (crtc_h << 16) | crtc_w); - I915_WRITE(SPRSCALE(pipe), sprscale); - I915_WRITE(SPRCTL(pipe), sprctl); - I915_MODIFY_DISPBASE(SPRSURF(pipe), obj->gtt_offset); + ivb_calc_plane(plane, fb, coords); + ivb_prepare_plane(plane); + ivb_commit_plane(plane); POSTING_READ(SPRSURF(pipe)); }
@@ -300,12 +351,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... */ - 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; @@ -318,63 +369,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; - - intel_plane = to_intel_plane(plane); + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_plane_regs *regs = &intel_plane->regs;
- 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); + regs->keyval = key->min_value; + regs->keymaxval = key->max_value; + regs->keymsk = 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; - - intel_plane = to_intel_plane(plane); + struct intel_plane *intel_plane = to_intel_plane(plane); + const struct intel_plane_regs *regs = &intel_plane->regs;
- 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; @@ -383,47 +423,55 @@ 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; - 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; + + if (!coords->visible) { + regs->cntr &= ~DVS_ENABLE; + /* Disable the scaler */ + regs->scale = 0; + return; + }
- dvscntr = I915_READ(DVSCNTR(pipe)); + 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--; @@ -431,29 +479,70 @@ 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; if (obj->tiling_mode != I915_TILING_NONE) { y += fb->offsets[0] / fb->pitches[0]; x += fb->offsets[0] % fb->pitches[0] / pixel_size;
- I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x); - } else { - unsigned long offset; + regs->tileoff = (y << 16) | x; + } else + regs->linoff = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size; + regs->size = (crtc_h << 16) | crtc_w; + regs->surf = I915_LO_DISPBASE(regs->surf) | obj->gtt_offset; +}
- offset = fb->offsets[0] + y * fb->pitches[0] + x * pixel_size; - I915_WRITE(DVSLINOFF(pipe), 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); +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)); }
@@ -464,21 +553,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) @@ -487,7 +589,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 @@ -496,12 +599,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); @@ -513,49 +618,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; - - intel_plane = to_intel_plane(plane); + struct intel_plane *intel_plane = to_intel_plane(plane); + struct intel_plane_regs *regs = &intel_plane->regs;
- 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); + regs->keyval = key->min_value; + regs->keymaxval = key->max_value; + regs->keymsk = 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; - - intel_plane = to_intel_plane(plane); + struct intel_plane *intel_plane = to_intel_plane(plane); + const struct intel_plane_regs *regs = &intel_plane->regs;
- 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; @@ -793,6 +888,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; @@ -815,6 +911,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); @@ -831,6 +934,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 0fb1e09..ac46281 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7830,6 +7830,9 @@ static void intel_setup_outputs(struct drm_device *dev) intel_encoder_clones(encoder); }
+ /* FIXME error handling */ + intel_atomic_init(dev); + if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) ironlake_init_pch_refclk(dev); } @@ -8565,6 +8568,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 6ab8f65..616d5d3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -627,4 +627,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 69420e7..f37e8a7 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);
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky: - wait_for_vblank requires the pipe id, which could then change on every modeset - similarly userspace doing MI_WAIT for scanlines needs to know the actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
An example why the kernel needs to pick the crtc for userspace: - on ivb only pipe A has a 7x5 panel fitter, so usually you want to put the panel on the first crtc - but if you run in a 3 pipe configuration and have an eDP panel, it's better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C) - similarly when we have a 3 pipe configuration with all encoders on the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each). - afaik these kind of asymmetric constraints won't disappear anytime soon, haswell definitely still has some.
Cheers, Daniel
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
An example why the kernel needs to pick the crtc for userspace:
- on ivb only pipe A has a 7x5 panel fitter, so usually you want to
put the panel on the first crtc
- but if you run in a 3 pipe configuration and have an eDP panel, it's
better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C)
- similarly when we have a 3 pipe configuration with all encoders on
the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each).
- afaik these kind of asymmetric constraints won't disappear anytime
soon, haswell definitely still has some.
Yeah that's a good point... adding a virtual crtc layer would solve this and let us preserve the existing ABI.
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
An example why the kernel needs to pick the crtc for userspace:
- on ivb only pipe A has a 7x5 panel fitter, so usually you want to
put the panel on the first crtc
- but if you run in a 3 pipe configuration and have an eDP panel, it's
better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C)
- similarly when we have a 3 pipe configuration with all encoders on
the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each).
- afaik these kind of asymmetric constraints won't disappear anytime
soon, haswell definitely still has some.
Yeah that's a good point... adding a virtual crtc layer would solve this and let us preserve the existing ABI.
How would the state handling work? I mean if drm_crtc X currently has some state, drm_crtc Y has some other state, and then we do a modeset which would require swapping the roles of the crtcs, what would happen to the bits of state that we didn't specify?
If we'd do the remapping below the drm crtc layer, the state would always be tied to the drm crtc. But that would definitely require mostly uniform hardware "crtcs" so that the capabilities of the drm_crtcs wouldn't keep changing whenever the remap happens.
Well, I suppose we could tie the state to the virtual crtc instead, and doing an operation on a real drm_crtc would also change the state of the currently bound virtual crtc. And then changing the virtual<->real mapping would just copy the state over from the virtual crtc to the real drm_crtc.
And if we do it for crtcs, then we'd need to do it for planes as well, because the plane<->crtc mapping can be fixed or otherwise limited in some fashion.
Either way it sounds rather messy to me.
Another option would be just leave it up to userspace to make the correct choice between crtcs and planes. But then user space needs to be equipped with more hardware specific knowledge, so it's not a very appealing idea either.
On Thu, 1 Nov 2012 19:07:02 +0200 Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
An example why the kernel needs to pick the crtc for userspace:
- on ivb only pipe A has a 7x5 panel fitter, so usually you want to
put the panel on the first crtc
- but if you run in a 3 pipe configuration and have an eDP panel, it's
better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C)
- similarly when we have a 3 pipe configuration with all encoders on
the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each).
- afaik these kind of asymmetric constraints won't disappear anytime
soon, haswell definitely still has some.
Yeah that's a good point... adding a virtual crtc layer would solve this and let us preserve the existing ABI.
How would the state handling work? I mean if drm_crtc X currently has some state, drm_crtc Y has some other state, and then we do a modeset which would require swapping the roles of the crtcs, what would happen to the bits of state that we didn't specify?
If we'd do the remapping below the drm crtc layer, the state would always be tied to the drm crtc. But that would definitely require mostly uniform hardware "crtcs" so that the capabilities of the drm_crtcs wouldn't keep changing whenever the remap happens.
Well, I suppose we could tie the state to the virtual crtc instead, and doing an operation on a real drm_crtc would also change the state of the currently bound virtual crtc. And then changing the virtual<->real mapping would just copy the state over from the virtual crtc to the real drm_crtc.
And if we do it for crtcs, then we'd need to do it for planes as well, because the plane<->crtc mapping can be fixed or otherwise limited in some fashion.
Either way it sounds rather messy to me.
Another option would be just leave it up to userspace to make the correct choice between crtcs and planes. But then user space needs to be equipped with more hardware specific knowledge, so it's not a very appealing idea either.
Yeah it gets ugly one way or another. From a userspace perspective, keeping the ugliness in the kernel is nice, and if we have to do it somewhere I guess I'd prefer that.
However, we can't always hide hw details like that in the kernel through generic interfaces (e.g. for sprites and all their restrictions) so userspace will have to have some knowledge one way or another, and maybe it's not that bad to push some of the other limitation knowledge into our userspace code.
Ambivalent.
On Thu, Nov 01, 2012 at 10:12:21AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 19:07:02 +0200 Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
An example why the kernel needs to pick the crtc for userspace:
- on ivb only pipe A has a 7x5 panel fitter, so usually you want to
put the panel on the first crtc
- but if you run in a 3 pipe configuration and have an eDP panel, it's
better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C)
- similarly when we have a 3 pipe configuration with all encoders on
the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each).
- afaik these kind of asymmetric constraints won't disappear anytime
soon, haswell definitely still has some.
Yeah that's a good point... adding a virtual crtc layer would solve this and let us preserve the existing ABI.
How would the state handling work? I mean if drm_crtc X currently has some state, drm_crtc Y has some other state, and then we do a modeset which would require swapping the roles of the crtcs, what would happen to the bits of state that we didn't specify?
If we'd do the remapping below the drm crtc layer, the state would always be tied to the drm crtc. But that would definitely require mostly uniform hardware "crtcs" so that the capabilities of the drm_crtcs wouldn't keep changing whenever the remap happens.
Well, I suppose we could tie the state to the virtual crtc instead, and doing an operation on a real drm_crtc would also change the state of the currently bound virtual crtc. And then changing the virtual<->real mapping would just copy the state over from the virtual crtc to the real drm_crtc.
And if we do it for crtcs, then we'd need to do it for planes as well, because the plane<->crtc mapping can be fixed or otherwise limited in some fashion.
Either way it sounds rather messy to me.
Another option would be just leave it up to userspace to make the correct choice between crtcs and planes. But then user space needs to be equipped with more hardware specific knowledge, so it's not a very appealing idea either.
Yeah it gets ugly one way or another. From a userspace perspective, keeping the ugliness in the kernel is nice, and if we have to do it somewhere I guess I'd prefer that.
My tentative Grand Plan (tm) for the atomic modeset implementation of such things is to pimp the new struct intel_crtc_config to contain all the configuration state (so with Rob's atomic modeset proposal that would also embed the drm_crtc_state struct). It would also contain all the derived state like pll settings, fdi lanes, ...
Now the improve >mode_adjust stage, now called ->compute_config allocates such a struct for each crtc, does some prep, calls down into encoder->compute_config callbacks, then applies any fixups and screaming required to come up with a working global config. All rather hazy, I know ;-)
But e.g. for the above case of trying to squeeze the fdi links into the available sets of fdi lanes I guess we could first compute the upper bound for the fdi link requirements (the current wip stuff already pre-computes the pipe_bpp from the fb->depth). Then check whether that fits, do any readjustments (I already have a has_pch_encoder attribute, maybe at the wrong spot still, but we should be able to know which outputs need fdi links). If there's no way to fit it, reassign pipes a bit or try dithering. Once that works, call into encoders ...
Or maybe we could do a loop, since the encoders also limit the pipe_bpp. So first pipe_bpp from the fb->depth, then check for output properties (6bpc dithering on lvds, or a puny dp link), then check whether it fits into fdi. If not, dither more or reassign, then re-run the encoder config computations. If it still has too high bw requirementsmuch, give up.
In any case, and disregarding the above ramblings, I plan to make the intel_crtc_config thing free-standing for the ->compute_config stage, so we could easily add a preferred_crtc pointer to it since it's not tied to a specific crtc at that point. The problem now is that the connector->encoder->crtc links are embedded into the connectors and encoders, so they are not free-standing. But if we want to support atomic modeset on all properties (which I think we should), we need to fix that anyway ...
However, we can't always hide hw details like that in the kernel through generic interfaces (e.g. for sprites and all their restrictions) so userspace will have to have some knowledge one way or another, and maybe it's not that bad to push some of the other limitation knowledge into our userspace code.
I disagree, there's no sane way that userspace can figure out that the 3 pipe config it wants would work, but only if it moves the eDP output to some other pipe. So if our aim is to support that (I know, we have more pressing and less lofty things on the table), the interface should allow it.
Cheers, Daniel
On Thu, Nov 01, 2012 at 11:39:58PM +0100, Daniel Vetter wrote:
On Thu, Nov 01, 2012 at 10:12:21AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 19:07:02 +0200 Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
An example why the kernel needs to pick the crtc for userspace:
- on ivb only pipe A has a 7x5 panel fitter, so usually you want to
put the panel on the first crtc
- but if you run in a 3 pipe configuration and have an eDP panel, it's
better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C)
- similarly when we have a 3 pipe configuration with all encoders on
the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each).
- afaik these kind of asymmetric constraints won't disappear anytime
soon, haswell definitely still has some.
Yeah that's a good point... adding a virtual crtc layer would solve this and let us preserve the existing ABI.
How would the state handling work? I mean if drm_crtc X currently has some state, drm_crtc Y has some other state, and then we do a modeset which would require swapping the roles of the crtcs, what would happen to the bits of state that we didn't specify?
If we'd do the remapping below the drm crtc layer, the state would always be tied to the drm crtc. But that would definitely require mostly uniform hardware "crtcs" so that the capabilities of the drm_crtcs wouldn't keep changing whenever the remap happens.
Well, I suppose we could tie the state to the virtual crtc instead, and doing an operation on a real drm_crtc would also change the state of the currently bound virtual crtc. And then changing the virtual<->real mapping would just copy the state over from the virtual crtc to the real drm_crtc.
And if we do it for crtcs, then we'd need to do it for planes as well, because the plane<->crtc mapping can be fixed or otherwise limited in some fashion.
Either way it sounds rather messy to me.
Another option would be just leave it up to userspace to make the correct choice between crtcs and planes. But then user space needs to be equipped with more hardware specific knowledge, so it's not a very appealing idea either.
Yeah it gets ugly one way or another. From a userspace perspective, keeping the ugliness in the kernel is nice, and if we have to do it somewhere I guess I'd prefer that.
My tentative Grand Plan (tm) for the atomic modeset implementation of such things is to pimp the new struct intel_crtc_config to contain all the configuration state (so with Rob's atomic modeset proposal that would also embed the drm_crtc_state struct). It would also contain all the derived state like pll settings, fdi lanes, ...
Now the improve >mode_adjust stage, now called ->compute_config allocates such a struct for each crtc, does some prep, calls down into encoder->compute_config callbacks, then applies any fixups and screaming required to come up with a working global config. All rather hazy, I know ;-)
But e.g. for the above case of trying to squeeze the fdi links into the available sets of fdi lanes I guess we could first compute the upper bound for the fdi link requirements (the current wip stuff already pre-computes the pipe_bpp from the fb->depth). Then check whether that fits, do any readjustments (I already have a has_pch_encoder attribute, maybe at the wrong spot still, but we should be able to know which outputs need fdi links). If there's no way to fit it, reassign pipes a bit or try dithering. Once that works, call into encoders ...
Or maybe we could do a loop, since the encoders also limit the pipe_bpp. So first pipe_bpp from the fb->depth, then check for output properties (6bpc dithering on lvds, or a puny dp link), then check whether it fits into fdi. If not, dither more or reassign, then re-run the encoder config computations. If it still has too high bw requirementsmuch, give up.
In any case, and disregarding the above ramblings, I plan to make the intel_crtc_config thing free-standing for the ->compute_config stage, so we could easily add a preferred_crtc pointer to it since it's not tied to a specific crtc at that point. The problem now is that the connector->encoder->crtc links are embedded into the connectors and encoders, so they are not free-standing. But if we want to support atomic modeset on all properties (which I think we should), we need to fix that anyway ...
I'm not sure what the placement of the encoder and crtc pointers has to do with atomic modeset.
Now, all of what you describe above is about the internals, and seems perfectly reasonable. It sounds like the same idea had when I started plaing around with atomic modeset. That is pulling the state out of the various structures into some kind of state object that can be manipulated w/o disturbing the rest of the system. But I gave up on the idea due to the sheer scope of the work, and I wanted to make something that works sooner rather than later. My current code just goes around and shovels the new state into the object structs themselves, relying mostly on the save/restore code to reset the various bits of state to the original values when things fail.
But anyway, the user visible state handling is still going to be a problem with them virtual crtcs. We need to come up with a plan for that if that's really the route we want to take.
On Thu, Nov 1, 2012 at 5:39 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Nov 01, 2012 at 10:12:21AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 19:07:02 +0200 Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote:
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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
I don't know if it is possible, but it would be nice to try to clean up crtc<->pipe stuff.. userspace, at least modetest, assumes crtc == crtc_list[pipe]. But I haven't noticed yet anywhere that this relationship is documented. And if you are thinking about doing what I think you are thinking about doing, then this assumption would no longer hold for i915.
How frequently do you emit waits for scanline? Places where the pipe # ends up in cmdstream, would it be too crazy to handle that like a reloc where the kernel goes and fixes up the actual value in the cmdstream as it does it's GEM reloc pass? If you did this then you could virtualize pipe as well as crtc.
An example why the kernel needs to pick the crtc for userspace:
- on ivb only pipe A has a 7x5 panel fitter, so usually you want to
put the panel on the first crtc
- but if you run in a 3 pipe configuration and have an eDP panel, it's
better to put the eDP output on pipe C, since then pipes A&B will have full 4-lane fdi links to the pch ports (instead of otherwise only 2 lanes each on links B&C)
- similarly when we have a 3 pipe configuration with all encoders on
the pch, we need to move the mode with the highest dotclock to pipe A (since that's the only one which will have 4 lanes, pipe B&C will only have 2 each).
- afaik these kind of asymmetric constraints won't disappear anytime
soon, haswell definitely still has some.
Yeah that's a good point... adding a virtual crtc layer would solve this and let us preserve the existing ABI.
How would the state handling work? I mean if drm_crtc X currently has some state, drm_crtc Y has some other state, and then we do a modeset which would require swapping the roles of the crtcs, what would happen to the bits of state that we didn't specify?
If we'd do the remapping below the drm crtc layer, the state would always be tied to the drm crtc. But that would definitely require mostly uniform hardware "crtcs" so that the capabilities of the drm_crtcs wouldn't keep changing whenever the remap happens.
Well, I suppose we could tie the state to the virtual crtc instead, and doing an operation on a real drm_crtc would also change the state of the currently bound virtual crtc. And then changing the virtual<->real mapping would just copy the state over from the virtual crtc to the real drm_crtc.
And if we do it for crtcs, then we'd need to do it for planes as well, because the plane<->crtc mapping can be fixed or otherwise limited in some fashion.
Either way it sounds rather messy to me.
Another option would be just leave it up to userspace to make the correct choice between crtcs and planes. But then user space needs to be equipped with more hardware specific knowledge, so it's not a very appealing idea either.
Yeah it gets ugly one way or another. From a userspace perspective, keeping the ugliness in the kernel is nice, and if we have to do it somewhere I guess I'd prefer that.
My tentative Grand Plan (tm) for the atomic modeset implementation of such things is to pimp the new struct intel_crtc_config to contain all the configuration state (so with Rob's atomic modeset proposal that would also embed the drm_crtc_state struct). It would also contain all the derived state like pll settings, fdi lanes, ...
Now the improve >mode_adjust stage, now called ->compute_config allocates such a struct for each crtc, does some prep, calls down into encoder->compute_config callbacks, then applies any fixups and screaming required to come up with a working global config. All rather hazy, I know ;-)
But e.g. for the above case of trying to squeeze the fdi links into the available sets of fdi lanes I guess we could first compute the upper bound for the fdi link requirements (the current wip stuff already pre-computes the pipe_bpp from the fb->depth). Then check whether that fits, do any readjustments (I already have a has_pch_encoder attribute, maybe at the wrong spot still, but we should be able to know which outputs need fdi links). If there's no way to fit it, reassign pipes a bit or try dithering. Once that works, call into encoders ...
Or maybe we could do a loop, since the encoders also limit the pipe_bpp. So first pipe_bpp from the fb->depth, then check for output properties (6bpc dithering on lvds, or a puny dp link), then check whether it fits into fdi. If not, dither more or reassign, then re-run the encoder config computations. If it still has too high bw requirementsmuch, give up.
In any case, and disregarding the above ramblings, I plan to make the intel_crtc_config thing free-standing for the ->compute_config stage, so we could easily add a preferred_crtc pointer to it since it's not tied to a specific crtc at that point. The problem now is that the connector->encoder->crtc links are embedded into the connectors and encoders, so they are not free-standing. But if we want to support atomic modeset on all properties (which I think we should), we need to fix that anyway ...
I think for full atomic modeset, then the drm_*_state structs end up having the link pointers, ie 'struct drm_connector_state' has the ptr to 'struct drm_encoder', and so on..
BR, -R
However, we can't always hide hw details like that in the kernel through generic interfaces (e.g. for sprites and all their restrictions) so userspace will have to have some knowledge one way or another, and maybe it's not that bad to push some of the other limitation knowledge into our userspace code.
I disagree, there's no sane way that userspace can figure out that the 3 pipe config it wants would work, but only if it moves the eDP output to some other pipe. So if our aim is to support that (I know, we have more pressing and less lofty things on the table), the interface should allow it.
Cheers, Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
On Wed, Nov 07, 2012 at 02:29:45PM -0600, Rob Clark wrote:
On Thu, Nov 1, 2012 at 5:39 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Nov 01, 2012 at 10:12:21AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 19:07:02 +0200 Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote: > 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
I have to admit I'm not on top of the latest ioctl/interface discussion, but one new requirement for the modeset (not the pageflip part) of the all this atomic stuff I've discovered is that the kernel needs to be able to select the crtcs for each output completely unrestricted. I think userspace should simply pass in abstract crtc ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual crtcs it has selected.
We can't do that remapping internally because the crtc abstraction is leaky:
- wait_for_vblank requires the pipe id, which could then change on every modeset
- similarly userspace doing MI_WAIT for scanlines needs to know the
actual hw pipe in use by a crtc. And current userspace presumes that the mapping between crtc->pipe doesn't change.
I don't know if it is possible, but it would be nice to try to clean up crtc<->pipe stuff.. userspace, at least modetest, assumes crtc == crtc_list[pipe]. But I haven't noticed yet anywhere that this relationship is documented. And if you are thinking about doing what I think you are thinking about doing, then this assumption would no longer hold for i915.
This relationship does already no longer hold on i915 - on gen3 at least we sometimes switch the crtc->pipe assignement (to make fbc more useful), which means even with todays code userspace cannot assume (when running on i915), that the vblank pipe satisfies crtc == crtc_list[pipe].
How frequently do you emit waits for scanline? Places where the pipe # ends up in cmdstream, would it be too crazy to handle that like a reloc where the kernel goes and fixes up the actual value in the cmdstream as it does it's GEM reloc pass? If you did this then you could virtualize pipe as well as crtc.
Every dri2 copyregion or Xv upload to the frontbuffer on pre-gen6 will use it. And we need old userspace to keep on working - presumably the fb layer will switch to using the new atomic modeset stuff (if available) to figure out an optimal config, so this is relevant. -Daniel
On Fri, Nov 9, 2012 at 3:20 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Wed, Nov 07, 2012 at 02:29:45PM -0600, Rob Clark wrote:
On Thu, Nov 1, 2012 at 5:39 PM, Daniel Vetter daniel@ffwll.ch wrote:
On Thu, Nov 01, 2012 at 10:12:21AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 19:07:02 +0200 Ville Syrjälä ville.syrjala@linux.intel.com wrote:
On Thu, Nov 01, 2012 at 07:39:12AM -0700, Jesse Barnes wrote:
On Thu, 1 Nov 2012 12:12:35 +0100 Daniel Vetter daniel@ffwll.ch wrote:
> On Thu, Oct 25, 2012 at 8:05 PM, ville.syrjala@linux.intel.com wrote: > > 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 > > I have to admit I'm not on top of the latest ioctl/interface > discussion, but one new requirement for the modeset (not the pageflip > part) of the all this atomic stuff I've discovered is that the kernel > needs to be able to select the crtcs for each output completely > unrestricted. I think userspace should simply pass in abstract crtc > ids (e.g. 0, 1, 2, ...) and the kernel then passes back the actual > crtcs it has selected. > > We can't do that remapping internally because the crtc abstraction is leaky: > - wait_for_vblank requires the pipe id, which could then change on every modeset > - similarly userspace doing MI_WAIT for scanlines needs to know the > actual hw pipe in use by a crtc. > And current userspace presumes that the mapping between crtc->pipe > doesn't change.
I don't know if it is possible, but it would be nice to try to clean up crtc<->pipe stuff.. userspace, at least modetest, assumes crtc == crtc_list[pipe]. But I haven't noticed yet anywhere that this relationship is documented. And if you are thinking about doing what I think you are thinking about doing, then this assumption would no longer hold for i915.
This relationship does already no longer hold on i915 - on gen3 at least we sometimes switch the crtc->pipe assignement (to make fbc more useful), which means even with todays code userspace cannot assume (when running on i915), that the vblank pipe satisfies crtc == crtc_list[pipe].
hmm, how does this not break weston compositor-drm (and modetest w/ vsync'd flipping.. although I suppose that is a less important use-case)
How frequently do you emit waits for scanline? Places where the pipe # ends up in cmdstream, would it be too crazy to handle that like a reloc where the kernel goes and fixes up the actual value in the cmdstream as it does it's GEM reloc pass? If you did this then you could virtualize pipe as well as crtc.
Every dri2 copyregion or Xv upload to the frontbuffer on pre-gen6 will use it. And we need old userspace to keep on working - presumably the fb layer will switch to using the new atomic modeset stuff (if available) to figure out an optimal config, so this is relevant.
yeah, not quite sure how the backwards-compat should work.. you'd have to somehow only dynamically reassign crtcs if you could tell that userspace is new enough to cope..
BR, -R
-Daniel
Daniel Vetter Software Engineer, Intel Corporation +41 (0) 79 365 57 48 - http://blog.ffwll.ch
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 acc4317..33999f1 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1794,6 +1794,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; }
@@ -1834,6 +1836,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 302b6e6..51b36c1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -36,6 +36,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> @@ -882,6 +883,8 @@ typedef struct drm_i915_private { struct work_struct parity_error_work; bool hw_contexts_disabled; uint32_t hw_context_size; + + struct drm_flip_driver flip_driver; } drm_i915_private_t;
/* Iterate over initialised rings */ @@ -1100,6 +1103,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 32e1bda..ee22e97 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -563,8 +563,10 @@ static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS) 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); @@ -697,8 +699,10 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) 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); @@ -784,11 +788,15 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) 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 ac46281..756cf94 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3357,6 +3357,7 @@ static void ironlake_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);
@@ -3493,6 +3494,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 616d5d3..4784c44 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); \ @@ -227,6 +228,9 @@ struct intel_crtc { /* We can share PLLs across outputs if the timings match */ struct intel_pch_pll *pch_pll; 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 { @@ -257,6 +261,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 { @@ -629,5 +634,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 4784c44..b9b96d3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -637,4 +637,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 06d62e70..88fdd0d 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -574,7 +574,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; @@ -593,7 +593,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 | 6 ++++-- 1 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 756cf94..6a1ed7e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3311,7 +3311,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); @@ -3463,7 +3464,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 6a1ed7e..43cff11 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2026,6 +2026,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 = fb->offsets[0] + y * fb->pitches[0] + x * cpp;
if (INTEL_INFO(dev)->gen >= 4) { @@ -2133,6 +2138,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 = fb->offsets[0] + y * fb->pitches[0] + x * (fb->bits_per_pixel / 8); intel_crtc->dspaddr_offset = gen4_compute_dspaddr_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);
dri-devel@lists.freedesktop.org