diff mbox

[v2,21/22] drm/i915: Rewrite fb rotation GTT handling

Message ID 1444931985-26045-1-git-send-email-ville.syrjala@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ville Syrjälä Oct. 15, 2015, 5:59 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

Redo the fb rotation handling in order to:
- eliminate the NV12 special casing
- handle fb->offsets[] properly
- make the rotation handling reasier for the plane code

To achieve these goals we reduce intel_rotation_info to only contain
(for each plane) the rotated view width,height,stride in tile units,
and the page offset into the object where the plane starts. Each plane
is handled exactly the same way, no special casing for NV12 or other
formats. We then store the computed rotation_info under
intel_framebuffer so that we don't have to recompute it again.

To handle fb->offsets[] we treat them as a linear offsets and convert
them to x/y offsets from the start of the relevant GTT mapping (either
normal or rotated). We store the x/y offsets under intel_framebuffer,
and for some extra convenience we also store the rotated pitch (ie.
tile aligned plane height). So for each plane we have the normal
x/y offsets, rotated x/y offsets, and the rotated pitch. The normal
pitch is available already in fb->pitches[].

While we're gathering up all that extra information, we can also easily
compute the storage requirements for the framebuffer, so that we can
check that the object is big enough to hold it.

When it comes time to deal with the plane source coordinates, we first
rotate the clipped src coordinates to match the relevant GTT view
orientation, then add to them the fb x/y offsets. Next we compute
the aligned surface page offset, and as a result we're left with some
residual x/y offsets. Finally, if required by the hardware, we convert
the remaining x/y offsets into a linear offset.

For gen2/3 we simply skip computing the final page offset, and just
convert the src+fb x/y offsets directly into a linear offset since
that's what the hardware wants.

After this all platforms, incluing SKL+, compute these things in exactly
the same way (excluding alignemnt differences).

v2: Use BIT(DRM_ROTATE_270) instead of ROTATE_270 when rotating
    plane src coordinates
    Drop some spurious changes that got left behind during development

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_gem_gtt.c  |  66 ++----
 drivers/gpu/drm/i915/i915_gem_gtt.h  |  14 +-
 drivers/gpu/drm/i915/intel_display.c | 448 ++++++++++++++++++++++++-----------
 drivers/gpu/drm/i915/intel_drv.h     |  32 ++-
 drivers/gpu/drm/i915/intel_sprite.c  | 102 ++++----
 5 files changed, 402 insertions(+), 260 deletions(-)

Comments

Daniel Vetter Oct. 21, 2015, 12:01 p.m. UTC | #1
On Thu, Oct 15, 2015 at 08:59:45PM +0300, ville.syrjala@linux.intel.com wrote:
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> Redo the fb rotation handling in order to:
> - eliminate the NV12 special casing
> - handle fb->offsets[] properly
> - make the rotation handling reasier for the plane code

I think this is too big and should be split a bit. There's also a pile of
renames mixed int on top.

> To achieve these goals we reduce intel_rotation_info to only contain
> (for each plane) the rotated view width,height,stride in tile units,
> and the page offset into the object where the plane starts. Each plane
> is handled exactly the same way, no special casing for NV12 or other
> formats.

That sounds like a good prep patch to split out.

> We then store the computed rotation_info under
> intel_framebuffer so that we don't have to recompute it again.

And another one, plus then following up with the fallout.

This patch also fixes the intel_plane_obj_offset issue for earlier in the
series. Looks good otherwise from reading through it.

> To handle fb->offsets[] we treat them as a linear offsets and convert
> them to x/y offsets from the start of the relevant GTT mapping (either
> normal or rotated). We store the x/y offsets under intel_framebuffer,
> and for some extra convenience we also store the rotated pitch (ie.
> tile aligned plane height). So for each plane we have the normal
> x/y offsets, rotated x/y offsets, and the rotated pitch. The normal
> pitch is available already in fb->pitches[].
> 
> While we're gathering up all that extra information, we can also easily
> compute the storage requirements for the framebuffer, so that we can
> check that the object is big enough to hold it.
> 
> When it comes time to deal with the plane source coordinates, we first
> rotate the clipped src coordinates to match the relevant GTT view
> orientation, then add to them the fb x/y offsets. Next we compute
> the aligned surface page offset, and as a result we're left with some
> residual x/y offsets. Finally, if required by the hardware, we convert
> the remaining x/y offsets into a linear offset.
> 
> For gen2/3 we simply skip computing the final page offset, and just
> convert the src+fb x/y offsets directly into a linear offset since
> that's what the hardware wants.
> 
> After this all platforms, incluing SKL+, compute these things in exactly
> the same way (excluding alignemnt differences).
> 
> v2: Use BIT(DRM_ROTATE_270) instead of ROTATE_270 when rotating
>     plane src coordinates
>     Drop some spurious changes that got left behind during development
> 
> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

Two comments below inline.
-Daniel

> ---
>  drivers/gpu/drm/i915/i915_gem_gtt.c  |  66 ++----
>  drivers/gpu/drm/i915/i915_gem_gtt.h  |  14 +-
>  drivers/gpu/drm/i915/intel_display.c | 448 ++++++++++++++++++++++++-----------
>  drivers/gpu/drm/i915/intel_drv.h     |  32 ++-
>  drivers/gpu/drm/i915/intel_sprite.c  | 102 ++++----
>  5 files changed, 402 insertions(+), 260 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> index bb95b86..98aee6c 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> @@ -3255,11 +3255,6 @@ rotate_pages(const dma_addr_t *in, unsigned int offset,
>  	unsigned int column, row;
>  	unsigned int src_idx;
>  
> -	if (!sg) {
> -		st->nents = 0;
> -		sg = st->sgl;
> -	}
> -
>  	for (column = 0; column < width; column++) {
>  		src_idx = stride * (height - 1) + column;
>  		for (row = 0; row < height; row++) {
> @@ -3280,16 +3275,14 @@ rotate_pages(const dma_addr_t *in, unsigned int offset,
>  }
>  
>  static struct sg_table *
> -intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
> +intel_rotate_fb_obj_pages(const struct intel_rotation_info *info,
>  			  struct drm_i915_gem_object *obj)
>  {
> -	unsigned int size_pages = rot_info->size >> PAGE_SHIFT;
> -	unsigned int size_pages_uv;
> +	unsigned int size = intel_rotation_info_size(info);
>  	struct sg_page_iter sg_iter;
>  	unsigned long i;
>  	dma_addr_t *page_addr_list;
>  	struct sg_table *st;
> -	unsigned int uv_start_page;
>  	struct scatterlist *sg;
>  	int ret = -ENOMEM;
>  
> @@ -3299,57 +3292,32 @@ intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
>  	if (!page_addr_list)
>  		return ERR_PTR(ret);
>  
> -	/* Account for UV plane with NV12. */
> -	if (rot_info->pixel_format == DRM_FORMAT_NV12)
> -		size_pages_uv = rot_info->size_uv >> PAGE_SHIFT;
> -	else
> -		size_pages_uv = 0;
> -
>  	/* Allocate target SG list. */
>  	st = kmalloc(sizeof(*st), GFP_KERNEL);
>  	if (!st)
>  		goto err_st_alloc;
>  
> -	ret = sg_alloc_table(st, size_pages + size_pages_uv, GFP_KERNEL);
> +	ret = sg_alloc_table(st, size, GFP_KERNEL);
>  	if (ret)
>  		goto err_sg_alloc;
>  
>  	/* Populate source page list from the object. */
>  	i = 0;
>  	for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
> -		page_addr_list[i] = sg_page_iter_dma_address(&sg_iter);
> -		i++;
> +		page_addr_list[i++] = sg_page_iter_dma_address(&sg_iter);
>  	}
>  
> -	/* Rotate the pages. */
> -	sg = rotate_pages(page_addr_list, 0,
> -		     rot_info->width_pages, rot_info->height_pages,
> -		     rot_info->width_pages,
> -		     st, NULL);
> +	st->nents = 0;
> +	sg = st->sgl;
>  
> -	/* Append the UV plane if NV12. */
> -	if (rot_info->pixel_format == DRM_FORMAT_NV12) {
> -		uv_start_page = size_pages;
> -
> -		/* Check for tile-row un-alignment. */
> -		if (offset_in_page(rot_info->uv_offset))
> -			uv_start_page--;
> -
> -		rot_info->uv_start_page = uv_start_page;
> -
> -		rotate_pages(page_addr_list, uv_start_page,
> -			     rot_info->width_pages_uv,
> -			     rot_info->height_pages_uv,
> -			     rot_info->width_pages_uv,
> -			     st, sg);
> +	for (i = 0 ; i < ARRAY_SIZE(info->plane); i++) {
> +		sg = rotate_pages(page_addr_list, info->plane[i].offset,
> +				  info->plane[i].width, info->plane[i].height,
> +				  info->plane[i].stride, st, sg);
>  	}
>  
> -	DRM_DEBUG_KMS(
> -		      "Created rotated page mapping for object size %zu (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %u pages (%u plane 0)).\n",
> -		      obj->base.size, rot_info->pitch, rot_info->height,
> -		      rot_info->pixel_format, rot_info->width_pages,
> -		      rot_info->height_pages, size_pages + size_pages_uv,
> -		      size_pages);
> +	DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages)\n",
> +		      obj->base.size, info->plane[0].width, info->plane[0].height, size);
>  
>  	drm_free_large(page_addr_list);
>  
> @@ -3360,12 +3328,8 @@ err_sg_alloc:
>  err_st_alloc:
>  	drm_free_large(page_addr_list);
>  
> -	DRM_DEBUG_KMS(
> -		      "Failed to create rotated mapping for object size %zu! (%d) (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %u pages (%u plane 0))\n",
> -		      obj->base.size, ret, rot_info->pitch, rot_info->height,
> -		      rot_info->pixel_format, rot_info->width_pages,
> -		      rot_info->height_pages, size_pages + size_pages_uv,
> -		      size_pages);
> +	DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n",
> +		      obj->base.size, info->plane[0].width, info->plane[0].height, size);
>  	return ERR_PTR(ret);
>  }
>  
> @@ -3516,7 +3480,7 @@ i915_ggtt_view_size(struct drm_i915_gem_object *obj,
>  	if (view->type == I915_GGTT_VIEW_NORMAL) {
>  		return obj->base.size;
>  	} else if (view->type == I915_GGTT_VIEW_ROTATED) {
> -		return view->rotated.size;
> +		return intel_rotation_info_size(&view->rotated) << PAGE_SHIFT;
>  	} else if (view->type == I915_GGTT_VIEW_PARTIAL) {
>  		return view->partial.size << PAGE_SHIFT;
>  	} else {
> diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
> index 68de734..ea28f7d 100644
> --- a/drivers/gpu/drm/i915/i915_gem_gtt.h
> +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
> @@ -136,16 +136,10 @@ enum i915_ggtt_view_type {
>  };
>  
>  struct intel_rotation_info {
> -	unsigned int height;
> -	unsigned int pitch;
> -	unsigned int uv_offset;
> -	uint32_t pixel_format;
> -	uint64_t fb_modifier;
> -	unsigned int width_pages, height_pages;
> -	uint64_t size;
> -	unsigned int width_pages_uv, height_pages_uv;
> -	uint64_t size_uv;
> -	unsigned int uv_start_page;
> +	struct {
> +		/* tiles */
> +		unsigned int width, height, stride, offset;
> +	} plane[2];
>  };
>  
>  struct i915_ggtt_view {
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 028dc4a..0dcb710 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -2274,49 +2274,28 @@ intel_fb_align_height(struct drm_device *dev, unsigned int height,
>  	return ALIGN(height, tile_height);
>  }
>  
> -static int
> +unsigned int intel_rotation_info_size(const struct intel_rotation_info *info)
> +{
> +	unsigned int size = 0;
> +	int i;
> +
> +	for (i = 0 ; i < ARRAY_SIZE(info->plane); i++)
> +		size += info->plane[i].width * info->plane[i].height;
> +
> +	return size;
> +}
> +
> +static void
>  intel_fill_fb_ggtt_view(struct i915_ggtt_view *view,
>  			const struct drm_framebuffer *fb,
>  			unsigned int rotation)
>  {
> -	struct drm_i915_private *dev_priv = to_i915(fb->dev);
> -	struct intel_rotation_info *info = &view->rotated;
> -	unsigned int tile_size, tile_width, tile_height, cpp;
> -
> -	*view = i915_ggtt_view_normal;
> -
> -	if (!intel_rotation_90_or_270(rotation))
> -		return 0;
> -
> -	*view = i915_ggtt_view_rotated;
> -
> -	info->height = fb->height;
> -	info->pixel_format = fb->pixel_format;
> -	info->pitch = fb->pitches[0];
> -	info->uv_offset = fb->offsets[1];
> -	info->fb_modifier = fb->modifier[0];
> -
> -	tile_size = intel_tile_size(dev_priv);
> -
> -	cpp = drm_format_plane_cpp(fb->pixel_format, 0);
> -	tile_width = intel_tile_width(dev_priv, cpp, fb->modifier[0]);
> -	tile_height = tile_size / tile_width;
> -
> -	info->width_pages = DIV_ROUND_UP(fb->pitches[0], tile_width);
> -	info->height_pages = DIV_ROUND_UP(fb->height, tile_height);
> -	info->size = info->width_pages * info->height_pages * tile_size;
> -
> -	if (info->pixel_format == DRM_FORMAT_NV12) {
> -		cpp = drm_format_plane_cpp(fb->pixel_format, 1);
> -		tile_width = intel_tile_width(dev_priv, fb->modifier[1], cpp);
> -		tile_height = tile_size / tile_width;
> -
> -		info->width_pages_uv = DIV_ROUND_UP(fb->pitches[1], tile_width);
> -		info->height_pages_uv = DIV_ROUND_UP(fb->height / 2, tile_height);
> -		info->size_uv = info->width_pages_uv * info->height_pages_uv * tile_size;
> +	if (intel_rotation_90_or_270(rotation)) {
> +		*view = i915_ggtt_view_rotated;
> +		view->rotated = to_intel_framebuffer(fb)->info;
> +	} else {
> +		*view = i915_ggtt_view_normal;
>  	}
> -
> -	return 0;
>  }
>  
>  static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv)
> @@ -2368,9 +2347,7 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
>  
>  	alignment = intel_surf_alignment(dev_priv, fb->modifier[0]);
>  
> -	ret = intel_fill_fb_ggtt_view(&view, fb, rotation);
> -	if (ret)
> -		return ret;
> +	intel_fill_fb_ggtt_view(&view, fb, rotation);
>  
>  	/* Note that the w/a also requires 64 PTE of padding following the
>  	 * bo. We currently fill all unused PTE with the shadow page and so
> @@ -2433,18 +2410,31 @@ static void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation
>  {
>  	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
>  	struct i915_ggtt_view view;
> -	int ret;
>  
>  	WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex));
>  
> -	ret = intel_fill_fb_ggtt_view(&view, fb, rotation);
> -	WARN_ONCE(ret, "Couldn't get view from plane state!");
> +	intel_fill_fb_ggtt_view(&view, fb, rotation);
>  
>  	i915_gem_object_unpin_fence(obj);
>  	i915_gem_object_unpin_from_display_plane(obj, &view);
>  }
>  
>  /*
> + * Convert the x/y offsets into a linear offset.
> + * Only valid with 0/180 degree rotation, which is fine since linear
> + * offset is only used with linear buffers on pre-hsw and tiled buffers
> + * with gen2/3, and 90/270 degree rotations isn't supported on any of them.
> + */
> +unsigned int intel_fb_xy_to_linear(int x, int y,
> +				   const struct drm_framebuffer *fb, int plane)
> +{
> +	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> +	unsigned int pitch = fb->pitches[plane];
> +
> +	return y * pitch + x * cpp;
> +}
> +
> +/*
>   * Return the tile dimensions in pixel units matching
>   * the specified rotation angle.
>   */
> @@ -2475,6 +2465,55 @@ static void intel_rotate_tile_dims(unsigned int *tile_width,
>  }
>  
>  /*
> + * Add the x/y offsets derived from fb->offsets[] to the user
> + * specified plane src x/y offsets. The resulting x/y offsets
> + * specify the start of scanout from the beginning of the gtt mapping.
> + */
> +void intel_add_fb_offsets(int *x, int *y,
> +			  const struct drm_framebuffer *fb, int plane,
> +			  unsigned int rotation)
> +
> +{
> +	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
> +	const struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
> +	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> +	unsigned int pitch;
> +
> +	if (intel_rotation_90_or_270(rotation)) {
> +		pitch = intel_fb->plane[plane].rotated.pitch;
> +
> +		*x += intel_fb->plane[plane].rotated.x;
> +		*y += intel_fb->plane[plane].rotated.y;
> +	} else {
> +		pitch = fb->pitches[plane];
> +
> +		*x += intel_fb->plane[plane].normal.x;
> +		*y += intel_fb->plane[plane].normal.y;
> +	}
> +
> +	/* minimize x */
> +	if (fb->modifier[plane] != DRM_FORMAT_MOD_NONE) {
> +		unsigned int tile_size, tile_width, tile_height;
> +
> +		tile_size = intel_tile_size(dev_priv);
> +		tile_width = intel_tile_width(dev_priv, fb->modifier[plane], cpp);
> +		tile_height = tile_size / tile_width;
> +
> +		intel_rotate_tile_dims(&tile_width, &tile_height,
> +				       &pitch, cpp, rotation);
> +
> +		*y += *x / pitch * tile_height;
> +		*x  = *x % pitch;
> +	} else {
> +		/* in pixels */
> +		pitch /= cpp;
> +
> +		*y += *x / pitch;
> +		*x  = *x % pitch;
> +	}

This is called before we do the entire page_offset dance, so I don't think
we need a special case for tiled vs. untiled.
-Daniel

> +}
> +
> +/*
>   * Adjust the page offset by moving the difference into
>   * the x/y offsets.
>   *
> @@ -2514,20 +2553,23 @@ static void intel_adjust_page_offset(int *x, int *y,
>   * In the 90/270 rotated case, x and y are assumed
>   * to be already rotated to match the rotated GTT view, and
>   * pitch is the tile_height aligned framebuffer height.
> + *
> + * This function is used when computing the derived information
> + * under intel_framebuffer, so using any of that information
> + * here is not allowed. Anything under drm_framebuffer can be
> + * used. This is why the user has to pass in the pitch since it
> + * is specified in the rotated orientation.
>   */
> -unsigned long intel_compute_page_offset(int *x, int *y,
> -					const struct drm_framebuffer *fb, int plane,
> -					unsigned int pitch,
> -					unsigned int rotation)
> +static unsigned int _intel_compute_page_offset(const struct drm_i915_private *dev_priv,
> +					       int *x, int *y,
> +					       const struct drm_framebuffer *fb, int plane,
> +					       unsigned int pitch,
> +					       unsigned int rotation,
> +					       unsigned int alignment)
>  {
> -	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
>  	uint64_t fb_modifier = fb->modifier[plane];
>  	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> -	unsigned int offset, alignment;
> -
> -	alignment = intel_surf_alignment(dev_priv, fb_modifier);
> -	if (alignment)
> -		alignment--;
> +	unsigned int offset;
>  
>  	if (fb_modifier != DRM_FORMAT_MOD_NONE) {
>  		unsigned int tile_size, tile_width, tile_height;
> @@ -2560,6 +2602,145 @@ unsigned long intel_compute_page_offset(int *x, int *y,
>  	return offset & ~alignment;
>  }
>  
> +unsigned int intel_compute_page_offset(int *x, int *y,
> +				       const struct drm_framebuffer *fb, int plane,
> +				       unsigned int pitch,
> +				       unsigned int rotation)
> +{
> +	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
> +	unsigned int alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]);
> +
> +	return _intel_compute_page_offset(dev_priv, x, y, fb, plane, pitch,
> +					  rotation, alignment ? (alignment - 1) : 0);
> +}
> +
> +/* Convert the fb->offset[] linear offset into x/y offsets */
> +static void intel_fb_offset_to_xy(int *x, int *y,
> +				  const struct drm_framebuffer *fb, int plane)
> +{
> +	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> +	unsigned int pitch = fb->pitches[plane];
> +	unsigned int linear_offset = fb->offsets[plane];
> +
> +	*y = linear_offset / pitch;
> +	*x = linear_offset % pitch / cpp;
> +}
> +
> +static int
> +intel_fill_fb_info(struct drm_i915_private *dev_priv,
> +		   struct drm_framebuffer *fb)
> +{
> +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
> +	struct intel_rotation_info *info = &intel_fb->info;
> +	unsigned int tile_size;
> +	unsigned int gtt_offset_rotated = 0;
> +	unsigned int max_size = 0;
> +	uint32_t format = fb->pixel_format;
> +	int i, num_planes = drm_format_num_planes(format);
> +
> +	tile_size = intel_tile_size(dev_priv);
> +
> +	for (i = 0; i < num_planes; i++) {
> +		unsigned int width, height;
> +		unsigned int cpp, offset, size;
> +		int x, y;
> +
> +		cpp = drm_format_plane_cpp(format, i);
> +		width = drm_format_plane_width(fb->width, format, i);
> +		height = drm_format_plane_height(fb->height, format, i);
> +
> +		intel_fb_offset_to_xy(&x, &y, fb, i);
> +
> +		/*
> +		 * First pixel of the framebuffer from
> +		 * the start of the normal gtt mapping.
> +		 */
> +		intel_fb->plane[i].normal.x = x;
> +		intel_fb->plane[i].normal.y = y;
> +
> +		offset = _intel_compute_page_offset(dev_priv, &x, &y,
> +						    fb, 0, fb->pitches[i],
> +						    BIT(DRM_ROTATE_0),
> +						    tile_size);
> +		offset /= tile_size;
> +		DRM_DEBUG("page offset %u pages\n", offset);
> +
> +		if (fb->modifier[i] != DRM_FORMAT_MOD_NONE) {
> +			unsigned int tile_width, tile_height;
> +			unsigned int pitch;
> +			struct drm_rect r;
> +
> +			tile_width = intel_tile_width(dev_priv, fb->modifier[i], cpp);
> +			tile_height = tile_size / tile_width;
> +
> +			info->plane[i].offset = offset;
> +			info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i], tile_width);
> +			info->plane[i].width = DIV_ROUND_UP((x + width) * cpp, tile_width);
> +			info->plane[i].height = DIV_ROUND_UP(y + height, tile_height);
> +
> +
> +			intel_fb->plane[i].rotated.pitch =
> +				info->plane[i].height * tile_height;
> +
> +			/* how many tiles does this plane need */
> +			size = info->plane[i].stride * info->plane[i].height;
> +			/*
> +			 * If the plane isn't horizontally tile aligned,
> +			 * we need one more tile.
> +			 */
> +			if (x != 0)
> +				size++;
> +
> +			pitch = intel_fb->plane[i].rotated.pitch;
> +
> +			/* rotate the x/y offsets to match the GTT view */
> +			r.x1 = x;
> +			r.y1 = y;
> +			r.x2 = x + width;
> +			r.y2 = y + height;
> +			drm_rect_rotate(&r, width, pitch, BIT(DRM_ROTATE_270));
> +			x = r.x1;
> +			y = r.y1;
> +
> +			intel_rotate_tile_dims(&tile_width, &tile_height, &pitch,
> +					       cpp, BIT(DRM_ROTATE_270));
> +
> +			/*
> +			 * We only keep the x/y offsets, so push all of the
> +			 * gtt offset into the x/y offsets.
> +			 */
> +			intel_adjust_page_offset(&x, &y, tile_width, tile_height,
> +						 tile_size, pitch,
> +						 gtt_offset_rotated * tile_size, 0);
> +
> +			gtt_offset_rotated += info->plane[i].width * info->plane[i].height;
> +
> +			/*
> +			 * First pixel of the framebuffer from
> +			 * the start of the rotated gtt mapping.
> +			 */
> +			intel_fb->plane[i].rotated.x = x;
> +			intel_fb->plane[i].rotated.y = y;
> +		} else {
> +			size = DIV_ROUND_UP((y + height) * fb->pitches[i] +
> +					    x * cpp, tile_size);
> +		}
> +		DRM_DEBUG("%d offset %u, size %u, stride %u, height %u\n",
> +			  i, offset, size, info->plane[i].stride, info->plane[i].height);
> +
> +		/* how many tiles in total needed in the bo */
> +		max_size = max(max_size, offset + size);
> +	}
> +
> +	if (max_size * tile_size > to_intel_framebuffer(fb)->obj->base.size) {
> +		DRM_DEBUG("fb too big for bo (need %u bytes, have %zu bytes)\n",
> +			  max_size * tile_size, to_intel_framebuffer(fb)->obj->base.size);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
>  static int i9xx_format_to_fourcc(int format)
>  {
>  	switch (format) {
> @@ -2761,7 +2942,7 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
>  	struct drm_i915_gem_object *obj;
>  	int plane = intel_crtc->plane;
>  	unsigned int rotation;
> -	unsigned long linear_offset;
> +	unsigned int linear_offset;
>  	u32 dspcntr;
>  	u32 reg = DSPCNTR(plane);
>  	int pixel_size;
> @@ -2839,30 +3020,25 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
>  	if (IS_G4X(dev))
>  		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
>  
> -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
>  
> -	if (INTEL_INFO(dev)->gen >= 4) {
> +	if (INTEL_INFO(dev)->gen >= 4)
>  		intel_crtc->dspaddr_offset =
>  			intel_compute_page_offset(&x, &y, fb, 0,
>  						  fb->pitches[0], rotation);
> -		linear_offset -= intel_crtc->dspaddr_offset;
> -	} else {
> -		intel_crtc->dspaddr_offset = linear_offset;
> -	}
>  
>  	if (crtc->primary->state->rotation == BIT(DRM_ROTATE_180)) {
>  		dspcntr |= DISPPLANE_ROTATE_180;
>  
>  		x += (intel_crtc->config->pipe_src_w - 1);
>  		y += (intel_crtc->config->pipe_src_h - 1);
> -
> -		/* Finding the last pixel of the last line of the display
> -		data and adding to linear_offset*/
> -		linear_offset +=
> -			(intel_crtc->config->pipe_src_h - 1) * fb->pitches[0] +
> -			(intel_crtc->config->pipe_src_w - 1) * pixel_size;
>  	}
>  
> +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> +
> +	if (INTEL_INFO(dev)->gen < 4)
> +		intel_crtc->dspaddr_offset = linear_offset;
> +
>  	intel_crtc->adjusted_x = x;
>  	intel_crtc->adjusted_y = y;
>  
> @@ -2871,7 +3047,8 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
>  	I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
>  	if (INTEL_INFO(dev)->gen >= 4) {
>  		I915_WRITE(DSPSURF(plane),
> -			   i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
> +			   intel_surf_gtt_offset(fb, 0, rotation) +
> +			   intel_crtc->dspaddr_offset);
>  		I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
>  		I915_WRITE(DSPLINOFF(plane), linear_offset);
>  	} else
> @@ -2891,7 +3068,7 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
>  	struct drm_i915_gem_object *obj;
>  	int plane = intel_crtc->plane;
>  	unsigned int rotation;
> -	unsigned long linear_offset;
> +	unsigned int linear_offset;
>  	u32 dspcntr;
>  	u32 reg = DSPCNTR(plane);
>  	int pixel_size;
> @@ -2946,26 +3123,22 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
>  	if (!IS_HASWELL(dev) && !IS_BROADWELL(dev))
>  		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
>  
> -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
>  	intel_crtc->dspaddr_offset =
>  		intel_compute_page_offset(&x, &y, fb, 0,
>  					  fb->pitches[0], rotation);
> -	linear_offset -= intel_crtc->dspaddr_offset;
> +
>  	if (rotation == BIT(DRM_ROTATE_180)) {
>  		dspcntr |= DISPPLANE_ROTATE_180;
>  
>  		if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
>  			x += (intel_crtc->config->pipe_src_w - 1);
>  			y += (intel_crtc->config->pipe_src_h - 1);
> -
> -			/* Finding the last pixel of the last line of the display
> -			data and adding to linear_offset*/
> -			linear_offset +=
> -				(intel_crtc->config->pipe_src_h - 1) * fb->pitches[0] +
> -				(intel_crtc->config->pipe_src_w - 1) * pixel_size;
>  		}
>  	}
>  
> +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> +
>  	intel_crtc->adjusted_x = x;
>  	intel_crtc->adjusted_y = y;
>  
> @@ -2973,7 +3146,8 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
>  
>  	I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
>  	I915_WRITE(DSPSURF(plane),
> -		   i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
> +		   intel_surf_gtt_offset(fb, 0, rotation) +
> +		   intel_crtc->dspaddr_offset);
>  	if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
>  		I915_WRITE(DSPOFFSET(plane), (y << 16) | x);
>  	} else {
> @@ -3000,30 +3174,15 @@ u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv,
>  	}
>  }
>  
> -unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
> -				     struct drm_i915_gem_object *obj,
> -				     unsigned int plane)
> +unsigned int intel_surf_gtt_offset(struct drm_framebuffer *fb, int plane,
> +				   unsigned int rotation)

If you want to frob the interface, then I'd replace the fb and rotation
with the plane state.

>  {
> -	const struct i915_ggtt_view *view = &i915_ggtt_view_normal;
> -	struct i915_vma *vma;
> -	unsigned char *offset;
> +	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
> +	struct i915_ggtt_view view;
>  
> -	if (intel_rotation_90_or_270(intel_plane->base.state->rotation))
> -		view = &i915_ggtt_view_rotated;
> +	intel_fill_fb_ggtt_view(&view, fb, rotation);
>  
> -	vma = i915_gem_obj_to_ggtt_view(obj, view);
> -	if (WARN(!vma, "ggtt vma for display object not found! (view=%u)\n",
> -		view->type))
> -		return -1;
> -
> -	offset = (unsigned char *)vma->node.start;
> -
> -	if (plane == 1) {
> -		offset += vma->ggtt_view.rotated.uv_start_page *
> -			  PAGE_SIZE;
> -	}
> -
> -	return (unsigned long)offset;
> +	return i915_gem_obj_ggtt_offset_view(obj, &view);
>  }
>  
>  static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id)
> @@ -3142,18 +3301,16 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
>  	struct drm_plane *plane = crtc->primary;
> +	struct intel_framebuffer *intel_fb;
>  	bool visible = to_intel_plane_state(plane->state)->visible;
> -	struct drm_i915_gem_object *obj;
>  	int pipe = intel_crtc->pipe;
>  	u32 plane_ctl, stride_div, stride;
> -	u32 tile_height, plane_offset, plane_size;
>  	unsigned int rotation;
> -	int x_offset, y_offset;
>  	unsigned long surf_addr;
>  	struct intel_crtc_state *crtc_state = intel_crtc->config;
>  	struct intel_plane_state *plane_state;
> -	int src_x = 0, src_y = 0, src_w = 0, src_h = 0;
> -	int dst_x = 0, dst_y = 0, dst_w = 0, dst_h = 0;
> +	int src_w, src_h;
> +	int dst_x, dst_y, dst_w, dst_h;
>  	int scaler_id = -1;
>  	int pixel_size;
>  
> @@ -3166,6 +3323,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
>  		return;
>  	}
>  
> +	intel_fb = to_intel_framebuffer(fb);
>  	pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
>  
>  	plane_ctl = PLANE_CTL_ENABLE |
> @@ -3179,16 +3337,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
>  	rotation = plane->state->rotation;
>  	plane_ctl |= skl_plane_ctl_rotation(rotation);
>  
> -	obj = intel_fb_obj(fb);
> -	stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> -					       fb->pixel_format);
> -	surf_addr = intel_plane_obj_offset(to_intel_plane(plane), obj, 0);
> -
> -	WARN_ON(drm_rect_width(&plane_state->src) == 0);
> -
>  	scaler_id = plane_state->scaler_id;
> -	src_x = plane_state->src.x1 >> 16;
> -	src_y = plane_state->src.y1 >> 16;
>  	src_w = drm_rect_width(&plane_state->src) >> 16;
>  	src_h = drm_rect_height(&plane_state->src) >> 16;
>  	dst_x = plane_state->dst.x1;
> @@ -3196,31 +3345,48 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
>  	dst_w = drm_rect_width(&plane_state->dst);
>  	dst_h = drm_rect_height(&plane_state->dst);
>  
> -	WARN_ON(x != src_x || y != src_y);
> -
>  	if (intel_rotation_90_or_270(rotation)) {
> -		/* stride = Surface height in tiles */
> -		tile_height = intel_tile_height(dev_priv, fb->modifier[0],
> -						pixel_size);
> -		stride = DIV_ROUND_UP(fb->height, tile_height);
> -		x_offset = stride * tile_height - y - src_h;
> -		y_offset = x;
> -		plane_size = (src_w - 1) << 16 | (src_h - 1);
> +		struct drm_rect r = {
> +			.x1 = x,
> +			.x2 = x + src_w,
> +			.y1 = y,
> +			.y2 = y + src_h,
> +		};
> +
> +		/* Rotate src coordinates to match rotated GTT view */
> +		drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
> +
> +		x = r.x1;
> +		y = r.y1;
> +		src_w = drm_rect_width(&r);
> +		src_h = drm_rect_height(&r);
> +
> +		stride_div = intel_tile_height(dev_priv, fb->modifier[0],
> +					       pixel_size);
> +		stride = intel_fb->plane[0].rotated.pitch;
>  	} else {
> -		stride = fb->pitches[0] / stride_div;
> -		x_offset = x;
> -		y_offset = y;
> -		plane_size = (src_h - 1) << 16 | (src_w - 1);
> +		stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> +						       fb->pixel_format);
> +		stride = fb->pitches[0];
>  	}
> -	plane_offset = y_offset << 16 | x_offset;
>  
> -	intel_crtc->adjusted_x = x_offset;
> -	intel_crtc->adjusted_y = y_offset;
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> +	surf_addr = intel_compute_page_offset(&x, &y, fb, 0,
> +					      stride, rotation);
> +
> +	/* Sizes are 0 based */
> +	src_w--;
> +	src_h--;
> +	dst_w--;
> +	dst_h--;
> +
> +	intel_crtc->adjusted_x = x;
> +	intel_crtc->adjusted_y = y;
>  
>  	I915_WRITE(PLANE_CTL(pipe, 0), plane_ctl);
> -	I915_WRITE(PLANE_OFFSET(pipe, 0), plane_offset);
> -	I915_WRITE(PLANE_SIZE(pipe, 0), plane_size);
> -	I915_WRITE(PLANE_STRIDE(pipe, 0), stride);
> +	I915_WRITE(PLANE_OFFSET(pipe, 0), (y << 16) | x);
> +	I915_WRITE(PLANE_STRIDE(pipe, 0), stride / stride_div);
> +	I915_WRITE(PLANE_SIZE(pipe, 0), (src_h << 16) | src_w);
>  
>  	if (scaler_id >= 0) {
>  		uint32_t ps_ctrl = 0;
> @@ -3237,7 +3403,8 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
>  		I915_WRITE(PLANE_POS(pipe, 0), (dst_y << 16) | dst_x);
>  	}
>  
> -	I915_WRITE(PLANE_SURF(pipe, 0), surf_addr);
> +	I915_WRITE(PLANE_SURF(pipe, 0),
> +		   intel_surf_gtt_offset(fb, 0, rotation) + surf_addr);
>  
>  	POSTING_READ(PLANE_SURF(pipe, 0));
>  }
> @@ -11521,8 +11688,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
>  	if (ret)
>  		goto cleanup_pending;
>  
> -	work->gtt_offset = intel_plane_obj_offset(to_intel_plane(primary),
> -						  obj, 0);
> +	work->gtt_offset = intel_surf_gtt_offset(fb, 0, primary->state->rotation);
>  	work->gtt_offset += intel_crtc->dspaddr_offset;
>  
>  	if (mmio_flip) {
> @@ -14319,7 +14485,6 @@ static int intel_framebuffer_init(struct drm_device *dev,
>  				  struct drm_i915_gem_object *obj)
>  {
>  	struct drm_i915_private *dev_priv = to_i915(dev);
> -	unsigned int aligned_height;
>  	int ret;
>  	u32 pitch_limit, stride_alignment;
>  
> @@ -14443,16 +14608,13 @@ static int intel_framebuffer_init(struct drm_device *dev,
>  	if (ret)
>  		return ret;
>  
> -	aligned_height = intel_fb_align_height(dev, mode_cmd->height,
> -					       mode_cmd->pixel_format,
> -					       mode_cmd->modifier[0]);
> -	/* FIXME drm helper for size checks (especially planar formats)? */
> -	if (obj->base.size < aligned_height * mode_cmd->pitches[0])
> -		return -EINVAL;
> -
>  	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
>  	intel_fb->obj = obj;
>  
> +	ret = intel_fill_fb_info(dev_priv, &intel_fb->base);
> +	if (ret)
> +		return ret;
> +
>  	ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
>  	if (ret) {
>  		DRM_ERROR("framebuffer init failed %d\n", ret);
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 36d049d..395afd3 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -118,6 +118,19 @@ enum intel_output_type {
>  struct intel_framebuffer {
>  	struct drm_framebuffer base;
>  	struct drm_i915_gem_object *obj;
> +	struct intel_rotation_info info;
> +
> +	struct {
> +		struct {
> +			/* pixels */
> +			unsigned int x, y;
> +		} normal;
> +		struct {
> +			/* pixels */
> +			unsigned int x, y;
> +			unsigned int pitch;
> +		} rotated;
> +	} plane[2];
>  };
>  
>  struct intel_fbdev {
> @@ -1028,6 +1041,12 @@ void i915_audio_component_init(struct drm_i915_private *dev_priv);
>  void i915_audio_component_cleanup(struct drm_i915_private *dev_priv);
>  
>  /* intel_display.c */
> +unsigned int intel_rotation_info_size(const struct intel_rotation_info *info);
> +unsigned int intel_fb_xy_to_linear(int x, int y,
> +				   const struct drm_framebuffer *fb, int plane);
> +void intel_add_fb_offsets(int *x, int *y,
> +			  const struct drm_framebuffer *fb, int plane,
> +			  unsigned int rotation);
>  extern const struct drm_plane_funcs intel_plane_funcs;
>  bool intel_has_pending_fb_unpin(struct drm_device *dev);
>  int intel_pch_rawclk(struct drm_device *dev);
> @@ -1134,10 +1153,10 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv,
>  void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state);
>  #define assert_pipe_enabled(d, p) assert_pipe(d, p, true)
>  #define assert_pipe_disabled(d, p) assert_pipe(d, p, false)
> -unsigned long intel_compute_page_offset(int *x, int *y,
> -					const struct drm_framebuffer *fb, int plane,
> -					unsigned int pitch,
> -					unsigned int rotation);
> +unsigned int intel_compute_page_offset(int *x, int *y,
> +				       const struct drm_framebuffer *fb, int plane,
> +				       unsigned int pitch,
> +				       unsigned int rotation);
>  void intel_prepare_reset(struct drm_device *dev);
>  void intel_finish_reset(struct drm_device *dev);
>  void hsw_enable_pc8(struct drm_i915_private *dev_priv);
> @@ -1174,9 +1193,8 @@ void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file);
>  int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
>  int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
>  
> -unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
> -				     struct drm_i915_gem_object *obj,
> -				     unsigned int plane);
> +unsigned int intel_surf_gtt_offset(struct drm_framebuffer *fb, int plane,
> +				   unsigned int rotation);
>  
>  u32 skl_plane_ctl_format(uint32_t pixel_format);
>  u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> index 828c3eb..d9d37b0 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -188,17 +188,15 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
>  	struct drm_device *dev = drm_plane->dev;
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	struct intel_plane *intel_plane = to_intel_plane(drm_plane);
> -	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
> +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
>  	const int pipe = intel_plane->pipe;
>  	const int plane = intel_plane->plane + 1;
>  	u32 plane_ctl, stride_div, stride;
>  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
>  	const struct drm_intel_sprite_colorkey *key =
>  		&to_intel_plane_state(drm_plane->state)->ckey;
> -	unsigned long surf_addr;
> -	u32 tile_height, plane_offset, plane_size;
> +	unsigned int surf_addr;
>  	unsigned int rotation;
> -	int x_offset, y_offset;
>  	struct intel_crtc_state *crtc_state = to_intel_crtc(crtc)->config;
>  	int scaler_id;
>  
> @@ -215,17 +213,8 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
>  				       pixel_size, true,
>  				       src_w != crtc_w || src_h != crtc_h);
>  
> -	stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> -					       fb->pixel_format);
> -
>  	scaler_id = to_intel_plane_state(drm_plane->state)->scaler_id;
>  
> -	/* Sizes are 0 based */
> -	src_w--;
> -	src_h--;
> -	crtc_w--;
> -	crtc_h--;
> -
>  	if (key->flags) {
>  		I915_WRITE(PLANE_KEYVAL(pipe, plane), key->min_value);
>  		I915_WRITE(PLANE_KEYMAX(pipe, plane), key->max_value);
> @@ -237,27 +226,43 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
>  	else if (key->flags & I915_SET_COLORKEY_SOURCE)
>  		plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE;
>  
> -	surf_addr = intel_plane_obj_offset(intel_plane, obj, 0);
> -
>  	if (intel_rotation_90_or_270(rotation)) {
> -		/* stride: Surface height in tiles */
> -		tile_height = intel_tile_height(dev_priv, fb->modifier[0],
> -						pixel_size);
> -		stride = DIV_ROUND_UP(fb->height, tile_height);
> -		plane_size = (src_w << 16) | src_h;
> -		x_offset = stride * tile_height - y - (src_h + 1);
> -		y_offset = x;
> +		struct drm_rect r = {
> +			.x1 = x,
> +			.x2 = x + src_w,
> +			.y1 = y,
> +			.y2 = y + src_h,
> +		};
> +
> +		/* Rotate src coordinates to match rotated GTT view */
> +		drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
> +
> +		x = r.x1;
> +		y = r.y1;
> +		src_w = drm_rect_width(&r);
> +		src_h = drm_rect_height(&r);
> +
> +		stride_div = intel_tile_height(dev_priv, fb->modifier[0], pixel_size);
> +		stride = intel_fb->plane[0].rotated.pitch;
>  	} else {
> -		stride = fb->pitches[0] / stride_div;
> -		plane_size = (src_h << 16) | src_w;
> -		x_offset = x;
> -		y_offset = y;
> +		stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> +						       fb->pixel_format);
> +		stride = fb->pitches[0];
>  	}
> -	plane_offset = y_offset << 16 | x_offset;
>  
> -	I915_WRITE(PLANE_OFFSET(pipe, plane), plane_offset);
> -	I915_WRITE(PLANE_STRIDE(pipe, plane), stride);
> -	I915_WRITE(PLANE_SIZE(pipe, plane), plane_size);
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> +	surf_addr = intel_compute_page_offset(&x, &y, fb, 0,
> +					      stride, rotation);
> +
> +	/* Sizes are 0 based */
> +	src_w--;
> +	src_h--;
> +	crtc_w--;
> +	crtc_h--;
> +
> +	I915_WRITE(PLANE_OFFSET(pipe, plane), (y << 16) | x);
> +	I915_WRITE(PLANE_STRIDE(pipe, plane), stride / stride_div);
> +	I915_WRITE(PLANE_SIZE(pipe, plane), (src_h << 16) | src_w);
>  
>  	/* program plane scaler */
>  	if (scaler_id >= 0) {
> @@ -279,7 +284,8 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
>  	}
>  
>  	I915_WRITE(PLANE_CTL(pipe, plane), plane_ctl);
> -	I915_WRITE(PLANE_SURF(pipe, plane), surf_addr);
> +	I915_WRITE(PLANE_SURF(pipe, plane),
> +		   intel_surf_gtt_offset(fb, 0, rotation) + surf_addr);
>  	POSTING_READ(PLANE_SURF(pipe, plane));
>  }
>  
> @@ -355,8 +361,7 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  	int plane = intel_plane->plane;
>  	u32 sprctl;
>  	unsigned int rotation = dplane->state->rotation;
> -	unsigned long sprsurf_offset, linear_offset;
> -	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> +	unsigned int sprsurf_offset, linear_offset;
>  	const struct drm_intel_sprite_colorkey *key =
>  		&to_intel_plane_state(dplane->state)->ckey;
>  
> @@ -420,19 +425,19 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  	crtc_w--;
>  	crtc_h--;
>  
> -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
>  	sprsurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
>  						   fb->pitches[0], rotation);
> -	linear_offset -= sprsurf_offset;
>  
>  	if (rotation == BIT(DRM_ROTATE_180)) {
>  		sprctl |= SP_ROTATE_180;
>  
>  		x += src_w;
>  		y += src_h;
> -		linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
>  	}
>  
> +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> +
>  	if (key->flags) {
>  		I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value);
>  		I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value);
> @@ -457,8 +462,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  
>  	I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);
>  	I915_WRITE(SPCNTR(pipe, plane), sprctl);
> -	I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
> -		   sprsurf_offset);
> +	I915_WRITE(SPSURF(pipe, plane),
> +		   intel_surf_gtt_offset(fb, 0, rotation) + sprsurf_offset);
>  	POSTING_READ(SPSURF(pipe, plane));
>  }
>  
> @@ -492,7 +497,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	enum pipe pipe = intel_plane->pipe;
>  	u32 sprctl, sprscale = 0;
>  	unsigned int rotation = plane->state->rotation;
> -	unsigned long sprsurf_offset, linear_offset;
> +	unsigned int sprsurf_offset, linear_offset;
>  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
>  	const struct drm_intel_sprite_colorkey *key =
>  		&to_intel_plane_state(plane->state)->ckey;
> @@ -552,10 +557,9 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	if (crtc_w != src_w || crtc_h != src_h)
>  		sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
>  
> -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
>  	sprsurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
>  						   fb->pitches[0], rotation);
> -	linear_offset -= sprsurf_offset;
>  
>  	if (rotation == BIT(DRM_ROTATE_180)) {
>  		sprctl |= SPRITE_ROTATE_180;
> @@ -564,11 +568,11 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  		if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
>  			x += src_w;
>  			y += src_h;
> -			linear_offset += src_h * fb->pitches[0] +
> -				src_w * pixel_size;
>  		}
>  	}
>  
> +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> +
>  	if (key->flags) {
>  		I915_WRITE(SPRKEYVAL(pipe), key->min_value);
>  		I915_WRITE(SPRKEYMAX(pipe), key->max_value);
> @@ -597,7 +601,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  		I915_WRITE(SPRSCALE(pipe), sprscale);
>  	I915_WRITE(SPRCTL(pipe), sprctl);
>  	I915_WRITE(SPRSURF(pipe),
> -		   i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
> +		   intel_surf_gtt_offset(fb, 0, rotation) + sprsurf_offset);
>  	POSTING_READ(SPRSURF(pipe));
>  }
>  
> @@ -632,7 +636,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
>  	int pipe = intel_plane->pipe;
>  	unsigned int rotation = plane->state->rotation;
> -	unsigned long dvssurf_offset, linear_offset;
> +	unsigned int dvssurf_offset, linear_offset;
>  	u32 dvscntr, dvsscale;
>  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
>  	const struct drm_intel_sprite_colorkey *key =
> @@ -689,19 +693,19 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	if (crtc_w != src_w || crtc_h != src_h)
>  		dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
>  
> -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
>  	dvssurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
>  						   fb->pitches[0], rotation);
> -	linear_offset -= dvssurf_offset;
>  
>  	if (rotation == BIT(DRM_ROTATE_180)) {
>  		dvscntr |= DVS_ROTATE_180;
>  
>  		x += src_w;
>  		y += src_h;
> -		linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
>  	}
>  
> +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> +
>  	if (key->flags) {
>  		I915_WRITE(DVSKEYVAL(pipe), key->min_value);
>  		I915_WRITE(DVSKEYMAX(pipe), key->max_value);
> @@ -725,7 +729,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
>  	I915_WRITE(DVSSCALE(pipe), dvsscale);
>  	I915_WRITE(DVSCNTR(pipe), dvscntr);
>  	I915_WRITE(DVSSURF(pipe),
> -		   i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
> +		   intel_surf_gtt_offset(fb, 0, rotation) + dvssurf_offset);
>  	POSTING_READ(DVSSURF(pipe));
>  }
>  
> -- 
> 2.4.9
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Ville Syrjälä Oct. 21, 2015, 2:19 p.m. UTC | #2
On Wed, Oct 21, 2015 at 02:01:01PM +0200, Daniel Vetter wrote:
> On Thu, Oct 15, 2015 at 08:59:45PM +0300, ville.syrjala@linux.intel.com wrote:
> > From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > 
> > Redo the fb rotation handling in order to:
> > - eliminate the NV12 special casing
> > - handle fb->offsets[] properly
> > - make the rotation handling reasier for the plane code
> 
> I think this is too big and should be split a bit. There's also a pile of
> renames mixed int on top.

It grew from a bunch of sepearate changes, but they got gobbled
together due to changing the entire approach a few times. In the end I
was too scared to split it up anymore. But I guess I should really try.

> 
> > To achieve these goals we reduce intel_rotation_info to only contain
> > (for each plane) the rotated view width,height,stride in tile units,
> > and the page offset into the object where the plane starts. Each plane
> > is handled exactly the same way, no special casing for NV12 or other
> > formats.
> 
> That sounds like a good prep patch to split out.
> 
> > We then store the computed rotation_info under
> > intel_framebuffer so that we don't have to recompute it again.
> 
> And another one, plus then following up with the fallout.
> 
> This patch also fixes the intel_plane_obj_offset issue for earlier in the
> series. Looks good otherwise from reading through it.
> 
> > To handle fb->offsets[] we treat them as a linear offsets and convert
> > them to x/y offsets from the start of the relevant GTT mapping (either
> > normal or rotated). We store the x/y offsets under intel_framebuffer,
> > and for some extra convenience we also store the rotated pitch (ie.
> > tile aligned plane height). So for each plane we have the normal
> > x/y offsets, rotated x/y offsets, and the rotated pitch. The normal
> > pitch is available already in fb->pitches[].
> > 
> > While we're gathering up all that extra information, we can also easily
> > compute the storage requirements for the framebuffer, so that we can
> > check that the object is big enough to hold it.
> > 
> > When it comes time to deal with the plane source coordinates, we first
> > rotate the clipped src coordinates to match the relevant GTT view
> > orientation, then add to them the fb x/y offsets. Next we compute
> > the aligned surface page offset, and as a result we're left with some
> > residual x/y offsets. Finally, if required by the hardware, we convert
> > the remaining x/y offsets into a linear offset.
> > 
> > For gen2/3 we simply skip computing the final page offset, and just
> > convert the src+fb x/y offsets directly into a linear offset since
> > that's what the hardware wants.
> > 
> > After this all platforms, incluing SKL+, compute these things in exactly
> > the same way (excluding alignemnt differences).
> > 
> > v2: Use BIT(DRM_ROTATE_270) instead of ROTATE_270 when rotating
> >     plane src coordinates
> >     Drop some spurious changes that got left behind during development
> > 
> > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> Two comments below inline.
> -Daniel
> 
> > ---
> >  drivers/gpu/drm/i915/i915_gem_gtt.c  |  66 ++----
> >  drivers/gpu/drm/i915/i915_gem_gtt.h  |  14 +-
> >  drivers/gpu/drm/i915/intel_display.c | 448 ++++++++++++++++++++++++-----------
> >  drivers/gpu/drm/i915/intel_drv.h     |  32 ++-
> >  drivers/gpu/drm/i915/intel_sprite.c  | 102 ++++----
> >  5 files changed, 402 insertions(+), 260 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
> > index bb95b86..98aee6c 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_gtt.c
> > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
> > @@ -3255,11 +3255,6 @@ rotate_pages(const dma_addr_t *in, unsigned int offset,
> >  	unsigned int column, row;
> >  	unsigned int src_idx;
> >  
> > -	if (!sg) {
> > -		st->nents = 0;
> > -		sg = st->sgl;
> > -	}
> > -
> >  	for (column = 0; column < width; column++) {
> >  		src_idx = stride * (height - 1) + column;
> >  		for (row = 0; row < height; row++) {
> > @@ -3280,16 +3275,14 @@ rotate_pages(const dma_addr_t *in, unsigned int offset,
> >  }
> >  
> >  static struct sg_table *
> > -intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
> > +intel_rotate_fb_obj_pages(const struct intel_rotation_info *info,
> >  			  struct drm_i915_gem_object *obj)
> >  {
> > -	unsigned int size_pages = rot_info->size >> PAGE_SHIFT;
> > -	unsigned int size_pages_uv;
> > +	unsigned int size = intel_rotation_info_size(info);
> >  	struct sg_page_iter sg_iter;
> >  	unsigned long i;
> >  	dma_addr_t *page_addr_list;
> >  	struct sg_table *st;
> > -	unsigned int uv_start_page;
> >  	struct scatterlist *sg;
> >  	int ret = -ENOMEM;
> >  
> > @@ -3299,57 +3292,32 @@ intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
> >  	if (!page_addr_list)
> >  		return ERR_PTR(ret);
> >  
> > -	/* Account for UV plane with NV12. */
> > -	if (rot_info->pixel_format == DRM_FORMAT_NV12)
> > -		size_pages_uv = rot_info->size_uv >> PAGE_SHIFT;
> > -	else
> > -		size_pages_uv = 0;
> > -
> >  	/* Allocate target SG list. */
> >  	st = kmalloc(sizeof(*st), GFP_KERNEL);
> >  	if (!st)
> >  		goto err_st_alloc;
> >  
> > -	ret = sg_alloc_table(st, size_pages + size_pages_uv, GFP_KERNEL);
> > +	ret = sg_alloc_table(st, size, GFP_KERNEL);
> >  	if (ret)
> >  		goto err_sg_alloc;
> >  
> >  	/* Populate source page list from the object. */
> >  	i = 0;
> >  	for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
> > -		page_addr_list[i] = sg_page_iter_dma_address(&sg_iter);
> > -		i++;
> > +		page_addr_list[i++] = sg_page_iter_dma_address(&sg_iter);
> >  	}
> >  
> > -	/* Rotate the pages. */
> > -	sg = rotate_pages(page_addr_list, 0,
> > -		     rot_info->width_pages, rot_info->height_pages,
> > -		     rot_info->width_pages,
> > -		     st, NULL);
> > +	st->nents = 0;
> > +	sg = st->sgl;
> >  
> > -	/* Append the UV plane if NV12. */
> > -	if (rot_info->pixel_format == DRM_FORMAT_NV12) {
> > -		uv_start_page = size_pages;
> > -
> > -		/* Check for tile-row un-alignment. */
> > -		if (offset_in_page(rot_info->uv_offset))
> > -			uv_start_page--;
> > -
> > -		rot_info->uv_start_page = uv_start_page;
> > -
> > -		rotate_pages(page_addr_list, uv_start_page,
> > -			     rot_info->width_pages_uv,
> > -			     rot_info->height_pages_uv,
> > -			     rot_info->width_pages_uv,
> > -			     st, sg);
> > +	for (i = 0 ; i < ARRAY_SIZE(info->plane); i++) {
> > +		sg = rotate_pages(page_addr_list, info->plane[i].offset,
> > +				  info->plane[i].width, info->plane[i].height,
> > +				  info->plane[i].stride, st, sg);
> >  	}
> >  
> > -	DRM_DEBUG_KMS(
> > -		      "Created rotated page mapping for object size %zu (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %u pages (%u plane 0)).\n",
> > -		      obj->base.size, rot_info->pitch, rot_info->height,
> > -		      rot_info->pixel_format, rot_info->width_pages,
> > -		      rot_info->height_pages, size_pages + size_pages_uv,
> > -		      size_pages);
> > +	DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages)\n",
> > +		      obj->base.size, info->plane[0].width, info->plane[0].height, size);
> >  
> >  	drm_free_large(page_addr_list);
> >  
> > @@ -3360,12 +3328,8 @@ err_sg_alloc:
> >  err_st_alloc:
> >  	drm_free_large(page_addr_list);
> >  
> > -	DRM_DEBUG_KMS(
> > -		      "Failed to create rotated mapping for object size %zu! (%d) (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %u pages (%u plane 0))\n",
> > -		      obj->base.size, ret, rot_info->pitch, rot_info->height,
> > -		      rot_info->pixel_format, rot_info->width_pages,
> > -		      rot_info->height_pages, size_pages + size_pages_uv,
> > -		      size_pages);
> > +	DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n",
> > +		      obj->base.size, info->plane[0].width, info->plane[0].height, size);
> >  	return ERR_PTR(ret);
> >  }
> >  
> > @@ -3516,7 +3480,7 @@ i915_ggtt_view_size(struct drm_i915_gem_object *obj,
> >  	if (view->type == I915_GGTT_VIEW_NORMAL) {
> >  		return obj->base.size;
> >  	} else if (view->type == I915_GGTT_VIEW_ROTATED) {
> > -		return view->rotated.size;
> > +		return intel_rotation_info_size(&view->rotated) << PAGE_SHIFT;
> >  	} else if (view->type == I915_GGTT_VIEW_PARTIAL) {
> >  		return view->partial.size << PAGE_SHIFT;
> >  	} else {
> > diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
> > index 68de734..ea28f7d 100644
> > --- a/drivers/gpu/drm/i915/i915_gem_gtt.h
> > +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
> > @@ -136,16 +136,10 @@ enum i915_ggtt_view_type {
> >  };
> >  
> >  struct intel_rotation_info {
> > -	unsigned int height;
> > -	unsigned int pitch;
> > -	unsigned int uv_offset;
> > -	uint32_t pixel_format;
> > -	uint64_t fb_modifier;
> > -	unsigned int width_pages, height_pages;
> > -	uint64_t size;
> > -	unsigned int width_pages_uv, height_pages_uv;
> > -	uint64_t size_uv;
> > -	unsigned int uv_start_page;
> > +	struct {
> > +		/* tiles */
> > +		unsigned int width, height, stride, offset;
> > +	} plane[2];
> >  };
> >  
> >  struct i915_ggtt_view {
> > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> > index 028dc4a..0dcb710 100644
> > --- a/drivers/gpu/drm/i915/intel_display.c
> > +++ b/drivers/gpu/drm/i915/intel_display.c
> > @@ -2274,49 +2274,28 @@ intel_fb_align_height(struct drm_device *dev, unsigned int height,
> >  	return ALIGN(height, tile_height);
> >  }
> >  
> > -static int
> > +unsigned int intel_rotation_info_size(const struct intel_rotation_info *info)
> > +{
> > +	unsigned int size = 0;
> > +	int i;
> > +
> > +	for (i = 0 ; i < ARRAY_SIZE(info->plane); i++)
> > +		size += info->plane[i].width * info->plane[i].height;
> > +
> > +	return size;
> > +}
> > +
> > +static void
> >  intel_fill_fb_ggtt_view(struct i915_ggtt_view *view,
> >  			const struct drm_framebuffer *fb,
> >  			unsigned int rotation)
> >  {
> > -	struct drm_i915_private *dev_priv = to_i915(fb->dev);
> > -	struct intel_rotation_info *info = &view->rotated;
> > -	unsigned int tile_size, tile_width, tile_height, cpp;
> > -
> > -	*view = i915_ggtt_view_normal;
> > -
> > -	if (!intel_rotation_90_or_270(rotation))
> > -		return 0;
> > -
> > -	*view = i915_ggtt_view_rotated;
> > -
> > -	info->height = fb->height;
> > -	info->pixel_format = fb->pixel_format;
> > -	info->pitch = fb->pitches[0];
> > -	info->uv_offset = fb->offsets[1];
> > -	info->fb_modifier = fb->modifier[0];
> > -
> > -	tile_size = intel_tile_size(dev_priv);
> > -
> > -	cpp = drm_format_plane_cpp(fb->pixel_format, 0);
> > -	tile_width = intel_tile_width(dev_priv, cpp, fb->modifier[0]);
> > -	tile_height = tile_size / tile_width;
> > -
> > -	info->width_pages = DIV_ROUND_UP(fb->pitches[0], tile_width);
> > -	info->height_pages = DIV_ROUND_UP(fb->height, tile_height);
> > -	info->size = info->width_pages * info->height_pages * tile_size;
> > -
> > -	if (info->pixel_format == DRM_FORMAT_NV12) {
> > -		cpp = drm_format_plane_cpp(fb->pixel_format, 1);
> > -		tile_width = intel_tile_width(dev_priv, fb->modifier[1], cpp);
> > -		tile_height = tile_size / tile_width;
> > -
> > -		info->width_pages_uv = DIV_ROUND_UP(fb->pitches[1], tile_width);
> > -		info->height_pages_uv = DIV_ROUND_UP(fb->height / 2, tile_height);
> > -		info->size_uv = info->width_pages_uv * info->height_pages_uv * tile_size;
> > +	if (intel_rotation_90_or_270(rotation)) {
> > +		*view = i915_ggtt_view_rotated;
> > +		view->rotated = to_intel_framebuffer(fb)->info;
> > +	} else {
> > +		*view = i915_ggtt_view_normal;
> >  	}
> > -
> > -	return 0;
> >  }
> >  
> >  static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv)
> > @@ -2368,9 +2347,7 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
> >  
> >  	alignment = intel_surf_alignment(dev_priv, fb->modifier[0]);
> >  
> > -	ret = intel_fill_fb_ggtt_view(&view, fb, rotation);
> > -	if (ret)
> > -		return ret;
> > +	intel_fill_fb_ggtt_view(&view, fb, rotation);
> >  
> >  	/* Note that the w/a also requires 64 PTE of padding following the
> >  	 * bo. We currently fill all unused PTE with the shadow page and so
> > @@ -2433,18 +2410,31 @@ static void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation
> >  {
> >  	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
> >  	struct i915_ggtt_view view;
> > -	int ret;
> >  
> >  	WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex));
> >  
> > -	ret = intel_fill_fb_ggtt_view(&view, fb, rotation);
> > -	WARN_ONCE(ret, "Couldn't get view from plane state!");
> > +	intel_fill_fb_ggtt_view(&view, fb, rotation);
> >  
> >  	i915_gem_object_unpin_fence(obj);
> >  	i915_gem_object_unpin_from_display_plane(obj, &view);
> >  }
> >  
> >  /*
> > + * Convert the x/y offsets into a linear offset.
> > + * Only valid with 0/180 degree rotation, which is fine since linear
> > + * offset is only used with linear buffers on pre-hsw and tiled buffers
> > + * with gen2/3, and 90/270 degree rotations isn't supported on any of them.
> > + */
> > +unsigned int intel_fb_xy_to_linear(int x, int y,
> > +				   const struct drm_framebuffer *fb, int plane)
> > +{
> > +	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> > +	unsigned int pitch = fb->pitches[plane];
> > +
> > +	return y * pitch + x * cpp;
> > +}
> > +
> > +/*
> >   * Return the tile dimensions in pixel units matching
> >   * the specified rotation angle.
> >   */
> > @@ -2475,6 +2465,55 @@ static void intel_rotate_tile_dims(unsigned int *tile_width,
> >  }
> >  
> >  /*
> > + * Add the x/y offsets derived from fb->offsets[] to the user
> > + * specified plane src x/y offsets. The resulting x/y offsets
> > + * specify the start of scanout from the beginning of the gtt mapping.
> > + */
> > +void intel_add_fb_offsets(int *x, int *y,
> > +			  const struct drm_framebuffer *fb, int plane,
> > +			  unsigned int rotation)
> > +
> > +{
> > +	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
> > +	const struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
> > +	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> > +	unsigned int pitch;
> > +
> > +	if (intel_rotation_90_or_270(rotation)) {
> > +		pitch = intel_fb->plane[plane].rotated.pitch;
> > +
> > +		*x += intel_fb->plane[plane].rotated.x;
> > +		*y += intel_fb->plane[plane].rotated.y;
> > +	} else {
> > +		pitch = fb->pitches[plane];
> > +
> > +		*x += intel_fb->plane[plane].normal.x;
> > +		*y += intel_fb->plane[plane].normal.y;
> > +	}
> > +
> > +	/* minimize x */
> > +	if (fb->modifier[plane] != DRM_FORMAT_MOD_NONE) {
> > +		unsigned int tile_size, tile_width, tile_height;
> > +
> > +		tile_size = intel_tile_size(dev_priv);
> > +		tile_width = intel_tile_width(dev_priv, fb->modifier[plane], cpp);
> > +		tile_height = tile_size / tile_width;
> > +
> > +		intel_rotate_tile_dims(&tile_width, &tile_height,
> > +				       &pitch, cpp, rotation);
> > +
> > +		*y += *x / pitch * tile_height;
> > +		*x  = *x % pitch;
> > +	} else {
> > +		/* in pixels */
> > +		pitch /= cpp;
> > +
> > +		*y += *x / pitch;
> > +		*x  = *x % pitch;
> > +	}
> 
> This is called before we do the entire page_offset dance, so I don't think
> we need a special case for tiled vs. untiled.

Hmm. I think this is leftover from some earlier version where I did
stuff in a different order. Yeah, the page_offset stuff should deal
with this just fine, and for gen2/3 we just convert it to a linear 
offset anyway so it's the same thing in the end. I'll just throw the
entire "minimize x" crap in the bin.

> -Daniel
> 
> > +}
> > +
> > +/*
> >   * Adjust the page offset by moving the difference into
> >   * the x/y offsets.
> >   *
> > @@ -2514,20 +2553,23 @@ static void intel_adjust_page_offset(int *x, int *y,
> >   * In the 90/270 rotated case, x and y are assumed
> >   * to be already rotated to match the rotated GTT view, and
> >   * pitch is the tile_height aligned framebuffer height.
> > + *
> > + * This function is used when computing the derived information
> > + * under intel_framebuffer, so using any of that information
> > + * here is not allowed. Anything under drm_framebuffer can be
> > + * used. This is why the user has to pass in the pitch since it
> > + * is specified in the rotated orientation.
> >   */
> > -unsigned long intel_compute_page_offset(int *x, int *y,
> > -					const struct drm_framebuffer *fb, int plane,
> > -					unsigned int pitch,
> > -					unsigned int rotation)
> > +static unsigned int _intel_compute_page_offset(const struct drm_i915_private *dev_priv,
> > +					       int *x, int *y,
> > +					       const struct drm_framebuffer *fb, int plane,
> > +					       unsigned int pitch,
> > +					       unsigned int rotation,
> > +					       unsigned int alignment)
> >  {
> > -	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
> >  	uint64_t fb_modifier = fb->modifier[plane];
> >  	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> > -	unsigned int offset, alignment;
> > -
> > -	alignment = intel_surf_alignment(dev_priv, fb_modifier);
> > -	if (alignment)
> > -		alignment--;
> > +	unsigned int offset;
> >  
> >  	if (fb_modifier != DRM_FORMAT_MOD_NONE) {
> >  		unsigned int tile_size, tile_width, tile_height;
> > @@ -2560,6 +2602,145 @@ unsigned long intel_compute_page_offset(int *x, int *y,
> >  	return offset & ~alignment;
> >  }
> >  
> > +unsigned int intel_compute_page_offset(int *x, int *y,
> > +				       const struct drm_framebuffer *fb, int plane,
> > +				       unsigned int pitch,
> > +				       unsigned int rotation)
> > +{
> > +	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
> > +	unsigned int alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]);
> > +
> > +	return _intel_compute_page_offset(dev_priv, x, y, fb, plane, pitch,
> > +					  rotation, alignment ? (alignment - 1) : 0);
> > +}
> > +
> > +/* Convert the fb->offset[] linear offset into x/y offsets */
> > +static void intel_fb_offset_to_xy(int *x, int *y,
> > +				  const struct drm_framebuffer *fb, int plane)
> > +{
> > +	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
> > +	unsigned int pitch = fb->pitches[plane];
> > +	unsigned int linear_offset = fb->offsets[plane];
> > +
> > +	*y = linear_offset / pitch;
> > +	*x = linear_offset % pitch / cpp;
> > +}
> > +
> > +static int
> > +intel_fill_fb_info(struct drm_i915_private *dev_priv,
> > +		   struct drm_framebuffer *fb)
> > +{
> > +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
> > +	struct intel_rotation_info *info = &intel_fb->info;
> > +	unsigned int tile_size;
> > +	unsigned int gtt_offset_rotated = 0;
> > +	unsigned int max_size = 0;
> > +	uint32_t format = fb->pixel_format;
> > +	int i, num_planes = drm_format_num_planes(format);
> > +
> > +	tile_size = intel_tile_size(dev_priv);
> > +
> > +	for (i = 0; i < num_planes; i++) {
> > +		unsigned int width, height;
> > +		unsigned int cpp, offset, size;
> > +		int x, y;
> > +
> > +		cpp = drm_format_plane_cpp(format, i);
> > +		width = drm_format_plane_width(fb->width, format, i);
> > +		height = drm_format_plane_height(fb->height, format, i);
> > +
> > +		intel_fb_offset_to_xy(&x, &y, fb, i);
> > +
> > +		/*
> > +		 * First pixel of the framebuffer from
> > +		 * the start of the normal gtt mapping.
> > +		 */
> > +		intel_fb->plane[i].normal.x = x;
> > +		intel_fb->plane[i].normal.y = y;
> > +
> > +		offset = _intel_compute_page_offset(dev_priv, &x, &y,
> > +						    fb, 0, fb->pitches[i],
> > +						    BIT(DRM_ROTATE_0),
> > +						    tile_size);
> > +		offset /= tile_size;
> > +		DRM_DEBUG("page offset %u pages\n", offset);
> > +
> > +		if (fb->modifier[i] != DRM_FORMAT_MOD_NONE) {
> > +			unsigned int tile_width, tile_height;
> > +			unsigned int pitch;
> > +			struct drm_rect r;
> > +
> > +			tile_width = intel_tile_width(dev_priv, fb->modifier[i], cpp);
> > +			tile_height = tile_size / tile_width;
> > +
> > +			info->plane[i].offset = offset;
> > +			info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i], tile_width);
> > +			info->plane[i].width = DIV_ROUND_UP((x + width) * cpp, tile_width);
> > +			info->plane[i].height = DIV_ROUND_UP(y + height, tile_height);
> > +
> > +
> > +			intel_fb->plane[i].rotated.pitch =
> > +				info->plane[i].height * tile_height;
> > +
> > +			/* how many tiles does this plane need */
> > +			size = info->plane[i].stride * info->plane[i].height;
> > +			/*
> > +			 * If the plane isn't horizontally tile aligned,
> > +			 * we need one more tile.
> > +			 */
> > +			if (x != 0)
> > +				size++;
> > +
> > +			pitch = intel_fb->plane[i].rotated.pitch;
> > +
> > +			/* rotate the x/y offsets to match the GTT view */
> > +			r.x1 = x;
> > +			r.y1 = y;
> > +			r.x2 = x + width;
> > +			r.y2 = y + height;
> > +			drm_rect_rotate(&r, width, pitch, BIT(DRM_ROTATE_270));
> > +			x = r.x1;
> > +			y = r.y1;
> > +
> > +			intel_rotate_tile_dims(&tile_width, &tile_height, &pitch,
> > +					       cpp, BIT(DRM_ROTATE_270));
> > +
> > +			/*
> > +			 * We only keep the x/y offsets, so push all of the
> > +			 * gtt offset into the x/y offsets.
> > +			 */
> > +			intel_adjust_page_offset(&x, &y, tile_width, tile_height,
> > +						 tile_size, pitch,
> > +						 gtt_offset_rotated * tile_size, 0);
> > +
> > +			gtt_offset_rotated += info->plane[i].width * info->plane[i].height;
> > +
> > +			/*
> > +			 * First pixel of the framebuffer from
> > +			 * the start of the rotated gtt mapping.
> > +			 */
> > +			intel_fb->plane[i].rotated.x = x;
> > +			intel_fb->plane[i].rotated.y = y;
> > +		} else {
> > +			size = DIV_ROUND_UP((y + height) * fb->pitches[i] +
> > +					    x * cpp, tile_size);
> > +		}
> > +		DRM_DEBUG("%d offset %u, size %u, stride %u, height %u\n",
> > +			  i, offset, size, info->plane[i].stride, info->plane[i].height);
> > +
> > +		/* how many tiles in total needed in the bo */
> > +		max_size = max(max_size, offset + size);
> > +	}
> > +
> > +	if (max_size * tile_size > to_intel_framebuffer(fb)->obj->base.size) {
> > +		DRM_DEBUG("fb too big for bo (need %u bytes, have %zu bytes)\n",
> > +			  max_size * tile_size, to_intel_framebuffer(fb)->obj->base.size);
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static int i9xx_format_to_fourcc(int format)
> >  {
> >  	switch (format) {
> > @@ -2761,7 +2942,7 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
> >  	struct drm_i915_gem_object *obj;
> >  	int plane = intel_crtc->plane;
> >  	unsigned int rotation;
> > -	unsigned long linear_offset;
> > +	unsigned int linear_offset;
> >  	u32 dspcntr;
> >  	u32 reg = DSPCNTR(plane);
> >  	int pixel_size;
> > @@ -2839,30 +3020,25 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
> >  	if (IS_G4X(dev))
> >  		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
> >  
> > -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> >  
> > -	if (INTEL_INFO(dev)->gen >= 4) {
> > +	if (INTEL_INFO(dev)->gen >= 4)
> >  		intel_crtc->dspaddr_offset =
> >  			intel_compute_page_offset(&x, &y, fb, 0,
> >  						  fb->pitches[0], rotation);
> > -		linear_offset -= intel_crtc->dspaddr_offset;
> > -	} else {
> > -		intel_crtc->dspaddr_offset = linear_offset;
> > -	}
> >  
> >  	if (crtc->primary->state->rotation == BIT(DRM_ROTATE_180)) {
> >  		dspcntr |= DISPPLANE_ROTATE_180;
> >  
> >  		x += (intel_crtc->config->pipe_src_w - 1);
> >  		y += (intel_crtc->config->pipe_src_h - 1);
> > -
> > -		/* Finding the last pixel of the last line of the display
> > -		data and adding to linear_offset*/
> > -		linear_offset +=
> > -			(intel_crtc->config->pipe_src_h - 1) * fb->pitches[0] +
> > -			(intel_crtc->config->pipe_src_w - 1) * pixel_size;
> >  	}
> >  
> > +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> > +
> > +	if (INTEL_INFO(dev)->gen < 4)
> > +		intel_crtc->dspaddr_offset = linear_offset;
> > +
> >  	intel_crtc->adjusted_x = x;
> >  	intel_crtc->adjusted_y = y;
> >  
> > @@ -2871,7 +3047,8 @@ static void i9xx_update_primary_plane(struct drm_crtc *crtc,
> >  	I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
> >  	if (INTEL_INFO(dev)->gen >= 4) {
> >  		I915_WRITE(DSPSURF(plane),
> > -			   i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
> > +			   intel_surf_gtt_offset(fb, 0, rotation) +
> > +			   intel_crtc->dspaddr_offset);
> >  		I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
> >  		I915_WRITE(DSPLINOFF(plane), linear_offset);
> >  	} else
> > @@ -2891,7 +3068,7 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
> >  	struct drm_i915_gem_object *obj;
> >  	int plane = intel_crtc->plane;
> >  	unsigned int rotation;
> > -	unsigned long linear_offset;
> > +	unsigned int linear_offset;
> >  	u32 dspcntr;
> >  	u32 reg = DSPCNTR(plane);
> >  	int pixel_size;
> > @@ -2946,26 +3123,22 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
> >  	if (!IS_HASWELL(dev) && !IS_BROADWELL(dev))
> >  		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
> >  
> > -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> >  	intel_crtc->dspaddr_offset =
> >  		intel_compute_page_offset(&x, &y, fb, 0,
> >  					  fb->pitches[0], rotation);
> > -	linear_offset -= intel_crtc->dspaddr_offset;
> > +
> >  	if (rotation == BIT(DRM_ROTATE_180)) {
> >  		dspcntr |= DISPPLANE_ROTATE_180;
> >  
> >  		if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
> >  			x += (intel_crtc->config->pipe_src_w - 1);
> >  			y += (intel_crtc->config->pipe_src_h - 1);
> > -
> > -			/* Finding the last pixel of the last line of the display
> > -			data and adding to linear_offset*/
> > -			linear_offset +=
> > -				(intel_crtc->config->pipe_src_h - 1) * fb->pitches[0] +
> > -				(intel_crtc->config->pipe_src_w - 1) * pixel_size;
> >  		}
> >  	}
> >  
> > +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> > +
> >  	intel_crtc->adjusted_x = x;
> >  	intel_crtc->adjusted_y = y;
> >  
> > @@ -2973,7 +3146,8 @@ static void ironlake_update_primary_plane(struct drm_crtc *crtc,
> >  
> >  	I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
> >  	I915_WRITE(DSPSURF(plane),
> > -		   i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
> > +		   intel_surf_gtt_offset(fb, 0, rotation) +
> > +		   intel_crtc->dspaddr_offset);
> >  	if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
> >  		I915_WRITE(DSPOFFSET(plane), (y << 16) | x);
> >  	} else {
> > @@ -3000,30 +3174,15 @@ u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv,
> >  	}
> >  }
> >  
> > -unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
> > -				     struct drm_i915_gem_object *obj,
> > -				     unsigned int plane)
> > +unsigned int intel_surf_gtt_offset(struct drm_framebuffer *fb, int plane,
> > +				   unsigned int rotation)
> 
> If you want to frob the interface, then I'd replace the fb and rotation
> with the plane state.

Hmm. Yeah here we should always have a plane state, so could pass that
in. It's a bit funky passing in the plane state and the (color) plane
index. Usually the latter goes with fb or pixel format. But I think I
already have some wm cleanup stuff somewhere that does the same actually,
so might as well do it here.

I'm also thinking we may want to move a bunch of this coordinate
mangling into the check phase of the operation and reduce the commit
to just getting the vma offset and adding it with the page_offset
computed in check(). But I'll leave that for the future.

> 
> >  {
> > -	const struct i915_ggtt_view *view = &i915_ggtt_view_normal;
> > -	struct i915_vma *vma;
> > -	unsigned char *offset;
> > +	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
> > +	struct i915_ggtt_view view;
> >  
> > -	if (intel_rotation_90_or_270(intel_plane->base.state->rotation))
> > -		view = &i915_ggtt_view_rotated;
> > +	intel_fill_fb_ggtt_view(&view, fb, rotation);
> >  
> > -	vma = i915_gem_obj_to_ggtt_view(obj, view);
> > -	if (WARN(!vma, "ggtt vma for display object not found! (view=%u)\n",
> > -		view->type))
> > -		return -1;
> > -
> > -	offset = (unsigned char *)vma->node.start;
> > -
> > -	if (plane == 1) {
> > -		offset += vma->ggtt_view.rotated.uv_start_page *
> > -			  PAGE_SIZE;
> > -	}
> > -
> > -	return (unsigned long)offset;
> > +	return i915_gem_obj_ggtt_offset_view(obj, &view);
> >  }
> >  
> >  static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id)
> > @@ -3142,18 +3301,16 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
> >  	struct drm_i915_private *dev_priv = dev->dev_private;
> >  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> >  	struct drm_plane *plane = crtc->primary;
> > +	struct intel_framebuffer *intel_fb;
> >  	bool visible = to_intel_plane_state(plane->state)->visible;
> > -	struct drm_i915_gem_object *obj;
> >  	int pipe = intel_crtc->pipe;
> >  	u32 plane_ctl, stride_div, stride;
> > -	u32 tile_height, plane_offset, plane_size;
> >  	unsigned int rotation;
> > -	int x_offset, y_offset;
> >  	unsigned long surf_addr;
> >  	struct intel_crtc_state *crtc_state = intel_crtc->config;
> >  	struct intel_plane_state *plane_state;
> > -	int src_x = 0, src_y = 0, src_w = 0, src_h = 0;
> > -	int dst_x = 0, dst_y = 0, dst_w = 0, dst_h = 0;
> > +	int src_w, src_h;
> > +	int dst_x, dst_y, dst_w, dst_h;
> >  	int scaler_id = -1;
> >  	int pixel_size;
> >  
> > @@ -3166,6 +3323,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
> >  		return;
> >  	}
> >  
> > +	intel_fb = to_intel_framebuffer(fb);
> >  	pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> >  
> >  	plane_ctl = PLANE_CTL_ENABLE |
> > @@ -3179,16 +3337,7 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
> >  	rotation = plane->state->rotation;
> >  	plane_ctl |= skl_plane_ctl_rotation(rotation);
> >  
> > -	obj = intel_fb_obj(fb);
> > -	stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> > -					       fb->pixel_format);
> > -	surf_addr = intel_plane_obj_offset(to_intel_plane(plane), obj, 0);
> > -
> > -	WARN_ON(drm_rect_width(&plane_state->src) == 0);
> > -
> >  	scaler_id = plane_state->scaler_id;
> > -	src_x = plane_state->src.x1 >> 16;
> > -	src_y = plane_state->src.y1 >> 16;
> >  	src_w = drm_rect_width(&plane_state->src) >> 16;
> >  	src_h = drm_rect_height(&plane_state->src) >> 16;
> >  	dst_x = plane_state->dst.x1;
> > @@ -3196,31 +3345,48 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
> >  	dst_w = drm_rect_width(&plane_state->dst);
> >  	dst_h = drm_rect_height(&plane_state->dst);
> >  
> > -	WARN_ON(x != src_x || y != src_y);
> > -
> >  	if (intel_rotation_90_or_270(rotation)) {
> > -		/* stride = Surface height in tiles */
> > -		tile_height = intel_tile_height(dev_priv, fb->modifier[0],
> > -						pixel_size);
> > -		stride = DIV_ROUND_UP(fb->height, tile_height);
> > -		x_offset = stride * tile_height - y - src_h;
> > -		y_offset = x;
> > -		plane_size = (src_w - 1) << 16 | (src_h - 1);
> > +		struct drm_rect r = {
> > +			.x1 = x,
> > +			.x2 = x + src_w,
> > +			.y1 = y,
> > +			.y2 = y + src_h,
> > +		};
> > +
> > +		/* Rotate src coordinates to match rotated GTT view */
> > +		drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
> > +
> > +		x = r.x1;
> > +		y = r.y1;
> > +		src_w = drm_rect_width(&r);
> > +		src_h = drm_rect_height(&r);
> > +
> > +		stride_div = intel_tile_height(dev_priv, fb->modifier[0],
> > +					       pixel_size);
> > +		stride = intel_fb->plane[0].rotated.pitch;
> >  	} else {
> > -		stride = fb->pitches[0] / stride_div;
> > -		x_offset = x;
> > -		y_offset = y;
> > -		plane_size = (src_h - 1) << 16 | (src_w - 1);
> > +		stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> > +						       fb->pixel_format);
> > +		stride = fb->pitches[0];
> >  	}
> > -	plane_offset = y_offset << 16 | x_offset;
> >  
> > -	intel_crtc->adjusted_x = x_offset;
> > -	intel_crtc->adjusted_y = y_offset;
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> > +	surf_addr = intel_compute_page_offset(&x, &y, fb, 0,
> > +					      stride, rotation);
> > +
> > +	/* Sizes are 0 based */
> > +	src_w--;
> > +	src_h--;
> > +	dst_w--;
> > +	dst_h--;
> > +
> > +	intel_crtc->adjusted_x = x;
> > +	intel_crtc->adjusted_y = y;
> >  
> >  	I915_WRITE(PLANE_CTL(pipe, 0), plane_ctl);
> > -	I915_WRITE(PLANE_OFFSET(pipe, 0), plane_offset);
> > -	I915_WRITE(PLANE_SIZE(pipe, 0), plane_size);
> > -	I915_WRITE(PLANE_STRIDE(pipe, 0), stride);
> > +	I915_WRITE(PLANE_OFFSET(pipe, 0), (y << 16) | x);
> > +	I915_WRITE(PLANE_STRIDE(pipe, 0), stride / stride_div);
> > +	I915_WRITE(PLANE_SIZE(pipe, 0), (src_h << 16) | src_w);
> >  
> >  	if (scaler_id >= 0) {
> >  		uint32_t ps_ctrl = 0;
> > @@ -3237,7 +3403,8 @@ static void skylake_update_primary_plane(struct drm_crtc *crtc,
> >  		I915_WRITE(PLANE_POS(pipe, 0), (dst_y << 16) | dst_x);
> >  	}
> >  
> > -	I915_WRITE(PLANE_SURF(pipe, 0), surf_addr);
> > +	I915_WRITE(PLANE_SURF(pipe, 0),
> > +		   intel_surf_gtt_offset(fb, 0, rotation) + surf_addr);
> >  
> >  	POSTING_READ(PLANE_SURF(pipe, 0));
> >  }
> > @@ -11521,8 +11688,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
> >  	if (ret)
> >  		goto cleanup_pending;
> >  
> > -	work->gtt_offset = intel_plane_obj_offset(to_intel_plane(primary),
> > -						  obj, 0);
> > +	work->gtt_offset = intel_surf_gtt_offset(fb, 0, primary->state->rotation);
> >  	work->gtt_offset += intel_crtc->dspaddr_offset;
> >  
> >  	if (mmio_flip) {
> > @@ -14319,7 +14485,6 @@ static int intel_framebuffer_init(struct drm_device *dev,
> >  				  struct drm_i915_gem_object *obj)
> >  {
> >  	struct drm_i915_private *dev_priv = to_i915(dev);
> > -	unsigned int aligned_height;
> >  	int ret;
> >  	u32 pitch_limit, stride_alignment;
> >  
> > @@ -14443,16 +14608,13 @@ static int intel_framebuffer_init(struct drm_device *dev,
> >  	if (ret)
> >  		return ret;
> >  
> > -	aligned_height = intel_fb_align_height(dev, mode_cmd->height,
> > -					       mode_cmd->pixel_format,
> > -					       mode_cmd->modifier[0]);
> > -	/* FIXME drm helper for size checks (especially planar formats)? */
> > -	if (obj->base.size < aligned_height * mode_cmd->pitches[0])
> > -		return -EINVAL;
> > -
> >  	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
> >  	intel_fb->obj = obj;
> >  
> > +	ret = intel_fill_fb_info(dev_priv, &intel_fb->base);
> > +	if (ret)
> > +		return ret;
> > +
> >  	ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
> >  	if (ret) {
> >  		DRM_ERROR("framebuffer init failed %d\n", ret);
> > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> > index 36d049d..395afd3 100644
> > --- a/drivers/gpu/drm/i915/intel_drv.h
> > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > @@ -118,6 +118,19 @@ enum intel_output_type {
> >  struct intel_framebuffer {
> >  	struct drm_framebuffer base;
> >  	struct drm_i915_gem_object *obj;
> > +	struct intel_rotation_info info;
> > +
> > +	struct {
> > +		struct {
> > +			/* pixels */
> > +			unsigned int x, y;
> > +		} normal;
> > +		struct {
> > +			/* pixels */
> > +			unsigned int x, y;
> > +			unsigned int pitch;
> > +		} rotated;
> > +	} plane[2];
> >  };
> >  
> >  struct intel_fbdev {
> > @@ -1028,6 +1041,12 @@ void i915_audio_component_init(struct drm_i915_private *dev_priv);
> >  void i915_audio_component_cleanup(struct drm_i915_private *dev_priv);
> >  
> >  /* intel_display.c */
> > +unsigned int intel_rotation_info_size(const struct intel_rotation_info *info);
> > +unsigned int intel_fb_xy_to_linear(int x, int y,
> > +				   const struct drm_framebuffer *fb, int plane);
> > +void intel_add_fb_offsets(int *x, int *y,
> > +			  const struct drm_framebuffer *fb, int plane,
> > +			  unsigned int rotation);
> >  extern const struct drm_plane_funcs intel_plane_funcs;
> >  bool intel_has_pending_fb_unpin(struct drm_device *dev);
> >  int intel_pch_rawclk(struct drm_device *dev);
> > @@ -1134,10 +1153,10 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv,
> >  void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state);
> >  #define assert_pipe_enabled(d, p) assert_pipe(d, p, true)
> >  #define assert_pipe_disabled(d, p) assert_pipe(d, p, false)
> > -unsigned long intel_compute_page_offset(int *x, int *y,
> > -					const struct drm_framebuffer *fb, int plane,
> > -					unsigned int pitch,
> > -					unsigned int rotation);
> > +unsigned int intel_compute_page_offset(int *x, int *y,
> > +				       const struct drm_framebuffer *fb, int plane,
> > +				       unsigned int pitch,
> > +				       unsigned int rotation);
> >  void intel_prepare_reset(struct drm_device *dev);
> >  void intel_finish_reset(struct drm_device *dev);
> >  void hsw_enable_pc8(struct drm_i915_private *dev_priv);
> > @@ -1174,9 +1193,8 @@ void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file);
> >  int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
> >  int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
> >  
> > -unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
> > -				     struct drm_i915_gem_object *obj,
> > -				     unsigned int plane);
> > +unsigned int intel_surf_gtt_offset(struct drm_framebuffer *fb, int plane,
> > +				   unsigned int rotation);
> >  
> >  u32 skl_plane_ctl_format(uint32_t pixel_format);
> >  u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
> > diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> > index 828c3eb..d9d37b0 100644
> > --- a/drivers/gpu/drm/i915/intel_sprite.c
> > +++ b/drivers/gpu/drm/i915/intel_sprite.c
> > @@ -188,17 +188,15 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
> >  	struct drm_device *dev = drm_plane->dev;
> >  	struct drm_i915_private *dev_priv = dev->dev_private;
> >  	struct intel_plane *intel_plane = to_intel_plane(drm_plane);
> > -	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
> > +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
> >  	const int pipe = intel_plane->pipe;
> >  	const int plane = intel_plane->plane + 1;
> >  	u32 plane_ctl, stride_div, stride;
> >  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> >  	const struct drm_intel_sprite_colorkey *key =
> >  		&to_intel_plane_state(drm_plane->state)->ckey;
> > -	unsigned long surf_addr;
> > -	u32 tile_height, plane_offset, plane_size;
> > +	unsigned int surf_addr;
> >  	unsigned int rotation;
> > -	int x_offset, y_offset;
> >  	struct intel_crtc_state *crtc_state = to_intel_crtc(crtc)->config;
> >  	int scaler_id;
> >  
> > @@ -215,17 +213,8 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
> >  				       pixel_size, true,
> >  				       src_w != crtc_w || src_h != crtc_h);
> >  
> > -	stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> > -					       fb->pixel_format);
> > -
> >  	scaler_id = to_intel_plane_state(drm_plane->state)->scaler_id;
> >  
> > -	/* Sizes are 0 based */
> > -	src_w--;
> > -	src_h--;
> > -	crtc_w--;
> > -	crtc_h--;
> > -
> >  	if (key->flags) {
> >  		I915_WRITE(PLANE_KEYVAL(pipe, plane), key->min_value);
> >  		I915_WRITE(PLANE_KEYMAX(pipe, plane), key->max_value);
> > @@ -237,27 +226,43 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
> >  	else if (key->flags & I915_SET_COLORKEY_SOURCE)
> >  		plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE;
> >  
> > -	surf_addr = intel_plane_obj_offset(intel_plane, obj, 0);
> > -
> >  	if (intel_rotation_90_or_270(rotation)) {
> > -		/* stride: Surface height in tiles */
> > -		tile_height = intel_tile_height(dev_priv, fb->modifier[0],
> > -						pixel_size);
> > -		stride = DIV_ROUND_UP(fb->height, tile_height);
> > -		plane_size = (src_w << 16) | src_h;
> > -		x_offset = stride * tile_height - y - (src_h + 1);
> > -		y_offset = x;
> > +		struct drm_rect r = {
> > +			.x1 = x,
> > +			.x2 = x + src_w,
> > +			.y1 = y,
> > +			.y2 = y + src_h,
> > +		};
> > +
> > +		/* Rotate src coordinates to match rotated GTT view */
> > +		drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
> > +
> > +		x = r.x1;
> > +		y = r.y1;
> > +		src_w = drm_rect_width(&r);
> > +		src_h = drm_rect_height(&r);
> > +
> > +		stride_div = intel_tile_height(dev_priv, fb->modifier[0], pixel_size);
> > +		stride = intel_fb->plane[0].rotated.pitch;
> >  	} else {
> > -		stride = fb->pitches[0] / stride_div;
> > -		plane_size = (src_h << 16) | src_w;
> > -		x_offset = x;
> > -		y_offset = y;
> > +		stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
> > +						       fb->pixel_format);
> > +		stride = fb->pitches[0];
> >  	}
> > -	plane_offset = y_offset << 16 | x_offset;
> >  
> > -	I915_WRITE(PLANE_OFFSET(pipe, plane), plane_offset);
> > -	I915_WRITE(PLANE_STRIDE(pipe, plane), stride);
> > -	I915_WRITE(PLANE_SIZE(pipe, plane), plane_size);
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> > +	surf_addr = intel_compute_page_offset(&x, &y, fb, 0,
> > +					      stride, rotation);
> > +
> > +	/* Sizes are 0 based */
> > +	src_w--;
> > +	src_h--;
> > +	crtc_w--;
> > +	crtc_h--;
> > +
> > +	I915_WRITE(PLANE_OFFSET(pipe, plane), (y << 16) | x);
> > +	I915_WRITE(PLANE_STRIDE(pipe, plane), stride / stride_div);
> > +	I915_WRITE(PLANE_SIZE(pipe, plane), (src_h << 16) | src_w);
> >  
> >  	/* program plane scaler */
> >  	if (scaler_id >= 0) {
> > @@ -279,7 +284,8 @@ skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
> >  	}
> >  
> >  	I915_WRITE(PLANE_CTL(pipe, plane), plane_ctl);
> > -	I915_WRITE(PLANE_SURF(pipe, plane), surf_addr);
> > +	I915_WRITE(PLANE_SURF(pipe, plane),
> > +		   intel_surf_gtt_offset(fb, 0, rotation) + surf_addr);
> >  	POSTING_READ(PLANE_SURF(pipe, plane));
> >  }
> >  
> > @@ -355,8 +361,7 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
> >  	int plane = intel_plane->plane;
> >  	u32 sprctl;
> >  	unsigned int rotation = dplane->state->rotation;
> > -	unsigned long sprsurf_offset, linear_offset;
> > -	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> > +	unsigned int sprsurf_offset, linear_offset;
> >  	const struct drm_intel_sprite_colorkey *key =
> >  		&to_intel_plane_state(dplane->state)->ckey;
> >  
> > @@ -420,19 +425,19 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
> >  	crtc_w--;
> >  	crtc_h--;
> >  
> > -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> >  	sprsurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
> >  						   fb->pitches[0], rotation);
> > -	linear_offset -= sprsurf_offset;
> >  
> >  	if (rotation == BIT(DRM_ROTATE_180)) {
> >  		sprctl |= SP_ROTATE_180;
> >  
> >  		x += src_w;
> >  		y += src_h;
> > -		linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
> >  	}
> >  
> > +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> > +
> >  	if (key->flags) {
> >  		I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value);
> >  		I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value);
> > @@ -457,8 +462,8 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
> >  
> >  	I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);
> >  	I915_WRITE(SPCNTR(pipe, plane), sprctl);
> > -	I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
> > -		   sprsurf_offset);
> > +	I915_WRITE(SPSURF(pipe, plane),
> > +		   intel_surf_gtt_offset(fb, 0, rotation) + sprsurf_offset);
> >  	POSTING_READ(SPSURF(pipe, plane));
> >  }
> >  
> > @@ -492,7 +497,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  	enum pipe pipe = intel_plane->pipe;
> >  	u32 sprctl, sprscale = 0;
> >  	unsigned int rotation = plane->state->rotation;
> > -	unsigned long sprsurf_offset, linear_offset;
> > +	unsigned int sprsurf_offset, linear_offset;
> >  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> >  	const struct drm_intel_sprite_colorkey *key =
> >  		&to_intel_plane_state(plane->state)->ckey;
> > @@ -552,10 +557,9 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  	if (crtc_w != src_w || crtc_h != src_h)
> >  		sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
> >  
> > -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> >  	sprsurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
> >  						   fb->pitches[0], rotation);
> > -	linear_offset -= sprsurf_offset;
> >  
> >  	if (rotation == BIT(DRM_ROTATE_180)) {
> >  		sprctl |= SPRITE_ROTATE_180;
> > @@ -564,11 +568,11 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  		if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
> >  			x += src_w;
> >  			y += src_h;
> > -			linear_offset += src_h * fb->pitches[0] +
> > -				src_w * pixel_size;
> >  		}
> >  	}
> >  
> > +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> > +
> >  	if (key->flags) {
> >  		I915_WRITE(SPRKEYVAL(pipe), key->min_value);
> >  		I915_WRITE(SPRKEYMAX(pipe), key->max_value);
> > @@ -597,7 +601,7 @@ ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  		I915_WRITE(SPRSCALE(pipe), sprscale);
> >  	I915_WRITE(SPRCTL(pipe), sprctl);
> >  	I915_WRITE(SPRSURF(pipe),
> > -		   i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
> > +		   intel_surf_gtt_offset(fb, 0, rotation) + sprsurf_offset);
> >  	POSTING_READ(SPRSURF(pipe));
> >  }
> >  
> > @@ -632,7 +636,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
> >  	int pipe = intel_plane->pipe;
> >  	unsigned int rotation = plane->state->rotation;
> > -	unsigned long dvssurf_offset, linear_offset;
> > +	unsigned int dvssurf_offset, linear_offset;
> >  	u32 dvscntr, dvsscale;
> >  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
> >  	const struct drm_intel_sprite_colorkey *key =
> > @@ -689,19 +693,19 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  	if (crtc_w != src_w || crtc_h != src_h)
> >  		dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
> >  
> > -	linear_offset = y * fb->pitches[0] + x * pixel_size;
> > +	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
> >  	dvssurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
> >  						   fb->pitches[0], rotation);
> > -	linear_offset -= dvssurf_offset;
> >  
> >  	if (rotation == BIT(DRM_ROTATE_180)) {
> >  		dvscntr |= DVS_ROTATE_180;
> >  
> >  		x += src_w;
> >  		y += src_h;
> > -		linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
> >  	}
> >  
> > +	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
> > +
> >  	if (key->flags) {
> >  		I915_WRITE(DVSKEYVAL(pipe), key->min_value);
> >  		I915_WRITE(DVSKEYMAX(pipe), key->max_value);
> > @@ -725,7 +729,7 @@ ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
> >  	I915_WRITE(DVSSCALE(pipe), dvsscale);
> >  	I915_WRITE(DVSCNTR(pipe), dvscntr);
> >  	I915_WRITE(DVSSURF(pipe),
> > -		   i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
> > +		   intel_surf_gtt_offset(fb, 0, rotation) + dvssurf_offset);
> >  	POSTING_READ(DVSSURF(pipe));
> >  }
> >  
> > -- 
> > 2.4.9
> > 
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index bb95b86..98aee6c 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -3255,11 +3255,6 @@  rotate_pages(const dma_addr_t *in, unsigned int offset,
 	unsigned int column, row;
 	unsigned int src_idx;
 
-	if (!sg) {
-		st->nents = 0;
-		sg = st->sgl;
-	}
-
 	for (column = 0; column < width; column++) {
 		src_idx = stride * (height - 1) + column;
 		for (row = 0; row < height; row++) {
@@ -3280,16 +3275,14 @@  rotate_pages(const dma_addr_t *in, unsigned int offset,
 }
 
 static struct sg_table *
-intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
+intel_rotate_fb_obj_pages(const struct intel_rotation_info *info,
 			  struct drm_i915_gem_object *obj)
 {
-	unsigned int size_pages = rot_info->size >> PAGE_SHIFT;
-	unsigned int size_pages_uv;
+	unsigned int size = intel_rotation_info_size(info);
 	struct sg_page_iter sg_iter;
 	unsigned long i;
 	dma_addr_t *page_addr_list;
 	struct sg_table *st;
-	unsigned int uv_start_page;
 	struct scatterlist *sg;
 	int ret = -ENOMEM;
 
@@ -3299,57 +3292,32 @@  intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
 	if (!page_addr_list)
 		return ERR_PTR(ret);
 
-	/* Account for UV plane with NV12. */
-	if (rot_info->pixel_format == DRM_FORMAT_NV12)
-		size_pages_uv = rot_info->size_uv >> PAGE_SHIFT;
-	else
-		size_pages_uv = 0;
-
 	/* Allocate target SG list. */
 	st = kmalloc(sizeof(*st), GFP_KERNEL);
 	if (!st)
 		goto err_st_alloc;
 
-	ret = sg_alloc_table(st, size_pages + size_pages_uv, GFP_KERNEL);
+	ret = sg_alloc_table(st, size, GFP_KERNEL);
 	if (ret)
 		goto err_sg_alloc;
 
 	/* Populate source page list from the object. */
 	i = 0;
 	for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, 0) {
-		page_addr_list[i] = sg_page_iter_dma_address(&sg_iter);
-		i++;
+		page_addr_list[i++] = sg_page_iter_dma_address(&sg_iter);
 	}
 
-	/* Rotate the pages. */
-	sg = rotate_pages(page_addr_list, 0,
-		     rot_info->width_pages, rot_info->height_pages,
-		     rot_info->width_pages,
-		     st, NULL);
+	st->nents = 0;
+	sg = st->sgl;
 
-	/* Append the UV plane if NV12. */
-	if (rot_info->pixel_format == DRM_FORMAT_NV12) {
-		uv_start_page = size_pages;
-
-		/* Check for tile-row un-alignment. */
-		if (offset_in_page(rot_info->uv_offset))
-			uv_start_page--;
-
-		rot_info->uv_start_page = uv_start_page;
-
-		rotate_pages(page_addr_list, uv_start_page,
-			     rot_info->width_pages_uv,
-			     rot_info->height_pages_uv,
-			     rot_info->width_pages_uv,
-			     st, sg);
+	for (i = 0 ; i < ARRAY_SIZE(info->plane); i++) {
+		sg = rotate_pages(page_addr_list, info->plane[i].offset,
+				  info->plane[i].width, info->plane[i].height,
+				  info->plane[i].stride, st, sg);
 	}
 
-	DRM_DEBUG_KMS(
-		      "Created rotated page mapping for object size %zu (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %u pages (%u plane 0)).\n",
-		      obj->base.size, rot_info->pitch, rot_info->height,
-		      rot_info->pixel_format, rot_info->width_pages,
-		      rot_info->height_pages, size_pages + size_pages_uv,
-		      size_pages);
+	DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages)\n",
+		      obj->base.size, info->plane[0].width, info->plane[0].height, size);
 
 	drm_free_large(page_addr_list);
 
@@ -3360,12 +3328,8 @@  err_sg_alloc:
 err_st_alloc:
 	drm_free_large(page_addr_list);
 
-	DRM_DEBUG_KMS(
-		      "Failed to create rotated mapping for object size %zu! (%d) (pitch=%u, height=%u, pixel_format=0x%x, %ux%u tiles, %u pages (%u plane 0))\n",
-		      obj->base.size, ret, rot_info->pitch, rot_info->height,
-		      rot_info->pixel_format, rot_info->width_pages,
-		      rot_info->height_pages, size_pages + size_pages_uv,
-		      size_pages);
+	DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n",
+		      obj->base.size, info->plane[0].width, info->plane[0].height, size);
 	return ERR_PTR(ret);
 }
 
@@ -3516,7 +3480,7 @@  i915_ggtt_view_size(struct drm_i915_gem_object *obj,
 	if (view->type == I915_GGTT_VIEW_NORMAL) {
 		return obj->base.size;
 	} else if (view->type == I915_GGTT_VIEW_ROTATED) {
-		return view->rotated.size;
+		return intel_rotation_info_size(&view->rotated) << PAGE_SHIFT;
 	} else if (view->type == I915_GGTT_VIEW_PARTIAL) {
 		return view->partial.size << PAGE_SHIFT;
 	} else {
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h
index 68de734..ea28f7d 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.h
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.h
@@ -136,16 +136,10 @@  enum i915_ggtt_view_type {
 };
 
 struct intel_rotation_info {
-	unsigned int height;
-	unsigned int pitch;
-	unsigned int uv_offset;
-	uint32_t pixel_format;
-	uint64_t fb_modifier;
-	unsigned int width_pages, height_pages;
-	uint64_t size;
-	unsigned int width_pages_uv, height_pages_uv;
-	uint64_t size_uv;
-	unsigned int uv_start_page;
+	struct {
+		/* tiles */
+		unsigned int width, height, stride, offset;
+	} plane[2];
 };
 
 struct i915_ggtt_view {
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 028dc4a..0dcb710 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2274,49 +2274,28 @@  intel_fb_align_height(struct drm_device *dev, unsigned int height,
 	return ALIGN(height, tile_height);
 }
 
-static int
+unsigned int intel_rotation_info_size(const struct intel_rotation_info *info)
+{
+	unsigned int size = 0;
+	int i;
+
+	for (i = 0 ; i < ARRAY_SIZE(info->plane); i++)
+		size += info->plane[i].width * info->plane[i].height;
+
+	return size;
+}
+
+static void
 intel_fill_fb_ggtt_view(struct i915_ggtt_view *view,
 			const struct drm_framebuffer *fb,
 			unsigned int rotation)
 {
-	struct drm_i915_private *dev_priv = to_i915(fb->dev);
-	struct intel_rotation_info *info = &view->rotated;
-	unsigned int tile_size, tile_width, tile_height, cpp;
-
-	*view = i915_ggtt_view_normal;
-
-	if (!intel_rotation_90_or_270(rotation))
-		return 0;
-
-	*view = i915_ggtt_view_rotated;
-
-	info->height = fb->height;
-	info->pixel_format = fb->pixel_format;
-	info->pitch = fb->pitches[0];
-	info->uv_offset = fb->offsets[1];
-	info->fb_modifier = fb->modifier[0];
-
-	tile_size = intel_tile_size(dev_priv);
-
-	cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-	tile_width = intel_tile_width(dev_priv, cpp, fb->modifier[0]);
-	tile_height = tile_size / tile_width;
-
-	info->width_pages = DIV_ROUND_UP(fb->pitches[0], tile_width);
-	info->height_pages = DIV_ROUND_UP(fb->height, tile_height);
-	info->size = info->width_pages * info->height_pages * tile_size;
-
-	if (info->pixel_format == DRM_FORMAT_NV12) {
-		cpp = drm_format_plane_cpp(fb->pixel_format, 1);
-		tile_width = intel_tile_width(dev_priv, fb->modifier[1], cpp);
-		tile_height = tile_size / tile_width;
-
-		info->width_pages_uv = DIV_ROUND_UP(fb->pitches[1], tile_width);
-		info->height_pages_uv = DIV_ROUND_UP(fb->height / 2, tile_height);
-		info->size_uv = info->width_pages_uv * info->height_pages_uv * tile_size;
+	if (intel_rotation_90_or_270(rotation)) {
+		*view = i915_ggtt_view_rotated;
+		view->rotated = to_intel_framebuffer(fb)->info;
+	} else {
+		*view = i915_ggtt_view_normal;
 	}
-
-	return 0;
 }
 
 static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv)
@@ -2368,9 +2347,7 @@  intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
 
 	alignment = intel_surf_alignment(dev_priv, fb->modifier[0]);
 
-	ret = intel_fill_fb_ggtt_view(&view, fb, rotation);
-	if (ret)
-		return ret;
+	intel_fill_fb_ggtt_view(&view, fb, rotation);
 
 	/* Note that the w/a also requires 64 PTE of padding following the
 	 * bo. We currently fill all unused PTE with the shadow page and so
@@ -2433,18 +2410,31 @@  static void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation
 {
 	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
 	struct i915_ggtt_view view;
-	int ret;
 
 	WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex));
 
-	ret = intel_fill_fb_ggtt_view(&view, fb, rotation);
-	WARN_ONCE(ret, "Couldn't get view from plane state!");
+	intel_fill_fb_ggtt_view(&view, fb, rotation);
 
 	i915_gem_object_unpin_fence(obj);
 	i915_gem_object_unpin_from_display_plane(obj, &view);
 }
 
 /*
+ * Convert the x/y offsets into a linear offset.
+ * Only valid with 0/180 degree rotation, which is fine since linear
+ * offset is only used with linear buffers on pre-hsw and tiled buffers
+ * with gen2/3, and 90/270 degree rotations isn't supported on any of them.
+ */
+unsigned int intel_fb_xy_to_linear(int x, int y,
+				   const struct drm_framebuffer *fb, int plane)
+{
+	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+	unsigned int pitch = fb->pitches[plane];
+
+	return y * pitch + x * cpp;
+}
+
+/*
  * Return the tile dimensions in pixel units matching
  * the specified rotation angle.
  */
@@ -2475,6 +2465,55 @@  static void intel_rotate_tile_dims(unsigned int *tile_width,
 }
 
 /*
+ * Add the x/y offsets derived from fb->offsets[] to the user
+ * specified plane src x/y offsets. The resulting x/y offsets
+ * specify the start of scanout from the beginning of the gtt mapping.
+ */
+void intel_add_fb_offsets(int *x, int *y,
+			  const struct drm_framebuffer *fb, int plane,
+			  unsigned int rotation)
+
+{
+	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
+	const struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+	unsigned int pitch;
+
+	if (intel_rotation_90_or_270(rotation)) {
+		pitch = intel_fb->plane[plane].rotated.pitch;
+
+		*x += intel_fb->plane[plane].rotated.x;
+		*y += intel_fb->plane[plane].rotated.y;
+	} else {
+		pitch = fb->pitches[plane];
+
+		*x += intel_fb->plane[plane].normal.x;
+		*y += intel_fb->plane[plane].normal.y;
+	}
+
+	/* minimize x */
+	if (fb->modifier[plane] != DRM_FORMAT_MOD_NONE) {
+		unsigned int tile_size, tile_width, tile_height;
+
+		tile_size = intel_tile_size(dev_priv);
+		tile_width = intel_tile_width(dev_priv, fb->modifier[plane], cpp);
+		tile_height = tile_size / tile_width;
+
+		intel_rotate_tile_dims(&tile_width, &tile_height,
+				       &pitch, cpp, rotation);
+
+		*y += *x / pitch * tile_height;
+		*x  = *x % pitch;
+	} else {
+		/* in pixels */
+		pitch /= cpp;
+
+		*y += *x / pitch;
+		*x  = *x % pitch;
+	}
+}
+
+/*
  * Adjust the page offset by moving the difference into
  * the x/y offsets.
  *
@@ -2514,20 +2553,23 @@  static void intel_adjust_page_offset(int *x, int *y,
  * In the 90/270 rotated case, x and y are assumed
  * to be already rotated to match the rotated GTT view, and
  * pitch is the tile_height aligned framebuffer height.
+ *
+ * This function is used when computing the derived information
+ * under intel_framebuffer, so using any of that information
+ * here is not allowed. Anything under drm_framebuffer can be
+ * used. This is why the user has to pass in the pitch since it
+ * is specified in the rotated orientation.
  */
-unsigned long intel_compute_page_offset(int *x, int *y,
-					const struct drm_framebuffer *fb, int plane,
-					unsigned int pitch,
-					unsigned int rotation)
+static unsigned int _intel_compute_page_offset(const struct drm_i915_private *dev_priv,
+					       int *x, int *y,
+					       const struct drm_framebuffer *fb, int plane,
+					       unsigned int pitch,
+					       unsigned int rotation,
+					       unsigned int alignment)
 {
-	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
 	uint64_t fb_modifier = fb->modifier[plane];
 	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
-	unsigned int offset, alignment;
-
-	alignment = intel_surf_alignment(dev_priv, fb_modifier);
-	if (alignment)
-		alignment--;
+	unsigned int offset;
 
 	if (fb_modifier != DRM_FORMAT_MOD_NONE) {
 		unsigned int tile_size, tile_width, tile_height;
@@ -2560,6 +2602,145 @@  unsigned long intel_compute_page_offset(int *x, int *y,
 	return offset & ~alignment;
 }
 
+unsigned int intel_compute_page_offset(int *x, int *y,
+				       const struct drm_framebuffer *fb, int plane,
+				       unsigned int pitch,
+				       unsigned int rotation)
+{
+	const struct drm_i915_private *dev_priv = to_i915(fb->dev);
+	unsigned int alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]);
+
+	return _intel_compute_page_offset(dev_priv, x, y, fb, plane, pitch,
+					  rotation, alignment ? (alignment - 1) : 0);
+}
+
+/* Convert the fb->offset[] linear offset into x/y offsets */
+static void intel_fb_offset_to_xy(int *x, int *y,
+				  const struct drm_framebuffer *fb, int plane)
+{
+	unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+	unsigned int pitch = fb->pitches[plane];
+	unsigned int linear_offset = fb->offsets[plane];
+
+	*y = linear_offset / pitch;
+	*x = linear_offset % pitch / cpp;
+}
+
+static int
+intel_fill_fb_info(struct drm_i915_private *dev_priv,
+		   struct drm_framebuffer *fb)
+{
+	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+	struct intel_rotation_info *info = &intel_fb->info;
+	unsigned int tile_size;
+	unsigned int gtt_offset_rotated = 0;
+	unsigned int max_size = 0;
+	uint32_t format = fb->pixel_format;
+	int i, num_planes = drm_format_num_planes(format);
+
+	tile_size = intel_tile_size(dev_priv);
+
+	for (i = 0; i < num_planes; i++) {
+		unsigned int width, height;
+		unsigned int cpp, offset, size;
+		int x, y;
+
+		cpp = drm_format_plane_cpp(format, i);
+		width = drm_format_plane_width(fb->width, format, i);
+		height = drm_format_plane_height(fb->height, format, i);
+
+		intel_fb_offset_to_xy(&x, &y, fb, i);
+
+		/*
+		 * First pixel of the framebuffer from
+		 * the start of the normal gtt mapping.
+		 */
+		intel_fb->plane[i].normal.x = x;
+		intel_fb->plane[i].normal.y = y;
+
+		offset = _intel_compute_page_offset(dev_priv, &x, &y,
+						    fb, 0, fb->pitches[i],
+						    BIT(DRM_ROTATE_0),
+						    tile_size);
+		offset /= tile_size;
+		DRM_DEBUG("page offset %u pages\n", offset);
+
+		if (fb->modifier[i] != DRM_FORMAT_MOD_NONE) {
+			unsigned int tile_width, tile_height;
+			unsigned int pitch;
+			struct drm_rect r;
+
+			tile_width = intel_tile_width(dev_priv, fb->modifier[i], cpp);
+			tile_height = tile_size / tile_width;
+
+			info->plane[i].offset = offset;
+			info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i], tile_width);
+			info->plane[i].width = DIV_ROUND_UP((x + width) * cpp, tile_width);
+			info->plane[i].height = DIV_ROUND_UP(y + height, tile_height);
+
+
+			intel_fb->plane[i].rotated.pitch =
+				info->plane[i].height * tile_height;
+
+			/* how many tiles does this plane need */
+			size = info->plane[i].stride * info->plane[i].height;
+			/*
+			 * If the plane isn't horizontally tile aligned,
+			 * we need one more tile.
+			 */
+			if (x != 0)
+				size++;
+
+			pitch = intel_fb->plane[i].rotated.pitch;
+
+			/* rotate the x/y offsets to match the GTT view */
+			r.x1 = x;
+			r.y1 = y;
+			r.x2 = x + width;
+			r.y2 = y + height;
+			drm_rect_rotate(&r, width, pitch, BIT(DRM_ROTATE_270));
+			x = r.x1;
+			y = r.y1;
+
+			intel_rotate_tile_dims(&tile_width, &tile_height, &pitch,
+					       cpp, BIT(DRM_ROTATE_270));
+
+			/*
+			 * We only keep the x/y offsets, so push all of the
+			 * gtt offset into the x/y offsets.
+			 */
+			intel_adjust_page_offset(&x, &y, tile_width, tile_height,
+						 tile_size, pitch,
+						 gtt_offset_rotated * tile_size, 0);
+
+			gtt_offset_rotated += info->plane[i].width * info->plane[i].height;
+
+			/*
+			 * First pixel of the framebuffer from
+			 * the start of the rotated gtt mapping.
+			 */
+			intel_fb->plane[i].rotated.x = x;
+			intel_fb->plane[i].rotated.y = y;
+		} else {
+			size = DIV_ROUND_UP((y + height) * fb->pitches[i] +
+					    x * cpp, tile_size);
+		}
+		DRM_DEBUG("%d offset %u, size %u, stride %u, height %u\n",
+			  i, offset, size, info->plane[i].stride, info->plane[i].height);
+
+		/* how many tiles in total needed in the bo */
+		max_size = max(max_size, offset + size);
+	}
+
+	if (max_size * tile_size > to_intel_framebuffer(fb)->obj->base.size) {
+		DRM_DEBUG("fb too big for bo (need %u bytes, have %zu bytes)\n",
+			  max_size * tile_size, to_intel_framebuffer(fb)->obj->base.size);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int i9xx_format_to_fourcc(int format)
 {
 	switch (format) {
@@ -2761,7 +2942,7 @@  static void i9xx_update_primary_plane(struct drm_crtc *crtc,
 	struct drm_i915_gem_object *obj;
 	int plane = intel_crtc->plane;
 	unsigned int rotation;
-	unsigned long linear_offset;
+	unsigned int linear_offset;
 	u32 dspcntr;
 	u32 reg = DSPCNTR(plane);
 	int pixel_size;
@@ -2839,30 +3020,25 @@  static void i9xx_update_primary_plane(struct drm_crtc *crtc,
 	if (IS_G4X(dev))
 		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
-	linear_offset = y * fb->pitches[0] + x * pixel_size;
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
 
-	if (INTEL_INFO(dev)->gen >= 4) {
+	if (INTEL_INFO(dev)->gen >= 4)
 		intel_crtc->dspaddr_offset =
 			intel_compute_page_offset(&x, &y, fb, 0,
 						  fb->pitches[0], rotation);
-		linear_offset -= intel_crtc->dspaddr_offset;
-	} else {
-		intel_crtc->dspaddr_offset = linear_offset;
-	}
 
 	if (crtc->primary->state->rotation == BIT(DRM_ROTATE_180)) {
 		dspcntr |= DISPPLANE_ROTATE_180;
 
 		x += (intel_crtc->config->pipe_src_w - 1);
 		y += (intel_crtc->config->pipe_src_h - 1);
-
-		/* Finding the last pixel of the last line of the display
-		data and adding to linear_offset*/
-		linear_offset +=
-			(intel_crtc->config->pipe_src_h - 1) * fb->pitches[0] +
-			(intel_crtc->config->pipe_src_w - 1) * pixel_size;
 	}
 
+	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
+
+	if (INTEL_INFO(dev)->gen < 4)
+		intel_crtc->dspaddr_offset = linear_offset;
+
 	intel_crtc->adjusted_x = x;
 	intel_crtc->adjusted_y = y;
 
@@ -2871,7 +3047,8 @@  static void i9xx_update_primary_plane(struct drm_crtc *crtc,
 	I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
 	if (INTEL_INFO(dev)->gen >= 4) {
 		I915_WRITE(DSPSURF(plane),
-			   i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+			   intel_surf_gtt_offset(fb, 0, rotation) +
+			   intel_crtc->dspaddr_offset);
 		I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
 		I915_WRITE(DSPLINOFF(plane), linear_offset);
 	} else
@@ -2891,7 +3068,7 @@  static void ironlake_update_primary_plane(struct drm_crtc *crtc,
 	struct drm_i915_gem_object *obj;
 	int plane = intel_crtc->plane;
 	unsigned int rotation;
-	unsigned long linear_offset;
+	unsigned int linear_offset;
 	u32 dspcntr;
 	u32 reg = DSPCNTR(plane);
 	int pixel_size;
@@ -2946,26 +3123,22 @@  static void ironlake_update_primary_plane(struct drm_crtc *crtc,
 	if (!IS_HASWELL(dev) && !IS_BROADWELL(dev))
 		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
-	linear_offset = y * fb->pitches[0] + x * pixel_size;
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
 	intel_crtc->dspaddr_offset =
 		intel_compute_page_offset(&x, &y, fb, 0,
 					  fb->pitches[0], rotation);
-	linear_offset -= intel_crtc->dspaddr_offset;
+
 	if (rotation == BIT(DRM_ROTATE_180)) {
 		dspcntr |= DISPPLANE_ROTATE_180;
 
 		if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
 			x += (intel_crtc->config->pipe_src_w - 1);
 			y += (intel_crtc->config->pipe_src_h - 1);
-
-			/* Finding the last pixel of the last line of the display
-			data and adding to linear_offset*/
-			linear_offset +=
-				(intel_crtc->config->pipe_src_h - 1) * fb->pitches[0] +
-				(intel_crtc->config->pipe_src_w - 1) * pixel_size;
 		}
 	}
 
+	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
+
 	intel_crtc->adjusted_x = x;
 	intel_crtc->adjusted_y = y;
 
@@ -2973,7 +3146,8 @@  static void ironlake_update_primary_plane(struct drm_crtc *crtc,
 
 	I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
 	I915_WRITE(DSPSURF(plane),
-		   i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+		   intel_surf_gtt_offset(fb, 0, rotation) +
+		   intel_crtc->dspaddr_offset);
 	if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
 		I915_WRITE(DSPOFFSET(plane), (y << 16) | x);
 	} else {
@@ -3000,30 +3174,15 @@  u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv,
 	}
 }
 
-unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
-				     struct drm_i915_gem_object *obj,
-				     unsigned int plane)
+unsigned int intel_surf_gtt_offset(struct drm_framebuffer *fb, int plane,
+				   unsigned int rotation)
 {
-	const struct i915_ggtt_view *view = &i915_ggtt_view_normal;
-	struct i915_vma *vma;
-	unsigned char *offset;
+	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+	struct i915_ggtt_view view;
 
-	if (intel_rotation_90_or_270(intel_plane->base.state->rotation))
-		view = &i915_ggtt_view_rotated;
+	intel_fill_fb_ggtt_view(&view, fb, rotation);
 
-	vma = i915_gem_obj_to_ggtt_view(obj, view);
-	if (WARN(!vma, "ggtt vma for display object not found! (view=%u)\n",
-		view->type))
-		return -1;
-
-	offset = (unsigned char *)vma->node.start;
-
-	if (plane == 1) {
-		offset += vma->ggtt_view.rotated.uv_start_page *
-			  PAGE_SIZE;
-	}
-
-	return (unsigned long)offset;
+	return i915_gem_obj_ggtt_offset_view(obj, &view);
 }
 
 static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id)
@@ -3142,18 +3301,16 @@  static void skylake_update_primary_plane(struct drm_crtc *crtc,
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	struct drm_plane *plane = crtc->primary;
+	struct intel_framebuffer *intel_fb;
 	bool visible = to_intel_plane_state(plane->state)->visible;
-	struct drm_i915_gem_object *obj;
 	int pipe = intel_crtc->pipe;
 	u32 plane_ctl, stride_div, stride;
-	u32 tile_height, plane_offset, plane_size;
 	unsigned int rotation;
-	int x_offset, y_offset;
 	unsigned long surf_addr;
 	struct intel_crtc_state *crtc_state = intel_crtc->config;
 	struct intel_plane_state *plane_state;
-	int src_x = 0, src_y = 0, src_w = 0, src_h = 0;
-	int dst_x = 0, dst_y = 0, dst_w = 0, dst_h = 0;
+	int src_w, src_h;
+	int dst_x, dst_y, dst_w, dst_h;
 	int scaler_id = -1;
 	int pixel_size;
 
@@ -3166,6 +3323,7 @@  static void skylake_update_primary_plane(struct drm_crtc *crtc,
 		return;
 	}
 
+	intel_fb = to_intel_framebuffer(fb);
 	pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 
 	plane_ctl = PLANE_CTL_ENABLE |
@@ -3179,16 +3337,7 @@  static void skylake_update_primary_plane(struct drm_crtc *crtc,
 	rotation = plane->state->rotation;
 	plane_ctl |= skl_plane_ctl_rotation(rotation);
 
-	obj = intel_fb_obj(fb);
-	stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
-					       fb->pixel_format);
-	surf_addr = intel_plane_obj_offset(to_intel_plane(plane), obj, 0);
-
-	WARN_ON(drm_rect_width(&plane_state->src) == 0);
-
 	scaler_id = plane_state->scaler_id;
-	src_x = plane_state->src.x1 >> 16;
-	src_y = plane_state->src.y1 >> 16;
 	src_w = drm_rect_width(&plane_state->src) >> 16;
 	src_h = drm_rect_height(&plane_state->src) >> 16;
 	dst_x = plane_state->dst.x1;
@@ -3196,31 +3345,48 @@  static void skylake_update_primary_plane(struct drm_crtc *crtc,
 	dst_w = drm_rect_width(&plane_state->dst);
 	dst_h = drm_rect_height(&plane_state->dst);
 
-	WARN_ON(x != src_x || y != src_y);
-
 	if (intel_rotation_90_or_270(rotation)) {
-		/* stride = Surface height in tiles */
-		tile_height = intel_tile_height(dev_priv, fb->modifier[0],
-						pixel_size);
-		stride = DIV_ROUND_UP(fb->height, tile_height);
-		x_offset = stride * tile_height - y - src_h;
-		y_offset = x;
-		plane_size = (src_w - 1) << 16 | (src_h - 1);
+		struct drm_rect r = {
+			.x1 = x,
+			.x2 = x + src_w,
+			.y1 = y,
+			.y2 = y + src_h,
+		};
+
+		/* Rotate src coordinates to match rotated GTT view */
+		drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
+
+		x = r.x1;
+		y = r.y1;
+		src_w = drm_rect_width(&r);
+		src_h = drm_rect_height(&r);
+
+		stride_div = intel_tile_height(dev_priv, fb->modifier[0],
+					       pixel_size);
+		stride = intel_fb->plane[0].rotated.pitch;
 	} else {
-		stride = fb->pitches[0] / stride_div;
-		x_offset = x;
-		y_offset = y;
-		plane_size = (src_h - 1) << 16 | (src_w - 1);
+		stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
+						       fb->pixel_format);
+		stride = fb->pitches[0];
 	}
-	plane_offset = y_offset << 16 | x_offset;
 
-	intel_crtc->adjusted_x = x_offset;
-	intel_crtc->adjusted_y = y_offset;
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
+	surf_addr = intel_compute_page_offset(&x, &y, fb, 0,
+					      stride, rotation);
+
+	/* Sizes are 0 based */
+	src_w--;
+	src_h--;
+	dst_w--;
+	dst_h--;
+
+	intel_crtc->adjusted_x = x;
+	intel_crtc->adjusted_y = y;
 
 	I915_WRITE(PLANE_CTL(pipe, 0), plane_ctl);
-	I915_WRITE(PLANE_OFFSET(pipe, 0), plane_offset);
-	I915_WRITE(PLANE_SIZE(pipe, 0), plane_size);
-	I915_WRITE(PLANE_STRIDE(pipe, 0), stride);
+	I915_WRITE(PLANE_OFFSET(pipe, 0), (y << 16) | x);
+	I915_WRITE(PLANE_STRIDE(pipe, 0), stride / stride_div);
+	I915_WRITE(PLANE_SIZE(pipe, 0), (src_h << 16) | src_w);
 
 	if (scaler_id >= 0) {
 		uint32_t ps_ctrl = 0;
@@ -3237,7 +3403,8 @@  static void skylake_update_primary_plane(struct drm_crtc *crtc,
 		I915_WRITE(PLANE_POS(pipe, 0), (dst_y << 16) | dst_x);
 	}
 
-	I915_WRITE(PLANE_SURF(pipe, 0), surf_addr);
+	I915_WRITE(PLANE_SURF(pipe, 0),
+		   intel_surf_gtt_offset(fb, 0, rotation) + surf_addr);
 
 	POSTING_READ(PLANE_SURF(pipe, 0));
 }
@@ -11521,8 +11688,7 @@  static int intel_crtc_page_flip(struct drm_crtc *crtc,
 	if (ret)
 		goto cleanup_pending;
 
-	work->gtt_offset = intel_plane_obj_offset(to_intel_plane(primary),
-						  obj, 0);
+	work->gtt_offset = intel_surf_gtt_offset(fb, 0, primary->state->rotation);
 	work->gtt_offset += intel_crtc->dspaddr_offset;
 
 	if (mmio_flip) {
@@ -14319,7 +14485,6 @@  static int intel_framebuffer_init(struct drm_device *dev,
 				  struct drm_i915_gem_object *obj)
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	unsigned int aligned_height;
 	int ret;
 	u32 pitch_limit, stride_alignment;
 
@@ -14443,16 +14608,13 @@  static int intel_framebuffer_init(struct drm_device *dev,
 	if (ret)
 		return ret;
 
-	aligned_height = intel_fb_align_height(dev, mode_cmd->height,
-					       mode_cmd->pixel_format,
-					       mode_cmd->modifier[0]);
-	/* FIXME drm helper for size checks (especially planar formats)? */
-	if (obj->base.size < aligned_height * mode_cmd->pitches[0])
-		return -EINVAL;
-
 	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
 	intel_fb->obj = obj;
 
+	ret = intel_fill_fb_info(dev_priv, &intel_fb->base);
+	if (ret)
+		return ret;
+
 	ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
 	if (ret) {
 		DRM_ERROR("framebuffer init failed %d\n", ret);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 36d049d..395afd3 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -118,6 +118,19 @@  enum intel_output_type {
 struct intel_framebuffer {
 	struct drm_framebuffer base;
 	struct drm_i915_gem_object *obj;
+	struct intel_rotation_info info;
+
+	struct {
+		struct {
+			/* pixels */
+			unsigned int x, y;
+		} normal;
+		struct {
+			/* pixels */
+			unsigned int x, y;
+			unsigned int pitch;
+		} rotated;
+	} plane[2];
 };
 
 struct intel_fbdev {
@@ -1028,6 +1041,12 @@  void i915_audio_component_init(struct drm_i915_private *dev_priv);
 void i915_audio_component_cleanup(struct drm_i915_private *dev_priv);
 
 /* intel_display.c */
+unsigned int intel_rotation_info_size(const struct intel_rotation_info *info);
+unsigned int intel_fb_xy_to_linear(int x, int y,
+				   const struct drm_framebuffer *fb, int plane);
+void intel_add_fb_offsets(int *x, int *y,
+			  const struct drm_framebuffer *fb, int plane,
+			  unsigned int rotation);
 extern const struct drm_plane_funcs intel_plane_funcs;
 bool intel_has_pending_fb_unpin(struct drm_device *dev);
 int intel_pch_rawclk(struct drm_device *dev);
@@ -1134,10 +1153,10 @@  void assert_fdi_rx_pll(struct drm_i915_private *dev_priv,
 void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state);
 #define assert_pipe_enabled(d, p) assert_pipe(d, p, true)
 #define assert_pipe_disabled(d, p) assert_pipe(d, p, false)
-unsigned long intel_compute_page_offset(int *x, int *y,
-					const struct drm_framebuffer *fb, int plane,
-					unsigned int pitch,
-					unsigned int rotation);
+unsigned int intel_compute_page_offset(int *x, int *y,
+				       const struct drm_framebuffer *fb, int plane,
+				       unsigned int pitch,
+				       unsigned int rotation);
 void intel_prepare_reset(struct drm_device *dev);
 void intel_finish_reset(struct drm_device *dev);
 void hsw_enable_pc8(struct drm_i915_private *dev_priv);
@@ -1174,9 +1193,8 @@  void intel_modeset_preclose(struct drm_device *dev, struct drm_file *file);
 int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
 int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
 
-unsigned long intel_plane_obj_offset(struct intel_plane *intel_plane,
-				     struct drm_i915_gem_object *obj,
-				     unsigned int plane);
+unsigned int intel_surf_gtt_offset(struct drm_framebuffer *fb, int plane,
+				   unsigned int rotation);
 
 u32 skl_plane_ctl_format(uint32_t pixel_format);
 u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 828c3eb..d9d37b0 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -188,17 +188,15 @@  skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
 	struct drm_device *dev = drm_plane->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_plane *intel_plane = to_intel_plane(drm_plane);
-	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
 	const int pipe = intel_plane->pipe;
 	const int plane = intel_plane->plane + 1;
 	u32 plane_ctl, stride_div, stride;
 	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 	const struct drm_intel_sprite_colorkey *key =
 		&to_intel_plane_state(drm_plane->state)->ckey;
-	unsigned long surf_addr;
-	u32 tile_height, plane_offset, plane_size;
+	unsigned int surf_addr;
 	unsigned int rotation;
-	int x_offset, y_offset;
 	struct intel_crtc_state *crtc_state = to_intel_crtc(crtc)->config;
 	int scaler_id;
 
@@ -215,17 +213,8 @@  skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
 				       pixel_size, true,
 				       src_w != crtc_w || src_h != crtc_h);
 
-	stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
-					       fb->pixel_format);
-
 	scaler_id = to_intel_plane_state(drm_plane->state)->scaler_id;
 
-	/* Sizes are 0 based */
-	src_w--;
-	src_h--;
-	crtc_w--;
-	crtc_h--;
-
 	if (key->flags) {
 		I915_WRITE(PLANE_KEYVAL(pipe, plane), key->min_value);
 		I915_WRITE(PLANE_KEYMAX(pipe, plane), key->max_value);
@@ -237,27 +226,43 @@  skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
 	else if (key->flags & I915_SET_COLORKEY_SOURCE)
 		plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE;
 
-	surf_addr = intel_plane_obj_offset(intel_plane, obj, 0);
-
 	if (intel_rotation_90_or_270(rotation)) {
-		/* stride: Surface height in tiles */
-		tile_height = intel_tile_height(dev_priv, fb->modifier[0],
-						pixel_size);
-		stride = DIV_ROUND_UP(fb->height, tile_height);
-		plane_size = (src_w << 16) | src_h;
-		x_offset = stride * tile_height - y - (src_h + 1);
-		y_offset = x;
+		struct drm_rect r = {
+			.x1 = x,
+			.x2 = x + src_w,
+			.y1 = y,
+			.y2 = y + src_h,
+		};
+
+		/* Rotate src coordinates to match rotated GTT view */
+		drm_rect_rotate(&r, fb->width, fb->height, BIT(DRM_ROTATE_270));
+
+		x = r.x1;
+		y = r.y1;
+		src_w = drm_rect_width(&r);
+		src_h = drm_rect_height(&r);
+
+		stride_div = intel_tile_height(dev_priv, fb->modifier[0], pixel_size);
+		stride = intel_fb->plane[0].rotated.pitch;
 	} else {
-		stride = fb->pitches[0] / stride_div;
-		plane_size = (src_h << 16) | src_w;
-		x_offset = x;
-		y_offset = y;
+		stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
+						       fb->pixel_format);
+		stride = fb->pitches[0];
 	}
-	plane_offset = y_offset << 16 | x_offset;
 
-	I915_WRITE(PLANE_OFFSET(pipe, plane), plane_offset);
-	I915_WRITE(PLANE_STRIDE(pipe, plane), stride);
-	I915_WRITE(PLANE_SIZE(pipe, plane), plane_size);
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
+	surf_addr = intel_compute_page_offset(&x, &y, fb, 0,
+					      stride, rotation);
+
+	/* Sizes are 0 based */
+	src_w--;
+	src_h--;
+	crtc_w--;
+	crtc_h--;
+
+	I915_WRITE(PLANE_OFFSET(pipe, plane), (y << 16) | x);
+	I915_WRITE(PLANE_STRIDE(pipe, plane), stride / stride_div);
+	I915_WRITE(PLANE_SIZE(pipe, plane), (src_h << 16) | src_w);
 
 	/* program plane scaler */
 	if (scaler_id >= 0) {
@@ -279,7 +284,8 @@  skl_update_plane(struct drm_plane *drm_plane, struct drm_crtc *crtc,
 	}
 
 	I915_WRITE(PLANE_CTL(pipe, plane), plane_ctl);
-	I915_WRITE(PLANE_SURF(pipe, plane), surf_addr);
+	I915_WRITE(PLANE_SURF(pipe, plane),
+		   intel_surf_gtt_offset(fb, 0, rotation) + surf_addr);
 	POSTING_READ(PLANE_SURF(pipe, plane));
 }
 
@@ -355,8 +361,7 @@  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 	int plane = intel_plane->plane;
 	u32 sprctl;
 	unsigned int rotation = dplane->state->rotation;
-	unsigned long sprsurf_offset, linear_offset;
-	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
+	unsigned int sprsurf_offset, linear_offset;
 	const struct drm_intel_sprite_colorkey *key =
 		&to_intel_plane_state(dplane->state)->ckey;
 
@@ -420,19 +425,19 @@  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 	crtc_w--;
 	crtc_h--;
 
-	linear_offset = y * fb->pitches[0] + x * pixel_size;
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
 	sprsurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
 						   fb->pitches[0], rotation);
-	linear_offset -= sprsurf_offset;
 
 	if (rotation == BIT(DRM_ROTATE_180)) {
 		sprctl |= SP_ROTATE_180;
 
 		x += src_w;
 		y += src_h;
-		linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
 	}
 
+	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
+
 	if (key->flags) {
 		I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value);
 		I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value);
@@ -457,8 +462,8 @@  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 
 	I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);
 	I915_WRITE(SPCNTR(pipe, plane), sprctl);
-	I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
-		   sprsurf_offset);
+	I915_WRITE(SPSURF(pipe, plane),
+		   intel_surf_gtt_offset(fb, 0, rotation) + sprsurf_offset);
 	POSTING_READ(SPSURF(pipe, plane));
 }
 
@@ -492,7 +497,7 @@  ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	enum pipe pipe = intel_plane->pipe;
 	u32 sprctl, sprscale = 0;
 	unsigned int rotation = plane->state->rotation;
-	unsigned long sprsurf_offset, linear_offset;
+	unsigned int sprsurf_offset, linear_offset;
 	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 	const struct drm_intel_sprite_colorkey *key =
 		&to_intel_plane_state(plane->state)->ckey;
@@ -552,10 +557,9 @@  ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	if (crtc_w != src_w || crtc_h != src_h)
 		sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
 
-	linear_offset = y * fb->pitches[0] + x * pixel_size;
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
 	sprsurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
 						   fb->pitches[0], rotation);
-	linear_offset -= sprsurf_offset;
 
 	if (rotation == BIT(DRM_ROTATE_180)) {
 		sprctl |= SPRITE_ROTATE_180;
@@ -564,11 +568,11 @@  ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 		if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
 			x += src_w;
 			y += src_h;
-			linear_offset += src_h * fb->pitches[0] +
-				src_w * pixel_size;
 		}
 	}
 
+	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
+
 	if (key->flags) {
 		I915_WRITE(SPRKEYVAL(pipe), key->min_value);
 		I915_WRITE(SPRKEYMAX(pipe), key->max_value);
@@ -597,7 +601,7 @@  ivb_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 		I915_WRITE(SPRSCALE(pipe), sprscale);
 	I915_WRITE(SPRCTL(pipe), sprctl);
 	I915_WRITE(SPRSURF(pipe),
-		   i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
+		   intel_surf_gtt_offset(fb, 0, rotation) + sprsurf_offset);
 	POSTING_READ(SPRSURF(pipe));
 }
 
@@ -632,7 +636,7 @@  ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
 	int pipe = intel_plane->pipe;
 	unsigned int rotation = plane->state->rotation;
-	unsigned long dvssurf_offset, linear_offset;
+	unsigned int dvssurf_offset, linear_offset;
 	u32 dvscntr, dvsscale;
 	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 	const struct drm_intel_sprite_colorkey *key =
@@ -689,19 +693,19 @@  ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	if (crtc_w != src_w || crtc_h != src_h)
 		dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
 
-	linear_offset = y * fb->pitches[0] + x * pixel_size;
+	intel_add_fb_offsets(&x, &y, fb, 0, rotation);
 	dvssurf_offset = intel_compute_page_offset(&x, &y, fb, 0,
 						   fb->pitches[0], rotation);
-	linear_offset -= dvssurf_offset;
 
 	if (rotation == BIT(DRM_ROTATE_180)) {
 		dvscntr |= DVS_ROTATE_180;
 
 		x += src_w;
 		y += src_h;
-		linear_offset += src_h * fb->pitches[0] + src_w * pixel_size;
 	}
 
+	linear_offset = intel_fb_xy_to_linear(x, y, fb, 0);
+
 	if (key->flags) {
 		I915_WRITE(DVSKEYVAL(pipe), key->min_value);
 		I915_WRITE(DVSKEYMAX(pipe), key->max_value);
@@ -725,7 +729,7 @@  ilk_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
 	I915_WRITE(DVSSCALE(pipe), dvsscale);
 	I915_WRITE(DVSCNTR(pipe), dvscntr);
 	I915_WRITE(DVSSURF(pipe),
-		   i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
+		   intel_surf_gtt_offset(fb, 0, rotation) + dvssurf_offset);
 	POSTING_READ(DVSSURF(pipe));
 }