diff mbox

[v3] drm/i915: Deal with upside-down mounted LCD panels

Message ID 20170822165450.1716-2-hdegoede@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Hans de Goede Aug. 22, 2017, 4:54 p.m. UTC
On some (Bay Trail) devices the LCD panel is mounted upside-down.

This commit uses the code to read back the initial rotation of the
primary plane in get_initial_plane_config from Ville Syrjala's
"drm/fb-helper: Inherit rotation wip" patch and when re-using the
initial fb it stores that in intel_crtc.initial_rotation.

It adds an intel_plane_get_rotation helper which combines this
initial_rotation with any rotation requested by userspace and
uses this in all places which look at a planes rotation, thus
transparently dealing with upside-down LCD panels without requiring
any user-space or fbcon changes.

This fixes the kernel boot messages switching from being shown the right
way up in efifb to being shown upside down as soon as a native kms driver
loads, as well as any graphics displayed by userspace being upside-down.

Note this only deals with upside-down LCD panels / 180 degrees
rotation as the hardware in question only supports 180 degrees
rotation in hardware. The one model known which has a panel mounted
with 90/270 degrees rotation will need to be fully dealt with in
userspace, even the firmware boot screen / menus are rotated 90 degrees
on this one and there simply is nothing the kernel can do about this.

BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=94894
Cc: Ville Syrjala <ville.syrjala@linux.intel.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Changes in v2:
-Fix brown paperbag bug s/val & mask/val & ~mask/ to clear old rotation bits
Changes in v3:
-Rebase on current (2017 aug. 22) drm-intel-next-queued
---
 drivers/gpu/drm/i915/intel_atomic_plane.c |  7 ++-
 drivers/gpu/drm/i915/intel_display.c      | 91 +++++++++++++++++++++++++------
 drivers/gpu/drm/i915/intel_drv.h          | 32 +++++++++++
 drivers/gpu/drm/i915/intel_fbc.c          |  2 +-
 drivers/gpu/drm/i915/intel_pm.c           |  6 +-
 drivers/gpu/drm/i915/intel_sprite.c       | 14 ++---
 6 files changed, 121 insertions(+), 31 deletions(-)

Comments

kernel test robot Aug. 24, 2017, 10:45 p.m. UTC | #1
Hi Hans,

[auto build test WARNING on drm-intel/for-linux-next]
[also build test WARNING on next-20170824]
[cannot apply to v4.13-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Hans-de-Goede/drm-i915-Deal-with-upside-down-mounted-LCD-panels/20170825-061654
base:   git://anongit.freedesktop.org/drm-intel for-linux-next
config: x86_64-randconfig-x001-201734 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   drivers/gpu/drm/i915/intel_display.c: In function 'intel_fixup_initial_subpixel_order':
>> drivers/gpu/drm/i915/intel_display.c:2804:2: warning: enumeration value 'SubPixelUnknown' not handled in switch [-Wswitch]
     switch (connector->display_info.subpixel_order) {
     ^~~~~~
>> drivers/gpu/drm/i915/intel_display.c:2804:2: warning: enumeration value 'SubPixelNone' not handled in switch [-Wswitch]
   In file included from include/uapi/linux/stddef.h:1:0,
                    from include/linux/stddef.h:4,
                    from include/uapi/linux/posix_types.h:4,
                    from include/uapi/linux/types.h:13,
                    from include/linux/types.h:5,
                    from include/linux/list.h:4,
                    from include/linux/dmi.h:4,
                    from drivers/gpu/drm/i915/intel_display.c:27:
   drivers/gpu/drm/i915/intel_display.c: At top level:
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'strcpy' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:390:2: note: in expansion of macro 'if'
     if (p_size == (size_t)-1 && q_size == (size_t)-1)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'kmemdup' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:380:2: note: in expansion of macro 'if'
     if (p_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'kmemdup' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:378:2: note: in expansion of macro 'if'
     if (__builtin_constant_p(size) && p_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memchr_inv' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:369:2: note: in expansion of macro 'if'
     if (p_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memchr_inv' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:367:2: note: in expansion of macro 'if'
     if (__builtin_constant_p(size) && p_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memchr' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:358:2: note: in expansion of macro 'if'
     if (p_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memchr' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:356:2: note: in expansion of macro 'if'
     if (__builtin_constant_p(size) && p_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memcmp' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:348:2: note: in expansion of macro 'if'
     if (p_size < size || q_size < size)
     ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memcmp' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:345:3: note: in expansion of macro 'if'
      if (q_size < size)
      ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memcmp' which is not static
       ______f = {     \
       ^
   include/linux/compiler.h:154:23: note: in expansion of macro '__trace_if'
    #define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) )
                          ^~~~~~~~~~
   include/linux/string.h:343:3: note: in expansion of macro 'if'
      if (p_size < size)
      ^~
   include/linux/compiler.h:162:4: warning: '______f' is static but declared in inline function 'memcmp' which is not static

vim +/SubPixelUnknown +2804 drivers/gpu/drm/i915/intel_display.c

  2793	
  2794	static void
  2795	intel_fixup_initial_subpixel_order(struct drm_connector *connector,
  2796					   u8 initial_rotation)
  2797	{
  2798		enum subpixel_order new_order;
  2799	
  2800		/* We only support an initial rotation of DRM_MODE_ROTATE_180 for now */
  2801		if (initial_rotation != DRM_MODE_ROTATE_180)
  2802			return;
  2803	
> 2804		switch (connector->display_info.subpixel_order) {
  2805		case SubPixelHorizontalRGB: new_order = SubPixelHorizontalBGR; break;
  2806		case SubPixelHorizontalBGR: new_order = SubPixelHorizontalRGB; break;
  2807		case SubPixelVerticalRGB:   new_order = SubPixelVerticalBGR;   break;
  2808		case SubPixelVerticalBGR:   new_order = SubPixelVerticalRGB;   break;
  2809		}
  2810	
  2811		connector->display_info.subpixel_order = new_order;
  2812	}
  2813	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Aug. 24, 2017, 11:16 p.m. UTC | #2
Hi Hans,

[auto build test ERROR on drm-intel/for-linux-next]
[also build test ERROR on next-20170824]
[cannot apply to v4.13-rc6]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Hans-de-Goede/drm-i915-Deal-with-upside-down-mounted-LCD-panels/20170825-061654
base:   git://anongit.freedesktop.org/drm-intel for-linux-next
config: x86_64-randconfig-x009-201734 (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   drivers/gpu/drm/i915/intel_display.c: In function 'intel_fixup_initial_subpixel_order':
>> drivers/gpu/drm/i915/intel_display.c:2804:2: error: enumeration value 'SubPixelUnknown' not handled in switch [-Werror=switch]
     switch (connector->display_info.subpixel_order) {
     ^~~~~~
>> drivers/gpu/drm/i915/intel_display.c:2804:2: error: enumeration value 'SubPixelNone' not handled in switch [-Werror=switch]
   Cyclomatic Complexity 5 include/linux/compiler.h:__read_once_size
   Cyclomatic Complexity 5 include/linux/compiler.h:__write_once_size
   Cyclomatic Complexity 2 arch/x86/include/asm/bitops.h:set_bit
   Cyclomatic Complexity 2 arch/x86/include/asm/bitops.h:clear_bit
   Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:constant_test_bit
   Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:ffs
   Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:fls
   Cyclomatic Complexity 1 arch/x86/include/asm/bitops.h:fls64
   Cyclomatic Complexity 1 include/linux/bitops.h:fls_long
   Cyclomatic Complexity 1 include/linux/log2.h:__ilog2_u32
   Cyclomatic Complexity 1 include/linux/log2.h:__ilog2_u64
   Cyclomatic Complexity 1 include/linux/log2.h:__roundup_pow_of_two
   Cyclomatic Complexity 1 include/linux/list.h:INIT_LIST_HEAD
   Cyclomatic Complexity 1 include/linux/list.h:__list_del
   Cyclomatic Complexity 2 include/linux/list.h:__list_del_entry
   Cyclomatic Complexity 1 include/linux/list.h:list_del
   Cyclomatic Complexity 1 include/linux/err.h:ERR_PTR
   Cyclomatic Complexity 1 include/linux/err.h:PTR_ERR
   Cyclomatic Complexity 1 include/linux/err.h:IS_ERR
   Cyclomatic Complexity 1 include/linux/err.h:ERR_CAST
   Cyclomatic Complexity 2 include/linux/err.h:PTR_ERR_OR_ZERO
   Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:atomic_read
   Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:atomic_inc
   Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:atomic_dec
   Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:atomic_dec_and_test
   Cyclomatic Complexity 2 arch/x86/include/asm/atomic.h:atomic_try_cmpxchg
   Cyclomatic Complexity 1 arch/x86/include/asm/atomic.h:atomic_or
   Cyclomatic Complexity 3 arch/x86/include/asm/atomic.h:__atomic_add_unless
   Cyclomatic Complexity 1 arch/x86/include/asm/atomic64_64.h:atomic64_read
   Cyclomatic Complexity 1 include/linux/atomic.h:atomic_add_unless
   Cyclomatic Complexity 1 include/asm-generic/atomic-long.h:atomic_long_read
   Cyclomatic Complexity 1 include/linux/lockdep.h:lock_is_held
   Cyclomatic Complexity 1 include/asm-generic/getorder.h:__get_order
   Cyclomatic Complexity 1 arch/x86/include/asm/paravirt.h:arch_local_save_flags
   Cyclomatic Complexity 1 include/linux/math64.h:div_u64_rem
   Cyclomatic Complexity 1 include/linux/math64.h:div_u64
   Cyclomatic Complexity 1 arch/x86/include/asm/irqflags.h:arch_irqs_disabled_flags
   Cyclomatic Complexity 1 arch/x86/include/asm/processor.h:rep_nop
   Cyclomatic Complexity 1 arch/x86/include/asm/processor.h:cpu_relax
   Cyclomatic Complexity 1 include/linux/mutex.h:__mutex_owner
   Cyclomatic Complexity 1 include/linux/mutex.h:mutex_is_locked
   Cyclomatic Complexity 1 arch/x86/include/asm/preempt.h:preempt_count
   Cyclomatic Complexity 5 arch/x86/include/asm/preempt.h:__preempt_count_add
   Cyclomatic Complexity 5 arch/x86/include/asm/preempt.h:__preempt_count_sub
   Cyclomatic Complexity 1 include/linux/rcupdate.h:__rcu_read_lock
   Cyclomatic Complexity 1 include/linux/rcupdate.h:__rcu_read_unlock
   Cyclomatic Complexity 1 include/linux/spinlock.h:spinlock_check
   Cyclomatic Complexity 1 include/linux/spinlock.h:spin_lock
   Cyclomatic Complexity 1 include/linux/spinlock.h:spin_lock_irq
   Cyclomatic Complexity 1 include/linux/spinlock.h:spin_unlock
   Cyclomatic Complexity 1 include/linux/spinlock.h:spin_unlock_irq
   Cyclomatic Complexity 1 include/linux/spinlock.h:spin_unlock_irqrestore
   Cyclomatic Complexity 1 include/linux/jiffies.h:_msecs_to_jiffies
   Cyclomatic Complexity 3 include/linux/jiffies.h:msecs_to_jiffies
   Cyclomatic Complexity 1 include/linux/jiffies.h:_usecs_to_jiffies
   Cyclomatic Complexity 3 include/linux/jiffies.h:usecs_to_jiffies
   Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_lock_acquire
   Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_lock_release
   Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_read_lock
   Cyclomatic Complexity 1 include/linux/rcupdate.h:rcu_read_unlock
   Cyclomatic Complexity 1 include/linux/workqueue.h:__init_work
   Cyclomatic Complexity 1 include/linux/workqueue.h:queue_work
   Cyclomatic Complexity 1 include/linux/workqueue.h:schedule_work
   Cyclomatic Complexity 1 include/linux/workqueue.h:flush_scheduled_work
   Cyclomatic Complexity 1 include/linux/llist.h:init_llist_head
   Cyclomatic Complexity 1 include/linux/llist.h:llist_empty
   Cyclomatic Complexity 1 include/linux/llist.h:llist_add
   Cyclomatic Complexity 1 include/linux/llist.h:llist_del_all
   Cyclomatic Complexity 1 include/linux/idr.h:idr_find
   Cyclomatic Complexity 1 include/linux/refcount.h:refcount_inc_not_zero
   Cyclomatic Complexity 1 include/linux/refcount.h:refcount_inc
   Cyclomatic Complexity 1 include/linux/refcount.h:refcount_dec_and_test
   Cyclomatic Complexity 1 include/linux/kref.h:kref_get
   Cyclomatic Complexity 2 include/linux/kref.h:kref_put
   Cyclomatic Complexity 1 include/linux/kref.h:kref_get_unless_zero
   Cyclomatic Complexity 1 include/linux/kasan.h:kasan_kmalloc
   Cyclomatic Complexity 28 include/linux/slab.h:kmalloc_index
   Cyclomatic Complexity 1 include/linux/slab.h:kmem_cache_alloc_trace
   Cyclomatic Complexity 1 include/linux/slab.h:kmalloc_order_trace
   Cyclomatic Complexity 68 include/linux/slab.h:kmalloc_large
   Cyclomatic Complexity 5 include/linux/slab.h:kmalloc
   Cyclomatic Complexity 1 include/linux/slab.h:kzalloc
   Cyclomatic Complexity 1 arch/x86/include/asm/io.h:readl
   Cyclomatic Complexity 1 arch/x86/include/asm/io.h:writel
   Cyclomatic Complexity 1 arch/x86/include/asm/io.h:outb
   Cyclomatic Complexity 1 arch/x86/include/asm/io.h:inb
   Cyclomatic Complexity 1 include/linux/vgaarb.h:vga_get
   Cyclomatic Complexity 1 include/linux/vgaarb.h:vga_get_uninterruptible
   Cyclomatic Complexity 2 include/linux/dma-fence.h:dma_fence_put
   Cyclomatic Complexity 2 include/linux/dma-fence.h:dma_fence_get
   Cyclomatic Complexity 2 include/linux/dma-fence.h:dma_fence_get_rcu
   Cyclomatic Complexity 4 include/linux/dma-fence.h:dma_fence_get_rcu_safe
   Cyclomatic Complexity 1 include/linux/ww_mutex.h:ww_mutex_is_locked
   Cyclomatic Complexity 1 include/drm/drm_modeset_lock.h:drm_modeset_is_locked
   Cyclomatic Complexity 1 include/drm/drm_rect.h:drm_rect_width
   Cyclomatic Complexity 1 include/drm/drm_rect.h:drm_rect_height
   Cyclomatic Complexity 1 include/drm/drm_framebuffer.h:drm_framebuffer_get
   Cyclomatic Complexity 1 include/drm/drm_framebuffer.h:drm_framebuffer_put
   Cyclomatic Complexity 1 include/drm/drm_framebuffer.h:drm_framebuffer_reference
   Cyclomatic Complexity 1 include/drm/drm_framebuffer.h:drm_framebuffer_unreference

vim +/SubPixelUnknown +2804 drivers/gpu/drm/i915/intel_display.c

  2793	
  2794	static void
  2795	intel_fixup_initial_subpixel_order(struct drm_connector *connector,
  2796					   u8 initial_rotation)
  2797	{
  2798		enum subpixel_order new_order;
  2799	
  2800		/* We only support an initial rotation of DRM_MODE_ROTATE_180 for now */
  2801		if (initial_rotation != DRM_MODE_ROTATE_180)
  2802			return;
  2803	
> 2804		switch (connector->display_info.subpixel_order) {
  2805		case SubPixelHorizontalRGB: new_order = SubPixelHorizontalBGR; break;
  2806		case SubPixelHorizontalBGR: new_order = SubPixelHorizontalRGB; break;
  2807		case SubPixelVerticalRGB:   new_order = SubPixelVerticalBGR;   break;
  2808		case SubPixelVerticalBGR:   new_order = SubPixelVerticalRGB;   break;
  2809		}
  2810	
  2811		connector->display_info.subpixel_order = new_order;
  2812	}
  2813	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c
index ee76fab7bb6f..824aaba5112b 100644
--- a/drivers/gpu/drm/i915/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/intel_atomic_plane.c
@@ -116,6 +116,7 @@  int intel_plane_atomic_check_with_state(struct intel_crtc_state *crtc_state,
 	struct intel_plane *intel_plane = to_intel_plane(plane);
 	const struct drm_display_mode *adjusted_mode =
 		&crtc_state->base.adjusted_mode;
+	unsigned int rotation = intel_plane_get_rotation(state);
 	int ret;
 
 	/*
@@ -135,7 +136,7 @@  int intel_plane_atomic_check_with_state(struct intel_crtc_state *crtc_state,
 	intel_state->clip.y2 =
 		crtc_state->base.enable ? crtc_state->pipe_src_h : 0;
 
-	if (state->fb && drm_rotation_90_or_270(state->rotation)) {
+	if (state->fb && drm_rotation_90_or_270(rotation)) {
 		struct drm_format_name_buf format_name;
 
 		if (state->fb->modifier != I915_FORMAT_MOD_Y_TILED &&
@@ -164,8 +165,8 @@  int intel_plane_atomic_check_with_state(struct intel_crtc_state *crtc_state,
 
 	/* CHV ignores the mirror bit when the rotate bit is set :( */
 	if (IS_CHERRYVIEW(dev_priv) &&
-	    state->rotation & DRM_MODE_ROTATE_180 &&
-	    state->rotation & DRM_MODE_REFLECT_X) {
+	    rotation & DRM_MODE_ROTATE_180 &&
+	    rotation & DRM_MODE_REFLECT_X) {
 		DRM_DEBUG_KMS("Cannot rotate and reflect at the same time\n");
 		return -EINVAL;
 	}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3b95cf953335..c3d9c27248d7 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2277,7 +2277,7 @@  void intel_add_fb_offsets(int *x, int *y,
 
 {
 	const struct intel_framebuffer *intel_fb = to_intel_framebuffer(state->base.fb);
-	unsigned int rotation = state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&state->base);
 
 	if (drm_rotation_90_or_270(rotation)) {
 		*x += intel_fb->rotated[plane].x;
@@ -2330,7 +2330,7 @@  static u32 intel_adjust_tile_offset(int *x, int *y,
 	const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev);
 	const struct drm_framebuffer *fb = state->base.fb;
 	unsigned int cpp = fb->format->cpp[plane];
-	unsigned int rotation = state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&state->base);
 	unsigned int pitch = intel_fb_pitch(fb, plane, rotation);
 
 	WARN_ON(new_offset > old_offset);
@@ -2434,7 +2434,7 @@  u32 intel_compute_tile_offset(int *x, int *y,
 	struct intel_plane *intel_plane = to_intel_plane(state->base.plane);
 	struct drm_i915_private *dev_priv = to_i915(intel_plane->base.dev);
 	const struct drm_framebuffer *fb = state->base.fb;
-	unsigned int rotation = state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&state->base);
 	int pitch = intel_fb_pitch(fb, plane, rotation);
 	u32 alignment;
 
@@ -2792,6 +2792,44 @@  intel_set_plane_visible(struct intel_crtc_state *crtc_state,
 }
 
 static void
+intel_fixup_initial_subpixel_order(struct drm_connector *connector,
+				   u8 initial_rotation)
+{
+	enum subpixel_order new_order;
+
+	/* We only support an initial rotation of DRM_MODE_ROTATE_180 for now */
+	if (initial_rotation != DRM_MODE_ROTATE_180)
+		return;
+
+	switch (connector->display_info.subpixel_order) {
+	case SubPixelHorizontalRGB: new_order = SubPixelHorizontalBGR; break;
+	case SubPixelHorizontalBGR: new_order = SubPixelHorizontalRGB; break;
+	case SubPixelVerticalRGB:   new_order = SubPixelVerticalBGR;   break;
+	case SubPixelVerticalBGR:   new_order = SubPixelVerticalRGB;   break;
+	}
+
+	connector->display_info.subpixel_order = new_order;
+}
+
+static void
+intel_set_initial_rotation(struct intel_crtc *intel_crtc, u8 rotation)
+{
+	struct drm_crtc_state *state = intel_crtc->base.state;
+	struct drm_device *dev = intel_crtc->base.dev;
+	struct drm_connector_list_iter conn_iter;
+	struct drm_connector *conn;
+
+	intel_crtc->initial_rotation = rotation;
+
+	drm_connector_list_iter_begin(dev, &conn_iter);
+	drm_for_each_connector_iter(conn, &conn_iter) {
+		if (state->connector_mask & (1 << drm_connector_index(conn)))
+			intel_fixup_initial_subpixel_order(conn, rotation);
+	}
+	drm_connector_list_iter_end(&conn_iter);
+}
+
+static void
 intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
 			     struct intel_initial_plane_config *plane_config)
 {
@@ -2858,9 +2896,11 @@  intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
 	return;
 
 valid_fb:
+	intel_set_initial_rotation(intel_crtc, plane_config->rotation);
+
 	mutex_lock(&dev->struct_mutex);
 	intel_state->vma =
-		intel_pin_and_fence_fb_obj(fb, primary->state->rotation);
+		intel_pin_and_fence_fb_obj(fb, intel_crtc->initial_rotation);
 	mutex_unlock(&dev->struct_mutex);
 	if (IS_ERR(intel_state->vma)) {
 		DRM_ERROR("failed to pin boot fb on pipe %d: %li\n",
@@ -2986,7 +3026,7 @@  static bool skl_check_main_ccs_coordinates(struct intel_plane_state *plane_state
 static int skl_check_main_surface(struct intel_plane_state *plane_state)
 {
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	int x = plane_state->base.src.x1 >> 16;
 	int y = plane_state->base.src.y1 >> 16;
 	int w = drm_rect_width(&plane_state->base.src) >> 16;
@@ -3064,7 +3104,7 @@  static int skl_check_main_surface(struct intel_plane_state *plane_state)
 static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
 {
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	int max_width = skl_max_plane_width(fb, 1, rotation);
 	int max_height = 4096;
 	int x = plane_state->base.src.x1 >> 17;
@@ -3095,6 +3135,7 @@  static int skl_check_ccs_aux_surface(struct intel_plane_state *plane_state)
 	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
 	struct intel_crtc *crtc = to_intel_crtc(plane_state->base.crtc);
 	const struct drm_framebuffer *fb = plane_state->base.fb;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	int src_x = plane_state->base.src.x1 >> 16;
 	int src_y = plane_state->base.src.y1 >> 16;
 	int hsub = fb->format->hsub;
@@ -3117,7 +3158,7 @@  static int skl_check_ccs_aux_surface(struct intel_plane_state *plane_state)
 		return -EINVAL;
 	}
 
-	if (plane_state->base.rotation & ~(DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180)) {
+	if (rotation & ~(DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180)) {
 		DRM_DEBUG_KMS("RC support only with 0/180 degree rotation %x\n",
 			      plane_state->base.rotation);
 		return -EINVAL;
@@ -3136,7 +3177,7 @@  static int skl_check_ccs_aux_surface(struct intel_plane_state *plane_state)
 int skl_check_plane_surface(struct intel_plane_state *plane_state)
 {
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	int ret;
 
 	if (!plane_state->base.visible)
@@ -3181,7 +3222,7 @@  static u32 i9xx_plane_ctl(const struct intel_crtc_state *crtc_state,
 		to_i915(plane_state->base.plane->dev);
 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	u32 dspcntr;
 
 	dspcntr = DISPLAY_PLANE_ENABLE | DISPPLANE_GAMMA_ENABLE;
@@ -3254,7 +3295,8 @@  int i9xx_check_plane_surface(struct intel_plane_state *plane_state)
 
 	/* HSW/BDW do this automagically in hardware */
 	if (!IS_HASWELL(dev_priv) && !IS_BROADWELL(dev_priv)) {
-		unsigned int rotation = plane_state->base.rotation;
+		unsigned int rotation =
+			intel_plane_get_rotation(&plane_state->base);
 		int src_w = drm_rect_width(&plane_state->base.src) >> 16;
 		int src_h = drm_rect_height(&plane_state->base.src) >> 16;
 
@@ -3508,7 +3550,7 @@  u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state,
 	struct drm_i915_private *dev_priv =
 		to_i915(plane_state->base.plane->dev);
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
 	u32 plane_ctl;
 
@@ -3543,7 +3585,7 @@  static void skylake_update_primary_plane(struct intel_plane *plane,
 	enum plane_id plane_id = plane->id;
 	enum pipe pipe = plane->pipe;
 	u32 plane_ctl = plane_state->ctl;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	u32 stride = skl_plane_stride(fb, 0, rotation);
 	u32 aux_stride = skl_plane_stride(fb, 1, rotation);
 	u32 surf_addr = plane_state->main.offset;
@@ -7503,6 +7545,9 @@  i9xx_get_initial_plane_config(struct intel_crtc *crtc,
 			plane_config->tiling = I915_TILING_X;
 			fb->modifier = I915_FORMAT_MOD_X_TILED;
 		}
+
+		if (val & DISPPLANE_ROTATE_180)
+			plane_config->rotation = DRM_MODE_ROTATE_180;
 	}
 
 	pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
@@ -8556,6 +8601,9 @@  skylake_get_initial_plane_config(struct intel_crtc *crtc,
 		goto error;
 	}
 
+	if ((val & PLANE_CTL_ROTATE_MASK) == PLANE_CTL_ROTATE_180)
+		plane_config->rotation = DRM_MODE_ROTATE_180;
+
 	base = I915_READ(PLANE_SURF(pipe, 0)) & 0xfffff000;
 	plane_config->base = base;
 
@@ -8641,6 +8689,9 @@  ironlake_get_initial_plane_config(struct intel_crtc *crtc,
 			plane_config->tiling = I915_TILING_X;
 			fb->modifier = I915_FORMAT_MOD_X_TILED;
 		}
+
+		if (val & DISPPLANE_ROTATE_180)
+			plane_config->rotation = DRM_MODE_ROTATE_180;
 	}
 
 	pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
@@ -9346,7 +9397,7 @@  static u32 intel_cursor_base(const struct intel_plane_state *plane_state)
 
 	/* ILK+ do this automagically */
 	if (HAS_GMCH_DISPLAY(dev_priv) &&
-	    plane_state->base.rotation & DRM_MODE_ROTATE_180)
+	    intel_plane_get_rotation(&plane_state->base) & DRM_MODE_ROTATE_180)
 		base += (plane_state->base.crtc_h *
 			 plane_state->base.crtc_w - 1) * fb->format->cpp[0];
 
@@ -9568,7 +9619,7 @@  static u32 i9xx_cursor_ctl(const struct intel_crtc_state *crtc_state,
 		return 0;
 	}
 
-	if (plane_state->base.rotation & DRM_MODE_ROTATE_180)
+	if (intel_plane_get_rotation(&plane_state->base) & DRM_MODE_ROTATE_180)
 		cntl |= CURSOR_ROTATE_180;
 
 	return cntl;
@@ -9601,7 +9652,7 @@  static bool i9xx_cursor_size_ok(const struct intel_plane_state *plane_state)
 	 * cursors.
 	 */
 	if (HAS_CUR_FBC(dev_priv) &&
-	    plane_state->base.rotation & DRM_MODE_ROTATE_0) {
+	    intel_plane_get_rotation(&plane_state->base) & DRM_MODE_ROTATE_0) {
 		if (height < 8 || height > width)
 			return false;
 	} else {
@@ -12718,7 +12769,8 @@  intel_prepare_plane_fb(struct drm_plane *plane,
 	} else {
 		struct i915_vma *vma;
 
-		vma = intel_pin_and_fence_fb_obj(fb, new_state->rotation);
+		vma = intel_pin_and_fence_fb_obj(fb,
+				intel_plane_get_rotation(new_state));
 		if (!IS_ERR(vma))
 			to_intel_plane_state(new_state)->vma = vma;
 		else
@@ -13088,7 +13140,8 @@  intel_legacy_cursor_update(struct drm_plane *plane,
 			goto out_unlock;
 		}
 	} else {
-		vma = intel_pin_and_fence_fb_obj(fb, new_plane_state->rotation);
+		vma = intel_pin_and_fence_fb_obj(fb,
+				intel_plane_get_rotation(new_plane_state));
 		if (IS_ERR(vma)) {
 			DRM_DEBUG_KMS("failed to pin object\n");
 
@@ -14558,7 +14611,9 @@  int intel_modeset_init(struct drm_device *dev)
 	drm_modeset_unlock_all(dev);
 
 	for_each_intel_crtc(dev, crtc) {
-		struct intel_initial_plane_config plane_config = {};
+		struct intel_initial_plane_config plane_config = {
+			.rotation = DRM_MODE_ROTATE_0
+		};
 
 		if (!crtc->active)
 			continue;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 2940d393ecfd..602421e29f00 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -447,6 +447,7 @@  struct intel_initial_plane_config {
 	unsigned int tiling;
 	int size;
 	u32 base;
+	uint8_t rotation; /* DRM_MODE_ROTATE_* */
 };
 
 #define SKL_MIN_SRC_W 8
@@ -801,6 +802,12 @@  struct intel_crtc {
 	unsigned long long enabled_power_domains;
 	struct intel_overlay *overlay;
 
+	/*
+	 * Initial DRM_MODE_ROTATE_* as read back from the hardware at init,
+	 * this is used to compensate for e.g. upside-down mounted lcd-panels.
+	 */
+	uint8_t initial_rotation;
+
 	/* Display surface base address adjustement for pageflips. Note that on
 	 * gen4+ this only adjusts up to a tile, offsets within a tile are
 	 * handled in the hw itself (with the TILEOFF register). */
@@ -1465,6 +1472,31 @@  static inline u32 intel_plane_ggtt_offset(const struct intel_plane_state *state)
 	return i915_ggtt_offset(state->vma);
 }
 
+static inline unsigned int
+intel_plane_get_rotation(const struct drm_plane_state *plane_state)
+{
+	unsigned int rotation = plane_state->rotation;
+	unsigned int new_rotation = DRM_MODE_ROTATE_0;
+	struct intel_crtc *intel_crtc;
+
+	if (!plane_state->crtc)
+		return rotation;
+
+	/* We only support an initial rotation of DRM_MODE_ROTATE_180 for now */
+	intel_crtc = to_intel_crtc(plane_state->crtc);
+	if (intel_crtc->initial_rotation != DRM_MODE_ROTATE_180)
+		return rotation;
+
+	switch (rotation & DRM_MODE_ROTATE_MASK) {
+	case DRM_MODE_ROTATE_0:   new_rotation = DRM_MODE_ROTATE_180;	break;
+	case DRM_MODE_ROTATE_90:  new_rotation = DRM_MODE_ROTATE_270;	break;
+	case DRM_MODE_ROTATE_180: new_rotation = DRM_MODE_ROTATE_0;	break;
+	case DRM_MODE_ROTATE_270: new_rotation = DRM_MODE_ROTATE_90;	break;
+	}
+
+	return (rotation & ~DRM_MODE_ROTATE_MASK) | new_rotation;
+}
+
 u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state,
 		  const struct intel_plane_state *plane_state);
 u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane,
diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c
index 3fca9fa39a8e..eb9010061316 100644
--- a/drivers/gpu/drm/i915/intel_fbc.c
+++ b/drivers/gpu/drm/i915/intel_fbc.c
@@ -737,7 +737,7 @@  static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
 	if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
 		cache->crtc.hsw_bdw_pixel_rate = crtc_state->pixel_rate;
 
-	cache->plane.rotation = plane_state->base.rotation;
+	cache->plane.rotation = intel_plane_get_rotation(&plane_state->base);
 	/*
 	 * Src coordinates are already rotated by 270 degrees for
 	 * the 90/270 degree plane rotation cases (to match the
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 68187bcfded4..0e4bbeb966ca 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -4080,6 +4080,7 @@  skl_ddb_min_alloc(const struct drm_plane_state *pstate,
 {
 	struct drm_framebuffer *fb = pstate->fb;
 	struct intel_plane_state *intel_pstate = to_intel_plane_state(pstate);
+	unsigned int rotation = intel_plane_get_rotation(pstate);
 	uint32_t src_w, src_h;
 	uint32_t min_scanlines = 8;
 	uint8_t plane_bpp;
@@ -4117,7 +4118,7 @@  skl_ddb_min_alloc(const struct drm_plane_state *pstate,
 	else
 		plane_bpp = fb->format->cpp[0];
 
-	if (drm_rotation_90_or_270(pstate->rotation)) {
+	if (drm_rotation_90_or_270(rotation)) {
 		switch (plane_bpp) {
 		case 1:
 			min_scanlines = 32;
@@ -4382,6 +4383,7 @@  static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
 	struct intel_plane *plane = to_intel_plane(intel_pstate->base.plane);
 	const struct drm_plane_state *pstate = &intel_pstate->base;
 	const struct drm_framebuffer *fb = pstate->fb;
+	unsigned int rotation = intel_plane_get_rotation(pstate);
 	uint32_t latency = dev_priv->wm.skl_latency[level];
 	uint_fixed_16_16_t method1, method2;
 	uint_fixed_16_16_t plane_blocks_per_line;
@@ -4434,7 +4436,7 @@  static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
 							fb->format->cpp[0];
 	plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, intel_pstate);
 
-	if (drm_rotation_90_or_270(pstate->rotation)) {
+	if (drm_rotation_90_or_270(rotation)) {
 
 		switch (cpp) {
 		case 1:
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index 524933b01483..ad061078fb0e 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -237,7 +237,7 @@  skl_update_plane(struct intel_plane *plane,
 	u32 plane_ctl = plane_state->ctl;
 	const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
 	u32 surf_addr = plane_state->main.offset;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	u32 stride = skl_plane_stride(fb, 0, rotation);
 	u32 aux_stride = skl_plane_stride(fb, 1, rotation);
 	int crtc_x = plane_state->base.dst.x1;
@@ -367,7 +367,7 @@  static u32 vlv_sprite_ctl(const struct intel_crtc_state *crtc_state,
 			  const struct intel_plane_state *plane_state)
 {
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
 	u32 sprctl;
 
@@ -507,7 +507,7 @@  static u32 ivb_sprite_ctl(const struct intel_crtc_state *crtc_state,
 	struct drm_i915_private *dev_priv =
 		to_i915(plane_state->base.plane->dev);
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
 	u32 sprctl;
 
@@ -647,7 +647,7 @@  static u32 g4x_sprite_ctl(const struct intel_crtc_state *crtc_state,
 	struct drm_i915_private *dev_priv =
 		to_i915(plane_state->base.plane->dev);
 	const struct drm_framebuffer *fb = plane_state->base.fb;
-	unsigned int rotation = plane_state->base.rotation;
+	unsigned int rotation = intel_plane_get_rotation(&plane_state->base);
 	const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
 	u32 dvscntr;
 
@@ -780,6 +780,7 @@  intel_check_sprite_plane(struct intel_plane *plane,
 	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
 	struct drm_framebuffer *fb = state->base.fb;
+	unsigned int rotation = intel_plane_get_rotation(&state->base);
 	int crtc_x, crtc_y;
 	unsigned int crtc_w, crtc_h;
 	uint32_t src_x, src_y, src_w, src_h;
@@ -834,8 +835,7 @@  intel_check_sprite_plane(struct intel_plane *plane,
 	 * coordinates and sizes. We probably need some way to decide whether
 	 * more strict checking should be done instead.
 	 */
-	drm_rect_rotate(src, fb->width << 16, fb->height << 16,
-			state->base.rotation);
+	drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
 
 	hscale = drm_rect_calc_hscale_relaxed(src, dst, min_scale, max_scale);
 	BUG_ON(hscale < 0);
@@ -876,7 +876,7 @@  intel_check_sprite_plane(struct intel_plane *plane,
 				     drm_rect_height(dst) * vscale - drm_rect_height(src));
 
 		drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16,
-				    state->base.rotation);
+				    rotation);
 
 		/* sanity check to make sure the src viewport wasn't enlarged */
 		WARN_ON(src->x1 < (int) state->base.src_x ||