Browse Source

Merge tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc into drm-next

UAPI Changes:
- Return -ENODEV instead of -ENXIO when creating cma fb w/o valid gem (Daniel)
- Add aspect ratio and custom scaling propertis to connector state (Maarten)

Cross-subsystem Changes:
- None

Core Changes:
- Add Laurent as bridge reviewer and Andrzej as bridge maintainer (Archit)
- Maintain new STM driver through -misc (Yannick)
- Misc doc improvements (as is tradition) (Daniel)
- Add driver-private objects to atomic state (Dhinakaran)
- Deprecate preclose hook in modern drivers (use postclose) (Daniel)
- Add hwmode to vblank struct. This fixes mode access in irq context and reduced
  a bunch of boilerplate (Daniel)

Driver Changes:
- vc4: Add out-fence support to vc4 V3D rendering (Eric)
- stm: Add stm32f429 display hw and am-480272h3tmqw-t01h panel support (Yannick)
- vc4: Remove 256MB cma limit from vc4 (Eric)
- dw-hdmi: Disable audio when inactive, instead of always enabled (Romain)
- zte: Add support for VGA to the ZTE driver (Shawn)
- i915: Track DP MST bandwidth and check it in atomic_check (Dhinakaran)
- vgem: Enable gem dmabuf import iface to facilitate ion testing (Laura)
- vc4: Add support for Cygnus (new dt compat string + couple bug fixes) (Eric)
- pl111: Add driver for pl111 CLCD display controller (Eric/Tom)
- vgem: Subclass drm_device instead of standalone platform device (Chris)

Cc: Archit Taneja <architt@codeaurora.org>
Cc: Eric Anholt <eric@anholt.net>
Cc: Yannick Fertre <yannick.fertre@st.com>
Cc: Romain Perier <romain.perier@collabora.com>
Cc: Navare, Manasi D <manasi.d.navare@intel.com>
Cc: Shawn Guo <shawn.guo@linaro.org>
Cc: Dhinakaran Pandiyan <dhinakaran.pandiyan@intel.com>
Cc: Laura Abbott <labbott@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Tom Cooksey <tom.cooksey@arm.com>
Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>

* tag 'drm-misc-next-2017-05-16' of git://anongit.freedesktop.org/git/drm-misc: (72 commits)
  drm: add missing declaration to drm_blend.h
  drm/dp: Wait up all outstanding tx waiters
  drm/dp: Read the tx msg state once after checking for an event
  drm/prime: Forward declare struct device
  drm/vblank: Lock down vblank->hwmode more
  drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
  drm/vblank: Add FIXME comments about moving the vblank ts hooks
  drm/vblank: Switch to bool in_vblank_irq in get_vblank_timestamp
  drm/vblank: Switch drm_driver->get_vblank_timestamp to return a bool
  drm/vgem: Convert to a struct drm_device subclass
  gpu: drm: gma500: remove dead code
  drm/sti: Adjust two checks for null pointers in sti_hqvdp_probe()
  drm/sti: Fix typos in a comment line
  drm/sti: Fix a typo in a comment line
  drm/sti: Replace 17 seq_puts() calls by seq_putc()
  drm/sti: Reduce function calls for sequence output at five places
  drm/sti: use seq_puts to display a string
  drm: Nerf the preclose callback for modern drivers
  drm/exynos: Merge pre/postclose hooks
  drm/tegra: switch to postclose
  ...
Dave Airlie 8 years ago
parent
commit
e98c58e55f
100 changed files with 4808 additions and 797 deletions
  1. 5 2
      Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt
  2. 36 0
      Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
  3. 21 0
      Documentation/devicetree/bindings/display/zte,vou.txt
  4. 1 0
      Documentation/gpu/index.rst
  5. 6 0
      Documentation/gpu/pl111.rst
  6. 17 0
      MAINTAINERS
  7. 3 5
      drivers/dma-buf/dma-buf.c
  8. 5 0
      drivers/dma-buf/dma-fence.c
  9. 3 3
      drivers/dma-buf/sync_debug.c
  10. 2 11
      drivers/dma-buf/sync_file.c
  11. 4 0
      drivers/gpu/drm/Kconfig
  12. 2 0
      drivers/gpu/drm/Makefile
  13. 0 4
      drivers/gpu/drm/amd/amdgpu/amdgpu.h
  14. 12 2
      drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
  15. 0 41
      drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
  16. 3 0
      drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
  17. 3 3
      drivers/gpu/drm/bridge/sii902x.c
  18. 38 8
      drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
  19. 80 7
      drivers/gpu/drm/drm_atomic.c
  20. 9 3
      drivers/gpu/drm/drm_atomic_helper.c
  21. 6 3
      drivers/gpu/drm/drm_color_mgmt.c
  22. 64 0
      drivers/gpu/drm/drm_connector.c
  23. 157 7
      drivers/gpu/drm/drm_dp_mst_topology.c
  24. 28 1
      drivers/gpu/drm/drm_fb_cma_helper.c
  25. 4 4
      drivers/gpu/drm/drm_file.c
  26. 72 57
      drivers/gpu/drm/drm_irq.c
  27. 5 6
      drivers/gpu/drm/drm_plane_helper.c
  28. 24 6
      drivers/gpu/drm/drm_prime.c
  29. 1 7
      drivers/gpu/drm/exynos/exynos_drm_drv.c
  30. 9 42
      drivers/gpu/drm/gma500/mdfld_tpo_vid.c
  31. 13 49
      drivers/gpu/drm/i915/i915_irq.c
  32. 2 9
      drivers/gpu/drm/i915/intel_display.c
  33. 42 6
      drivers/gpu/drm/i915/intel_dp_mst.c
  34. 0 1
      drivers/gpu/drm/i915/intel_drv.h
  35. 3 15
      drivers/gpu/drm/i915/intel_hdmi.c
  36. 3 20
      drivers/gpu/drm/i915/intel_sdvo.c
  37. 8 37
      drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
  38. 8 31
      drivers/gpu/drm/nouveau/nouveau_display.c
  39. 3 5
      drivers/gpu/drm/nouveau/nouveau_display.h
  40. 2 9
      drivers/gpu/drm/nouveau/nouveau_drm.c
  41. 12 0
      drivers/gpu/drm/pl111/Kconfig
  42. 5 0
      drivers/gpu/drm/pl111/Makefile
  43. 127 0
      drivers/gpu/drm/pl111/pl111_connector.c
  44. 344 0
      drivers/gpu/drm/pl111/pl111_display.c
  45. 56 0
      drivers/gpu/drm/pl111/pl111_drm.h
  46. 272 0
      drivers/gpu/drm/pl111/pl111_drv.c
  47. 12 6
      drivers/gpu/drm/radeon/radeon_drv.c
  48. 0 37
      drivers/gpu/drm/radeon/radeon_kms.c
  49. 3 0
      drivers/gpu/drm/radeon/radeon_mode.h
  50. 3 11
      drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
  51. 1 2
      drivers/gpu/drm/rockchip/rockchip_drm_drv.h
  52. 9 11
      drivers/gpu/drm/rockchip/rockchip_drm_vop.c
  53. 28 0
      drivers/gpu/drm/selftests/test-drm_mm.c
  54. 2 3
      drivers/gpu/drm/sti/sti_cursor.c
  55. 1 2
      drivers/gpu/drm/sti/sti_dvo.c
  56. 2 3
      drivers/gpu/drm/sti/sti_gdp.c
  57. 3 6
      drivers/gpu/drm/sti/sti_hda.c
  58. 10 13
      drivers/gpu/drm/sti/sti_hdmi.c
  59. 3 4
      drivers/gpu/drm/sti/sti_hqvdp.c
  60. 1 2
      drivers/gpu/drm/sti/sti_mixer.c
  61. 3 4
      drivers/gpu/drm/sti/sti_tvout.c
  62. 2 3
      drivers/gpu/drm/sti/sti_vid.c
  63. 16 0
      drivers/gpu/drm/stm/Kconfig
  64. 7 0
      drivers/gpu/drm/stm/Makefile
  65. 221 0
      drivers/gpu/drm/stm/drv.c
  66. 1160 0
      drivers/gpu/drm/stm/ltdc.c
  67. 40 0
      drivers/gpu/drm/stm/ltdc.h
  68. 2 2
      drivers/gpu/drm/tegra/drm.c
  69. 1 0
      drivers/gpu/drm/vc4/Makefile
  70. 36 1
      drivers/gpu/drm/vc4/vc4_bo.c
  71. 10 24
      drivers/gpu/drm/vc4/vc4_crtc.c
  72. 4 2
      drivers/gpu/drm/vc4/vc4_drv.c
  73. 57 13
      drivers/gpu/drm/vc4/vc4_drv.h
  74. 56 0
      drivers/gpu/drm/vc4/vc4_fence.c
  75. 150 11
      drivers/gpu/drm/vc4/vc4_gem.c
  76. 92 82
      drivers/gpu/drm/vc4/vc4_hdmi.c
  77. 32 33
      drivers/gpu/drm/vc4/vc4_irq.c
  78. 6 4
      drivers/gpu/drm/vc4/vc4_kms.c
  79. 1 2
      drivers/gpu/drm/vc4/vc4_render_cl.c
  80. 180 0
      drivers/gpu/drm/vc4/vc4_v3d.c
  81. 24 30
      drivers/gpu/drm/vc4/vc4_validate.c
  82. 152 42
      drivers/gpu/drm/vgem/vgem_drv.c
  83. 2 0
      drivers/gpu/drm/vgem/vgem_drv.h
  84. 1 0
      drivers/gpu/drm/zte/Makefile
  85. 31 0
      drivers/gpu/drm/zte/zx_common_regs.h
  86. 1 0
      drivers/gpu/drm/zte/zx_drm_drv.c
  87. 1 0
      drivers/gpu/drm/zte/zx_drm_drv.h
  88. 1 0
      drivers/gpu/drm/zte/zx_plane.c
  89. 0 18
      drivers/gpu/drm/zte/zx_plane_regs.h
  90. 531 0
      drivers/gpu/drm/zte/zx_vga.c
  91. 36 0
      drivers/gpu/drm/zte/zx_vga_regs.h
  92. 31 5
      drivers/gpu/drm/zte/zx_vou.c
  93. 10 2
      drivers/gpu/drm/zte/zx_vou_regs.h
  94. 0 9
      include/drm/drmP.h
  95. 95 0
      include/drm/drm_atomic.h
  96. 1 0
      include/drm/drm_blend.h
  97. 2 0
      include/drm/drm_color_mgmt.h
  98. 21 0
      include/drm/drm_connector.h
  99. 28 6
      include/drm/drm_crtc.h
  100. 166 0
      include/drm/drm_dp_helper.h

+ 5 - 2
Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt

@@ -5,7 +5,7 @@ with HDMI output and the HVS (Hardware Video Scaler) for compositing
 display planes.
 display planes.
 
 
 Required properties for VC4:
 Required properties for VC4:
-- compatible:	Should be "brcm,bcm2835-vc4"
+- compatible:	Should be "brcm,bcm2835-vc4" or "brcm,cygnus-vc4"
 
 
 Required properties for Pixel Valve:
 Required properties for Pixel Valve:
 - compatible:	Should be one of "brcm,bcm2835-pixelvalve0",
 - compatible:	Should be one of "brcm,bcm2835-pixelvalve0",
@@ -54,11 +54,14 @@ Required properties for VEC:
 		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
 		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
 
 
 Required properties for V3D:
 Required properties for V3D:
-- compatible:	Should be "brcm,bcm2835-v3d"
+- compatible:	Should be "brcm,bcm2835-v3d" or "brcm,cygnus-v3d"
 - reg:		Physical base address and length of the V3D's registers
 - reg:		Physical base address and length of the V3D's registers
 - interrupts:	The interrupt number
 - interrupts:	The interrupt number
 		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
 		  See bindings/interrupt-controller/brcm,bcm2835-armctrl-ic.txt
 
 
+Optional properties for V3D:
+- clocks:	The clock the unit runs on
+
 Required properties for DSI:
 Required properties for DSI:
 - compatible:	Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1"
 - compatible:	Should be "brcm,bcm2835-dsi0" or "brcm,bcm2835-dsi1"
 - reg:		Physical base address and length of the DSI block's registers
 - reg:		Physical base address and length of the DSI block's registers

+ 36 - 0
Documentation/devicetree/bindings/display/st,stm32-ltdc.txt

@@ -0,0 +1,36 @@
+* STMicroelectronics STM32 lcd-tft display controller
+
+- ltdc: lcd-tft display controller host
+  must be a sub-node of st-display-subsystem
+  Required properties:
+  - compatible: "st,stm32-ltdc"
+  - reg: Physical base address of the IP registers and length of memory mapped region.
+  - clocks: A list of phandle + clock-specifier pairs, one for each
+    entry in 'clock-names'.
+  - clock-names: A list of clock names. For ltdc it should contain:
+      - "lcd" for the clock feeding the output pixel clock & IP clock.
+  - resets: reset to be used by the device (defined by use of RCC macro).
+  Required nodes:
+    - Video port for RGB output.
+
+Example:
+
+/ {
+	...
+	soc {
+	...
+		ltdc: display-controller@40016800 {
+			compatible = "st,stm32-ltdc";
+			reg = <0x40016800 0x200>;
+			interrupts = <88>, <89>;
+			resets = <&rcc STM32F4_APB2_RESET(LTDC)>;
+			clocks = <&rcc 1 CLK_LCD>;
+			clock-names = "lcd";
+
+			port {
+				ltdc_out_rgb: endpoint {
+				};
+			};
+		};
+	};
+};

+ 21 - 0
Documentation/devicetree/bindings/display/zte,vou.txt

@@ -58,6 +58,18 @@ Required properties:
    integer cells.  The first cell is the offset of SYSCTRL register used
    integer cells.  The first cell is the offset of SYSCTRL register used
    to control TV Encoder DAC power, and the second cell is the bit mask.
    to control TV Encoder DAC power, and the second cell is the bit mask.
 
 
+* VGA output device
+
+Required properties:
+ - compatible: should be "zte,zx296718-vga"
+ - reg: Physical base address and length of the VGA device IO region
+ - interrupts : VGA interrupt number to CPU
+ - clocks: Phandle with clock-specifier pointing to VGA I2C clock.
+ - clock-names: Must be "i2c_wclk".
+ - zte,vga-power-control: the phandle to SYSCTRL block followed by two
+   integer cells.  The first cell is the offset of SYSCTRL register used
+   to control VGA DAC power, and the second cell is the bit mask.
+
 Example:
 Example:
 
 
 vou: vou@1440000 {
 vou: vou@1440000 {
@@ -81,6 +93,15 @@ vou: vou@1440000 {
 			      "main_wclk", "aux_wclk";
 			      "main_wclk", "aux_wclk";
 	};
 	};
 
 
+	vga: vga@8000 {
+		compatible = "zte,zx296718-vga";
+		reg = <0x8000 0x1000>;
+		interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&topcrm VGA_I2C_WCLK>;
+		clock-names = "i2c_wclk";
+		zte,vga-power-control = <&sysctrl 0x170 0xe0>;
+	};
+
 	hdmi: hdmi@c000 {
 	hdmi: hdmi@c000 {
 		compatible = "zte,zx296718-hdmi";
 		compatible = "zte,zx296718-hdmi";
 		reg = <0xc000 0x4000>;
 		reg = <0xc000 0x4000>;

+ 1 - 0
Documentation/gpu/index.rst

@@ -12,6 +12,7 @@ Linux GPU Driver Developer's Guide
    drm-uapi
    drm-uapi
    i915
    i915
    meson
    meson
+   pl111
    tinydrm
    tinydrm
    vc4
    vc4
    vga-switcheroo
    vga-switcheroo

+ 6 - 0
Documentation/gpu/pl111.rst

@@ -0,0 +1,6 @@
+==========================================
+ drm/pl111 ARM PrimeCell PL111 CLCD Driver
+==========================================
+
+.. kernel-doc:: drivers/gpu/drm/pl111/pl111_drv.c
+   :doc: ARM PrimeCell PL111 CLCD Driver

+ 17 - 0
MAINTAINERS

@@ -4228,6 +4228,12 @@ F:	include/drm/drm*
 F:	include/uapi/drm/drm*
 F:	include/uapi/drm/drm*
 F:	include/linux/vga*
 F:	include/linux/vga*
 
 
+DRM DRIVER FOR ARM PL111 CLCD
+M:	Eric Anholt <eric@anholt.net>
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+S:	Supported
+F:	drivers/gpu/drm/pl111/
+
 DRM DRIVER FOR AST SERVER GRAPHICS CHIPS
 DRM DRIVER FOR AST SERVER GRAPHICS CHIPS
 M:	Dave Airlie <airlied@redhat.com>
 M:	Dave Airlie <airlied@redhat.com>
 S:	Odd Fixes
 S:	Odd Fixes
@@ -4235,6 +4241,8 @@ F:	drivers/gpu/drm/ast/
 
 
 DRM DRIVERS FOR BRIDGE CHIPS
 DRM DRIVERS FOR BRIDGE CHIPS
 M:	Archit Taneja <architt@codeaurora.org>
 M:	Archit Taneja <architt@codeaurora.org>
+M:	Andrzej Hajda <a.hajda@samsung.com>
+R:	Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
 S:	Maintained
 S:	Maintained
 T:	git git://anongit.freedesktop.org/drm/drm-misc
 T:	git git://anongit.freedesktop.org/drm/drm-misc
 F:	drivers/gpu/drm/bridge/
 F:	drivers/gpu/drm/bridge/
@@ -4491,6 +4499,15 @@ S:	Maintained
 F:	drivers/gpu/drm/sti
 F:	drivers/gpu/drm/sti
 F:	Documentation/devicetree/bindings/display/st,stih4xx.txt
 F:	Documentation/devicetree/bindings/display/st,stih4xx.txt
 
 
+DRM DRIVERS FOR STM
+M:	Yannick Fertre <yannick.fertre@st.com>
+M:	Philippe Cornu <philippe.cornu@st.com>
+L:	dri-devel@lists.freedesktop.org
+T:	git git://anongit.freedesktop.org/drm/drm-misc
+S:	Maintained
+F:	drivers/gpu/drm/stm
+F:	Documentation/devicetree/bindings/display/st,stm32-ltdc.txt
+
 DRM DRIVER FOR TDFX VIDEO CARDS
 DRM DRIVER FOR TDFX VIDEO CARDS
 S:	Orphan / Obsolete
 S:	Orphan / Obsolete
 F:	drivers/gpu/drm/tdfx/
 F:	drivers/gpu/drm/tdfx/

+ 3 - 5
drivers/dma-buf/dma-buf.c

@@ -558,8 +558,8 @@ struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf,
 	if (WARN_ON(!dmabuf || !dev))
 	if (WARN_ON(!dmabuf || !dev))
 		return ERR_PTR(-EINVAL);
 		return ERR_PTR(-EINVAL);
 
 
-	attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL);
-	if (attach == NULL)
+	attach = kzalloc(sizeof(*attach), GFP_KERNEL);
+	if (!attach)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 
 
 	attach->dev = dev;
 	attach->dev = dev;
@@ -1122,9 +1122,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused)
 		attach_count = 0;
 		attach_count = 0;
 
 
 		list_for_each_entry(attach_obj, &buf_obj->attachments, node) {
 		list_for_each_entry(attach_obj, &buf_obj->attachments, node) {
-			seq_puts(s, "\t");
-
-			seq_printf(s, "%s\n", dev_name(attach_obj->dev));
+			seq_printf(s, "\t%s\n", dev_name(attach_obj->dev));
 			attach_count++;
 			attach_count++;
 		}
 		}
 
 

+ 5 - 0
drivers/dma-buf/dma-fence.c

@@ -402,6 +402,11 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout)
 		}
 		}
 	}
 	}
 
 
+	if (!timeout) {
+		ret = 0;
+		goto out;
+	}
+
 	cb.base.func = dma_fence_default_wait_cb;
 	cb.base.func = dma_fence_default_wait_cb;
 	cb.task = current;
 	cb.task = current;
 	list_add(&cb.base.node, &fence->cb_list);
 	list_add(&cb.base.node, &fence->cb_list);

+ 3 - 3
drivers/dma-buf/sync_debug.c

@@ -110,7 +110,7 @@ static void sync_print_fence(struct seq_file *s,
 		}
 		}
 	}
 	}
 
 
-	seq_puts(s, "\n");
+	seq_putc(s, '\n');
 }
 }
 
 
 static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
 static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
@@ -161,7 +161,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused)
 				     sync_timeline_list);
 				     sync_timeline_list);
 
 
 		sync_print_obj(s, obj);
 		sync_print_obj(s, obj);
-		seq_puts(s, "\n");
+		seq_putc(s, '\n');
 	}
 	}
 	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
 	spin_unlock_irqrestore(&sync_timeline_list_lock, flags);
 
 
@@ -173,7 +173,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused)
 			container_of(pos, struct sync_file, sync_file_list);
 			container_of(pos, struct sync_file, sync_file_list);
 
 
 		sync_print_sync_file(s, sync_file);
 		sync_print_sync_file(s, sync_file);
-		seq_puts(s, "\n");
+		seq_putc(s, '\n');
 	}
 	}
 	spin_unlock_irqrestore(&sync_file_list_lock, flags);
 	spin_unlock_irqrestore(&sync_file_list_lock, flags);
 	return 0;
 	return 0;

+ 2 - 11
drivers/dma-buf/sync_file.c

@@ -41,8 +41,6 @@ static struct sync_file *sync_file_alloc(void)
 	if (IS_ERR(sync_file->file))
 	if (IS_ERR(sync_file->file))
 		goto err;
 		goto err;
 
 
-	kref_init(&sync_file->kref);
-
 	init_waitqueue_head(&sync_file->wq);
 	init_waitqueue_head(&sync_file->wq);
 
 
 	INIT_LIST_HEAD(&sync_file->cb.node);
 	INIT_LIST_HEAD(&sync_file->cb.node);
@@ -277,22 +275,15 @@ err:
 
 
 }
 }
 
 
-static void sync_file_free(struct kref *kref)
+static int sync_file_release(struct inode *inode, struct file *file)
 {
 {
-	struct sync_file *sync_file = container_of(kref, struct sync_file,
-						     kref);
+	struct sync_file *sync_file = file->private_data;
 
 
 	if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
 	if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
 		dma_fence_remove_callback(sync_file->fence, &sync_file->cb);
 		dma_fence_remove_callback(sync_file->fence, &sync_file->cb);
 	dma_fence_put(sync_file->fence);
 	dma_fence_put(sync_file->fence);
 	kfree(sync_file);
 	kfree(sync_file);
-}
-
-static int sync_file_release(struct inode *inode, struct file *file)
-{
-	struct sync_file *sync_file = file->private_data;
 
 
-	kref_put(&sync_file->kref, sync_file_free);
 	return 0;
 	return 0;
 }
 }
 
 

+ 4 - 0
drivers/gpu/drm/Kconfig

@@ -246,6 +246,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig"
 
 
 source "drivers/gpu/drm/tegra/Kconfig"
 source "drivers/gpu/drm/tegra/Kconfig"
 
 
+source "drivers/gpu/drm/stm/Kconfig"
+
 source "drivers/gpu/drm/panel/Kconfig"
 source "drivers/gpu/drm/panel/Kconfig"
 
 
 source "drivers/gpu/drm/bridge/Kconfig"
 source "drivers/gpu/drm/bridge/Kconfig"
@@ -274,6 +276,8 @@ source "drivers/gpu/drm/meson/Kconfig"
 
 
 source "drivers/gpu/drm/tinydrm/Kconfig"
 source "drivers/gpu/drm/tinydrm/Kconfig"
 
 
+source "drivers/gpu/drm/pl111/Kconfig"
+
 # Keep legacy drivers last
 # Keep legacy drivers last
 
 
 menuconfig DRM_LEGACY
 menuconfig DRM_LEGACY

+ 2 - 0
drivers/gpu/drm/Makefile

@@ -82,6 +82,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/
 obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
 obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_MSM) += msm/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
 obj-$(CONFIG_DRM_TEGRA) += tegra/
+obj-$(CONFIG_DRM_STM) += stm/
 obj-$(CONFIG_DRM_STI) += sti/
 obj-$(CONFIG_DRM_STI) += sti/
 obj-$(CONFIG_DRM_IMX) += imx/
 obj-$(CONFIG_DRM_IMX) += imx/
 obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
 obj-$(CONFIG_DRM_MEDIATEK) += mediatek/
@@ -96,3 +97,4 @@ obj-y			+= hisilicon/
 obj-$(CONFIG_DRM_ZTE)	+= zte/
 obj-$(CONFIG_DRM_ZTE)	+= zte/
 obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
 obj-$(CONFIG_DRM_MXSFB)	+= mxsfb/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm/
+obj-$(CONFIG_DRM_PL111) += pl111/

+ 0 - 4
drivers/gpu/drm/amd/amdgpu/amdgpu.h

@@ -1912,10 +1912,6 @@ int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon);
 u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
-int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
-				    int *max_error,
-				    struct timeval *vblank_time,
-				    unsigned flags);
 long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
 long amdgpu_kms_compat_ioctl(struct file *filp, unsigned int cmd,
 			     unsigned long arg);
 			     unsigned long arg);
 
 

+ 12 - 2
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c

@@ -715,6 +715,16 @@ static const struct file_operations amdgpu_driver_kms_fops = {
 #endif
 #endif
 };
 };
 
 
+static bool
+amdgpu_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
+				 bool in_vblank_irq, int *vpos, int *hpos,
+				 ktime_t *stime, ktime_t *etime,
+				 const struct drm_display_mode *mode)
+{
+	return amdgpu_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
+					  stime, etime, mode);
+}
+
 static struct drm_driver kms_driver = {
 static struct drm_driver kms_driver = {
 	.driver_features =
 	.driver_features =
 	    DRIVER_USE_AGP |
 	    DRIVER_USE_AGP |
@@ -729,8 +739,8 @@ static struct drm_driver kms_driver = {
 	.get_vblank_counter = amdgpu_get_vblank_counter_kms,
 	.get_vblank_counter = amdgpu_get_vblank_counter_kms,
 	.enable_vblank = amdgpu_enable_vblank_kms,
 	.enable_vblank = amdgpu_enable_vblank_kms,
 	.disable_vblank = amdgpu_disable_vblank_kms,
 	.disable_vblank = amdgpu_disable_vblank_kms,
-	.get_vblank_timestamp = amdgpu_get_vblank_timestamp_kms,
-	.get_scanout_position = amdgpu_get_crtc_scanoutpos,
+	.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
+	.get_scanout_position = amdgpu_get_crtc_scanout_position,
 #if defined(CONFIG_DEBUG_FS)
 #if defined(CONFIG_DEBUG_FS)
 	.debugfs_init = amdgpu_debugfs_init,
 	.debugfs_init = amdgpu_debugfs_init,
 #endif
 #endif

+ 0 - 41
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c

@@ -945,47 +945,6 @@ void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe)
 	amdgpu_irq_put(adev, &adev->crtc_irq, idx);
 	amdgpu_irq_put(adev, &adev->crtc_irq, idx);
 }
 }
 
 
-/**
- * amdgpu_get_vblank_timestamp_kms - get vblank timestamp
- *
- * @dev: drm dev pointer
- * @crtc: crtc to get the timestamp for
- * @max_error: max error
- * @vblank_time: time value
- * @flags: flags passed to the driver
- *
- * Gets the timestamp on the requested crtc based on the
- * scanout position.  (all asics).
- * Returns postive status flags on success, negative error on failure.
- */
-int amdgpu_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
-				    int *max_error,
-				    struct timeval *vblank_time,
-				    unsigned flags)
-{
-	struct drm_crtc *crtc;
-	struct amdgpu_device *adev = dev->dev_private;
-
-	if (pipe >= dev->num_crtcs) {
-		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return -EINVAL;
-	}
-
-	/* Get associated drm_crtc: */
-	crtc = &adev->mode_info.crtcs[pipe]->base;
-	if (!crtc) {
-		/* This can occur on driver load if some component fails to
-		 * initialize completely and driver is unloaded */
-		DRM_ERROR("Uninitialized crtc %d\n", pipe);
-		return -EINVAL;
-	}
-
-	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-						     vblank_time, flags,
-						     &crtc->hwmode);
-}
-
 const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
 const struct drm_ioctl_desc amdgpu_ioctls_kms[] = {
 	DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),
 	DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW),

+ 3 - 0
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h

@@ -534,6 +534,9 @@ struct amdgpu_framebuffer {
 				((em) == ATOM_ENCODER_MODE_DP_MST))
 				((em) == ATOM_ENCODER_MODE_DP_MST))
 
 
 /* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
 /* Driver internal use only flags of amdgpu_get_crtc_scanoutpos() */
+#define DRM_SCANOUTPOS_VALID        (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
 #define USE_REAL_VBLANKSTART		(1 << 30)
 #define USE_REAL_VBLANKSTART		(1 << 30)
 #define GET_DISTANCE_TO_VBLANKSTART	(1 << 31)
 #define GET_DISTANCE_TO_VBLANKSTART	(1 << 31)
 
 

+ 3 - 3
drivers/gpu/drm/bridge/sii902x.c

@@ -160,7 +160,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
 		 time_before(jiffies, timeout));
 		 time_before(jiffies, timeout));
 
 
 	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
 	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-		dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus");
+		dev_err(&sii902x->i2c->dev, "failed to acquire the i2c bus\n");
 		return -ETIMEDOUT;
 		return -ETIMEDOUT;
 	}
 	}
 
 
@@ -202,7 +202,7 @@ static int sii902x_get_modes(struct drm_connector *connector)
 
 
 	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
 	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
 		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
 		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-		dev_err(&sii902x->i2c->dev, "failed to release the i2c bus");
+		dev_err(&sii902x->i2c->dev, "failed to release the i2c bus\n");
 		return -ETIMEDOUT;
 		return -ETIMEDOUT;
 	}
 	}
 
 
@@ -298,7 +298,7 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge)
 
 
 	if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
 	if (!drm_core_check_feature(drm, DRIVER_ATOMIC)) {
 		dev_err(&sii902x->i2c->dev,
 		dev_err(&sii902x->i2c->dev,
-			"sii902x driver is only compatible with DRM devices supporting atomic updates");
+			"sii902x driver is only compatible with DRM devices supporting atomic updates\n");
 		return -ENOTSUPP;
 		return -ENOTSUPP;
 	}
 	}
 
 

+ 38 - 8
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c

@@ -173,6 +173,8 @@ struct dw_hdmi {
 
 
 	unsigned int reg_shift;
 	unsigned int reg_shift;
 	struct regmap *regm;
 	struct regmap *regm;
+	void (*enable_audio)(struct dw_hdmi *hdmi);
+	void (*disable_audio)(struct dw_hdmi *hdmi);
 };
 };
 
 
 #define HDMI_IH_PHY_STAT0_RX_SENSE \
 #define HDMI_IH_PHY_STAT0_RX_SENSE \
@@ -542,13 +544,41 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate)
 }
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
 EXPORT_SYMBOL_GPL(dw_hdmi_set_sample_rate);
 
 
+static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi, bool enable)
+{
+	hdmi_modb(hdmi, enable ? 0 : HDMI_MC_CLKDIS_AUDCLK_DISABLE,
+		  HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+}
+
+static void dw_hdmi_ahb_audio_enable(struct dw_hdmi *hdmi)
+{
+	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+}
+
+static void dw_hdmi_ahb_audio_disable(struct dw_hdmi *hdmi)
+{
+	hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+}
+
+static void dw_hdmi_i2s_audio_enable(struct dw_hdmi *hdmi)
+{
+	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+	hdmi_enable_audio_clk(hdmi, true);
+}
+
+static void dw_hdmi_i2s_audio_disable(struct dw_hdmi *hdmi)
+{
+	hdmi_enable_audio_clk(hdmi, false);
+}
+
 void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
 void dw_hdmi_audio_enable(struct dw_hdmi *hdmi)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 
 
 	spin_lock_irqsave(&hdmi->audio_lock, flags);
 	spin_lock_irqsave(&hdmi->audio_lock, flags);
 	hdmi->audio_enable = true;
 	hdmi->audio_enable = true;
-	hdmi_set_cts_n(hdmi, hdmi->audio_cts, hdmi->audio_n);
+	if (hdmi->enable_audio)
+		hdmi->enable_audio(hdmi);
 	spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 	spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 }
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_enable);
@@ -559,7 +589,8 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi)
 
 
 	spin_lock_irqsave(&hdmi->audio_lock, flags);
 	spin_lock_irqsave(&hdmi->audio_lock, flags);
 	hdmi->audio_enable = false;
 	hdmi->audio_enable = false;
-	hdmi_set_cts_n(hdmi, hdmi->audio_cts, 0);
+	if (hdmi->disable_audio)
+		hdmi->disable_audio(hdmi);
 	spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 	spin_unlock_irqrestore(&hdmi->audio_lock, flags);
 }
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
 EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable);
@@ -1573,11 +1604,6 @@ static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
 			    HDMI_MC_FLOWCTRL);
 			    HDMI_MC_FLOWCTRL);
 }
 }
 
 
-static void hdmi_enable_audio_clk(struct dw_hdmi *hdmi)
-{
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
-}
-
 /* Workaround to clear the overflow condition */
 /* Workaround to clear the overflow condition */
 static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
 static void dw_hdmi_clear_overflow(struct dw_hdmi *hdmi)
 {
 {
@@ -1691,7 +1717,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
 
 
 		/* HDMI Initialization Step E - Configure audio */
 		/* HDMI Initialization Step E - Configure audio */
 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
 		hdmi_clk_regenerator_update_pixel_clock(hdmi);
-		hdmi_enable_audio_clk(hdmi);
+		hdmi_enable_audio_clk(hdmi, true);
 	}
 	}
 
 
 	/* not for DVI mode */
 	/* not for DVI mode */
@@ -2403,6 +2429,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
 		audio.irq = irq;
 		audio.irq = irq;
 		audio.hdmi = hdmi;
 		audio.hdmi = hdmi;
 		audio.eld = hdmi->connector.eld;
 		audio.eld = hdmi->connector.eld;
+		hdmi->enable_audio = dw_hdmi_ahb_audio_enable;
+		hdmi->disable_audio = dw_hdmi_ahb_audio_disable;
 
 
 		pdevinfo.name = "dw-hdmi-ahb-audio";
 		pdevinfo.name = "dw-hdmi-ahb-audio";
 		pdevinfo.data = &audio;
 		pdevinfo.data = &audio;
@@ -2415,6 +2443,8 @@ __dw_hdmi_probe(struct platform_device *pdev,
 		audio.hdmi	= hdmi;
 		audio.hdmi	= hdmi;
 		audio.write	= hdmi_writeb;
 		audio.write	= hdmi_writeb;
 		audio.read	= hdmi_readb;
 		audio.read	= hdmi_readb;
+		hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
+		hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
 
 
 		pdevinfo.name = "dw-hdmi-i2s-audio";
 		pdevinfo.name = "dw-hdmi-i2s-audio";
 		pdevinfo.data = &audio;
 		pdevinfo.data = &audio;

+ 80 - 7
drivers/gpu/drm/drm_atomic.c

@@ -57,6 +57,7 @@ void drm_atomic_state_default_release(struct drm_atomic_state *state)
 	kfree(state->connectors);
 	kfree(state->connectors);
 	kfree(state->crtcs);
 	kfree(state->crtcs);
 	kfree(state->planes);
 	kfree(state->planes);
+	kfree(state->private_objs);
 }
 }
 EXPORT_SYMBOL(drm_atomic_state_default_release);
 EXPORT_SYMBOL(drm_atomic_state_default_release);
 
 
@@ -184,6 +185,17 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
 		state->planes[i].ptr = NULL;
 		state->planes[i].ptr = NULL;
 		state->planes[i].state = NULL;
 		state->planes[i].state = NULL;
 	}
 	}
+
+	for (i = 0; i < state->num_private_objs; i++) {
+		void *obj_state = state->private_objs[i].obj_state;
+
+		state->private_objs[i].funcs->destroy_state(obj_state);
+		state->private_objs[i].obj = NULL;
+		state->private_objs[i].obj_state = NULL;
+		state->private_objs[i].funcs = NULL;
+	}
+	state->num_private_objs = 0;
+
 }
 }
 EXPORT_SYMBOL(drm_atomic_state_default_clear);
 EXPORT_SYMBOL(drm_atomic_state_default_clear);
 
 
@@ -425,7 +437,7 @@ drm_atomic_replace_property_blob(struct drm_property_blob **blob,
 }
 }
 
 
 static int
 static int
-drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
+drm_atomic_replace_property_blob_from_id(struct drm_device *dev,
 					 struct drm_property_blob **blob,
 					 struct drm_property_blob **blob,
 					 uint64_t blob_id,
 					 uint64_t blob_id,
 					 ssize_t expected_size,
 					 ssize_t expected_size,
@@ -434,7 +446,7 @@ drm_atomic_replace_property_blob_from_id(struct drm_crtc *crtc,
 	struct drm_property_blob *new_blob = NULL;
 	struct drm_property_blob *new_blob = NULL;
 
 
 	if (blob_id != 0) {
 	if (blob_id != 0) {
-		new_blob = drm_property_lookup_blob(crtc->dev, blob_id);
+		new_blob = drm_property_lookup_blob(dev, blob_id);
 		if (new_blob == NULL)
 		if (new_blob == NULL)
 			return -EINVAL;
 			return -EINVAL;
 
 
@@ -483,7 +495,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
 		drm_property_blob_put(mode);
 		drm_property_blob_put(mode);
 		return ret;
 		return ret;
 	} else if (property == config->degamma_lut_property) {
 	} else if (property == config->degamma_lut_property) {
-		ret = drm_atomic_replace_property_blob_from_id(crtc,
+		ret = drm_atomic_replace_property_blob_from_id(dev,
 					&state->degamma_lut,
 					&state->degamma_lut,
 					val,
 					val,
 					-1,
 					-1,
@@ -491,7 +503,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
 		state->color_mgmt_changed |= replaced;
 		state->color_mgmt_changed |= replaced;
 		return ret;
 		return ret;
 	} else if (property == config->ctm_property) {
 	} else if (property == config->ctm_property) {
-		ret = drm_atomic_replace_property_blob_from_id(crtc,
+		ret = drm_atomic_replace_property_blob_from_id(dev,
 					&state->ctm,
 					&state->ctm,
 					val,
 					val,
 					sizeof(struct drm_color_ctm),
 					sizeof(struct drm_color_ctm),
@@ -499,7 +511,7 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
 		state->color_mgmt_changed |= replaced;
 		state->color_mgmt_changed |= replaced;
 		return ret;
 		return ret;
 	} else if (property == config->gamma_lut_property) {
 	} else if (property == config->gamma_lut_property) {
-		ret = drm_atomic_replace_property_blob_from_id(crtc,
+		ret = drm_atomic_replace_property_blob_from_id(dev,
 					&state->gamma_lut,
 					&state->gamma_lut,
 					val,
 					val,
 					-1,
 					-1,
@@ -977,6 +989,59 @@ static void drm_atomic_plane_print_state(struct drm_printer *p,
 		plane->funcs->atomic_print_state(p, state);
 		plane->funcs->atomic_print_state(p, state);
 }
 }
 
 
+/**
+ * drm_atomic_get_private_obj_state - get private object state
+ * @state: global atomic state
+ * @obj: private object to get the state for
+ * @funcs: pointer to the struct of function pointers that identify the object
+ * type
+ *
+ * This function returns the private object state for the given private object,
+ * allocating the state if needed. It does not grab any locks as the caller is
+ * expected to care of any required locking.
+ *
+ * RETURNS:
+ *
+ * Either the allocated state or the error code encoded into a pointer.
+ */
+void *
+drm_atomic_get_private_obj_state(struct drm_atomic_state *state, void *obj,
+			      const struct drm_private_state_funcs *funcs)
+{
+	int index, num_objs, i;
+	size_t size;
+	struct __drm_private_objs_state *arr;
+
+	for (i = 0; i < state->num_private_objs; i++)
+		if (obj == state->private_objs[i].obj &&
+		    state->private_objs[i].obj_state)
+			return state->private_objs[i].obj_state;
+
+	num_objs = state->num_private_objs + 1;
+	size = sizeof(*state->private_objs) * num_objs;
+	arr = krealloc(state->private_objs, size, GFP_KERNEL);
+	if (!arr)
+		return ERR_PTR(-ENOMEM);
+
+	state->private_objs = arr;
+	index = state->num_private_objs;
+	memset(&state->private_objs[index], 0, sizeof(*state->private_objs));
+
+	state->private_objs[index].obj_state = funcs->duplicate_state(state, obj);
+	if (!state->private_objs[index].obj_state)
+		return ERR_PTR(-ENOMEM);
+
+	state->private_objs[index].obj = obj;
+	state->private_objs[index].funcs = funcs;
+	state->num_private_objs = num_objs;
+
+	DRM_DEBUG_ATOMIC("Added new private object state %p to %p\n",
+			 state->private_objs[index].obj_state, state);
+
+	return state->private_objs[index].obj_state;
+}
+EXPORT_SYMBOL(drm_atomic_get_private_obj_state);
+
 /**
 /**
  * drm_atomic_get_connector_state - get connector state
  * drm_atomic_get_connector_state - get connector state
  * @state: global atomic state object
  * @state: global atomic state object
@@ -1123,6 +1188,10 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
 		 */
 		 */
 		if (state->link_status != DRM_LINK_STATUS_GOOD)
 		if (state->link_status != DRM_LINK_STATUS_GOOD)
 			state->link_status = val;
 			state->link_status = val;
+	} else if (property == config->aspect_ratio_property) {
+		state->picture_aspect_ratio = val;
+	} else if (property == connector->scaling_mode_property) {
+		state->scaling_mode = val;
 	} else if (connector->funcs->atomic_set_property) {
 	} else if (connector->funcs->atomic_set_property) {
 		return connector->funcs->atomic_set_property(connector,
 		return connector->funcs->atomic_set_property(connector,
 				state, property, val);
 				state, property, val);
@@ -1199,6 +1268,10 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
 		*val = state->tv.hue;
 		*val = state->tv.hue;
 	} else if (property == config->link_status_property) {
 	} else if (property == config->link_status_property) {
 		*val = state->link_status;
 		*val = state->link_status;
+	} else if (property == config->aspect_ratio_property) {
+		*val = state->picture_aspect_ratio;
+	} else if (property == connector->scaling_mode_property) {
+		*val = state->scaling_mode;
 	} else if (connector->funcs->atomic_get_property) {
 	} else if (connector->funcs->atomic_get_property) {
 		return connector->funcs->atomic_get_property(connector,
 		return connector->funcs->atomic_get_property(connector,
 				state, property, val);
 				state, property, val);
@@ -1618,7 +1691,7 @@ int drm_atomic_commit(struct drm_atomic_state *state)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	DRM_DEBUG_ATOMIC("commiting %p\n", state);
+	DRM_DEBUG_ATOMIC("committing %p\n", state);
 
 
 	return config->funcs->atomic_commit(state->dev, state, false);
 	return config->funcs->atomic_commit(state->dev, state, false);
 }
 }
@@ -1647,7 +1720,7 @@ int drm_atomic_nonblocking_commit(struct drm_atomic_state *state)
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	DRM_DEBUG_ATOMIC("commiting %p nonblocking\n", state);
+	DRM_DEBUG_ATOMIC("committing %p nonblocking\n", state);
 
 
 	return config->funcs->atomic_commit(state->dev, state, true);
 	return config->funcs->atomic_commit(state->dev, state, true);
 }
 }

+ 9 - 3
drivers/gpu/drm/drm_atomic_helper.c

@@ -1070,8 +1070,8 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
  *
  *
  * Note that @pre_swap is needed since the point where we block for fences moves
  * Note that @pre_swap is needed since the point where we block for fences moves
  * around depending upon whether an atomic commit is blocking or
  * around depending upon whether an atomic commit is blocking or
- * non-blocking. For async commit all waiting needs to happen after
- * drm_atomic_helper_swap_state() is called, but for synchronous commits we want
+ * non-blocking. For non-blocking commit all waiting needs to happen after
+ * drm_atomic_helper_swap_state() is called, but for blocking commits we want
  * to wait **before** we do anything that can't be easily rolled back. That is
  * to wait **before** we do anything that can't be easily rolled back. That is
  * before we call drm_atomic_helper_swap_state().
  * before we call drm_atomic_helper_swap_state().
  *
  *
@@ -2032,6 +2032,8 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
 	struct drm_plane *plane;
 	struct drm_plane *plane;
 	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct drm_plane_state *old_plane_state, *new_plane_state;
 	struct drm_crtc_commit *commit;
 	struct drm_crtc_commit *commit;
+	void *obj, *obj_state;
+	const struct drm_private_state_funcs *funcs;
 
 
 	if (stall) {
 	if (stall) {
 		for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
 		for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
@@ -2092,6 +2094,9 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
 		state->planes[i].state = old_plane_state;
 		state->planes[i].state = old_plane_state;
 		plane->state = new_plane_state;
 		plane->state = new_plane_state;
 	}
 	}
+
+	__for_each_private_obj(state, obj, obj_state, i, funcs)
+		funcs->swap_state(obj, &state->private_objs[i].obj_state);
 }
 }
 EXPORT_SYMBOL(drm_atomic_helper_swap_state);
 EXPORT_SYMBOL(drm_atomic_helper_swap_state);
 
 
@@ -3517,7 +3522,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state);
  *
  *
  * Implements support for legacy gamma correction table for drivers
  * Implements support for legacy gamma correction table for drivers
  * that support color management through the DEGAMMA_LUT/GAMMA_LUT
  * that support color management through the DEGAMMA_LUT/GAMMA_LUT
- * properties.
+ * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for
+ * how the atomic color management and gamma tables work.
  */
  */
 int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
 int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
 				       u16 *red, u16 *green, u16 *blue,
 				       u16 *red, u16 *green, u16 *blue,

+ 6 - 3
drivers/gpu/drm/drm_color_mgmt.c

@@ -43,7 +43,8 @@
  *
  *
  *	Setting this to NULL (blob property value set to 0) means a
  *	Setting this to NULL (blob property value set to 0) means a
  *	linear/pass-thru gamma table should be used. This is generally the
  *	linear/pass-thru gamma table should be used. This is generally the
- *	driver boot-up state too.
+ *	driver boot-up state too. Drivers can access this blob through
+ *	&drm_crtc_state.degamma_lut.
  *
  *
  * “DEGAMMA_LUT_SIZE”:
  * “DEGAMMA_LUT_SIZE”:
  *	Unsinged range property to give the size of the lookup table to be set
  *	Unsinged range property to give the size of the lookup table to be set
@@ -60,7 +61,8 @@
  *
  *
  *	Setting this to NULL (blob property value set to 0) means a
  *	Setting this to NULL (blob property value set to 0) means a
  *	unit/pass-thru matrix should be used. This is generally the driver
  *	unit/pass-thru matrix should be used. This is generally the driver
- *	boot-up state too.
+ *	boot-up state too. Drivers can access the blob for the color conversion
+ *	matrix through &drm_crtc_state.ctm.
  *
  *
  * “GAMMA_LUT”:
  * “GAMMA_LUT”:
  *	Blob property to set the gamma lookup table (LUT) mapping pixel data
  *	Blob property to set the gamma lookup table (LUT) mapping pixel data
@@ -72,7 +74,8 @@
  *
  *
  *	Setting this to NULL (blob property value set to 0) means a
  *	Setting this to NULL (blob property value set to 0) means a
  *	linear/pass-thru gamma table should be used. This is generally the
  *	linear/pass-thru gamma table should be used. This is generally the
- *	driver boot-up state too.
+ *	driver boot-up state too. Drivers can access this blob through
+ *	&drm_crtc_state.gamma_lut.
  *
  *
  * “GAMMA_LUT_SIZE”:
  * “GAMMA_LUT_SIZE”:
  *	Unsigned range property to give the size of the lookup table to be set
  *	Unsigned range property to give the size of the lookup table to be set

+ 64 - 0
drivers/gpu/drm/drm_connector.c

@@ -941,6 +941,10 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
  *
  *
  * Called by a driver the first time it's needed, must be attached to desired
  * Called by a driver the first time it's needed, must be attached to desired
  * connectors.
  * connectors.
+ *
+ * Atomic drivers should use drm_connector_attach_scaling_mode_property()
+ * instead to correctly assign &drm_connector_state.picture_aspect_ratio
+ * in the atomic state.
  */
  */
 int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 {
 {
@@ -960,6 +964,66 @@ int drm_mode_create_scaling_mode_property(struct drm_device *dev)
 }
 }
 EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
 EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
 
 
+/**
+ * drm_connector_attach_scaling_mode_property - attach atomic scaling mode property
+ * @connector: connector to attach scaling mode property on.
+ * @scaling_mode_mask: or'ed mask of BIT(%DRM_MODE_SCALE_\*).
+ *
+ * This is used to add support for scaling mode to atomic drivers.
+ * The scaling mode will be set to &drm_connector_state.picture_aspect_ratio
+ * and can be used from &drm_connector_helper_funcs->atomic_check for validation.
+ *
+ * This is the atomic version of drm_mode_create_scaling_mode_property().
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
+					       u32 scaling_mode_mask)
+{
+	struct drm_device *dev = connector->dev;
+	struct drm_property *scaling_mode_property;
+	int i, j = 0;
+	const unsigned valid_scaling_mode_mask =
+		(1U << ARRAY_SIZE(drm_scaling_mode_enum_list)) - 1;
+
+	if (WARN_ON(hweight32(scaling_mode_mask) < 2 ||
+		    scaling_mode_mask & ~valid_scaling_mode_mask))
+		return -EINVAL;
+
+	scaling_mode_property =
+		drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+				    hweight32(scaling_mode_mask));
+
+	if (!scaling_mode_property)
+		return -ENOMEM;
+
+	for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) {
+		int ret;
+
+		if (!(BIT(i) & scaling_mode_mask))
+			continue;
+
+		ret = drm_property_add_enum(scaling_mode_property, j++,
+					    drm_scaling_mode_enum_list[i].type,
+					    drm_scaling_mode_enum_list[i].name);
+
+		if (ret) {
+			drm_property_destroy(dev, scaling_mode_property);
+
+			return ret;
+		}
+	}
+
+	drm_object_attach_property(&connector->base,
+				   scaling_mode_property, 0);
+
+	connector->scaling_mode_property = scaling_mode_property;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_scaling_mode_property);
+
 /**
 /**
  * drm_mode_create_aspect_ratio_property - create aspect ratio property
  * drm_mode_create_aspect_ratio_property - create aspect ratio property
  * @dev: DRM device
  * @dev: DRM device

+ 157 - 7
drivers/gpu/drm/drm_dp_mst_topology.c

@@ -737,16 +737,16 @@ static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
 static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
 static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
 			      struct drm_dp_sideband_msg_tx *txmsg)
 			      struct drm_dp_sideband_msg_tx *txmsg)
 {
 {
-	bool ret;
+	unsigned int state;
 
 
 	/*
 	/*
 	 * All updates to txmsg->state are protected by mgr->qlock, and the two
 	 * All updates to txmsg->state are protected by mgr->qlock, and the two
 	 * cases we check here are terminal states. For those the barriers
 	 * cases we check here are terminal states. For those the barriers
 	 * provided by the wake_up/wait_event pair are enough.
 	 * provided by the wake_up/wait_event pair are enough.
 	 */
 	 */
-	ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
-	       txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
-	return ret;
+	state = READ_ONCE(txmsg->state);
+	return (state == DRM_DP_SIDEBAND_TX_RX ||
+		state == DRM_DP_SIDEBAND_TX_TIMEOUT);
 }
 }
 
 
 static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
 static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
@@ -855,7 +855,7 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
 	mutex_unlock(&mstb->mgr->qlock);
 	mutex_unlock(&mstb->mgr->qlock);
 
 
 	if (wake_tx)
 	if (wake_tx)
-		wake_up(&mstb->mgr->tx_waitq);
+		wake_up_all(&mstb->mgr->tx_waitq);
 
 
 	kref_put(kref, drm_dp_free_mst_branch_device);
 	kref_put(kref, drm_dp_free_mst_branch_device);
 }
 }
@@ -1510,7 +1510,7 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
 		if (txmsg->seqno != -1)
 		if (txmsg->seqno != -1)
 			txmsg->dst->tx_slots[txmsg->seqno] = NULL;
 			txmsg->dst->tx_slots[txmsg->seqno] = NULL;
 		txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
 		txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
-		wake_up(&mgr->tx_waitq);
+		wake_up_all(&mgr->tx_waitq);
 	}
 	}
 }
 }
 
 
@@ -2258,7 +2258,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
 		mstb->tx_slots[slot] = NULL;
 		mstb->tx_slots[slot] = NULL;
 		mutex_unlock(&mgr->qlock);
 		mutex_unlock(&mgr->qlock);
 
 
-		wake_up(&mgr->tx_waitq);
+		wake_up_all(&mgr->tx_waitq);
 	}
 	}
 	return ret;
 	return ret;
 }
 }
@@ -2497,6 +2497,81 @@ static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @port: port to find vcpi slots for
+ * @pbn: bandwidth required for the mode in PBN
+ *
+ * RETURNS:
+ * Total slots in the atomic state assigned for this port or error
+ */
+int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
+				  struct drm_dp_mst_topology_mgr *mgr,
+				  struct drm_dp_mst_port *port, int pbn)
+{
+	struct drm_dp_mst_topology_state *topology_state;
+	int req_slots;
+
+	topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+	if (topology_state == NULL)
+		return -ENOMEM;
+
+	port = drm_dp_get_validated_port_ref(mgr, port);
+	if (port == NULL)
+		return -EINVAL;
+	req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+	DRM_DEBUG_KMS("vcpi slots req=%d, avail=%d\n",
+			req_slots, topology_state->avail_slots);
+
+	if (req_slots > topology_state->avail_slots) {
+		drm_dp_put_port(port);
+		return -ENOSPC;
+	}
+
+	topology_state->avail_slots -= req_slots;
+	DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
+
+	drm_dp_put_port(port);
+	return req_slots;
+}
+EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
+
+/**
+ * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
+ * @state: global atomic state
+ * @mgr: MST topology manager for the port
+ * @slots: number of vcpi slots to release
+ *
+ * RETURNS:
+ * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
+ * negative error code
+ */
+int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
+				     struct drm_dp_mst_topology_mgr *mgr,
+				     int slots)
+{
+	struct drm_dp_mst_topology_state *topology_state;
+
+	topology_state = drm_atomic_get_mst_topology_state(state, mgr);
+	if (topology_state == NULL)
+		return -ENOMEM;
+
+	/* We cannot rely on port->vcpi.num_slots to update
+	 * topology_state->avail_slots as the port may not exist if the parent
+	 * branch device was unplugged. This should be fixed by tracking
+	 * per-port slot allocation in drm_dp_mst_topology_state instead of
+	 * depending on the caller to tell us how many slots to release.
+	 */
+	topology_state->avail_slots += slots;
+	DRM_DEBUG_KMS("vcpi slots released=%d, avail=%d\n",
+			slots, topology_state->avail_slots);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
+
 /**
 /**
  * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
  * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
  * @mgr: manager for this port
  * @mgr: manager for this port
@@ -2936,6 +3011,69 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
 		(*mgr->cbs->hotplug)(mgr);
 		(*mgr->cbs->hotplug)(mgr);
 }
 }
 
 
+void *drm_dp_mst_duplicate_state(struct drm_atomic_state *state, void *obj)
+{
+	struct drm_dp_mst_topology_mgr *mgr = obj;
+	struct drm_dp_mst_topology_state *new_mst_state;
+
+	if (WARN_ON(!mgr->state))
+		return NULL;
+
+	new_mst_state = kmemdup(mgr->state, sizeof(*new_mst_state), GFP_KERNEL);
+	if (new_mst_state)
+		new_mst_state->state = state;
+	return new_mst_state;
+}
+
+void drm_dp_mst_swap_state(void *obj, void **obj_state_ptr)
+{
+	struct drm_dp_mst_topology_mgr *mgr = obj;
+	struct drm_dp_mst_topology_state **topology_state_ptr;
+
+	topology_state_ptr = (struct drm_dp_mst_topology_state **)obj_state_ptr;
+
+	mgr->state->state = (*topology_state_ptr)->state;
+	swap(*topology_state_ptr, mgr->state);
+	mgr->state->state = NULL;
+}
+
+void drm_dp_mst_destroy_state(void *obj_state)
+{
+	kfree(obj_state);
+}
+
+static const struct drm_private_state_funcs mst_state_funcs = {
+	.duplicate_state = drm_dp_mst_duplicate_state,
+	.swap_state = drm_dp_mst_swap_state,
+	.destroy_state = drm_dp_mst_destroy_state,
+};
+
+/**
+ * drm_atomic_get_mst_topology_state: get MST topology state
+ *
+ * @state: global atomic state
+ * @mgr: MST topology manager, also the private object in this case
+ *
+ * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
+ * state vtable so that the private object state returned is that of a MST
+ * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
+ * to care of the locking, so warn if don't hold the connection_mutex.
+ *
+ * RETURNS:
+ *
+ * The MST topology state or error pointer.
+ */
+struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
+								    struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_device *dev = mgr->dev;
+
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
+	return drm_atomic_get_private_obj_state(state, mgr,
+						&mst_state_funcs);
+}
+EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
+
 /**
 /**
  * drm_dp_mst_topology_mgr_init - initialise a topology manager
  * drm_dp_mst_topology_mgr_init - initialise a topology manager
  * @mgr: manager struct to initialise
  * @mgr: manager struct to initialise
@@ -2980,6 +3118,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
 	if (test_calc_pbn_mode() < 0)
 	if (test_calc_pbn_mode() < 0)
 		DRM_ERROR("MST PBN self-test failed\n");
 		DRM_ERROR("MST PBN self-test failed\n");
 
 
+	mgr->state = kzalloc(sizeof(*mgr->state), GFP_KERNEL);
+	if (mgr->state == NULL)
+		return -ENOMEM;
+	mgr->state->mgr = mgr;
+
+	/* max. time slots - one slot for MTP header */
+	mgr->state->avail_slots = 63;
+	mgr->funcs = &mst_state_funcs;
+
 	return 0;
 	return 0;
 }
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
@@ -3000,6 +3147,9 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
 	mutex_unlock(&mgr->payload_lock);
 	mutex_unlock(&mgr->payload_lock);
 	mgr->dev = NULL;
 	mgr->dev = NULL;
 	mgr->aux = NULL;
 	mgr->aux = NULL;
+	kfree(mgr->state);
+	mgr->state = NULL;
+	mgr->funcs = NULL;
 }
 }
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
 EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
 
 

+ 28 - 1
drivers/gpu/drm/drm_fb_cma_helper.c

@@ -189,7 +189,7 @@ struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev,
 		obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
 		obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]);
 		if (!obj) {
 		if (!obj) {
 			dev_err(dev->dev, "Failed to lookup GEM object\n");
 			dev_err(dev->dev, "Failed to lookup GEM object\n");
-			ret = -ENXIO;
+			ret = -ENOENT;
 			goto err_gem_object_put;
 			goto err_gem_object_put;
 		}
 		}
 
 
@@ -259,6 +259,33 @@ struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
 }
 }
 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
 EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
 
 
+/**
+ * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer
+ * @fb: The framebuffer
+ * @state: Which state of drm plane
+ * @plane: Which plane
+ * Return the CMA GEM address for given framebuffer.
+ *
+ * This function will usually be called from the PLANE callback functions.
+ */
+dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb,
+				   struct drm_plane_state *state,
+				   unsigned int plane)
+{
+	struct drm_fb_cma *fb_cma = to_fb_cma(fb);
+	dma_addr_t paddr;
+
+	if (plane >= 4)
+		return 0;
+
+	paddr = fb_cma->obj[plane]->paddr + fb->offsets[plane];
+	paddr += fb->format->cpp[plane] * (state->src_x >> 16);
+	paddr += fb->pitches[plane] * (state->src_y >> 16);
+
+	return paddr;
+}
+EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr);
+
 /**
 /**
  * drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
  * drm_fb_cma_prepare_fb() - Prepare CMA framebuffer
  * @plane: Which plane
  * @plane: Which plane

+ 4 - 4
drivers/gpu/drm/drm_file.c

@@ -351,9 +351,8 @@ void drm_lastclose(struct drm_device * dev)
  *
  *
  * This function must be used by drivers as their &file_operations.release
  * This function must be used by drivers as their &file_operations.release
  * method. It frees any resources associated with the open file, and calls the
  * method. It frees any resources associated with the open file, and calls the
- * &drm_driver.preclose and &drm_driver.lastclose driver callbacks. If this is
- * the last open file for the DRM device also proceeds to call the
- * &drm_driver.lastclose driver callback.
+ * &drm_driver.postclose driver callback. If this is the last open file for the
+ * DRM device also proceeds to call the &drm_driver.lastclose driver callback.
  *
  *
  * RETURNS:
  * RETURNS:
  *
  *
@@ -373,7 +372,8 @@ int drm_release(struct inode *inode, struct file *filp)
 	list_del(&file_priv->lhead);
 	list_del(&file_priv->lhead);
 	mutex_unlock(&dev->filelist_mutex);
 	mutex_unlock(&dev->filelist_mutex);
 
 
-	if (dev->driver->preclose)
+	if (drm_core_check_feature(dev, DRIVER_LEGACY) &&
+	    dev->driver->preclose)
 		dev->driver->preclose(dev, file_priv);
 		dev->driver->preclose(dev, file_priv);
 
 
 	/* ========================================================
 	/* ========================================================

+ 72 - 57
drivers/gpu/drm/drm_irq.c

@@ -54,7 +54,7 @@
 
 
 static bool
 static bool
 drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
 drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-			  struct timeval *tvblank, unsigned flags);
+			  struct timeval *tvblank, bool in_vblank_irq);
 
 
 static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
 static unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
 
 
@@ -138,7 +138,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
 	 */
 	 */
 	do {
 	do {
 		cur_vblank = __get_vblank_counter(dev, pipe);
 		cur_vblank = __get_vblank_counter(dev, pipe);
-		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, 0);
+		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false);
 	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 
 
 	/*
 	/*
@@ -171,7 +171,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
  * device vblank fields.
  * device vblank fields.
  */
  */
 static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
-				    unsigned long flags)
+				    bool in_vblank_irq)
 {
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	u32 cur_vblank, diff;
 	u32 cur_vblank, diff;
@@ -194,7 +194,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 	 */
 	 */
 	do {
 	do {
 		cur_vblank = __get_vblank_counter(dev, pipe);
 		cur_vblank = __get_vblank_counter(dev, pipe);
-		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, flags);
+		rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq);
 	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 	} while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0);
 
 
 	if (dev->max_vblank_count != 0) {
 	if (dev->max_vblank_count != 0) {
@@ -214,13 +214,13 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 		 */
 		 */
 		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
 		diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns);
 
 
-		if (diff == 0 && flags & DRM_CALLED_FROM_VBLIRQ)
+		if (diff == 0 && in_vblank_irq)
 			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
 			DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored."
 				      " diff_ns = %lld, framedur_ns = %d)\n",
 				      " diff_ns = %lld, framedur_ns = %d)\n",
 				      pipe, (long long) diff_ns, framedur_ns);
 				      pipe, (long long) diff_ns, framedur_ns);
 	} else {
 	} else {
 		/* some kind of default for drivers w/o accurate vbl timestamping */
 		/* some kind of default for drivers w/o accurate vbl timestamping */
-		diff = (flags & DRM_CALLED_FROM_VBLIRQ) != 0;
+		diff = in_vblank_irq ? 1 : 0;
 	}
 	}
 
 
 	/*
 	/*
@@ -253,7 +253,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
 	 * Otherwise reinitialize delayed at next vblank interrupt and assign 0
 	 * for now, to mark the vblanktimestamp as invalid.
 	 * for now, to mark the vblanktimestamp as invalid.
 	 */
 	 */
-	if (!rc && (flags & DRM_CALLED_FROM_VBLIRQ) == 0)
+	if (!rc && in_vblank_irq)
 		t_vblank = (struct timeval) {0, 0};
 		t_vblank = (struct timeval) {0, 0};
 
 
 	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
 	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
@@ -291,7 +291,7 @@ u32 drm_accurate_vblank_count(struct drm_crtc *crtc)
 
 
 	spin_lock_irqsave(&dev->vblank_time_lock, flags);
 	spin_lock_irqsave(&dev->vblank_time_lock, flags);
 
 
-	drm_update_vblank_count(dev, pipe, 0);
+	drm_update_vblank_count(dev, pipe, false);
 	vblank = drm_vblank_count(dev, pipe);
 	vblank = drm_vblank_count(dev, pipe);
 
 
 	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
 	spin_unlock_irqrestore(&dev->vblank_time_lock, flags);
@@ -349,7 +349,7 @@ static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe)
 	 * this time. This makes the count account for the entire time
 	 * this time. This makes the count account for the entire time
 	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
 	 * between drm_crtc_vblank_on() and drm_crtc_vblank_off().
 	 */
 	 */
-	drm_update_vblank_count(dev, pipe, 0);
+	drm_update_vblank_count(dev, pipe, false);
 
 
 	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
 	spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags);
 }
 }
@@ -684,6 +684,7 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc,
 
 
 	vblank->linedur_ns  = linedur_ns;
 	vblank->linedur_ns  = linedur_ns;
 	vblank->framedur_ns = framedur_ns;
 	vblank->framedur_ns = framedur_ns;
+	vblank->hwmode = *mode;
 
 
 	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
 	DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n",
 		  crtc->base.id, mode->crtc_htotal,
 		  crtc->base.id, mode->crtc_htotal,
@@ -700,10 +701,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * @max_error: Desired maximum allowable error in timestamps (nanosecs)
  * @max_error: Desired maximum allowable error in timestamps (nanosecs)
  *             On return contains true maximum error of timestamp
  *             On return contains true maximum error of timestamp
  * @vblank_time: Pointer to struct timeval which should receive the timestamp
  * @vblank_time: Pointer to struct timeval which should receive the timestamp
- * @flags: Flags to pass to driver:
- *         0 = Default,
- *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
- * @mode: mode which defines the scanout timings
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
  *
  *
  * Implements calculation of exact vblank timestamps from given drm_display_mode
  * Implements calculation of exact vblank timestamps from given drm_display_mode
  * timings and current video scanout position of a CRTC. This can be called from
  * timings and current video scanout position of a CRTC. This can be called from
@@ -723,52 +724,62 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * returns as no operation if a doublescan or interlaced video mode is
  * returns as no operation if a doublescan or interlaced video mode is
  * active. Higher level code is expected to handle this.
  * active. Higher level code is expected to handle this.
  *
  *
- * Returns:
- * Negative value on error, failure or if not supported in current
- * video mode:
- *
- * -EINVAL    Invalid CRTC.
- * -EAGAIN    Temporary unavailable, e.g., called before initial modeset.
- * -ENOTSUPP  Function not supported in current display mode.
- * -EIO       Failed, e.g., due to failed scanout position query.
+ * This function can be used to implement the &drm_driver.get_vblank_timestamp
+ * directly, if the driver implements the &drm_driver.get_scanout_position hook.
  *
  *
- * Returns or'ed positive status flags on success:
+ * Note that atomic drivers must call drm_calc_timestamping_constants() before
+ * enabling a CRTC. The atomic helpers already take care of that in
+ * drm_atomic_helper_update_legacy_modeset_state().
  *
  *
- * DRM_VBLANKTIME_SCANOUTPOS_METHOD - Signal this method used for timestamping.
- * DRM_VBLANKTIME_INVBL - Timestamp taken while scanout was in vblank interval.
+ * Returns:
  *
  *
+ * Returns true on success, and false on failure, i.e. when no accurate
+ * timestamp could be acquired.
  */
  */
-int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
-					  unsigned int pipe,
-					  int *max_error,
-					  struct timeval *vblank_time,
-					  unsigned flags,
-					  const struct drm_display_mode *mode)
+bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
+					   unsigned int pipe,
+					   int *max_error,
+					   struct timeval *vblank_time,
+					   bool in_vblank_irq)
 {
 {
 	struct timeval tv_etime;
 	struct timeval tv_etime;
 	ktime_t stime, etime;
 	ktime_t stime, etime;
-	unsigned int vbl_status;
-	int ret = DRM_VBLANKTIME_SCANOUTPOS_METHOD;
+	bool vbl_status;
+	struct drm_crtc *crtc;
+	const struct drm_display_mode *mode;
+	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	int vpos, hpos, i;
 	int vpos, hpos, i;
 	int delta_ns, duration_ns;
 	int delta_ns, duration_ns;
 
 
-	if (pipe >= dev->num_crtcs) {
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return false;
+
+	crtc = drm_crtc_from_index(dev, pipe);
+
+	if (pipe >= dev->num_crtcs || !crtc) {
 		DRM_ERROR("Invalid crtc %u\n", pipe);
 		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return -EINVAL;
+		return false;
 	}
 	}
 
 
 	/* Scanout position query not supported? Should not happen. */
 	/* Scanout position query not supported? Should not happen. */
 	if (!dev->driver->get_scanout_position) {
 	if (!dev->driver->get_scanout_position) {
 		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
 		DRM_ERROR("Called from driver w/o get_scanout_position()!?\n");
-		return -EIO;
+		return false;
 	}
 	}
 
 
+	if (drm_drv_uses_atomic_modeset(dev))
+		mode = &vblank->hwmode;
+	else
+		mode = &crtc->hwmode;
+
 	/* If mode timing undefined, just return as no-op:
 	/* If mode timing undefined, just return as no-op:
 	 * Happens during initial modesetting of a crtc.
 	 * Happens during initial modesetting of a crtc.
 	 */
 	 */
 	if (mode->crtc_clock == 0) {
 	if (mode->crtc_clock == 0) {
 		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
 		DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe);
-		return -EAGAIN;
+		WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev));
+
+		return false;
 	}
 	}
 
 
 	/* Get current scanout position with system timestamp.
 	/* Get current scanout position with system timestamp.
@@ -783,16 +794,17 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 		 * Get vertical and horizontal scanout position vpos, hpos,
 		 * Get vertical and horizontal scanout position vpos, hpos,
 		 * and bounding timestamps stime, etime, pre/post query.
 		 * and bounding timestamps stime, etime, pre/post query.
 		 */
 		 */
-		vbl_status = dev->driver->get_scanout_position(dev, pipe, flags,
+		vbl_status = dev->driver->get_scanout_position(dev, pipe,
+							       in_vblank_irq,
 							       &vpos, &hpos,
 							       &vpos, &hpos,
 							       &stime, &etime,
 							       &stime, &etime,
 							       mode);
 							       mode);
 
 
 		/* Return as no-op if scanout query unsupported or failed. */
 		/* Return as no-op if scanout query unsupported or failed. */
-		if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
-			DRM_DEBUG("crtc %u : scanoutpos query failed [0x%x].\n",
-				  pipe, vbl_status);
-			return -EIO;
+		if (!vbl_status) {
+			DRM_DEBUG("crtc %u : scanoutpos query failed.\n",
+				  pipe);
+			return false;
 		}
 		}
 
 
 		/* Compute uncertainty in timestamp of scanout position query. */
 		/* Compute uncertainty in timestamp of scanout position query. */
@@ -830,13 +842,13 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 	etime = ktime_sub_ns(etime, delta_ns);
 	etime = ktime_sub_ns(etime, delta_ns);
 	*vblank_time = ktime_to_timeval(etime);
 	*vblank_time = ktime_to_timeval(etime);
 
 
-	DRM_DEBUG_VBL("crtc %u : v 0x%x p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
-		      pipe, vbl_status, hpos, vpos,
+	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+		      pipe, hpos, vpos,
 		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
 		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
 		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
 		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
 		      duration_ns/1000, i);
 		      duration_ns/1000, i);
 
 
-	return ret;
+	return true;
 }
 }
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 
 
@@ -854,9 +866,10 @@ static struct timeval get_drm_timestamp(void)
  * @dev: DRM device
  * @dev: DRM device
  * @pipe: index of CRTC whose vblank timestamp to retrieve
  * @pipe: index of CRTC whose vblank timestamp to retrieve
  * @tvblank: Pointer to target struct timeval which should receive the timestamp
  * @tvblank: Pointer to target struct timeval which should receive the timestamp
- * @flags: Flags to pass to driver:
- *         0 = Default,
- *         DRM_CALLED_FROM_VBLIRQ = If function is called from vbl IRQ handler
+ * @in_vblank_irq:
+ *     True when called from drm_crtc_handle_vblank().  Some drivers
+ *     need to apply some workarounds for gpu-specific vblank irq quirks
+ *     if flag is set.
  *
  *
  * Fetches the system timestamp corresponding to the time of the most recent
  * Fetches the system timestamp corresponding to the time of the most recent
  * vblank interval on specified CRTC. May call into kms-driver to
  * vblank interval on specified CRTC. May call into kms-driver to
@@ -870,27 +883,25 @@ static struct timeval get_drm_timestamp(void)
  */
  */
 static bool
 static bool
 drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
 drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
-			  struct timeval *tvblank, unsigned flags)
+			  struct timeval *tvblank, bool in_vblank_irq)
 {
 {
-	int ret;
+	bool ret = false;
 
 
 	/* Define requested maximum error on timestamps (nanoseconds). */
 	/* Define requested maximum error on timestamps (nanoseconds). */
 	int max_error = (int) drm_timestamp_precision * 1000;
 	int max_error = (int) drm_timestamp_precision * 1000;
 
 
 	/* Query driver if possible and precision timestamping enabled. */
 	/* Query driver if possible and precision timestamping enabled. */
-	if (dev->driver->get_vblank_timestamp && (max_error > 0)) {
+	if (dev->driver->get_vblank_timestamp && (max_error > 0))
 		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
 		ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error,
-							tvblank, flags);
-		if (ret > 0)
-			return true;
-	}
+							tvblank, in_vblank_irq);
 
 
 	/* GPU high precision timestamp query unsupported or failed.
 	/* GPU high precision timestamp query unsupported or failed.
 	 * Return current monotonic/gettimeofday timestamp as best estimate.
 	 * Return current monotonic/gettimeofday timestamp as best estimate.
 	 */
 	 */
-	*tvblank = get_drm_timestamp();
+	if (!ret)
+		*tvblank = get_drm_timestamp();
 
 
-	return false;
+	return ret;
 }
 }
 
 
 /**
 /**
@@ -1329,6 +1340,10 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
 		send_vblank_event(dev, e, seq, &now);
 		send_vblank_event(dev, e, seq, &now);
 	}
 	}
 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
 	spin_unlock_irqrestore(&dev->event_lock, irqflags);
+
+	/* Will be reset by the modeset helpers when re-enabling the crtc by
+	 * calling drm_calc_timestamping_constants(). */
+	vblank->hwmode.crtc_clock = 0;
 }
 }
 EXPORT_SYMBOL(drm_crtc_vblank_off);
 EXPORT_SYMBOL(drm_crtc_vblank_off);
 
 
@@ -1760,7 +1775,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe)
 		return false;
 		return false;
 	}
 	}
 
 
-	drm_update_vblank_count(dev, pipe, DRM_CALLED_FROM_VBLIRQ);
+	drm_update_vblank_count(dev, pipe, true);
 
 
 	spin_unlock(&dev->vblank_time_lock);
 	spin_unlock(&dev->vblank_time_lock);
 
 

+ 5 - 6
drivers/gpu/drm/drm_plane_helper.c

@@ -381,6 +381,7 @@ EXPORT_SYMBOL(drm_primary_helper_update);
 /**
 /**
  * drm_primary_helper_disable() - Helper for primary plane disable
  * drm_primary_helper_disable() - Helper for primary plane disable
  * @plane: plane to disable
  * @plane: plane to disable
+ * @ctx: lock acquire context, not used here
  *
  *
  * Provides a default plane disable handler for primary planes.  This is handler
  * Provides a default plane disable handler for primary planes.  This is handler
  * is called in response to a userspace SetPlane operation on the plane with a
  * is called in response to a userspace SetPlane operation on the plane with a
@@ -510,12 +511,10 @@ int drm_plane_helper_commit(struct drm_plane *plane,
 	if (plane_funcs->cleanup_fb)
 	if (plane_funcs->cleanup_fb)
 		plane_funcs->cleanup_fb(plane, plane_state);
 		plane_funcs->cleanup_fb(plane, plane_state);
 out:
 out:
-	if (plane_state) {
-		if (plane->funcs->atomic_destroy_state)
-			plane->funcs->atomic_destroy_state(plane, plane_state);
-		else
-			drm_atomic_helper_plane_destroy_state(plane, plane_state);
-	}
+	if (plane->funcs->atomic_destroy_state)
+		plane->funcs->atomic_destroy_state(plane, plane_state);
+	else
+		drm_atomic_helper_plane_destroy_state(plane, plane_state);
 
 
 	return ret;
 	return ret;
 }
 }

+ 24 - 6
drivers/gpu/drm/drm_prime.c

@@ -595,15 +595,18 @@ out_unlock:
 EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
 EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
 
 
 /**
 /**
- * drm_gem_prime_import - helper library implementation of the import callback
+ * drm_gem_prime_import_dev - core implementation of the import callback
  * @dev: drm_device to import into
  * @dev: drm_device to import into
  * @dma_buf: dma-buf object to import
  * @dma_buf: dma-buf object to import
+ * @attach_dev: struct device to dma_buf attach
  *
  *
- * This is the implementation of the gem_prime_import functions for GEM drivers
- * using the PRIME helpers.
+ * This is the core of drm_gem_prime_import. It's designed to be called by
+ * drivers who want to use a different device structure than dev->dev for
+ * attaching via dma_buf.
  */
  */
-struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
-					    struct dma_buf *dma_buf)
+struct drm_gem_object *drm_gem_prime_import_dev(struct drm_device *dev,
+					    struct dma_buf *dma_buf,
+					    struct device *attach_dev)
 {
 {
 	struct dma_buf_attachment *attach;
 	struct dma_buf_attachment *attach;
 	struct sg_table *sgt;
 	struct sg_table *sgt;
@@ -625,7 +628,7 @@ struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
 	if (!dev->driver->gem_prime_import_sg_table)
 	if (!dev->driver->gem_prime_import_sg_table)
 		return ERR_PTR(-EINVAL);
 		return ERR_PTR(-EINVAL);
 
 
-	attach = dma_buf_attach(dma_buf, dev->dev);
+	attach = dma_buf_attach(dma_buf, attach_dev);
 	if (IS_ERR(attach))
 	if (IS_ERR(attach))
 		return ERR_CAST(attach);
 		return ERR_CAST(attach);
 
 
@@ -655,6 +658,21 @@ fail_detach:
 
 
 	return ERR_PTR(ret);
 	return ERR_PTR(ret);
 }
 }
+EXPORT_SYMBOL(drm_gem_prime_import_dev);
+
+/**
+ * drm_gem_prime_import - helper library implementation of the import callback
+ * @dev: drm_device to import into
+ * @dma_buf: dma-buf object to import
+ *
+ * This is the implementation of the gem_prime_import functions for GEM drivers
+ * using the PRIME helpers.
+ */
+struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
+					    struct dma_buf *dma_buf)
+{
+	return drm_gem_prime_import_dev(dev, dma_buf, dev->dev);
+}
 EXPORT_SYMBOL(drm_gem_prime_import);
 EXPORT_SYMBOL(drm_gem_prime_import);
 
 
 /**
 /**

+ 1 - 7
drivers/gpu/drm/exynos/exynos_drm_drv.c

@@ -82,14 +82,9 @@ err_file_priv_free:
 	return ret;
 	return ret;
 }
 }
 
 
-static void exynos_drm_preclose(struct drm_device *dev,
-					struct drm_file *file)
-{
-	exynos_drm_subdrv_close(dev, file);
-}
-
 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
 static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
 {
 {
+	exynos_drm_subdrv_close(dev, file);
 	kfree(file->driver_priv);
 	kfree(file->driver_priv);
 	file->driver_priv = NULL;
 	file->driver_priv = NULL;
 }
 }
@@ -145,7 +140,6 @@ static struct drm_driver exynos_drm_driver = {
 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME
 				  | DRIVER_ATOMIC | DRIVER_RENDER,
 				  | DRIVER_ATOMIC | DRIVER_RENDER,
 	.open			= exynos_drm_open,
 	.open			= exynos_drm_open,
-	.preclose		= exynos_drm_preclose,
 	.lastclose		= exynos_drm_lastclose,
 	.lastclose		= exynos_drm_lastclose,
 	.postclose		= exynos_drm_postclose,
 	.postclose		= exynos_drm_postclose,
 	.gem_free_object_unlocked = exynos_drm_gem_free_object,
 	.gem_free_object_unlocked = exynos_drm_gem_free_object,

+ 9 - 42
drivers/gpu/drm/gma500/mdfld_tpo_vid.c

@@ -32,53 +32,20 @@ static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev)
 	struct drm_display_mode *mode;
 	struct drm_display_mode *mode;
 	struct drm_psb_private *dev_priv = dev->dev_private;
 	struct drm_psb_private *dev_priv = dev->dev_private;
 	struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
 	struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
-	bool use_gct = false;
 
 
 	mode = kzalloc(sizeof(*mode), GFP_KERNEL);
 	mode = kzalloc(sizeof(*mode), GFP_KERNEL);
 	if (!mode)
 	if (!mode)
 		return NULL;
 		return NULL;
 
 
-	if (use_gct) {
-		mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
-		mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
-		mode->hsync_start = mode->hdisplay +
-				((ti->hsync_offset_hi << 8) |
-				ti->hsync_offset_lo);
-		mode->hsync_end = mode->hsync_start +
-				((ti->hsync_pulse_width_hi << 8) |
-				ti->hsync_pulse_width_lo);
-		mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) |
-								ti->hblank_lo);
-		mode->vsync_start =
-			mode->vdisplay + ((ti->vsync_offset_hi << 8) |
-						ti->vsync_offset_lo);
-		mode->vsync_end =
-			mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) |
-						ti->vsync_pulse_width_lo);
-		mode->vtotal = mode->vdisplay +
-				((ti->vblank_hi << 8) | ti->vblank_lo);
-		mode->clock = ti->pixel_clock * 10;
-
-		dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
-		dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
-		dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
-		dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
-		dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
-		dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
-		dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
-		dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
-		dev_dbg(dev->dev, "clock is %d\n", mode->clock);
-	} else {
-		mode->hdisplay = 864;
-		mode->vdisplay = 480;
-		mode->hsync_start = 873;
-		mode->hsync_end = 876;
-		mode->htotal = 887;
-		mode->vsync_start = 487;
-		mode->vsync_end = 490;
-		mode->vtotal = 499;
-		mode->clock = 33264;
-	}
+	mode->hdisplay = 864;
+	mode->vdisplay = 480;
+	mode->hsync_start = 873;
+	mode->hsync_end = 876;
+	mode->htotal = 887;
+	mode->vsync_start = 487;
+	mode->vsync_end = 490;
+	mode->vtotal = 499;
+	mode->clock = 33264;
 
 
 	drm_mode_set_name(mode);
 	drm_mode_set_name(mode);
 	drm_mode_set_crtcinfo(mode, 0);
 	drm_mode_set_crtcinfo(mode, 0);

+ 13 - 49
drivers/gpu/drm/i915/i915_irq.c

@@ -720,9 +720,7 @@ static u32 i915_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	i915_reg_t high_frame, low_frame;
 	i915_reg_t high_frame, low_frame;
 	u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
 	u32 high1, high2, low, pixel, vbl_start, hsync_start, htotal;
-	struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
-								pipe);
-	const struct drm_display_mode *mode = &intel_crtc->base.hwmode;
+	const struct drm_display_mode *mode = &dev->vblank[pipe].hwmode;
 	unsigned long irqflags;
 	unsigned long irqflags;
 
 
 	htotal = mode->crtc_htotal;
 	htotal = mode->crtc_htotal;
@@ -779,13 +777,17 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 {
 {
 	struct drm_device *dev = crtc->base.dev;
 	struct drm_device *dev = crtc->base.dev;
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	const struct drm_display_mode *mode = &crtc->base.hwmode;
+	const struct drm_display_mode *mode;
+	struct drm_vblank_crtc *vblank;
 	enum pipe pipe = crtc->pipe;
 	enum pipe pipe = crtc->pipe;
 	int position, vtotal;
 	int position, vtotal;
 
 
 	if (!crtc->active)
 	if (!crtc->active)
 		return -1;
 		return -1;
 
 
+	vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
+	mode = &vblank->hwmode;
+
 	vtotal = mode->crtc_vtotal;
 	vtotal = mode->crtc_vtotal;
 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 		vtotal /= 2;
 		vtotal /= 2;
@@ -827,10 +829,10 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 	return (position + crtc->scanline_offset) % vtotal;
 	return (position + crtc->scanline_offset) % vtotal;
 }
 }
 
 
-static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
-				    unsigned int flags, int *vpos, int *hpos,
-				    ktime_t *stime, ktime_t *etime,
-				    const struct drm_display_mode *mode)
+static bool i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
+				     bool in_vblank_irq, int *vpos, int *hpos,
+				     ktime_t *stime, ktime_t *etime,
+				     const struct drm_display_mode *mode)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
 	struct intel_crtc *intel_crtc = intel_get_crtc_for_pipe(dev_priv,
@@ -838,13 +840,12 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
 	int position;
 	int position;
 	int vbl_start, vbl_end, hsync_start, htotal, vtotal;
 	int vbl_start, vbl_end, hsync_start, htotal, vtotal;
 	bool in_vbl = true;
 	bool in_vbl = true;
-	int ret = 0;
 	unsigned long irqflags;
 	unsigned long irqflags;
 
 
 	if (WARN_ON(!mode->crtc_clock)) {
 	if (WARN_ON(!mode->crtc_clock)) {
 		DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
 		DRM_DEBUG_DRIVER("trying to get scanoutpos for disabled "
 				 "pipe %c\n", pipe_name(pipe));
 				 "pipe %c\n", pipe_name(pipe));
-		return 0;
+		return false;
 	}
 	}
 
 
 	htotal = mode->crtc_htotal;
 	htotal = mode->crtc_htotal;
@@ -859,8 +860,6 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
 		vtotal /= 2;
 		vtotal /= 2;
 	}
 	}
 
 
-	ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
 	/*
 	/*
 	 * Lock uncore.lock, as we will do multiple timing critical raw
 	 * Lock uncore.lock, as we will do multiple timing critical raw
 	 * register reads, potentially with preemption disabled, so the
 	 * register reads, potentially with preemption disabled, so the
@@ -944,11 +943,7 @@ static int i915_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
 		*hpos = position - (*vpos * htotal);
 		*hpos = position - (*vpos * htotal);
 	}
 	}
 
 
-	/* In vblank? */
-	if (in_vbl)
-		ret |= DRM_SCANOUTPOS_IN_VBLANK;
-
-	return ret;
+	return true;
 }
 }
 
 
 int intel_get_crtc_scanline(struct intel_crtc *crtc)
 int intel_get_crtc_scanline(struct intel_crtc *crtc)
@@ -964,37 +959,6 @@ int intel_get_crtc_scanline(struct intel_crtc *crtc)
 	return position;
 	return position;
 }
 }
 
 
-static int i915_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
-			      int *max_error,
-			      struct timeval *vblank_time,
-			      unsigned flags)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-	struct intel_crtc *crtc;
-
-	if (pipe >= INTEL_INFO(dev_priv)->num_pipes) {
-		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return -EINVAL;
-	}
-
-	/* Get drm_crtc to timestamp: */
-	crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
-	if (crtc == NULL) {
-		DRM_ERROR("Invalid crtc %u\n", pipe);
-		return -EINVAL;
-	}
-
-	if (!crtc->base.hwmode.crtc_clock) {
-		DRM_DEBUG_KMS("crtc %u is disabled\n", pipe);
-		return -EBUSY;
-	}
-
-	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-						     vblank_time, flags,
-						     &crtc->base.hwmode);
-}
-
 static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
 static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
 {
 {
 	u32 busy_up, busy_down, max_avg, min_avg;
 	u32 busy_up, busy_down, max_avg, min_avg;
@@ -4294,7 +4258,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 
 
 	dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
 	dev_priv->hotplug.hpd_storm_threshold = HPD_STORM_DEFAULT_THRESHOLD;
 
 
-	dev->driver->get_vblank_timestamp = i915_get_vblank_timestamp;
+	dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
 	dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
 	dev->driver->get_scanout_position = i915_get_crtc_scanoutpos;
 
 
 	if (IS_CHERRYVIEW(dev_priv)) {
 	if (IS_CHERRYVIEW(dev_priv)) {

+ 2 - 9
drivers/gpu/drm/i915/intel_display.c

@@ -11444,12 +11444,6 @@ intel_modeset_update_crtc_state(struct drm_atomic_state *state)
 	for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
 	for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
 		to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state);
 		to_intel_crtc(crtc)->config = to_intel_crtc_state(new_crtc_state);
 
 
-		/* Update hwmode for vblank functions */
-		if (new_crtc_state->active)
-			crtc->hwmode = new_crtc_state->adjusted_mode;
-		else
-			crtc->hwmode.crtc_clock = 0;
-
 		/*
 		/*
 		 * Update legacy state to satisfy fbc code. This can
 		 * Update legacy state to satisfy fbc code. This can
 		 * be removed when fbc uses the atomic state.
 		 * be removed when fbc uses the atomic state.
@@ -15425,8 +15419,6 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
 			to_intel_crtc_state(crtc->base.state);
 			to_intel_crtc_state(crtc->base.state);
 		int pixclk = 0;
 		int pixclk = 0;
 
 
-		crtc->base.hwmode = crtc_state->base.adjusted_mode;
-
 		memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
 		memset(&crtc->base.mode, 0, sizeof(crtc->base.mode));
 		if (crtc_state->base.active) {
 		if (crtc_state->base.active) {
 			intel_mode_from_pipe_config(&crtc->base.mode, crtc_state);
 			intel_mode_from_pipe_config(&crtc->base.mode, crtc_state);
@@ -15456,7 +15448,8 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
 			if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled)
 			if (IS_BROADWELL(dev_priv) && crtc_state->ips_enabled)
 				pixclk = DIV_ROUND_UP(pixclk * 100, 95);
 				pixclk = DIV_ROUND_UP(pixclk * 100, 95);
 
 
-			drm_calc_timestamping_constants(&crtc->base, &crtc->base.hwmode);
+			drm_calc_timestamping_constants(&crtc->base,
+							&crtc_state->base.adjusted_mode);
 			update_scanline_offset(crtc);
 			update_scanline_offset(crtc);
 		}
 		}
 
 

+ 42 - 6
drivers/gpu/drm/i915/intel_dp_mst.c

@@ -39,7 +39,7 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
 	struct intel_dp *intel_dp = &intel_dig_port->dp;
 	struct intel_dp *intel_dp = &intel_dig_port->dp;
 	struct intel_connector *connector =
 	struct intel_connector *connector =
 		to_intel_connector(conn_state->connector);
 		to_intel_connector(conn_state->connector);
-	struct drm_atomic_state *state;
+	struct drm_atomic_state *state = pipe_config->base.state;
 	int bpp;
 	int bpp;
 	int lane_count, slots;
 	int lane_count, slots;
 	const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
 	const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
@@ -57,20 +57,24 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
 	 * seem to suggest we should do otherwise.
 	 * seem to suggest we should do otherwise.
 	 */
 	 */
 	lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
 	lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
-
 	pipe_config->lane_count = lane_count;
 	pipe_config->lane_count = lane_count;
 
 
 	pipe_config->pipe_bpp = bpp;
 	pipe_config->pipe_bpp = bpp;
-	pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
 
 
-	state = pipe_config->base.state;
+	pipe_config->port_clock = intel_dp_max_link_rate(intel_dp);
 
 
 	if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port))
 	if (drm_dp_mst_port_has_audio(&intel_dp->mst_mgr, connector->port))
 		pipe_config->has_audio = true;
 		pipe_config->has_audio = true;
-	mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
 
 
+	mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
 	pipe_config->pbn = mst_pbn;
 	pipe_config->pbn = mst_pbn;
-	slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+	slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
+					      connector->port, mst_pbn);
+	if (slots < 0) {
+		DRM_DEBUG_KMS("failed finding vcpi slots:%d\n", slots);
+		return false;
+	}
 
 
 	intel_link_compute_m_n(bpp, lane_count,
 	intel_link_compute_m_n(bpp, lane_count,
 			       adjusted_mode->crtc_clock,
 			       adjusted_mode->crtc_clock,
@@ -80,7 +84,38 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
 	pipe_config->dp_m_n.tu = slots;
 	pipe_config->dp_m_n.tu = slots;
 
 
 	return true;
 	return true;
+}
 
 
+static int intel_dp_mst_atomic_check(struct drm_connector *connector,
+		struct drm_connector_state *new_conn_state)
+{
+	struct drm_atomic_state *state = new_conn_state->state;
+	struct drm_connector_state *old_conn_state;
+	struct drm_crtc *old_crtc;
+	struct drm_crtc_state *crtc_state;
+	int slots, ret = 0;
+
+	old_conn_state = drm_atomic_get_old_connector_state(state, connector);
+	old_crtc = old_conn_state->crtc;
+	if (!old_crtc)
+		return ret;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state, old_crtc);
+	slots = to_intel_crtc_state(crtc_state)->dp_m_n.tu;
+	if (drm_atomic_crtc_needs_modeset(crtc_state) && slots > 0) {
+		struct drm_dp_mst_topology_mgr *mgr;
+		struct drm_encoder *old_encoder;
+
+		old_encoder = old_conn_state->best_encoder;
+		mgr = &enc_to_mst(old_encoder)->primary->dp.mst_mgr;
+
+		ret = drm_dp_atomic_release_vcpi_slots(state, mgr, slots);
+		if (ret)
+			DRM_DEBUG_KMS("failed releasing %d vcpi slots:%d\n", slots, ret);
+		else
+			to_intel_crtc_state(crtc_state)->dp_m_n.tu = 0;
+	}
+	return ret;
 }
 }
 
 
 static void intel_mst_disable_dp(struct intel_encoder *encoder,
 static void intel_mst_disable_dp(struct intel_encoder *encoder,
@@ -387,6 +422,7 @@ static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_fun
 	.mode_valid = intel_dp_mst_mode_valid,
 	.mode_valid = intel_dp_mst_mode_valid,
 	.atomic_best_encoder = intel_mst_atomic_best_encoder,
 	.atomic_best_encoder = intel_mst_atomic_best_encoder,
 	.best_encoder = intel_mst_best_encoder,
 	.best_encoder = intel_mst_best_encoder,
+	.atomic_check = intel_dp_mst_atomic_check,
 };
 };
 
 
 static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
 static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)

+ 0 - 1
drivers/gpu/drm/i915/intel_drv.h

@@ -869,7 +869,6 @@ struct intel_hdmi {
 	bool has_audio;
 	bool has_audio;
 	enum hdmi_force_audio force_audio;
 	enum hdmi_force_audio force_audio;
 	bool rgb_quant_range_selectable;
 	bool rgb_quant_range_selectable;
-	enum hdmi_picture_aspect aspect_ratio;
 	struct intel_connector *attached_connector;
 	struct intel_connector *attached_connector;
 	void (*write_infoframe)(struct drm_encoder *encoder,
 	void (*write_infoframe)(struct drm_encoder *encoder,
 				const struct intel_crtc_state *crtc_state,
 				const struct intel_crtc_state *crtc_state,

+ 3 - 15
drivers/gpu/drm/i915/intel_hdmi.c

@@ -1403,7 +1403,7 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder,
 	}
 	}
 
 
 	/* Set user selected PAR to incoming mode's member */
 	/* Set user selected PAR to incoming mode's member */
-	adjusted_mode->picture_aspect_ratio = intel_hdmi->aspect_ratio;
+	adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
 
 
 	pipe_config->lane_count = 4;
 	pipe_config->lane_count = 4;
 
 
@@ -1649,19 +1649,7 @@ intel_hdmi_set_property(struct drm_connector *connector,
 	}
 	}
 
 
 	if (property == connector->dev->mode_config.aspect_ratio_property) {
 	if (property == connector->dev->mode_config.aspect_ratio_property) {
-		switch (val) {
-		case DRM_MODE_PICTURE_ASPECT_NONE:
-			intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
-			break;
-		case DRM_MODE_PICTURE_ASPECT_4_3:
-			intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
-			break;
-		case DRM_MODE_PICTURE_ASPECT_16_9:
-			intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
-			break;
-		default:
-			return -EINVAL;
-		}
+		connector->state->picture_aspect_ratio = val;
 		goto done;
 		goto done;
 	}
 	}
 
 
@@ -1823,7 +1811,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
 	intel_attach_broadcast_rgb_property(connector);
 	intel_attach_broadcast_rgb_property(connector);
 	intel_hdmi->color_range_auto = true;
 	intel_hdmi->color_range_auto = true;
 	intel_attach_aspect_ratio_property(connector);
 	intel_attach_aspect_ratio_property(connector);
-	intel_hdmi->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+	connector->state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
 }
 }
 
 
 /*
 /*

+ 3 - 20
drivers/gpu/drm/i915/intel_sdvo.c

@@ -106,11 +106,6 @@ struct intel_sdvo {
 	uint32_t color_range;
 	uint32_t color_range;
 	bool color_range_auto;
 	bool color_range_auto;
 
 
-	/**
-	 * HDMI user specified aspect ratio
-	 */
-	enum hdmi_picture_aspect aspect_ratio;
-
 	/**
 	/**
 	 * This is set if we're going to treat the device as TV-out.
 	 * This is set if we're going to treat the device as TV-out.
 	 *
 	 *
@@ -1186,7 +1181,7 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
 
 
 	/* Set user selected PAR to incoming mode's member */
 	/* Set user selected PAR to incoming mode's member */
 	if (intel_sdvo->is_hdmi)
 	if (intel_sdvo->is_hdmi)
-		adjusted_mode->picture_aspect_ratio = intel_sdvo->aspect_ratio;
+		adjusted_mode->picture_aspect_ratio = conn_state->picture_aspect_ratio;
 
 
 	return true;
 	return true;
 }
 }
@@ -2067,19 +2062,7 @@ intel_sdvo_set_property(struct drm_connector *connector,
 	}
 	}
 
 
 	if (property == connector->dev->mode_config.aspect_ratio_property) {
 	if (property == connector->dev->mode_config.aspect_ratio_property) {
-		switch (val) {
-		case DRM_MODE_PICTURE_ASPECT_NONE:
-			intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
-			break;
-		case DRM_MODE_PICTURE_ASPECT_4_3:
-			intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_4_3;
-			break;
-		case DRM_MODE_PICTURE_ASPECT_16_9:
-			intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
-			break;
-		default:
-			return -EINVAL;
-		}
+		connector->state->picture_aspect_ratio = val;
 		goto done;
 		goto done;
 	}
 	}
 
 
@@ -2418,7 +2401,7 @@ intel_sdvo_add_hdmi_properties(struct intel_sdvo *intel_sdvo,
 		intel_sdvo->color_range_auto = true;
 		intel_sdvo->color_range_auto = true;
 	}
 	}
 	intel_attach_aspect_ratio_property(&connector->base.base);
 	intel_attach_aspect_ratio_property(&connector->base.base);
-	intel_sdvo->aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
+	connector->base.base.state->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE;
 }
 }
 
 
 static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)
 static struct intel_sdvo_connector *intel_sdvo_connector_alloc(void)

+ 8 - 37
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c

@@ -527,31 +527,28 @@ static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
 	return NULL;
 	return NULL;
 }
 }
 
 
-static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
-			       unsigned int flags, int *vpos, int *hpos,
-			       ktime_t *stime, ktime_t *etime,
-			       const struct drm_display_mode *mode)
+static bool mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
+				bool in_vblank_irq, int *vpos, int *hpos,
+				ktime_t *stime, ktime_t *etime,
+				const struct drm_display_mode *mode)
 {
 {
 	struct msm_drm_private *priv = dev->dev_private;
 	struct msm_drm_private *priv = dev->dev_private;
 	struct drm_crtc *crtc;
 	struct drm_crtc *crtc;
 	struct drm_encoder *encoder;
 	struct drm_encoder *encoder;
 	int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
 	int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
-	int ret = 0;
 
 
 	crtc = priv->crtcs[pipe];
 	crtc = priv->crtcs[pipe];
 	if (!crtc) {
 	if (!crtc) {
 		DRM_ERROR("Invalid crtc %d\n", pipe);
 		DRM_ERROR("Invalid crtc %d\n", pipe);
-		return 0;
+		return false;
 	}
 	}
 
 
 	encoder = get_encoder_from_crtc(crtc);
 	encoder = get_encoder_from_crtc(crtc);
 	if (!encoder) {
 	if (!encoder) {
 		DRM_ERROR("no encoder found for crtc %d\n", pipe);
 		DRM_ERROR("no encoder found for crtc %d\n", pipe);
-		return 0;
+		return false;
 	}
 	}
 
 
-	ret |= DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE;
-
 	vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
 	vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
 	vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
 	vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
 
 
@@ -575,10 +572,8 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
 
 
 	if (line < vactive_start) {
 	if (line < vactive_start) {
 		line -= vactive_start;
 		line -= vactive_start;
-		ret |= DRM_SCANOUTPOS_IN_VBLANK;
 	} else if (line > vactive_end) {
 	} else if (line > vactive_end) {
 		line = line - vfp_end - vactive_start;
 		line = line - vfp_end - vactive_start;
-		ret |= DRM_SCANOUTPOS_IN_VBLANK;
 	} else {
 	} else {
 		line -= vactive_start;
 		line -= vactive_start;
 	}
 	}
@@ -589,31 +584,7 @@ static int mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
 	if (etime)
 	if (etime)
 		*etime = ktime_get();
 		*etime = ktime_get();
 
 
-	return ret;
-}
-
-static int mdp5_get_vblank_timestamp(struct drm_device *dev, unsigned int pipe,
-				     int *max_error,
-				     struct timeval *vblank_time,
-				     unsigned flags)
-{
-	struct msm_drm_private *priv = dev->dev_private;
-	struct drm_crtc *crtc;
-
-	if (pipe < 0 || pipe >= priv->num_crtcs) {
-		DRM_ERROR("Invalid crtc %d\n", pipe);
-		return -EINVAL;
-	}
-
-	crtc = priv->crtcs[pipe];
-	if (!crtc) {
-		DRM_ERROR("Invalid crtc %d\n", pipe);
-		return -EINVAL;
-	}
-
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, pipe, max_error,
-						     vblank_time, flags,
-						     &crtc->mode);
+	return true;
 }
 }
 
 
 static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
 static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
@@ -725,7 +696,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
 	dev->mode_config.max_width = 0xffff;
 	dev->mode_config.max_width = 0xffff;
 	dev->mode_config.max_height = 0xffff;
 	dev->mode_config.max_height = 0xffff;
 
 
-	dev->driver->get_vblank_timestamp = mdp5_get_vblank_timestamp;
+	dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
 	dev->driver->get_scanout_position = mdp5_get_scanoutpos;
 	dev->driver->get_scanout_position = mdp5_get_scanoutpos;
 	dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
 	dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
 	dev->max_vblank_count = 0xffffffff;
 	dev->max_vblank_count = 0xffffffff;

+ 8 - 31
drivers/gpu/drm/nouveau/nouveau_display.c

@@ -98,7 +98,7 @@ calc(int blanks, int blanke, int total, int line)
 	return line;
 	return line;
 }
 }
 
 
-static int
+static bool
 nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
 nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
 				ktime_t *stime, ktime_t *etime)
 				ktime_t *stime, ktime_t *etime)
 {
 {
@@ -111,16 +111,16 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
 	};
 	};
 	struct nouveau_display *disp = nouveau_display(crtc->dev);
 	struct nouveau_display *disp = nouveau_display(crtc->dev);
 	struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
 	struct drm_vblank_crtc *vblank = &crtc->dev->vblank[drm_crtc_index(crtc)];
-	int ret, retry = 20;
+	int retry = 20;
+	bool ret = false;
 
 
 	do {
 	do {
 		ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
 		ret = nvif_mthd(&disp->disp, 0, &args, sizeof(args));
 		if (ret != 0)
 		if (ret != 0)
-			return 0;
+			return false;
 
 
 		if (args.scan.vline) {
 		if (args.scan.vline) {
-			ret |= DRM_SCANOUTPOS_ACCURATE;
-			ret |= DRM_SCANOUTPOS_VALID;
+			ret = true;
 			break;
 			break;
 		}
 		}
 
 
@@ -133,14 +133,12 @@ nouveau_display_scanoutpos_head(struct drm_crtc *crtc, int *vpos, int *hpos,
 	if (stime) *stime = ns_to_ktime(args.scan.time[0]);
 	if (stime) *stime = ns_to_ktime(args.scan.time[0]);
 	if (etime) *etime = ns_to_ktime(args.scan.time[1]);
 	if (etime) *etime = ns_to_ktime(args.scan.time[1]);
 
 
-	if (*vpos < 0)
-		ret |= DRM_SCANOUTPOS_IN_VBLANK;
 	return ret;
 	return ret;
 }
 }
 
 
-int
+bool
 nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
 nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
-			   unsigned int flags, int *vpos, int *hpos,
+			   bool in_vblank_irq, int *vpos, int *hpos,
 			   ktime_t *stime, ktime_t *etime,
 			   ktime_t *stime, ktime_t *etime,
 			   const struct drm_display_mode *mode)
 			   const struct drm_display_mode *mode)
 {
 {
@@ -153,28 +151,7 @@ nouveau_display_scanoutpos(struct drm_device *dev, unsigned int pipe,
 		}
 		}
 	}
 	}
 
 
-	return 0;
-}
-
-int
-nouveau_display_vblstamp(struct drm_device *dev, unsigned int pipe,
-			 int *max_error, struct timeval *time, unsigned flags)
-{
-	struct drm_crtc *crtc;
-
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		if (nouveau_crtc(crtc)->index == pipe) {
-			struct drm_display_mode *mode;
-			if (drm_drv_uses_atomic_modeset(dev))
-				mode = &crtc->state->adjusted_mode;
-			else
-				mode = &crtc->hwmode;
-			return drm_calc_vbltimestamp_from_scanoutpos(dev,
-					pipe, max_error, time, flags, mode);
-		}
-	}
-
-	return -EINVAL;
+	return false;
 }
 }
 
 
 static void
 static void

+ 3 - 5
drivers/gpu/drm/nouveau/nouveau_display.h

@@ -68,11 +68,9 @@ int  nouveau_display_suspend(struct drm_device *dev, bool runtime);
 void nouveau_display_resume(struct drm_device *dev, bool runtime);
 void nouveau_display_resume(struct drm_device *dev, bool runtime);
 int  nouveau_display_vblank_enable(struct drm_device *, unsigned int);
 int  nouveau_display_vblank_enable(struct drm_device *, unsigned int);
 void nouveau_display_vblank_disable(struct drm_device *, unsigned int);
 void nouveau_display_vblank_disable(struct drm_device *, unsigned int);
-int  nouveau_display_scanoutpos(struct drm_device *, unsigned int,
-				unsigned int, int *, int *, ktime_t *,
-				ktime_t *, const struct drm_display_mode *);
-int  nouveau_display_vblstamp(struct drm_device *, unsigned int, int *,
-			      struct timeval *, unsigned);
+bool  nouveau_display_scanoutpos(struct drm_device *, unsigned int,
+				 bool, int *, int *, ktime_t *,
+				 ktime_t *, const struct drm_display_mode *);
 
 
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 int  nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 			    struct drm_pending_vblank_event *event,
 			    struct drm_pending_vblank_event *event,

+ 2 - 9
drivers/gpu/drm/nouveau/nouveau_drm.c

@@ -881,7 +881,7 @@ done:
 }
 }
 
 
 static void
 static void
-nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
+nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
 {
 {
 	struct nouveau_cli *cli = nouveau_cli(fpriv);
 	struct nouveau_cli *cli = nouveau_cli(fpriv);
 	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nouveau_drm *drm = nouveau_drm(dev);
@@ -897,12 +897,6 @@ nouveau_drm_preclose(struct drm_device *dev, struct drm_file *fpriv)
 	list_del(&cli->head);
 	list_del(&cli->head);
 	mutex_unlock(&drm->client.mutex);
 	mutex_unlock(&drm->client.mutex);
 
 
-}
-
-static void
-nouveau_drm_postclose(struct drm_device *dev, struct drm_file *fpriv)
-{
-	struct nouveau_cli *cli = nouveau_cli(fpriv);
 	nouveau_cli_fini(cli);
 	nouveau_cli_fini(cli);
 	kfree(cli);
 	kfree(cli);
 	pm_runtime_mark_last_busy(dev->dev);
 	pm_runtime_mark_last_busy(dev->dev);
@@ -974,7 +968,6 @@ driver_stub = {
 	.load = nouveau_drm_load,
 	.load = nouveau_drm_load,
 	.unload = nouveau_drm_unload,
 	.unload = nouveau_drm_unload,
 	.open = nouveau_drm_open,
 	.open = nouveau_drm_open,
-	.preclose = nouveau_drm_preclose,
 	.postclose = nouveau_drm_postclose,
 	.postclose = nouveau_drm_postclose,
 	.lastclose = nouveau_vga_lastclose,
 	.lastclose = nouveau_vga_lastclose,
 
 
@@ -985,7 +978,7 @@ driver_stub = {
 	.enable_vblank = nouveau_display_vblank_enable,
 	.enable_vblank = nouveau_display_vblank_enable,
 	.disable_vblank = nouveau_display_vblank_disable,
 	.disable_vblank = nouveau_display_vblank_disable,
 	.get_scanout_position = nouveau_display_scanoutpos,
 	.get_scanout_position = nouveau_display_scanoutpos,
-	.get_vblank_timestamp = nouveau_display_vblstamp,
+	.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
 
 
 	.ioctls = nouveau_ioctls,
 	.ioctls = nouveau_ioctls,
 	.num_ioctls = ARRAY_SIZE(nouveau_ioctls),
 	.num_ioctls = ARRAY_SIZE(nouveau_ioctls),

+ 12 - 0
drivers/gpu/drm/pl111/Kconfig

@@ -0,0 +1,12 @@
+config DRM_PL111
+	tristate "DRM Support for PL111 CLCD Controller"
+	depends on DRM
+	depends on ARM || ARM64 || COMPILE_TEST
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_GEM_CMA_HELPER
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+	help
+	  Choose this option for DRM support for the PL111 CLCD controller.
+	  If M is selected the module will be called pl111_drm.
+

+ 5 - 0
drivers/gpu/drm/pl111/Makefile

@@ -0,0 +1,5 @@
+pl111_drm-y +=	pl111_connector.o \
+		pl111_display.o \
+		pl111_drv.o
+
+obj-$(CONFIG_DRM_PL111) += pl111_drm.o

+ 127 - 0
drivers/gpu/drm/pl111/pl111_connector.c

@@ -0,0 +1,127 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * pl111_drm_connector.c
+ * Implementation of the connector functions for PL111 DRM
+ */
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+
+#include "pl111_drm.h"
+
+static void pl111_connector_destroy(struct drm_connector *connector)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	if (pl111_connector->panel)
+		drm_panel_detach(pl111_connector->panel);
+
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static enum drm_connector_status pl111_connector_detect(struct drm_connector
+							*connector, bool force)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	return (pl111_connector->panel ?
+		connector_status_connected :
+		connector_status_disconnected);
+}
+
+static int pl111_connector_helper_get_modes(struct drm_connector *connector)
+{
+	struct pl111_drm_connector *pl111_connector =
+		to_pl111_connector(connector);
+
+	if (!pl111_connector->panel)
+		return 0;
+
+	return drm_panel_get_modes(pl111_connector->panel);
+}
+
+const struct drm_connector_funcs connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = pl111_connector_destroy,
+	.detect = pl111_connector_detect,
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+const struct drm_connector_helper_funcs connector_helper_funcs = {
+	.get_modes = pl111_connector_helper_get_modes,
+};
+
+/* Walks the OF graph to find the panel node and then asks DRM to look
+ * up the panel.
+ */
+static struct drm_panel *pl111_get_panel(struct device *dev)
+{
+	struct device_node *endpoint, *panel_node;
+	struct device_node *np = dev->of_node;
+	struct drm_panel *panel;
+
+	endpoint = of_graph_get_next_endpoint(np, NULL);
+	if (!endpoint) {
+		dev_err(dev, "no endpoint to fetch panel\n");
+		return NULL;
+	}
+
+	/* don't proceed if we have an endpoint but no panel_node tied to it */
+	panel_node = of_graph_get_remote_port_parent(endpoint);
+	of_node_put(endpoint);
+	if (!panel_node) {
+		dev_err(dev, "no valid panel node\n");
+		return NULL;
+	}
+
+	panel = of_drm_find_panel(panel_node);
+	of_node_put(panel_node);
+
+	return panel;
+}
+
+int pl111_connector_init(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	struct pl111_drm_connector *pl111_connector = &priv->connector;
+	struct drm_connector *connector = &pl111_connector->connector;
+
+	drm_connector_init(dev, connector, &connector_funcs,
+			   DRM_MODE_CONNECTOR_DPI);
+	drm_connector_helper_add(connector, &connector_helper_funcs);
+
+	pl111_connector->panel = pl111_get_panel(dev->dev);
+	if (pl111_connector->panel)
+		drm_panel_attach(pl111_connector->panel, connector);
+
+	return 0;
+}
+

+ 344 - 0
drivers/gpu/drm/pl111/pl111_display.c

@@ -0,0 +1,344 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#include <linux/amba/clcd-regs.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/dma-buf.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+irqreturn_t pl111_irq(int irq, void *data)
+{
+	struct pl111_drm_dev_private *priv = data;
+	u32 irq_stat;
+	irqreturn_t status = IRQ_NONE;
+
+	irq_stat = readl(priv->regs + CLCD_PL111_MIS);
+
+	if (!irq_stat)
+		return IRQ_NONE;
+
+	if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) {
+		drm_crtc_handle_vblank(&priv->pipe.crtc);
+
+		status = IRQ_HANDLED;
+	}
+
+	/* Clear the interrupt once done */
+	writel(irq_stat, priv->regs + CLCD_PL111_ICR);
+
+	return status;
+}
+
+static u32 pl111_get_fb_offset(struct drm_plane_state *pstate)
+{
+	struct drm_framebuffer *fb = pstate->fb;
+	struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
+
+	return (obj->paddr +
+		fb->offsets[0] +
+		fb->format->cpp[0] * pstate->src_x +
+		fb->pitches[0] * pstate->src_y);
+}
+
+static int pl111_display_check(struct drm_simple_display_pipe *pipe,
+			       struct drm_plane_state *pstate,
+			       struct drm_crtc_state *cstate)
+{
+	const struct drm_display_mode *mode = &cstate->mode;
+	struct drm_framebuffer *old_fb = pipe->plane.state->fb;
+	struct drm_framebuffer *fb = pstate->fb;
+
+	if (mode->hdisplay % 16)
+		return -EINVAL;
+
+	if (fb) {
+		u32 offset = pl111_get_fb_offset(pstate);
+
+		/* FB base address must be dword aligned. */
+		if (offset & 3)
+			return -EINVAL;
+
+		/* There's no pitch register -- the mode's hdisplay
+		 * controls it.
+		 */
+		if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0])
+			return -EINVAL;
+
+		/* We can't change the FB format in a flicker-free
+		 * manner (and only update it during CRTC enable).
+		 */
+		if (old_fb && old_fb->format != fb->format)
+			cstate->mode_changed = true;
+	}
+
+	return 0;
+}
+
+static void pl111_display_enable(struct drm_simple_display_pipe *pipe,
+				 struct drm_crtc_state *cstate)
+{
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_plane *plane = &pipe->plane;
+	struct drm_device *drm = crtc->dev;
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+	const struct drm_display_mode *mode = &cstate->mode;
+	struct drm_framebuffer *fb = plane->state->fb;
+	struct drm_connector *connector = &priv->connector.connector;
+	u32 cntl;
+	u32 ppl, hsw, hfp, hbp;
+	u32 lpp, vsw, vfp, vbp;
+	u32 cpl;
+	int ret;
+
+	ret = clk_set_rate(priv->clk, mode->clock * 1000);
+	if (ret) {
+		dev_err(drm->dev,
+			"Failed to set pixel clock rate to %d: %d\n",
+			mode->clock * 1000, ret);
+	}
+
+	clk_prepare_enable(priv->clk);
+
+	ppl = (mode->hdisplay / 16) - 1;
+	hsw = mode->hsync_end - mode->hsync_start - 1;
+	hfp = mode->hsync_start - mode->hdisplay - 1;
+	hbp = mode->htotal - mode->hsync_end - 1;
+
+	lpp = mode->vdisplay - 1;
+	vsw = mode->vsync_end - mode->vsync_start - 1;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+	cpl = mode->hdisplay - 1;
+
+	writel((ppl << 2) |
+	       (hsw << 8) |
+	       (hfp << 16) |
+	       (hbp << 24),
+	       priv->regs + CLCD_TIM0);
+	writel(lpp |
+	       (vsw << 10) |
+	       (vfp << 16) |
+	       (vbp << 24),
+	       priv->regs + CLCD_TIM1);
+	/* XXX: We currently always use CLCDCLK with no divisor.  We
+	 * could probably reduce power consumption by using HCLK
+	 * (apb_pclk) with a divisor when it gets us near our target
+	 * pixel clock.
+	 */
+	writel(((mode->flags & DRM_MODE_FLAG_NHSYNC) ? TIM2_IHS : 0) |
+	       ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? TIM2_IVS : 0) |
+	       ((connector->display_info.bus_flags &
+		 DRM_BUS_FLAG_DE_LOW) ? TIM2_IOE : 0) |
+	       ((connector->display_info.bus_flags &
+		 DRM_BUS_FLAG_PIXDATA_NEGEDGE) ? TIM2_IPC : 0) |
+	       TIM2_BCD |
+	       (cpl << 16),
+	       priv->regs + CLCD_TIM2);
+	writel(0, priv->regs + CLCD_TIM3);
+
+	drm_panel_prepare(priv->connector.panel);
+
+	/* Enable and Power Up */
+	cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDPWR | CNTL_LCDVCOMP(1);
+
+	/* Note that the the hardware's format reader takes 'r' from
+	 * the low bit, while DRM formats list channels from high bit
+	 * to low bit as you read left to right.
+	 */
+	switch (fb->format->format) {
+	case DRM_FORMAT_ABGR8888:
+	case DRM_FORMAT_XBGR8888:
+		cntl |= CNTL_LCDBPP24;
+		break;
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+		cntl |= CNTL_LCDBPP24 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_BGR565:
+		cntl |= CNTL_LCDBPP16_565;
+		break;
+	case DRM_FORMAT_RGB565:
+		cntl |= CNTL_LCDBPP16_565 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_ABGR1555:
+	case DRM_FORMAT_XBGR1555:
+		cntl |= CNTL_LCDBPP16;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		cntl |= CNTL_LCDBPP16 | CNTL_BGR;
+		break;
+	case DRM_FORMAT_ABGR4444:
+	case DRM_FORMAT_XBGR4444:
+		cntl |= CNTL_LCDBPP16_444;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_XRGB4444:
+		cntl |= CNTL_LCDBPP16_444 | CNTL_BGR;
+		break;
+	default:
+		WARN_ONCE(true, "Unknown FB format 0x%08x\n",
+			  fb->format->format);
+		break;
+	}
+
+	writel(cntl, priv->regs + CLCD_PL111_CNTL);
+
+	drm_panel_enable(priv->connector.panel);
+
+	drm_crtc_vblank_on(crtc);
+}
+
+void pl111_display_disable(struct drm_simple_display_pipe *pipe)
+{
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_device *drm = crtc->dev;
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+
+	drm_crtc_vblank_off(crtc);
+
+	drm_panel_disable(priv->connector.panel);
+
+	/* Disable and Power Down */
+	writel(0, priv->regs + CLCD_PL111_CNTL);
+
+	drm_panel_unprepare(priv->connector.panel);
+
+	clk_disable_unprepare(priv->clk);
+}
+
+static void pl111_display_update(struct drm_simple_display_pipe *pipe,
+				 struct drm_plane_state *old_pstate)
+{
+	struct drm_crtc *crtc = &pipe->crtc;
+	struct drm_device *drm = crtc->dev;
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+	struct drm_pending_vblank_event *event = crtc->state->event;
+	struct drm_plane *plane = &pipe->plane;
+	struct drm_plane_state *pstate = plane->state;
+	struct drm_framebuffer *fb = pstate->fb;
+
+	if (fb) {
+		u32 addr = pl111_get_fb_offset(pstate);
+
+		writel(addr, priv->regs + CLCD_UBAS);
+	}
+
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+
+	writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + CLCD_PL111_IENB);
+
+	return 0;
+}
+
+void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc)
+{
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+
+	writel(0, priv->regs + CLCD_PL111_IENB);
+}
+
+static int pl111_display_prepare_fb(struct drm_simple_display_pipe *pipe,
+				    struct drm_plane_state *plane_state)
+{
+	return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
+}
+
+const struct drm_simple_display_pipe_funcs pl111_display_funcs = {
+	.check = pl111_display_check,
+	.enable = pl111_display_enable,
+	.disable = pl111_display_disable,
+	.update = pl111_display_update,
+	.prepare_fb = pl111_display_prepare_fb,
+};
+
+int pl111_display_init(struct drm_device *drm)
+{
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+	struct device *dev = drm->dev;
+	struct device_node *endpoint;
+	u32 tft_r0b0g0[3];
+	int ret;
+	static const u32 formats[] = {
+		DRM_FORMAT_ABGR8888,
+		DRM_FORMAT_XBGR8888,
+		DRM_FORMAT_ARGB8888,
+		DRM_FORMAT_XRGB8888,
+		DRM_FORMAT_BGR565,
+		DRM_FORMAT_RGB565,
+		DRM_FORMAT_ABGR1555,
+		DRM_FORMAT_XBGR1555,
+		DRM_FORMAT_ARGB1555,
+		DRM_FORMAT_XRGB1555,
+		DRM_FORMAT_ABGR4444,
+		DRM_FORMAT_XBGR4444,
+		DRM_FORMAT_ARGB4444,
+		DRM_FORMAT_XRGB4444,
+	};
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint)
+		return -ENODEV;
+
+	if (of_property_read_u32_array(endpoint,
+				       "arm,pl11x,tft-r0g0b0-pads",
+				       tft_r0b0g0,
+				       ARRAY_SIZE(tft_r0b0g0)) != 0) {
+		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads should be 3 ints\n");
+		of_node_put(endpoint);
+		return -ENOENT;
+	}
+	of_node_put(endpoint);
+
+	if (tft_r0b0g0[0] != 0 ||
+	    tft_r0b0g0[1] != 8 ||
+	    tft_r0b0g0[2] != 16) {
+		dev_err(dev, "arm,pl11x,tft-r0g0b0-pads != [0,8,16] not yet supported\n");
+		return -EINVAL;
+	}
+
+	ret = drm_simple_display_pipe_init(drm, &priv->pipe,
+					   &pl111_display_funcs,
+					   formats, ARRAY_SIZE(formats),
+					   &priv->connector.connector);
+	if (ret)
+		return ret;
+
+	return 0;
+}

+ 56 - 0
drivers/gpu/drm/pl111/pl111_drm.h

@@ -0,0 +1,56 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+#ifndef _PL111_DRM_H_
+#define _PL111_DRM_H_
+
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#define CLCD_IRQ_NEXTBASE_UPDATE BIT(2)
+
+struct pl111_drm_connector {
+	struct drm_connector connector;
+	struct drm_panel *panel;
+};
+
+struct pl111_drm_dev_private {
+	struct drm_device *drm;
+
+	struct pl111_drm_connector connector;
+	struct drm_simple_display_pipe pipe;
+	struct drm_fbdev_cma *fbdev;
+
+	void *regs;
+	struct clk *clk;
+};
+
+#define to_pl111_connector(x) \
+	container_of(x, struct pl111_drm_connector, connector)
+
+int pl111_display_init(struct drm_device *dev);
+int pl111_enable_vblank(struct drm_device *drm, unsigned int crtc);
+void pl111_disable_vblank(struct drm_device *drm, unsigned int crtc);
+irqreturn_t pl111_irq(int irq, void *data);
+int pl111_connector_init(struct drm_device *dev);
+int pl111_encoder_init(struct drm_device *dev);
+int pl111_dumb_create(struct drm_file *file_priv,
+		      struct drm_device *dev,
+		      struct drm_mode_create_dumb *args);
+
+#endif /* _PL111_DRM_H_ */

+ 272 - 0
drivers/gpu/drm/pl111/pl111_drv.c

@@ -0,0 +1,272 @@
+/*
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (c) 2006-2008 Intel Corporation
+ * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms of
+ * such GNU licence.
+ *
+ */
+
+/**
+ * DOC: ARM PrimeCell PL111 CLCD Driver
+ *
+ * The PL111 is a simple LCD controller that can support TFT and STN
+ * displays.  This driver exposes a standard KMS interface for them.
+ *
+ * This driver uses the same Device Tree binding as the fbdev CLCD
+ * driver.  While the fbdev driver supports panels that may be
+ * connected to the CLCD internally to the CLCD driver, in DRM the
+ * panels get split out to drivers/gpu/drm/panels/.  This means that,
+ * in converting from using fbdev to using DRM, you also need to write
+ * a panel driver (which may be as simple as an entry in
+ * panel-simple.c).
+ *
+ * The driver currently doesn't expose the cursor.  The DRM API for
+ * cursors requires support for 64x64 ARGB8888 cursor images, while
+ * the hardware can only support 64x64 monochrome with masking
+ * cursors.  While one could imagine trying to hack something together
+ * to look at the ARGB8888 and program reasonable in monochrome, we
+ * just don't expose the cursor at all instead, and leave cursor
+ * support to the X11 software cursor layer.
+ *
+ * TODO:
+ *
+ * - Fix race between setting plane base address and getting IRQ for
+ *   vsync firing the pageflip completion.
+ *
+ * - Expose the correct set of formats we can support based on the
+ *   "arm,pl11x,tft-r0g0b0-pads" DT property.
+ *
+ * - Use the "max-memory-bandwidth" DT property to filter the
+ *   supported formats.
+ *
+ * - Read back hardware state at boot to skip reprogramming the
+ *   hardware when doing a no-op modeset.
+ *
+ * - Use the internal clock divisor to reduce power consumption by
+ *   using HCLK (apb_pclk) when appropriate.
+ */
+
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd-regs.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "pl111_drm.h"
+
+#define DRIVER_DESC      "DRM module for PL111"
+
+struct drm_mode_config_funcs mode_config_funcs = {
+	.fb_create = drm_fb_cma_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static int pl111_modeset_init(struct drm_device *dev)
+{
+	struct drm_mode_config *mode_config;
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+	int ret = 0;
+
+	drm_mode_config_init(dev);
+	mode_config = &dev->mode_config;
+	mode_config->funcs = &mode_config_funcs;
+	mode_config->min_width = 1;
+	mode_config->max_width = 1024;
+	mode_config->min_height = 1;
+	mode_config->max_height = 768;
+
+	ret = pl111_connector_init(dev);
+	if (ret) {
+		dev_err(dev->dev, "Failed to create pl111_drm_connector\n");
+		goto out_config;
+	}
+
+	/* Don't actually attach if we didn't find a drm_panel
+	 * attached to us.  This will allow a kernel to include both
+	 * the fbdev pl111 driver and this one, and choose between
+	 * them based on which subsystem has support for the panel.
+	 */
+	if (!priv->connector.panel) {
+		dev_info(dev->dev,
+			 "Disabling due to lack of DRM panel device.\n");
+		ret = -ENODEV;
+		goto out_config;
+	}
+
+	ret = pl111_display_init(dev);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to init display\n");
+		goto out_config;
+	}
+
+	ret = drm_vblank_init(dev, 1);
+	if (ret != 0) {
+		dev_err(dev->dev, "Failed to init vblank\n");
+		goto out_config;
+	}
+
+	drm_mode_config_reset(dev);
+
+	priv->fbdev = drm_fbdev_cma_init(dev, 32,
+					 dev->mode_config.num_connector);
+
+	drm_kms_helper_poll_init(dev);
+
+	goto finish;
+
+out_config:
+	drm_mode_config_cleanup(dev);
+finish:
+	return ret;
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drm_fops);
+
+static void pl111_lastclose(struct drm_device *dev)
+{
+	struct pl111_drm_dev_private *priv = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static struct drm_driver pl111_drm_driver = {
+	.driver_features =
+		DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
+	.lastclose = pl111_lastclose,
+	.ioctls = NULL,
+	.fops = &drm_fops,
+	.name = "pl111",
+	.desc = DRIVER_DESC,
+	.date = "20170317",
+	.major = 1,
+	.minor = 0,
+	.patchlevel = 0,
+	.dumb_create = drm_gem_cma_dumb_create,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.gem_free_object = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+
+	.enable_vblank = pl111_enable_vblank,
+	.disable_vblank = pl111_disable_vblank,
+
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
+};
+
+#ifdef CONFIG_ARM_AMBA
+static int pl111_amba_probe(struct amba_device *amba_dev,
+			    const struct amba_id *id)
+{
+	struct device *dev = &amba_dev->dev;
+	struct pl111_drm_dev_private *priv;
+	struct drm_device *drm;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	drm = drm_dev_alloc(&pl111_drm_driver, dev);
+	if (IS_ERR(drm))
+		return PTR_ERR(drm);
+	amba_set_drvdata(amba_dev, drm);
+	priv->drm = drm;
+	drm->dev_private = priv;
+
+	priv->clk = devm_clk_get(dev, "clcdclk");
+	if (IS_ERR(priv->clk)) {
+		dev_err(dev, "CLCD: unable to get clk.\n");
+		ret = PTR_ERR(priv->clk);
+		goto dev_unref;
+	}
+
+	priv->regs = devm_ioremap_resource(dev, &amba_dev->res);
+	if (!priv->regs) {
+		dev_err(dev, "%s failed mmio\n", __func__);
+		return -EINVAL;
+	}
+
+	/* turn off interrupts before requesting the irq */
+	writel(0, priv->regs + CLCD_PL111_IENB);
+
+	ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0,
+			       "pl111", priv);
+	if (ret != 0) {
+		dev_err(dev, "%s failed irq %d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = pl111_modeset_init(drm);
+	if (ret != 0)
+		goto dev_unref;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto dev_unref;
+
+	return 0;
+
+dev_unref:
+	drm_dev_unref(drm);
+	return ret;
+}
+
+static int pl111_amba_remove(struct amba_device *amba_dev)
+{
+	struct drm_device *drm = amba_get_drvdata(amba_dev);
+	struct pl111_drm_dev_private *priv = drm->dev_private;
+
+	drm_dev_unregister(drm);
+	if (priv->fbdev)
+		drm_fbdev_cma_fini(priv->fbdev);
+	drm_mode_config_cleanup(drm);
+	drm_dev_unref(drm);
+
+	return 0;
+}
+
+static struct amba_id pl111_id_table[] = {
+	{
+		.id = 0x00041111,
+		.mask = 0x000fffff,
+	},
+	{0, 0},
+};
+
+static struct amba_driver pl111_amba_driver = {
+	.drv = {
+		.name = "drm-clcd-pl111",
+	},
+	.probe = pl111_amba_probe,
+	.remove = pl111_amba_remove,
+	.id_table = pl111_id_table,
+};
+
+module_amba_driver(pl111_amba_driver);
+#endif /* CONFIG_ARM_AMBA */
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_LICENSE("GPL");

+ 12 - 6
drivers/gpu/drm/radeon/radeon_drv.c

@@ -115,10 +115,6 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
 u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 u32 radeon_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 int radeon_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 int radeon_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void radeon_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void radeon_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
-int radeon_get_vblank_timestamp_kms(struct drm_device *dev, unsigned int pipe,
-				    int *max_error,
-				    struct timeval *vblank_time,
-				    unsigned flags);
 void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
 void radeon_driver_irq_preinstall_kms(struct drm_device *dev);
 int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
 int radeon_driver_irq_postinstall_kms(struct drm_device *dev);
 void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
 void radeon_driver_irq_uninstall_kms(struct drm_device *dev);
@@ -530,6 +526,16 @@ static const struct file_operations radeon_driver_kms_fops = {
 #endif
 #endif
 };
 };
 
 
+static bool
+radeon_get_crtc_scanout_position(struct drm_device *dev, unsigned int pipe,
+				 bool in_vblank_irq, int *vpos, int *hpos,
+				 ktime_t *stime, ktime_t *etime,
+				 const struct drm_display_mode *mode)
+{
+	return radeon_get_crtc_scanoutpos(dev, pipe, 0, vpos, hpos,
+					  stime, etime, mode);
+}
+
 static struct drm_driver kms_driver = {
 static struct drm_driver kms_driver = {
 	.driver_features =
 	.driver_features =
 	    DRIVER_USE_AGP |
 	    DRIVER_USE_AGP |
@@ -544,8 +550,8 @@ static struct drm_driver kms_driver = {
 	.get_vblank_counter = radeon_get_vblank_counter_kms,
 	.get_vblank_counter = radeon_get_vblank_counter_kms,
 	.enable_vblank = radeon_enable_vblank_kms,
 	.enable_vblank = radeon_enable_vblank_kms,
 	.disable_vblank = radeon_disable_vblank_kms,
 	.disable_vblank = radeon_disable_vblank_kms,
-	.get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
-	.get_scanout_position = radeon_get_crtc_scanoutpos,
+	.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
+	.get_scanout_position = radeon_get_crtc_scanout_position,
 	.irq_preinstall = radeon_driver_irq_preinstall_kms,
 	.irq_preinstall = radeon_driver_irq_preinstall_kms,
 	.irq_postinstall = radeon_driver_irq_postinstall_kms,
 	.irq_postinstall = radeon_driver_irq_postinstall_kms,
 	.irq_uninstall = radeon_driver_irq_uninstall_kms,
 	.irq_uninstall = radeon_driver_irq_uninstall_kms,

+ 0 - 37
drivers/gpu/drm/radeon/radeon_kms.c

@@ -858,43 +858,6 @@ void radeon_disable_vblank_kms(struct drm_device *dev, int crtc)
 	spin_unlock_irqrestore(&rdev->irq.lock, irqflags);
 	spin_unlock_irqrestore(&rdev->irq.lock, irqflags);
 }
 }
 
 
-/**
- * radeon_get_vblank_timestamp_kms - get vblank timestamp
- *
- * @dev: drm dev pointer
- * @crtc: crtc to get the timestamp for
- * @max_error: max error
- * @vblank_time: time value
- * @flags: flags passed to the driver
- *
- * Gets the timestamp on the requested crtc based on the
- * scanout position.  (all asics).
- * Returns postive status flags on success, negative error on failure.
- */
-int radeon_get_vblank_timestamp_kms(struct drm_device *dev, int crtc,
-				    int *max_error,
-				    struct timeval *vblank_time,
-				    unsigned flags)
-{
-	struct drm_crtc *drmcrtc;
-	struct radeon_device *rdev = dev->dev_private;
-
-	if (crtc < 0 || crtc >= dev->num_crtcs) {
-		DRM_ERROR("Invalid crtc %d\n", crtc);
-		return -EINVAL;
-	}
-
-	/* Get associated drm_crtc: */
-	drmcrtc = &rdev->mode_info.crtcs[crtc]->base;
-	if (!drmcrtc)
-		return -EINVAL;
-
-	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc, max_error,
-						     vblank_time, flags,
-						     &drmcrtc->hwmode);
-}
-
 const struct drm_ioctl_desc radeon_ioctls_kms[] = {
 const struct drm_ioctl_desc radeon_ioctls_kms[] = {
 	DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF_DRV(RADEON_CP_INIT, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF_DRV(RADEON_CP_START, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 	DRM_IOCTL_DEF_DRV(RADEON_CP_START, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),

+ 3 - 0
drivers/gpu/drm/radeon/radeon_mode.h

@@ -691,6 +691,9 @@ struct atom_voltage_table
 };
 };
 
 
 /* Driver internal use only flags of radeon_get_crtc_scanoutpos() */
 /* Driver internal use only flags of radeon_get_crtc_scanoutpos() */
+#define DRM_SCANOUTPOS_VALID        (1 << 0)
+#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
+#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
 #define USE_REAL_VBLANKSTART 		(1 << 30)
 #define USE_REAL_VBLANKSTART 		(1 << 30)
 #define GET_DISTANCE_TO_VBLANKSTART	(1 << 31)
 #define GET_DISTANCE_TO_VBLANKSTART	(1 << 31)
 
 

+ 3 - 11
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c

@@ -104,26 +104,18 @@ static void analogix_dp_psr_work(struct work_struct *work)
 {
 {
 	struct rockchip_dp_device *dp =
 	struct rockchip_dp_device *dp =
 				container_of(work, typeof(*dp), psr_work);
 				container_of(work, typeof(*dp), psr_work);
-	struct drm_crtc *crtc = dp->encoder.crtc;
-	int psr_state = dp->psr_state;
-	int vact_end;
 	int ret;
 	int ret;
 	unsigned long flags;
 	unsigned long flags;
 
 
-	if (!crtc)
-		return;
-
-	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
-
-	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
-					  PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+	ret = rockchip_drm_wait_vact_end(dp->encoder.crtc,
+					 PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
 	if (ret) {
 	if (ret) {
 		dev_err(dp->dev, "line flag interrupt did not arrive\n");
 		dev_err(dp->dev, "line flag interrupt did not arrive\n");
 		return;
 		return;
 	}
 	}
 
 
 	spin_lock_irqsave(&dp->psr_lock, flags);
 	spin_lock_irqsave(&dp->psr_lock, flags);
-	if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+	if (dp->psr_state == EDP_VSC_PSR_STATE_ACTIVE)
 		analogix_dp_enable_psr(dp->dev);
 		analogix_dp_enable_psr(dp->dev);
 	else
 	else
 		analogix_dp_disable_psr(dp->dev);
 		analogix_dp_disable_psr(dp->dev);

+ 1 - 2
drivers/gpu/drm/rockchip/rockchip_drm_drv.h

@@ -62,8 +62,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
 				    struct device *dev);
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
-				unsigned int mstimeout);
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
 
 
 extern struct platform_driver cdn_dp_driver;
 extern struct platform_driver cdn_dp_driver;
 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
 extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;

+ 9 - 11
drivers/gpu/drm/rockchip/rockchip_drm_vop.c

@@ -468,7 +468,7 @@ static bool vop_line_flag_irq_is_enabled(struct vop *vop)
 	return !!line_flag_irq;
 	return !!line_flag_irq;
 }
 }
 
 
-static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+static void vop_line_flag_irq_enable(struct vop *vop)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
 
 
@@ -477,7 +477,6 @@ static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
 
 
 	spin_lock_irqsave(&vop->irq_lock, flags);
 	spin_lock_irqsave(&vop->irq_lock, flags);
 
 
-	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
 	VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
 	VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
 	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
 	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
 
 
@@ -981,6 +980,8 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 	VOP_CTRL_SET(vop, vact_st_end, val);
 	VOP_CTRL_SET(vop, vact_st_end, val);
 	VOP_CTRL_SET(vop, vpost_st_end, val);
 	VOP_CTRL_SET(vop, vpost_st_end, val);
 
 
+	VOP_CTRL_SET(vop, line_flag_num[0], vact_end);
+
 	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
 	clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
 
 
 	VOP_CTRL_SET(vop, standby, 0);
 	VOP_CTRL_SET(vop, standby, 0);
@@ -1507,19 +1508,16 @@ static void vop_win_init(struct vop *vop)
 }
 }
 
 
 /**
 /**
- * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * rockchip_drm_wait_vact_end
  * @crtc: CRTC to enable line flag
  * @crtc: CRTC to enable line flag
- * @line_num: interested line number
  * @mstimeout: millisecond for timeout
  * @mstimeout: millisecond for timeout
  *
  *
- * Driver would hold here until the interested line flag interrupt have
- * happened or timeout to wait.
+ * Wait for vact_end line flag irq or timeout.
  *
  *
  * Returns:
  * Returns:
  * Zero on success, negative errno on failure.
  * Zero on success, negative errno on failure.
  */
  */
-int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
-				unsigned int mstimeout)
+int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout)
 {
 {
 	struct vop *vop = to_vop(crtc);
 	struct vop *vop = to_vop(crtc);
 	unsigned long jiffies_left;
 	unsigned long jiffies_left;
@@ -1527,14 +1525,14 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
 	if (!crtc || !vop->is_enabled)
 	if (!crtc || !vop->is_enabled)
 		return -ENODEV;
 		return -ENODEV;
 
 
-	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+	if (mstimeout <= 0)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (vop_line_flag_irq_is_enabled(vop))
 	if (vop_line_flag_irq_is_enabled(vop))
 		return -EBUSY;
 		return -EBUSY;
 
 
 	reinit_completion(&vop->line_flag_completion);
 	reinit_completion(&vop->line_flag_completion);
-	vop_line_flag_irq_enable(vop, line_num);
+	vop_line_flag_irq_enable(vop);
 
 
 	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
 	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
 						   msecs_to_jiffies(mstimeout));
 						   msecs_to_jiffies(mstimeout));
@@ -1547,7 +1545,7 @@ int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
 
 
 	return 0;
 	return 0;
 }
 }
-EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+EXPORT_SYMBOL(rockchip_drm_wait_vact_end);
 
 
 static int vop_bind(struct device *dev, struct device *master, void *data)
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 {

+ 28 - 0
drivers/gpu/drm/selftests/test-drm_mm.c

@@ -514,6 +514,8 @@ static int igt_reserve(void *ignored)
 		ret = __igt_reserve(count, size + 1);
 		ret = __igt_reserve(count, size + 1);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
+
+		cond_resched();
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -712,6 +714,10 @@ static int igt_insert(void *ignored)
 			return ret;
 			return ret;
 
 
 		ret = __igt_insert(count, size + 1, false);
 		ret = __igt_insert(count, size + 1, false);
+		if (ret)
+			return ret;
+
+		cond_resched();
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -741,6 +747,10 @@ static int igt_replace(void *ignored)
 			return ret;
 			return ret;
 
 
 		ret = __igt_insert(count, size + 1, true);
 		ret = __igt_insert(count, size + 1, true);
+		if (ret)
+			return ret;
+
+		cond_resched();
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -1011,6 +1021,8 @@ static int igt_insert_range(void *ignored)
 		ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
 		ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
+
+		cond_resched();
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -1056,6 +1068,7 @@ static int igt_align(void *ignored)
 		drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_for_each_node_safe(node, next, &mm)
 			drm_mm_remove_node(node);
 			drm_mm_remove_node(node);
 		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
 		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -1097,6 +1110,8 @@ static int igt_align_pot(int max)
 			       align, bit);
 			       align, bit);
 			goto out;
 			goto out;
 		}
 		}
+
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -1471,6 +1486,8 @@ static int igt_evict(void *ignored)
 				goto out;
 				goto out;
 			}
 			}
 		}
 		}
+
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -1566,6 +1583,8 @@ static int igt_evict_range(void *ignored)
 				goto out;
 				goto out;
 			}
 			}
 		}
 		}
+
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -1683,6 +1702,7 @@ static int igt_topdown(void *ignored)
 		drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_for_each_node_safe(node, next, &mm)
 			drm_mm_remove_node(node);
 			drm_mm_remove_node(node);
 		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
 		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -1783,6 +1803,7 @@ static int igt_bottomup(void *ignored)
 		drm_mm_for_each_node_safe(node, next, &mm)
 		drm_mm_for_each_node_safe(node, next, &mm)
 			drm_mm_remove_node(node);
 			drm_mm_remove_node(node);
 		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
 		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -1970,6 +1991,8 @@ static int igt_color(void *ignored)
 			drm_mm_remove_node(node);
 			drm_mm_remove_node(node);
 			kfree(node);
 			kfree(node);
 		}
 		}
+
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -2047,6 +2070,7 @@ static int evict_color(struct drm_mm *mm,
 		}
 		}
 	}
 	}
 
 
+	cond_resched();
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2132,6 +2156,8 @@ static int igt_color_evict(void *ignored)
 				goto out;
 				goto out;
 			}
 			}
 		}
 		}
+
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;
@@ -2231,6 +2257,8 @@ static int igt_color_evict_range(void *ignored)
 				goto out;
 				goto out;
 			}
 			}
 		}
 		}
+
+		cond_resched();
 	}
 	}
 
 
 	ret = 0;
 	ret = 0;

+ 2 - 3
drivers/gpu/drm/sti/sti_cursor.c

@@ -33,7 +33,7 @@
 #define STI_CURS_MAX_SIZE   128
 #define STI_CURS_MAX_SIZE   128
 
 
 /*
 /*
- * pixmap dma buffer stucture
+ * pixmap dma buffer structure
  *
  *
  * @paddr:  physical address
  * @paddr:  physical address
  * @size:   buffer size
  * @size:   buffer size
@@ -121,8 +121,7 @@ static int cursor_dbg_show(struct seq_file *s, void *data)
 	cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
 	cursor_dbg_cml(s, cursor, readl(cursor->regs + CUR_CML));
 	DBGFS_DUMP(CUR_AWS);
 	DBGFS_DUMP(CUR_AWS);
 	DBGFS_DUMP(CUR_AWE);
 	DBGFS_DUMP(CUR_AWE);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 2
drivers/gpu/drm/sti/sti_dvo.c

@@ -186,8 +186,7 @@ static int dvo_dbg_show(struct seq_file *s, void *data)
 	DBGFS_DUMP(DVO_LUT_PROG_MID);
 	DBGFS_DUMP(DVO_LUT_PROG_MID);
 	DBGFS_DUMP(DVO_LUT_PROG_HIGH);
 	DBGFS_DUMP(DVO_LUT_PROG_HIGH);
 	dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
 	dvo_dbg_awg_microcode(s, dvo->regs + DVO_DIGSYNC_INSTR_I);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 

+ 2 - 3
drivers/gpu/drm/sti/sti_gdp.c

@@ -149,7 +149,7 @@ static void gdp_dbg_ctl(struct seq_file *s, int val)
 	seq_puts(s, "\tColor:");
 	seq_puts(s, "\tColor:");
 	for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) {
 	for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) {
 		if (gdp_format_to_str[i].format == (val & 0x1F)) {
 		if (gdp_format_to_str[i].format == (val & 0x1F)) {
-			seq_printf(s, gdp_format_to_str[i].name);
+			seq_puts(s, gdp_format_to_str[i].name);
 			break;
 			break;
 		}
 		}
 	}
 	}
@@ -266,8 +266,7 @@ static void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node)
 	seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2);
 	seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2);
 	seq_printf(s, "\n\tPPT  0x%08X", node->gam_gdp_ppt);
 	seq_printf(s, "\n\tPPT  0x%08X", node->gam_gdp_ppt);
 	gdp_dbg_ppt(s, node->gam_gdp_ppt);
 	gdp_dbg_ppt(s, node->gam_gdp_ppt);
-	seq_printf(s, "\n\tCML  0x%08X", node->gam_gdp_cml);
-	seq_puts(s, "\n");
+	seq_printf(s, "\n\tCML  0x%08X\n", node->gam_gdp_cml);
 }
 }
 
 
 static int gdp_node_dbg_show(struct seq_file *s, void *arg)
 static int gdp_node_dbg_show(struct seq_file *s, void *arg)

+ 3 - 6
drivers/gpu/drm/sti/sti_hda.c

@@ -320,8 +320,7 @@ static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
 {
 {
 	unsigned int i;
 	unsigned int i;
 
 
-	seq_puts(s, "\n\n");
-	seq_puts(s, "  HDA AWG microcode:");
+	seq_puts(s, "\n\n  HDA AWG microcode:");
 	for (i = 0; i < AWG_MAX_INST; i++) {
 	for (i = 0; i < AWG_MAX_INST; i++) {
 		if (i % 8 == 0)
 		if (i % 8 == 0)
 			seq_printf(s, "\n  %04X:", i);
 			seq_printf(s, "\n  %04X:", i);
@@ -333,8 +332,7 @@ static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg)
 {
 {
 	u32 val = readl(reg);
 	u32 val = readl(reg);
 
 
-	seq_puts(s, "\n");
-	seq_printf(s, "\n  %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
+	seq_printf(s, "\n\n  %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
 	seq_puts(s, "\tHD DACs ");
 	seq_puts(s, "\tHD DACs ");
 	seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled");
 	seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled");
 }
 }
@@ -356,8 +354,7 @@ static int hda_dbg_show(struct seq_file *s, void *data)
 	hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI);
 	hda_dbg_awg_microcode(s, hda->regs + HDA_SYNC_AWGI);
 	if (hda->video_dacs_ctrl)
 	if (hda->video_dacs_ctrl)
 		hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl);
 		hda_dbg_video_dacs_ctrl(s, hda->video_dacs_ctrl);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 

+ 10 - 13
drivers/gpu/drm/sti/sti_hdmi.c

@@ -592,7 +592,7 @@ static void hdmi_dbg_cfg(struct seq_file *s, int val)
 {
 {
 	int tmp;
 	int tmp;
 
 
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	tmp = val & HDMI_CFG_HDMI_NOT_DVI;
 	tmp = val & HDMI_CFG_HDMI_NOT_DVI;
 	DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI");
 	DBGFS_PRINT_STR("mode:", tmp ? "HDMI" : "DVI");
 	seq_puts(s, "\t\t\t\t\t");
 	seq_puts(s, "\t\t\t\t\t");
@@ -616,7 +616,7 @@ static void hdmi_dbg_sta(struct seq_file *s, int val)
 {
 {
 	int tmp;
 	int tmp;
 
 
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	tmp = (val & HDMI_STA_DLL_LCK);
 	tmp = (val & HDMI_STA_DLL_LCK);
 	DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked");
 	DBGFS_PRINT_STR("pll:", tmp ? "locked" : "not locked");
 	seq_puts(s, "\t\t\t\t\t");
 	seq_puts(s, "\t\t\t\t\t");
@@ -632,7 +632,7 @@ static void hdmi_dbg_sw_di_cfg(struct seq_file *s, int val)
 			       "once every field",
 			       "once every field",
 			       "once every frame"};
 			       "once every frame"};
 
 
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1));
 	tmp = (val & HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, 1));
 	DBGFS_PRINT_STR("Data island 1:", en_di[tmp]);
 	DBGFS_PRINT_STR("Data island 1:", en_di[tmp]);
 	seq_puts(s, "\t\t\t\t\t");
 	seq_puts(s, "\t\t\t\t\t");
@@ -664,16 +664,16 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
 	DBGFS_DUMP("\n", HDMI_STA);
 	DBGFS_DUMP("\n", HDMI_STA);
 	hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA));
 	hdmi_dbg_sta(s, hdmi_read(hdmi, HDMI_STA));
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN);
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMIN);
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN));
 	DBGFS_PRINT_INT("Xmin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMIN));
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX);
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_XMAX);
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX));
 	DBGFS_PRINT_INT("Xmax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_XMAX));
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN);
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMIN);
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN));
 	DBGFS_PRINT_INT("Ymin:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMIN));
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX);
 	DBGFS_DUMP("", HDMI_ACTIVE_VID_YMAX);
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX));
 	DBGFS_PRINT_INT("Ymax:", hdmi_read(hdmi, HDMI_ACTIVE_VID_YMAX));
 	DBGFS_DUMP("", HDMI_SW_DI_CFG);
 	DBGFS_DUMP("", HDMI_SW_DI_CFG);
 	hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG));
 	hdmi_dbg_sw_di_cfg(s, hdmi_read(hdmi, HDMI_SW_DI_CFG));
@@ -692,8 +692,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AVI);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AVI);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AVI);
-	seq_puts(s, "\n");
-	seq_printf(s, "\n AUDIO Infoframe (Data Island slot N=%d):",
+	seq_printf(s, "\n\n AUDIO Infoframe (Data Island slot N=%d):",
 		   HDMI_IFRAME_SLOT_AUDIO);
 		   HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_AUDIO);
@@ -703,8 +702,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_AUDIO);
-	seq_puts(s, "\n");
-	seq_printf(s, "\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
+	seq_printf(s, "\n\n VENDOR SPECIFIC Infoframe (Data Island slot N=%d):",
 		   HDMI_IFRAME_SLOT_VENDOR);
 		   HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_HEAD_WORD, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD0, HDMI_IFRAME_SLOT_VENDOR);
@@ -714,8 +712,7 @@ static int hdmi_dbg_show(struct seq_file *s, void *data)
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD4, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD5, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR);
 	DBGFS_DUMP_DI(HDMI_SW_DI_N_PKT_WORD6, HDMI_IFRAME_SLOT_VENDOR);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 

+ 3 - 4
drivers/gpu/drm/sti/sti_hqvdp.c

@@ -625,8 +625,7 @@ static int hqvdp_dbg_show(struct seq_file *s, void *data)
 		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
 		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);
 	}
 	}
 
 
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1357,12 +1356,12 @@ static int sti_hqvdp_probe(struct platform_device *pdev)
 
 
 	/* Get Memory resources */
 	/* Get Memory resources */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (res == NULL) {
+	if (!res) {
 		DRM_ERROR("Get memory resource failed\n");
 		DRM_ERROR("Get memory resource failed\n");
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
 	hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res));
 	hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res));
-	if (hqvdp->regs == NULL) {
+	if (!hqvdp->regs) {
 		DRM_ERROR("Register mapping failed\n");
 		DRM_ERROR("Register mapping failed\n");
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}

+ 1 - 2
drivers/gpu/drm/sti/sti_mixer.c

@@ -162,8 +162,7 @@ static int mixer_dbg_show(struct seq_file *s, void *arg)
 	DBGFS_DUMP(GAM_MIXER_MBP);
 	DBGFS_DUMP(GAM_MIXER_MBP);
 	DBGFS_DUMP(GAM_MIXER_MX0);
 	DBGFS_DUMP(GAM_MIXER_MX0);
 	mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
 	mixer_dbg_mxn(s, mixer->regs + GAM_MIXER_MX0);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 

+ 3 - 4
drivers/gpu/drm/sti/sti_tvout.c

@@ -459,7 +459,7 @@ static void tvout_dbg_vip(struct seq_file *s, int val)
 				   "Aux (color matrix by-passed)",
 				   "Aux (color matrix by-passed)",
 				   "", "", "", "", "", "Force value"};
 				   "", "", "", "", "", "Force value"};
 
 
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT;
 	r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
 	r = (val & mask) >> TVO_VIP_REORDER_R_SHIFT;
 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
 	mask = TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT;
@@ -558,8 +558,7 @@ static int tvout_dbg_show(struct seq_file *s, void *data)
 	DBGFS_DUMP(TVO_CSC_AUX_M6);
 	DBGFS_DUMP(TVO_CSC_AUX_M6);
 	DBGFS_DUMP(TVO_CSC_AUX_M7);
 	DBGFS_DUMP(TVO_CSC_AUX_M7);
 	DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
 	DBGFS_DUMP(TVO_AUX_IN_VID_FORMAT);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 
@@ -847,7 +846,7 @@ static int sti_tvout_probe(struct platform_device *pdev)
 
 
 	tvout->dev = dev;
 	tvout->dev = dev;
 
 
-	/* get Memory ressources */
+	/* get memory resources */
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg");
 	if (!res) {
 	if (!res) {
 		DRM_ERROR("Invalid glue resource\n");
 		DRM_ERROR("Invalid glue resource\n");

+ 2 - 3
drivers/gpu/drm/sti/sti_vid.c

@@ -61,7 +61,7 @@
 static void vid_dbg_ctl(struct seq_file *s, int val)
 static void vid_dbg_ctl(struct seq_file *s, int val)
 {
 {
 	val = val >> 30;
 	val = val >> 30;
-	seq_puts(s, "\t");
+	seq_putc(s, '\t');
 
 
 	if (!(val & 1))
 	if (!(val & 1))
 		seq_puts(s, "NOT ");
 		seq_puts(s, "NOT ");
@@ -114,8 +114,7 @@ static int vid_dbg_show(struct seq_file *s, void *arg)
 	DBGFS_DUMP(VID_BC);
 	DBGFS_DUMP(VID_BC);
 	DBGFS_DUMP(VID_TINT);
 	DBGFS_DUMP(VID_TINT);
 	DBGFS_DUMP(VID_CSAT);
 	DBGFS_DUMP(VID_CSAT);
-	seq_puts(s, "\n");
-
+	seq_putc(s, '\n');
 	return 0;
 	return 0;
 }
 }
 
 

+ 16 - 0
drivers/gpu/drm/stm/Kconfig

@@ -0,0 +1,16 @@
+config DRM_STM
+	tristate "DRM Support for STMicroelectronics SoC Series"
+	depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM)
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL
+	select VIDEOMODE_HELPERS
+	select FB_PROVIDE_GET_FB_UNMAPPED_AREA
+	default y
+
+	help
+	  Enable support for the on-chip display controller on
+	  STMicroelectronics STM32 MCUs.
+	  To compile this driver as a module, choose M here: the module
+	  will be called stm-drm.

+ 7 - 0
drivers/gpu/drm/stm/Makefile

@@ -0,0 +1,7 @@
+ccflags-y := -Iinclude/drm
+
+stm-drm-y := \
+	drv.o \
+	ltdc.o
+
+obj-$(CONFIG_DRM_STM) += stm-drm.o

+ 221 - 0
drivers/gpu/drm/stm/drv.c

@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/component.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "ltdc.h"
+
+#define DRIVER_NAME		"stm"
+#define DRIVER_DESC		"STMicroelectronics SoC DRM"
+#define DRIVER_DATE		"20170330"
+#define DRIVER_MAJOR		1
+#define DRIVER_MINOR		0
+#define DRIVER_PATCH_LEVEL	0
+
+#define STM_MAX_FB_WIDTH	2048
+#define STM_MAX_FB_HEIGHT	2048 /* same as width to handle orientation */
+
+static void drv_output_poll_changed(struct drm_device *ddev)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	drm_fbdev_cma_hotplug_event(ldev->fbdev);
+}
+
+static const struct drm_mode_config_funcs drv_mode_config_funcs = {
+	.fb_create = drm_fb_cma_create,
+	.output_poll_changed = drv_output_poll_changed,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void drv_lastclose(struct drm_device *ddev)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	DRM_DEBUG("%s\n", __func__);
+
+	drm_fbdev_cma_restore_mode(ldev->fbdev);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
+
+static struct drm_driver drv_driver = {
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
+			   DRIVER_ATOMIC,
+	.lastclose = drv_lastclose,
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCH_LEVEL,
+	.fops = &drv_driver_fops,
+	.dumb_create = drm_gem_cma_dumb_create,
+	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_free_object_unlocked = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap = drm_gem_cma_prime_mmap,
+	.enable_vblank = ltdc_crtc_enable_vblank,
+	.disable_vblank = ltdc_crtc_disable_vblank,
+};
+
+static int drv_load(struct drm_device *ddev)
+{
+	struct platform_device *pdev = to_platform_device(ddev->dev);
+	struct drm_fbdev_cma *fbdev;
+	struct ltdc_device *ldev;
+	int ret;
+
+	DRM_DEBUG("%s\n", __func__);
+
+	ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL);
+	if (!ldev)
+		return -ENOMEM;
+
+	ddev->dev_private = (void *)ldev;
+
+	drm_mode_config_init(ddev);
+
+	/*
+	 * set max width and height as default value.
+	 * this value would be used to check framebuffer size limitation
+	 * at drm_mode_addfb().
+	 */
+	ddev->mode_config.min_width = 0;
+	ddev->mode_config.min_height = 0;
+	ddev->mode_config.max_width = STM_MAX_FB_WIDTH;
+	ddev->mode_config.max_height = STM_MAX_FB_HEIGHT;
+	ddev->mode_config.funcs = &drv_mode_config_funcs;
+
+	ret = ltdc_load(ddev);
+	if (ret)
+		goto err;
+
+	drm_mode_config_reset(ddev);
+	drm_kms_helper_poll_init(ddev);
+
+	if (ddev->mode_config.num_connector) {
+		ldev = ddev->dev_private;
+		fbdev = drm_fbdev_cma_init(ddev, 16,
+					   ddev->mode_config.num_connector);
+		if (IS_ERR(fbdev)) {
+			DRM_DEBUG("Warning: fails to create fbdev\n");
+			fbdev = NULL;
+		}
+		ldev->fbdev = fbdev;
+	}
+
+	platform_set_drvdata(pdev, ddev);
+
+	return 0;
+err:
+	drm_mode_config_cleanup(ddev);
+	return ret;
+}
+
+static void drv_unload(struct drm_device *ddev)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	DRM_DEBUG("%s\n", __func__);
+
+	if (ldev->fbdev) {
+		drm_fbdev_cma_fini(ldev->fbdev);
+		ldev->fbdev = NULL;
+	}
+	drm_kms_helper_poll_fini(ddev);
+	ltdc_unload(ddev);
+	drm_mode_config_cleanup(ddev);
+}
+
+static int stm_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct drm_device *ddev;
+	int ret;
+
+	DRM_DEBUG("%s\n", __func__);
+
+	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+	ddev = drm_dev_alloc(&drv_driver, dev);
+	if (IS_ERR(ddev))
+		return PTR_ERR(ddev);
+
+	ret = drv_load(ddev);
+	if (ret)
+		goto err_unref;
+
+	ret = drm_dev_register(ddev, 0);
+	if (ret)
+		goto err_unref;
+
+	return 0;
+
+err_unref:
+	drm_dev_unref(ddev);
+
+	return ret;
+}
+
+static int stm_drm_platform_remove(struct platform_device *pdev)
+{
+	struct drm_device *ddev = platform_get_drvdata(pdev);
+
+	DRM_DEBUG("%s\n", __func__);
+
+	drm_dev_unregister(ddev);
+	drv_unload(ddev);
+	drm_dev_unref(ddev);
+
+	return 0;
+}
+
+static const struct of_device_id drv_dt_ids[] = {
+	{ .compatible = "st,stm32-ltdc"},
+	{ /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, drv_dt_ids);
+
+static struct platform_driver stm_drm_platform_driver = {
+	.probe = stm_drm_platform_probe,
+	.remove = stm_drm_platform_remove,
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = drv_dt_ids,
+	},
+};
+
+module_platform_driver(stm_drm_platform_driver);
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");

+ 1160 - 0
drivers/gpu/drm/stm/ltdc.c

@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/reset.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_plane_helper.h>
+
+#include <video/videomode.h>
+
+#include "ltdc.h"
+
+#define NB_CRTC 1
+#define CRTC_MASK GENMASK(NB_CRTC - 1, 0)
+
+#define MAX_IRQ 4
+
+#define HWVER_10200 0x010200
+#define HWVER_10300 0x010300
+#define HWVER_20101 0x020101
+
+/*
+ * The address of some registers depends on the HW version: such registers have
+ * an extra offset specified with reg_ofs.
+ */
+#define REG_OFS_NONE	0
+#define REG_OFS_4	4 /* Insertion of "Layer Configuration 2" reg */
+#define REG_OFS		(ldev->caps.reg_ofs)
+#define LAY_OFS		0x80	/* Register Offset between 2 layers */
+
+/* Global register offsets */
+#define LTDC_IDR	0x0000 /* IDentification */
+#define LTDC_LCR	0x0004 /* Layer Count */
+#define LTDC_SSCR	0x0008 /* Synchronization Size Configuration */
+#define LTDC_BPCR	0x000C /* Back Porch Configuration */
+#define LTDC_AWCR	0x0010 /* Active Width Configuration */
+#define LTDC_TWCR	0x0014 /* Total Width Configuration */
+#define LTDC_GCR	0x0018 /* Global Control */
+#define LTDC_GC1R	0x001C /* Global Configuration 1 */
+#define LTDC_GC2R	0x0020 /* Global Configuration 2 */
+#define LTDC_SRCR	0x0024 /* Shadow Reload Configuration */
+#define LTDC_GACR	0x0028 /* GAmma Correction */
+#define LTDC_BCCR	0x002C /* Background Color Configuration */
+#define LTDC_IER	0x0034 /* Interrupt Enable */
+#define LTDC_ISR	0x0038 /* Interrupt Status */
+#define LTDC_ICR	0x003C /* Interrupt Clear */
+#define LTDC_LIPCR	0x0040 /* Line Interrupt Position Configuration */
+#define LTDC_CPSR	0x0044 /* Current Position Status */
+#define LTDC_CDSR	0x0048 /* Current Display Status */
+
+/* Layer register offsets */
+#define LTDC_L1LC1R	(0x0080)	   /* L1 Layer Configuration 1 */
+#define LTDC_L1LC2R	(0x0084)	   /* L1 Layer Configuration 2 */
+#define LTDC_L1CR	(0x0084 + REG_OFS) /* L1 Control */
+#define LTDC_L1WHPCR	(0x0088 + REG_OFS) /* L1 Window Hor Position Config */
+#define LTDC_L1WVPCR	(0x008C + REG_OFS) /* L1 Window Vert Position Config */
+#define LTDC_L1CKCR	(0x0090 + REG_OFS) /* L1 Color Keying Configuration */
+#define LTDC_L1PFCR	(0x0094 + REG_OFS) /* L1 Pixel Format Configuration */
+#define LTDC_L1CACR	(0x0098 + REG_OFS) /* L1 Constant Alpha Config */
+#define LTDC_L1DCCR	(0x009C + REG_OFS) /* L1 Default Color Configuration */
+#define LTDC_L1BFCR	(0x00A0 + REG_OFS) /* L1 Blend Factors Configuration */
+#define LTDC_L1FBBCR	(0x00A4 + REG_OFS) /* L1 FrameBuffer Bus Control */
+#define LTDC_L1AFBCR	(0x00A8 + REG_OFS) /* L1 AuxFB Control */
+#define LTDC_L1CFBAR	(0x00AC + REG_OFS) /* L1 Color FrameBuffer Address */
+#define LTDC_L1CFBLR	(0x00B0 + REG_OFS) /* L1 Color FrameBuffer Length */
+#define LTDC_L1CFBLNR	(0x00B4 + REG_OFS) /* L1 Color FrameBuffer Line Nb */
+#define LTDC_L1AFBAR	(0x00B8 + REG_OFS) /* L1 AuxFB Address */
+#define LTDC_L1AFBLR	(0x00BC + REG_OFS) /* L1 AuxFB Length */
+#define LTDC_L1AFBLNR	(0x00C0 + REG_OFS) /* L1 AuxFB Line Number */
+#define LTDC_L1CLUTWR	(0x00C4 + REG_OFS) /* L1 CLUT Write */
+#define LTDC_L1YS1R	(0x00E0 + REG_OFS) /* L1 YCbCr Scale 1 */
+#define LTDC_L1YS2R	(0x00E4 + REG_OFS) /* L1 YCbCr Scale 2 */
+
+/* Bit definitions */
+#define SSCR_VSH	GENMASK(10, 0)	/* Vertical Synchronization Height */
+#define SSCR_HSW	GENMASK(27, 16)	/* Horizontal Synchronization Width */
+
+#define BPCR_AVBP	GENMASK(10, 0)	/* Accumulated Vertical Back Porch */
+#define BPCR_AHBP	GENMASK(27, 16)	/* Accumulated Horizontal Back Porch */
+
+#define AWCR_AAH	GENMASK(10, 0)	/* Accumulated Active Height */
+#define AWCR_AAW	GENMASK(27, 16)	/* Accumulated Active Width */
+
+#define TWCR_TOTALH	GENMASK(10, 0)	/* TOTAL Height */
+#define TWCR_TOTALW	GENMASK(27, 16)	/* TOTAL Width */
+
+#define GCR_LTDCEN	BIT(0)		/* LTDC ENable */
+#define GCR_DEN		BIT(16)		/* Dither ENable */
+#define GCR_PCPOL	BIT(28)		/* Pixel Clock POLarity */
+#define GCR_DEPOL	BIT(29)		/* Data Enable POLarity */
+#define GCR_VSPOL	BIT(30)		/* Vertical Synchro POLarity */
+#define GCR_HSPOL	BIT(31)		/* Horizontal Synchro POLarity */
+
+#define GC1R_WBCH	GENMASK(3, 0)	/* Width of Blue CHannel output */
+#define GC1R_WGCH	GENMASK(7, 4)	/* Width of Green Channel output */
+#define GC1R_WRCH	GENMASK(11, 8)	/* Width of Red Channel output */
+#define GC1R_PBEN	BIT(12)		/* Precise Blending ENable */
+#define GC1R_DT		GENMASK(15, 14)	/* Dithering Technique */
+#define GC1R_GCT	GENMASK(19, 17)	/* Gamma Correction Technique */
+#define GC1R_SHREN	BIT(21)		/* SHadow Registers ENabled */
+#define GC1R_BCP	BIT(22)		/* Background Colour Programmable */
+#define GC1R_BBEN	BIT(23)		/* Background Blending ENabled */
+#define GC1R_LNIP	BIT(24)		/* Line Number IRQ Position */
+#define GC1R_TP		BIT(25)		/* Timing Programmable */
+#define GC1R_IPP	BIT(26)		/* IRQ Polarity Programmable */
+#define GC1R_SPP	BIT(27)		/* Sync Polarity Programmable */
+#define GC1R_DWP	BIT(28)		/* Dither Width Programmable */
+#define GC1R_STREN	BIT(29)		/* STatus Registers ENabled */
+#define GC1R_BMEN	BIT(31)		/* Blind Mode ENabled */
+
+#define GC2R_EDCA	BIT(0)		/* External Display Control Ability  */
+#define GC2R_STSAEN	BIT(1)		/* Slave Timing Sync Ability ENabled */
+#define GC2R_DVAEN	BIT(2)		/* Dual-View Ability ENabled */
+#define GC2R_DPAEN	BIT(3)		/* Dual-Port Ability ENabled */
+#define GC2R_BW		GENMASK(6, 4)	/* Bus Width (log2 of nb of bytes) */
+#define GC2R_EDCEN	BIT(7)		/* External Display Control ENabled */
+
+#define SRCR_IMR	BIT(0)		/* IMmediate Reload */
+#define SRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
+
+#define BCCR_BCBLACK	0x00		/* Background Color BLACK */
+#define BCCR_BCBLUE	GENMASK(7, 0)	/* Background Color BLUE */
+#define BCCR_BCGREEN	GENMASK(15, 8)	/* Background Color GREEN */
+#define BCCR_BCRED	GENMASK(23, 16)	/* Background Color RED */
+#define BCCR_BCWHITE	GENMASK(23, 0)	/* Background Color WHITE */
+
+#define IER_LIE		BIT(0)		/* Line Interrupt Enable */
+#define IER_FUIE	BIT(1)		/* Fifo Underrun Interrupt Enable */
+#define IER_TERRIE	BIT(2)		/* Transfer ERRor Interrupt Enable */
+#define IER_RRIE	BIT(3)		/* Register Reload Interrupt enable */
+
+#define ISR_LIF		BIT(0)		/* Line Interrupt Flag */
+#define ISR_FUIF	BIT(1)		/* Fifo Underrun Interrupt Flag */
+#define ISR_TERRIF	BIT(2)		/* Transfer ERRor Interrupt Flag */
+#define ISR_RRIF	BIT(3)		/* Register Reload Interrupt Flag */
+
+#define LXCR_LEN	BIT(0)		/* Layer ENable */
+#define LXCR_COLKEN	BIT(1)		/* Color Keying Enable */
+#define LXCR_CLUTEN	BIT(4)		/* Color Look-Up Table ENable */
+
+#define LXWHPCR_WHSTPOS	GENMASK(11, 0)	/* Window Horizontal StarT POSition */
+#define LXWHPCR_WHSPPOS	GENMASK(27, 16)	/* Window Horizontal StoP POSition */
+
+#define LXWVPCR_WVSTPOS	GENMASK(10, 0)	/* Window Vertical StarT POSition */
+#define LXWVPCR_WVSPPOS	GENMASK(26, 16)	/* Window Vertical StoP POSition */
+
+#define LXPFCR_PF	GENMASK(2, 0)	/* Pixel Format */
+
+#define LXCACR_CONSTA	GENMASK(7, 0)	/* CONSTant Alpha */
+
+#define LXBFCR_BF2	GENMASK(2, 0)	/* Blending Factor 2 */
+#define LXBFCR_BF1	GENMASK(10, 8)	/* Blending Factor 1 */
+
+#define LXCFBLR_CFBLL	GENMASK(12, 0)	/* Color Frame Buffer Line Length */
+#define LXCFBLR_CFBP	GENMASK(28, 16)	/* Color Frame Buffer Pitch in bytes */
+
+#define LXCFBLNR_CFBLN	GENMASK(10, 0)	 /* Color Frame Buffer Line Number */
+
+#define HSPOL_AL   0		/* Horizontal Sync POLarity Active Low */
+#define VSPOL_AL   0		/* Vertical Sync POLarity Active Low */
+#define DEPOL_AL   0		/* Data Enable POLarity Active Low */
+#define PCPOL_IPC  0		/* Input Pixel Clock */
+#define HSPOL_AH   GCR_HSPOL	/* Horizontal Sync POLarity Active High */
+#define VSPOL_AH   GCR_VSPOL	/* Vertical Sync POLarity Active High */
+#define DEPOL_AH   GCR_DEPOL	/* Data Enable POLarity Active High */
+#define PCPOL_IIPC GCR_PCPOL	/* Inverted Input Pixel Clock */
+#define CONSTA_MAX 0xFF		/* CONSTant Alpha MAX= 1.0 */
+#define BF1_PAXCA  0x600	/* Pixel Alpha x Constant Alpha */
+#define BF1_CA     0x400	/* Constant Alpha */
+#define BF2_1PAXCA 0x007	/* 1 - (Pixel Alpha x Constant Alpha) */
+#define BF2_1CA	   0x005	/* 1 - Constant Alpha */
+
+#define NB_PF           8       /* Max nb of HW pixel format */
+
+enum ltdc_pix_fmt {
+	PF_NONE,
+	/* RGB formats */
+	PF_ARGB8888,    /* ARGB [32 bits] */
+	PF_RGBA8888,    /* RGBA [32 bits] */
+	PF_RGB888,      /* RGB [24 bits] */
+	PF_RGB565,      /* RGB [16 bits] */
+	PF_ARGB1555,    /* ARGB A:1 bit RGB:15 bits [16 bits] */
+	PF_ARGB4444,    /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
+	/* Indexed formats */
+	PF_L8,          /* Indexed 8 bits [8 bits] */
+	PF_AL44,        /* Alpha:4 bits + indexed 4 bits [8 bits] */
+	PF_AL88         /* Alpha:8 bits + indexed 8 bits [16 bits] */
+};
+
+/* The index gives the encoding of the pixel format for an HW version */
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a0[NB_PF] = {
+	PF_ARGB8888,	/* 0x00 */
+	PF_RGB888,	/* 0x01 */
+	PF_RGB565,	/* 0x02 */
+	PF_ARGB1555,	/* 0x03 */
+	PF_ARGB4444,	/* 0x04 */
+	PF_L8,		/* 0x05 */
+	PF_AL44,	/* 0x06 */
+	PF_AL88		/* 0x07 */
+};
+
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
+	PF_ARGB8888,	/* 0x00 */
+	PF_RGB888,	/* 0x01 */
+	PF_RGB565,	/* 0x02 */
+	PF_RGBA8888,	/* 0x03 */
+	PF_AL44,	/* 0x04 */
+	PF_L8,		/* 0x05 */
+	PF_ARGB1555,	/* 0x06 */
+	PF_ARGB4444	/* 0x07 */
+};
+
+static inline u32 reg_read(void __iomem *base, u32 reg)
+{
+	return readl_relaxed(base + reg);
+}
+
+static inline void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+	writel_relaxed(val, base + reg);
+}
+
+static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
+{
+	reg_write(base, reg, reg_read(base, reg) | mask);
+}
+
+static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
+{
+	reg_write(base, reg, reg_read(base, reg) & ~mask);
+}
+
+static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
+				   u32 val)
+{
+	reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
+}
+
+static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
+{
+	return (struct ltdc_device *)crtc->dev->dev_private;
+}
+
+static inline struct ltdc_device *plane_to_ltdc(struct drm_plane *plane)
+{
+	return (struct ltdc_device *)plane->dev->dev_private;
+}
+
+static inline struct ltdc_device *encoder_to_ltdc(struct drm_encoder *enc)
+{
+	return (struct ltdc_device *)enc->dev->dev_private;
+}
+
+static inline struct ltdc_device *connector_to_ltdc(struct drm_connector *con)
+{
+	return (struct ltdc_device *)con->dev->dev_private;
+}
+
+static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
+{
+	enum ltdc_pix_fmt pf;
+
+	switch (drm_fmt) {
+	case DRM_FORMAT_ARGB8888:
+	case DRM_FORMAT_XRGB8888:
+		pf = PF_ARGB8888;
+		break;
+	case DRM_FORMAT_RGBA8888:
+	case DRM_FORMAT_RGBX8888:
+		pf = PF_RGBA8888;
+		break;
+	case DRM_FORMAT_RGB888:
+		pf = PF_RGB888;
+		break;
+	case DRM_FORMAT_RGB565:
+		pf = PF_RGB565;
+		break;
+	case DRM_FORMAT_ARGB1555:
+	case DRM_FORMAT_XRGB1555:
+		pf = PF_ARGB1555;
+		break;
+	case DRM_FORMAT_ARGB4444:
+	case DRM_FORMAT_XRGB4444:
+		pf = PF_ARGB4444;
+		break;
+	case DRM_FORMAT_C8:
+		pf = PF_L8;
+		break;
+	default:
+		pf = PF_NONE;
+		break;
+	/* Note: There are no DRM_FORMAT for AL44 and AL88 */
+	}
+
+	return pf;
+}
+
+static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
+{
+	switch (pf) {
+	case PF_ARGB8888:
+		return DRM_FORMAT_ARGB8888;
+	case PF_RGBA8888:
+		return DRM_FORMAT_RGBA8888;
+	case PF_RGB888:
+		return DRM_FORMAT_RGB888;
+	case PF_RGB565:
+		return DRM_FORMAT_RGB565;
+	case PF_ARGB1555:
+		return DRM_FORMAT_ARGB1555;
+	case PF_ARGB4444:
+		return DRM_FORMAT_ARGB4444;
+	case PF_L8:
+		return DRM_FORMAT_C8;
+	case PF_AL44: /* No DRM support */
+	case PF_AL88: /* No DRM support */
+	case PF_NONE:
+	default:
+		return 0;
+	}
+}
+
+static irqreturn_t ltdc_irq_thread(int irq, void *arg)
+{
+	struct drm_device *ddev = arg;
+	struct ltdc_device *ldev = ddev->dev_private;
+	struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
+
+	/* Line IRQ : trigger the vblank event */
+	if (ldev->irq_status & ISR_LIF)
+		drm_crtc_handle_vblank(crtc);
+
+	/* Save FIFO Underrun & Transfer Error status */
+	mutex_lock(&ldev->err_lock);
+	if (ldev->irq_status & ISR_FUIF)
+		ldev->error_status |= ISR_FUIF;
+	if (ldev->irq_status & ISR_TERRIF)
+		ldev->error_status |= ISR_TERRIF;
+	mutex_unlock(&ldev->err_lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ltdc_irq(int irq, void *arg)
+{
+	struct drm_device *ddev = arg;
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	/* Read & Clear the interrupt status */
+	ldev->irq_status = reg_read(ldev->regs, LTDC_ISR);
+	reg_write(ldev->regs, LTDC_ICR, ldev->irq_status);
+
+	return IRQ_WAKE_THREAD;
+}
+
+/*
+ * DRM_CRTC
+ */
+
+static void ltdc_crtc_load_lut(struct drm_crtc *crtc)
+{
+	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+	unsigned int i, lay;
+
+	for (lay = 0; lay < ldev->caps.nb_layers; lay++)
+		for (i = 0; i < 256; i++)
+			reg_write(ldev->regs, LTDC_L1CLUTWR + lay * LAY_OFS,
+				  ldev->clut[i]);
+}
+
+static void ltdc_crtc_enable(struct drm_crtc *crtc)
+{
+	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/* Sets the background color value */
+	reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
+
+	/* Enable IRQ */
+	reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+	/* Immediately commit the planes */
+	reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+
+	/* Enable LTDC */
+	reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+	drm_crtc_vblank_on(crtc);
+}
+
+static void ltdc_crtc_disable(struct drm_crtc *crtc)
+{
+	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_crtc_vblank_off(crtc);
+
+	/* disable LTDC */
+	reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+
+	/* disable IRQ */
+	reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+
+	/* immediately commit disable of layers before switching off LTDC */
+	reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+}
+
+static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct videomode vm;
+	int rate = mode->clock * 1000;
+	u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
+	u32 total_width, total_height;
+	u32 val;
+
+	drm_display_mode_to_videomode(mode, &vm);
+
+	DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
+	DRM_DEBUG_DRIVER("Video mode: %dx%d", vm.hactive, vm.vactive);
+	DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
+			 vm.hfront_porch, vm.hback_porch, vm.hsync_len,
+			 vm.vfront_porch, vm.vback_porch, vm.vsync_len);
+
+	/* Convert video timings to ltdc timings */
+	hsync = vm.hsync_len - 1;
+	vsync = vm.vsync_len - 1;
+	accum_hbp = hsync + vm.hback_porch;
+	accum_vbp = vsync + vm.vback_porch;
+	accum_act_w = accum_hbp + vm.hactive;
+	accum_act_h = accum_vbp + vm.vactive;
+	total_width = accum_act_w + vm.hfront_porch;
+	total_height = accum_act_h + vm.vfront_porch;
+
+	clk_disable(ldev->pixel_clk);
+
+	if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
+		DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
+		return;
+	}
+
+	clk_enable(ldev->pixel_clk);
+
+	/* Configures the HS, VS, DE and PC polarities. */
+	val = HSPOL_AL | HSPOL_AL | DEPOL_AL | PCPOL_IPC;
+
+	if (vm.flags & DISPLAY_FLAGS_HSYNC_HIGH)
+		val |= HSPOL_AH;
+
+	if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
+		val |= VSPOL_AH;
+
+	if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
+		val |= DEPOL_AH;
+
+	if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+		val |= PCPOL_IIPC;
+
+	reg_update_bits(ldev->regs, LTDC_GCR,
+			GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+
+	/* Set Synchronization size */
+	val = (hsync << 16) | vsync;
+	reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+
+	/* Set Accumulated Back porch */
+	val = (accum_hbp << 16) | accum_vbp;
+	reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+
+	/* Set Accumulated Active Width */
+	val = (accum_act_w << 16) | accum_act_h;
+	reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+
+	/* Set total width & height */
+	val = (total_width << 16) | total_height;
+	reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+
+	reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1));
+}
+
+static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
+				   struct drm_crtc_state *old_crtc_state)
+{
+	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+	struct drm_pending_vblank_event *event = crtc->state->event;
+
+	DRM_DEBUG_ATOMIC("\n");
+
+	/* Commit shadow registers = update planes at next vblank */
+	reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		if (drm_crtc_vblank_get(crtc) == 0)
+			drm_crtc_arm_vblank_event(crtc, event);
+		else
+			drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
+	.load_lut = ltdc_crtc_load_lut,
+	.enable = ltdc_crtc_enable,
+	.disable = ltdc_crtc_disable,
+	.mode_set_nofb = ltdc_crtc_mode_set_nofb,
+	.atomic_flush = ltdc_crtc_atomic_flush,
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	DRM_DEBUG_DRIVER("\n");
+	reg_set(ldev->regs, LTDC_IER, IER_LIE);
+
+	return 0;
+}
+
+void ltdc_crtc_disable_vblank(struct drm_device *ddev, unsigned int pipe)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	DRM_DEBUG_DRIVER("\n");
+	reg_clear(ldev->regs, LTDC_IER, IER_LIE);
+}
+
+static struct drm_crtc_funcs ltdc_crtc_funcs = {
+	.destroy = drm_crtc_cleanup,
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+/*
+ * DRM_PLANE
+ */
+
+static int ltdc_plane_atomic_check(struct drm_plane *plane,
+				   struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	u32 src_x, src_y, src_w, src_h;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (!fb)
+		return 0;
+
+	/* convert src_ from 16:16 format */
+	src_x = state->src_x >> 16;
+	src_y = state->src_y >> 16;
+	src_w = state->src_w >> 16;
+	src_h = state->src_h >> 16;
+
+	/* Reject scaling */
+	if ((src_w != state->crtc_w) || (src_h != state->crtc_h)) {
+		DRM_ERROR("Scaling is not supported");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void ltdc_plane_atomic_update(struct drm_plane *plane,
+				     struct drm_plane_state *oldstate)
+{
+	struct ltdc_device *ldev = plane_to_ltdc(plane);
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	u32 lofs = plane->index * LAY_OFS;
+	u32 x0 = state->crtc_x;
+	u32 x1 = state->crtc_x + state->crtc_w - 1;
+	u32 y0 = state->crtc_y;
+	u32 y1 = state->crtc_y + state->crtc_h - 1;
+	u32 src_x, src_y, src_w, src_h;
+	u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
+	enum ltdc_pix_fmt pf;
+
+	if (!state->crtc || !fb) {
+		DRM_DEBUG_DRIVER("fb or crtc NULL");
+		return;
+	}
+
+	/* convert src_ from 16:16 format */
+	src_x = state->src_x >> 16;
+	src_y = state->src_y >> 16;
+	src_w = state->src_w >> 16;
+	src_h = state->src_h >> 16;
+
+	DRM_DEBUG_DRIVER(
+		"plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
+		plane->base.id, fb->base.id,
+		src_w, src_h, src_x, src_y,
+		state->crtc_w, state->crtc_h, state->crtc_x, state->crtc_y);
+
+	bpcr = reg_read(ldev->regs, LTDC_BPCR);
+	ahbp = (bpcr & BPCR_AHBP) >> 16;
+	avbp = bpcr & BPCR_AVBP;
+
+	/* Configures the horizontal start and stop position */
+	val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
+	reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
+			LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
+
+	/* Configures the vertical start and stop position */
+	val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
+	reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
+			LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
+
+	/* Specifies the pixel format */
+	pf = to_ltdc_pixelformat(fb->format->format);
+	for (val = 0; val < NB_PF; val++)
+		if (ldev->caps.pix_fmt_hw[val] == pf)
+			break;
+
+	if (val == NB_PF) {
+		DRM_ERROR("Pixel format %.4s not supported\n",
+			  (char *)&fb->format->format);
+		val = 0; /* set by default ARGB 32 bits */
+	}
+	reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
+
+	/* Configures the color frame buffer pitch in bytes & line length */
+	pitch_in_bytes = fb->pitches[0];
+	line_length = drm_format_plane_cpp(fb->format->format, 0) *
+		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+	val = ((pitch_in_bytes << 16) | line_length);
+	reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
+			LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+	/* Specifies the constant alpha value */
+	val = CONSTA_MAX;
+	reg_update_bits(ldev->regs, LTDC_L1CACR + lofs,
+			LXCACR_CONSTA, val);
+
+	/* Specifies the blending factors */
+	val = BF1_PAXCA | BF2_1PAXCA;
+	reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
+			LXBFCR_BF2 | LXBFCR_BF1, val);
+
+	/* Configures the frame buffer line number */
+	val = y1 - y0 + 1;
+	reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs,
+			LXCFBLNR_CFBLN, val);
+
+	/* Sets the FB address */
+	paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);
+
+	DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
+	reg_write(ldev->regs, LTDC_L1CFBAR + lofs, paddr);
+
+	/* Enable layer and CLUT if needed */
+	val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
+	val |= LXCR_LEN;
+	reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
+			LXCR_LEN | LXCR_CLUTEN, val);
+
+	mutex_lock(&ldev->err_lock);
+	if (ldev->error_status & ISR_FUIF) {
+		DRM_DEBUG_DRIVER("Fifo underrun\n");
+		ldev->error_status &= ~ISR_FUIF;
+	}
+	if (ldev->error_status & ISR_TERRIF) {
+		DRM_DEBUG_DRIVER("Transfer error\n");
+		ldev->error_status &= ~ISR_TERRIF;
+	}
+	mutex_unlock(&ldev->err_lock);
+}
+
+static void ltdc_plane_atomic_disable(struct drm_plane *plane,
+				      struct drm_plane_state *oldstate)
+{
+	struct ltdc_device *ldev = plane_to_ltdc(plane);
+	u32 lofs = plane->index * LAY_OFS;
+
+	/* disable layer */
+	reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
+
+	DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
+			 oldstate->crtc->base.id, plane->base.id);
+}
+
+static struct drm_plane_funcs ltdc_plane_funcs = {
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.destroy = drm_plane_cleanup,
+	.set_property = drm_atomic_helper_plane_set_property,
+	.reset = drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
+	.atomic_check = ltdc_plane_atomic_check,
+	.atomic_update = ltdc_plane_atomic_update,
+	.atomic_disable = ltdc_plane_atomic_disable,
+};
+
+static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
+					   enum drm_plane_type type)
+{
+	unsigned long possible_crtcs = CRTC_MASK;
+	struct ltdc_device *ldev = ddev->dev_private;
+	struct device *dev = ddev->dev;
+	struct drm_plane *plane;
+	unsigned int i, nb_fmt = 0;
+	u32 formats[NB_PF];
+	u32 drm_fmt;
+	int ret;
+
+	/* Get supported pixel formats */
+	for (i = 0; i < NB_PF; i++) {
+		drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
+		if (!drm_fmt)
+			continue;
+		formats[nb_fmt++] = drm_fmt;
+	}
+
+	plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
+	if (!plane)
+		return 0;
+
+	ret = drm_universal_plane_init(ddev, plane, possible_crtcs,
+				       &ltdc_plane_funcs, formats, nb_fmt,
+				       type, NULL);
+	if (ret < 0)
+		return 0;
+
+	drm_plane_helper_add(plane, &ltdc_plane_helper_funcs);
+
+	DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
+
+	return plane;
+}
+
+static void ltdc_plane_destroy_all(struct drm_device *ddev)
+{
+	struct drm_plane *plane, *plane_temp;
+
+	list_for_each_entry_safe(plane, plane_temp,
+				 &ddev->mode_config.plane_list, head)
+		drm_plane_cleanup(plane);
+}
+
+static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+	struct drm_plane *primary, *overlay;
+	unsigned int i;
+	int res;
+
+	primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
+	if (!primary) {
+		DRM_ERROR("Can not create primary plane\n");
+		return -EINVAL;
+	}
+
+	res = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+					&ltdc_crtc_funcs, NULL);
+	if (res) {
+		DRM_ERROR("Can not initialize CRTC\n");
+		goto cleanup;
+	}
+
+	drm_crtc_helper_add(crtc, &ltdc_crtc_helper_funcs);
+
+	DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
+
+	/* Add planes. Note : the first layer is used by primary plane */
+	for (i = 1; i < ldev->caps.nb_layers; i++) {
+		overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
+		if (!overlay) {
+			res = -ENOMEM;
+			DRM_ERROR("Can not create overlay plane %d\n", i);
+			goto cleanup;
+		}
+	}
+
+	return 0;
+
+cleanup:
+	ltdc_plane_destroy_all(ddev);
+	return res;
+}
+
+/*
+ * DRM_ENCODER
+ */
+
+static void ltdc_rgb_encoder_enable(struct drm_encoder *encoder)
+{
+	struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_panel_prepare(ldev->panel);
+	drm_panel_enable(ldev->panel);
+}
+
+static void ltdc_rgb_encoder_disable(struct drm_encoder *encoder)
+{
+	struct ltdc_device *ldev = encoder_to_ltdc(encoder);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_panel_disable(ldev->panel);
+	drm_panel_unprepare(ldev->panel);
+}
+
+static const struct drm_encoder_helper_funcs ltdc_rgb_encoder_helper_funcs = {
+	.enable = ltdc_rgb_encoder_enable,
+	.disable = ltdc_rgb_encoder_disable,
+};
+
+static const struct drm_encoder_funcs ltdc_rgb_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static struct drm_encoder *ltdc_rgb_encoder_create(struct drm_device *ddev)
+{
+	struct drm_encoder *encoder;
+
+	encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
+	if (!encoder)
+		return NULL;
+
+	encoder->possible_crtcs = CRTC_MASK;
+	encoder->possible_clones = 0; /* No cloning support */
+
+	drm_encoder_init(ddev, encoder, &ltdc_rgb_encoder_funcs,
+			 DRM_MODE_ENCODER_DPI, NULL);
+
+	drm_encoder_helper_add(encoder, &ltdc_rgb_encoder_helper_funcs);
+
+	DRM_DEBUG_DRIVER("RGB encoder:%d created\n", encoder->base.id);
+
+	return encoder;
+}
+
+/*
+ * DRM_CONNECTOR
+ */
+
+static int ltdc_rgb_connector_get_modes(struct drm_connector *connector)
+{
+	struct drm_device *ddev = connector->dev;
+	struct ltdc_device *ldev = ddev->dev_private;
+	int ret = 0;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (ldev->panel)
+		ret = drm_panel_get_modes(ldev->panel);
+
+	return ret < 0 ? 0 : ret;
+}
+
+static struct drm_connector_helper_funcs ltdc_rgb_connector_helper_funcs = {
+	.get_modes = ltdc_rgb_connector_get_modes,
+};
+
+static enum drm_connector_status
+ltdc_rgb_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct ltdc_device *ldev = connector_to_ltdc(connector);
+
+	return ldev->panel ? connector_status_connected :
+	       connector_status_disconnected;
+}
+
+static void ltdc_rgb_connector_destroy(struct drm_connector *connector)
+{
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static const struct drm_connector_funcs ltdc_rgb_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = ltdc_rgb_connector_detect,
+	.destroy = ltdc_rgb_connector_destroy,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+struct drm_connector *ltdc_rgb_connector_create(struct drm_device *ddev)
+{
+	struct drm_connector *connector;
+	int err;
+
+	connector = devm_kzalloc(ddev->dev, sizeof(*connector), GFP_KERNEL);
+	if (!connector) {
+		DRM_ERROR("Failed to allocate connector\n");
+		return NULL;
+	}
+
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	err = drm_connector_init(ddev, connector, &ltdc_rgb_connector_funcs,
+				 DRM_MODE_CONNECTOR_DPI);
+	if (err) {
+		DRM_ERROR("Failed to initialize connector\n");
+		return NULL;
+	}
+
+	drm_connector_helper_add(connector, &ltdc_rgb_connector_helper_funcs);
+
+	DRM_DEBUG_DRIVER("RGB connector:%d created\n", connector->base.id);
+
+	return connector;
+}
+
+static int ltdc_get_caps(struct drm_device *ddev)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+	u32 bus_width_log2, lcr, gc2r;
+
+	/* at least 1 layer must be managed */
+	lcr = reg_read(ldev->regs, LTDC_LCR);
+
+	ldev->caps.nb_layers = max_t(int, lcr, 1);
+
+	/* set data bus width */
+	gc2r = reg_read(ldev->regs, LTDC_GC2R);
+	bus_width_log2 = (gc2r & GC2R_BW) >> 4;
+	ldev->caps.bus_width = 8 << bus_width_log2;
+	ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR);
+
+	switch (ldev->caps.hw_version) {
+	case HWVER_10200:
+	case HWVER_10300:
+		ldev->caps.reg_ofs = REG_OFS_NONE;
+		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+		break;
+	case HWVER_20101:
+		ldev->caps.reg_ofs = REG_OFS_4;
+		ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static struct drm_panel *ltdc_get_panel(struct drm_device *ddev)
+{
+	struct device *dev = ddev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *entity, *port = NULL;
+	struct drm_panel *panel = NULL;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	/*
+	 * Parse ltdc node to get remote port and find RGB panel / HDMI slave
+	 * If a dsi or a bridge (hdmi, lvds...) is connected to ltdc,
+	 * a remote port & RGB panel will not be found.
+	 */
+	for_each_endpoint_of_node(np, entity) {
+		if (!of_device_is_available(entity))
+			continue;
+
+		port = of_graph_get_remote_port_parent(entity);
+		if (port) {
+			panel = of_drm_find_panel(port);
+			of_node_put(port);
+			if (panel) {
+				DRM_DEBUG_DRIVER("remote panel %s\n",
+						 port->full_name);
+			} else {
+				DRM_DEBUG_DRIVER("panel missing\n");
+				of_node_put(entity);
+			}
+		}
+	}
+
+	return panel;
+}
+
+int ltdc_load(struct drm_device *ddev)
+{
+	struct platform_device *pdev = to_platform_device(ddev->dev);
+	struct ltdc_device *ldev = ddev->dev_private;
+	struct device *dev = ddev->dev;
+	struct device_node *np = dev->of_node;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector = NULL;
+	struct drm_crtc *crtc;
+	struct reset_control *rstc;
+	struct resource res;
+	int irq, ret, i;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	ldev->panel = ltdc_get_panel(ddev);
+	if (!ldev->panel)
+		return -EPROBE_DEFER;
+
+	rstc = of_reset_control_get(np, NULL);
+
+	mutex_init(&ldev->err_lock);
+
+	ldev->pixel_clk = devm_clk_get(dev, "lcd");
+	if (IS_ERR(ldev->pixel_clk)) {
+		DRM_ERROR("Unable to get lcd clock\n");
+		return -ENODEV;
+	}
+
+	if (clk_prepare_enable(ldev->pixel_clk)) {
+		DRM_ERROR("Unable to prepare pixel clock\n");
+		return -ENODEV;
+	}
+
+	if (of_address_to_resource(np, 0, &res)) {
+		DRM_ERROR("Unable to get resource\n");
+		return -ENODEV;
+	}
+
+	ldev->regs = devm_ioremap_resource(dev, &res);
+	if (IS_ERR(ldev->regs)) {
+		DRM_ERROR("Unable to get ltdc registers\n");
+		return PTR_ERR(ldev->regs);
+	}
+
+	for (i = 0; i < MAX_IRQ; i++) {
+		irq = platform_get_irq(pdev, i);
+		if (irq < 0)
+			continue;
+
+		ret = devm_request_threaded_irq(dev, irq, ltdc_irq,
+						ltdc_irq_thread, IRQF_ONESHOT,
+						dev_name(dev), ddev);
+		if (ret) {
+			DRM_ERROR("Failed to register LTDC interrupt\n");
+			return ret;
+		}
+	}
+
+	if (!IS_ERR(rstc))
+		reset_control_deassert(rstc);
+
+	/* Disable interrupts */
+	reg_clear(ldev->regs, LTDC_IER,
+		  IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
+
+	ret = ltdc_get_caps(ddev);
+	if (ret) {
+		DRM_ERROR("hardware identifier (0x%08x) not supported!\n",
+			  ldev->caps.hw_version);
+		return ret;
+	}
+
+	DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version);
+
+	if (ldev->panel) {
+		encoder = ltdc_rgb_encoder_create(ddev);
+		if (!encoder) {
+			DRM_ERROR("Failed to create RGB encoder\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		connector = ltdc_rgb_connector_create(ddev);
+		if (!connector) {
+			DRM_ERROR("Failed to create RGB connector\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		ret = drm_mode_connector_attach_encoder(connector, encoder);
+		if (ret) {
+			DRM_ERROR("Failed to attach connector to encoder\n");
+			goto err;
+		}
+
+		drm_panel_attach(ldev->panel, connector);
+	}
+
+	crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
+	if (!crtc) {
+		DRM_ERROR("Failed to allocate crtc\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = ltdc_crtc_init(ddev, crtc);
+	if (ret) {
+		DRM_ERROR("Failed to init crtc\n");
+		goto err;
+	}
+
+	ret = drm_vblank_init(ddev, NB_CRTC);
+	if (ret) {
+		DRM_ERROR("Failed calling drm_vblank_init()\n");
+		goto err;
+	}
+
+	/* Allow usage of vblank without having to call drm_irq_install */
+	ddev->irq_enabled = 1;
+
+	return 0;
+err:
+	if (ldev->panel)
+		drm_panel_detach(ldev->panel);
+
+	clk_disable_unprepare(ldev->pixel_clk);
+
+	return ret;
+}
+
+void ltdc_unload(struct drm_device *ddev)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_vblank_cleanup(ddev);
+
+	if (ldev->panel)
+		drm_panel_detach(ldev->panel);
+
+	clk_disable_unprepare(ldev->pixel_clk);
+}
+
+MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
+MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
+MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>");
+MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver");
+MODULE_LICENSE("GPL v2");

+ 40 - 0
drivers/gpu/drm/stm/ltdc.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu@st.com>
+ *          Yannick Fertre <yannick.fertre@st.com>
+ *          Fabien Dessenne <fabien.dessenne@st.com>
+ *          Mickael Reulier <mickael.reulier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#ifndef _LTDC_H_
+#define _LTDC_H_
+
+struct ltdc_caps {
+	u32 hw_version;		/* hardware version */
+	u32 nb_layers;		/* number of supported layers */
+	u32 reg_ofs;		/* register offset for applicable regs */
+	u32 bus_width;		/* bus width (32 or 64 bits) */
+	const u32 *pix_fmt_hw;	/* supported pixel formats */
+};
+
+struct ltdc_device {
+	struct drm_fbdev_cma *fbdev;
+	void __iomem *regs;
+	struct clk *pixel_clk;	/* lcd pixel clock */
+	struct drm_panel *panel;
+	struct mutex err_lock;	/* protecting error_status */
+	struct ltdc_caps caps;
+	u32 clut[256];		/* color look up table */
+	u32 error_status;
+	u32 irq_status;
+};
+
+int ltdc_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe);
+void ltdc_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe);
+int ltdc_load(struct drm_device *ddev);
+void ltdc_unload(struct drm_device *ddev);
+
+#endif

+ 2 - 2
drivers/gpu/drm/tegra/drm.c

@@ -892,7 +892,7 @@ static int tegra_drm_context_cleanup(int id, void *p, void *data)
 	return 0;
 	return 0;
 }
 }
 
 
-static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
+static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
 {
 {
 	struct tegra_drm_file *fpriv = file->driver_priv;
 	struct tegra_drm_file *fpriv = file->driver_priv;
 
 
@@ -960,7 +960,7 @@ static struct drm_driver tegra_drm_driver = {
 	.load = tegra_drm_load,
 	.load = tegra_drm_load,
 	.unload = tegra_drm_unload,
 	.unload = tegra_drm_unload,
 	.open = tegra_drm_open,
 	.open = tegra_drm_open,
-	.preclose = tegra_drm_preclose,
+	.postclose = tegra_drm_postclose,
 	.lastclose = tegra_drm_lastclose,
 	.lastclose = tegra_drm_lastclose,
 
 
 #if defined(CONFIG_DEBUG_FS)
 #if defined(CONFIG_DEBUG_FS)

+ 1 - 0
drivers/gpu/drm/vc4/Makefile

@@ -9,6 +9,7 @@ vc4-y := \
 	vc4_drv.o \
 	vc4_drv.o \
 	vc4_dpi.o \
 	vc4_dpi.o \
 	vc4_dsi.o \
 	vc4_dsi.o \
+	vc4_fence.o \
 	vc4_kms.o \
 	vc4_kms.o \
 	vc4_gem.o \
 	vc4_gem.o \
 	vc4_hdmi.o \
 	vc4_hdmi.o \

+ 36 - 1
drivers/gpu/drm/vc4/vc4_bo.c

@@ -19,6 +19,8 @@
  * rendering can return quickly.
  * rendering can return quickly.
  */
  */
 
 
+#include <linux/dma-buf.h>
+
 #include "vc4_drv.h"
 #include "vc4_drv.h"
 #include "uapi/drm/vc4_drm.h"
 #include "uapi/drm/vc4_drm.h"
 
 
@@ -88,6 +90,10 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
 
 
 	vc4->bo_stats.num_allocated--;
 	vc4->bo_stats.num_allocated--;
 	vc4->bo_stats.size_allocated -= obj->size;
 	vc4->bo_stats.size_allocated -= obj->size;
+
+	if (bo->resv == &bo->_resv)
+		reservation_object_fini(bo->resv);
+
 	drm_gem_cma_free_object(obj);
 	drm_gem_cma_free_object(obj);
 }
 }
 
 
@@ -244,8 +250,12 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size,
 			return ERR_PTR(-ENOMEM);
 			return ERR_PTR(-ENOMEM);
 		}
 		}
 	}
 	}
+	bo = to_vc4_bo(&cma_obj->base);
 
 
-	return to_vc4_bo(&cma_obj->base);
+	bo->resv = &bo->_resv;
+	reservation_object_init(bo->resv);
+
+	return bo;
 }
 }
 
 
 int vc4_dumb_create(struct drm_file *file_priv,
 int vc4_dumb_create(struct drm_file *file_priv,
@@ -369,6 +379,13 @@ static void vc4_bo_cache_time_timer(unsigned long data)
 	schedule_work(&vc4->bo_cache.time_work);
 	schedule_work(&vc4->bo_cache.time_work);
 }
 }
 
 
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj)
+{
+	struct vc4_bo *bo = to_vc4_bo(obj);
+
+	return bo->resv;
+}
+
 struct dma_buf *
 struct dma_buf *
 vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
 vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags)
 {
 {
@@ -440,6 +457,24 @@ void *vc4_prime_vmap(struct drm_gem_object *obj)
 	return drm_gem_cma_prime_vmap(obj);
 	return drm_gem_cma_prime_vmap(obj);
 }
 }
 
 
+struct drm_gem_object *
+vc4_prime_import_sg_table(struct drm_device *dev,
+			  struct dma_buf_attachment *attach,
+			  struct sg_table *sgt)
+{
+	struct drm_gem_object *obj;
+	struct vc4_bo *bo;
+
+	obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt);
+	if (IS_ERR(obj))
+		return obj;
+
+	bo = to_vc4_bo(obj);
+	bo->resv = attach->dmabuf->resv;
+
+	return obj;
+}
+
 int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
 int vc4_create_bo_ioctl(struct drm_device *dev, void *data,
 			struct drm_file *file_priv)
 			struct drm_file *file_priv)
 {
 {

+ 10 - 24
drivers/gpu/drm/vc4/vc4_crtc.c

@@ -151,10 +151,10 @@ int vc4_crtc_debugfs_regs(struct seq_file *m, void *unused)
 }
 }
 #endif
 #endif
 
 
-int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
-			    unsigned int flags, int *vpos, int *hpos,
-			    ktime_t *stime, ktime_t *etime,
-			    const struct drm_display_mode *mode)
+bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+			     bool in_vblank_irq, int *vpos, int *hpos,
+			     ktime_t *stime, ktime_t *etime,
+			     const struct drm_display_mode *mode)
 {
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
 	struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
@@ -162,7 +162,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 	u32 val;
 	u32 val;
 	int fifo_lines;
 	int fifo_lines;
 	int vblank_lines;
 	int vblank_lines;
-	int ret = 0;
+	bool ret = false;
 
 
 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
 
@@ -198,7 +198,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 	fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
 	fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
 
 
 	if (fifo_lines > 0)
 	if (fifo_lines > 0)
-		ret |= DRM_SCANOUTPOS_VALID;
+		ret = true;
 
 
 	/* HVS more than fifo_lines into frame for compositing? */
 	/* HVS more than fifo_lines into frame for compositing? */
 	if (*vpos > fifo_lines) {
 	if (*vpos > fifo_lines) {
@@ -216,7 +216,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 		 */
 		 */
 		*vpos -= fifo_lines + 1;
 		*vpos -= fifo_lines + 1;
 
 
-		ret |= DRM_SCANOUTPOS_ACCURATE;
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -229,10 +228,9 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 	 * We can't get meaningful readings wrt. scanline position of the PV
 	 * We can't get meaningful readings wrt. scanline position of the PV
 	 * and need to make things up in a approximative but consistent way.
 	 * and need to make things up in a approximative but consistent way.
 	 */
 	 */
-	ret |= DRM_SCANOUTPOS_IN_VBLANK;
 	vblank_lines = mode->vtotal - mode->vdisplay;
 	vblank_lines = mode->vtotal - mode->vdisplay;
 
 
-	if (flags & DRM_CALLED_FROM_VBLIRQ) {
+	if (in_vblank_irq) {
 		/*
 		/*
 		 * Assume the irq handler got called close to first
 		 * Assume the irq handler got called close to first
 		 * line of vblank, so PV has about a full vblank
 		 * line of vblank, so PV has about a full vblank
@@ -254,9 +252,10 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 		 * we are at the very beginning of vblank, as the hvs just
 		 * we are at the very beginning of vblank, as the hvs just
 		 * started refilling, and the stime and etime timestamps
 		 * started refilling, and the stime and etime timestamps
 		 * truly correspond to start of vblank.
 		 * truly correspond to start of vblank.
+		 *
+		 * Unfortunately there's no way to report this to upper levels
+		 * and make it more useful.
 		 */
 		 */
-		if ((val & SCALER_DISPSTATX_FULL) != SCALER_DISPSTATX_FULL)
-			ret |= DRM_SCANOUTPOS_ACCURATE;
 	} else {
 	} else {
 		/*
 		/*
 		 * No clue where we are inside vblank. Return a vpos of zero,
 		 * No clue where we are inside vblank. Return a vpos of zero,
@@ -270,19 +269,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 	return ret;
 	return ret;
 }
 }
 
 
-int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
-				  int *max_error, struct timeval *vblank_time,
-				  unsigned flags)
-{
-	struct drm_crtc *crtc = drm_crtc_from_index(dev, crtc_id);
-	struct drm_crtc_state *state = crtc->state;
-
-	/* Helper routine in DRM core does all the work: */
-	return drm_calc_vbltimestamp_from_scanoutpos(dev, crtc_id, max_error,
-						     vblank_time, flags,
-						     &state->adjusted_mode);
-}
-
 static void vc4_crtc_destroy(struct drm_crtc *crtc)
 static void vc4_crtc_destroy(struct drm_crtc *crtc)
 {
 {
 	drm_crtc_cleanup(crtc);
 	drm_crtc_cleanup(crtc);

+ 4 - 2
drivers/gpu/drm/vc4/vc4_drv.c

@@ -154,7 +154,7 @@ static struct drm_driver vc4_drm_driver = {
 	.irq_uninstall = vc4_irq_uninstall,
 	.irq_uninstall = vc4_irq_uninstall,
 
 
 	.get_scanout_position = vc4_crtc_get_scanoutpos,
 	.get_scanout_position = vc4_crtc_get_scanoutpos,
-	.get_vblank_timestamp = vc4_crtc_get_vblank_timestamp,
+	.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
 
 
 #if defined(CONFIG_DEBUG_FS)
 #if defined(CONFIG_DEBUG_FS)
 	.debugfs_init = vc4_debugfs_init,
 	.debugfs_init = vc4_debugfs_init,
@@ -168,8 +168,9 @@ static struct drm_driver vc4_drm_driver = {
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 	.gem_prime_import = drm_gem_prime_import,
 	.gem_prime_import = drm_gem_prime_import,
 	.gem_prime_export = vc4_prime_export,
 	.gem_prime_export = vc4_prime_export,
+	.gem_prime_res_obj = vc4_prime_res_obj,
 	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
 	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table,
-	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_import_sg_table = vc4_prime_import_sg_table,
 	.gem_prime_vmap = vc4_prime_vmap,
 	.gem_prime_vmap = vc4_prime_vmap,
 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
 	.gem_prime_mmap = vc4_prime_mmap,
 	.gem_prime_mmap = vc4_prime_mmap,
@@ -334,6 +335,7 @@ static int vc4_platform_drm_remove(struct platform_device *pdev)
 
 
 static const struct of_device_id vc4_of_match[] = {
 static const struct of_device_id vc4_of_match[] = {
 	{ .compatible = "brcm,bcm2835-vc4", },
 	{ .compatible = "brcm,bcm2835-vc4", },
+	{ .compatible = "brcm,cygnus-vc4", },
 	{},
 	{},
 };
 };
 MODULE_DEVICE_TABLE(of, vc4_of_match);
 MODULE_DEVICE_TABLE(of, vc4_of_match);

+ 57 - 13
drivers/gpu/drm/vc4/vc4_drv.h

@@ -8,7 +8,9 @@
 
 
 #include "drmP.h"
 #include "drmP.h"
 #include "drm_gem_cma_helper.h"
 #include "drm_gem_cma_helper.h"
+#include "drm_gem_cma_helper.h"
 
 
+#include <linux/reservation.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_encoder.h>
 
 
 struct vc4_dev {
 struct vc4_dev {
@@ -56,6 +58,8 @@ struct vc4_dev {
 	/* Protects bo_cache and the BO stats. */
 	/* Protects bo_cache and the BO stats. */
 	struct mutex bo_lock;
 	struct mutex bo_lock;
 
 
+	uint64_t dma_fence_context;
+
 	/* Sequence number for the last job queued in bin_job_list.
 	/* Sequence number for the last job queued in bin_job_list.
 	 * Starts at 0 (no jobs emitted).
 	 * Starts at 0 (no jobs emitted).
 	 */
 	 */
@@ -95,12 +99,23 @@ struct vc4_dev {
 	 */
 	 */
 	struct list_head seqno_cb_list;
 	struct list_head seqno_cb_list;
 
 
-	/* The binner overflow memory that's currently set up in
-	 * BPOA/BPOS registers.  When overflow occurs and a new one is
-	 * allocated, the previous one will be moved to
-	 * vc4->current_exec's free list.
+	/* The memory used for storing binner tile alloc, tile state,
+	 * and overflow memory allocations.  This is freed when V3D
+	 * powers down.
+	 */
+	struct vc4_bo *bin_bo;
+
+	/* Size of blocks allocated within bin_bo. */
+	uint32_t bin_alloc_size;
+
+	/* Bitmask of the bin_alloc_size chunks in bin_bo that are
+	 * used.
 	 */
 	 */
-	struct vc4_bo *overflow_mem;
+	uint32_t bin_alloc_used;
+
+	/* Bitmask of the current bin_alloc used for overflow memory. */
+	uint32_t bin_alloc_overflow;
+
 	struct work_struct overflow_mem_work;
 	struct work_struct overflow_mem_work;
 
 
 	int power_refcount;
 	int power_refcount;
@@ -150,6 +165,10 @@ struct vc4_bo {
 	 * DRM_IOCTL_VC4_CREATE_SHADER_BO.
 	 * DRM_IOCTL_VC4_CREATE_SHADER_BO.
 	 */
 	 */
 	struct vc4_validated_shader_info *validated_shader;
 	struct vc4_validated_shader_info *validated_shader;
+
+	/* normally (resv == &_resv) except for imported bo's */
+	struct reservation_object *resv;
+	struct reservation_object _resv;
 };
 };
 
 
 static inline struct vc4_bo *
 static inline struct vc4_bo *
@@ -158,6 +177,19 @@ to_vc4_bo(struct drm_gem_object *bo)
 	return (struct vc4_bo *)bo;
 	return (struct vc4_bo *)bo;
 }
 }
 
 
+struct vc4_fence {
+	struct dma_fence base;
+	struct drm_device *dev;
+	/* vc4 seqno for signaled() test */
+	uint64_t seqno;
+};
+
+static inline struct vc4_fence *
+to_vc4_fence(struct dma_fence *fence)
+{
+	return (struct vc4_fence *)fence;
+}
+
 struct vc4_seqno_cb {
 struct vc4_seqno_cb {
 	struct work_struct work;
 	struct work_struct work;
 	uint64_t seqno;
 	uint64_t seqno;
@@ -168,6 +200,7 @@ struct vc4_v3d {
 	struct vc4_dev *vc4;
 	struct vc4_dev *vc4;
 	struct platform_device *pdev;
 	struct platform_device *pdev;
 	void __iomem *regs;
 	void __iomem *regs;
+	struct clk *clk;
 };
 };
 
 
 struct vc4_hvs {
 struct vc4_hvs {
@@ -230,6 +263,8 @@ struct vc4_exec_info {
 	/* Latest write_seqno of any BO that binning depends on. */
 	/* Latest write_seqno of any BO that binning depends on. */
 	uint64_t bin_dep_seqno;
 	uint64_t bin_dep_seqno;
 
 
+	struct dma_fence *fence;
+
 	/* Last current addresses the hardware was processing when the
 	/* Last current addresses the hardware was processing when the
 	 * hangcheck timer checked on us.
 	 * hangcheck timer checked on us.
 	 */
 	 */
@@ -293,8 +328,12 @@ struct vc4_exec_info {
 	bool found_increment_semaphore_packet;
 	bool found_increment_semaphore_packet;
 	bool found_flush;
 	bool found_flush;
 	uint8_t bin_tiles_x, bin_tiles_y;
 	uint8_t bin_tiles_x, bin_tiles_y;
-	struct drm_gem_cma_object *tile_bo;
+	/* Physical address of the start of the tile alloc array
+	 * (where each tile's binned CL will start)
+	 */
 	uint32_t tile_alloc_offset;
 	uint32_t tile_alloc_offset;
+	/* Bitmask of which binner slots are freed when this job completes. */
+	uint32_t bin_slots;
 
 
 	/**
 	/**
 	 * Computed addresses pointing into exec_bo where we start the
 	 * Computed addresses pointing into exec_bo where we start the
@@ -436,7 +475,11 @@ int vc4_mmap_bo_ioctl(struct drm_device *dev, void *data,
 int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
 int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data,
 			     struct drm_file *file_priv);
 			     struct drm_file *file_priv);
 int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
 int vc4_mmap(struct file *filp, struct vm_area_struct *vma);
+struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj);
 int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
 int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma);
+struct drm_gem_object *vc4_prime_import_sg_table(struct drm_device *dev,
+						 struct dma_buf_attachment *attach,
+						 struct sg_table *sgt);
 void *vc4_prime_vmap(struct drm_gem_object *obj);
 void *vc4_prime_vmap(struct drm_gem_object *obj);
 void vc4_bo_cache_init(struct drm_device *dev);
 void vc4_bo_cache_init(struct drm_device *dev);
 void vc4_bo_cache_destroy(struct drm_device *dev);
 void vc4_bo_cache_destroy(struct drm_device *dev);
@@ -446,13 +489,10 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *arg);
 extern struct platform_driver vc4_crtc_driver;
 extern struct platform_driver vc4_crtc_driver;
 bool vc4_event_pending(struct drm_crtc *crtc);
 bool vc4_event_pending(struct drm_crtc *crtc);
 int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
 int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg);
-int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
-			    unsigned int flags, int *vpos, int *hpos,
-			    ktime_t *stime, ktime_t *etime,
-			    const struct drm_display_mode *mode);
-int vc4_crtc_get_vblank_timestamp(struct drm_device *dev, unsigned int crtc_id,
-				  int *max_error, struct timeval *vblank_time,
-				  unsigned flags);
+bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
+			     bool in_vblank_irq, int *vpos, int *hpos,
+			     ktime_t *stime, ktime_t *etime,
+			     const struct drm_display_mode *mode);
 
 
 /* vc4_debugfs.c */
 /* vc4_debugfs.c */
 int vc4_debugfs_init(struct drm_minor *minor);
 int vc4_debugfs_init(struct drm_minor *minor);
@@ -468,6 +508,9 @@ int vc4_dpi_debugfs_regs(struct seq_file *m, void *unused);
 extern struct platform_driver vc4_dsi_driver;
 extern struct platform_driver vc4_dsi_driver;
 int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
 int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused);
 
 
+/* vc4_fence.c */
+extern const struct dma_fence_ops vc4_fence_ops;
+
 /* vc4_gem.c */
 /* vc4_gem.c */
 void vc4_gem_init(struct drm_device *dev);
 void vc4_gem_init(struct drm_device *dev);
 void vc4_gem_destroy(struct drm_device *dev);
 void vc4_gem_destroy(struct drm_device *dev);
@@ -522,6 +565,7 @@ void vc4_plane_async_set_fb(struct drm_plane *plane,
 extern struct platform_driver vc4_v3d_driver;
 extern struct platform_driver vc4_v3d_driver;
 int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
 int vc4_v3d_debugfs_regs(struct seq_file *m, void *unused);
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4);
 
 
 /* vc4_validate.c */
 /* vc4_validate.c */
 int
 int

+ 56 - 0
drivers/gpu/drm/vc4/vc4_fence.c

@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2017 Broadcom
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "vc4_drv.h"
+
+static const char *vc4_fence_get_driver_name(struct dma_fence *fence)
+{
+	return "vc4";
+}
+
+static const char *vc4_fence_get_timeline_name(struct dma_fence *fence)
+{
+	return "vc4-v3d";
+}
+
+static bool vc4_fence_enable_signaling(struct dma_fence *fence)
+{
+	return true;
+}
+
+static bool vc4_fence_signaled(struct dma_fence *fence)
+{
+	struct vc4_fence *f = to_vc4_fence(fence);
+	struct vc4_dev *vc4 = to_vc4_dev(f->dev);
+
+	return vc4->finished_seqno >= f->seqno;
+}
+
+const struct dma_fence_ops vc4_fence_ops = {
+	.get_driver_name = vc4_fence_get_driver_name,
+	.get_timeline_name = vc4_fence_get_timeline_name,
+	.enable_signaling = vc4_fence_enable_signaling,
+	.signaled = vc4_fence_signaled,
+	.wait = dma_fence_default_wait,
+	.release = dma_fence_free,
+};

+ 150 - 11
drivers/gpu/drm/vc4/vc4_gem.c

@@ -463,6 +463,8 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
 	for (i = 0; i < exec->bo_count; i++) {
 	for (i = 0; i < exec->bo_count; i++) {
 		bo = to_vc4_bo(&exec->bo[i]->base);
 		bo = to_vc4_bo(&exec->bo[i]->base);
 		bo->seqno = seqno;
 		bo->seqno = seqno;
+
+		reservation_object_add_shared_fence(bo->resv, exec->fence);
 	}
 	}
 
 
 	list_for_each_entry(bo, &exec->unref_list, unref_head) {
 	list_for_each_entry(bo, &exec->unref_list, unref_head) {
@@ -472,7 +474,103 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
 	for (i = 0; i < exec->rcl_write_bo_count; i++) {
 	for (i = 0; i < exec->rcl_write_bo_count; i++) {
 		bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
 		bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
 		bo->write_seqno = seqno;
 		bo->write_seqno = seqno;
+
+		reservation_object_add_excl_fence(bo->resv, exec->fence);
+	}
+}
+
+static void
+vc4_unlock_bo_reservations(struct drm_device *dev,
+			   struct vc4_exec_info *exec,
+			   struct ww_acquire_ctx *acquire_ctx)
+{
+	int i;
+
+	for (i = 0; i < exec->bo_count; i++) {
+		struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base);
+
+		ww_mutex_unlock(&bo->resv->lock);
+	}
+
+	ww_acquire_fini(acquire_ctx);
+}
+
+/* Takes the reservation lock on all the BOs being referenced, so that
+ * at queue submit time we can update the reservations.
+ *
+ * We don't lock the RCL the tile alloc/state BOs, or overflow memory
+ * (all of which are on exec->unref_list).  They're entirely private
+ * to vc4, so we don't attach dma-buf fences to them.
+ */
+static int
+vc4_lock_bo_reservations(struct drm_device *dev,
+			 struct vc4_exec_info *exec,
+			 struct ww_acquire_ctx *acquire_ctx)
+{
+	int contended_lock = -1;
+	int i, ret;
+	struct vc4_bo *bo;
+
+	ww_acquire_init(acquire_ctx, &reservation_ww_class);
+
+retry:
+	if (contended_lock != -1) {
+		bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+		ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock,
+						       acquire_ctx);
+		if (ret) {
+			ww_acquire_done(acquire_ctx);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < exec->bo_count; i++) {
+		if (i == contended_lock)
+			continue;
+
+		bo = to_vc4_bo(&exec->bo[i]->base);
+
+		ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx);
+		if (ret) {
+			int j;
+
+			for (j = 0; j < i; j++) {
+				bo = to_vc4_bo(&exec->bo[j]->base);
+				ww_mutex_unlock(&bo->resv->lock);
+			}
+
+			if (contended_lock != -1 && contended_lock >= i) {
+				bo = to_vc4_bo(&exec->bo[contended_lock]->base);
+
+				ww_mutex_unlock(&bo->resv->lock);
+			}
+
+			if (ret == -EDEADLK) {
+				contended_lock = i;
+				goto retry;
+			}
+
+			ww_acquire_done(acquire_ctx);
+			return ret;
+		}
 	}
 	}
+
+	ww_acquire_done(acquire_ctx);
+
+	/* Reserve space for our shared (read-only) fence references,
+	 * before we commit the CL to the hardware.
+	 */
+	for (i = 0; i < exec->bo_count; i++) {
+		bo = to_vc4_bo(&exec->bo[i]->base);
+
+		ret = reservation_object_reserve_shared(bo->resv);
+		if (ret) {
+			vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+			return ret;
+		}
+	}
+
+	return 0;
 }
 }
 
 
 /* Queues a struct vc4_exec_info for execution.  If no job is
 /* Queues a struct vc4_exec_info for execution.  If no job is
@@ -484,19 +582,34 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
  * then bump the end address.  That's a change for a later date,
  * then bump the end address.  That's a change for a later date,
  * though.
  * though.
  */
  */
-static void
-vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
+static int
+vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec,
+		 struct ww_acquire_ctx *acquire_ctx)
 {
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	uint64_t seqno;
 	uint64_t seqno;
 	unsigned long irqflags;
 	unsigned long irqflags;
+	struct vc4_fence *fence;
+
+	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+	if (!fence)
+		return -ENOMEM;
+	fence->dev = dev;
 
 
 	spin_lock_irqsave(&vc4->job_lock, irqflags);
 	spin_lock_irqsave(&vc4->job_lock, irqflags);
 
 
 	seqno = ++vc4->emit_seqno;
 	seqno = ++vc4->emit_seqno;
 	exec->seqno = seqno;
 	exec->seqno = seqno;
+
+	dma_fence_init(&fence->base, &vc4_fence_ops, &vc4->job_lock,
+		       vc4->dma_fence_context, exec->seqno);
+	fence->seqno = exec->seqno;
+	exec->fence = &fence->base;
+
 	vc4_update_bo_seqnos(exec, seqno);
 	vc4_update_bo_seqnos(exec, seqno);
 
 
+	vc4_unlock_bo_reservations(dev, exec, acquire_ctx);
+
 	list_add_tail(&exec->head, &vc4->bin_job_list);
 	list_add_tail(&exec->head, &vc4->bin_job_list);
 
 
 	/* If no job was executing, kick ours off.  Otherwise, it'll
 	/* If no job was executing, kick ours off.  Otherwise, it'll
@@ -509,6 +622,8 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec)
 	}
 	}
 
 
 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+	return 0;
 }
 }
 
 
 /**
 /**
@@ -705,8 +820,15 @@ static void
 vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
 vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
 {
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	unsigned long irqflags;
 	unsigned i;
 	unsigned i;
 
 
+	/* If we got force-completed because of GPU reset rather than
+	 * through our IRQ handler, signal the fence now.
+	 */
+	if (exec->fence)
+		dma_fence_signal(exec->fence);
+
 	if (exec->bo) {
 	if (exec->bo) {
 		for (i = 0; i < exec->bo_count; i++)
 		for (i = 0; i < exec->bo_count; i++)
 			drm_gem_object_unreference_unlocked(&exec->bo[i]->base);
 			drm_gem_object_unreference_unlocked(&exec->bo[i]->base);
@@ -720,6 +842,11 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec)
 		drm_gem_object_unreference_unlocked(&bo->base.base);
 		drm_gem_object_unreference_unlocked(&bo->base.base);
 	}
 	}
 
 
+	/* Free up the allocation of any bin slots we used. */
+	spin_lock_irqsave(&vc4->job_lock, irqflags);
+	vc4->bin_alloc_used &= ~exec->bin_slots;
+	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
 	mutex_lock(&vc4->power_lock);
 	mutex_lock(&vc4->power_lock);
 	if (--vc4->power_refcount == 0) {
 	if (--vc4->power_refcount == 0) {
 		pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
 		pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev);
@@ -874,6 +1001,7 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_vc4_submit_cl *args = data;
 	struct drm_vc4_submit_cl *args = data;
 	struct vc4_exec_info *exec;
 	struct vc4_exec_info *exec;
+	struct ww_acquire_ctx acquire_ctx;
 	int ret = 0;
 	int ret = 0;
 
 
 	if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
 	if ((args->flags & ~VC4_SUBMIT_CL_USE_CLEAR_COLOR) != 0) {
@@ -888,13 +1016,16 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
 	}
 	}
 
 
 	mutex_lock(&vc4->power_lock);
 	mutex_lock(&vc4->power_lock);
-	if (vc4->power_refcount++ == 0)
+	if (vc4->power_refcount++ == 0) {
 		ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
 		ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev);
-	mutex_unlock(&vc4->power_lock);
-	if (ret < 0) {
-		kfree(exec);
-		return ret;
+		if (ret < 0) {
+			mutex_unlock(&vc4->power_lock);
+			vc4->power_refcount--;
+			kfree(exec);
+			return ret;
+		}
 	}
 	}
+	mutex_unlock(&vc4->power_lock);
 
 
 	exec->args = args;
 	exec->args = args;
 	INIT_LIST_HEAD(&exec->unref_list);
 	INIT_LIST_HEAD(&exec->unref_list);
@@ -916,12 +1047,18 @@ vc4_submit_cl_ioctl(struct drm_device *dev, void *data,
 	if (ret)
 	if (ret)
 		goto fail;
 		goto fail;
 
 
+	ret = vc4_lock_bo_reservations(dev, exec, &acquire_ctx);
+	if (ret)
+		goto fail;
+
 	/* Clear this out of the struct we'll be putting in the queue,
 	/* Clear this out of the struct we'll be putting in the queue,
 	 * since it's part of our stack.
 	 * since it's part of our stack.
 	 */
 	 */
 	exec->args = NULL;
 	exec->args = NULL;
 
 
-	vc4_queue_submit(dev, exec);
+	ret = vc4_queue_submit(dev, exec, &acquire_ctx);
+	if (ret)
+		goto fail;
 
 
 	/* Return the seqno for our job. */
 	/* Return the seqno for our job. */
 	args->seqno = vc4->emit_seqno;
 	args->seqno = vc4->emit_seqno;
@@ -939,6 +1076,8 @@ vc4_gem_init(struct drm_device *dev)
 {
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 
 
+	vc4->dma_fence_context = dma_fence_context_alloc(1);
+
 	INIT_LIST_HEAD(&vc4->bin_job_list);
 	INIT_LIST_HEAD(&vc4->bin_job_list);
 	INIT_LIST_HEAD(&vc4->render_job_list);
 	INIT_LIST_HEAD(&vc4->render_job_list);
 	INIT_LIST_HEAD(&vc4->job_done_list);
 	INIT_LIST_HEAD(&vc4->job_done_list);
@@ -968,9 +1107,9 @@ vc4_gem_destroy(struct drm_device *dev)
 	/* V3D should already have disabled its interrupt and cleared
 	/* V3D should already have disabled its interrupt and cleared
 	 * the overflow allocation registers.  Now free the object.
 	 * the overflow allocation registers.  Now free the object.
 	 */
 	 */
-	if (vc4->overflow_mem) {
-		drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
-		vc4->overflow_mem = NULL;
+	if (vc4->bin_bo) {
+		drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+		vc4->bin_bo = NULL;
 	}
 	}
 
 
 	if (vc4->hang_state)
 	if (vc4->hang_state)

+ 92 - 82
drivers/gpu/drm/vc4/vc4_hdmi.c

@@ -51,6 +51,7 @@
 #include "linux/of_address.h"
 #include "linux/of_address.h"
 #include "linux/of_gpio.h"
 #include "linux/of_gpio.h"
 #include "linux/of_platform.h"
 #include "linux/of_platform.h"
+#include "linux/pm_runtime.h"
 #include "linux/rational.h"
 #include "linux/rational.h"
 #include "sound/dmaengine_pcm.h"
 #include "sound/dmaengine_pcm.h"
 #include "sound/pcm_drm_eld.h"
 #include "sound/pcm_drm_eld.h"
@@ -449,13 +450,38 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
 	vc4_hdmi_set_spd_infoframe(encoder);
 	vc4_hdmi_set_spd_infoframe(encoder);
 }
 }
 
 
-static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
-				      struct drm_display_mode *unadjusted_mode,
-				      struct drm_display_mode *mode)
+static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_hdmi *hdmi = vc4->hdmi;
+	int ret;
+
+	HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+	HD_WRITE(VC4_HD_VID_CTL,
+		 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+
+	HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+	udelay(1);
+	HD_WRITE(VC4_HD_M_CTL, 0);
+
+	clk_disable_unprepare(hdmi->hsm_clock);
+	clk_disable_unprepare(hdmi->pixel_clock);
+
+	ret = pm_runtime_put(&hdmi->pdev->dev);
+	if (ret < 0)
+		DRM_ERROR("Failed to release power domain: %d\n", ret);
+}
+
+static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
 {
 {
+	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
 	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
 	struct drm_device *dev = encoder->dev;
 	struct drm_device *dev = encoder->dev;
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct vc4_hdmi *hdmi = vc4->hdmi;
 	bool debug_dump_regs = false;
 	bool debug_dump_regs = false;
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
 	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
 	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
@@ -475,6 +501,64 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 					interlaced,
 					interlaced,
 					VC4_HDMI_VERTB_VBP));
 					VC4_HDMI_VERTB_VBP));
 	u32 csc_ctl;
 	u32 csc_ctl;
+	int ret;
+
+	ret = pm_runtime_get_sync(&hdmi->pdev->dev);
+	if (ret < 0) {
+		DRM_ERROR("Failed to retain power domain: %d\n", ret);
+		return;
+	}
+
+	/* This is the rate that is set by the firmware.  The number
+	 * needs to be a bit higher than the pixel clock rate
+	 * (generally 148.5Mhz).
+	 */
+	ret = clk_set_rate(hdmi->hsm_clock, 163682864);
+	if (ret) {
+		DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+		return;
+	}
+
+	ret = clk_set_rate(hdmi->pixel_clock,
+			   mode->clock * 1000 *
+			   ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
+	if (ret) {
+		DRM_ERROR("Failed to set pixel clock rate: %d\n", ret);
+		return;
+	}
+
+	ret = clk_prepare_enable(hdmi->pixel_clock);
+	if (ret) {
+		DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
+		return;
+	}
+
+	ret = clk_prepare_enable(hdmi->hsm_clock);
+	if (ret) {
+		DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
+			  ret);
+		clk_disable_unprepare(hdmi->pixel_clock);
+		return;
+	}
+
+	HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
+	udelay(1);
+	HD_WRITE(VC4_HD_M_CTL, 0);
+
+	HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
+
+	HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
+		   VC4_HDMI_SW_RESET_HDMI |
+		   VC4_HDMI_SW_RESET_FORMAT_DETECT);
+
+	HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
+
+	/* PHY should be in reset, like
+	 * vc4_hdmi_encoder_disable() does.
+	 */
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
+
+	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
 
 
 	if (debug_dump_regs) {
 	if (debug_dump_regs) {
 		DRM_INFO("HDMI regs before:\n");
 		DRM_INFO("HDMI regs before:\n");
@@ -483,9 +567,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
 
 	HD_WRITE(VC4_HD_VID_CTL, 0);
 	HD_WRITE(VC4_HD_VID_CTL, 0);
 
 
-	clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
-		     ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
-
 	HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
 	HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
 		   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
 		   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
 		   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
 		   VC4_HDMI_SCHEDULER_CONTROL_MANUAL_FORMAT |
@@ -559,28 +640,6 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 		DRM_INFO("HDMI regs after:\n");
 		DRM_INFO("HDMI regs after:\n");
 		vc4_hdmi_dump_regs(dev);
 		vc4_hdmi_dump_regs(dev);
 	}
 	}
-}
-
-static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
-{
-	struct drm_device *dev = encoder->dev;
-	struct vc4_dev *vc4 = to_vc4_dev(dev);
-
-	HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
-
-	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
-	HD_WRITE(VC4_HD_VID_CTL,
-		 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
-}
-
-static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
-{
-	struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
-	struct drm_device *dev = encoder->dev;
-	struct vc4_dev *vc4 = to_vc4_dev(dev);
-	int ret;
-
-	HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0);
 
 
 	HD_WRITE(VC4_HD_VID_CTL,
 	HD_WRITE(VC4_HD_VID_CTL,
 		 HD_READ(VC4_HD_VID_CTL) |
 		 HD_READ(VC4_HD_VID_CTL) |
@@ -646,7 +705,6 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
 }
 }
 
 
 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
-	.mode_set = vc4_hdmi_encoder_mode_set,
 	.disable = vc4_hdmi_encoder_disable,
 	.disable = vc4_hdmi_encoder_disable,
 	.enable = vc4_hdmi_encoder_enable,
 	.enable = vc4_hdmi_encoder_enable,
 };
 };
@@ -1147,33 +1205,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 		return -EPROBE_DEFER;
 		return -EPROBE_DEFER;
 	}
 	}
 
 
-	/* Enable the clocks at startup.  We can't quite recover from
-	 * turning off the pixel clock during disable/enables yet, so
-	 * it's always running.
-	 */
-	ret = clk_prepare_enable(hdmi->pixel_clock);
-	if (ret) {
-		DRM_ERROR("Failed to turn on pixel clock: %d\n", ret);
-		goto err_put_i2c;
-	}
-
-	/* This is the rate that is set by the firmware.  The number
-	 * needs to be a bit higher than the pixel clock rate
-	 * (generally 148.5Mhz).
-	 */
-	ret = clk_set_rate(hdmi->hsm_clock, 163682864);
-	if (ret) {
-		DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
-		goto err_unprepare_pix;
-	}
-
-	ret = clk_prepare_enable(hdmi->hsm_clock);
-	if (ret) {
-		DRM_ERROR("Failed to turn on HDMI state machine clock: %d\n",
-			  ret);
-		goto err_unprepare_pix;
-	}
-
 	/* Only use the GPIO HPD pin if present in the DT, otherwise
 	/* Only use the GPIO HPD pin if present in the DT, otherwise
 	 * we'll use the HDMI core's register.
 	 * we'll use the HDMI core's register.
 	 */
 	 */
@@ -1185,7 +1216,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 							 &hpd_gpio_flags);
 							 &hpd_gpio_flags);
 		if (hdmi->hpd_gpio < 0) {
 		if (hdmi->hpd_gpio < 0) {
 			ret = hdmi->hpd_gpio;
 			ret = hdmi->hpd_gpio;
-			goto err_unprepare_hsm;
+			goto err_put_i2c;
 		}
 		}
 
 
 		hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
 		hdmi->hpd_active_low = hpd_gpio_flags & OF_GPIO_ACTIVE_LOW;
@@ -1193,25 +1224,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 
 	vc4->hdmi = hdmi;
 	vc4->hdmi = hdmi;
 
 
-	/* HDMI core must be enabled. */
-	if (!(HD_READ(VC4_HD_M_CTL) & VC4_HD_M_ENABLE)) {
-		HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_SW_RST);
-		udelay(1);
-		HD_WRITE(VC4_HD_M_CTL, 0);
-
-		HD_WRITE(VC4_HD_M_CTL, VC4_HD_M_ENABLE);
-
-		HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL,
-			   VC4_HDMI_SW_RESET_HDMI |
-			   VC4_HDMI_SW_RESET_FORMAT_DETECT);
-
-		HDMI_WRITE(VC4_HDMI_SW_RESET_CONTROL, 0);
-
-		/* PHY should be in reset, like
-		 * vc4_hdmi_encoder_disable() does.
-		 */
-		HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
-	}
+	pm_runtime_enable(dev);
 
 
 	drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
 	drm_encoder_init(drm, hdmi->encoder, &vc4_hdmi_encoder_funcs,
 			 DRM_MODE_ENCODER_TMDS, NULL);
 			 DRM_MODE_ENCODER_TMDS, NULL);
@@ -1231,10 +1244,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 
 err_destroy_encoder:
 err_destroy_encoder:
 	vc4_hdmi_encoder_destroy(hdmi->encoder);
 	vc4_hdmi_encoder_destroy(hdmi->encoder);
-err_unprepare_hsm:
-	clk_disable_unprepare(hdmi->hsm_clock);
-err_unprepare_pix:
-	clk_disable_unprepare(hdmi->pixel_clock);
+	pm_runtime_disable(dev);
 err_put_i2c:
 err_put_i2c:
 	put_device(&hdmi->ddc->dev);
 	put_device(&hdmi->ddc->dev);
 
 
@@ -1253,8 +1263,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	vc4_hdmi_connector_destroy(hdmi->connector);
 	vc4_hdmi_connector_destroy(hdmi->connector);
 	vc4_hdmi_encoder_destroy(hdmi->encoder);
 	vc4_hdmi_encoder_destroy(hdmi->encoder);
 
 
-	clk_disable_unprepare(hdmi->pixel_clock);
-	clk_disable_unprepare(hdmi->hsm_clock);
+	pm_runtime_disable(dev);
+
 	put_device(&hdmi->ddc->dev);
 	put_device(&hdmi->ddc->dev);
 
 
 	vc4->hdmi = NULL;
 	vc4->hdmi = NULL;

+ 32 - 33
drivers/gpu/drm/vc4/vc4_irq.c

@@ -59,50 +59,45 @@ vc4_overflow_mem_work(struct work_struct *work)
 {
 {
 	struct vc4_dev *vc4 =
 	struct vc4_dev *vc4 =
 		container_of(work, struct vc4_dev, overflow_mem_work);
 		container_of(work, struct vc4_dev, overflow_mem_work);
-	struct drm_device *dev = vc4->dev;
-	struct vc4_bo *bo;
+	struct vc4_bo *bo = vc4->bin_bo;
+	int bin_bo_slot;
+	struct vc4_exec_info *exec;
+	unsigned long irqflags;
 
 
-	bo = vc4_bo_create(dev, 256 * 1024, true);
-	if (IS_ERR(bo)) {
+	bin_bo_slot = vc4_v3d_get_bin_slot(vc4);
+	if (bin_bo_slot < 0) {
 		DRM_ERROR("Couldn't allocate binner overflow mem\n");
 		DRM_ERROR("Couldn't allocate binner overflow mem\n");
 		return;
 		return;
 	}
 	}
 
 
-	/* If there's a job executing currently, then our previous
-	 * overflow allocation is getting used in that job and we need
-	 * to queue it to be released when the job is done.  But if no
-	 * job is executing at all, then we can free the old overflow
-	 * object direcctly.
-	 *
-	 * No lock necessary for this pointer since we're the only
-	 * ones that update the pointer, and our workqueue won't
-	 * reenter.
-	 */
-	if (vc4->overflow_mem) {
-		struct vc4_exec_info *current_exec;
-		unsigned long irqflags;
-
-		spin_lock_irqsave(&vc4->job_lock, irqflags);
-		current_exec = vc4_first_bin_job(vc4);
-		if (!current_exec)
-			current_exec = vc4_last_render_job(vc4);
-		if (current_exec) {
-			vc4->overflow_mem->seqno = current_exec->seqno;
-			list_add_tail(&vc4->overflow_mem->unref_head,
-				      &current_exec->unref_list);
-			vc4->overflow_mem = NULL;
+	spin_lock_irqsave(&vc4->job_lock, irqflags);
+
+	if (vc4->bin_alloc_overflow) {
+		/* If we had overflow memory allocated previously,
+		 * then that chunk will free when the current bin job
+		 * is done.  If we don't have a bin job running, then
+		 * the chunk will be done whenever the list of render
+		 * jobs has drained.
+		 */
+		exec = vc4_first_bin_job(vc4);
+		if (!exec)
+			exec = vc4_last_render_job(vc4);
+		if (exec) {
+			exec->bin_slots |= vc4->bin_alloc_overflow;
+		} else {
+			/* There's nothing queued in the hardware, so
+			 * the old slot is free immediately.
+			 */
+			vc4->bin_alloc_used &= ~vc4->bin_alloc_overflow;
 		}
 		}
-		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 	}
 	}
+	vc4->bin_alloc_overflow = BIT(bin_bo_slot);
 
 
-	if (vc4->overflow_mem)
-		drm_gem_object_unreference_unlocked(&vc4->overflow_mem->base.base);
-	vc4->overflow_mem = bo;
-
-	V3D_WRITE(V3D_BPOA, bo->base.paddr);
+	V3D_WRITE(V3D_BPOA, bo->base.paddr + bin_bo_slot * vc4->bin_alloc_size);
 	V3D_WRITE(V3D_BPOS, bo->base.base.size);
 	V3D_WRITE(V3D_BPOS, bo->base.base.size);
 	V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
 	V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM);
 	V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
 	V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM);
+	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
 }
 }
 
 
 static void
 static void
@@ -142,6 +137,10 @@ vc4_irq_finish_render_job(struct drm_device *dev)
 
 
 	vc4->finished_seqno++;
 	vc4->finished_seqno++;
 	list_move_tail(&exec->head, &vc4->job_done_list);
 	list_move_tail(&exec->head, &vc4->job_done_list);
+	if (exec->fence) {
+		dma_fence_signal_locked(exec->fence);
+		exec->fence = NULL;
+	}
 	vc4_submit_next_render_job(dev);
 	vc4_submit_next_render_job(dev);
 
 
 	wake_up_all(&vc4->job_wait_queue);
 	wake_up_all(&vc4->job_wait_queue);

+ 6 - 4
drivers/gpu/drm/vc4/vc4_kms.c

@@ -230,10 +230,12 @@ int vc4_kms_load(struct drm_device *dev)
 
 
 	drm_mode_config_reset(dev);
 	drm_mode_config_reset(dev);
 
 
-	vc4->fbdev = drm_fbdev_cma_init(dev, 32,
-					dev->mode_config.num_connector);
-	if (IS_ERR(vc4->fbdev))
-		vc4->fbdev = NULL;
+	if (dev->mode_config.num_connector) {
+		vc4->fbdev = drm_fbdev_cma_init(dev, 32,
+						dev->mode_config.num_connector);
+		if (IS_ERR(vc4->fbdev))
+			vc4->fbdev = NULL;
+	}
 
 
 	drm_kms_helper_poll_init(dev);
 	drm_kms_helper_poll_init(dev);
 
 

+ 1 - 2
drivers/gpu/drm/vc4/vc4_render_cl.c

@@ -182,8 +182,7 @@ static void emit_tile(struct vc4_exec_info *exec,
 
 
 	if (has_bin) {
 	if (has_bin) {
 		rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
 		rcl_u8(setup, VC4_PACKET_BRANCH_TO_SUB_LIST);
-		rcl_u32(setup, (exec->tile_bo->paddr +
-				exec->tile_alloc_offset +
+		rcl_u32(setup, (exec->tile_alloc_offset +
 				(y * exec->bin_tiles_x + x) * 32));
 				(y * exec->bin_tiles_x + x) * 32));
 	}
 	}
 
 

+ 180 - 0
drivers/gpu/drm/vc4/vc4_v3d.c

@@ -16,6 +16,7 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
+#include "linux/clk.h"
 #include "linux/component.h"
 #include "linux/component.h"
 #include "linux/pm_runtime.h"
 #include "linux/pm_runtime.h"
 #include "vc4_drv.h"
 #include "vc4_drv.h"
@@ -156,6 +157,144 @@ static void vc4_v3d_init_hw(struct drm_device *dev)
 	V3D_WRITE(V3D_VPMBASE, 0);
 	V3D_WRITE(V3D_VPMBASE, 0);
 }
 }
 
 
+int vc4_v3d_get_bin_slot(struct vc4_dev *vc4)
+{
+	struct drm_device *dev = vc4->dev;
+	unsigned long irqflags;
+	int slot;
+	uint64_t seqno = 0;
+	struct vc4_exec_info *exec;
+
+try_again:
+	spin_lock_irqsave(&vc4->job_lock, irqflags);
+	slot = ffs(~vc4->bin_alloc_used);
+	if (slot != 0) {
+		/* Switch from ffs() bit index to a 0-based index. */
+		slot--;
+		vc4->bin_alloc_used |= BIT(slot);
+		spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+		return slot;
+	}
+
+	/* Couldn't find an open slot.  Wait for render to complete
+	 * and try again.
+	 */
+	exec = vc4_last_render_job(vc4);
+	if (exec)
+		seqno = exec->seqno;
+	spin_unlock_irqrestore(&vc4->job_lock, irqflags);
+
+	if (seqno) {
+		int ret = vc4_wait_for_seqno(dev, seqno, ~0ull, true);
+
+		if (ret == 0)
+			goto try_again;
+
+		return ret;
+	}
+
+	return -ENOMEM;
+}
+
+/**
+ * vc4_allocate_bin_bo() - allocates the memory that will be used for
+ * tile binning.
+ *
+ * The binner has a limitation that the addresses in the tile state
+ * buffer that point into the tile alloc buffer or binner overflow
+ * memory only have 28 bits (256MB), and the top 4 on the bus for
+ * tile alloc references end up coming from the tile state buffer's
+ * address.
+ *
+ * To work around this, we allocate a single large buffer while V3D is
+ * in use, make sure that it has the top 4 bits constant across its
+ * entire extent, and then put the tile state, tile alloc, and binner
+ * overflow memory inside that buffer.
+ *
+ * This creates a limitation where we may not be able to execute a job
+ * if it doesn't fit within the buffer that we allocated up front.
+ * However, it turns out that 16MB is "enough for anybody", and
+ * real-world applications run into allocation failures from the
+ * overall CMA pool before they make scenes complicated enough to run
+ * out of bin space.
+ */
+int
+vc4_allocate_bin_bo(struct drm_device *drm)
+{
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct vc4_v3d *v3d = vc4->v3d;
+	uint32_t size = 16 * 1024 * 1024;
+	int ret = 0;
+	struct list_head list;
+
+	/* We may need to try allocating more than once to get a BO
+	 * that doesn't cross 256MB.  Track the ones we've allocated
+	 * that failed so far, so that we can free them when we've got
+	 * one that succeeded (if we freed them right away, our next
+	 * allocation would probably be the same chunk of memory).
+	 */
+	INIT_LIST_HEAD(&list);
+
+	while (true) {
+		struct vc4_bo *bo = vc4_bo_create(drm, size, true);
+
+		if (IS_ERR(bo)) {
+			ret = PTR_ERR(bo);
+
+			dev_err(&v3d->pdev->dev,
+				"Failed to allocate memory for tile binning: "
+				"%d. You may need to enable CMA or give it "
+				"more memory.",
+				ret);
+			break;
+		}
+
+		/* Check if this BO won't trigger the addressing bug. */
+		if ((bo->base.paddr & 0xf0000000) ==
+		    ((bo->base.paddr + bo->base.base.size - 1) & 0xf0000000)) {
+			vc4->bin_bo = bo;
+
+			/* Set up for allocating 512KB chunks of
+			 * binner memory.  The biggest allocation we
+			 * need to do is for the initial tile alloc +
+			 * tile state buffer.  We can render to a
+			 * maximum of ((2048*2048) / (32*32) = 4096
+			 * tiles in a frame (until we do floating
+			 * point rendering, at which point it would be
+			 * 8192).  Tile state is 48b/tile (rounded to
+			 * a page), and tile alloc is 32b/tile
+			 * (rounded to a page), plus a page of extra,
+			 * for a total of 320kb for our worst-case.
+			 * We choose 512kb so that it divides evenly
+			 * into our 16MB, and the rest of the 512kb
+			 * will be used as storage for the overflow
+			 * from the initial 32b CL per bin.
+			 */
+			vc4->bin_alloc_size = 512 * 1024;
+			vc4->bin_alloc_used = 0;
+			vc4->bin_alloc_overflow = 0;
+			WARN_ON_ONCE(sizeof(vc4->bin_alloc_used) * 8 !=
+				     bo->base.base.size / vc4->bin_alloc_size);
+
+			break;
+		}
+
+		/* Put it on the list to free later, and try again. */
+		list_add(&bo->unref_head, &list);
+	}
+
+	/* Free all the BOs we allocated but didn't choose. */
+	while (!list_empty(&list)) {
+		struct vc4_bo *bo = list_last_entry(&list,
+						    struct vc4_bo, unref_head);
+
+		list_del(&bo->unref_head);
+		drm_gem_object_put_unlocked(&bo->base.base);
+	}
+
+	return ret;
+}
+
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 static int vc4_v3d_runtime_suspend(struct device *dev)
 static int vc4_v3d_runtime_suspend(struct device *dev)
 {
 {
@@ -164,6 +303,11 @@ static int vc4_v3d_runtime_suspend(struct device *dev)
 
 
 	vc4_irq_uninstall(vc4->dev);
 	vc4_irq_uninstall(vc4->dev);
 
 
+	drm_gem_object_put_unlocked(&vc4->bin_bo->base.base);
+	vc4->bin_bo = NULL;
+
+	clk_disable_unprepare(v3d->clk);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -171,6 +315,15 @@ static int vc4_v3d_runtime_resume(struct device *dev)
 {
 {
 	struct vc4_v3d *v3d = dev_get_drvdata(dev);
 	struct vc4_v3d *v3d = dev_get_drvdata(dev);
 	struct vc4_dev *vc4 = v3d->vc4;
 	struct vc4_dev *vc4 = v3d->vc4;
+	int ret;
+
+	ret = vc4_allocate_bin_bo(vc4->dev);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(v3d->clk);
+	if (ret != 0)
+		return ret;
 
 
 	vc4_v3d_init_hw(vc4->dev);
 	vc4_v3d_init_hw(vc4->dev);
 	vc4_irq_postinstall(vc4->dev);
 	vc4_irq_postinstall(vc4->dev);
@@ -202,12 +355,38 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 	vc4->v3d = v3d;
 	vc4->v3d = v3d;
 	v3d->vc4 = vc4;
 	v3d->vc4 = vc4;
 
 
+	v3d->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(v3d->clk)) {
+		int ret = PTR_ERR(v3d->clk);
+
+		if (ret == -ENOENT) {
+			/* bcm2835 didn't have a clock reference in the DT. */
+			ret = 0;
+			v3d->clk = NULL;
+		} else {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "Failed to get V3D clock: %d\n",
+					ret);
+			return ret;
+		}
+	}
+
 	if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
 	if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
 		DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
 		DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
 			  V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
 			  V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	ret = clk_prepare_enable(v3d->clk);
+	if (ret != 0)
+		return ret;
+
+	ret = vc4_allocate_bin_bo(drm);
+	if (ret) {
+		clk_disable_unprepare(v3d->clk);
+		return ret;
+	}
+
 	/* Reset the binner overflow address/size at setup, to be sure
 	/* Reset the binner overflow address/size at setup, to be sure
 	 * we don't reuse an old one.
 	 * we don't reuse an old one.
 	 */
 	 */
@@ -271,6 +450,7 @@ static int vc4_v3d_dev_remove(struct platform_device *pdev)
 
 
 static const struct of_device_id vc4_v3d_dt_match[] = {
 static const struct of_device_id vc4_v3d_dt_match[] = {
 	{ .compatible = "brcm,bcm2835-v3d" },
 	{ .compatible = "brcm,bcm2835-v3d" },
+	{ .compatible = "brcm,cygnus-v3d" },
 	{ .compatible = "brcm,vc4-v3d" },
 	{ .compatible = "brcm,vc4-v3d" },
 	{}
 	{}
 };
 };

+ 24 - 30
drivers/gpu/drm/vc4/vc4_validate.c

@@ -348,10 +348,11 @@ static int
 validate_tile_binning_config(VALIDATE_ARGS)
 validate_tile_binning_config(VALIDATE_ARGS)
 {
 {
 	struct drm_device *dev = exec->exec_bo->base.dev;
 	struct drm_device *dev = exec->exec_bo->base.dev;
-	struct vc4_bo *tile_bo;
+	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	uint8_t flags;
 	uint8_t flags;
-	uint32_t tile_state_size, tile_alloc_size;
-	uint32_t tile_count;
+	uint32_t tile_state_size;
+	uint32_t tile_count, bin_addr;
+	int bin_slot;
 
 
 	if (exec->found_tile_binning_mode_config_packet) {
 	if (exec->found_tile_binning_mode_config_packet) {
 		DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
 		DRM_ERROR("Duplicate VC4_PACKET_TILE_BINNING_MODE_CONFIG\n");
@@ -377,13 +378,28 @@ validate_tile_binning_config(VALIDATE_ARGS)
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	bin_slot = vc4_v3d_get_bin_slot(vc4);
+	if (bin_slot < 0) {
+		if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) {
+			DRM_ERROR("Failed to allocate binner memory: %d\n",
+				  bin_slot);
+		}
+		return bin_slot;
+	}
+
+	/* The slot we allocated will only be used by this job, and is
+	 * free when the job completes rendering.
+	 */
+	exec->bin_slots |= BIT(bin_slot);
+	bin_addr = vc4->bin_bo->base.paddr + bin_slot * vc4->bin_alloc_size;
+
 	/* The tile state data array is 48 bytes per tile, and we put it at
 	/* The tile state data array is 48 bytes per tile, and we put it at
 	 * the start of a BO containing both it and the tile alloc.
 	 * the start of a BO containing both it and the tile alloc.
 	 */
 	 */
 	tile_state_size = 48 * tile_count;
 	tile_state_size = 48 * tile_count;
 
 
 	/* Since the tile alloc array will follow us, align. */
 	/* Since the tile alloc array will follow us, align. */
-	exec->tile_alloc_offset = roundup(tile_state_size, 4096);
+	exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096);
 
 
 	*(uint8_t *)(validated + 14) =
 	*(uint8_t *)(validated + 14) =
 		((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
 		((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK |
@@ -394,35 +410,13 @@ validate_tile_binning_config(VALIDATE_ARGS)
 		 VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
 		 VC4_SET_FIELD(VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE_128,
 			       VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
 			       VC4_BIN_CONFIG_ALLOC_BLOCK_SIZE));
 
 
-	/* Initial block size. */
-	tile_alloc_size = 32 * tile_count;
-
-	/*
-	 * The initial allocation gets rounded to the next 256 bytes before
-	 * the hardware starts fulfilling further allocations.
-	 */
-	tile_alloc_size = roundup(tile_alloc_size, 256);
-
-	/* Add space for the extra allocations.  This is what gets used first,
-	 * before overflow memory.  It must have at least 4096 bytes, but we
-	 * want to avoid overflow memory usage if possible.
-	 */
-	tile_alloc_size += 1024 * 1024;
-
-	tile_bo = vc4_bo_create(dev, exec->tile_alloc_offset + tile_alloc_size,
-				true);
-	exec->tile_bo = &tile_bo->base;
-	if (IS_ERR(exec->tile_bo))
-		return PTR_ERR(exec->tile_bo);
-	list_add_tail(&tile_bo->unref_head, &exec->unref_list);
-
 	/* tile alloc address. */
 	/* tile alloc address. */
-	*(uint32_t *)(validated + 0) = (exec->tile_bo->paddr +
-					exec->tile_alloc_offset);
+	*(uint32_t *)(validated + 0) = exec->tile_alloc_offset;
 	/* tile alloc size. */
 	/* tile alloc size. */
-	*(uint32_t *)(validated + 4) = tile_alloc_size;
+	*(uint32_t *)(validated + 4) = (bin_addr + vc4->bin_alloc_size -
+					exec->tile_alloc_offset);
 	/* tile state address. */
 	/* tile state address. */
-	*(uint32_t *)(validated + 8) = exec->tile_bo->paddr;
+	*(uint32_t *)(validated + 8) = bin_addr;
 
 
 	return 0;
 	return 0;
 }
 }

+ 152 - 42
drivers/gpu/drm/vgem/vgem_drv.c

@@ -42,10 +42,20 @@
 #define DRIVER_MAJOR	1
 #define DRIVER_MAJOR	1
 #define DRIVER_MINOR	0
 #define DRIVER_MINOR	0
 
 
+static struct vgem_device {
+	struct drm_device drm;
+	struct platform_device *platform;
+} *vgem_device;
+
 static void vgem_gem_free_object(struct drm_gem_object *obj)
 static void vgem_gem_free_object(struct drm_gem_object *obj)
 {
 {
 	struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
 	struct drm_vgem_gem_object *vgem_obj = to_vgem_bo(obj);
 
 
+	drm_free_large(vgem_obj->pages);
+
+	if (obj->import_attach)
+		drm_prime_gem_destroy(obj, vgem_obj->table);
+
 	drm_gem_object_release(obj);
 	drm_gem_object_release(obj);
 	kfree(vgem_obj);
 	kfree(vgem_obj);
 }
 }
@@ -56,26 +66,49 @@ static int vgem_gem_fault(struct vm_fault *vmf)
 	struct drm_vgem_gem_object *obj = vma->vm_private_data;
 	struct drm_vgem_gem_object *obj = vma->vm_private_data;
 	/* We don't use vmf->pgoff since that has the fake offset */
 	/* We don't use vmf->pgoff since that has the fake offset */
 	unsigned long vaddr = vmf->address;
 	unsigned long vaddr = vmf->address;
-	struct page *page;
-
-	page = shmem_read_mapping_page(file_inode(obj->base.filp)->i_mapping,
-				       (vaddr - vma->vm_start) >> PAGE_SHIFT);
-	if (!IS_ERR(page)) {
-		vmf->page = page;
-		return 0;
-	} else switch (PTR_ERR(page)) {
-		case -ENOSPC:
-		case -ENOMEM:
-			return VM_FAULT_OOM;
-		case -EBUSY:
-			return VM_FAULT_RETRY;
-		case -EFAULT:
-		case -EINVAL:
-			return VM_FAULT_SIGBUS;
-		default:
-			WARN_ON_ONCE(PTR_ERR(page));
-			return VM_FAULT_SIGBUS;
+	int ret;
+	loff_t num_pages;
+	pgoff_t page_offset;
+	page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
+
+	num_pages = DIV_ROUND_UP(obj->base.size, PAGE_SIZE);
+
+	if (page_offset > num_pages)
+		return VM_FAULT_SIGBUS;
+
+	if (obj->pages) {
+		get_page(obj->pages[page_offset]);
+		vmf->page = obj->pages[page_offset];
+		ret = 0;
+	} else {
+		struct page *page;
+
+		page = shmem_read_mapping_page(
+					file_inode(obj->base.filp)->i_mapping,
+					page_offset);
+		if (!IS_ERR(page)) {
+			vmf->page = page;
+			ret = 0;
+		} else switch (PTR_ERR(page)) {
+			case -ENOSPC:
+			case -ENOMEM:
+				ret = VM_FAULT_OOM;
+				break;
+			case -EBUSY:
+				ret = VM_FAULT_RETRY;
+				break;
+			case -EFAULT:
+			case -EINVAL:
+				ret = VM_FAULT_SIGBUS;
+				break;
+			default:
+				WARN_ON(PTR_ERR(page));
+				ret = VM_FAULT_SIGBUS;
+				break;
+		}
+
 	}
 	}
+	return ret;
 }
 }
 
 
 static const struct vm_operations_struct vgem_gem_vm_ops = {
 static const struct vm_operations_struct vgem_gem_vm_ops = {
@@ -112,12 +145,8 @@ static void vgem_postclose(struct drm_device *dev, struct drm_file *file)
 	kfree(vfile);
 	kfree(vfile);
 }
 }
 
 
-/* ioctls */
-
-static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
-					      struct drm_file *file,
-					      unsigned int *handle,
-					      unsigned long size)
+static struct drm_vgem_gem_object *__vgem_gem_create(struct drm_device *dev,
+						unsigned long size)
 {
 {
 	struct drm_vgem_gem_object *obj;
 	struct drm_vgem_gem_object *obj;
 	int ret;
 	int ret;
@@ -127,8 +156,31 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 
 
 	ret = drm_gem_object_init(dev, &obj->base, roundup(size, PAGE_SIZE));
 	ret = drm_gem_object_init(dev, &obj->base, roundup(size, PAGE_SIZE));
-	if (ret)
-		goto err_free;
+	if (ret) {
+		kfree(obj);
+		return ERR_PTR(ret);
+	}
+
+	return obj;
+}
+
+static void __vgem_gem_destroy(struct drm_vgem_gem_object *obj)
+{
+	drm_gem_object_release(&obj->base);
+	kfree(obj);
+}
+
+static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
+					      struct drm_file *file,
+					      unsigned int *handle,
+					      unsigned long size)
+{
+	struct drm_vgem_gem_object *obj;
+	int ret;
+
+	obj = __vgem_gem_create(dev, size);
+	if (IS_ERR(obj))
+		return ERR_CAST(obj);
 
 
 	ret = drm_gem_handle_create(file, &obj->base, handle);
 	ret = drm_gem_handle_create(file, &obj->base, handle);
 	drm_gem_object_unreference_unlocked(&obj->base);
 	drm_gem_object_unreference_unlocked(&obj->base);
@@ -137,9 +189,8 @@ static struct drm_gem_object *vgem_gem_create(struct drm_device *dev,
 
 
 	return &obj->base;
 	return &obj->base;
 
 
-err_free:
-	kfree(obj);
 err:
 err:
+	__vgem_gem_destroy(obj);
 	return ERR_PTR(ret);
 	return ERR_PTR(ret);
 }
 }
 
 
@@ -256,6 +307,37 @@ static struct sg_table *vgem_prime_get_sg_table(struct drm_gem_object *obj)
 	return st;
 	return st;
 }
 }
 
 
+static struct drm_gem_object* vgem_prime_import(struct drm_device *dev,
+						struct dma_buf *dma_buf)
+{
+	struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
+
+	return drm_gem_prime_import_dev(dev, dma_buf, &vgem->platform->dev);
+}
+
+static struct drm_gem_object *vgem_prime_import_sg_table(struct drm_device *dev,
+			struct dma_buf_attachment *attach, struct sg_table *sg)
+{
+	struct drm_vgem_gem_object *obj;
+	int npages;
+
+	obj = __vgem_gem_create(dev, attach->dmabuf->size);
+	if (IS_ERR(obj))
+		return ERR_CAST(obj);
+
+	npages = PAGE_ALIGN(attach->dmabuf->size) / PAGE_SIZE;
+
+	obj->table = sg;
+	obj->pages = drm_malloc_ab(npages, sizeof(struct page *));
+	if (!obj->pages) {
+		__vgem_gem_destroy(obj);
+		return ERR_PTR(-ENOMEM);
+	}
+	drm_prime_sg_to_page_addr_arrays(obj->table, obj->pages, NULL,
+					npages);
+	return &obj->base;
+}
+
 static void *vgem_prime_vmap(struct drm_gem_object *obj)
 static void *vgem_prime_vmap(struct drm_gem_object *obj)
 {
 {
 	long n_pages = obj->size >> PAGE_SHIFT;
 	long n_pages = obj->size >> PAGE_SHIFT;
@@ -300,8 +382,19 @@ static int vgem_prime_mmap(struct drm_gem_object *obj,
 	return 0;
 	return 0;
 }
 }
 
 
+static void vgem_release(struct drm_device *dev)
+{
+	struct vgem_device *vgem = container_of(dev, typeof(*vgem), drm);
+
+	platform_device_unregister(vgem->platform);
+	drm_dev_fini(&vgem->drm);
+
+	kfree(vgem);
+}
+
 static struct drm_driver vgem_driver = {
 static struct drm_driver vgem_driver = {
 	.driver_features		= DRIVER_GEM | DRIVER_PRIME,
 	.driver_features		= DRIVER_GEM | DRIVER_PRIME,
+	.release			= vgem_release,
 	.open				= vgem_open,
 	.open				= vgem_open,
 	.postclose			= vgem_postclose,
 	.postclose			= vgem_postclose,
 	.gem_free_object_unlocked	= vgem_gem_free_object,
 	.gem_free_object_unlocked	= vgem_gem_free_object,
@@ -314,8 +407,11 @@ static struct drm_driver vgem_driver = {
 	.dumb_map_offset		= vgem_gem_dumb_map,
 	.dumb_map_offset		= vgem_gem_dumb_map,
 
 
 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 	.gem_prime_pin = vgem_prime_pin,
 	.gem_prime_pin = vgem_prime_pin,
+	.gem_prime_import = vgem_prime_import,
 	.gem_prime_export = drm_gem_prime_export,
 	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_import_sg_table = vgem_prime_import_sg_table,
 	.gem_prime_get_sg_table = vgem_prime_get_sg_table,
 	.gem_prime_get_sg_table = vgem_prime_get_sg_table,
 	.gem_prime_vmap = vgem_prime_vmap,
 	.gem_prime_vmap = vgem_prime_vmap,
 	.gem_prime_vunmap = vgem_prime_vunmap,
 	.gem_prime_vunmap = vgem_prime_vunmap,
@@ -328,34 +424,48 @@ static struct drm_driver vgem_driver = {
 	.minor	= DRIVER_MINOR,
 	.minor	= DRIVER_MINOR,
 };
 };
 
 
-static struct drm_device *vgem_device;
-
 static int __init vgem_init(void)
 static int __init vgem_init(void)
 {
 {
 	int ret;
 	int ret;
 
 
-	vgem_device = drm_dev_alloc(&vgem_driver, NULL);
-	if (IS_ERR(vgem_device)) {
-		ret = PTR_ERR(vgem_device);
-		goto out;
+	vgem_device = kzalloc(sizeof(*vgem_device), GFP_KERNEL);
+	if (!vgem_device)
+		return -ENOMEM;
+
+	ret = drm_dev_init(&vgem_device->drm, &vgem_driver, NULL);
+	if (ret)
+		goto out_free;
+
+	vgem_device->platform =
+		platform_device_register_simple("vgem", -1, NULL, 0);
+	if (!vgem_device->platform) {
+		ret = -ENODEV;
+		goto out_fini;
 	}
 	}
 
 
-	ret  = drm_dev_register(vgem_device, 0);
+	dma_coerce_mask_and_coherent(&vgem_device->platform->dev,
+				     DMA_BIT_MASK(64));
+
+	/* Final step: expose the device/driver to userspace */
+	ret  = drm_dev_register(&vgem_device->drm, 0);
 	if (ret)
 	if (ret)
-		goto out_unref;
+		goto out_unregister;
 
 
 	return 0;
 	return 0;
 
 
-out_unref:
-	drm_dev_unref(vgem_device);
-out:
+out_unregister:
+	platform_device_unregister(vgem_device->platform);
+out_fini:
+	drm_dev_fini(&vgem_device->drm);
+out_free:
+	kfree(vgem_device);
 	return ret;
 	return ret;
 }
 }
 
 
 static void __exit vgem_exit(void)
 static void __exit vgem_exit(void)
 {
 {
-	drm_dev_unregister(vgem_device);
-	drm_dev_unref(vgem_device);
+	drm_dev_unregister(&vgem_device->drm);
+	drm_dev_unref(&vgem_device->drm);
 }
 }
 
 
 module_init(vgem_init);
 module_init(vgem_init);

+ 2 - 0
drivers/gpu/drm/vgem/vgem_drv.h

@@ -43,6 +43,8 @@ struct vgem_file {
 #define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base)
 #define to_vgem_bo(x) container_of(x, struct drm_vgem_gem_object, base)
 struct drm_vgem_gem_object {
 struct drm_vgem_gem_object {
 	struct drm_gem_object base;
 	struct drm_gem_object base;
+	struct page **pages;
+	struct sg_table *table;
 };
 };
 
 
 int vgem_fence_open(struct vgem_file *file);
 int vgem_fence_open(struct vgem_file *file);

+ 1 - 0
drivers/gpu/drm/zte/Makefile

@@ -3,6 +3,7 @@ zxdrm-y := \
 	zx_hdmi.o \
 	zx_hdmi.o \
 	zx_plane.o \
 	zx_plane.o \
 	zx_tvenc.o \
 	zx_tvenc.o \
+	zx_vga.o \
 	zx_vou.o
 	zx_vou.o
 
 
 obj-$(CONFIG_DRM_ZTE) += zxdrm.o
 obj-$(CONFIG_DRM_ZTE) += zxdrm.o

+ 31 - 0
drivers/gpu/drm/zte/zx_common_regs.h

@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZX_COMMON_REGS_H__
+#define __ZX_COMMON_REGS_H__
+
+/* CSC registers */
+#define CSC_CTRL0			0x30
+#define CSC_COV_MODE_SHIFT		16
+#define CSC_COV_MODE_MASK		(0xffff << CSC_COV_MODE_SHIFT)
+#define CSC_BT601_IMAGE_RGB2YCBCR	0
+#define CSC_BT601_IMAGE_YCBCR2RGB	1
+#define CSC_BT601_VIDEO_RGB2YCBCR	2
+#define CSC_BT601_VIDEO_YCBCR2RGB	3
+#define CSC_BT709_IMAGE_RGB2YCBCR	4
+#define CSC_BT709_IMAGE_YCBCR2RGB	5
+#define CSC_BT709_VIDEO_RGB2YCBCR	6
+#define CSC_BT709_VIDEO_YCBCR2RGB	7
+#define CSC_BT2020_IMAGE_RGB2YCBCR	8
+#define CSC_BT2020_IMAGE_YCBCR2RGB	9
+#define CSC_BT2020_VIDEO_RGB2YCBCR	10
+#define CSC_BT2020_VIDEO_YCBCR2RGB	11
+#define CSC_WORK_ENABLE			BIT(0)
+
+#endif /* __ZX_COMMON_REGS_H__ */

+ 1 - 0
drivers/gpu/drm/zte/zx_drm_drv.c

@@ -233,6 +233,7 @@ static struct platform_driver *drivers[] = {
 	&zx_crtc_driver,
 	&zx_crtc_driver,
 	&zx_hdmi_driver,
 	&zx_hdmi_driver,
 	&zx_tvenc_driver,
 	&zx_tvenc_driver,
+	&zx_vga_driver,
 	&zx_drm_platform_driver,
 	&zx_drm_platform_driver,
 };
 };
 
 

+ 1 - 0
drivers/gpu/drm/zte/zx_drm_drv.h

@@ -14,6 +14,7 @@
 extern struct platform_driver zx_crtc_driver;
 extern struct platform_driver zx_crtc_driver;
 extern struct platform_driver zx_hdmi_driver;
 extern struct platform_driver zx_hdmi_driver;
 extern struct platform_driver zx_tvenc_driver;
 extern struct platform_driver zx_tvenc_driver;
+extern struct platform_driver zx_vga_driver;
 
 
 static inline u32 zx_readl(void __iomem *reg)
 static inline u32 zx_readl(void __iomem *reg)
 {
 {

+ 1 - 0
drivers/gpu/drm/zte/zx_plane.c

@@ -16,6 +16,7 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 #include <drm/drmP.h>
 
 
+#include "zx_common_regs.h"
 #include "zx_drm_drv.h"
 #include "zx_drm_drv.h"
 #include "zx_plane.h"
 #include "zx_plane.h"
 #include "zx_plane_regs.h"
 #include "zx_plane_regs.h"

+ 0 - 18
drivers/gpu/drm/zte/zx_plane_regs.h

@@ -77,24 +77,6 @@
 #define LUMA_STRIDE(x)	 (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
 #define LUMA_STRIDE(x)	 (((x) << LUMA_STRIDE_SHIFT) & LUMA_STRIDE_MASK)
 #define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
 #define CHROMA_STRIDE(x) (((x) << CHROMA_STRIDE_SHIFT) & CHROMA_STRIDE_MASK)
 
 
-/* CSC registers */
-#define CSC_CTRL0			0x30
-#define CSC_COV_MODE_SHIFT		16
-#define CSC_COV_MODE_MASK		(0xffff << CSC_COV_MODE_SHIFT)
-#define CSC_BT601_IMAGE_RGB2YCBCR	0
-#define CSC_BT601_IMAGE_YCBCR2RGB	1
-#define CSC_BT601_VIDEO_RGB2YCBCR	2
-#define CSC_BT601_VIDEO_YCBCR2RGB	3
-#define CSC_BT709_IMAGE_RGB2YCBCR	4
-#define CSC_BT709_IMAGE_YCBCR2RGB	5
-#define CSC_BT709_VIDEO_RGB2YCBCR	6
-#define CSC_BT709_VIDEO_YCBCR2RGB	7
-#define CSC_BT2020_IMAGE_RGB2YCBCR	8
-#define CSC_BT2020_IMAGE_YCBCR2RGB	9
-#define CSC_BT2020_VIDEO_RGB2YCBCR	10
-#define CSC_BT2020_VIDEO_YCBCR2RGB	11
-#define CSC_WORK_ENABLE			BIT(0)
-
 /* RSZ registers */
 /* RSZ registers */
 #define RSZ_SRC_CFG			0x00
 #define RSZ_SRC_CFG			0x00
 #define RSZ_DEST_CFG			0x04
 #define RSZ_DEST_CFG			0x04

+ 531 - 0
drivers/gpu/drm/zte/zx_vga.c

@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drmP.h>
+
+#include "zx_drm_drv.h"
+#include "zx_vga_regs.h"
+#include "zx_vou.h"
+
+struct zx_vga_pwrctrl {
+	struct regmap *regmap;
+	u32 reg;
+	u32 mask;
+};
+
+struct zx_vga_i2c {
+	struct i2c_adapter adap;
+	struct mutex lock;
+};
+
+struct zx_vga {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct zx_vga_i2c *ddc;
+	struct device *dev;
+	void __iomem *mmio;
+	struct clk *i2c_wclk;
+	struct zx_vga_pwrctrl pwrctrl;
+	struct completion complete;
+	bool connected;
+};
+
+#define to_zx_vga(x) container_of(x, struct zx_vga, x)
+
+static void zx_vga_encoder_enable(struct drm_encoder *encoder)
+{
+	struct zx_vga *vga = to_zx_vga(encoder);
+	struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+
+	/* Set bit to power up VGA DACs */
+	regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask,
+			   pwrctrl->mask);
+
+	vou_inf_enable(VOU_VGA, encoder->crtc);
+}
+
+static void zx_vga_encoder_disable(struct drm_encoder *encoder)
+{
+	struct zx_vga *vga = to_zx_vga(encoder);
+	struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+
+	vou_inf_disable(VOU_VGA, encoder->crtc);
+
+	/* Clear bit to power down VGA DACs */
+	regmap_update_bits(pwrctrl->regmap, pwrctrl->reg, pwrctrl->mask, 0);
+}
+
+static const struct drm_encoder_helper_funcs zx_vga_encoder_helper_funcs = {
+	.enable	= zx_vga_encoder_enable,
+	.disable = zx_vga_encoder_disable,
+};
+
+static const struct drm_encoder_funcs zx_vga_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int zx_vga_connector_get_modes(struct drm_connector *connector)
+{
+	struct zx_vga *vga = to_zx_vga(connector);
+	struct edid *edid;
+	int ret;
+
+	/*
+	 * Clear both detection bits to switch I2C bus from device
+	 * detecting to EDID reading.
+	 */
+	zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, 0);
+
+	edid = drm_get_edid(connector, &vga->ddc->adap);
+	if (!edid) {
+		/*
+		 * If EDID reading fails, we set the device state into
+		 * disconnected.  Locking is not required here, since the
+		 * VGA_AUTO_DETECT_SEL register write in irq handler cannot
+		 * be triggered when both detection bits are cleared as above.
+		 */
+		zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
+			  VGA_DETECT_SEL_NO_DEVICE);
+		vga->connected = false;
+		return 0;
+	}
+
+	/*
+	 * As edid reading succeeds, device must be connected, so we set
+	 * up detection bit for unplug interrupt here.
+	 */
+	zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_HAS_DEVICE);
+
+	drm_mode_connector_update_edid_property(connector, edid);
+	ret = drm_add_edid_modes(connector, edid);
+	kfree(edid);
+
+	return ret;
+}
+
+static enum drm_mode_status
+zx_vga_connector_mode_valid(struct drm_connector *connector,
+			    struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs zx_vga_connector_helper_funcs = {
+	.get_modes = zx_vga_connector_get_modes,
+	.mode_valid = zx_vga_connector_mode_valid,
+};
+
+static enum drm_connector_status
+zx_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct zx_vga *vga = to_zx_vga(connector);
+
+	return vga->connected ? connector_status_connected :
+				connector_status_disconnected;
+}
+
+static const struct drm_connector_funcs zx_vga_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = zx_vga_connector_detect,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int zx_vga_register(struct drm_device *drm, struct zx_vga *vga)
+{
+	struct drm_encoder *encoder = &vga->encoder;
+	struct drm_connector *connector = &vga->connector;
+	struct device *dev = vga->dev;
+	int ret;
+
+	encoder->possible_crtcs = VOU_CRTC_MASK;
+
+	ret = drm_encoder_init(drm, encoder, &zx_vga_encoder_funcs,
+			       DRM_MODE_ENCODER_DAC, NULL);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to init encoder: %d\n", ret);
+		return ret;
+	};
+
+	drm_encoder_helper_add(encoder, &zx_vga_encoder_helper_funcs);
+
+	vga->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	ret = drm_connector_init(drm, connector, &zx_vga_connector_funcs,
+				 DRM_MODE_CONNECTOR_VGA);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to init connector: %d\n", ret);
+		goto clean_encoder;
+	};
+
+	drm_connector_helper_add(connector, &zx_vga_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to attach encoder: %d\n", ret);
+		goto clean_connector;
+	};
+
+	return 0;
+
+clean_connector:
+	drm_connector_cleanup(connector);
+clean_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static int zx_vga_pwrctrl_init(struct zx_vga *vga)
+{
+	struct zx_vga_pwrctrl *pwrctrl = &vga->pwrctrl;
+	struct device *dev = vga->dev;
+	struct of_phandle_args out_args;
+	struct regmap *regmap;
+	int ret;
+
+	ret = of_parse_phandle_with_fixed_args(dev->of_node,
+				"zte,vga-power-control", 2, 0, &out_args);
+	if (ret)
+		return ret;
+
+	regmap = syscon_node_to_regmap(out_args.np);
+	if (IS_ERR(regmap)) {
+		ret = PTR_ERR(regmap);
+		goto out;
+	}
+
+	pwrctrl->regmap = regmap;
+	pwrctrl->reg = out_args.args[0];
+	pwrctrl->mask = out_args.args[1];
+
+out:
+	of_node_put(out_args.np);
+	return ret;
+}
+
+static int zx_vga_i2c_read(struct zx_vga *vga, struct i2c_msg *msg)
+{
+	int len = msg->len;
+	u8 *buf = msg->buf;
+	u32 offset = 0;
+	int i;
+
+	reinit_completion(&vga->complete);
+
+	/* Select combo write */
+	zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_COMBO, VGA_CMD_COMBO);
+	zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_RW, 0);
+
+	while (len > 0) {
+		u32 cnt;
+
+		/* Clear RX FIFO */
+		zx_writel_mask(vga->mmio + VGA_RXF_CTRL, VGA_RX_FIFO_CLEAR,
+			       VGA_RX_FIFO_CLEAR);
+
+		/* Data offset to read from */
+		zx_writel(vga->mmio + VGA_SUB_ADDR, offset);
+
+		/* Kick off the transfer */
+		zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS,
+			       VGA_CMD_TRANS);
+
+		if (!wait_for_completion_timeout(&vga->complete,
+						 msecs_to_jiffies(1000))) {
+			DRM_DEV_ERROR(vga->dev, "transfer timeout\n");
+			return -ETIMEDOUT;
+		}
+
+		cnt = zx_readl(vga->mmio + VGA_RXF_STATUS);
+		cnt = (cnt & VGA_RXF_COUNT_MASK) >> VGA_RXF_COUNT_SHIFT;
+		/* FIFO status may report more data than we need to read */
+		cnt = min_t(u32, len, cnt);
+
+		for (i = 0; i < cnt; i++)
+			*buf++ = zx_readl(vga->mmio + VGA_DATA);
+
+		len -= cnt;
+		offset += cnt;
+	}
+
+	return 0;
+}
+
+static int zx_vga_i2c_write(struct zx_vga *vga, struct i2c_msg *msg)
+{
+	/*
+	 * The DDC I2C adapter is only for reading EDID data, so we assume
+	 * that the write to this adapter must be the EDID data offset.
+	 */
+	if ((msg->len != 1) || ((msg->addr != DDC_ADDR)))
+		return -EINVAL;
+
+	/* Hardware will take care of the slave address shifting */
+	zx_writel(vga->mmio + VGA_DEVICE_ADDR, msg->addr);
+
+	return 0;
+}
+
+static int zx_vga_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+			   int num)
+{
+	struct zx_vga *vga = i2c_get_adapdata(adap);
+	struct zx_vga_i2c *ddc = vga->ddc;
+	int ret = 0;
+	int i;
+
+	mutex_lock(&ddc->lock);
+
+	for (i = 0; i < num; i++) {
+		if (msgs[i].flags & I2C_M_RD)
+			ret = zx_vga_i2c_read(vga, &msgs[i]);
+		else
+			ret = zx_vga_i2c_write(vga, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	mutex_unlock(&ddc->lock);
+
+	return ret;
+}
+
+static u32 zx_vga_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm zx_vga_algorithm = {
+	.master_xfer	= zx_vga_i2c_xfer,
+	.functionality	= zx_vga_i2c_func,
+};
+
+static int zx_vga_ddc_register(struct zx_vga *vga)
+{
+	struct device *dev = vga->dev;
+	struct i2c_adapter *adap;
+	struct zx_vga_i2c *ddc;
+	int ret;
+
+	ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
+	if (!ddc)
+		return -ENOMEM;
+
+	vga->ddc = ddc;
+	mutex_init(&ddc->lock);
+
+	adap = &ddc->adap;
+	adap->owner = THIS_MODULE;
+	adap->class = I2C_CLASS_DDC;
+	adap->dev.parent = dev;
+	adap->algo = &zx_vga_algorithm;
+	snprintf(adap->name, sizeof(adap->name), "zx vga i2c");
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to add I2C adapter: %d\n", ret);
+		return ret;
+	}
+
+	i2c_set_adapdata(adap, vga);
+
+	return 0;
+}
+
+static irqreturn_t zx_vga_irq_thread(int irq, void *dev_id)
+{
+	struct zx_vga *vga = dev_id;
+
+	drm_helper_hpd_irq_event(vga->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t zx_vga_irq_handler(int irq, void *dev_id)
+{
+	struct zx_vga *vga = dev_id;
+	u32 status;
+
+	status = zx_readl(vga->mmio + VGA_I2C_STATUS);
+
+	/* Clear interrupt status */
+	zx_writel_mask(vga->mmio + VGA_I2C_STATUS, VGA_CLEAR_IRQ,
+		       VGA_CLEAR_IRQ);
+
+	if (status & VGA_DEVICE_CONNECTED) {
+		/*
+		 * Since VGA_DETECT_SEL bits need to be reset for switching DDC
+		 * bus from device detection to EDID read, rather than setting
+		 * up HAS_DEVICE bit here, we need to do that in .get_modes
+		 * hook for unplug detecting after EDID read succeeds.
+		 */
+		vga->connected = true;
+		return IRQ_WAKE_THREAD;
+	}
+
+	if (status & VGA_DEVICE_DISCONNECTED) {
+		zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL,
+			  VGA_DETECT_SEL_NO_DEVICE);
+		vga->connected = false;
+		return IRQ_WAKE_THREAD;
+	}
+
+	if (status & VGA_TRANS_DONE) {
+		complete(&vga->complete);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void zx_vga_hw_init(struct zx_vga *vga)
+{
+	unsigned long ref = clk_get_rate(vga->i2c_wclk);
+	int div;
+
+	/*
+	 * Set up I2C fast speed divider per formula below to get 400kHz.
+	 *   scl = ref / ((div + 1) * 4)
+	 */
+	div = DIV_ROUND_UP(ref / 1000, 400 * 4) - 1;
+	zx_writel(vga->mmio + VGA_CLK_DIV_FS, div);
+
+	/* Set up device detection */
+	zx_writel(vga->mmio + VGA_AUTO_DETECT_PARA, 0x80);
+	zx_writel(vga->mmio + VGA_AUTO_DETECT_SEL, VGA_DETECT_SEL_NO_DEVICE);
+
+	/*
+	 * We need to poke monitor via DDC bus to get connection irq
+	 * start working.
+	 */
+	zx_writel(vga->mmio + VGA_DEVICE_ADDR, DDC_ADDR);
+	zx_writel_mask(vga->mmio + VGA_CMD_CFG, VGA_CMD_TRANS, VGA_CMD_TRANS);
+}
+
+static int zx_vga_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct resource *res;
+	struct zx_vga *vga;
+	int irq;
+	int ret;
+
+	vga = devm_kzalloc(dev, sizeof(*vga), GFP_KERNEL);
+	if (!vga)
+		return -ENOMEM;
+
+	vga->dev = dev;
+	dev_set_drvdata(dev, vga);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	vga->mmio = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vga->mmio))
+		return PTR_ERR(vga->mmio);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	vga->i2c_wclk = devm_clk_get(dev, "i2c_wclk");
+	if (IS_ERR(vga->i2c_wclk)) {
+		ret = PTR_ERR(vga->i2c_wclk);
+		DRM_DEV_ERROR(dev, "failed to get i2c_wclk: %d\n", ret);
+		return ret;
+	}
+
+	ret = zx_vga_pwrctrl_init(vga);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to init power control: %d\n", ret);
+		return ret;
+	}
+
+	ret = zx_vga_ddc_register(vga);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to register ddc: %d\n", ret);
+		return ret;
+	}
+
+	ret = zx_vga_register(drm, vga);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to register vga: %d\n", ret);
+		return ret;
+	}
+
+	init_completion(&vga->complete);
+
+	ret = devm_request_threaded_irq(dev, irq, zx_vga_irq_handler,
+					zx_vga_irq_thread, IRQF_SHARED,
+					dev_name(dev), vga);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to request threaded irq: %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(vga->i2c_wclk);
+	if (ret)
+		return ret;
+
+	zx_vga_hw_init(vga);
+
+	return 0;
+}
+
+static void zx_vga_unbind(struct device *dev, struct device *master,
+			  void *data)
+{
+	struct zx_vga *vga = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(vga->i2c_wclk);
+}
+
+static const struct component_ops zx_vga_component_ops = {
+	.bind = zx_vga_bind,
+	.unbind = zx_vga_unbind,
+};
+
+static int zx_vga_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &zx_vga_component_ops);
+}
+
+static int zx_vga_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &zx_vga_component_ops);
+	return 0;
+}
+
+static const struct of_device_id zx_vga_of_match[] = {
+	{ .compatible = "zte,zx296718-vga", },
+	{ /* end */ },
+};
+MODULE_DEVICE_TABLE(of, zx_vga_of_match);
+
+struct platform_driver zx_vga_driver = {
+	.probe = zx_vga_probe,
+	.remove = zx_vga_remove,
+	.driver	= {
+		.name = "zx-vga",
+		.of_match_table	= zx_vga_of_match,
+	},
+};

+ 36 - 0
drivers/gpu/drm/zte/zx_vga_regs.h

@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Sanechips Technology Co., Ltd.
+ * Copyright 2017 Linaro Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZX_VGA_REGS_H__
+#define __ZX_VGA_REGS_H__
+
+#define VGA_CMD_CFG			0x04
+#define VGA_CMD_TRANS			BIT(6)
+#define VGA_CMD_COMBO			BIT(5)
+#define VGA_CMD_RW			BIT(4)
+#define VGA_SUB_ADDR			0x0c
+#define VGA_DEVICE_ADDR			0x10
+#define VGA_CLK_DIV_FS			0x14
+#define VGA_RXF_CTRL			0x20
+#define VGA_RX_FIFO_CLEAR		BIT(7)
+#define VGA_DATA			0x24
+#define VGA_I2C_STATUS			0x28
+#define VGA_DEVICE_DISCONNECTED		BIT(7)
+#define VGA_DEVICE_CONNECTED		BIT(6)
+#define VGA_CLEAR_IRQ			BIT(4)
+#define VGA_TRANS_DONE			BIT(0)
+#define VGA_RXF_STATUS			0x30
+#define VGA_RXF_COUNT_SHIFT		2
+#define VGA_RXF_COUNT_MASK		GENMASK(7, 2)
+#define VGA_AUTO_DETECT_PARA		0x34
+#define VGA_AUTO_DETECT_SEL		0x38
+#define VGA_DETECT_SEL_HAS_DEVICE	BIT(1)
+#define VGA_DETECT_SEL_NO_DEVICE	BIT(0)
+
+#endif /* __ZX_VGA_REGS_H__ */

+ 31 - 5
drivers/gpu/drm/zte/zx_vou.c

@@ -23,6 +23,7 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drmP.h>
 #include <drm/drmP.h>
 
 
+#include "zx_common_regs.h"
 #include "zx_drm_drv.h"
 #include "zx_drm_drv.h"
 #include "zx_plane.h"
 #include "zx_plane.h"
 #include "zx_vou.h"
 #include "zx_vou.h"
@@ -122,6 +123,8 @@ struct zx_crtc {
 	struct drm_plane *primary;
 	struct drm_plane *primary;
 	struct zx_vou_hw *vou;
 	struct zx_vou_hw *vou;
 	void __iomem *chnreg;
 	void __iomem *chnreg;
+	void __iomem *chncsc;
+	void __iomem *dither;
 	const struct zx_crtc_regs *regs;
 	const struct zx_crtc_regs *regs;
 	const struct zx_crtc_bits *bits;
 	const struct zx_crtc_bits *bits;
 	enum vou_chn_type chn_type;
 	enum vou_chn_type chn_type;
@@ -204,6 +207,11 @@ static struct vou_inf vou_infs[] = {
 		.clocks_en_bits = BIT(15),
 		.clocks_en_bits = BIT(15),
 		.clocks_sel_bits = BIT(11) | BIT(0),
 		.clocks_sel_bits = BIT(11) | BIT(0),
 	},
 	},
+	[VOU_VGA] = {
+		.data_sel = VOU_RGB_888,
+		.clocks_en_bits = BIT(1),
+		.clocks_sel_bits = BIT(10),
+	},
 };
 };
 
 
 static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
 static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
@@ -227,9 +235,26 @@ void vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc)
 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
 	struct zx_crtc *zcrtc = to_zx_crtc(crtc);
 	struct zx_vou_hw *vou = zcrtc->vou;
 	struct zx_vou_hw *vou = zcrtc->vou;
 	struct vou_inf *inf = &vou_infs[id];
 	struct vou_inf *inf = &vou_infs[id];
+	void __iomem *dither = zcrtc->dither;
+	void __iomem *csc = zcrtc->chncsc;
 	bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
 	bool is_main = zcrtc->chn_type == VOU_CHN_MAIN;
 	u32 data_sel_shift = id << 1;
 	u32 data_sel_shift = id << 1;
 
 
+	if (inf->data_sel != VOU_YUV444) {
+		/* Enable channel CSC for RGB output */
+		zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK,
+			       CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT);
+		zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE,
+			       CSC_WORK_ENABLE);
+
+		/* Bypass Dither block for RGB output */
+		zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS,
+			       DITHER_BYSPASS);
+	} else {
+		zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0);
+		zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0);
+	}
+
 	/* Select data format */
 	/* Select data format */
 	zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
 	zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift,
 		       inf->data_sel << data_sel_shift);
 		       inf->data_sel << data_sel_shift);
@@ -525,20 +550,24 @@ static int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou,
 
 
 	if (chn_type == VOU_CHN_MAIN) {
 	if (chn_type == VOU_CHN_MAIN) {
 		zplane->layer = vou->osd + MAIN_GL_OFFSET;
 		zplane->layer = vou->osd + MAIN_GL_OFFSET;
-		zplane->csc = vou->osd + MAIN_CSC_OFFSET;
+		zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET;
 		zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
 		zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET;
 		zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
 		zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET;
 		zplane->bits = &zx_gl_bits[0];
 		zplane->bits = &zx_gl_bits[0];
 		zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
 		zcrtc->chnreg = vou->osd + OSD_MAIN_CHN;
+		zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET;
+		zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET;
 		zcrtc->regs = &main_crtc_regs;
 		zcrtc->regs = &main_crtc_regs;
 		zcrtc->bits = &main_crtc_bits;
 		zcrtc->bits = &main_crtc_bits;
 	} else {
 	} else {
 		zplane->layer = vou->osd + AUX_GL_OFFSET;
 		zplane->layer = vou->osd + AUX_GL_OFFSET;
-		zplane->csc = vou->osd + AUX_CSC_OFFSET;
+		zplane->csc = vou->osd + AUX_GL_CSC_OFFSET;
 		zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
 		zplane->hbsc = vou->osd + AUX_HBSC_OFFSET;
 		zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
 		zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET;
 		zplane->bits = &zx_gl_bits[1];
 		zplane->bits = &zx_gl_bits[1];
 		zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
 		zcrtc->chnreg = vou->osd + OSD_AUX_CHN;
+		zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET;
+		zcrtc->dither = vou->osd + AUX_DITHER_OFFSET;
 		zcrtc->regs = &aux_crtc_regs;
 		zcrtc->regs = &aux_crtc_regs;
 		zcrtc->bits = &aux_crtc_bits;
 		zcrtc->bits = &aux_crtc_bits;
 	}
 	}
@@ -705,9 +734,6 @@ static void vou_hw_init(struct zx_vou_hw *vou)
 	/* Release reset for all VOU modules */
 	/* Release reset for all VOU modules */
 	zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
 	zx_writel(vou->vouctl + VOU_SOFT_RST, ~0);
 
 
-	/* Enable clock auto-gating for all VOU modules */
-	zx_writel(vou->vouctl + VOU_CLK_REQEN, ~0);
-
 	/* Enable all VOU module clocks */
 	/* Enable all VOU module clocks */
 	zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
 	zx_writel(vou->vouctl + VOU_CLK_EN, ~0);
 
 

+ 10 - 2
drivers/gpu/drm/zte/zx_vou_regs.h

@@ -13,13 +13,17 @@
 
 
 /* Sub-module offset */
 /* Sub-module offset */
 #define MAIN_GL_OFFSET			0x130
 #define MAIN_GL_OFFSET			0x130
-#define MAIN_CSC_OFFSET			0x580
+#define MAIN_GL_CSC_OFFSET		0x580
+#define MAIN_CHN_CSC_OFFSET		0x6c0
 #define MAIN_HBSC_OFFSET		0x820
 #define MAIN_HBSC_OFFSET		0x820
+#define MAIN_DITHER_OFFSET		0x960
 #define MAIN_RSZ_OFFSET			0x600 /* OTFPPU sub-module */
 #define MAIN_RSZ_OFFSET			0x600 /* OTFPPU sub-module */
 
 
 #define AUX_GL_OFFSET			0x200
 #define AUX_GL_OFFSET			0x200
-#define AUX_CSC_OFFSET			0x5d0
+#define AUX_GL_CSC_OFFSET		0x5d0
+#define AUX_CHN_CSC_OFFSET		0x710
 #define AUX_HBSC_OFFSET			0x860
 #define AUX_HBSC_OFFSET			0x860
+#define AUX_DITHER_OFFSET		0x970
 #define AUX_RSZ_OFFSET			0x800
 #define AUX_RSZ_OFFSET			0x800
 
 
 #define OSD_VL0_OFFSET			0x040
 #define OSD_VL0_OFFSET			0x040
@@ -78,6 +82,10 @@
 #define CHN_INTERLACE_BUF_CTRL		0x24
 #define CHN_INTERLACE_BUF_CTRL		0x24
 #define CHN_INTERLACE_EN		BIT(2)
 #define CHN_INTERLACE_EN		BIT(2)
 
 
+/* Dither registers */
+#define OSD_DITHER_CTRL0		0x00
+#define DITHER_BYSPASS			BIT(31)
+
 /* TIMING_CTRL registers */
 /* TIMING_CTRL registers */
 #define TIMING_TC_ENABLE		0x04
 #define TIMING_TC_ENABLE		0x04
 #define AUX_TC_EN			BIT(1)
 #define AUX_TC_EN			BIT(1)

+ 0 - 9
include/drm/drmP.h

@@ -320,15 +320,6 @@ struct pci_controller;
 #define DRM_IF_VERSION(maj, min) (maj << 16 | min)
 #define DRM_IF_VERSION(maj, min) (maj << 16 | min)
 
 
 
 
-/* Flags and return codes for get_vblank_timestamp() driver function. */
-#define DRM_CALLED_FROM_VBLIRQ 1
-#define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0)
-
-/* get_scanout_position() return flags */
-#define DRM_SCANOUTPOS_VALID        (1 << 0)
-#define DRM_SCANOUTPOS_IN_VBLANK    (1 << 1)
-#define DRM_SCANOUTPOS_ACCURATE     (1 << 2)
-
 /**
 /**
  * DRM device structure. This structure represent a complete card that
  * DRM device structure. This structure represent a complete card that
  * may contain multiple heads.
  * may contain multiple heads.

+ 95 - 0
include/drm/drm_atomic.h

@@ -154,6 +154,53 @@ struct __drm_connnectors_state {
 	struct drm_connector_state *state, *old_state, *new_state;
 	struct drm_connector_state *state, *old_state, *new_state;
 };
 };
 
 
+/**
+ * struct drm_private_state_funcs - atomic state functions for private objects
+ *
+ * These hooks are used by atomic helpers to create, swap and destroy states of
+ * private objects. The structure itself is used as a vtable to identify the
+ * associated private object type. Each private object type that needs to be
+ * added to the atomic states is expected to have an implementation of these
+ * hooks and pass a pointer to it's drm_private_state_funcs struct to
+ * drm_atomic_get_private_obj_state().
+ */
+struct drm_private_state_funcs {
+	/**
+	 * @duplicate_state:
+	 *
+	 * Duplicate the current state of the private object and return it. It
+	 * is an error to call this before obj->state has been initialized.
+	 *
+	 * RETURNS:
+	 *
+	 * Duplicated atomic state or NULL when obj->state is not
+	 * initialized or allocation failed.
+	 */
+	void *(*duplicate_state)(struct drm_atomic_state *state, void *obj);
+
+	/**
+	 * @swap_state:
+	 *
+	 * This function swaps the existing state of a private object @obj with
+	 * it's newly created state, the pointer to which is passed as
+	 * @obj_state_ptr.
+	 */
+	void (*swap_state)(void *obj, void **obj_state_ptr);
+
+	/**
+	 * @destroy_state:
+	 *
+	 * Frees the private object state created with @duplicate_state.
+	 */
+	void (*destroy_state)(void *obj_state);
+};
+
+struct __drm_private_objs_state {
+	void *obj;
+	void *obj_state;
+	const struct drm_private_state_funcs *funcs;
+};
+
 /**
 /**
  * struct drm_atomic_state - the global state object for atomic updates
  * struct drm_atomic_state - the global state object for atomic updates
  * @ref: count of all references to this state (will not be freed until zero)
  * @ref: count of all references to this state (will not be freed until zero)
@@ -164,6 +211,8 @@ struct __drm_connnectors_state {
  * @crtcs: pointer to array of CRTC pointers
  * @crtcs: pointer to array of CRTC pointers
  * @num_connector: size of the @connectors and @connector_states arrays
  * @num_connector: size of the @connectors and @connector_states arrays
  * @connectors: pointer to array of structures with per-connector data
  * @connectors: pointer to array of structures with per-connector data
+ * @num_private_objs: size of the @private_objs array
+ * @private_objs: pointer to array of private object pointers
  * @acquire_ctx: acquire context for this atomic modeset state update
  * @acquire_ctx: acquire context for this atomic modeset state update
  */
  */
 struct drm_atomic_state {
 struct drm_atomic_state {
@@ -176,6 +225,8 @@ struct drm_atomic_state {
 	struct __drm_crtcs_state *crtcs;
 	struct __drm_crtcs_state *crtcs;
 	int num_connector;
 	int num_connector;
 	struct __drm_connnectors_state *connectors;
 	struct __drm_connnectors_state *connectors;
+	int num_private_objs;
+	struct __drm_private_objs_state *private_objs;
 
 
 	struct drm_modeset_acquire_ctx *acquire_ctx;
 	struct drm_modeset_acquire_ctx *acquire_ctx;
 
 
@@ -268,6 +319,11 @@ int drm_atomic_connector_set_property(struct drm_connector *connector,
 		struct drm_connector_state *state, struct drm_property *property,
 		struct drm_connector_state *state, struct drm_property *property,
 		uint64_t val);
 		uint64_t val);
 
 
+void * __must_check
+drm_atomic_get_private_obj_state(struct drm_atomic_state *state,
+			      void *obj,
+			      const struct drm_private_state_funcs *funcs);
+
 /**
 /**
  * drm_atomic_get_existing_crtc_state - get crtc state, if it exists
  * drm_atomic_get_existing_crtc_state - get crtc state, if it exists
  * @state: global atomic state object
  * @state: global atomic state object
@@ -752,6 +808,45 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p);
 	     (__i)++)							\
 	     (__i)++)							\
 		for_each_if (plane)
 		for_each_if (plane)
 
 
+/**
+ * __for_each_private_obj - iterate over all private objects
+ * @__state: &struct drm_atomic_state pointer
+ * @obj: private object iteration cursor
+ * @obj_state: private object state iteration cursor
+ * @__i: int iteration cursor, for macro-internal use
+ * @__funcs: &struct drm_private_state_funcs iteration cursor
+ *
+ * This macro iterates over the array containing private object data in atomic
+ * state
+ */
+#define __for_each_private_obj(__state, obj, obj_state, __i, __funcs)	\
+	for ((__i) = 0;							\
+	     (__i) < (__state)->num_private_objs &&			\
+	     ((obj) = (__state)->private_objs[__i].obj,			\
+	      (__funcs) = (__state)->private_objs[__i].funcs,		\
+	      (obj_state) = (__state)->private_objs[__i].obj_state,	\
+	      1);							\
+	     (__i)++)							\
+
+/**
+ * for_each_private_obj - iterate over a specify type of private object
+ * @__state: &struct drm_atomic_state pointer
+ * @obj_funcs: &struct drm_private_state_funcs function table to filter
+ * 	private objects
+ * @obj: private object iteration cursor
+ * @obj_state: private object state iteration cursor
+ * @__i: int iteration cursor, for macro-internal use
+ * @__funcs: &struct drm_private_state_funcs iteration cursor
+ *
+ * This macro iterates over the private objects state array while filtering the
+ * objects based on the vfunc table that is passed as @obj_funcs. New macros
+ * can be created by passing in the vfunc table associated with a specific
+ * private object.
+ */
+#define for_each_private_obj(__state, obj_funcs, obj, obj_state, __i, __funcs)	\
+	__for_each_private_obj(__state, obj, obj_state, __i, __funcs)		\
+		for_each_if (__funcs == obj_funcs)
+
 /**
 /**
  * drm_atomic_crtc_needs_modeset - compute combined modeset need
  * drm_atomic_crtc_needs_modeset - compute combined modeset need
  * @state: &drm_crtc_state for the CRTC
  * @state: &drm_crtc_state for the CRTC

+ 1 - 0
include/drm/drm_blend.h

@@ -28,6 +28,7 @@
 
 
 struct drm_device;
 struct drm_device;
 struct drm_atomic_state;
 struct drm_atomic_state;
+struct drm_plane;
 
 
 /*
 /*
  * Rotation property bits. DRM_ROTATE_<degrees> rotates the image by the
  * Rotation property bits. DRM_ROTATE_<degrees> rotates the image by the

+ 2 - 0
include/drm/drm_color_mgmt.h

@@ -25,6 +25,8 @@
 
 
 #include <linux/ctype.h>
 #include <linux/ctype.h>
 
 
+struct drm_crtc;
+
 uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision);
 uint32_t drm_color_lut_extract(uint32_t user_input, uint32_t bit_precision);
 
 
 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
 void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,

+ 21 - 0
include/drm/drm_connector.h

@@ -25,6 +25,7 @@
 
 
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/ctype.h>
 #include <linux/ctype.h>
+#include <linux/hdmi.h>
 #include <drm/drm_mode_object.h>
 #include <drm/drm_mode_object.h>
 
 
 #include <uapi/drm/drm_mode.h>
 #include <uapi/drm/drm_mode.h>
@@ -326,6 +327,21 @@ struct drm_connector_state {
 	struct drm_atomic_state *state;
 	struct drm_atomic_state *state;
 
 
 	struct drm_tv_connector_state tv;
 	struct drm_tv_connector_state tv;
+
+	/**
+	 * @picture_aspect_ratio: Connector property to control the
+	 * HDMI infoframe aspect ratio setting.
+	 *
+	 * The %DRM_MODE_PICTURE_ASPECT_\* values much match the
+	 * values for &enum hdmi_picture_aspect
+	 */
+	enum hdmi_picture_aspect picture_aspect_ratio;
+
+	/**
+	 * @scaling_mode: Connector property to control the
+	 * upscaling, mostly used for built-in panels.
+	 */
+	unsigned int scaling_mode;
 };
 };
 
 
 /**
 /**
@@ -675,6 +691,7 @@ struct drm_cmdline_mode {
  * @tile_v_loc: vertical location of this tile
  * @tile_v_loc: vertical location of this tile
  * @tile_h_size: horizontal size of this tile.
  * @tile_h_size: horizontal size of this tile.
  * @tile_v_size: vertical size of this tile.
  * @tile_v_size: vertical size of this tile.
+ * @scaling_mode_property:  Optional atomic property to control the upscaling.
  *
  *
  * Each connector may be connected to one or more CRTCs, or may be clonable by
  * Each connector may be connected to one or more CRTCs, or may be clonable by
  * another connector if they can share a CRTC.  Each connector also has a specific
  * another connector if they can share a CRTC.  Each connector also has a specific
@@ -754,6 +771,8 @@ struct drm_connector {
 	struct drm_property_blob *edid_blob_ptr;
 	struct drm_property_blob *edid_blob_ptr;
 	struct drm_object_properties properties;
 	struct drm_object_properties properties;
 
 
+	struct drm_property *scaling_mode_property;
+
 	/**
 	/**
 	 * @path_blob_ptr:
 	 * @path_blob_ptr:
 	 *
 	 *
@@ -953,6 +972,8 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
 				  unsigned int num_modes,
 				  unsigned int num_modes,
 				  const char * const modes[]);
 				  const char * const modes[]);
 int drm_mode_create_scaling_mode_property(struct drm_device *dev);
 int drm_mode_create_scaling_mode_property(struct drm_device *dev);
+int drm_connector_attach_scaling_mode_property(struct drm_connector *connector,
+					       u32 scaling_mode_mask);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
 int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
 int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
 
 

+ 28 - 6
include/drm/drm_crtc.h

@@ -93,11 +93,6 @@ struct drm_plane_helper_funcs;
  * @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings
  * @adjusted_mode: for use by helpers and drivers to compute adjusted mode timings
  * @mode: current mode timings
  * @mode: current mode timings
  * @mode_blob: &drm_property_blob for @mode
  * @mode_blob: &drm_property_blob for @mode
- * @degamma_lut: Lookup table for converting framebuffer pixel data
- *	before apply the conversion matrix
- * @ctm: Transformation matrix
- * @gamma_lut: Lookup table for converting pixel data after the
- *	conversion matrix
  * @state: backpointer to global drm_atomic_state
  * @state: backpointer to global drm_atomic_state
  *
  *
  * Note that the distinction between @enable and @active is rather subtile:
  * Note that the distinction between @enable and @active is rather subtile:
@@ -144,9 +139,30 @@ struct drm_crtc_state {
 	/* blob property to expose current mode to atomic userspace */
 	/* blob property to expose current mode to atomic userspace */
 	struct drm_property_blob *mode_blob;
 	struct drm_property_blob *mode_blob;
 
 
-	/* blob property to expose color management to userspace */
+	/**
+	 * @degamma_lut:
+	 *
+	 * Lookup table for converting framebuffer pixel data before apply the
+	 * color conversion matrix @ctm. See drm_crtc_enable_color_mgmt(). The
+	 * blob (if not NULL) is an array of &struct drm_color_lut.
+	 */
 	struct drm_property_blob *degamma_lut;
 	struct drm_property_blob *degamma_lut;
+
+	/**
+	 * @ctm:
+	 *
+	 * Color transformation matrix. See drm_crtc_enable_color_mgmt(). The
+	 * blob (if not NULL) is a &struct drm_color_ctm.
+	 */
 	struct drm_property_blob *ctm;
 	struct drm_property_blob *ctm;
+
+	/**
+	 * @gamma_lut:
+	 *
+	 * Lookup table for converting pixel data after the color conversion
+	 * matrix @ctm.  See drm_crtc_enable_color_mgmt(). The blob (if not
+	 * NULL) is an array of &struct drm_color_lut.
+	 */
 	struct drm_property_blob *gamma_lut;
 	struct drm_property_blob *gamma_lut;
 
 
 	/**
 	/**
@@ -313,6 +329,12 @@ struct drm_crtc_funcs {
 	 *
 	 *
 	 * This callback is optional.
 	 * This callback is optional.
 	 *
 	 *
+	 * Atomic drivers who want to support gamma tables should implement the
+	 * atomic color management support, enabled by calling
+	 * drm_crtc_enable_color_mgmt(), which then supports the legacy gamma
+	 * interface through the drm_atomic_helper_legacy_gamma_set()
+	 * compatibility implementation.
+	 *
 	 * NOTE:
 	 * NOTE:
 	 *
 	 *
 	 * Drivers that support gamma tables and also fbdev emulation through
 	 * Drivers that support gamma tables and also fbdev emulation through

+ 166 - 0
include/drm/drm_dp_helper.h

@@ -179,6 +179,111 @@
 
 
 #define DP_GUID				    0x030   /* 1.2 */
 #define DP_GUID				    0x030   /* 1.2 */
 
 
+#define DP_DSC_SUPPORT                      0x060   /* DP 1.4 */
+# define DP_DSC_DECOMPRESSION_IS_SUPPORTED  (1 << 0)
+
+#define DP_DSC_REV                          0x061
+# define DP_DSC_MAJOR_MASK                  (0xf << 0)
+# define DP_DSC_MINOR_MASK                  (0xf << 4)
+# define DP_DSC_MAJOR_SHIFT                 0
+# define DP_DSC_MINOR_SHIFT                 4
+
+#define DP_DSC_RC_BUF_BLK_SIZE              0x062
+# define DP_DSC_RC_BUF_BLK_SIZE_1           0x0
+# define DP_DSC_RC_BUF_BLK_SIZE_4           0x1
+# define DP_DSC_RC_BUF_BLK_SIZE_16          0x2
+# define DP_DSC_RC_BUF_BLK_SIZE_64          0x3
+
+#define DP_DSC_RC_BUF_SIZE                  0x063
+
+#define DP_DSC_SLICE_CAP_1                  0x064
+# define DP_DSC_1_PER_DP_DSC_SINK           (1 << 0)
+# define DP_DSC_2_PER_DP_DSC_SINK           (1 << 1)
+# define DP_DSC_4_PER_DP_DSC_SINK           (1 << 3)
+# define DP_DSC_6_PER_DP_DSC_SINK           (1 << 4)
+# define DP_DSC_8_PER_DP_DSC_SINK           (1 << 5)
+# define DP_DSC_10_PER_DP_DSC_SINK          (1 << 6)
+# define DP_DSC_12_PER_DP_DSC_SINK          (1 << 7)
+
+#define DP_DSC_LINE_BUF_BIT_DEPTH           0x065
+# define DP_DSC_LINE_BUF_BIT_DEPTH_MASK     (0xf << 0)
+# define DP_DSC_LINE_BUF_BIT_DEPTH_9        0x0
+# define DP_DSC_LINE_BUF_BIT_DEPTH_10       0x1
+# define DP_DSC_LINE_BUF_BIT_DEPTH_11       0x2
+# define DP_DSC_LINE_BUF_BIT_DEPTH_12       0x3
+# define DP_DSC_LINE_BUF_BIT_DEPTH_13       0x4
+# define DP_DSC_LINE_BUF_BIT_DEPTH_14       0x5
+# define DP_DSC_LINE_BUF_BIT_DEPTH_15       0x6
+# define DP_DSC_LINE_BUF_BIT_DEPTH_16       0x7
+# define DP_DSC_LINE_BUF_BIT_DEPTH_8        0x8
+
+#define DP_DSC_BLK_PREDICTION_SUPPORT       0x066
+# define DP_DSC_BLK_PREDICTION_IS_SUPPORTED (1 << 0)
+
+#define DP_DSC_MAX_BITS_PER_PIXEL_LOW       0x067   /* eDP 1.4 */
+
+#define DP_DSC_MAX_BITS_PER_PIXEL_HI        0x068   /* eDP 1.4 */
+
+#define DP_DSC_DEC_COLOR_FORMAT_CAP         0x069
+# define DP_DSC_RGB                         (1 << 0)
+# define DP_DSC_YCbCr444                    (1 << 1)
+# define DP_DSC_YCbCr422_Simple             (1 << 2)
+# define DP_DSC_YCbCr422_Native             (1 << 3)
+# define DP_DSC_YCbCr420_Native             (1 << 4)
+
+#define DP_DSC_DEC_COLOR_DEPTH_CAP          0x06A
+# define DP_DSC_8_BPC                       (1 << 1)
+# define DP_DSC_10_BPC                      (1 << 2)
+# define DP_DSC_12_BPC                      (1 << 3)
+
+#define DP_DSC_PEAK_THROUGHPUT              0x06B
+# define DP_DSC_THROUGHPUT_MODE_0_MASK      (0xf << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_SHIFT     0
+# define DP_DSC_THROUGHPUT_MODE_0_340       (1 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_400       (2 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_450       (3 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_500       (4 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_550       (5 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_600       (6 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_650       (7 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_700       (8 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_750       (9 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_800       (10 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_850       (11 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_900       (12 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_950       (13 << 0)
+# define DP_DSC_THROUGHPUT_MODE_0_1000      (14 << 0)
+# define DP_DSC_THROUGHPUT_MODE_1_MASK      (0xf << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_SHIFT     4
+# define DP_DSC_THROUGHPUT_MODE_1_340       (1 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_400       (2 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_450       (3 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_500       (4 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_550       (5 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_600       (6 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_650       (7 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_700       (8 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_750       (9 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_800       (10 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_850       (11 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_900       (12 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_950       (13 << 4)
+# define DP_DSC_THROUGHPUT_MODE_1_1000      (14 << 4)
+
+#define DP_DSC_MAX_SLICE_WIDTH              0x06C
+
+#define DP_DSC_SLICE_CAP_2                  0x06D
+# define DP_DSC_16_PER_DP_DSC_SINK          (1 << 0)
+# define DP_DSC_20_PER_DP_DSC_SINK          (1 << 1)
+# define DP_DSC_24_PER_DP_DSC_SINK          (1 << 2)
+
+#define DP_DSC_BITS_PER_PIXEL_INC           0x06F
+# define DP_DSC_BITS_PER_PIXEL_1_16         0x0
+# define DP_DSC_BITS_PER_PIXEL_1_8          0x1
+# define DP_DSC_BITS_PER_PIXEL_1_4          0x2
+# define DP_DSC_BITS_PER_PIXEL_1_2          0x3
+# define DP_DSC_BITS_PER_PIXEL_1            0x4
+
 #define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
 #define DP_PSR_SUPPORT                      0x070   /* XXX 1.2? */
 # define DP_PSR_IS_SUPPORTED                1
 # define DP_PSR_IS_SUPPORTED                1
 # define DP_PSR2_IS_SUPPORTED		    2	    /* eDP 1.4 */
 # define DP_PSR2_IS_SUPPORTED		    2	    /* eDP 1.4 */
@@ -339,6 +444,8 @@
 #define DP_AUX_FRAME_SYNC_VALUE		    0x15c   /* eDP 1.4 */
 #define DP_AUX_FRAME_SYNC_VALUE		    0x15c   /* eDP 1.4 */
 # define DP_AUX_FRAME_SYNC_VALID	    (1 << 0)
 # define DP_AUX_FRAME_SYNC_VALID	    (1 << 0)
 
 
+#define DP_DSC_ENABLE                       0x160   /* DP 1.4 */
+
 #define DP_PSR_EN_CFG			    0x170   /* XXX 1.2? */
 #define DP_PSR_EN_CFG			    0x170   /* XXX 1.2? */
 # define DP_PSR_ENABLE			    (1 << 0)
 # define DP_PSR_ENABLE			    (1 << 0)
 # define DP_PSR_MAIN_LINK_ACTIVE	    (1 << 1)
 # define DP_PSR_MAIN_LINK_ACTIVE	    (1 << 1)
@@ -603,6 +710,9 @@
 #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* 1.2 */
 #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0   0x2003   /* 1.2 */
 
 
 #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
 #define DP_DEVICE_SERVICE_IRQ_VECTOR_ESI1   0x2004   /* 1.2 */
+# define DP_RX_GTC_MSTR_REQ_STATUS_CHANGE    (1 << 0)
+# define DP_LOCK_ACQUISITION_REQUEST         (1 << 1)
+# define DP_CEC_IRQ                          (1 << 2)
 
 
 #define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
 #define DP_LINK_SERVICE_IRQ_VECTOR_ESI0     0x2005   /* 1.2 */
 
 
@@ -636,6 +746,62 @@
 # define DP_VSC_EXT_CEA_SDP_SUPPORTED			(1 << 6)  /* DP 1.4 */
 # define DP_VSC_EXT_CEA_SDP_SUPPORTED			(1 << 6)  /* DP 1.4 */
 # define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED		(1 << 7)  /* DP 1.4 */
 # define DP_VSC_EXT_CEA_SDP_CHAINING_SUPPORTED		(1 << 7)  /* DP 1.4 */
 
 
+/* HDMI CEC tunneling over AUX DP 1.3 section 5.3.3.3.1 DPCD 1.4+ */
+#define DP_CEC_TUNNELING_CAPABILITY            0x3000
+# define DP_CEC_TUNNELING_CAPABLE               (1 << 0)
+# define DP_CEC_SNOOPING_CAPABLE                (1 << 1)
+# define DP_CEC_MULTIPLE_LA_CAPABLE             (1 << 2)
+
+#define DP_CEC_TUNNELING_CONTROL               0x3001
+# define DP_CEC_TUNNELING_ENABLE                (1 << 0)
+# define DP_CEC_SNOOPING_ENABLE                 (1 << 1)
+
+#define DP_CEC_RX_MESSAGE_INFO                 0x3002
+# define DP_CEC_RX_MESSAGE_LEN_MASK             (0xf << 0)
+# define DP_CEC_RX_MESSAGE_LEN_SHIFT            0
+# define DP_CEC_RX_MESSAGE_HPD_STATE            (1 << 4)
+# define DP_CEC_RX_MESSAGE_HPD_LOST             (1 << 5)
+# define DP_CEC_RX_MESSAGE_ACKED                (1 << 6)
+# define DP_CEC_RX_MESSAGE_ENDED                (1 << 7)
+
+#define DP_CEC_TX_MESSAGE_INFO                 0x3003
+# define DP_CEC_TX_MESSAGE_LEN_MASK             (0xf << 0)
+# define DP_CEC_TX_MESSAGE_LEN_SHIFT            0
+# define DP_CEC_TX_RETRY_COUNT_MASK             (0x7 << 4)
+# define DP_CEC_TX_RETRY_COUNT_SHIFT            4
+# define DP_CEC_TX_MESSAGE_SEND                 (1 << 7)
+
+#define DP_CEC_TUNNELING_IRQ_FLAGS             0x3004
+# define DP_CEC_RX_MESSAGE_INFO_VALID           (1 << 0)
+# define DP_CEC_RX_MESSAGE_OVERFLOW             (1 << 1)
+# define DP_CEC_TX_MESSAGE_SENT                 (1 << 4)
+# define DP_CEC_TX_LINE_ERROR                   (1 << 5)
+# define DP_CEC_TX_ADDRESS_NACK_ERROR           (1 << 6)
+# define DP_CEC_TX_DATA_NACK_ERROR              (1 << 7)
+
+#define DP_CEC_LOGICAL_ADDRESS_MASK            0x300E /* 0x300F word */
+# define DP_CEC_LOGICAL_ADDRESS_0               (1 << 0)
+# define DP_CEC_LOGICAL_ADDRESS_1               (1 << 1)
+# define DP_CEC_LOGICAL_ADDRESS_2               (1 << 2)
+# define DP_CEC_LOGICAL_ADDRESS_3               (1 << 3)
+# define DP_CEC_LOGICAL_ADDRESS_4               (1 << 4)
+# define DP_CEC_LOGICAL_ADDRESS_5               (1 << 5)
+# define DP_CEC_LOGICAL_ADDRESS_6               (1 << 6)
+# define DP_CEC_LOGICAL_ADDRESS_7               (1 << 7)
+#define DP_CEC_LOGICAL_ADDRESS_MASK_2          0x300F /* 0x300E word */
+# define DP_CEC_LOGICAL_ADDRESS_8               (1 << 0)
+# define DP_CEC_LOGICAL_ADDRESS_9               (1 << 1)
+# define DP_CEC_LOGICAL_ADDRESS_10              (1 << 2)
+# define DP_CEC_LOGICAL_ADDRESS_11              (1 << 3)
+# define DP_CEC_LOGICAL_ADDRESS_12              (1 << 4)
+# define DP_CEC_LOGICAL_ADDRESS_13              (1 << 5)
+# define DP_CEC_LOGICAL_ADDRESS_14              (1 << 6)
+# define DP_CEC_LOGICAL_ADDRESS_15              (1 << 7)
+
+#define DP_CEC_RX_MESSAGE_BUFFER               0x3010
+#define DP_CEC_TX_MESSAGE_BUFFER               0x3020
+#define DP_CEC_MESSAGE_BUFFER_LENGTH             0x10
+
 /* DP 1.2 Sideband message defines */
 /* DP 1.2 Sideband message defines */
 /* peer device type - DP 1.2a Table 2-92 */
 /* peer device type - DP 1.2a Table 2-92 */
 #define DP_PEER_DEVICE_NONE		0x0
 #define DP_PEER_DEVICE_NONE		0x0

Some files were not shown because too many files changed in this diff