diff mbox

[1/1] drm/i915: Enabling plane rotation control through sysfs

Message ID 1390571323-4885-1-git-send-email-sagar.a.kamble@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

sagar.a.kamble@intel.com Jan. 24, 2014, 1:48 p.m. UTC
From: Sagar Kamble <sagar.a.kamble@intel.com>

This patch enables 180 degree rotation for primary and sprite planes
through sysfs interface.

Signed-off-by: Uma Shankar <uma.shankar@intel.com>
Signed-off-by: Sagar Kamble <sagar.a.kamble@intel.com>
---
 drivers/gpu/drm/i915/i915_reg.h      |   1 +
 drivers/gpu/drm/i915/i915_sysfs.c    | 124 +++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_display.c |  28 +++++++-
 drivers/gpu/drm/i915/intel_drv.h     |   8 +++
 drivers/gpu/drm/i915/intel_sprite.c  |  38 +++++++++--
 5 files changed, 191 insertions(+), 8 deletions(-)

Comments

Ville Syrjälä Jan. 24, 2014, 2:14 p.m. UTC | #1
On Fri, Jan 24, 2014 at 07:18:43PM +0530, sagar.a.kamble@intel.com wrote:
> From: Sagar Kamble <sagar.a.kamble@intel.com>
> 
> This patch enables 180 degree rotation for primary and sprite planes
> through sysfs interface.

NAK.

See here for the right apporach:
http://lists.freedesktop.org/archives/intel-gfx/2013-September/033951.html

> 
> Signed-off-by: Uma Shankar <uma.shankar@intel.com>
> Signed-off-by: Sagar Kamble <sagar.a.kamble@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_reg.h      |   1 +
>  drivers/gpu/drm/i915/i915_sysfs.c    | 124 +++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_display.c |  28 +++++++-
>  drivers/gpu/drm/i915/intel_drv.h     |   8 +++
>  drivers/gpu/drm/i915/intel_sprite.c  |  38 +++++++++--
>  5 files changed, 191 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index 5d06ad6..92fa3d2 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -3555,6 +3555,7 @@
>  #define   DISPPLANE_STEREO_POLARITY_SECOND	(1<<18)
>  #define   DISPPLANE_TRICKLE_FEED_DISABLE	(1<<14) /* Ironlake */
>  #define   DISPPLANE_TILED			(1<<10)
> +#define   DISPPLANE_180_ROTATION_ENABLE		(1<<15)
>  #define _DSPAADDR		(dev_priv->info->display_mmio_offset + 0x70184)
>  #define _DSPASTRIDE		(dev_priv->info->display_mmio_offset + 0x70188)
>  #define _DSPAPOS		(dev_priv->info->display_mmio_offset + 0x7018C) /* reserved */
> diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
> index 33bcae3..12a214c 100644
> --- a/drivers/gpu/drm/i915/i915_sysfs.c
> +++ b/drivers/gpu/drm/i915/i915_sysfs.c
> @@ -127,6 +127,122 @@ static struct attribute_group rc6_attr_group = {
>  };
>  #endif
>  
> +int i915_set_180_rotation(struct drm_device *dev,
> +				struct i915_180_rotation *rotation)
> +{
> +	struct drm_mode_object *obj;
> +	struct drm_crtc *crtc;
> +	struct intel_crtc *intel_crtc;
> +	struct drm_plane *plane;
> +	struct intel_plane *intel_plane;
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	int ret = 0;
> +
> +	if (rotation->obj_type == DRM_MODE_OBJECT_PLANE) {
> +		obj = drm_mode_object_find(dev, rotation->obj_id,
> +						DRM_MODE_OBJECT_PLANE);
> +		if (!obj) {
> +			DRM_DEBUG_DRIVER("Unknown PLANE ID %d\n",
> +					rotation->obj_id);
> +			return -EINVAL;
> +		}
> +
> +		plane = obj_to_plane(obj);
> +		intel_plane = to_intel_plane(plane);
> +		DRM_DEBUG_DRIVER("[SPRITE:%d] rotation set\n",
> +						intel_plane->base.base.id);
> +		intel_plane->rotate180 = (rotation->rotate & 0x1) ?
> +							true : false;
> +		ret = 1;
> +	} else if (rotation->obj_type == DRM_MODE_OBJECT_CRTC) {
> +		obj = drm_mode_object_find(dev, rotation->obj_id,
> +						DRM_MODE_OBJECT_CRTC);
> +		if (!obj) {
> +			DRM_DEBUG_DRIVER("Unknown CRTC ID %d\n",
> +						rotation->obj_id);
> +			return -EINVAL;
> +		}
> +
> +		crtc = obj_to_crtc(obj);
> +		if (!crtc->enabled) {
> +			DRM_DEBUG_DRIVER("[CRTC:%d] not active\n", crtc->base.id);
> +			return ret;
> +		}
> +		DRM_DEBUG_DRIVER("[CRTC:%d] rotation set\n", crtc->base.id);
> +		intel_crtc = to_intel_crtc(crtc);
> +		intel_crtc->rotate180 = (rotation->rotate & 0x1) ?
> +							true : false;
> +		dev_priv->display.update_plane(crtc, crtc->fb, 0, 0);
> +		ret = 1;
> +	}
> +
> +	return ret;
> +}
> +
> +static ssize_t
> +i915_180_rotation_store(struct device *kdev,
> +			     struct device_attribute *attr,
> +			     const char *buf, size_t count)
> +{
> +	struct drm_minor *minor = dev_to_drm_minor(kdev);
> +	struct drm_device *dev = minor->dev;
> +	struct i915_180_rotation rotation;
> +	char buf1[30], id[11], type[11], val[2], format[18];
> +	int len = 0, ret, no_of_tokens;
> +	u32 rotate;
> +
> +	if (count == 0)
> +		return -EINVAL;
> +
> +	/* Reset the string */
> +	memset(buf1, 0, 30);
> +	if (count > 0) {
> +		if (count > sizeof(buf1) - 1)
> +			return -EINVAL;
> +		memcpy(buf1, buf, count);
> +	}
> +
> +	scnprintf(format, sizeof(format), "%%%zus %%%zus %%%zus",
> +			sizeof(id), sizeof(type), sizeof(val));
> +
> +	no_of_tokens = sscanf(buf1, format, id, type, val);
> +	if (no_of_tokens < 3)
> +		return len;
> +
> +	ret = kstrtou32(id, 0, &(rotation.obj_id));
> +	if (ret)
> +		return ret;
> +
> +	ret = kstrtou32(type, 0, &(rotation.obj_type));
> +	if (ret)
> +		return ret;
> +
> +	ret = kstrtou32(val, 0, &rotate);
> +	if (ret)
> +		return ret;
> +
> +	rotation.rotate = rotate ? true : false;
> +
> +	if (!i915_set_180_rotation(dev, &rotation))
> +		return -EINVAL;
> +
> +	return count;
> +}
> +
> +static DEVICE_ATTR(i915_180_rotation, S_IWUSR,
> +				NULL,
> +				i915_180_rotation_store);
> +
> +static struct attribute *display_attrs[] = {
> +	&dev_attr_i915_180_rotation.attr,
> +	NULL
> +};
> +
> +static struct attribute_group display_attr_group = {
> +	.name = "display",
> +	.attrs =  display_attrs
> +};
> +
>  static int l3_access_valid(struct drm_device *dev, loff_t offset)
>  {
>  	if (!HAS_L3_DPF(dev))
> @@ -568,6 +684,13 @@ void i915_setup_sysfs(struct drm_device *dev)
>  			DRM_ERROR("RC6 residency sysfs setup failed\n");
>  	}
>  #endif
> +	if (INTEL_INFO(dev)->gen >= 6) {
> +		ret = sysfs_create_group(&dev->primary->kdev->kobj,
> +					&display_attr_group);
> +		if (ret)
> +			DRM_ERROR("Display sysfs setup failed\n");
> +	}
> +
>  	if (HAS_L3_DPF(dev)) {
>  		ret = device_create_bin_file(dev->primary->kdev, &dpf_attrs);
>  		if (ret)
> @@ -607,4 +730,5 @@ void i915_teardown_sysfs(struct drm_device *dev)
>  #ifdef CONFIG_PM
>  	sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6_attr_group);
>  #endif
> +	sysfs_unmerge_group(&dev->primary->kdev->kobj, &display_attr_group);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index ec96002..5a1dc0b 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -52,7 +52,6 @@ static void ironlake_pch_clock_get(struct intel_crtc *crtc,
>  static int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
>  			  int x, int y, struct drm_framebuffer *old_fb);
>  
> -
>  typedef struct {
>  	int	min, max;
>  } intel_range_t;
> @@ -2034,7 +2033,10 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
>  	struct intel_framebuffer *intel_fb;
>  	struct drm_i915_gem_object *obj;
>  	int plane = intel_crtc->plane;
> +	int pipe = intel_crtc->pipe;
>  	unsigned long linear_offset;
> +	bool rotate = false;
> +	int pixel_size = 0;
>  	u32 dspcntr;
>  	u32 reg;
>  
> @@ -2047,6 +2049,7 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
>  		return -EINVAL;
>  	}
>  
> +	pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
>  	intel_fb = to_intel_framebuffer(fb);
>  	obj = intel_fb->obj;
>  
> @@ -2085,6 +2088,9 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
>  		BUG();
>  	}
>  
> +	if (intel_crtc->rotate180 && (pipe == 0))
> +		rotate = true;
> +
>  	if (INTEL_INFO(dev)->gen >= 4) {
>  		if (obj->tiling_mode != I915_TILING_NONE)
>  			dspcntr |= DISPPLANE_TILED;
> @@ -2095,6 +2101,11 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
>  	if (IS_G4X(dev))
>  		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
>  
> +	if (rotate)
> +		dspcntr |= DISPPLANE_180_ROTATION_ENABLE;
> +	else
> +		dspcntr &= ~DISPPLANE_180_ROTATION_ENABLE;
> +
>  	I915_WRITE(reg, dspcntr);
>  
>  	linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
> @@ -2116,8 +2127,18 @@ static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
>  	if (INTEL_INFO(dev)->gen >= 4) {
>  		I915_MODIFY_DISPBASE(DSPSURF(plane),
>  				     i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
> -		I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
> -		I915_WRITE(DSPLINOFF(plane), linear_offset);
> +		if (rotate) {
> +			I915_WRITE(DSPTILEOFF(plane),
> +				   (((y + fb->height - 1) << 16) |
> +				    (x + fb->width - 1)));
> +			I915_WRITE(DSPLINOFF(plane),
> +				   linear_offset +
> +				   (fb->height - 1) * fb->pitches[0] +
> +				   fb->width * pixel_size);
> +		} else {
> +			I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
> +			I915_WRITE(DSPLINOFF(plane), linear_offset);
> +		}
>  	} else
>  		I915_WRITE(DSPADDR(plane), i915_gem_obj_ggtt_offset(obj) + linear_offset);
>  	POSTING_READ(reg);
> @@ -10317,6 +10338,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
>  	dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
>  	dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
>  
> +	intel_crtc->rotate180 = false;
>  	drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
>  }
>  
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 7b3c209..1c199fd 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -327,10 +327,17 @@ struct intel_pipe_wm {
>  	bool fbc_wm_enabled;
>  };
>  
> +struct i915_180_rotation {
> +	u32 obj_id;
> +	u32 obj_type;
> +	bool rotate;
> +};
> +
>  struct intel_crtc {
>  	struct drm_crtc base;
>  	enum pipe pipe;
>  	enum plane plane;
> +	bool rotate180;
>  	u8 lut_r[256], lut_g[256], lut_b[256];
>  	/*
>  	 * Whether the crtc and the connected output pipeline is active. Implies
> @@ -392,6 +399,7 @@ struct intel_plane {
>  	struct drm_i915_gem_object *obj;
>  	bool can_scale;
>  	int max_downscale;
> +	bool rotate180;
>  	u32 lut_r[1024], lut_g[1024], lut_b[1024];
>  	int crtc_x, crtc_y;
>  	unsigned int crtc_w, crtc_h;
> diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
> index ed9fa7c..f3224fa 100644
> --- a/drivers/gpu/drm/i915/intel_sprite.c
> +++ b/drivers/gpu/drm/i915/intel_sprite.c
> @@ -51,6 +51,7 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  	int pipe = intel_plane->pipe;
>  	int plane = intel_plane->plane;
>  	u32 sprctl;
> +	bool rotate = false;
>  	unsigned long sprsurf_offset, linear_offset;
>  	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
>  
> @@ -118,6 +119,9 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  	intel_update_sprite_watermarks(dplane, crtc, src_w, pixel_size, true,
>  				       src_w != crtc_w || src_h != crtc_h);
>  
> +	if (intel_plane->rotate180  && (pipe == 0))
> +		rotate = true;
> +
>  	/* Sizes are 0 based */
>  	src_w--;
>  	src_h--;
> @@ -132,14 +136,37 @@ vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
>  	linear_offset -= sprsurf_offset;
>  
>  	I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
> -	I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
> -
> -	if (obj->tiling_mode != I915_TILING_NONE)
> -		I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);
> +	if (rotate)
> +		I915_WRITE(SPPOS(pipe, plane),
> +			((crtc->hwmode.vdisplay - (crtc_y + crtc_h + 1))
> +			<< 16) |
> +			(crtc->hwmode.hdisplay - (crtc_x + crtc_w + 1)));
>  	else
> -		I915_WRITE(SPLINOFF(pipe, plane), linear_offset);
> +		I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
>  
> +	if (obj->tiling_mode != I915_TILING_NONE) {
> +		if (rotate)
> +			I915_WRITE(SPTILEOFF(pipe, plane),
> +				((y + crtc_h) << 16) | (x + crtc_w));
> +		else
> +			I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);
> +	} else {
> +		if (rotate) {
> +			int rot_linoff = linear_offset +
> +					 crtc_h * fb->pitches[0] +
> +					 (crtc_w + 1) * pixel_size;
> +			I915_WRITE(SPLINOFF(pipe, plane), rot_linoff);
> +
> +		} else
> +			I915_WRITE(SPLINOFF(pipe, plane), linear_offset);
> +	}
>  	I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);
> +
> +	if (rotate)
> +		sprctl |= DISPPLANE_180_ROTATION_ENABLE;
> +	else
> +		sprctl &= ~DISPPLANE_180_ROTATION_ENABLE;
> +
>  	I915_WRITE(SPCNTR(pipe, plane), sprctl);
>  	I915_MODIFY_DISPBASE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
>  			     sprsurf_offset);
> @@ -1141,6 +1168,7 @@ intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
>  
>  	intel_plane->pipe = pipe;
>  	intel_plane->plane = plane;
> +	intel_plane->rotate180 = false;
>  	possible_crtcs = (1 << pipe);
>  	ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs,
>  			     &intel_plane_funcs,
> -- 
> 1.8.5
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Ville Syrjälä Jan. 24, 2014, 2:24 p.m. UTC | #2
On Fri, Jan 24, 2014 at 04:14:10PM +0200, Ville Syrjälä wrote:
> On Fri, Jan 24, 2014 at 07:18:43PM +0530, sagar.a.kamble@intel.com wrote:
> > From: Sagar Kamble <sagar.a.kamble@intel.com>
> > 
> > This patch enables 180 degree rotation for primary and sprite planes
> > through sysfs interface.
> 
> NAK.
> 
> See here for the right apporach:
> http://lists.freedesktop.org/archives/intel-gfx/2013-September/033951.html

And BTW if you're really interested in getting this stuff in, we (Daniel
actually :) need tests in i-g-t. We have the display CRC stuff availalbe,
so fully automated tests are possible for this stuff. So this is
definitely one area where we're lacking manpower.

Some ideas for simple sprite tests:
- make sure it appears on the right spot on the screen
- make sure it doesn't appear when off screen
- make sure it clips correctly when partially off screen
- repeat everything w/ 180 degree rotation

One idea for a color key test:
- clear primary to solid color (or some fancier image)
- clear sprite to another color (or some fancier image)
- fill a small rect in the color key on the primary
- place the sprite on top of the color keyed are but make it slightly
  bigger than the area, and make sure only the color keyed area shows
  the sprite

So this would all involve rendering the reference images w/ software,
and then reproducing the expected result using the sprite, and making
sure it all matches up.

Finally come up with more tests as more properties get added (z order,
blending, etc.).
Ville Syrjälä Jan. 28, 2014, 3:59 p.m. UTC | #3
On Fri, Jan 24, 2014 at 04:24:46PM +0200, Ville Syrjälä wrote:
> On Fri, Jan 24, 2014 at 04:14:10PM +0200, Ville Syrjälä wrote:
> > On Fri, Jan 24, 2014 at 07:18:43PM +0530, sagar.a.kamble@intel.com wrote:
> > > From: Sagar Kamble <sagar.a.kamble@intel.com>
> > > 
> > > This patch enables 180 degree rotation for primary and sprite planes
> > > through sysfs interface.
> > 
> > NAK.
> > 
> > See here for the right apporach:
> > http://lists.freedesktop.org/archives/intel-gfx/2013-September/033951.html
> 
> And BTW if you're really interested in getting this stuff in, we (Daniel
> actually :) need tests in i-g-t. We have the display CRC stuff availalbe,
> so fully automated tests are possible for this stuff. So this is
> definitely one area where we're lacking manpower.
> 
> Some ideas for simple sprite tests:
> - make sure it appears on the right spot on the screen
> - make sure it doesn't appear when off screen
> - make sure it clips correctly when partially off screen
> - repeat everything w/ 180 degree rotation

Chris asked for tiled+sprites tests, so I figured I'd list a few more
things here for posterity:

- test both linear and tiled buffers
- test different pixel formats (dithering, csc, etc. might make this
  a bit tricky)
- test panning inside a larger fb by adjusting the src
  coordinates (overlaps a bit with the clipping tests, but
  I think having separate tests for just panning w/o clipping
  is still good)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 5d06ad6..92fa3d2 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3555,6 +3555,7 @@ 
 #define   DISPPLANE_STEREO_POLARITY_SECOND	(1<<18)
 #define   DISPPLANE_TRICKLE_FEED_DISABLE	(1<<14) /* Ironlake */
 #define   DISPPLANE_TILED			(1<<10)
+#define   DISPPLANE_180_ROTATION_ENABLE		(1<<15)
 #define _DSPAADDR		(dev_priv->info->display_mmio_offset + 0x70184)
 #define _DSPASTRIDE		(dev_priv->info->display_mmio_offset + 0x70188)
 #define _DSPAPOS		(dev_priv->info->display_mmio_offset + 0x7018C) /* reserved */
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 33bcae3..12a214c 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -127,6 +127,122 @@  static struct attribute_group rc6_attr_group = {
 };
 #endif
 
+int i915_set_180_rotation(struct drm_device *dev,
+				struct i915_180_rotation *rotation)
+{
+	struct drm_mode_object *obj;
+	struct drm_crtc *crtc;
+	struct intel_crtc *intel_crtc;
+	struct drm_plane *plane;
+	struct intel_plane *intel_plane;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int ret = 0;
+
+	if (rotation->obj_type == DRM_MODE_OBJECT_PLANE) {
+		obj = drm_mode_object_find(dev, rotation->obj_id,
+						DRM_MODE_OBJECT_PLANE);
+		if (!obj) {
+			DRM_DEBUG_DRIVER("Unknown PLANE ID %d\n",
+					rotation->obj_id);
+			return -EINVAL;
+		}
+
+		plane = obj_to_plane(obj);
+		intel_plane = to_intel_plane(plane);
+		DRM_DEBUG_DRIVER("[SPRITE:%d] rotation set\n",
+						intel_plane->base.base.id);
+		intel_plane->rotate180 = (rotation->rotate & 0x1) ?
+							true : false;
+		ret = 1;
+	} else if (rotation->obj_type == DRM_MODE_OBJECT_CRTC) {
+		obj = drm_mode_object_find(dev, rotation->obj_id,
+						DRM_MODE_OBJECT_CRTC);
+		if (!obj) {
+			DRM_DEBUG_DRIVER("Unknown CRTC ID %d\n",
+						rotation->obj_id);
+			return -EINVAL;
+		}
+
+		crtc = obj_to_crtc(obj);
+		if (!crtc->enabled) {
+			DRM_DEBUG_DRIVER("[CRTC:%d] not active\n", crtc->base.id);
+			return ret;
+		}
+		DRM_DEBUG_DRIVER("[CRTC:%d] rotation set\n", crtc->base.id);
+		intel_crtc = to_intel_crtc(crtc);
+		intel_crtc->rotate180 = (rotation->rotate & 0x1) ?
+							true : false;
+		dev_priv->display.update_plane(crtc, crtc->fb, 0, 0);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+static ssize_t
+i915_180_rotation_store(struct device *kdev,
+			     struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct drm_minor *minor = dev_to_drm_minor(kdev);
+	struct drm_device *dev = minor->dev;
+	struct i915_180_rotation rotation;
+	char buf1[30], id[11], type[11], val[2], format[18];
+	int len = 0, ret, no_of_tokens;
+	u32 rotate;
+
+	if (count == 0)
+		return -EINVAL;
+
+	/* Reset the string */
+	memset(buf1, 0, 30);
+	if (count > 0) {
+		if (count > sizeof(buf1) - 1)
+			return -EINVAL;
+		memcpy(buf1, buf, count);
+	}
+
+	scnprintf(format, sizeof(format), "%%%zus %%%zus %%%zus",
+			sizeof(id), sizeof(type), sizeof(val));
+
+	no_of_tokens = sscanf(buf1, format, id, type, val);
+	if (no_of_tokens < 3)
+		return len;
+
+	ret = kstrtou32(id, 0, &(rotation.obj_id));
+	if (ret)
+		return ret;
+
+	ret = kstrtou32(type, 0, &(rotation.obj_type));
+	if (ret)
+		return ret;
+
+	ret = kstrtou32(val, 0, &rotate);
+	if (ret)
+		return ret;
+
+	rotation.rotate = rotate ? true : false;
+
+	if (!i915_set_180_rotation(dev, &rotation))
+		return -EINVAL;
+
+	return count;
+}
+
+static DEVICE_ATTR(i915_180_rotation, S_IWUSR,
+				NULL,
+				i915_180_rotation_store);
+
+static struct attribute *display_attrs[] = {
+	&dev_attr_i915_180_rotation.attr,
+	NULL
+};
+
+static struct attribute_group display_attr_group = {
+	.name = "display",
+	.attrs =  display_attrs
+};
+
 static int l3_access_valid(struct drm_device *dev, loff_t offset)
 {
 	if (!HAS_L3_DPF(dev))
@@ -568,6 +684,13 @@  void i915_setup_sysfs(struct drm_device *dev)
 			DRM_ERROR("RC6 residency sysfs setup failed\n");
 	}
 #endif
+	if (INTEL_INFO(dev)->gen >= 6) {
+		ret = sysfs_create_group(&dev->primary->kdev->kobj,
+					&display_attr_group);
+		if (ret)
+			DRM_ERROR("Display sysfs setup failed\n");
+	}
+
 	if (HAS_L3_DPF(dev)) {
 		ret = device_create_bin_file(dev->primary->kdev, &dpf_attrs);
 		if (ret)
@@ -607,4 +730,5 @@  void i915_teardown_sysfs(struct drm_device *dev)
 #ifdef CONFIG_PM
 	sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6_attr_group);
 #endif
+	sysfs_unmerge_group(&dev->primary->kdev->kobj, &display_attr_group);
 }
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index ec96002..5a1dc0b 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -52,7 +52,6 @@  static void ironlake_pch_clock_get(struct intel_crtc *crtc,
 static int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
 			  int x, int y, struct drm_framebuffer *old_fb);
 
-
 typedef struct {
 	int	min, max;
 } intel_range_t;
@@ -2034,7 +2033,10 @@  static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 	struct intel_framebuffer *intel_fb;
 	struct drm_i915_gem_object *obj;
 	int plane = intel_crtc->plane;
+	int pipe = intel_crtc->pipe;
 	unsigned long linear_offset;
+	bool rotate = false;
+	int pixel_size = 0;
 	u32 dspcntr;
 	u32 reg;
 
@@ -2047,6 +2049,7 @@  static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		return -EINVAL;
 	}
 
+	pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 	intel_fb = to_intel_framebuffer(fb);
 	obj = intel_fb->obj;
 
@@ -2085,6 +2088,9 @@  static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		BUG();
 	}
 
+	if (intel_crtc->rotate180 && (pipe == 0))
+		rotate = true;
+
 	if (INTEL_INFO(dev)->gen >= 4) {
 		if (obj->tiling_mode != I915_TILING_NONE)
 			dspcntr |= DISPPLANE_TILED;
@@ -2095,6 +2101,11 @@  static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 	if (IS_G4X(dev))
 		dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
+	if (rotate)
+		dspcntr |= DISPPLANE_180_ROTATION_ENABLE;
+	else
+		dspcntr &= ~DISPPLANE_180_ROTATION_ENABLE;
+
 	I915_WRITE(reg, dspcntr);
 
 	linear_offset = y * fb->pitches[0] + x * (fb->bits_per_pixel / 8);
@@ -2116,8 +2127,18 @@  static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 	if (INTEL_INFO(dev)->gen >= 4) {
 		I915_MODIFY_DISPBASE(DSPSURF(plane),
 				     i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
-		I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
-		I915_WRITE(DSPLINOFF(plane), linear_offset);
+		if (rotate) {
+			I915_WRITE(DSPTILEOFF(plane),
+				   (((y + fb->height - 1) << 16) |
+				    (x + fb->width - 1)));
+			I915_WRITE(DSPLINOFF(plane),
+				   linear_offset +
+				   (fb->height - 1) * fb->pitches[0] +
+				   fb->width * pixel_size);
+		} else {
+			I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
+			I915_WRITE(DSPLINOFF(plane), linear_offset);
+		}
 	} else
 		I915_WRITE(DSPADDR(plane), i915_gem_obj_ggtt_offset(obj) + linear_offset);
 	POSTING_READ(reg);
@@ -10317,6 +10338,7 @@  static void intel_crtc_init(struct drm_device *dev, int pipe)
 	dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
 	dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
 
+	intel_crtc->rotate180 = false;
 	drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
 }
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 7b3c209..1c199fd 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -327,10 +327,17 @@  struct intel_pipe_wm {
 	bool fbc_wm_enabled;
 };
 
+struct i915_180_rotation {
+	u32 obj_id;
+	u32 obj_type;
+	bool rotate;
+};
+
 struct intel_crtc {
 	struct drm_crtc base;
 	enum pipe pipe;
 	enum plane plane;
+	bool rotate180;
 	u8 lut_r[256], lut_g[256], lut_b[256];
 	/*
 	 * Whether the crtc and the connected output pipeline is active. Implies
@@ -392,6 +399,7 @@  struct intel_plane {
 	struct drm_i915_gem_object *obj;
 	bool can_scale;
 	int max_downscale;
+	bool rotate180;
 	u32 lut_r[1024], lut_g[1024], lut_b[1024];
 	int crtc_x, crtc_y;
 	unsigned int crtc_w, crtc_h;
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index ed9fa7c..f3224fa 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -51,6 +51,7 @@  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 	int pipe = intel_plane->pipe;
 	int plane = intel_plane->plane;
 	u32 sprctl;
+	bool rotate = false;
 	unsigned long sprsurf_offset, linear_offset;
 	int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0);
 
@@ -118,6 +119,9 @@  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 	intel_update_sprite_watermarks(dplane, crtc, src_w, pixel_size, true,
 				       src_w != crtc_w || src_h != crtc_h);
 
+	if (intel_plane->rotate180  && (pipe == 0))
+		rotate = true;
+
 	/* Sizes are 0 based */
 	src_w--;
 	src_h--;
@@ -132,14 +136,37 @@  vlv_update_plane(struct drm_plane *dplane, struct drm_crtc *crtc,
 	linear_offset -= sprsurf_offset;
 
 	I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
-	I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
-
-	if (obj->tiling_mode != I915_TILING_NONE)
-		I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);
+	if (rotate)
+		I915_WRITE(SPPOS(pipe, plane),
+			((crtc->hwmode.vdisplay - (crtc_y + crtc_h + 1))
+			<< 16) |
+			(crtc->hwmode.hdisplay - (crtc_x + crtc_w + 1)));
 	else
-		I915_WRITE(SPLINOFF(pipe, plane), linear_offset);
+		I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
 
+	if (obj->tiling_mode != I915_TILING_NONE) {
+		if (rotate)
+			I915_WRITE(SPTILEOFF(pipe, plane),
+				((y + crtc_h) << 16) | (x + crtc_w));
+		else
+			I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);
+	} else {
+		if (rotate) {
+			int rot_linoff = linear_offset +
+					 crtc_h * fb->pitches[0] +
+					 (crtc_w + 1) * pixel_size;
+			I915_WRITE(SPLINOFF(pipe, plane), rot_linoff);
+
+		} else
+			I915_WRITE(SPLINOFF(pipe, plane), linear_offset);
+	}
 	I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);
+
+	if (rotate)
+		sprctl |= DISPPLANE_180_ROTATION_ENABLE;
+	else
+		sprctl &= ~DISPPLANE_180_ROTATION_ENABLE;
+
 	I915_WRITE(SPCNTR(pipe, plane), sprctl);
 	I915_MODIFY_DISPBASE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
 			     sprsurf_offset);
@@ -1141,6 +1168,7 @@  intel_plane_init(struct drm_device *dev, enum pipe pipe, int plane)
 
 	intel_plane->pipe = pipe;
 	intel_plane->plane = plane;
+	intel_plane->rotate180 = false;
 	possible_crtcs = (1 << pipe);
 	ret = drm_plane_init(dev, &intel_plane->base, possible_crtcs,
 			     &intel_plane_funcs,