Browse Source

Merge remote-tracking branch 'pfdo/drm-next' into drm-next

Pull in drm-next for the object find API changes.

Fix the one place the API crashes.

Signed-off-by: Dave Airlie <airlied@redhat.com>
Dave Airlie 7 years ago
parent
commit
bd21a37d41
100 changed files with 4854 additions and 2017 deletions
  1. 4 0
      Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
  2. 49 0
      Documentation/devicetree/bindings/display/bridge/sii9234.txt
  3. 49 0
      Documentation/devicetree/bindings/display/panel/raspberrypi,7inch-touchscreen.txt
  4. 3 0
      Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
  5. 42 14
      drivers/dma-buf/reservation.c
  6. 8 8
      drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
  7. 2 2
      drivers/gpu/drm/amd/amdgpu/dce_virtual.c
  8. 1 1
      drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
  9. 1 1
      drivers/gpu/drm/ast/ast_mode.c
  10. 1 1
      drivers/gpu/drm/bochs/bochs_kms.c
  11. 9 1
      drivers/gpu/drm/bridge/Kconfig
  12. 1 0
      drivers/gpu/drm/bridge/Makefile
  13. 8 0
      drivers/gpu/drm/bridge/adv7511/Kconfig
  14. 1 0
      drivers/gpu/drm/bridge/adv7511/Makefile
  15. 36 7
      drivers/gpu/drm/bridge/adv7511/adv7511.h
  16. 337 0
      drivers/gpu/drm/bridge/adv7511/adv7511_cec.c
  17. 101 15
      drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
  18. 2 36
      drivers/gpu/drm/bridge/adv7511/adv7533.c
  19. 9 1
      drivers/gpu/drm/bridge/panel.c
  20. 994 0
      drivers/gpu/drm/bridge/sii9234.c
  21. 91 5
      drivers/gpu/drm/bridge/sil-sii8620.c
  22. 1 4
      drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
  23. 1 1
      drivers/gpu/drm/cirrus/cirrus_mode.c
  24. 4 7
      drivers/gpu/drm/drm_atomic.c
  25. 4 3
      drivers/gpu/drm/drm_atomic_helper.c
  26. 2 2
      drivers/gpu/drm/drm_color_mgmt.c
  27. 1 1
      drivers/gpu/drm/drm_connector.c
  28. 4 4
      drivers/gpu/drm/drm_crtc.c
  29. 2 2
      drivers/gpu/drm/drm_crtc_helper.c
  30. 1 0
      drivers/gpu/drm/drm_crtc_internal.h
  31. 5 2
      drivers/gpu/drm/drm_dp_helper.c
  32. 1 1
      drivers/gpu/drm/drm_encoder.c
  33. 1 1
      drivers/gpu/drm/drm_fb_helper.c
  34. 5 4
      drivers/gpu/drm/drm_framebuffer.c
  35. 52 30
      drivers/gpu/drm/drm_gem_framebuffer_helper.c
  36. 0 1
      drivers/gpu/drm/drm_internal.h
  37. 1 1
      drivers/gpu/drm/drm_ioctl.c
  38. 6 4
      drivers/gpu/drm/drm_mode_object.c
  39. 7 7
      drivers/gpu/drm/drm_plane.c
  40. 1 1
      drivers/gpu/drm/drm_plane_helper.c
  41. 1 1
      drivers/gpu/drm/drm_probe_helper.c
  42. 3 3
      drivers/gpu/drm/drm_property.c
  43. 2 1
      drivers/gpu/drm/drm_syncobj.c
  44. 67 73
      drivers/gpu/drm/drm_vblank.c
  45. 0 2
      drivers/gpu/drm/etnaviv/Kconfig
  46. 2 1
      drivers/gpu/drm/etnaviv/Makefile
  47. 36 0
      drivers/gpu/drm/etnaviv/etnaviv_buffer.c
  48. 14 1
      drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c
  49. 5 1
      drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h
  50. 38 1
      drivers/gpu/drm/etnaviv/etnaviv_drv.c
  51. 1 5
      drivers/gpu/drm/etnaviv/etnaviv_drv.h
  52. 0 19
      drivers/gpu/drm/etnaviv/etnaviv_gem.c
  53. 67 2
      drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
  54. 167 50
      drivers/gpu/drm/etnaviv/etnaviv_gpu.c
  55. 11 2
      drivers/gpu/drm/etnaviv/etnaviv_gpu.h
  56. 73 124
      drivers/gpu/drm/etnaviv/etnaviv_iommu.c
  57. 5 2
      drivers/gpu/drm/etnaviv/etnaviv_iommu.h
  58. 52 68
      drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
  59. 68 38
      drivers/gpu/drm/etnaviv/etnaviv_mmu.c
  60. 23 13
      drivers/gpu/drm/etnaviv/etnaviv_mmu.h
  61. 495 0
      drivers/gpu/drm/etnaviv/etnaviv_perfmon.c
  62. 49 0
      drivers/gpu/drm/etnaviv/etnaviv_perfmon.h
  63. 1 1
      drivers/gpu/drm/gma500/mid_bios.c
  64. 4 5
      drivers/gpu/drm/gma500/psb_intel_sdvo.c
  65. 1 1
      drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c
  66. 2 2
      drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
  67. 1 0
      drivers/gpu/drm/i915/Kconfig
  68. 2 1
      drivers/gpu/drm/i915/Makefile
  69. 22 8
      drivers/gpu/drm/i915/gvt/cfg_space.c
  70. 24 13
      drivers/gpu/drm/i915/gvt/cmd_parser.c
  71. 95 32
      drivers/gpu/drm/i915/gvt/execlist.c
  72. 2 3
      drivers/gpu/drm/i915/gvt/gtt.c
  73. 1 1
      drivers/gpu/drm/i915/gvt/gvt.c
  74. 12 2
      drivers/gpu/drm/i915/gvt/gvt.h
  75. 26 18
      drivers/gpu/drm/i915/gvt/kvmgt.c
  76. 45 2
      drivers/gpu/drm/i915/gvt/mmio.c
  77. 1 1
      drivers/gpu/drm/i915/gvt/render.c
  78. 80 34
      drivers/gpu/drm/i915/gvt/scheduler.c
  79. 1 0
      drivers/gpu/drm/i915/gvt/scheduler.h
  80. 86 30
      drivers/gpu/drm/i915/i915_debugfs.c
  81. 26 16
      drivers/gpu/drm/i915/i915_drv.c
  82. 131 67
      drivers/gpu/drm/i915/i915_drv.h
  83. 10 50
      drivers/gpu/drm/i915/i915_gem.c
  84. 6 6
      drivers/gpu/drm/i915/i915_gem_context.c
  85. 7 8
      drivers/gpu/drm/i915/i915_gem_execbuffer.c
  86. 259 67
      drivers/gpu/drm/i915/i915_gem_gtt.c
  87. 41 5
      drivers/gpu/drm/i915/i915_gem_gtt.h
  88. 22 10
      drivers/gpu/drm/i915/i915_gem_request.c
  89. 0 35
      drivers/gpu/drm/i915/i915_gem_request.h
  90. 32 51
      drivers/gpu/drm/i915/i915_gem_userptr.c
  91. 18 11
      drivers/gpu/drm/i915/i915_gpu_error.c
  92. 141 217
      drivers/gpu/drm/i915/i915_guc_submission.c
  93. 417 544
      drivers/gpu/drm/i915/i915_irq.c
  94. 109 0
      drivers/gpu/drm/i915/i915_oa_cflgt2.c
  95. 34 0
      drivers/gpu/drm/i915/i915_oa_cflgt2.h
  96. 72 136
      drivers/gpu/drm/i915/i915_params.c
  97. 43 43
      drivers/gpu/drm/i915/i915_params.h
  98. 9 10
      drivers/gpu/drm/i915/i915_pci.c
  99. 9 5
      drivers/gpu/drm/i915/i915_perf.c
  100. 35 7
      drivers/gpu/drm/i915/i915_reg.h

+ 4 - 0
Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt

@@ -68,6 +68,8 @@ Optional properties:
 - adi,disable-timing-generator: Only for ADV7533. Disables the internal timing
 - adi,disable-timing-generator: Only for ADV7533. Disables the internal timing
   generator. The chip will rely on the sync signals in the DSI data lanes,
   generator. The chip will rely on the sync signals in the DSI data lanes,
   rather than generate its own timings for HDMI output.
   rather than generate its own timings for HDMI output.
+- clocks: from common clock binding: reference to the CEC clock.
+- clock-names: from common clock binding: must be "cec".
 
 
 Required nodes:
 Required nodes:
 
 
@@ -89,6 +91,8 @@ Example
 		reg = <39>;
 		reg = <39>;
 		interrupt-parent = <&gpio3>;
 		interrupt-parent = <&gpio3>;
 		interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
 		interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
+		clocks = <&cec_clock>;
+		clock-names = "cec";
 
 
 		adi,input-depth = <8>;
 		adi,input-depth = <8>;
 		adi,input-colorspace = "rgb";
 		adi,input-colorspace = "rgb";

+ 49 - 0
Documentation/devicetree/bindings/display/bridge/sii9234.txt

@@ -0,0 +1,49 @@
+Silicon Image SiI9234 HDMI/MHL bridge bindings
+
+Required properties:
+	- compatible : "sil,sii9234".
+	- reg : I2C address for TPI interface, use 0x39
+	- avcc33-supply : MHL/USB Switch Supply Voltage (3.3V)
+	- iovcc18-supply : I/O Supply Voltage (1.8V)
+	- avcc12-supply : TMDS Analog Supply Voltage (1.2V)
+	- cvcc12-supply : Digital Core Supply Voltage (1.2V)
+	- interrupts, interrupt-parent: interrupt specifier of INT pin
+	- reset-gpios: gpio specifier of RESET pin (active low)
+	- video interfaces: Device node can contain two video interface port
+			    nodes for HDMI encoder and connector according to [1].
+			    - port@0 - MHL to HDMI
+			    - port@1 - MHL to connector
+
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+
+
+Example:
+	sii9234@39 {
+		compatible = "sil,sii9234";
+		reg = <0x39>;
+		avcc33-supply = <&vcc33mhl>;
+		iovcc18-supply = <&vcc18mhl>;
+		avcc12-supply = <&vsil12>;
+		cvcc12-supply = <&vsil12>;
+		reset-gpios = <&gpf3 4 GPIO_ACTIVE_LOW>;
+		interrupt-parent = <&gpf3>;
+		interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				mhl_to_hdmi: endpoint {
+					remote-endpoint = <&hdmi_to_mhl>;
+				};
+			};
+			port@1 {
+				reg = <1>;
+				mhl_to_connector: endpoint {
+					remote-endpoint = <&connector_to_mhl>;
+				};
+			};
+		};
+	};

+ 49 - 0
Documentation/devicetree/bindings/display/panel/raspberrypi,7inch-touchscreen.txt

@@ -0,0 +1,49 @@
+This binding covers the official 7" (800x480) Raspberry Pi touchscreen
+panel.
+
+This DSI panel contains:
+
+- TC358762 DSI->DPI bridge
+- Atmel microcontroller on I2C for power sequencing the DSI bridge and
+  controlling backlight
+- Touchscreen controller on I2C for touch input
+
+and this binding covers the DSI display parts but not its touch input.
+
+Required properties:
+- compatible:	Must be "raspberrypi,7inch-touchscreen-panel"
+- reg:		Must be "45"
+- port:		See panel-common.txt
+
+Example:
+
+dsi1: dsi@7e700000 {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	<...>
+
+	port {
+		dsi_out_port: endpoint {
+			remote-endpoint = <&panel_dsi_port>;
+		};
+	};
+};
+
+i2c_dsi: i2c {
+	compatible = "i2c-gpio";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	gpios = <&gpio 28 0
+		 &gpio 29 0>;
+
+	lcd@45 {
+		compatible = "raspberrypi,7inch-touchscreen-panel";
+		reg = <0x45>;
+
+		port {
+			panel_dsi_port: endpoint {
+				remote-endpoint = <&dsi_out_port>;
+			};
+		};
+	};
+};

+ 3 - 0
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt

@@ -41,14 +41,17 @@ CEC. It is one end of the pipeline.
 Required properties:
 Required properties:
   - compatible: value must be one of:
   - compatible: value must be one of:
     * allwinner,sun5i-a10s-hdmi
     * allwinner,sun5i-a10s-hdmi
+    * allwinner,sun6i-a31-hdmi
   - reg: base address and size of memory-mapped region
   - reg: base address and size of memory-mapped region
   - interrupts: interrupt associated to this IP
   - interrupts: interrupt associated to this IP
   - clocks: phandles to the clocks feeding the HDMI encoder
   - clocks: phandles to the clocks feeding the HDMI encoder
     * ahb: the HDMI interface clock
     * ahb: the HDMI interface clock
     * mod: the HDMI module clock
     * mod: the HDMI module clock
+    * ddc: the HDMI ddc clock (A31 only)
     * pll-0: the first video PLL
     * pll-0: the first video PLL
     * pll-1: the second video PLL
     * pll-1: the second video PLL
   - clock-names: the clock names mentioned above
   - clock-names: the clock names mentioned above
+  - resets: phandle to the reset control for the HDMI encoder (A31 only)
   - dmas: phandles to the DMA channels used by the HDMI encoder
   - dmas: phandles to the DMA channels used by the HDMI encoder
     * ddc-tx: The channel for DDC transmission
     * ddc-tx: The channel for DDC transmission
     * ddc-rx: The channel for DDC reception
     * ddc-rx: The channel for DDC reception

+ 42 - 14
drivers/dma-buf/reservation.c

@@ -266,8 +266,7 @@ EXPORT_SYMBOL(reservation_object_add_excl_fence);
 * @dst: the destination reservation object
 * @dst: the destination reservation object
 * @src: the source reservation object
 * @src: the source reservation object
 *
 *
-* Copy all fences from src to dst. Both src->lock as well as dst-lock must be
-* held.
+* Copy all fences from src to dst. dst-lock must be held.
 */
 */
 int reservation_object_copy_fences(struct reservation_object *dst,
 int reservation_object_copy_fences(struct reservation_object *dst,
 				   struct reservation_object *src)
 				   struct reservation_object *src)
@@ -277,33 +276,62 @@ int reservation_object_copy_fences(struct reservation_object *dst,
 	size_t size;
 	size_t size;
 	unsigned i;
 	unsigned i;
 
 
-	src_list = reservation_object_get_list(src);
+	rcu_read_lock();
+	src_list = rcu_dereference(src->fence);
 
 
+retry:
 	if (src_list) {
 	if (src_list) {
-		size = offsetof(typeof(*src_list),
-				shared[src_list->shared_count]);
+		unsigned shared_count = src_list->shared_count;
+
+		size = offsetof(typeof(*src_list), shared[shared_count]);
+		rcu_read_unlock();
+
 		dst_list = kmalloc(size, GFP_KERNEL);
 		dst_list = kmalloc(size, GFP_KERNEL);
 		if (!dst_list)
 		if (!dst_list)
 			return -ENOMEM;
 			return -ENOMEM;
 
 
-		dst_list->shared_count = src_list->shared_count;
-		dst_list->shared_max = src_list->shared_count;
-		for (i = 0; i < src_list->shared_count; ++i)
-			dst_list->shared[i] =
-				dma_fence_get(src_list->shared[i]);
+		rcu_read_lock();
+		src_list = rcu_dereference(src->fence);
+		if (!src_list || src_list->shared_count > shared_count) {
+			kfree(dst_list);
+			goto retry;
+		}
+
+		dst_list->shared_count = 0;
+		dst_list->shared_max = shared_count;
+		for (i = 0; i < src_list->shared_count; ++i) {
+			struct dma_fence *fence;
+
+			fence = rcu_dereference(src_list->shared[i]);
+			if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
+				     &fence->flags))
+				continue;
+
+			if (!dma_fence_get_rcu(fence)) {
+				kfree(dst_list);
+				src_list = rcu_dereference(src->fence);
+				goto retry;
+			}
+
+			if (dma_fence_is_signaled(fence)) {
+				dma_fence_put(fence);
+				continue;
+			}
+
+			dst_list->shared[dst_list->shared_count++] = fence;
+		}
 	} else {
 	} else {
 		dst_list = NULL;
 		dst_list = NULL;
 	}
 	}
 
 
+	new = dma_fence_get_rcu_safe(&src->fence_excl);
+	rcu_read_unlock();
+
 	kfree(dst->staged);
 	kfree(dst->staged);
 	dst->staged = NULL;
 	dst->staged = NULL;
 
 
 	src_list = reservation_object_get_list(dst);
 	src_list = reservation_object_get_list(dst);
-
 	old = reservation_object_get_excl(dst);
 	old = reservation_object_get_excl(dst);
-	new = reservation_object_get_excl(src);
-
-	dma_fence_get(new);
 
 
 	preempt_disable();
 	preempt_disable();
 	write_seqcount_begin(&dst->seq);
 	write_seqcount_begin(&dst->seq);

+ 8 - 8
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c

@@ -231,7 +231,7 @@ amdgpu_connector_update_scratch_regs(struct drm_connector *connector,
 		if (connector->encoder_ids[i] == 0)
 		if (connector->encoder_ids[i] == 0)
 			break;
 			break;
 
 
-		encoder = drm_encoder_find(connector->dev,
+		encoder = drm_encoder_find(connector->dev, NULL,
 					connector->encoder_ids[i]);
 					connector->encoder_ids[i]);
 		if (!encoder)
 		if (!encoder)
 			continue;
 			continue;
@@ -256,7 +256,7 @@ amdgpu_connector_find_encoder(struct drm_connector *connector,
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 		if (connector->encoder_ids[i] == 0)
 		if (connector->encoder_ids[i] == 0)
 			break;
 			break;
-		encoder = drm_encoder_find(connector->dev,
+		encoder = drm_encoder_find(connector->dev, NULL,
 					connector->encoder_ids[i]);
 					connector->encoder_ids[i]);
 		if (!encoder)
 		if (!encoder)
 			continue;
 			continue;
@@ -372,7 +372,7 @@ amdgpu_connector_best_single_encoder(struct drm_connector *connector)
 
 
 	/* pick the encoder ids */
 	/* pick the encoder ids */
 	if (enc_id)
 	if (enc_id)
-		return drm_encoder_find(connector->dev, enc_id);
+		return drm_encoder_find(connector->dev, NULL, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -1077,7 +1077,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force)
 			if (connector->encoder_ids[i] == 0)
 			if (connector->encoder_ids[i] == 0)
 				break;
 				break;
 
 
-			encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+			encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
 			if (!encoder)
 			if (!encoder)
 				continue;
 				continue;
 
 
@@ -1134,7 +1134,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
 		if (connector->encoder_ids[i] == 0)
 		if (connector->encoder_ids[i] == 0)
 			break;
 			break;
 
 
-		encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+		encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
 		if (!encoder)
 		if (!encoder)
 			continue;
 			continue;
 
 
@@ -1153,7 +1153,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector)
 	/* then check use digitial */
 	/* then check use digitial */
 	/* pick the first one */
 	/* pick the first one */
 	if (enc_id)
 	if (enc_id)
-		return drm_encoder_find(connector->dev, enc_id);
+		return drm_encoder_find(connector->dev, NULL, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -1294,7 +1294,7 @@ u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn
 		if (connector->encoder_ids[i] == 0)
 		if (connector->encoder_ids[i] == 0)
 			break;
 			break;
 
 
-		encoder = drm_encoder_find(connector->dev,
+		encoder = drm_encoder_find(connector->dev, NULL,
 					connector->encoder_ids[i]);
 					connector->encoder_ids[i]);
 		if (!encoder)
 		if (!encoder)
 			continue;
 			continue;
@@ -1323,7 +1323,7 @@ static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector)
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 		if (connector->encoder_ids[i] == 0)
 		if (connector->encoder_ids[i] == 0)
 			break;
 			break;
-		encoder = drm_encoder_find(connector->dev,
+		encoder = drm_encoder_find(connector->dev, NULL,
 					connector->encoder_ids[i]);
 					connector->encoder_ids[i]);
 		if (!encoder)
 		if (!encoder)
 			continue;
 			continue;

+ 2 - 2
drivers/gpu/drm/amd/amdgpu/dce_virtual.c

@@ -288,7 +288,7 @@ dce_virtual_encoder(struct drm_connector *connector)
 		if (connector->encoder_ids[i] == 0)
 		if (connector->encoder_ids[i] == 0)
 			break;
 			break;
 
 
-		encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+		encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]);
 		if (!encoder)
 		if (!encoder)
 			continue;
 			continue;
 
 
@@ -298,7 +298,7 @@ dce_virtual_encoder(struct drm_connector *connector)
 
 
 	/* pick the first one */
 	/* pick the first one */
 	if (enc_id)
 	if (enc_id)
-		return drm_encoder_find(connector->dev, enc_id);
+		return drm_encoder_find(connector->dev, NULL, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 

+ 1 - 1
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c

@@ -2638,7 +2638,7 @@ static struct drm_encoder *best_encoder(struct drm_connector *connector)
 
 
 	/* pick the encoder ids */
 	/* pick the encoder ids */
 	if (enc_id) {
 	if (enc_id) {
-		obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
+		obj = drm_mode_object_find(connector->dev, NULL, enc_id, DRM_MODE_OBJECT_ENCODER);
 		if (!obj) {
 		if (!obj) {
 			DRM_ERROR("Couldn't find a matching encoder for our connector\n");
 			DRM_ERROR("Couldn't find a matching encoder for our connector\n");
 			return NULL;
 			return NULL;

+ 1 - 1
drivers/gpu/drm/ast/ast_mode.c

@@ -713,7 +713,7 @@ static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connect
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
 	/* pick the encoder ids */
 	/* pick the encoder ids */
 	if (enc_id)
 	if (enc_id)
-		return drm_encoder_find(connector->dev, enc_id);
+		return drm_encoder_find(connector->dev, NULL, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 

+ 1 - 1
drivers/gpu/drm/bochs/bochs_kms.c

@@ -213,7 +213,7 @@ bochs_connector_best_encoder(struct drm_connector *connector)
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
 	/* pick the encoder ids */
 	/* pick the encoder ids */
 	if (enc_id)
 	if (enc_id)
-		return drm_encoder_find(connector->dev, enc_id);
+		return drm_encoder_find(connector->dev, NULL, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 

+ 9 - 1
drivers/gpu/drm/bridge/Kconfig

@@ -71,7 +71,7 @@ config DRM_PARADE_PS8622
 
 
 config DRM_SIL_SII8620
 config DRM_SIL_SII8620
 	tristate "Silicon Image SII8620 HDMI/MHL bridge"
 	tristate "Silicon Image SII8620 HDMI/MHL bridge"
-	depends on OF
+	depends on OF && RC_CORE
 	select DRM_KMS_HELPER
 	select DRM_KMS_HELPER
 	help
 	help
 	  Silicon Image SII8620 HDMI/MHL bridge chip driver.
 	  Silicon Image SII8620 HDMI/MHL bridge chip driver.
@@ -84,6 +84,14 @@ config DRM_SII902X
 	---help---
 	---help---
 	  Silicon Image sii902x bridge chip driver.
 	  Silicon Image sii902x bridge chip driver.
 
 
+config DRM_SII9234
+	tristate "Silicon Image SII9234 HDMI/MHL bridge"
+	depends on OF
+	---help---
+	  Say Y here if you want support for the MHL interface.
+	  It is an I2C driver, that detects connection of MHL bridge
+	  and starts encapsulation of HDMI signal.
+
 config DRM_TOSHIBA_TC358767
 config DRM_TOSHIBA_TC358767
 	tristate "Toshiba TC358767 eDP bridge"
 	tristate "Toshiba TC358767 eDP bridge"
 	depends on OF
 	depends on OF

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

@@ -6,6 +6,7 @@ obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
 obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
 obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
 obj-$(CONFIG_DRM_SII902X) += sii902x.o
+obj-$(CONFIG_DRM_SII9234) += sii9234.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
 obj-$(CONFIG_DRM_TOSHIBA_TC358767) += tc358767.o
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
 obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/

+ 8 - 0
drivers/gpu/drm/bridge/adv7511/Kconfig

@@ -21,3 +21,11 @@ config DRM_I2C_ADV7533
 	default y
 	default y
 	help
 	help
 	  Support for the Analog Devices ADV7533 DSI to HDMI encoder.
 	  Support for the Analog Devices ADV7533 DSI to HDMI encoder.
+
+config DRM_I2C_ADV7511_CEC
+	bool "ADV7511/33 HDMI CEC driver"
+	depends on DRM_I2C_ADV7511
+	select CEC_CORE
+	default y
+	help
+	  When selected the HDMI transmitter will support the CEC feature.

+ 1 - 0
drivers/gpu/drm/bridge/adv7511/Makefile

@@ -1,4 +1,5 @@
 adv7511-y := adv7511_drv.o
 adv7511-y := adv7511_drv.o
 adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
 adv7511-$(CONFIG_DRM_I2C_ADV7511_AUDIO) += adv7511_audio.o
+adv7511-$(CONFIG_DRM_I2C_ADV7511_CEC) += adv7511_cec.o
 adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
 adv7511-$(CONFIG_DRM_I2C_ADV7533) += adv7533.o
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o
 obj-$(CONFIG_DRM_I2C_ADV7511) += adv7511.o

+ 36 - 7
drivers/gpu/drm/bridge/adv7511/adv7511.h

@@ -195,6 +195,25 @@
 #define ADV7511_PACKET_GM(x)	    ADV7511_PACKET(5, x)
 #define ADV7511_PACKET_GM(x)	    ADV7511_PACKET(5, x)
 #define ADV7511_PACKET_SPARE(x)	    ADV7511_PACKET(6, x)
 #define ADV7511_PACKET_SPARE(x)	    ADV7511_PACKET(6, x)
 
 
+#define ADV7511_REG_CEC_TX_FRAME_HDR	0x00
+#define ADV7511_REG_CEC_TX_FRAME_DATA0	0x01
+#define ADV7511_REG_CEC_TX_FRAME_LEN	0x10
+#define ADV7511_REG_CEC_TX_ENABLE	0x11
+#define ADV7511_REG_CEC_TX_RETRY	0x12
+#define ADV7511_REG_CEC_TX_LOW_DRV_CNT	0x14
+#define ADV7511_REG_CEC_RX_FRAME_HDR	0x15
+#define ADV7511_REG_CEC_RX_FRAME_DATA0	0x16
+#define ADV7511_REG_CEC_RX_FRAME_LEN	0x25
+#define ADV7511_REG_CEC_RX_ENABLE	0x26
+#define ADV7511_REG_CEC_RX_BUFFERS	0x4a
+#define ADV7511_REG_CEC_LOG_ADDR_MASK	0x4b
+#define ADV7511_REG_CEC_LOG_ADDR_0_1	0x4c
+#define ADV7511_REG_CEC_LOG_ADDR_2	0x4d
+#define ADV7511_REG_CEC_CLK_DIV		0x4e
+#define ADV7511_REG_CEC_SOFT_RESET	0x50
+
+#define ADV7533_REG_CEC_OFFSET		0x70
+
 enum adv7511_input_clock {
 enum adv7511_input_clock {
 	ADV7511_INPUT_CLOCK_1X,
 	ADV7511_INPUT_CLOCK_1X,
 	ADV7511_INPUT_CLOCK_2X,
 	ADV7511_INPUT_CLOCK_2X,
@@ -297,6 +316,8 @@ enum adv7511_type {
 	ADV7533,
 	ADV7533,
 };
 };
 
 
+#define ADV7511_MAX_ADDRS 3
+
 struct adv7511 {
 struct adv7511 {
 	struct i2c_client *i2c_main;
 	struct i2c_client *i2c_main;
 	struct i2c_client *i2c_edid;
 	struct i2c_client *i2c_edid;
@@ -341,15 +362,27 @@ struct adv7511 {
 
 
 	enum adv7511_type type;
 	enum adv7511_type type;
 	struct platform_device *audio_pdev;
 	struct platform_device *audio_pdev;
+
+	struct cec_adapter *cec_adap;
+	u8   cec_addr[ADV7511_MAX_ADDRS];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
+	struct clk *cec_clk;
+	u32 cec_clk_freq;
 };
 };
 
 
+#ifdef CONFIG_DRM_I2C_ADV7511_CEC
+int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
+		     unsigned int offset);
+void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1);
+#endif
+
 #ifdef CONFIG_DRM_I2C_ADV7533
 #ifdef CONFIG_DRM_I2C_ADV7533
 void adv7533_dsi_power_on(struct adv7511 *adv);
 void adv7533_dsi_power_on(struct adv7511 *adv);
 void adv7533_dsi_power_off(struct adv7511 *adv);
 void adv7533_dsi_power_off(struct adv7511 *adv);
 void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode);
 void adv7533_mode_set(struct adv7511 *adv, struct drm_display_mode *mode);
 int adv7533_patch_registers(struct adv7511 *adv);
 int adv7533_patch_registers(struct adv7511 *adv);
-void adv7533_uninit_cec(struct adv7511 *adv);
-int adv7533_init_cec(struct adv7511 *adv);
+int adv7533_patch_cec_registers(struct adv7511 *adv);
 int adv7533_attach_dsi(struct adv7511 *adv);
 int adv7533_attach_dsi(struct adv7511 *adv);
 void adv7533_detach_dsi(struct adv7511 *adv);
 void adv7533_detach_dsi(struct adv7511 *adv);
 int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
 int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv);
@@ -372,11 +405,7 @@ static inline int adv7533_patch_registers(struct adv7511 *adv)
 	return -ENODEV;
 	return -ENODEV;
 }
 }
 
 
-static inline void adv7533_uninit_cec(struct adv7511 *adv)
-{
-}
-
-static inline int adv7533_init_cec(struct adv7511 *adv)
+static inline int adv7533_patch_cec_registers(struct adv7511 *adv)
 {
 {
 	return -ENODEV;
 	return -ENODEV;
 }
 }

+ 337 - 0
drivers/gpu/drm/bridge/adv7511/adv7511_cec.c

@@ -0,0 +1,337 @@
+/*
+ * adv7511_cec.c - Analog Devices ADV7511/33 cec driver
+ *
+ * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ *
+ * This program is free software; you may redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 <linux/device.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <media/cec.h>
+
+#include "adv7511.h"
+
+#define ADV7511_INT1_CEC_MASK \
+	(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
+	 ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1)
+
+static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
+{
+	unsigned int offset = adv7511->type == ADV7533 ?
+					ADV7533_REG_CEC_OFFSET : 0;
+	unsigned int val;
+
+	if (regmap_read(adv7511->regmap_cec,
+			ADV7511_REG_CEC_TX_ENABLE + offset, &val))
+		return;
+
+	if ((val & 0x01) == 0)
+		return;
+
+	if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
+		cec_transmit_attempt_done(adv7511->cec_adap,
+					  CEC_TX_STATUS_ARB_LOST);
+		return;
+	}
+	if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
+		u8 status;
+		u8 err_cnt = 0;
+		u8 nack_cnt = 0;
+		u8 low_drive_cnt = 0;
+		unsigned int cnt;
+
+		/*
+		 * We set this status bit since this hardware performs
+		 * retransmissions.
+		 */
+		status = CEC_TX_STATUS_MAX_RETRIES;
+		if (regmap_read(adv7511->regmap_cec,
+			    ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
+			err_cnt = 1;
+			status |= CEC_TX_STATUS_ERROR;
+		} else {
+			nack_cnt = cnt & 0xf;
+			if (nack_cnt)
+				status |= CEC_TX_STATUS_NACK;
+			low_drive_cnt = cnt >> 4;
+			if (low_drive_cnt)
+				status |= CEC_TX_STATUS_LOW_DRIVE;
+		}
+		cec_transmit_done(adv7511->cec_adap, status,
+				  0, nack_cnt, low_drive_cnt, err_cnt);
+		return;
+	}
+	if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
+		cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
+		return;
+	}
+}
+
+void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
+{
+	unsigned int offset = adv7511->type == ADV7533 ?
+					ADV7533_REG_CEC_OFFSET : 0;
+	const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
+				ADV7511_INT1_CEC_TX_ARBIT_LOST |
+				ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
+	struct cec_msg msg = {};
+	unsigned int len;
+	unsigned int val;
+	u8 i;
+
+	if (irq1 & irq_tx_mask)
+		adv_cec_tx_raw_status(adv7511, irq1);
+
+	if (!(irq1 & ADV7511_INT1_CEC_RX_READY1))
+		return;
+
+	if (regmap_read(adv7511->regmap_cec,
+			ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len))
+		return;
+
+	msg.len = len & 0x1f;
+
+	if (msg.len > 16)
+		msg.len = 16;
+
+	if (!msg.len)
+		return;
+
+	for (i = 0; i < msg.len; i++) {
+		regmap_read(adv7511->regmap_cec,
+			    i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val);
+		msg.msg[i] = val;
+	}
+
+	/* toggle to re-enable rx 1 */
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_RX_BUFFERS + offset, 1);
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
+	cec_received_msg(adv7511->cec_adap, &msg);
+}
+
+static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+	struct adv7511 *adv7511 = cec_get_drvdata(adap);
+	unsigned int offset = adv7511->type == ADV7533 ?
+					ADV7533_REG_CEC_OFFSET : 0;
+
+	if (adv7511->i2c_cec == NULL)
+		return -EIO;
+
+	if (!adv7511->cec_enabled_adap && enable) {
+		/* power up cec section */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_CLK_DIV + offset,
+				   0x03, 0x01);
+		/* legacy mode and clear all rx buffers */
+		regmap_write(adv7511->regmap_cec,
+			     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07);
+		regmap_write(adv7511->regmap_cec,
+			     ADV7511_REG_CEC_RX_BUFFERS + offset, 0);
+		/* initially disable tx */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready 1 */
+		regmap_update_bits(adv7511->regmap,
+				   ADV7511_REG_INT_ENABLE(1), 0x3f,
+				   ADV7511_INT1_CEC_MASK);
+	} else if (adv7511->cec_enabled_adap && !enable) {
+		regmap_update_bits(adv7511->regmap,
+				   ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
+		/* disable address mask 1-3 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+				   0x70, 0x00);
+		/* power down cec section */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_CLK_DIV + offset,
+				   0x03, 0x00);
+		adv7511->cec_valid_addrs = 0;
+	}
+	adv7511->cec_enabled_adap = enable;
+	return 0;
+}
+
+static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
+{
+	struct adv7511 *adv7511 = cec_get_drvdata(adap);
+	unsigned int offset = adv7511->type == ADV7533 ?
+					ADV7533_REG_CEC_OFFSET : 0;
+	unsigned int i, free_idx = ADV7511_MAX_ADDRS;
+
+	if (!adv7511->cec_enabled_adap)
+		return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
+
+	if (addr == CEC_LOG_ADDR_INVALID) {
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+				   0x70, 0);
+		adv7511->cec_valid_addrs = 0;
+		return 0;
+	}
+
+	for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
+		bool is_valid = adv7511->cec_valid_addrs & (1 << i);
+
+		if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
+			free_idx = i;
+		if (is_valid && adv7511->cec_addr[i] == addr)
+			return 0;
+	}
+	if (i == ADV7511_MAX_ADDRS) {
+		i = free_idx;
+		if (i == ADV7511_MAX_ADDRS)
+			return -ENXIO;
+	}
+	adv7511->cec_addr[i] = addr;
+	adv7511->cec_valid_addrs |= 1 << i;
+
+	switch (i) {
+	case 0:
+		/* enable address mask 0 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+				   0x10, 0x10);
+		/* set address for mask 0 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
+				   0x0f, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+				   0x20, 0x20);
+		/* set address for mask 1 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
+				   0xf0, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
+				   0x40, 0x40);
+		/* set address for mask 1 */
+		regmap_update_bits(adv7511->regmap_cec,
+				   ADV7511_REG_CEC_LOG_ADDR_2 + offset,
+				   0x0f, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+				     u32 signal_free_time, struct cec_msg *msg)
+{
+	struct adv7511 *adv7511 = cec_get_drvdata(adap);
+	unsigned int offset = adv7511->type == ADV7533 ?
+					ADV7533_REG_CEC_OFFSET : 0;
+	u8 len = msg->len;
+	unsigned int i;
+
+	/*
+	 * The number of retries is the number of attempts - 1, but retry
+	 * at least once. It's not clear if a value of 0 is allowed, so
+	 * let's do at least one retry.
+	 */
+	regmap_update_bits(adv7511->regmap_cec,
+			   ADV7511_REG_CEC_TX_RETRY + offset,
+			   0x70, max(1, attempts - 1) << 4);
+
+	/* blocking, clear cec tx irq status */
+	regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
+
+	/* write data */
+	for (i = 0; i < len; i++)
+		regmap_write(adv7511->regmap_cec,
+			     i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
+			     msg->msg[i]);
+
+	/* set length (data + header) */
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
+	/* start transmit, enable tx */
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
+	return 0;
+}
+
+static const struct cec_adap_ops adv7511_cec_adap_ops = {
+	.adap_enable = adv7511_cec_adap_enable,
+	.adap_log_addr = adv7511_cec_adap_log_addr,
+	.adap_transmit = adv7511_cec_adap_transmit,
+};
+
+static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
+{
+	adv7511->cec_clk = devm_clk_get(dev, "cec");
+	if (IS_ERR(adv7511->cec_clk)) {
+		int ret = PTR_ERR(adv7511->cec_clk);
+
+		adv7511->cec_clk = NULL;
+		return ret;
+	}
+	clk_prepare_enable(adv7511->cec_clk);
+	adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
+	return 0;
+}
+
+int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511,
+		     unsigned int offset)
+{
+	int ret = adv7511_cec_parse_dt(dev, adv7511);
+
+	if (ret)
+		return ret;
+
+	adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
+		adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
+	if (IS_ERR(adv7511->cec_adap))
+		return PTR_ERR(adv7511->cec_adap);
+
+	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0);
+	/* cec soft reset */
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
+
+	/* legacy mode */
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00);
+
+	regmap_write(adv7511->regmap_cec,
+		     ADV7511_REG_CEC_CLK_DIV + offset,
+		     ((adv7511->cec_clk_freq / 750000) - 1) << 2);
+
+	ret = cec_register_adapter(adv7511->cec_adap, dev);
+	if (ret) {
+		cec_delete_adapter(adv7511->cec_adap);
+		adv7511->cec_adap = NULL;
+	}
+	return ret;
+}

+ 101 - 15
drivers/gpu/drm/bridge/adv7511/adv7511_drv.c

@@ -11,12 +11,15 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/of_device.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
 
 
 #include <drm/drmP.h>
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_edid.h>
 
 
+#include <media/cec.h>
+
 #include "adv7511.h"
 #include "adv7511.h"
 
 
 /* ADI recommended values for proper operation. */
 /* ADI recommended values for proper operation. */
@@ -336,8 +339,10 @@ static void __adv7511_power_on(struct adv7511 *adv7511)
 		 */
 		 */
 		regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
 		regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
 			     ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
 			     ADV7511_INT0_EDID_READY | ADV7511_INT0_HPD);
-		regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
-			     ADV7511_INT1_DDC_ERROR);
+		regmap_update_bits(adv7511->regmap,
+				   ADV7511_REG_INT_ENABLE(1),
+				   ADV7511_INT1_DDC_ERROR,
+				   ADV7511_INT1_DDC_ERROR);
 	}
 	}
 
 
 	/*
 	/*
@@ -373,6 +378,9 @@ static void __adv7511_power_off(struct adv7511 *adv7511)
 	regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
 	regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
 			   ADV7511_POWER_POWER_DOWN,
 			   ADV7511_POWER_POWER_DOWN,
 			   ADV7511_POWER_POWER_DOWN);
 			   ADV7511_POWER_POWER_DOWN);
+	regmap_update_bits(adv7511->regmap,
+			   ADV7511_REG_INT_ENABLE(1),
+			   ADV7511_INT1_DDC_ERROR, 0);
 	regcache_mark_dirty(adv7511->regmap);
 	regcache_mark_dirty(adv7511->regmap);
 }
 }
 
 
@@ -423,6 +431,8 @@ static void adv7511_hpd_work(struct work_struct *work)
 
 
 	if (adv7511->connector.status != status) {
 	if (adv7511->connector.status != status) {
 		adv7511->connector.status = status;
 		adv7511->connector.status = status;
+		if (status == connector_status_disconnected)
+			cec_phys_addr_invalidate(adv7511->cec_adap);
 		drm_kms_helper_hotplug_event(adv7511->connector.dev);
 		drm_kms_helper_hotplug_event(adv7511->connector.dev);
 	}
 	}
 }
 }
@@ -453,6 +463,10 @@ static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
 			wake_up_all(&adv7511->wq);
 			wake_up_all(&adv7511->wq);
 	}
 	}
 
 
+#ifdef CONFIG_DRM_I2C_ADV7511_CEC
+	adv7511_cec_irq_process(adv7511, irq1);
+#endif
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -595,6 +609,8 @@ static int adv7511_get_modes(struct adv7511 *adv7511,
 
 
 	kfree(edid);
 	kfree(edid);
 
 
+	cec_s_phys_addr_from_edid(adv7511->cec_adap, edid);
+
 	return count;
 	return count;
 }
 }
 
 
@@ -919,6 +935,65 @@ static void adv7511_uninit_regulators(struct adv7511 *adv)
 	regulator_bulk_disable(adv->num_supplies, adv->supplies);
 	regulator_bulk_disable(adv->num_supplies, adv->supplies);
 }
 }
 
 
+static bool adv7511_cec_register_volatile(struct device *dev, unsigned int reg)
+{
+	struct i2c_client *i2c = to_i2c_client(dev);
+	struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
+
+	if (adv7511->type == ADV7533)
+		reg -= ADV7533_REG_CEC_OFFSET;
+
+	switch (reg) {
+	case ADV7511_REG_CEC_RX_FRAME_HDR:
+	case ADV7511_REG_CEC_RX_FRAME_DATA0...
+		ADV7511_REG_CEC_RX_FRAME_DATA0 + 14:
+	case ADV7511_REG_CEC_RX_FRAME_LEN:
+	case ADV7511_REG_CEC_RX_BUFFERS:
+	case ADV7511_REG_CEC_TX_LOW_DRV_CNT:
+		return true;
+	}
+
+	return false;
+}
+
+static const struct regmap_config adv7511_cec_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = 0xff,
+	.cache_type = REGCACHE_RBTREE,
+	.volatile_reg = adv7511_cec_register_volatile,
+};
+
+static int adv7511_init_cec_regmap(struct adv7511 *adv)
+{
+	int ret;
+
+	adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
+				     adv->i2c_main->addr - 1);
+	if (!adv->i2c_cec)
+		return -ENOMEM;
+	i2c_set_clientdata(adv->i2c_cec, adv);
+
+	adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
+					&adv7511_cec_regmap_config);
+	if (IS_ERR(adv->regmap_cec)) {
+		ret = PTR_ERR(adv->regmap_cec);
+		goto err;
+	}
+
+	if (adv->type == ADV7533) {
+		ret = adv7533_patch_cec_registers(adv);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+err:
+	i2c_unregister_device(adv->i2c_cec);
+	return ret;
+}
+
 static int adv7511_parse_dt(struct device_node *np,
 static int adv7511_parse_dt(struct device_node *np,
 			    struct adv7511_link_config *config)
 			    struct adv7511_link_config *config)
 {
 {
@@ -1009,6 +1084,7 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 	struct device *dev = &i2c->dev;
 	struct device *dev = &i2c->dev;
 	unsigned int main_i2c_addr = i2c->addr << 1;
 	unsigned int main_i2c_addr = i2c->addr << 1;
 	unsigned int edid_i2c_addr = main_i2c_addr + 4;
 	unsigned int edid_i2c_addr = main_i2c_addr + 4;
+	unsigned int offset;
 	unsigned int val;
 	unsigned int val;
 	int ret;
 	int ret;
 
 
@@ -1092,11 +1168,9 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 		goto uninit_regulators;
 		goto uninit_regulators;
 	}
 	}
 
 
-	if (adv7511->type == ADV7533) {
-		ret = adv7533_init_cec(adv7511);
-		if (ret)
-			goto err_i2c_unregister_edid;
-	}
+	ret = adv7511_init_cec_regmap(adv7511);
+	if (ret)
+		goto err_i2c_unregister_edid;
 
 
 	INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
 	INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
 
 
@@ -1111,10 +1185,6 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 			goto err_unregister_cec;
 			goto err_unregister_cec;
 	}
 	}
 
 
-	/* CEC is unused for now */
-	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
-		     ADV7511_CEC_CTRL_POWER_DOWN);
-
 	adv7511_power_off(adv7511);
 	adv7511_power_off(adv7511);
 
 
 	i2c_set_clientdata(i2c, adv7511);
 	i2c_set_clientdata(i2c, adv7511);
@@ -1129,10 +1199,23 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 
 
 	adv7511_audio_init(dev, adv7511);
 	adv7511_audio_init(dev, adv7511);
 
 
+	offset = adv7511->type == ADV7533 ? ADV7533_REG_CEC_OFFSET : 0;
+
+#ifdef CONFIG_DRM_I2C_ADV7511_CEC
+	ret = adv7511_cec_init(dev, adv7511, offset);
+	if (ret)
+		goto err_unregister_cec;
+#else
+	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset,
+		     ADV7511_CEC_CTRL_POWER_DOWN);
+#endif
+
 	return 0;
 	return 0;
 
 
 err_unregister_cec:
 err_unregister_cec:
-	adv7533_uninit_cec(adv7511);
+	i2c_unregister_device(adv7511->i2c_cec);
+	if (adv7511->cec_clk)
+		clk_disable_unprepare(adv7511->cec_clk);
 err_i2c_unregister_edid:
 err_i2c_unregister_edid:
 	i2c_unregister_device(adv7511->i2c_edid);
 	i2c_unregister_device(adv7511->i2c_edid);
 uninit_regulators:
 uninit_regulators:
@@ -1145,10 +1228,11 @@ static int adv7511_remove(struct i2c_client *i2c)
 {
 {
 	struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
 	struct adv7511 *adv7511 = i2c_get_clientdata(i2c);
 
 
-	if (adv7511->type == ADV7533) {
+	if (adv7511->type == ADV7533)
 		adv7533_detach_dsi(adv7511);
 		adv7533_detach_dsi(adv7511);
-		adv7533_uninit_cec(adv7511);
-	}
+	i2c_unregister_device(adv7511->i2c_cec);
+	if (adv7511->cec_clk)
+		clk_disable_unprepare(adv7511->cec_clk);
 
 
 	adv7511_uninit_regulators(adv7511);
 	adv7511_uninit_regulators(adv7511);
 
 
@@ -1156,6 +1240,8 @@ static int adv7511_remove(struct i2c_client *i2c)
 
 
 	adv7511_audio_exit(adv7511);
 	adv7511_audio_exit(adv7511);
 
 
+	cec_unregister_adapter(adv7511->cec_adap);
+
 	i2c_unregister_device(adv7511->i2c_edid);
 	i2c_unregister_device(adv7511->i2c_edid);
 
 
 	return 0;
 	return 0;

+ 2 - 36
drivers/gpu/drm/bridge/adv7511/adv7533.c

@@ -32,14 +32,6 @@ static const struct reg_sequence adv7533_cec_fixed_registers[] = {
 	{ 0x05, 0xc8 },
 	{ 0x05, 0xc8 },
 };
 };
 
 
-static const struct regmap_config adv7533_cec_regmap_config = {
-	.reg_bits = 8,
-	.val_bits = 8,
-
-	.max_register = 0xff,
-	.cache_type = REGCACHE_RBTREE,
-};
-
 static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
 static void adv7511_dsi_config_timing_gen(struct adv7511 *adv)
 {
 {
 	struct mipi_dsi_device *dsi = adv->dsi;
 	struct mipi_dsi_device *dsi = adv->dsi;
@@ -145,37 +137,11 @@ int adv7533_patch_registers(struct adv7511 *adv)
 				     ARRAY_SIZE(adv7533_fixed_registers));
 				     ARRAY_SIZE(adv7533_fixed_registers));
 }
 }
 
 
-void adv7533_uninit_cec(struct adv7511 *adv)
-{
-	i2c_unregister_device(adv->i2c_cec);
-}
-
-int adv7533_init_cec(struct adv7511 *adv)
+int adv7533_patch_cec_registers(struct adv7511 *adv)
 {
 {
-	int ret;
-
-	adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
-				     adv->i2c_main->addr - 1);
-	if (!adv->i2c_cec)
-		return -ENOMEM;
-
-	adv->regmap_cec = devm_regmap_init_i2c(adv->i2c_cec,
-					&adv7533_cec_regmap_config);
-	if (IS_ERR(adv->regmap_cec)) {
-		ret = PTR_ERR(adv->regmap_cec);
-		goto err;
-	}
-
-	ret = regmap_register_patch(adv->regmap_cec,
+	return regmap_register_patch(adv->regmap_cec,
 				    adv7533_cec_fixed_registers,
 				    adv7533_cec_fixed_registers,
 				    ARRAY_SIZE(adv7533_cec_fixed_registers));
 				    ARRAY_SIZE(adv7533_cec_fixed_registers));
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	adv7533_uninit_cec(adv);
-	return ret;
 }
 }
 
 
 int adv7533_attach_dsi(struct adv7511 *adv)
 int adv7533_attach_dsi(struct adv7511 *adv)

+ 9 - 1
drivers/gpu/drm/bridge/panel.c

@@ -188,7 +188,15 @@ EXPORT_SYMBOL(drm_panel_bridge_add);
  */
  */
 void drm_panel_bridge_remove(struct drm_bridge *bridge)
 void drm_panel_bridge_remove(struct drm_bridge *bridge)
 {
 {
-	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+	struct panel_bridge *panel_bridge;
+
+	if (!bridge)
+		return;
+
+	if (bridge->funcs != &panel_bridge_bridge_funcs)
+		return;
+
+	panel_bridge = drm_bridge_to_panel_bridge(bridge);
 
 
 	drm_bridge_remove(bridge);
 	drm_bridge_remove(bridge);
 	devm_kfree(panel_bridge->panel->dev, bridge);
 	devm_kfree(panel_bridge->panel->dev, bridge);

+ 994 - 0
drivers/gpu/drm/bridge/sii9234.c

@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2017 Samsung Electronics
+ *
+ * Authors:
+ *    Tomasz Stanislawski <t.stanislaws@samsung.com>
+ *    Maciej Purski <m.purski@samsung.com>
+ *
+ * Based on sii9234 driver created by:
+ *    Adam Hampson <ahampson@sta.samsung.com>
+ *    Erik Gilling <konkers@android.com>
+ *    Shankar Bandal <shankar.b@samsung.com>
+ *    Dharam Kumar <dharam.kr@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program
+ *
+ */
+#include <drm/bridge/mhl.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#define CBUS_DEVCAP_OFFSET		0x80
+
+#define SII9234_MHL_VERSION		0x11
+#define SII9234_SCRATCHPAD_SIZE		0x10
+#define SII9234_INT_STAT_SIZE		0x33
+
+#define BIT_TMDS_CCTRL_TMDS_OE		BIT(4)
+#define MHL_HPD_OUT_OVR_EN		BIT(4)
+#define MHL_HPD_OUT_OVR_VAL		BIT(5)
+#define MHL_INIT_TIMEOUT		0x0C
+
+/* MHL Tx registers and bits */
+#define MHL_TX_SRST			0x05
+#define MHL_TX_SYSSTAT_REG		0x09
+#define MHL_TX_INTR1_REG		0x71
+#define MHL_TX_INTR4_REG		0x74
+#define MHL_TX_INTR1_ENABLE_REG		0x75
+#define MHL_TX_INTR4_ENABLE_REG		0x78
+#define MHL_TX_INT_CTRL_REG		0x79
+#define MHL_TX_TMDS_CCTRL		0x80
+#define MHL_TX_DISC_CTRL1_REG		0x90
+#define MHL_TX_DISC_CTRL2_REG		0x91
+#define MHL_TX_DISC_CTRL3_REG		0x92
+#define MHL_TX_DISC_CTRL4_REG		0x93
+#define MHL_TX_DISC_CTRL5_REG		0x94
+#define MHL_TX_DISC_CTRL6_REG		0x95
+#define MHL_TX_DISC_CTRL7_REG		0x96
+#define MHL_TX_DISC_CTRL8_REG		0x97
+#define MHL_TX_STAT2_REG		0x99
+#define MHL_TX_MHLTX_CTL1_REG		0xA0
+#define MHL_TX_MHLTX_CTL2_REG		0xA1
+#define MHL_TX_MHLTX_CTL4_REG		0xA3
+#define MHL_TX_MHLTX_CTL6_REG		0xA5
+#define MHL_TX_MHLTX_CTL7_REG		0xA6
+
+#define RSEN_STATUS			BIT(2)
+#define HPD_CHANGE_INT			BIT(6)
+#define RSEN_CHANGE_INT			BIT(5)
+#define RGND_READY_INT			BIT(6)
+#define VBUS_LOW_INT			BIT(5)
+#define CBUS_LKOUT_INT			BIT(4)
+#define MHL_DISC_FAIL_INT		BIT(3)
+#define MHL_EST_INT			BIT(2)
+#define HPD_CHANGE_INT_MASK		BIT(6)
+#define RSEN_CHANGE_INT_MASK		BIT(5)
+
+#define RGND_READY_MASK			BIT(6)
+#define CBUS_LKOUT_MASK			BIT(4)
+#define MHL_DISC_FAIL_MASK		BIT(3)
+#define MHL_EST_MASK			BIT(2)
+
+#define SKIP_GND			BIT(6)
+
+#define ATT_THRESH_SHIFT		0x04
+#define ATT_THRESH_MASK			(0x03 << ATT_THRESH_SHIFT)
+#define USB_D_OEN			BIT(3)
+#define DEGLITCH_TIME_MASK		0x07
+#define DEGLITCH_TIME_2MS		0
+#define DEGLITCH_TIME_4MS		1
+#define DEGLITCH_TIME_8MS		2
+#define DEGLITCH_TIME_16MS		3
+#define DEGLITCH_TIME_40MS		4
+#define DEGLITCH_TIME_50MS		5
+#define DEGLITCH_TIME_60MS		6
+#define DEGLITCH_TIME_128MS		7
+
+#define USB_D_OVR			BIT(7)
+#define USB_ID_OVR			BIT(6)
+#define DVRFLT_SEL			BIT(5)
+#define BLOCK_RGND_INT			BIT(4)
+#define SKIP_DEG			BIT(3)
+#define CI2CA_POL			BIT(2)
+#define CI2CA_WKUP			BIT(1)
+#define SINGLE_ATT			BIT(0)
+
+#define USB_D_ODN			BIT(5)
+#define VBUS_CHECK			BIT(2)
+#define RGND_INTP_MASK			0x03
+#define RGND_INTP_OPEN			0
+#define RGND_INTP_2K			1
+#define RGND_INTP_1K			2
+#define RGND_INTP_SHORT			3
+
+/* HDMI registers */
+#define HDMI_RX_TMDS0_CCTRL1_REG	0x10
+#define HDMI_RX_TMDS_CLK_EN_REG		0x11
+#define HDMI_RX_TMDS_CH_EN_REG		0x12
+#define HDMI_RX_PLL_CALREFSEL_REG	0x17
+#define HDMI_RX_PLL_VCOCAL_REG		0x1A
+#define HDMI_RX_EQ_DATA0_REG		0x22
+#define HDMI_RX_EQ_DATA1_REG		0x23
+#define HDMI_RX_EQ_DATA2_REG		0x24
+#define HDMI_RX_EQ_DATA3_REG		0x25
+#define HDMI_RX_EQ_DATA4_REG		0x26
+#define HDMI_RX_TMDS_ZONE_CTRL_REG	0x4C
+#define HDMI_RX_TMDS_MODE_CTRL_REG	0x4D
+
+/* CBUS registers */
+#define CBUS_INT_STATUS_1_REG		0x08
+#define CBUS_INTR1_ENABLE_REG		0x09
+#define CBUS_MSC_REQ_ABORT_REASON_REG	0x0D
+#define CBUS_INT_STATUS_2_REG		0x1E
+#define CBUS_INTR2_ENABLE_REG		0x1F
+#define CBUS_LINK_CONTROL_2_REG		0x31
+#define CBUS_MHL_STATUS_REG_0		0xB0
+#define CBUS_MHL_STATUS_REG_1		0xB1
+
+#define BIT_CBUS_RESET			BIT(3)
+#define SET_HPD_DOWNSTREAM		BIT(6)
+
+/* TPI registers */
+#define TPI_DPD_REG			0x3D
+
+/* Timeouts in msec */
+#define T_SRC_VBUS_CBUS_TO_STABLE	200
+#define T_SRC_CBUS_FLOAT		100
+#define T_SRC_CBUS_DEGLITCH		2
+#define T_SRC_RXSENSE_DEGLITCH		110
+
+#define MHL1_MAX_CLK			75000 /* in kHz */
+
+#define I2C_TPI_ADDR			0x3D
+#define I2C_HDMI_ADDR			0x49
+#define I2C_CBUS_ADDR			0x64
+
+enum sii9234_state {
+	ST_OFF,
+	ST_D3,
+	ST_RGND_INIT,
+	ST_RGND_1K,
+	ST_RSEN_HIGH,
+	ST_MHL_ESTABLISHED,
+	ST_FAILURE_DISCOVERY,
+	ST_FAILURE,
+};
+
+struct sii9234 {
+	struct i2c_client *client[4];
+	struct drm_bridge bridge;
+	struct device *dev;
+	struct gpio_desc *gpio_reset;
+	int i2c_error;
+	struct regulator_bulk_data supplies[4];
+
+	struct mutex lock; /* Protects fields below and device registers */
+	enum sii9234_state state;
+};
+
+enum sii9234_client_id {
+	I2C_MHL,
+	I2C_TPI,
+	I2C_HDMI,
+	I2C_CBUS,
+};
+
+static const char * const sii9234_client_name[] = {
+	[I2C_MHL] = "MHL",
+	[I2C_TPI] = "TPI",
+	[I2C_HDMI] = "HDMI",
+	[I2C_CBUS] = "CBUS",
+};
+
+static int sii9234_writeb(struct sii9234 *ctx, int id, int offset,
+			  int value)
+{
+	int ret;
+	struct i2c_client *client = ctx->client[id];
+
+	if (ctx->i2c_error)
+		return ctx->i2c_error;
+
+	ret = i2c_smbus_write_byte_data(client, offset, value);
+	if (ret < 0)
+		dev_err(ctx->dev, "writeb: %4s[0x%02x] <- 0x%02x\n",
+			sii9234_client_name[id], offset, value);
+	ctx->i2c_error = ret;
+
+	return ret;
+}
+
+static int sii9234_writebm(struct sii9234 *ctx, int id, int offset,
+			   int value, int mask)
+{
+	int ret;
+	struct i2c_client *client = ctx->client[id];
+
+	if (ctx->i2c_error)
+		return ctx->i2c_error;
+
+	ret = i2c_smbus_write_byte(client, offset);
+	if (ret < 0) {
+		dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
+			sii9234_client_name[id], offset, value);
+		ctx->i2c_error = ret;
+		return ret;
+	}
+
+	ret = i2c_smbus_read_byte(client);
+	if (ret < 0) {
+		dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
+			sii9234_client_name[id], offset, value);
+		ctx->i2c_error = ret;
+		return ret;
+	}
+
+	value = (value & mask) | (ret & ~mask);
+
+	ret = i2c_smbus_write_byte_data(client, offset, value);
+	if (ret < 0) {
+		dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n",
+			sii9234_client_name[id], offset, value);
+		ctx->i2c_error = ret;
+	}
+
+	return ret;
+}
+
+static int sii9234_readb(struct sii9234 *ctx, int id, int offset)
+{
+	int ret;
+	struct i2c_client *client = ctx->client[id];
+
+	if (ctx->i2c_error)
+		return ctx->i2c_error;
+
+	ret = i2c_smbus_write_byte(client, offset);
+	if (ret < 0) {
+		dev_err(ctx->dev, "readb: %4s[0x%02x]\n",
+			sii9234_client_name[id], offset);
+		ctx->i2c_error = ret;
+		return ret;
+	}
+
+	ret = i2c_smbus_read_byte(client);
+	if (ret < 0) {
+		dev_err(ctx->dev, "readb: %4s[0x%02x]\n",
+			sii9234_client_name[id], offset);
+		ctx->i2c_error = ret;
+	}
+
+	return ret;
+}
+
+static int sii9234_clear_error(struct sii9234 *ctx)
+{
+	int ret = ctx->i2c_error;
+
+	ctx->i2c_error = 0;
+
+	return ret;
+}
+
+#define mhl_tx_writeb(sii9234, offset, value) \
+	sii9234_writeb(sii9234, I2C_MHL, offset, value)
+#define mhl_tx_writebm(sii9234, offset, value, mask) \
+	sii9234_writebm(sii9234, I2C_MHL, offset, value, mask)
+#define mhl_tx_readb(sii9234, offset) \
+	sii9234_readb(sii9234, I2C_MHL, offset)
+#define cbus_writeb(sii9234, offset, value) \
+	sii9234_writeb(sii9234, I2C_CBUS, offset, value)
+#define cbus_writebm(sii9234, offset, value, mask) \
+	sii9234_writebm(sii9234, I2C_CBUS, offset, value, mask)
+#define cbus_readb(sii9234, offset) \
+	sii9234_readb(sii9234, I2C_CBUS, offset)
+#define hdmi_writeb(sii9234, offset, value) \
+	sii9234_writeb(sii9234, I2C_HDMI, offset, value)
+#define hdmi_writebm(sii9234, offset, value, mask) \
+	sii9234_writebm(sii9234, I2C_HDMI, offset, value, mask)
+#define hdmi_readb(sii9234, offset) \
+	sii9234_readb(sii9234, I2C_HDMI, offset)
+#define tpi_writeb(sii9234, offset, value) \
+	sii9234_writeb(sii9234, I2C_TPI, offset, value)
+#define tpi_writebm(sii9234, offset, value, mask) \
+	sii9234_writebm(sii9234, I2C_TPI, offset, value, mask)
+#define tpi_readb(sii9234, offset) \
+	sii9234_readb(sii9234, I2C_TPI, offset)
+
+static u8 sii9234_tmds_control(struct sii9234 *ctx, bool enable)
+{
+	mhl_tx_writebm(ctx, MHL_TX_TMDS_CCTRL, enable ? ~0 : 0,
+		       BIT_TMDS_CCTRL_TMDS_OE);
+	mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, enable ? ~0 : 0,
+		       MHL_HPD_OUT_OVR_EN | MHL_HPD_OUT_OVR_VAL);
+	return sii9234_clear_error(ctx);
+}
+
+static int sii9234_cbus_reset(struct sii9234 *ctx)
+{
+	int i;
+
+	mhl_tx_writebm(ctx, MHL_TX_SRST, ~0, BIT_CBUS_RESET);
+	msleep(T_SRC_CBUS_DEGLITCH);
+	mhl_tx_writebm(ctx, MHL_TX_SRST, 0, BIT_CBUS_RESET);
+
+	for (i = 0; i < 4; i++) {
+		/*
+		 * Enable WRITE_STAT interrupt for writes to all
+		 * 4 MSC Status registers.
+		 */
+		cbus_writeb(ctx, 0xE0 + i, 0xF2);
+		/*
+		 * Enable SET_INT interrupt for writes to all
+		 * 4 MSC Interrupt registers.
+		 */
+		cbus_writeb(ctx, 0xF0 + i, 0xF2);
+	}
+
+	return sii9234_clear_error(ctx);
+}
+
+/* Require to chek mhl imformation of samsung in cbus_init_register */
+static int sii9234_cbus_init(struct sii9234 *ctx)
+{
+	cbus_writeb(ctx, 0x07, 0xF2);
+	cbus_writeb(ctx, 0x40, 0x03);
+	cbus_writeb(ctx, 0x42, 0x06);
+	cbus_writeb(ctx, 0x36, 0x0C);
+	cbus_writeb(ctx, 0x3D, 0xFD);
+	cbus_writeb(ctx, 0x1C, 0x01);
+	cbus_writeb(ctx, 0x1D, 0x0F);
+	cbus_writeb(ctx, 0x44, 0x02);
+	/* Setup our devcap */
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEV_STATE, 0x00);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_MHL_VERSION,
+		    SII9234_MHL_VERSION);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_CAT,
+		    MHL_DCAP_CAT_SOURCE);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_H, 0x01);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_L, 0x41);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VID_LINK_MODE,
+		    MHL_DCAP_VID_LINK_RGB444 | MHL_DCAP_VID_LINK_YCBCR444);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VIDEO_TYPE,
+		    MHL_DCAP_VT_GRAPHICS);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_LOG_DEV_MAP,
+		    MHL_DCAP_LD_GUI);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_BANDWIDTH, 0x0F);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_FEATURE_FLAG,
+		    MHL_DCAP_FEATURE_RCP_SUPPORT | MHL_DCAP_FEATURE_RAP_SUPPORT
+			| MHL_DCAP_FEATURE_SP_SUPPORT);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_H, 0x0);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_L, 0x0);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_SCRATCHPAD_SIZE,
+		    SII9234_SCRATCHPAD_SIZE);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_INT_STAT_SIZE,
+		    SII9234_INT_STAT_SIZE);
+	cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_RESERVED, 0);
+	cbus_writebm(ctx, 0x31, 0x0C, 0x0C);
+	cbus_writeb(ctx, 0x30, 0x01);
+	cbus_writebm(ctx, 0x3C, 0x30, 0x38);
+	cbus_writebm(ctx, 0x22, 0x0D, 0x0F);
+	cbus_writebm(ctx, 0x2E, 0x15, 0x15);
+	cbus_writeb(ctx, CBUS_INTR1_ENABLE_REG, 0);
+	cbus_writeb(ctx, CBUS_INTR2_ENABLE_REG, 0);
+
+	return sii9234_clear_error(ctx);
+}
+
+static void force_usb_id_switch_open(struct sii9234 *ctx)
+{
+	/* Disable CBUS discovery */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0, 0x01);
+	/* Force USB ID switch to open */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR);
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86);
+	/* Force upstream HPD to 0 when not in MHL mode. */
+	mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x30);
+}
+
+static void release_usb_id_switch_open(struct sii9234 *ctx)
+{
+	msleep(T_SRC_CBUS_FLOAT);
+	/* Clear USB ID switch to open */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR);
+	/* Enable CBUS discovery */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 0x01);
+}
+
+static int sii9234_power_init(struct sii9234 *ctx)
+{
+	/* Force the SiI9234 into the D0 state. */
+	tpi_writeb(ctx, TPI_DPD_REG, 0x3F);
+	/* Enable TxPLL Clock */
+	hdmi_writeb(ctx, HDMI_RX_TMDS_CLK_EN_REG, 0x01);
+	/* Enable Tx Clock Path & Equalizer */
+	hdmi_writeb(ctx, HDMI_RX_TMDS_CH_EN_REG, 0x15);
+	/* Power Up TMDS */
+	mhl_tx_writeb(ctx, 0x08, 0x35);
+	return sii9234_clear_error(ctx);
+}
+
+static int sii9234_hdmi_init(struct sii9234 *ctx)
+{
+	hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
+	hdmi_writeb(ctx, HDMI_RX_PLL_CALREFSEL_REG, 0x03);
+	hdmi_writeb(ctx, HDMI_RX_PLL_VCOCAL_REG, 0x20);
+	hdmi_writeb(ctx, HDMI_RX_EQ_DATA0_REG, 0x8A);
+	hdmi_writeb(ctx, HDMI_RX_EQ_DATA1_REG, 0x6A);
+	hdmi_writeb(ctx, HDMI_RX_EQ_DATA2_REG, 0xAA);
+	hdmi_writeb(ctx, HDMI_RX_EQ_DATA3_REG, 0xCA);
+	hdmi_writeb(ctx, HDMI_RX_EQ_DATA4_REG, 0xEA);
+	hdmi_writeb(ctx, HDMI_RX_TMDS_ZONE_CTRL_REG, 0xA0);
+	hdmi_writeb(ctx, HDMI_RX_TMDS_MODE_CTRL_REG, 0x00);
+	mhl_tx_writeb(ctx, MHL_TX_TMDS_CCTRL, 0x34);
+	hdmi_writeb(ctx, 0x45, 0x44);
+	hdmi_writeb(ctx, 0x31, 0x0A);
+	hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
+
+	return sii9234_clear_error(ctx);
+}
+
+static int sii9234_mhl_tx_ctl_int(struct sii9234 *ctx)
+{
+	mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0xD0);
+	mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL2_REG, 0xFC);
+	mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL4_REG, 0xEB);
+	mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL7_REG, 0x0C);
+
+	return sii9234_clear_error(ctx);
+}
+
+static int sii9234_reset(struct sii9234 *ctx)
+{
+	int ret;
+
+	sii9234_clear_error(ctx);
+
+	ret = sii9234_power_init(ctx);
+	if (ret < 0)
+		return ret;
+	ret = sii9234_cbus_reset(ctx);
+	if (ret < 0)
+		return ret;
+	ret = sii9234_hdmi_init(ctx);
+	if (ret < 0)
+		return ret;
+	ret = sii9234_mhl_tx_ctl_int(ctx);
+	if (ret < 0)
+		return ret;
+
+	/* Enable HDCP Compliance safety */
+	mhl_tx_writeb(ctx, 0x2B, 0x01);
+	/* CBUS discovery cycle time for each drive and float = 150us */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0x04, 0x06);
+	/* Clear bit 6 (reg_skip_rgnd) */
+	mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL2_REG, (1 << 7) /* Reserved */
+		      | 2 << ATT_THRESH_SHIFT | DEGLITCH_TIME_50MS);
+	/*
+	 * Changed from 66 to 65 for 94[1:0] = 01 = 5k reg_cbusmhl_pup_sel
+	 * 1.8V CBUS VTH & GND threshold
+	 * to meet CTS 3.3.7.2 spec
+	 */
+	mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77);
+	cbus_writebm(ctx, CBUS_LINK_CONTROL_2_REG, ~0, MHL_INIT_TIMEOUT);
+	mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL6_REG, 0xA0);
+	/* RGND & single discovery attempt (RGND blocking) */
+	mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL6_REG, BLOCK_RGND_INT |
+		      DVRFLT_SEL | SINGLE_ATT);
+	/* Use VBUS path of discovery state machine */
+	mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL8_REG, 0);
+	/* 0x92[3] sets the CBUS / ID switch */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR);
+	/*
+	 * To allow RGND engine to operate correctly.
+	 * When moving the chip from D2 to D0 (power up, init regs)
+	 * the values should be
+	 * 94[1:0] = 01  reg_cbusmhl_pup_sel[1:0] should be set for 5k
+	 * 93[7:6] = 10  reg_cbusdisc_pup_sel[1:0] should be
+	 * set for 10k (default)
+	 * 93[5:4] = 00  reg_cbusidle_pup_sel[1:0] = open (default)
+	 */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86);
+	/*
+	 * Change from CC to 8C to match 5K
+	 * to meet CTS 3.3.72 spec
+	 */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C);
+	/* Configure the interrupt as active high */
+	mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x06);
+
+	msleep(25);
+
+	/* Release usb_id switch */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0,  USB_ID_OVR);
+	mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL1_REG, 0x27);
+
+	ret = sii9234_clear_error(ctx);
+	if (ret < 0)
+		return ret;
+	ret = sii9234_cbus_init(ctx);
+	if (ret < 0)
+		return ret;
+
+	/* Enable Auto soft reset on SCDT = 0 */
+	mhl_tx_writeb(ctx, 0x05, 0x04);
+	/* HDMI Transcode mode enable */
+	mhl_tx_writeb(ctx, 0x0D, 0x1C);
+	mhl_tx_writeb(ctx, MHL_TX_INTR4_ENABLE_REG,
+		      RGND_READY_MASK | CBUS_LKOUT_MASK
+			| MHL_DISC_FAIL_MASK | MHL_EST_MASK);
+	mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG, 0x60);
+
+	/* This point is very important before measure RGND impedance */
+	force_usb_id_switch_open(ctx);
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, 0, 0xF0);
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL5_REG, 0, 0x03);
+	release_usb_id_switch_open(ctx);
+
+	/* Force upstream HPD to 0 when not in MHL mode */
+	mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 1 << 5);
+	mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, ~0, 1 << 4);
+
+	return sii9234_clear_error(ctx);
+}
+
+static int sii9234_goto_d3(struct sii9234 *ctx)
+{
+	int ret;
+
+	dev_dbg(ctx->dev, "sii9234: detection started d3\n");
+
+	ret = sii9234_reset(ctx);
+	if (ret < 0)
+		goto exit;
+
+	hdmi_writeb(ctx, 0x01, 0x03);
+	tpi_writebm(ctx, TPI_DPD_REG, 0, 1);
+	/* I2C above is expected to fail because power goes down */
+	sii9234_clear_error(ctx);
+
+	ctx->state = ST_D3;
+
+	return 0;
+ exit:
+	dev_err(ctx->dev, "%s failed\n", __func__);
+	return -1;
+}
+
+static int sii9234_hw_on(struct sii9234 *ctx)
+{
+	return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static void sii9234_hw_off(struct sii9234 *ctx)
+{
+	gpiod_set_value(ctx->gpio_reset, 1);
+	msleep(20);
+	regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+}
+
+static void sii9234_hw_reset(struct sii9234 *ctx)
+{
+	gpiod_set_value(ctx->gpio_reset, 1);
+	msleep(20);
+	gpiod_set_value(ctx->gpio_reset, 0);
+}
+
+static void sii9234_cable_in(struct sii9234 *ctx)
+{
+	int ret;
+
+	mutex_lock(&ctx->lock);
+	if (ctx->state != ST_OFF)
+		goto unlock;
+	ret = sii9234_hw_on(ctx);
+	if (ret < 0)
+		goto unlock;
+
+	sii9234_hw_reset(ctx);
+	sii9234_goto_d3(ctx);
+	/* To avoid irq storm, when hw is in meta state */
+	enable_irq(to_i2c_client(ctx->dev)->irq);
+
+unlock:
+	mutex_unlock(&ctx->lock);
+}
+
+static void sii9234_cable_out(struct sii9234 *ctx)
+{
+	mutex_lock(&ctx->lock);
+
+	if (ctx->state == ST_OFF)
+		goto unlock;
+
+	disable_irq(to_i2c_client(ctx->dev)->irq);
+	tpi_writeb(ctx, TPI_DPD_REG, 0);
+	/* Turn on&off hpd festure for only QCT HDMI */
+	sii9234_hw_off(ctx);
+
+	ctx->state = ST_OFF;
+
+unlock:
+	mutex_unlock(&ctx->lock);
+}
+
+static enum sii9234_state sii9234_rgnd_ready_irq(struct sii9234 *ctx)
+{
+	int value;
+
+	if (ctx->state == ST_D3) {
+		int ret;
+
+		dev_dbg(ctx->dev, "RGND_READY_INT\n");
+		sii9234_hw_reset(ctx);
+
+		ret = sii9234_reset(ctx);
+		if (ret < 0) {
+			dev_err(ctx->dev, "sii9234_reset() failed\n");
+			return ST_FAILURE;
+		}
+
+		return ST_RGND_INIT;
+	}
+
+	/* Got interrupt in inappropriate state */
+	if (ctx->state != ST_RGND_INIT)
+		return ST_FAILURE;
+
+	value = mhl_tx_readb(ctx, MHL_TX_STAT2_REG);
+	if (sii9234_clear_error(ctx))
+		return ST_FAILURE;
+
+	if ((value & RGND_INTP_MASK) != RGND_INTP_1K) {
+		dev_warn(ctx->dev, "RGND is not 1k\n");
+		return ST_RGND_INIT;
+	}
+	dev_dbg(ctx->dev, "RGND 1K!!\n");
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C);
+	mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77);
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, 0x05);
+	if (sii9234_clear_error(ctx))
+		return ST_FAILURE;
+
+	msleep(T_SRC_VBUS_CBUS_TO_STABLE);
+	return ST_RGND_1K;
+}
+
+static enum sii9234_state sii9234_mhl_established(struct sii9234 *ctx)
+{
+	dev_dbg(ctx->dev, "mhl est interrupt\n");
+
+	/* Discovery override */
+	mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0x10);
+	/* Increase DDC translation layer timer (byte mode) */
+	cbus_writeb(ctx, 0x07, 0x32);
+	cbus_writebm(ctx, 0x44, ~0, 1 << 1);
+	/* Keep the discovery enabled. Need RGND interrupt */
+	mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 1);
+	mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG,
+		      RSEN_CHANGE_INT_MASK | HPD_CHANGE_INT_MASK);
+
+	if (sii9234_clear_error(ctx))
+		return ST_FAILURE;
+
+	return ST_MHL_ESTABLISHED;
+}
+
+static enum sii9234_state sii9234_hpd_change(struct sii9234 *ctx)
+{
+	int value;
+
+	value = cbus_readb(ctx, CBUS_MSC_REQ_ABORT_REASON_REG);
+	if (sii9234_clear_error(ctx))
+		return ST_FAILURE;
+
+	if (value & SET_HPD_DOWNSTREAM) {
+		/* Downstream HPD High, Enable TMDS */
+		sii9234_tmds_control(ctx, true);
+	} else {
+		/* Downstream HPD Low, Disable TMDS */
+		sii9234_tmds_control(ctx, false);
+	}
+
+	return ctx->state;
+}
+
+static enum sii9234_state sii9234_rsen_change(struct sii9234 *ctx)
+{
+	int value;
+
+	/* Work_around code to handle wrong interrupt */
+	if (ctx->state != ST_RGND_1K) {
+		dev_err(ctx->dev, "RSEN_HIGH without RGND_1K\n");
+		return ST_FAILURE;
+	}
+	value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG);
+	if (value < 0)
+		return ST_FAILURE;
+
+	if (value & RSEN_STATUS) {
+		dev_dbg(ctx->dev, "MHL cable connected.. RSEN High\n");
+		return ST_RSEN_HIGH;
+	}
+	dev_dbg(ctx->dev, "RSEN lost\n");
+	/*
+	 * Once RSEN loss is confirmed,we need to check
+	 * based on cable status and chip power status,whether
+	 * it is SINK Loss(HDMI cable not connected, TV Off)
+	 * or MHL cable disconnection
+	 * TODO: Define the below mhl_disconnection()
+	 */
+	msleep(T_SRC_RXSENSE_DEGLITCH);
+	value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG);
+	if (value < 0)
+		return ST_FAILURE;
+	dev_dbg(ctx->dev, "sys_stat: %x\n", value);
+
+	if (value & RSEN_STATUS) {
+		dev_dbg(ctx->dev, "RSEN recovery\n");
+		return ST_RSEN_HIGH;
+	}
+	dev_dbg(ctx->dev, "RSEN Really LOW\n");
+	/* To meet CTS 3.3.22.2 spec */
+	sii9234_tmds_control(ctx, false);
+	force_usb_id_switch_open(ctx);
+	release_usb_id_switch_open(ctx);
+
+	return ST_FAILURE;
+}
+
+static irqreturn_t sii9234_irq_thread(int irq, void *data)
+{
+	struct sii9234 *ctx = data;
+	int intr1, intr4;
+	int intr1_en, intr4_en;
+	int cbus_intr1, cbus_intr2;
+
+	dev_dbg(ctx->dev, "%s\n", __func__);
+
+	mutex_lock(&ctx->lock);
+
+	intr1 = mhl_tx_readb(ctx, MHL_TX_INTR1_REG);
+	intr4 = mhl_tx_readb(ctx, MHL_TX_INTR4_REG);
+	intr1_en = mhl_tx_readb(ctx, MHL_TX_INTR1_ENABLE_REG);
+	intr4_en = mhl_tx_readb(ctx, MHL_TX_INTR4_ENABLE_REG);
+	cbus_intr1 = cbus_readb(ctx, CBUS_INT_STATUS_1_REG);
+	cbus_intr2 = cbus_readb(ctx, CBUS_INT_STATUS_2_REG);
+
+	if (sii9234_clear_error(ctx))
+		goto done;
+
+	dev_dbg(ctx->dev, "irq %02x/%02x %02x/%02x %02x/%02x\n",
+		intr1, intr1_en, intr4, intr4_en, cbus_intr1, cbus_intr2);
+
+	if (intr4 & RGND_READY_INT)
+		ctx->state = sii9234_rgnd_ready_irq(ctx);
+	if (intr1 & RSEN_CHANGE_INT)
+		ctx->state = sii9234_rsen_change(ctx);
+	if (intr4 & MHL_EST_INT)
+		ctx->state = sii9234_mhl_established(ctx);
+	if (intr1 & HPD_CHANGE_INT)
+		ctx->state = sii9234_hpd_change(ctx);
+	if (intr4 & CBUS_LKOUT_INT)
+		ctx->state = ST_FAILURE;
+	if (intr4 & MHL_DISC_FAIL_INT)
+		ctx->state = ST_FAILURE_DISCOVERY;
+
+ done:
+	/* Clean interrupt status and pending flags */
+	mhl_tx_writeb(ctx, MHL_TX_INTR1_REG, intr1);
+	mhl_tx_writeb(ctx, MHL_TX_INTR4_REG, intr4);
+	cbus_writeb(ctx, CBUS_MHL_STATUS_REG_0, 0xFF);
+	cbus_writeb(ctx, CBUS_MHL_STATUS_REG_1, 0xFF);
+	cbus_writeb(ctx, CBUS_INT_STATUS_1_REG, cbus_intr1);
+	cbus_writeb(ctx, CBUS_INT_STATUS_2_REG, cbus_intr2);
+
+	sii9234_clear_error(ctx);
+
+	if (ctx->state == ST_FAILURE) {
+		dev_dbg(ctx->dev, "try to reset after failure\n");
+		sii9234_hw_reset(ctx);
+		sii9234_goto_d3(ctx);
+	}
+
+	if (ctx->state == ST_FAILURE_DISCOVERY) {
+		dev_err(ctx->dev, "discovery failed, no power for MHL?\n");
+		tpi_writebm(ctx, TPI_DPD_REG, 0, 1);
+		ctx->state = ST_D3;
+	}
+
+	mutex_unlock(&ctx->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int sii9234_init_resources(struct sii9234 *ctx,
+				  struct i2c_client *client)
+{
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+	int ret;
+
+	if (!ctx->dev->of_node) {
+		dev_err(ctx->dev, "not DT device\n");
+		return -ENODEV;
+	}
+
+	ctx->gpio_reset = devm_gpiod_get(ctx->dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(ctx->gpio_reset)) {
+		dev_err(ctx->dev, "failed to get reset gpio from DT\n");
+		return PTR_ERR(ctx->gpio_reset);
+	}
+
+	ctx->supplies[0].supply = "avcc12";
+	ctx->supplies[1].supply = "avcc33";
+	ctx->supplies[2].supply = "iovcc18";
+	ctx->supplies[3].supply = "cvcc12";
+	ret = devm_regulator_bulk_get(ctx->dev, 4, ctx->supplies);
+	if (ret) {
+		dev_err(ctx->dev, "regulator_bulk failed\n");
+		return ret;
+	}
+
+	ctx->client[I2C_MHL] = client;
+
+	ctx->client[I2C_TPI] = i2c_new_dummy(adapter, I2C_TPI_ADDR);
+	if (!ctx->client[I2C_TPI]) {
+		dev_err(ctx->dev, "failed to create TPI client\n");
+		return -ENODEV;
+	}
+
+	ctx->client[I2C_HDMI] = i2c_new_dummy(adapter, I2C_HDMI_ADDR);
+	if (!ctx->client[I2C_HDMI]) {
+		dev_err(ctx->dev, "failed to create HDMI RX client\n");
+		goto fail_tpi;
+	}
+
+	ctx->client[I2C_CBUS] = i2c_new_dummy(adapter, I2C_CBUS_ADDR);
+	if (!ctx->client[I2C_CBUS]) {
+		dev_err(ctx->dev, "failed to create CBUS client\n");
+		goto fail_hdmi;
+	}
+
+	return 0;
+
+fail_hdmi:
+	i2c_unregister_device(ctx->client[I2C_HDMI]);
+fail_tpi:
+	i2c_unregister_device(ctx->client[I2C_TPI]);
+
+	return -ENODEV;
+}
+
+static void sii9234_deinit_resources(struct sii9234 *ctx)
+{
+	i2c_unregister_device(ctx->client[I2C_CBUS]);
+	i2c_unregister_device(ctx->client[I2C_HDMI]);
+	i2c_unregister_device(ctx->client[I2C_TPI]);
+}
+
+static inline struct sii9234 *bridge_to_sii9234(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct sii9234, bridge);
+}
+
+static enum drm_mode_status sii9234_mode_valid(struct drm_bridge *bridge,
+					 const struct drm_display_mode *mode)
+{
+	if (mode->clock > MHL1_MAX_CLK)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static const struct drm_bridge_funcs sii9234_bridge_funcs = {
+	.mode_valid = sii9234_mode_valid,
+};
+
+static int sii9234_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+	struct sii9234 *ctx;
+	struct device *dev = &client->dev;
+	int ret;
+
+	ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	ctx->dev = dev;
+	mutex_init(&ctx->lock);
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(dev, "I2C adapter lacks SMBUS feature\n");
+		return -EIO;
+	}
+
+	if (!client->irq) {
+		dev_err(dev, "no irq provided\n");
+		return -EINVAL;
+	}
+
+	irq_set_status_flags(client->irq, IRQ_NOAUTOEN);
+	ret = devm_request_threaded_irq(dev, client->irq, NULL,
+					sii9234_irq_thread,
+					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+					"sii9234", ctx);
+	if (ret < 0) {
+		dev_err(dev, "failed to install IRQ handler\n");
+		return ret;
+	}
+
+	ret = sii9234_init_resources(ctx, client);
+	if (ret < 0)
+		return ret;
+
+	i2c_set_clientdata(client, ctx);
+
+	ctx->bridge.funcs = &sii9234_bridge_funcs;
+	ctx->bridge.of_node = dev->of_node;
+	drm_bridge_add(&ctx->bridge);
+
+	sii9234_cable_in(ctx);
+
+	return 0;
+}
+
+static int sii9234_remove(struct i2c_client *client)
+{
+	struct sii9234 *ctx = i2c_get_clientdata(client);
+
+	sii9234_cable_out(ctx);
+	drm_bridge_remove(&ctx->bridge);
+	sii9234_deinit_resources(ctx);
+
+	return 0;
+}
+
+static const struct of_device_id sii9234_dt_match[] = {
+	{ .compatible = "sil,sii9234" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, sii9234_dt_match);
+
+static const struct i2c_device_id sii9234_id[] = {
+	{ "SII9234", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, sii9234_id);
+
+static struct i2c_driver sii9234_driver = {
+	.driver = {
+		.name	= "sii9234",
+		.of_match_table = sii9234_dt_match,
+	},
+	.probe = sii9234_probe,
+	.remove = sii9234_remove,
+	.id_table = sii9234_id,
+};
+
+module_i2c_driver(sii9234_driver);
+MODULE_LICENSE("GPL");

+ 91 - 5
drivers/gpu/drm/bridge/sil-sii8620.c

@@ -28,6 +28,8 @@
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 
 
+#include <media/rc-core.h>
+
 #include "sil-sii8620.h"
 #include "sil-sii8620.h"
 
 
 #define SII8620_BURST_BUF_LEN 288
 #define SII8620_BURST_BUF_LEN 288
@@ -58,6 +60,7 @@ enum sii8620_mt_state {
 struct sii8620 {
 struct sii8620 {
 	struct drm_bridge bridge;
 	struct drm_bridge bridge;
 	struct device *dev;
 	struct device *dev;
+	struct rc_dev *rc_dev;
 	struct clk *clk_xtal;
 	struct clk *clk_xtal;
 	struct gpio_desc *gpio_reset;
 	struct gpio_desc *gpio_reset;
 	struct gpio_desc *gpio_int;
 	struct gpio_desc *gpio_int;
@@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code)
 	sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code);
 	sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code);
 }
 }
 
 
+static void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code)
+{
+	sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code);
+}
+
+static void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code)
+{
+	sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code);
+}
+
 static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
 static void sii8620_mt_read_devcap_send(struct sii8620 *ctx,
 					struct sii8620_mt_msg *msg)
 					struct sii8620_mt_msg *msg)
 {
 {
@@ -1753,6 +1766,25 @@ static void sii8620_send_features(struct sii8620 *ctx)
 	sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf));
 	sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf));
 }
 }
 
 
+static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
+{
+	bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK);
+
+	scancode &= MHL_RCP_KEY_ID_MASK;
+
+	if (!ctx->rc_dev) {
+		dev_dbg(ctx->dev, "RCP input device not initialized\n");
+		return false;
+	}
+
+	if (pressed)
+		rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
+	else
+		rc_keyup(ctx->rc_dev);
+
+	return true;
+}
+
 static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
 static void sii8620_msc_mr_set_int(struct sii8620 *ctx)
 {
 {
 	u8 ints[MHL_INT_SIZE];
 	u8 ints[MHL_INT_SIZE];
@@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx)
 
 
 static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx)
 static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx)
 {
 {
-	struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx);
+	struct sii8620_mt_msg *msg;
 	u8 buf[2];
 	u8 buf[2];
 
 
-	if (!msg)
-		return;
-
 	sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
 	sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2);
 
 
 	switch (buf[0]) {
 	switch (buf[0]) {
 	case MHL_MSC_MSG_RAPK:
 	case MHL_MSC_MSG_RAPK:
+		msg = sii8620_msc_msg_first(ctx);
+		if (!msg)
+			return;
 		msg->ret = buf[1];
 		msg->ret = buf[1];
 		ctx->mt_state = MT_STATE_DONE;
 		ctx->mt_state = MT_STATE_DONE;
 		break;
 		break;
+	case MHL_MSC_MSG_RCP:
+		if (!sii8620_rcp_consume(ctx, buf[1]))
+			sii8620_mt_rcpe(ctx,
+					MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE);
+		sii8620_mt_rcpk(ctx, buf[1]);
+		break;
 	default:
 	default:
 		dev_err(ctx->dev, "%s message type %d,%d not supported",
 		dev_err(ctx->dev, "%s message type %d,%d not supported",
 			__func__, buf[0], buf[1]);
 			__func__, buf[0], buf[1]);
@@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx)
 	enable_irq(to_i2c_client(ctx->dev)->irq);
 	enable_irq(to_i2c_client(ctx->dev)->irq);
 }
 }
 
 
+static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
+{
+	struct rc_dev *rc_dev;
+	int ret;
+
+	rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
+	if (!rc_dev) {
+		dev_err(ctx->dev, "Failed to allocate RC device\n");
+		ctx->error = -ENOMEM;
+		return;
+	}
+
+	rc_dev->input_phys = "sii8620/input0";
+	rc_dev->input_id.bustype = BUS_VIRTUAL;
+	rc_dev->map_name = RC_MAP_CEC;
+	rc_dev->allowed_protocols = RC_PROTO_BIT_CEC;
+	rc_dev->driver_name = "sii8620";
+	rc_dev->device_name = "sii8620";
+
+	ret = rc_register_device(rc_dev);
+
+	if (ret) {
+		dev_err(ctx->dev, "Failed to register RC device\n");
+		ctx->error = ret;
+		rc_free_device(ctx->rc_dev);
+		return;
+	}
+	ctx->rc_dev = rc_dev;
+}
+
 static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
 static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge)
 {
 {
 	return container_of(bridge, struct sii8620, bridge);
 	return container_of(bridge, struct sii8620, bridge);
 }
 }
 
 
+static int sii8620_attach(struct drm_bridge *bridge)
+{
+	struct sii8620 *ctx = bridge_to_sii8620(bridge);
+
+	sii8620_init_rcp_input_dev(ctx);
+
+	return sii8620_clear_error(ctx);
+}
+
+static void sii8620_detach(struct drm_bridge *bridge)
+{
+	struct sii8620 *ctx = bridge_to_sii8620(bridge);
+
+	rc_unregister_device(ctx->rc_dev);
+}
+
 static bool sii8620_mode_fixup(struct drm_bridge *bridge,
 static bool sii8620_mode_fixup(struct drm_bridge *bridge,
 			       const struct drm_display_mode *mode,
 			       const struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted_mode)
 			       struct drm_display_mode *adjusted_mode)
@@ -2151,6 +2235,8 @@ end:
 }
 }
 
 
 static const struct drm_bridge_funcs sii8620_bridge_funcs = {
 static const struct drm_bridge_funcs sii8620_bridge_funcs = {
+	.attach = sii8620_attach,
+	.detach = sii8620_detach,
 	.mode_fixup = sii8620_mode_fixup,
 	.mode_fixup = sii8620_mode_fixup,
 };
 };
 
 
@@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client)
 	struct sii8620 *ctx = i2c_get_clientdata(client);
 	struct sii8620 *ctx = i2c_get_clientdata(client);
 
 
 	disable_irq(to_i2c_client(ctx->dev)->irq);
 	disable_irq(to_i2c_client(ctx->dev)->irq);
-	drm_bridge_remove(&ctx->bridge);
 	sii8620_hw_off(ctx);
 	sii8620_hw_off(ctx);
+	drm_bridge_remove(&ctx->bridge);
 
 
 	return 0;
 	return 0;
 }
 }

+ 1 - 4
drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c

@@ -221,7 +221,6 @@ struct dw_mipi_dsi {
 	struct drm_bridge bridge;
 	struct drm_bridge bridge;
 	struct mipi_dsi_host dsi_host;
 	struct mipi_dsi_host dsi_host;
 	struct drm_bridge *panel_bridge;
 	struct drm_bridge *panel_bridge;
-	bool is_panel_bridge;
 	struct device *dev;
 	struct device *dev;
 	void __iomem *base;
 	void __iomem *base;
 
 
@@ -297,7 +296,6 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
 		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
 		bridge = drm_panel_bridge_add(panel, DRM_MODE_CONNECTOR_DSI);
 		if (IS_ERR(bridge))
 		if (IS_ERR(bridge))
 			return PTR_ERR(bridge);
 			return PTR_ERR(bridge);
-		dsi->is_panel_bridge = true;
 	}
 	}
 
 
 	dsi->panel_bridge = bridge;
 	dsi->panel_bridge = bridge;
@@ -312,8 +310,7 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
 {
 {
 	struct dw_mipi_dsi *dsi = host_to_dsi(host);
 	struct dw_mipi_dsi *dsi = host_to_dsi(host);
 
 
-	if (dsi->is_panel_bridge)
-		drm_panel_bridge_remove(dsi->panel_bridge);
+	drm_of_panel_bridge_remove(host->dev->of_node, 1, 0);
 
 
 	drm_bridge_remove(&dsi->bridge);
 	drm_bridge_remove(&dsi->bridge);
 
 

+ 1 - 1
drivers/gpu/drm/cirrus/cirrus_mode.c

@@ -457,7 +457,7 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
 	/* pick the encoder ids */
 	/* pick the encoder ids */
 	if (enc_id)
 	if (enc_id)
-		return drm_encoder_find(connector->dev, enc_id);
+		return drm_encoder_find(connector->dev, NULL, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 

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

@@ -182,9 +182,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state)
 	for (i = 0; i < state->num_private_objs; i++) {
 	for (i = 0; i < state->num_private_objs; i++) {
 		struct drm_private_obj *obj = state->private_objs[i].ptr;
 		struct drm_private_obj *obj = state->private_objs[i].ptr;
 
 
-		if (!obj)
-			continue;
-
 		obj->funcs->atomic_destroy_state(obj,
 		obj->funcs->atomic_destroy_state(obj,
 						 state->private_objs[i].state);
 						 state->private_objs[i].state);
 		state->private_objs[i].ptr = NULL;
 		state->private_objs[i].ptr = NULL;
@@ -718,7 +715,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
 	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_mode_config *config = &dev->mode_config;
 
 
 	if (property == config->prop_fb_id) {
 	if (property == config->prop_fb_id) {
-		struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val);
+		struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val);
 		drm_atomic_set_fb_for_plane(state, fb);
 		drm_atomic_set_fb_for_plane(state, fb);
 		if (fb)
 		if (fb)
 			drm_framebuffer_put(fb);
 			drm_framebuffer_put(fb);
@@ -734,7 +731,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
 			return -EINVAL;
 			return -EINVAL;
 
 
 	} else if (property == config->prop_crtc_id) {
 	} else if (property == config->prop_crtc_id) {
-		struct drm_crtc *crtc = drm_crtc_find(dev, val);
+		struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val);
 		return drm_atomic_set_crtc_for_plane(state, crtc);
 		return drm_atomic_set_crtc_for_plane(state, crtc);
 	} else if (property == config->prop_crtc_x) {
 	} else if (property == config->prop_crtc_x) {
 		state->crtc_x = U642I64(val);
 		state->crtc_x = U642I64(val);
@@ -1149,7 +1146,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
 	struct drm_mode_config *config = &dev->mode_config;
 	struct drm_mode_config *config = &dev->mode_config;
 
 
 	if (property == config->prop_crtc_id) {
 	if (property == config->prop_crtc_id) {
-		struct drm_crtc *crtc = drm_crtc_find(dev, val);
+		struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val);
 		return drm_atomic_set_crtc_for_connector(state, crtc);
 		return drm_atomic_set_crtc_for_connector(state, crtc);
 	} else if (property == config->dpms_property) {
 	} else if (property == config->dpms_property) {
 		/* setting DPMS property requires special handling, which
 		/* setting DPMS property requires special handling, which
@@ -2259,7 +2256,7 @@ retry:
 			goto out;
 			goto out;
 		}
 		}
 
 
-		obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
+		obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY);
 		if (!obj) {
 		if (!obj) {
 			ret = -ENOENT;
 			ret = -ENOENT;
 			goto out;
 			goto out;

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

@@ -1704,7 +1704,7 @@ crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc)
  * drm_atomic_helper_commit_cleanup_done().
  * drm_atomic_helper_commit_cleanup_done().
  *
  *
  * This is all implemented by in drm_atomic_helper_commit(), giving drivers a
  * This is all implemented by in drm_atomic_helper_commit(), giving drivers a
- * complete and esay-to-use default implementation of the atomic_commit() hook.
+ * complete and easy-to-use default implementation of the atomic_commit() hook.
  *
  *
  * The tracking of asynchronously executed and still pending commits is done
  * The tracking of asynchronously executed and still pending commits is done
  * using the core structure &drm_crtc_commit.
  * using the core structure &drm_crtc_commit.
@@ -1819,7 +1819,7 @@ EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
  * This function waits for all preceeding commits that touch the same CRTC as
  * This function waits for all preceeding commits that touch the same CRTC as
  * @old_state to both be committed to the hardware (as signalled by
  * @old_state to both be committed to the hardware (as signalled by
  * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
  * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
- * by calling drm_crtc_vblank_send_event() on the &drm_crtc_state.event).
+ * by calling drm_crtc_send_vblank_event() on the &drm_crtc_state.event).
  *
  *
  * This is part of the atomic helper support for nonblocking commits, see
  * This is part of the atomic helper support for nonblocking commits, see
  * drm_atomic_helper_setup_commit() for an overview.
  * drm_atomic_helper_setup_commit() for an overview.
@@ -3052,6 +3052,7 @@ out:
 		drm_modeset_backoff(&ctx);
 		drm_modeset_backoff(&ctx);
 	}
 	}
 
 
+	drm_atomic_state_put(state);
 	drm_modeset_drop_locks(&ctx);
 	drm_modeset_drop_locks(&ctx);
 	drm_modeset_acquire_fini(&ctx);
 	drm_modeset_acquire_fini(&ctx);
 
 
@@ -3206,7 +3207,7 @@ struct drm_encoder *
 drm_atomic_helper_best_encoder(struct drm_connector *connector)
 drm_atomic_helper_best_encoder(struct drm_connector *connector)
 {
 {
 	WARN_ON(connector->encoder_ids[1]);
 	WARN_ON(connector->encoder_ids[1]);
-	return drm_encoder_find(connector->dev, connector->encoder_ids[0]);
+	return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
 }
 }
 EXPORT_SYMBOL(drm_atomic_helper_best_encoder);
 EXPORT_SYMBOL(drm_atomic_helper_best_encoder);
 
 

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

@@ -230,7 +230,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+	crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
 	if (!crtc)
 	if (!crtc)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -308,7 +308,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+	crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
 	if (!crtc)
 	if (!crtc)
 		return -ENOENT;
 		return -ENOENT;
 
 

+ 1 - 1
drivers/gpu/drm/drm_connector.c

@@ -1310,7 +1310,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
 
 
 	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
 	memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
 
 
-	connector = drm_connector_lookup(dev, out_resp->connector_id);
+	connector = drm_connector_lookup(dev, file_priv, out_resp->connector_id);
 	if (!connector)
 	if (!connector)
 		return -ENOENT;
 		return -ENOENT;
 
 

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

@@ -402,7 +402,7 @@ int drm_mode_getcrtc(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+	crtc = drm_crtc_find(dev, file_priv, crtc_resp->crtc_id);
 	if (!crtc)
 	if (!crtc)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -569,7 +569,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
 	if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
 	if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
 		return -ERANGE;
 		return -ERANGE;
 
 
-	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
+	crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id);
 	if (!crtc) {
 	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
 		return -ENOENT;
 		return -ENOENT;
@@ -595,7 +595,7 @@ retry:
 			/* Make refcounting symmetric with the lookup path. */
 			/* Make refcounting symmetric with the lookup path. */
 			drm_framebuffer_get(fb);
 			drm_framebuffer_get(fb);
 		} else {
 		} else {
-			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
+			fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id);
 			if (!fb) {
 			if (!fb) {
 				DRM_DEBUG_KMS("Unknown FB ID%d\n",
 				DRM_DEBUG_KMS("Unknown FB ID%d\n",
 						crtc_req->fb_id);
 						crtc_req->fb_id);
@@ -680,7 +680,7 @@ retry:
 				goto out;
 				goto out;
 			}
 			}
 
 
-			connector = drm_connector_lookup(dev, out_id);
+			connector = drm_connector_lookup(dev, file_priv, out_id);
 			if (!connector) {
 			if (!connector) {
 				DRM_DEBUG_KMS("Connector id %d unknown\n",
 				DRM_DEBUG_KMS("Connector id %d unknown\n",
 						out_id);
 						out_id);

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

@@ -562,12 +562,12 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set,
 	 * Allocate space for the backup of all (non-pointer) encoder and
 	 * Allocate space for the backup of all (non-pointer) encoder and
 	 * connector data.
 	 * connector data.
 	 */
 	 */
-	save_encoder_crtcs = kzalloc(dev->mode_config.num_encoder *
+	save_encoder_crtcs = kcalloc(dev->mode_config.num_encoder,
 				sizeof(struct drm_crtc *), GFP_KERNEL);
 				sizeof(struct drm_crtc *), GFP_KERNEL);
 	if (!save_encoder_crtcs)
 	if (!save_encoder_crtcs)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	save_connector_encoders = kzalloc(dev->mode_config.num_connector *
+	save_connector_encoders = kcalloc(dev->mode_config.num_connector,
 				sizeof(struct drm_encoder *), GFP_KERNEL);
 				sizeof(struct drm_encoder *), GFP_KERNEL);
 	if (!save_connector_encoders) {
 	if (!save_connector_encoders) {
 		kfree(save_encoder_crtcs);
 		kfree(save_encoder_crtcs);

+ 1 - 0
drivers/gpu/drm/drm_crtc_internal.h

@@ -106,6 +106,7 @@ int drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj,
 void drm_mode_object_register(struct drm_device *dev,
 void drm_mode_object_register(struct drm_device *dev,
 			      struct drm_mode_object *obj);
 			      struct drm_mode_object *obj);
 struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
 struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
+					       struct drm_file *file_priv,
 					       uint32_t id, uint32_t type);
 					       uint32_t id, uint32_t type);
 void drm_mode_object_unregister(struct drm_device *dev,
 void drm_mode_object_unregister(struct drm_device *dev,
 				struct drm_mode_object *object);
 				struct drm_mode_object *object);

+ 5 - 2
drivers/gpu/drm/drm_dp_helper.c

@@ -137,8 +137,10 @@ EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
 u8 drm_dp_link_rate_to_bw_code(int link_rate)
 u8 drm_dp_link_rate_to_bw_code(int link_rate)
 {
 {
 	switch (link_rate) {
 	switch (link_rate) {
-	case 162000:
 	default:
 	default:
+		WARN(1, "unknown DP link rate %d, using %x\n", link_rate,
+		     DP_LINK_BW_1_62);
+	case 162000:
 		return DP_LINK_BW_1_62;
 		return DP_LINK_BW_1_62;
 	case 270000:
 	case 270000:
 		return DP_LINK_BW_2_7;
 		return DP_LINK_BW_2_7;
@@ -151,8 +153,9 @@ EXPORT_SYMBOL(drm_dp_link_rate_to_bw_code);
 int drm_dp_bw_code_to_link_rate(u8 link_bw)
 int drm_dp_bw_code_to_link_rate(u8 link_bw)
 {
 {
 	switch (link_bw) {
 	switch (link_bw) {
-	case DP_LINK_BW_1_62:
 	default:
 	default:
+		WARN(1, "unknown DP link BW code %x, using 162000\n", link_bw);
+	case DP_LINK_BW_1_62:
 		return 162000;
 		return 162000;
 	case DP_LINK_BW_2_7:
 	case DP_LINK_BW_2_7:
 		return 270000;
 		return 270000;

+ 1 - 1
drivers/gpu/drm/drm_encoder.c

@@ -220,7 +220,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+	encoder = drm_encoder_find(dev, file_priv, enc_resp->encoder_id);
 	if (!encoder)
 	if (!encoder)
 		return -ENOENT;
 		return -ENOENT;
 
 

+ 1 - 1
drivers/gpu/drm/drm_fb_helper.c

@@ -2266,7 +2266,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 	if (modes[n] == NULL)
 	if (modes[n] == NULL)
 		return best_score;
 		return best_score;
 
 
-	crtcs = kzalloc(fb_helper->connector_count *
+	crtcs = kcalloc(fb_helper->connector_count,
 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
 			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
 	if (!crtcs)
 	if (!crtcs)
 		return best_score;
 		return best_score;

+ 5 - 4
drivers/gpu/drm/drm_framebuffer.c

@@ -381,7 +381,7 @@ int drm_mode_rmfb(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	fb = drm_framebuffer_lookup(dev, *id);
+	fb = drm_framebuffer_lookup(dev, file_priv, *id);
 	if (!fb)
 	if (!fb)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -450,7 +450,7 @@ int drm_mode_getfb(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	fb = drm_framebuffer_lookup(dev, r->fb_id);
+	fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
 	if (!fb)
 	if (!fb)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -515,7 +515,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	fb = drm_framebuffer_lookup(dev, r->fb_id);
+	fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id);
 	if (!fb)
 	if (!fb)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -688,12 +688,13 @@ EXPORT_SYMBOL(drm_framebuffer_init);
  * again, using drm_framebuffer_put().
  * again, using drm_framebuffer_put().
  */
  */
 struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
 struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
+					       struct drm_file *file_priv,
 					       uint32_t id)
 					       uint32_t id)
 {
 {
 	struct drm_mode_object *obj;
 	struct drm_mode_object *obj;
 	struct drm_framebuffer *fb = NULL;
 	struct drm_framebuffer *fb = NULL;
 
 
-	obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB);
+	obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB);
 	if (obj)
 	if (obj)
 		fb = obj_to_fb(obj);
 		fb = obj_to_fb(obj);
 	return fb;
 	return fb;

+ 52 - 30
drivers/gpu/drm/drm_gem_framebuffer_helper.c

@@ -27,19 +27,24 @@
  * DOC: overview
  * DOC: overview
  *
  *
  * This library provides helpers for drivers that don't subclass
  * This library provides helpers for drivers that don't subclass
- * &drm_framebuffer and and use &drm_gem_object for their backing storage.
+ * &drm_framebuffer and use &drm_gem_object for their backing storage.
  *
  *
  * Drivers without additional needs to validate framebuffers can simply use
  * Drivers without additional needs to validate framebuffers can simply use
- * drm_gem_fb_create() and everything is wired up automatically. But all
- * parts can be used individually.
+ * drm_gem_fb_create() and everything is wired up automatically. Other drivers
+ * can use all parts independently.
  */
  */
 
 
 /**
 /**
- * drm_gem_fb_get_obj() - Get GEM object for framebuffer
- * @fb: The framebuffer
- * @plane: Which plane
+ * drm_gem_fb_get_obj() - Get GEM object backing the framebuffer
+ * @fb: Framebuffer
+ * @plane: Plane index
  *
  *
- * Returns the GEM object for given framebuffer.
+ * No additional reference is taken beyond the one that the &drm_frambuffer
+ * already holds.
+ *
+ * Returns:
+ * Pointer to &drm_gem_object for the given framebuffer and plane index or NULL
+ * if it does not exist.
  */
  */
 struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
 struct drm_gem_object *drm_gem_fb_get_obj(struct drm_framebuffer *fb,
 					  unsigned int plane)
 					  unsigned int plane)
@@ -82,7 +87,7 @@ drm_gem_fb_alloc(struct drm_device *dev,
 
 
 /**
 /**
  * drm_gem_fb_destroy - Free GEM backed framebuffer
  * drm_gem_fb_destroy - Free GEM backed framebuffer
- * @fb: DRM framebuffer
+ * @fb: Framebuffer
  *
  *
  * Frees a GEM backed framebuffer with its backing buffer(s) and the structure
  * Frees a GEM backed framebuffer with its backing buffer(s) and the structure
  * itself. Drivers can use this as their &drm_framebuffer_funcs->destroy
  * itself. Drivers can use this as their &drm_framebuffer_funcs->destroy
@@ -102,12 +107,13 @@ EXPORT_SYMBOL(drm_gem_fb_destroy);
 
 
 /**
 /**
  * drm_gem_fb_create_handle - Create handle for GEM backed framebuffer
  * drm_gem_fb_create_handle - Create handle for GEM backed framebuffer
- * @fb: DRM framebuffer
- * @file: drm file
- * @handle: handle created
+ * @fb: Framebuffer
+ * @file: DRM file to register the handle for
+ * @handle: Pointer to return the created handle
  *
  *
+ * This function creates a handle for the GEM object backing the framebuffer.
  * Drivers can use this as their &drm_framebuffer_funcs->create_handle
  * Drivers can use this as their &drm_framebuffer_funcs->create_handle
- * callback.
+ * callback. The GETFB IOCTL calls into this callback.
  *
  *
  * Returns:
  * Returns:
  * 0 on success or a negative error code on failure.
  * 0 on success or a negative error code on failure.
@@ -120,18 +126,21 @@ int drm_gem_fb_create_handle(struct drm_framebuffer *fb, struct drm_file *file,
 EXPORT_SYMBOL(drm_gem_fb_create_handle);
 EXPORT_SYMBOL(drm_gem_fb_create_handle);
 
 
 /**
 /**
- * drm_gem_fb_create_with_funcs() - helper function for the
+ * drm_gem_fb_create_with_funcs() - Helper function for the
  *                                  &drm_mode_config_funcs.fb_create
  *                                  &drm_mode_config_funcs.fb_create
  *                                  callback
  *                                  callback
  * @dev: DRM device
  * @dev: DRM device
- * @file: drm file for the ioctl call
- * @mode_cmd: metadata from the userspace fb creation request
+ * @file: DRM file that holds the GEM handle(s) backing the framebuffer
+ * @mode_cmd: Metadata from the userspace framebuffer creation request
  * @funcs: vtable to be used for the new framebuffer object
  * @funcs: vtable to be used for the new framebuffer object
  *
  *
  * This can be used to set &drm_framebuffer_funcs for drivers that need the
  * This can be used to set &drm_framebuffer_funcs for drivers that need the
  * &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
  * &drm_framebuffer_funcs.dirty callback. Use drm_gem_fb_create() if you don't
  * need to change &drm_framebuffer_funcs.
  * need to change &drm_framebuffer_funcs.
  * The function does buffer size validation.
  * The function does buffer size validation.
+ *
+ * Returns:
+ * Pointer to a &drm_framebuffer on success or an error pointer on failure.
  */
  */
 struct drm_framebuffer *
 struct drm_framebuffer *
 drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
 drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file,
@@ -192,15 +201,26 @@ static const struct drm_framebuffer_funcs drm_gem_fb_funcs = {
 };
 };
 
 
 /**
 /**
- * drm_gem_fb_create() - &drm_mode_config_funcs.fb_create callback function
+ * drm_gem_fb_create() - Helper function for the
+ *                       &drm_mode_config_funcs.fb_create callback
  * @dev: DRM device
  * @dev: DRM device
- * @file: drm file for the ioctl call
- * @mode_cmd: metadata from the userspace fb creation request
+ * @file: DRM file that holds the GEM handle(s) backing the framebuffer
+ * @mode_cmd: Metadata from the userspace framebuffer creation request
+ *
+ * This function creates a new framebuffer object described by
+ * &drm_mode_fb_cmd2. This description includes handles for the buffer(s)
+ * backing the framebuffer.
  *
  *
  * If your hardware has special alignment or pitch requirements these should be
  * If your hardware has special alignment or pitch requirements these should be
  * checked before calling this function. The function does buffer size
  * checked before calling this function. The function does buffer size
  * validation. Use drm_gem_fb_create_with_funcs() if you need to set
  * validation. Use drm_gem_fb_create_with_funcs() if you need to set
  * &drm_framebuffer_funcs.dirty.
  * &drm_framebuffer_funcs.dirty.
+ *
+ * Drivers can use this as their &drm_mode_config_funcs.fb_create callback.
+ * The ADDFB2 IOCTL calls into this callback.
+ *
+ * Returns:
+ * Pointer to a &drm_framebuffer on success or an error pointer on failure.
  */
  */
 struct drm_framebuffer *
 struct drm_framebuffer *
 drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
 drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
@@ -212,15 +232,15 @@ drm_gem_fb_create(struct drm_device *dev, struct drm_file *file,
 EXPORT_SYMBOL_GPL(drm_gem_fb_create);
 EXPORT_SYMBOL_GPL(drm_gem_fb_create);
 
 
 /**
 /**
- * drm_gem_fb_prepare_fb() - Prepare gem framebuffer
- * @plane: Which plane
- * @state: Plane state attach fence to
+ * drm_gem_fb_prepare_fb() - Prepare a GEM backed framebuffer
+ * @plane: Plane
+ * @state: Plane state the fence will be attached to
  *
  *
- * This can be used as the &drm_plane_helper_funcs.prepare_fb hook.
- *
- * This function checks if the plane FB has an dma-buf attached, extracts
- * the exclusive fence and attaches it to plane state for the atomic helper
- * to wait on.
+ * This function prepares a GEM backed framebuffer for scanout by checking if
+ * the plane framebuffer has a DMA-BUF attached. If it does, it extracts the
+ * exclusive fence and attaches it to the plane state for the atomic helper to
+ * wait on. This function can be used as the &drm_plane_helper_funcs.prepare_fb
+ * callback.
  *
  *
  * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple
  * There is no need for &drm_plane_helper_funcs.cleanup_fb hook for simple
  * gem based framebuffer drivers which have their buffers always pinned in
  * gem based framebuffer drivers which have their buffers always pinned in
@@ -246,17 +266,19 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane,
 EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
 EXPORT_SYMBOL_GPL(drm_gem_fb_prepare_fb);
 
 
 /**
 /**
- * drm_gem_fbdev_fb_create - Create a drm_framebuffer for fbdev emulation
+ * drm_gem_fbdev_fb_create - Create a GEM backed &drm_framebuffer for fbdev
+ *                           emulation
  * @dev: DRM device
  * @dev: DRM device
  * @sizes: fbdev size description
  * @sizes: fbdev size description
- * @pitch_align: optional pitch alignment
+ * @pitch_align: Optional pitch alignment
  * @obj: GEM object backing the framebuffer
  * @obj: GEM object backing the framebuffer
  * @funcs: vtable to be used for the new framebuffer object
  * @funcs: vtable to be used for the new framebuffer object
  *
  *
- * This function creates a framebuffer for use with fbdev emulation.
+ * This function creates a framebuffer from a &drm_fb_helper_surface_size
+ * description for use in the &drm_fb_helper_funcs.fb_probe callback.
  *
  *
  * Returns:
  * Returns:
- * Pointer to a drm_framebuffer on success or an error pointer on failure.
+ * Pointer to a &drm_framebuffer on success or an error pointer on failure.
  */
  */
 struct drm_framebuffer *
 struct drm_framebuffer *
 drm_gem_fbdev_fb_create(struct drm_device *dev,
 drm_gem_fbdev_fb_create(struct drm_device *dev,

+ 0 - 1
drivers/gpu/drm/drm_internal.h

@@ -55,7 +55,6 @@ int drm_clients_info(struct seq_file *m, void* data);
 int drm_gem_name_info(struct seq_file *m, void *data);
 int drm_gem_name_info(struct seq_file *m, void *data);
 
 
 /* drm_vblank.c */
 /* drm_vblank.c */
-extern unsigned int drm_timestamp_monotonic;
 void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
 void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe);
 void drm_vblank_cleanup(struct drm_device *dev);
 void drm_vblank_cleanup(struct drm_device *dev);
 
 

+ 1 - 1
drivers/gpu/drm/drm_ioctl.c

@@ -235,7 +235,7 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
 	/* Only some caps make sense with UMS/render-only drivers. */
 	/* Only some caps make sense with UMS/render-only drivers. */
 	switch (req->capability) {
 	switch (req->capability) {
 	case DRM_CAP_TIMESTAMP_MONOTONIC:
 	case DRM_CAP_TIMESTAMP_MONOTONIC:
-		req->value = drm_timestamp_monotonic;
+		req->value = 1;
 		return 0;
 		return 0;
 	case DRM_CAP_PRIME:
 	case DRM_CAP_PRIME:
 		req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0;
 		req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0;

+ 6 - 4
drivers/gpu/drm/drm_mode_object.c

@@ -105,6 +105,7 @@ void drm_mode_object_unregister(struct drm_device *dev,
 }
 }
 
 
 struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
 struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
+					       struct drm_file *file_priv,
 					       uint32_t id, uint32_t type)
 					       uint32_t id, uint32_t type)
 {
 {
 	struct drm_mode_object *obj = NULL;
 	struct drm_mode_object *obj = NULL;
@@ -127,7 +128,7 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
 
 
 /**
 /**
  * drm_mode_object_find - look up a drm object with static lifetime
  * drm_mode_object_find - look up a drm object with static lifetime
- * @dev: drm device
+ * @file_priv: drm file
  * @id: id of the mode object
  * @id: id of the mode object
  * @type: type of the mode object
  * @type: type of the mode object
  *
  *
@@ -136,11 +137,12 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
  * by callind drm_mode_object_put().
  * by callind drm_mode_object_put().
  */
  */
 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
 struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
+		struct drm_file *file_priv,
 		uint32_t id, uint32_t type)
 		uint32_t id, uint32_t type)
 {
 {
 	struct drm_mode_object *obj = NULL;
 	struct drm_mode_object *obj = NULL;
 
 
-	obj = __drm_mode_object_find(dev, id, type);
+	obj = __drm_mode_object_find(dev, file_priv, id, type);
 	return obj;
 	return obj;
 }
 }
 EXPORT_SYMBOL(drm_mode_object_find);
 EXPORT_SYMBOL(drm_mode_object_find);
@@ -359,7 +361,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
 
 
 	drm_modeset_lock_all(dev);
 	drm_modeset_lock_all(dev);
 
 
-	obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
+	obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
 	if (!obj) {
 	if (!obj) {
 		ret = -ENOENT;
 		ret = -ENOENT;
 		goto out;
 		goto out;
@@ -481,7 +483,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
+	arg_obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type);
 	if (!arg_obj)
 	if (!arg_obj)
 		return -ENOENT;
 		return -ENOENT;
 
 

+ 7 - 7
drivers/gpu/drm/drm_plane.c

@@ -513,7 +513,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	plane = drm_plane_find(dev, plane_resp->plane_id);
+	plane = drm_plane_find(dev, file_priv, plane_resp->plane_id);
 	if (!plane)
 	if (!plane)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -703,7 +703,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 	 * First, find the plane, crtc, and fb objects.  If not available,
 	 * First, find the plane, crtc, and fb objects.  If not available,
 	 * we don't bother to call the driver.
 	 * we don't bother to call the driver.
 	 */
 	 */
-	plane = drm_plane_find(dev, plane_req->plane_id);
+	plane = drm_plane_find(dev, file_priv, plane_req->plane_id);
 	if (!plane) {
 	if (!plane) {
 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
 		DRM_DEBUG_KMS("Unknown plane ID %d\n",
 			      plane_req->plane_id);
 			      plane_req->plane_id);
@@ -711,14 +711,14 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
 	}
 	}
 
 
 	if (plane_req->fb_id) {
 	if (plane_req->fb_id) {
-		fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
+		fb = drm_framebuffer_lookup(dev, file_priv, plane_req->fb_id);
 		if (!fb) {
 		if (!fb) {
 			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
 			DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
 				      plane_req->fb_id);
 				      plane_req->fb_id);
 			return -ENOENT;
 			return -ENOENT;
 		}
 		}
 
 
-		crtc = drm_crtc_find(dev, plane_req->crtc_id);
+		crtc = drm_crtc_find(dev, file_priv, plane_req->crtc_id);
 		if (!crtc) {
 		if (!crtc) {
 			drm_framebuffer_put(fb);
 			drm_framebuffer_put(fb);
 			DRM_DEBUG_KMS("Unknown crtc ID %d\n",
 			DRM_DEBUG_KMS("Unknown crtc ID %d\n",
@@ -829,7 +829,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
 	if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	crtc = drm_crtc_find(dev, req->crtc_id);
+	crtc = drm_crtc_find(dev, file_priv, req->crtc_id);
 	if (!crtc) {
 	if (!crtc) {
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
 		return -ENOENT;
 		return -ENOENT;
@@ -944,7 +944,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
 	if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	crtc = drm_crtc_find(dev, page_flip->crtc_id);
+	crtc = drm_crtc_find(dev, file_priv, page_flip->crtc_id);
 	if (!crtc)
 	if (!crtc)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -1005,7 +1005,7 @@ retry:
 		goto out;
 		goto out;
 	}
 	}
 
 
-	fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
+	fb = drm_framebuffer_lookup(dev, file_priv, page_flip->fb_id);
 	if (!fb) {
 	if (!fb) {
 		ret = -ENOENT;
 		ret = -ENOENT;
 		goto out;
 		goto out;

+ 1 - 1
drivers/gpu/drm/drm_plane_helper.c

@@ -354,7 +354,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
 	/* Find current connectors for CRTC */
 	/* Find current connectors for CRTC */
 	num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
 	num_connectors = get_connectors_for_crtc(crtc, NULL, 0);
 	BUG_ON(num_connectors == 0);
 	BUG_ON(num_connectors == 0);
-	connector_list = kzalloc(num_connectors * sizeof(*connector_list),
+	connector_list = kcalloc(num_connectors, sizeof(*connector_list),
 				 GFP_KERNEL);
 				 GFP_KERNEL);
 	if (!connector_list)
 	if (!connector_list)
 		return -ENOMEM;
 		return -ENOMEM;

+ 1 - 1
drivers/gpu/drm/drm_probe_helper.c

@@ -99,7 +99,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode,
 
 
 	/* Step 2: Validate against encoders and crtcs */
 	/* Step 2: Validate against encoders and crtcs */
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-		struct drm_encoder *encoder = drm_encoder_find(dev, ids[i]);
+		struct drm_encoder *encoder = drm_encoder_find(dev, NULL, ids[i]);
 		struct drm_crtc *crtc;
 		struct drm_crtc *crtc;
 
 
 		if (!encoder)
 		if (!encoder)

+ 3 - 3
drivers/gpu/drm/drm_property.c

@@ -450,7 +450,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	property = drm_property_find(dev, out_resp->prop_id);
+	property = drm_property_find(dev, file_priv, out_resp->prop_id);
 	if (!property)
 	if (!property)
 		return -ENOENT;
 		return -ENOENT;
 
 
@@ -634,7 +634,7 @@ struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
 	struct drm_mode_object *obj;
 	struct drm_mode_object *obj;
 	struct drm_property_blob *blob = NULL;
 	struct drm_property_blob *blob = NULL;
 
 
-	obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+	obj = __drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_BLOB);
 	if (obj)
 	if (obj)
 		blob = obj_to_blob(obj);
 		blob = obj_to_blob(obj);
 	return blob;
 	return blob;
@@ -897,7 +897,7 @@ bool drm_property_change_valid_get(struct drm_property *property,
 		if (value == 0)
 		if (value == 0)
 			return true;
 			return true;
 
 
-		*ref = __drm_mode_object_find(property->dev, value,
+		*ref = __drm_mode_object_find(property->dev, NULL, value,
 					      property->values[0]);
 					      property->values[0]);
 		return *ref != NULL;
 		return *ref != NULL;
 	}
 	}

+ 2 - 1
drivers/gpu/drm/drm_syncobj.c

@@ -845,7 +845,8 @@ static int drm_syncobj_array_wait(struct drm_device *dev,
 }
 }
 
 
 static int drm_syncobj_array_find(struct drm_file *file_private,
 static int drm_syncobj_array_find(struct drm_file *file_private,
-				  void *user_handles, uint32_t count_handles,
+				  void __user *user_handles,
+				  uint32_t count_handles,
 				  struct drm_syncobj ***syncobjs_out)
 				  struct drm_syncobj ***syncobjs_out)
 {
 {
 	uint32_t i, *handles;
 	uint32_t i, *handles;

+ 67 - 73
drivers/gpu/drm/drm_vblank.c

@@ -78,28 +78,20 @@
 
 
 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, bool in_vblank_irq);
+			  ktime_t *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. */
 
 
-/*
- * Default to use monotonic timestamps for wait-for-vblank and page-flip
- * complete events.
- */
-unsigned int drm_timestamp_monotonic = 1;
-
 static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
 static int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
 
 
 module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
 module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
 module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
 module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
-module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
 MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
 MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)");
 MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
 MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
-MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
 
 
 static void store_vblank(struct drm_device *dev, unsigned int pipe,
 static void store_vblank(struct drm_device *dev, unsigned int pipe,
 			 u32 vblank_count_inc,
 			 u32 vblank_count_inc,
-			 struct timeval *t_vblank, u32 last)
+			 ktime_t t_vblank, u32 last)
 {
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 
 
@@ -108,7 +100,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe,
 	vblank->last = last;
 	vblank->last = last;
 
 
 	write_seqlock(&vblank->seqlock);
 	write_seqlock(&vblank->seqlock);
-	vblank->time = *t_vblank;
+	vblank->time = t_vblank;
 	vblank->count += vblank_count_inc;
 	vblank->count += vblank_count_inc;
 	write_sequnlock(&vblank->seqlock);
 	write_sequnlock(&vblank->seqlock);
 }
 }
@@ -151,7 +143,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
 {
 {
 	u32 cur_vblank;
 	u32 cur_vblank;
 	bool rc;
 	bool rc;
-	struct timeval t_vblank;
+	ktime_t t_vblank;
 	int count = DRM_TIMESTAMP_MAXRETRIES;
 	int count = DRM_TIMESTAMP_MAXRETRIES;
 
 
 	spin_lock(&dev->vblank_time_lock);
 	spin_lock(&dev->vblank_time_lock);
@@ -171,13 +163,13 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe
 	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
 	 * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid.
 	 */
 	 */
 	if (!rc)
 	if (!rc)
-		t_vblank = (struct timeval) {0, 0};
+		t_vblank = 0;
 
 
 	/*
 	/*
 	 * +1 to make sure user will never see the same
 	 * +1 to make sure user will never see the same
 	 * vblank counter value before and after a modeset
 	 * vblank counter value before and after a modeset
 	 */
 	 */
-	store_vblank(dev, pipe, 1, &t_vblank, cur_vblank);
+	store_vblank(dev, pipe, 1, t_vblank, cur_vblank);
 
 
 	spin_unlock(&dev->vblank_time_lock);
 	spin_unlock(&dev->vblank_time_lock);
 }
 }
@@ -200,7 +192,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	u32 cur_vblank, diff;
 	u32 cur_vblank, diff;
 	bool rc;
 	bool rc;
-	struct timeval t_vblank;
+	ktime_t t_vblank;
 	int count = DRM_TIMESTAMP_MAXRETRIES;
 	int count = DRM_TIMESTAMP_MAXRETRIES;
 	int framedur_ns = vblank->framedur_ns;
 	int framedur_ns = vblank->framedur_ns;
 
 
@@ -225,11 +217,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 		/* trust the hw counter when it's around */
 		/* trust the hw counter when it's around */
 		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
 		diff = (cur_vblank - vblank->last) & dev->max_vblank_count;
 	} else if (rc && framedur_ns) {
 	} else if (rc && framedur_ns) {
-		const struct timeval *t_old;
-		u64 diff_ns;
-
-		t_old = &vblank->time;
-		diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old);
+		u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time));
 
 
 		/*
 		/*
 		 * Figure out how many vblanks we've missed based
 		 * Figure out how many vblanks we've missed based
@@ -278,9 +266,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe,
 	 * for now, to mark the vblanktimestamp as invalid.
 	 * for now, to mark the vblanktimestamp as invalid.
 	 */
 	 */
 	if (!rc && !in_vblank_irq)
 	if (!rc && !in_vblank_irq)
-		t_vblank = (struct timeval) {0, 0};
+		t_vblank = 0;
 
 
-	store_vblank(dev, pipe, diff, &t_vblank, cur_vblank);
+	store_vblank(dev, pipe, diff, t_vblank, cur_vblank);
 }
 }
 
 
 static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
 static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe)
@@ -556,7 +544,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * @pipe: index of CRTC whose vblank timestamp to retrieve
  * @pipe: index of CRTC whose vblank timestamp to retrieve
  * @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 time which should receive the timestamp
  * @in_vblank_irq:
  * @in_vblank_irq:
  *     True when called from drm_crtc_handle_vblank().  Some drivers
  *     True when called from drm_crtc_handle_vblank().  Some drivers
  *     need to apply some workarounds for gpu-specific vblank irq quirks
  *     need to apply some workarounds for gpu-specific vblank irq quirks
@@ -584,10 +572,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
 bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 					   unsigned int pipe,
 					   unsigned int pipe,
 					   int *max_error,
 					   int *max_error,
-					   struct timeval *vblank_time,
+					   ktime_t *vblank_time,
 					   bool in_vblank_irq)
 					   bool in_vblank_irq)
 {
 {
-	struct timeval tv_etime;
+	struct timespec64 ts_etime, ts_vblank_time;
 	ktime_t stime, etime;
 	ktime_t stime, etime;
 	bool vbl_status;
 	bool vbl_status;
 	struct drm_crtc *crtc;
 	struct drm_crtc *crtc;
@@ -676,41 +664,31 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
 	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
 	delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos),
 			   mode->crtc_clock);
 			   mode->crtc_clock);
 
 
-	if (!drm_timestamp_monotonic)
-		etime = ktime_mono_to_real(etime);
-
 	/* save this only for debugging purposes */
 	/* save this only for debugging purposes */
-	tv_etime = ktime_to_timeval(etime);
+	ts_etime = ktime_to_timespec64(etime);
+	ts_vblank_time = ktime_to_timespec64(*vblank_time);
 	/* Subtract time delta from raw timestamp to get final
 	/* Subtract time delta from raw timestamp to get final
 	 * vblank_time timestamp for end of vblank.
 	 * vblank_time timestamp for end of vblank.
 	 */
 	 */
 	etime = ktime_sub_ns(etime, delta_ns);
 	etime = ktime_sub_ns(etime, delta_ns);
-	*vblank_time = ktime_to_timeval(etime);
+	*vblank_time = etime;
 
 
-	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n",
+	DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, %d rep]\n",
 		      pipe, hpos, vpos,
 		      pipe, hpos, vpos,
-		      (long)tv_etime.tv_sec, (long)tv_etime.tv_usec,
-		      (long)vblank_time->tv_sec, (long)vblank_time->tv_usec,
-		      duration_ns/1000, i);
+		      (u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000,
+		      (u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000,
+		      duration_ns / 1000, i);
 
 
 	return true;
 	return true;
 }
 }
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos);
 
 
-static struct timeval get_drm_timestamp(void)
-{
-	ktime_t now;
-
-	now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real();
-	return ktime_to_timeval(now);
-}
-
 /**
 /**
  * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
  * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent
  *                             vblank interval
  *                             vblank interval
  * @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 time which should receive the timestamp
  * @in_vblank_irq:
  * @in_vblank_irq:
  *     True when called from drm_crtc_handle_vblank().  Some drivers
  *     True when called from drm_crtc_handle_vblank().  Some drivers
  *     need to apply some workarounds for gpu-specific vblank irq quirks
  *     need to apply some workarounds for gpu-specific vblank irq quirks
@@ -728,7 +706,7 @@ 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, bool in_vblank_irq)
+			  ktime_t *tvblank, bool in_vblank_irq)
 {
 {
 	bool ret = false;
 	bool ret = false;
 
 
@@ -744,7 +722,7 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe,
 	 * Return current monotonic/gettimeofday timestamp as best estimate.
 	 * Return current monotonic/gettimeofday timestamp as best estimate.
 	 */
 	 */
 	if (!ret)
 	if (!ret)
-		*tvblank = get_drm_timestamp();
+		*tvblank = ktime_get();
 
 
 	return ret;
 	return ret;
 }
 }
@@ -769,14 +747,14 @@ u32 drm_crtc_vblank_count(struct drm_crtc *crtc)
 EXPORT_SYMBOL(drm_crtc_vblank_count);
 EXPORT_SYMBOL(drm_crtc_vblank_count);
 
 
 static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
 static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
-				     struct timeval *vblanktime)
+				     ktime_t *vblanktime)
 {
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	u32 vblank_count;
 	u32 vblank_count;
 	unsigned int seq;
 	unsigned int seq;
 
 
 	if (WARN_ON(pipe >= dev->num_crtcs)) {
 	if (WARN_ON(pipe >= dev->num_crtcs)) {
-		*vblanktime = (struct timeval) { 0 };
+		*vblanktime = 0;
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -793,7 +771,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
  * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
  * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value
  *     and the system timestamp corresponding to that vblank counter value
  *     and the system timestamp corresponding to that vblank counter value
  * @crtc: which counter to retrieve
  * @crtc: which counter to retrieve
- * @vblanktime: Pointer to struct timeval to receive the vblank timestamp.
+ * @vblanktime: Pointer to time to receive the vblank timestamp.
  *
  *
  * Fetches the "cooked" vblank count value that represents the number of
  * Fetches the "cooked" vblank count value that represents the number of
  * vblank events since the system was booted, including lost events due to
  * vblank events since the system was booted, including lost events due to
@@ -801,7 +779,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe,
  * of the vblank interval that corresponds to the current vblank counter value.
  * of the vblank interval that corresponds to the current vblank counter value.
  */
  */
 u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
 u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc,
-				   struct timeval *vblanktime)
+				   ktime_t *vblanktime)
 {
 {
 	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
 	return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc),
 					 vblanktime);
 					 vblanktime);
@@ -810,11 +788,18 @@ EXPORT_SYMBOL(drm_crtc_vblank_count_and_time);
 
 
 static void send_vblank_event(struct drm_device *dev,
 static void send_vblank_event(struct drm_device *dev,
 		struct drm_pending_vblank_event *e,
 		struct drm_pending_vblank_event *e,
-		unsigned long seq, struct timeval *now)
+		unsigned long seq, ktime_t now)
 {
 {
+	struct timespec64 tv = ktime_to_timespec64(now);
+
 	e->event.sequence = seq;
 	e->event.sequence = seq;
-	e->event.tv_sec = now->tv_sec;
-	e->event.tv_usec = now->tv_usec;
+	/*
+	 * e->event is a user space structure, with hardcoded unsigned
+	 * 32-bit seconds/microseconds. This is safe as we always use
+	 * monotonic timestamps since linux-4.15
+	 */
+	e->event.tv_sec = tv.tv_sec;
+	e->event.tv_usec = tv.tv_nsec / 1000;
 
 
 	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
 	trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe,
 					 e->event.sequence);
 					 e->event.sequence);
@@ -869,7 +854,7 @@ void drm_crtc_arm_vblank_event(struct drm_crtc *crtc,
 	assert_spin_locked(&dev->event_lock);
 	assert_spin_locked(&dev->event_lock);
 
 
 	e->pipe = pipe;
 	e->pipe = pipe;
-	e->event.sequence = drm_vblank_count(dev, pipe);
+	e->event.sequence = drm_crtc_accurate_vblank_count(crtc) + 1;
 	e->event.crtc_id = crtc->base.id;
 	e->event.crtc_id = crtc->base.id;
 	list_add_tail(&e->base.link, &dev->vblank_event_list);
 	list_add_tail(&e->base.link, &dev->vblank_event_list);
 }
 }
@@ -891,18 +876,18 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
 {
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_device *dev = crtc->dev;
 	unsigned int seq, pipe = drm_crtc_index(crtc);
 	unsigned int seq, pipe = drm_crtc_index(crtc);
-	struct timeval now;
+	ktime_t now;
 
 
 	if (dev->num_crtcs > 0) {
 	if (dev->num_crtcs > 0) {
 		seq = drm_vblank_count_and_time(dev, pipe, &now);
 		seq = drm_vblank_count_and_time(dev, pipe, &now);
 	} else {
 	} else {
 		seq = 0;
 		seq = 0;
 
 
-		now = get_drm_timestamp();
+		now = ktime_get();
 	}
 	}
 	e->pipe = pipe;
 	e->pipe = pipe;
 	e->event.crtc_id = crtc->base.id;
 	e->event.crtc_id = crtc->base.id;
-	send_vblank_event(dev, e, seq, &now);
+	send_vblank_event(dev, e, seq, now);
 }
 }
 EXPORT_SYMBOL(drm_crtc_send_vblank_event);
 EXPORT_SYMBOL(drm_crtc_send_vblank_event);
 
 
@@ -1100,7 +1085,8 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
 	unsigned int pipe = drm_crtc_index(crtc);
 	unsigned int pipe = drm_crtc_index(crtc);
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_pending_vblank_event *e, *t;
 	struct drm_pending_vblank_event *e, *t;
-	struct timeval now;
+
+	ktime_t now;
 	unsigned long irqflags;
 	unsigned long irqflags;
 	unsigned int seq;
 	unsigned int seq;
 
 
@@ -1141,7 +1127,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc)
 			  e->event.sequence, seq);
 			  e->event.sequence, seq);
 		list_del(&e->base.link);
 		list_del(&e->base.link);
 		drm_vblank_put(dev, pipe);
 		drm_vblank_put(dev, pipe);
-		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);
 
 
@@ -1321,7 +1307,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
 {
 {
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_vblank_crtc *vblank = &dev->vblank[pipe];
 	struct drm_pending_vblank_event *e;
 	struct drm_pending_vblank_event *e;
-	struct timeval now;
+	ktime_t now;
 	unsigned long flags;
 	unsigned long flags;
 	unsigned int seq;
 	unsigned int seq;
 	int ret;
 	int ret;
@@ -1367,7 +1353,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
 	e->event.sequence = vblwait->request.sequence;
 	e->event.sequence = vblwait->request.sequence;
 	if (vblank_passed(seq, vblwait->request.sequence)) {
 	if (vblank_passed(seq, vblwait->request.sequence)) {
 		drm_vblank_put(dev, pipe);
 		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
+		send_vblank_event(dev, e, seq, now);
 		vblwait->reply.sequence = seq;
 		vblwait->reply.sequence = seq;
 	} else {
 	} else {
 		/* drm_handle_vblank_events will call drm_vblank_put */
 		/* drm_handle_vblank_events will call drm_vblank_put */
@@ -1398,6 +1384,23 @@ static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait)
 					  _DRM_VBLANK_NEXTONMISS));
 					  _DRM_VBLANK_NEXTONMISS));
 }
 }
 
 
+static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe,
+				  struct drm_wait_vblank_reply *reply)
+{
+	ktime_t now;
+	struct timespec64 ts;
+
+	/*
+	 * drm_wait_vblank_reply is a UAPI structure that uses 'long'
+	 * to store the seconds. This is safe as we always use monotonic
+	 * timestamps since linux-4.15.
+	 */
+	reply->sequence = drm_vblank_count_and_time(dev, pipe, &now);
+	ts = ktime_to_timespec64(now);
+	reply->tval_sec = (u32)ts.tv_sec;
+	reply->tval_usec = ts.tv_nsec / 1000;
+}
+
 int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
 int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
 			  struct drm_file *file_priv)
 			  struct drm_file *file_priv)
 {
 {
@@ -1439,12 +1442,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
 	if (dev->vblank_disable_immediate &&
 	if (dev->vblank_disable_immediate &&
 	    drm_wait_vblank_is_query(vblwait) &&
 	    drm_wait_vblank_is_query(vblwait) &&
 	    READ_ONCE(vblank->enabled)) {
 	    READ_ONCE(vblank->enabled)) {
-		struct timeval now;
-
-		vblwait->reply.sequence =
-			drm_vblank_count_and_time(dev, pipe, &now);
-		vblwait->reply.tval_sec = now.tv_sec;
-		vblwait->reply.tval_usec = now.tv_usec;
+		drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
 		return 0;
 		return 0;
 	}
 	}
 
 
@@ -1487,11 +1485,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data,
 	}
 	}
 
 
 	if (ret != -EINTR) {
 	if (ret != -EINTR) {
-		struct timeval now;
-
-		vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now);
-		vblwait->reply.tval_sec = now.tv_sec;
-		vblwait->reply.tval_usec = now.tv_usec;
+		drm_wait_vblank_reply(dev, pipe, &vblwait->reply);
 
 
 		DRM_DEBUG("crtc %d returning %u to client\n",
 		DRM_DEBUG("crtc %d returning %u to client\n",
 			  pipe, vblwait->reply.sequence);
 			  pipe, vblwait->reply.sequence);
@@ -1507,7 +1501,7 @@ done:
 static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
 static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
 {
 {
 	struct drm_pending_vblank_event *e, *t;
 	struct drm_pending_vblank_event *e, *t;
-	struct timeval now;
+	ktime_t now;
 	unsigned int seq;
 	unsigned int seq;
 
 
 	assert_spin_locked(&dev->event_lock);
 	assert_spin_locked(&dev->event_lock);
@@ -1525,7 +1519,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
 
 
 		list_del(&e->base.link);
 		list_del(&e->base.link);
 		drm_vblank_put(dev, pipe);
 		drm_vblank_put(dev, pipe);
-		send_vblank_event(dev, e, seq, &now);
+		send_vblank_event(dev, e, seq, now);
 	}
 	}
 
 
 	trace_drm_vblank_event(pipe, seq);
 	trace_drm_vblank_event(pipe, seq);

+ 0 - 2
drivers/gpu/drm/etnaviv/Kconfig

@@ -7,8 +7,6 @@ config DRM_ETNAVIV
 	select SHMEM
 	select SHMEM
 	select SYNC_FILE
 	select SYNC_FILE
 	select TMPFS
 	select TMPFS
-	select IOMMU_API
-	select IOMMU_SUPPORT
 	select WANT_DEV_COREDUMP
 	select WANT_DEV_COREDUMP
 	select CMA if HAVE_DMA_CONTIGUOUS
 	select CMA if HAVE_DMA_CONTIGUOUS
 	select DMA_CMA if HAVE_DMA_CONTIGUOUS
 	select DMA_CMA if HAVE_DMA_CONTIGUOUS

+ 2 - 1
drivers/gpu/drm/etnaviv/Makefile

@@ -10,6 +10,7 @@ etnaviv-y := \
 	etnaviv_gpu.o \
 	etnaviv_gpu.o \
 	etnaviv_iommu_v2.o \
 	etnaviv_iommu_v2.o \
 	etnaviv_iommu.o \
 	etnaviv_iommu.o \
-	etnaviv_mmu.o
+	etnaviv_mmu.o \
+	etnaviv_perfmon.o
 
 
 obj-$(CONFIG_DRM_ETNAVIV)	+= etnaviv.o
 obj-$(CONFIG_DRM_ETNAVIV)	+= etnaviv.o

+ 36 - 0
drivers/gpu/drm/etnaviv/etnaviv_buffer.c

@@ -250,6 +250,42 @@ void etnaviv_buffer_end(struct etnaviv_gpu *gpu)
 	}
 	}
 }
 }
 
 
+/* Append a 'sync point' to the ring buffer. */
+void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event)
+{
+	struct etnaviv_cmdbuf *buffer = gpu->buffer;
+	unsigned int waitlink_offset = buffer->user_size - 16;
+	u32 dwords, target;
+
+	/*
+	 * We need at most 3 dwords in the return target:
+	 * 1 event + 1 end + 1 wait + 1 link.
+	 */
+	dwords = 4;
+	target = etnaviv_buffer_reserve(gpu, buffer, dwords);
+
+	/* Signal sync point event */
+	CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) |
+		       VIVS_GL_EVENT_FROM_PE);
+
+	/* Stop the FE to 'pause' the GPU */
+	CMD_END(buffer);
+
+	/* Append waitlink */
+	CMD_WAIT(buffer);
+	CMD_LINK(buffer, 2, etnaviv_cmdbuf_get_va(buffer) +
+			    buffer->user_size - 4);
+
+	/*
+	 * Kick off the 'sync point' command by replacing the previous
+	 * WAIT with a link to the address in the ring buffer.
+	 */
+	etnaviv_buffer_replace_wait(buffer, waitlink_offset,
+				    VIV_FE_LINK_HEADER_OP_LINK |
+				    VIV_FE_LINK_HEADER_PREFETCH(dwords),
+				    target);
+}
+
 /* Append a command buffer to the ring buffer. */
 /* Append a command buffer to the ring buffer. */
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_cmdbuf *cmdbuf)
 	struct etnaviv_cmdbuf *cmdbuf)

+ 14 - 1
drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.c

@@ -19,6 +19,7 @@
 #include "etnaviv_cmdbuf.h"
 #include "etnaviv_cmdbuf.h"
 #include "etnaviv_gpu.h"
 #include "etnaviv_gpu.h"
 #include "etnaviv_mmu.h"
 #include "etnaviv_mmu.h"
+#include "etnaviv_perfmon.h"
 
 
 #define SUBALLOC_SIZE		SZ_256K
 #define SUBALLOC_SIZE		SZ_256K
 #define SUBALLOC_GRANULE	SZ_4K
 #define SUBALLOC_GRANULE	SZ_4K
@@ -87,9 +88,10 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc)
 
 
 struct etnaviv_cmdbuf *
 struct etnaviv_cmdbuf *
 etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
 etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
-		   size_t nr_bos)
+		   size_t nr_bos, size_t nr_pmrs)
 {
 {
 	struct etnaviv_cmdbuf *cmdbuf;
 	struct etnaviv_cmdbuf *cmdbuf;
+	struct etnaviv_perfmon_request *pmrs;
 	size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]),
 	size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]),
 				 sizeof(*cmdbuf));
 				 sizeof(*cmdbuf));
 	int granule_offs, order, ret;
 	int granule_offs, order, ret;
@@ -98,6 +100,12 @@ etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
 	if (!cmdbuf)
 	if (!cmdbuf)
 		return NULL;
 		return NULL;
 
 
+	sz = sizeof(*pmrs) * nr_pmrs;
+	pmrs = kzalloc(sz, GFP_KERNEL);
+	if (!pmrs)
+		goto out_free_cmdbuf;
+
+	cmdbuf->pmrs = pmrs;
 	cmdbuf->suballoc = suballoc;
 	cmdbuf->suballoc = suballoc;
 	cmdbuf->size = size;
 	cmdbuf->size = size;
 
 
@@ -124,6 +132,10 @@ retry:
 	cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset;
 	cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset;
 
 
 	return cmdbuf;
 	return cmdbuf;
+
+out_free_cmdbuf:
+	kfree(cmdbuf);
+	return NULL;
 }
 }
 
 
 void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
 void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
@@ -139,6 +151,7 @@ void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
 	suballoc->free_space = 1;
 	suballoc->free_space = 1;
 	mutex_unlock(&suballoc->lock);
 	mutex_unlock(&suballoc->lock);
 	wake_up_all(&suballoc->free_event);
 	wake_up_all(&suballoc->free_event);
+	kfree(cmdbuf->pmrs);
 	kfree(cmdbuf);
 	kfree(cmdbuf);
 }
 }
 
 

+ 5 - 1
drivers/gpu/drm/etnaviv/etnaviv_cmdbuf.h

@@ -21,6 +21,7 @@
 
 
 struct etnaviv_gpu;
 struct etnaviv_gpu;
 struct etnaviv_cmdbuf_suballoc;
 struct etnaviv_cmdbuf_suballoc;
+struct etnaviv_perfmon_request;
 
 
 struct etnaviv_cmdbuf {
 struct etnaviv_cmdbuf {
 	/* suballocator this cmdbuf is allocated from */
 	/* suballocator this cmdbuf is allocated from */
@@ -38,6 +39,9 @@ struct etnaviv_cmdbuf {
 	u32 exec_state;
 	u32 exec_state;
 	/* per GPU in-flight list */
 	/* per GPU in-flight list */
 	struct list_head node;
 	struct list_head node;
+	/* perfmon requests */
+	unsigned int nr_pmrs;
+	struct etnaviv_perfmon_request *pmrs;
 	/* BOs attached to this command buffer */
 	/* BOs attached to this command buffer */
 	unsigned int nr_bos;
 	unsigned int nr_bos;
 	struct etnaviv_vram_mapping *bo_map[0];
 	struct etnaviv_vram_mapping *bo_map[0];
@@ -49,7 +53,7 @@ void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc);
 
 
 struct etnaviv_cmdbuf *
 struct etnaviv_cmdbuf *
 etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
 etnaviv_cmdbuf_new(struct etnaviv_cmdbuf_suballoc *suballoc, u32 size,
-		   size_t nr_bos);
+		   size_t nr_bos, size_t nr_pmrs);
 void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf);
 void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf);
 
 
 u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf);
 u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf);

+ 38 - 1
drivers/gpu/drm/etnaviv/etnaviv_drv.c

@@ -23,6 +23,7 @@
 #include "etnaviv_gpu.h"
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_mmu.h"
 #include "etnaviv_mmu.h"
+#include "etnaviv_perfmon.h"
 
 
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
 #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING
 static bool reglog;
 static bool reglog;
@@ -451,6 +452,40 @@ static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data,
 	return ret;
 	return ret;
 }
 }
 
 
+static int etnaviv_ioctl_pm_query_dom(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct drm_etnaviv_pm_domain *args = data;
+	struct etnaviv_gpu *gpu;
+
+	if (args->pipe >= ETNA_MAX_PIPES)
+		return -EINVAL;
+
+	gpu = priv->gpu[args->pipe];
+	if (!gpu)
+		return -ENXIO;
+
+	return etnaviv_pm_query_dom(gpu, args);
+}
+
+static int etnaviv_ioctl_pm_query_sig(struct drm_device *dev, void *data,
+	struct drm_file *file)
+{
+	struct etnaviv_drm_private *priv = dev->dev_private;
+	struct drm_etnaviv_pm_signal *args = data;
+	struct etnaviv_gpu *gpu;
+
+	if (args->pipe >= ETNA_MAX_PIPES)
+		return -EINVAL;
+
+	gpu = priv->gpu[args->pipe];
+	if (!gpu)
+		return -ENXIO;
+
+	return etnaviv_pm_query_sig(gpu, args);
+}
+
 static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 #define ETNA_IOCTL(n, func, flags) \
 #define ETNA_IOCTL(n, func, flags) \
 	DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
 	DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
@@ -463,6 +498,8 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 	ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_USERPTR,  gem_userptr,  DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_USERPTR,  gem_userptr,  DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_WAIT,     gem_wait,     DRM_AUTH|DRM_RENDER_ALLOW),
 	ETNA_IOCTL(GEM_WAIT,     gem_wait,     DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_AUTH|DRM_RENDER_ALLOW),
+	ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_AUTH|DRM_RENDER_ALLOW),
 };
 };
 
 
 static const struct vm_operations_struct vm_ops = {
 static const struct vm_operations_struct vm_ops = {
@@ -513,7 +550,7 @@ static struct drm_driver etnaviv_drm_driver = {
 	.desc               = "etnaviv DRM",
 	.desc               = "etnaviv DRM",
 	.date               = "20151214",
 	.date               = "20151214",
 	.major              = 1,
 	.major              = 1,
-	.minor              = 1,
+	.minor              = 2,
 };
 };
 
 
 /*
 /*

+ 1 - 5
drivers/gpu/drm/etnaviv/etnaviv_drv.h

@@ -26,7 +26,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/list.h>
 #include <linux/list.h>
-#include <linux/iommu.h>
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/sizes.h>
 #include <linux/sizes.h>
 
 
@@ -92,15 +91,12 @@ int etnaviv_gem_cpu_fini(struct drm_gem_object *obj);
 void etnaviv_gem_free_object(struct drm_gem_object *obj);
 void etnaviv_gem_free_object(struct drm_gem_object *obj);
 int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 		u32 size, u32 flags, u32 *handle);
 		u32 size, u32 flags, u32 *handle);
-struct drm_gem_object *etnaviv_gem_new_locked(struct drm_device *dev,
-		u32 size, u32 flags);
-struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
-		u32 size, u32 flags);
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
 	uintptr_t ptr, u32 size, u32 flags, u32 *handle);
 	uintptr_t ptr, u32 size, u32 flags, u32 *handle);
 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
 u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr);
 u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr);
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
+void etnaviv_sync_point_queue(struct etnaviv_gpu *gpu, unsigned int event);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 	struct etnaviv_cmdbuf *cmdbuf);
 	struct etnaviv_cmdbuf *cmdbuf);
 void etnaviv_validate_init(void);
 void etnaviv_validate_init(void);

+ 0 - 19
drivers/gpu/drm/etnaviv/etnaviv_gem.c

@@ -704,25 +704,6 @@ int etnaviv_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 	return ret;
 	return ret;
 }
 }
 
 
-struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
-		u32 size, u32 flags)
-{
-	struct drm_gem_object *obj;
-	int ret;
-
-	obj = __etnaviv_gem_new(dev, size, flags);
-	if (IS_ERR(obj))
-		return obj;
-
-	ret = etnaviv_gem_obj_add(dev, obj);
-	if (ret < 0) {
-		drm_gem_object_put_unlocked(obj);
-		return ERR_PTR(ret);
-	}
-
-	return obj;
-}
-
 int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
 int etnaviv_gem_new_private(struct drm_device *dev, size_t size, u32 flags,
 	struct reservation_object *robj, const struct etnaviv_gem_ops *ops,
 	struct reservation_object *robj, const struct etnaviv_gem_ops *ops,
 	struct etnaviv_gem_object **res)
 	struct etnaviv_gem_object **res)

+ 67 - 2
drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c

@@ -21,6 +21,7 @@
 #include "etnaviv_drv.h"
 #include "etnaviv_drv.h"
 #include "etnaviv_gpu.h"
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_gem.h"
+#include "etnaviv_perfmon.h"
 
 
 /*
 /*
  * Cmdstream submission:
  * Cmdstream submission:
@@ -283,6 +284,54 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream,
 	return 0;
 	return 0;
 }
 }
 
 
+static int submit_perfmon_validate(struct etnaviv_gem_submit *submit,
+		struct etnaviv_cmdbuf *cmdbuf,
+		const struct drm_etnaviv_gem_submit_pmr *pmrs,
+		u32 nr_pms)
+{
+	u32 i;
+
+	for (i = 0; i < nr_pms; i++) {
+		const struct drm_etnaviv_gem_submit_pmr *r = pmrs + i;
+		struct etnaviv_gem_submit_bo *bo;
+		int ret;
+
+		ret = submit_bo(submit, r->read_idx, &bo);
+		if (ret)
+			return ret;
+
+		/* at offset 0 a sequence number gets stored used for userspace sync */
+		if (r->read_offset == 0) {
+			DRM_ERROR("perfmon request: offset is 0");
+			return -EINVAL;
+		}
+
+		if (r->read_offset >= bo->obj->base.size - sizeof(u32)) {
+			DRM_ERROR("perfmon request: offset %u outside object", i);
+			return -EINVAL;
+		}
+
+		if (r->flags & ~(ETNA_PM_PROCESS_PRE | ETNA_PM_PROCESS_POST)) {
+			DRM_ERROR("perfmon request: flags are not valid");
+			return -EINVAL;
+		}
+
+		if (etnaviv_pm_req_validate(r, cmdbuf->exec_state)) {
+			DRM_ERROR("perfmon request: domain or signal not valid");
+			return -EINVAL;
+		}
+
+		cmdbuf->pmrs[i].flags = r->flags;
+		cmdbuf->pmrs[i].domain = r->domain;
+		cmdbuf->pmrs[i].signal = r->signal;
+		cmdbuf->pmrs[i].sequence = r->sequence;
+		cmdbuf->pmrs[i].offset = r->read_offset;
+		cmdbuf->pmrs[i].bo_vma = etnaviv_gem_vmap(&bo->obj->base);
+	}
+
+	return 0;
+}
+
 static void submit_cleanup(struct etnaviv_gem_submit *submit)
 static void submit_cleanup(struct etnaviv_gem_submit *submit)
 {
 {
 	unsigned i;
 	unsigned i;
@@ -306,6 +355,7 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	struct etnaviv_drm_private *priv = dev->dev_private;
 	struct drm_etnaviv_gem_submit *args = data;
 	struct drm_etnaviv_gem_submit *args = data;
 	struct drm_etnaviv_gem_submit_reloc *relocs;
 	struct drm_etnaviv_gem_submit_reloc *relocs;
+	struct drm_etnaviv_gem_submit_pmr *pmrs;
 	struct drm_etnaviv_gem_submit_bo *bos;
 	struct drm_etnaviv_gem_submit_bo *bos;
 	struct etnaviv_gem_submit *submit;
 	struct etnaviv_gem_submit *submit;
 	struct etnaviv_cmdbuf *cmdbuf;
 	struct etnaviv_cmdbuf *cmdbuf;
@@ -347,11 +397,12 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	 */
 	 */
 	bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL);
 	bos = kvmalloc_array(args->nr_bos, sizeof(*bos), GFP_KERNEL);
 	relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL);
 	relocs = kvmalloc_array(args->nr_relocs, sizeof(*relocs), GFP_KERNEL);
+	pmrs = kvmalloc_array(args->nr_pmrs, sizeof(*pmrs), GFP_KERNEL);
 	stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL);
 	stream = kvmalloc_array(1, args->stream_size, GFP_KERNEL);
 	cmdbuf = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc,
 	cmdbuf = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc,
 				    ALIGN(args->stream_size, 8) + 8,
 				    ALIGN(args->stream_size, 8) + 8,
-				    args->nr_bos);
-	if (!bos || !relocs || !stream || !cmdbuf) {
+				    args->nr_bos, args->nr_pmrs);
+	if (!bos || !relocs || !pmrs || !stream || !cmdbuf) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto err_submit_cmds;
 		goto err_submit_cmds;
 	}
 	}
@@ -373,6 +424,14 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 		goto err_submit_cmds;
 		goto err_submit_cmds;
 	}
 	}
 
 
+	ret = copy_from_user(pmrs, u64_to_user_ptr(args->pmrs),
+			     args->nr_pmrs * sizeof(*pmrs));
+	if (ret) {
+		ret = -EFAULT;
+		goto err_submit_cmds;
+	}
+	cmdbuf->nr_pmrs = args->nr_pmrs;
+
 	ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
 	ret = copy_from_user(stream, u64_to_user_ptr(args->stream),
 			     args->stream_size);
 			     args->stream_size);
 	if (ret) {
 	if (ret) {
@@ -441,6 +500,10 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
 	if (ret)
 	if (ret)
 		goto out;
 		goto out;
 
 
+	ret = submit_perfmon_validate(submit, cmdbuf, pmrs, args->nr_pmrs);
+	if (ret)
+		goto out;
+
 	memcpy(cmdbuf->vaddr, stream, args->stream_size);
 	memcpy(cmdbuf->vaddr, stream, args->stream_size);
 	cmdbuf->user_size = ALIGN(args->stream_size, 8);
 	cmdbuf->user_size = ALIGN(args->stream_size, 8);
 
 
@@ -496,6 +559,8 @@ err_submit_cmds:
 		kvfree(bos);
 		kvfree(bos);
 	if (relocs)
 	if (relocs)
 		kvfree(relocs);
 		kvfree(relocs);
+	if (pmrs)
+		kvfree(pmrs);
 
 
 	return ret;
 	return ret;
 }
 }

+ 167 - 50
drivers/gpu/drm/etnaviv/etnaviv_gpu.c

@@ -25,6 +25,7 @@
 #include "etnaviv_gpu.h"
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_mmu.h"
 #include "etnaviv_mmu.h"
+#include "etnaviv_perfmon.h"
 #include "common.xml.h"
 #include "common.xml.h"
 #include "state.xml.h"
 #include "state.xml.h"
 #include "state_hi.xml.h"
 #include "state_hi.xml.h"
@@ -420,9 +421,10 @@ static void etnaviv_gpu_update_clock(struct etnaviv_gpu *gpu)
 			     gpu->base_rate_shader >> gpu->freq_scale);
 			     gpu->base_rate_shader >> gpu->freq_scale);
 	} else {
 	} else {
 		unsigned int fscale = 1 << (6 - gpu->freq_scale);
 		unsigned int fscale = 1 << (6 - gpu->freq_scale);
-		u32 clock = VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS |
-			    VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
+		u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
 
 
+		clock &= ~VIVS_HI_CLOCK_CONTROL_FSCALE_VAL__MASK;
+		clock |= VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
 		etnaviv_gpu_load_clock(gpu, clock);
 		etnaviv_gpu_load_clock(gpu, clock);
 	}
 	}
 }
 }
@@ -433,24 +435,14 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 	unsigned long timeout;
 	unsigned long timeout;
 	bool failed = true;
 	bool failed = true;
 
 
-	/* TODO
-	 *
-	 * - clock gating
-	 * - puls eater
-	 * - what about VG?
-	 */
-
 	/* We hope that the GPU resets in under one second */
 	/* We hope that the GPU resets in under one second */
 	timeout = jiffies + msecs_to_jiffies(1000);
 	timeout = jiffies + msecs_to_jiffies(1000);
 
 
 	while (time_is_after_jiffies(timeout)) {
 	while (time_is_after_jiffies(timeout)) {
 		/* enable clock */
 		/* enable clock */
-		etnaviv_gpu_update_clock(gpu);
-
-		control = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
-
-		/* Wait for stable clock.  Vivante's code waited for 1ms */
-		usleep_range(1000, 10000);
+		unsigned int fscale = 1 << (6 - gpu->freq_scale);
+		control = VIVS_HI_CLOCK_CONTROL_FSCALE_VAL(fscale);
+		etnaviv_gpu_load_clock(gpu, control);
 
 
 		/* isolate the GPU. */
 		/* isolate the GPU. */
 		control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU;
 		control |= VIVS_HI_CLOCK_CONTROL_ISOLATE_GPU;
@@ -461,7 +453,7 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
 		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
 
 
 		/* wait for reset. */
 		/* wait for reset. */
-		msleep(1);
+		usleep_range(10, 20);
 
 
 		/* reset soft reset bit. */
 		/* reset soft reset bit. */
 		control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET;
 		control &= ~VIVS_HI_CLOCK_CONTROL_SOFT_RESET;
@@ -490,6 +482,10 @@ static int etnaviv_hw_reset(struct etnaviv_gpu *gpu)
 			continue;
 			continue;
 		}
 		}
 
 
+		/* disable debug registers, as they are not normally needed */
+		control |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, control);
+
 		failed = false;
 		failed = false;
 		break;
 		break;
 	}
 	}
@@ -721,7 +717,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	}
 	}
 
 
 	/* Create buffer: */
 	/* Create buffer: */
-	gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0);
+	gpu->buffer = etnaviv_cmdbuf_new(gpu->cmdbuf_suballoc, PAGE_SIZE, 0, 0);
 	if (!gpu->buffer) {
 	if (!gpu->buffer) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		dev_err(gpu->dev, "could not create command buffer\n");
 		dev_err(gpu->dev, "could not create command buffer\n");
@@ -739,10 +735,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 	/* Setup event management */
 	/* Setup event management */
 	spin_lock_init(&gpu->event_spinlock);
 	spin_lock_init(&gpu->event_spinlock);
 	init_completion(&gpu->event_free);
 	init_completion(&gpu->event_free);
-	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
-		gpu->event[i].used = false;
+	bitmap_zero(gpu->event_bitmap, ETNA_NR_EVENTS);
+	for (i = 0; i < ARRAY_SIZE(gpu->event); i++)
 		complete(&gpu->event_free);
 		complete(&gpu->event_free);
-	}
 
 
 	/* Now program the hardware */
 	/* Now program the hardware */
 	mutex_lock(&gpu->lock);
 	mutex_lock(&gpu->lock);
@@ -926,7 +921,7 @@ static void recover_worker(struct work_struct *work)
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
 					       recover_work);
 					       recover_work);
 	unsigned long flags;
 	unsigned long flags;
-	unsigned int i;
+	unsigned int i = 0;
 
 
 	dev_err(gpu->dev, "hangcheck recover!\n");
 	dev_err(gpu->dev, "hangcheck recover!\n");
 
 
@@ -945,14 +940,12 @@ static void recover_worker(struct work_struct *work)
 
 
 	/* complete all events, the GPU won't do it after the reset */
 	/* complete all events, the GPU won't do it after the reset */
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
-	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
-		if (!gpu->event[i].used)
-			continue;
+	for_each_set_bit_from(i, gpu->event_bitmap, ETNA_NR_EVENTS) {
 		dma_fence_signal(gpu->event[i].fence);
 		dma_fence_signal(gpu->event[i].fence);
 		gpu->event[i].fence = NULL;
 		gpu->event[i].fence = NULL;
-		gpu->event[i].used = false;
 		complete(&gpu->event_free);
 		complete(&gpu->event_free);
 	}
 	}
+	bitmap_zero(gpu->event_bitmap, ETNA_NR_EVENTS);
 	spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	gpu->completed_fence = gpu->active_fence;
 	gpu->completed_fence = gpu->active_fence;
 
 
@@ -1140,30 +1133,45 @@ int etnaviv_gpu_fence_sync_obj(struct etnaviv_gem_object *etnaviv_obj,
  * event management:
  * event management:
  */
  */
 
 
-static unsigned int event_alloc(struct etnaviv_gpu *gpu)
+static int event_alloc(struct etnaviv_gpu *gpu, unsigned nr_events,
+	unsigned int *events)
 {
 {
-	unsigned long ret, flags;
-	unsigned int i, event = ~0U;
+	unsigned long flags, timeout = msecs_to_jiffies(10 * 10000);
+	unsigned i, acquired = 0;
 
 
-	ret = wait_for_completion_timeout(&gpu->event_free,
-					  msecs_to_jiffies(10 * 10000));
-	if (!ret)
-		dev_err(gpu->dev, "wait_for_completion_timeout failed");
+	for (i = 0; i < nr_events; i++) {
+		unsigned long ret;
 
 
-	spin_lock_irqsave(&gpu->event_spinlock, flags);
+		ret = wait_for_completion_timeout(&gpu->event_free, timeout);
 
 
-	/* find first free event */
-	for (i = 0; i < ARRAY_SIZE(gpu->event); i++) {
-		if (gpu->event[i].used == false) {
-			gpu->event[i].used = true;
-			event = i;
-			break;
+		if (!ret) {
+			dev_err(gpu->dev, "wait_for_completion_timeout failed");
+			goto out;
 		}
 		}
+
+		acquired++;
+		timeout = ret;
+	}
+
+	spin_lock_irqsave(&gpu->event_spinlock, flags);
+
+	for (i = 0; i < nr_events; i++) {
+		int event = find_first_zero_bit(gpu->event_bitmap, ETNA_NR_EVENTS);
+
+		events[i] = event;
+		memset(&gpu->event[event], 0, sizeof(struct etnaviv_event));
+		set_bit(event, gpu->event_bitmap);
 	}
 	}
 
 
 	spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 
 
-	return event;
+	return 0;
+
+out:
+	for (i = 0; i < acquired; i++)
+		complete(&gpu->event_free);
+
+	return -EBUSY;
 }
 }
 
 
 static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
@@ -1172,12 +1180,12 @@ static void event_free(struct etnaviv_gpu *gpu, unsigned int event)
 
 
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 	spin_lock_irqsave(&gpu->event_spinlock, flags);
 
 
-	if (gpu->event[event].used == false) {
+	if (!test_bit(event, gpu->event_bitmap)) {
 		dev_warn(gpu->dev, "event %u is already marked as free",
 		dev_warn(gpu->dev, "event %u is already marked as free",
 			 event);
 			 event);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 	} else {
 	} else {
-		gpu->event[event].used = false;
+		clear_bit(event, gpu->event_bitmap);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 		spin_unlock_irqrestore(&gpu->event_spinlock, flags);
 
 
 		complete(&gpu->event_free);
 		complete(&gpu->event_free);
@@ -1311,12 +1319,71 @@ void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu)
 	pm_runtime_put_autosuspend(gpu->dev);
 	pm_runtime_put_autosuspend(gpu->dev);
 }
 }
 
 
+static void sync_point_perfmon_sample(struct etnaviv_gpu *gpu,
+	struct etnaviv_event *event, unsigned int flags)
+{
+	const struct etnaviv_cmdbuf *cmdbuf = event->cmdbuf;
+	unsigned int i;
+
+	for (i = 0; i < cmdbuf->nr_pmrs; i++) {
+		const struct etnaviv_perfmon_request *pmr = cmdbuf->pmrs + i;
+
+		if (pmr->flags == flags)
+			etnaviv_perfmon_process(gpu, pmr);
+	}
+}
+
+static void sync_point_perfmon_sample_pre(struct etnaviv_gpu *gpu,
+	struct etnaviv_event *event)
+{
+	u32 val;
+
+	/* disable clock gating */
+	val = gpu_read(gpu, VIVS_PM_POWER_CONTROLS);
+	val &= ~VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
+	gpu_write(gpu, VIVS_PM_POWER_CONTROLS, val);
+
+	/* enable debug register */
+	val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
+	val &= ~VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val);
+
+	sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_PRE);
+}
+
+static void sync_point_perfmon_sample_post(struct etnaviv_gpu *gpu,
+	struct etnaviv_event *event)
+{
+	const struct etnaviv_cmdbuf *cmdbuf = event->cmdbuf;
+	unsigned int i;
+	u32 val;
+
+	sync_point_perfmon_sample(gpu, event, ETNA_PM_PROCESS_POST);
+
+	for (i = 0; i < cmdbuf->nr_pmrs; i++) {
+		const struct etnaviv_perfmon_request *pmr = cmdbuf->pmrs + i;
+
+		*pmr->bo_vma = pmr->sequence;
+	}
+
+	/* disable debug register */
+	val = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
+	val |= VIVS_HI_CLOCK_CONTROL_DISABLE_DEBUG_REGISTERS;
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, val);
+
+	/* enable clock gating */
+	val = gpu_read(gpu, VIVS_PM_POWER_CONTROLS);
+	val |= VIVS_PM_POWER_CONTROLS_ENABLE_MODULE_CLOCK_GATING;
+	gpu_write(gpu, VIVS_PM_POWER_CONTROLS, val);
+}
+
+
 /* add bo's to gpu's ring, and kick gpu: */
 /* add bo's to gpu's ring, and kick gpu: */
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf)
 	struct etnaviv_gem_submit *submit, struct etnaviv_cmdbuf *cmdbuf)
 {
 {
 	struct dma_fence *fence;
 	struct dma_fence *fence;
-	unsigned int event, i;
+	unsigned int i, nr_events = 1, event[3];
 	int ret;
 	int ret;
 
 
 	ret = etnaviv_gpu_pm_get_sync(gpu);
 	ret = etnaviv_gpu_pm_get_sync(gpu);
@@ -1332,10 +1399,19 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 	 *
 	 *
 	 */
 	 */
 
 
-	event = event_alloc(gpu);
-	if (unlikely(event == ~0U)) {
-		DRM_ERROR("no free event\n");
-		ret = -EBUSY;
+	/*
+	 * if there are performance monitor requests we need to have
+	 * - a sync point to re-configure gpu and process ETNA_PM_PROCESS_PRE
+	 *   requests.
+	 * - a sync point to re-configure gpu, process ETNA_PM_PROCESS_POST requests
+	 *   and update the sequence number for userspace.
+	 */
+	if (cmdbuf->nr_pmrs)
+		nr_events = 3;
+
+	ret = event_alloc(gpu, nr_events, event);
+	if (ret) {
+		DRM_ERROR("no free events\n");
 		goto out_pm_put;
 		goto out_pm_put;
 	}
 	}
 
 
@@ -1343,12 +1419,14 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 
 
 	fence = etnaviv_gpu_fence_alloc(gpu);
 	fence = etnaviv_gpu_fence_alloc(gpu);
 	if (!fence) {
 	if (!fence) {
-		event_free(gpu, event);
+		for (i = 0; i < nr_events; i++)
+			event_free(gpu, event[i]);
+
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto out_unlock;
 		goto out_unlock;
 	}
 	}
 
 
-	gpu->event[event].fence = fence;
+	gpu->event[event[0]].fence = fence;
 	submit->fence = dma_fence_get(fence);
 	submit->fence = dma_fence_get(fence);
 	gpu->active_fence = submit->fence->seqno;
 	gpu->active_fence = submit->fence->seqno;
 
 
@@ -1358,7 +1436,19 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
 		gpu->lastctx = cmdbuf->ctx;
 		gpu->lastctx = cmdbuf->ctx;
 	}
 	}
 
 
-	etnaviv_buffer_queue(gpu, event, cmdbuf);
+	if (cmdbuf->nr_pmrs) {
+		gpu->event[event[1]].sync_point = &sync_point_perfmon_sample_pre;
+		gpu->event[event[1]].cmdbuf = cmdbuf;
+		etnaviv_sync_point_queue(gpu, event[1]);
+	}
+
+	etnaviv_buffer_queue(gpu, event[0], cmdbuf);
+
+	if (cmdbuf->nr_pmrs) {
+		gpu->event[event[2]].sync_point = &sync_point_perfmon_sample_post;
+		gpu->event[event[2]].cmdbuf = cmdbuf;
+		etnaviv_sync_point_queue(gpu, event[2]);
+	}
 
 
 	cmdbuf->fence = fence;
 	cmdbuf->fence = fence;
 	list_add_tail(&cmdbuf->node, &gpu->active_cmd_list);
 	list_add_tail(&cmdbuf->node, &gpu->active_cmd_list);
@@ -1394,6 +1484,24 @@ out_pm_put:
 	return ret;
 	return ret;
 }
 }
 
 
+static void etnaviv_process_sync_point(struct etnaviv_gpu *gpu,
+	struct etnaviv_event *event)
+{
+	u32 addr = gpu_read(gpu, VIVS_FE_DMA_ADDRESS);
+
+	event->sync_point(gpu, event);
+	etnaviv_gpu_start_fe(gpu, addr + 2, 2);
+}
+
+static void sync_point_worker(struct work_struct *work)
+{
+	struct etnaviv_gpu *gpu = container_of(work, struct etnaviv_gpu,
+					       sync_point_work);
+
+	etnaviv_process_sync_point(gpu, &gpu->event[gpu->sync_point_event]);
+	event_free(gpu, gpu->sync_point_event);
+}
+
 /*
 /*
  * Init/Cleanup:
  * Init/Cleanup:
  */
  */
@@ -1440,7 +1548,15 @@ static irqreturn_t irq_handler(int irq, void *data)
 
 
 			dev_dbg(gpu->dev, "event %u\n", event);
 			dev_dbg(gpu->dev, "event %u\n", event);
 
 
+			if (gpu->event[event].sync_point) {
+				gpu->sync_point_event = event;
+				etnaviv_queue_work(gpu->drm, &gpu->sync_point_work);
+			}
+
 			fence = gpu->event[event].fence;
 			fence = gpu->event[event].fence;
+			if (!fence)
+				continue;
+
 			gpu->event[event].fence = NULL;
 			gpu->event[event].fence = NULL;
 			dma_fence_signal(fence);
 			dma_fence_signal(fence);
 
 
@@ -1645,6 +1761,7 @@ static int etnaviv_gpu_bind(struct device *dev, struct device *master,
 
 
 	INIT_LIST_HEAD(&gpu->active_cmd_list);
 	INIT_LIST_HEAD(&gpu->active_cmd_list);
 	INIT_WORK(&gpu->retire_work, retire_worker);
 	INIT_WORK(&gpu->retire_work, retire_worker);
+	INIT_WORK(&gpu->sync_point_work, sync_point_worker);
 	INIT_WORK(&gpu->recover_work, recover_worker);
 	INIT_WORK(&gpu->recover_work, recover_worker);
 	init_waitqueue_head(&gpu->fence_event);
 	init_waitqueue_head(&gpu->fence_event);
 
 

+ 11 - 2
drivers/gpu/drm/etnaviv/etnaviv_gpu.h

@@ -88,13 +88,17 @@ struct etnaviv_chip_identity {
 };
 };
 
 
 struct etnaviv_event {
 struct etnaviv_event {
-	bool used;
 	struct dma_fence *fence;
 	struct dma_fence *fence;
+	struct etnaviv_cmdbuf *cmdbuf;
+
+	void (*sync_point)(struct etnaviv_gpu *gpu, struct etnaviv_event *event);
 };
 };
 
 
 struct etnaviv_cmdbuf_suballoc;
 struct etnaviv_cmdbuf_suballoc;
 struct etnaviv_cmdbuf;
 struct etnaviv_cmdbuf;
 
 
+#define ETNA_NR_EVENTS 30
+
 struct etnaviv_gpu {
 struct etnaviv_gpu {
 	struct drm_device *drm;
 	struct drm_device *drm;
 	struct thermal_cooling_device *cooling;
 	struct thermal_cooling_device *cooling;
@@ -112,7 +116,8 @@ struct etnaviv_gpu {
 	u32 memory_base;
 	u32 memory_base;
 
 
 	/* event management: */
 	/* event management: */
-	struct etnaviv_event event[30];
+	DECLARE_BITMAP(event_bitmap, ETNA_NR_EVENTS);
+	struct etnaviv_event event[ETNA_NR_EVENTS];
 	struct completion event_free;
 	struct completion event_free;
 	spinlock_t event_spinlock;
 	spinlock_t event_spinlock;
 
 
@@ -133,6 +138,10 @@ struct etnaviv_gpu {
 	/* worker for handling active-list retiring: */
 	/* worker for handling active-list retiring: */
 	struct work_struct retire_work;
 	struct work_struct retire_work;
 
 
+	/* worker for handling 'sync' points: */
+	struct work_struct sync_point_work;
+	int sync_point_event;
+
 	void __iomem *mmio;
 	void __iomem *mmio;
 	int irq;
 	int irq;
 
 

+ 73 - 124
drivers/gpu/drm/etnaviv/etnaviv_iommu.c

@@ -14,7 +14,6 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
-#include <linux/iommu.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/sizes.h>
 #include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
@@ -31,174 +30,115 @@
 
 
 #define GPU_MEM_START	0x80000000
 #define GPU_MEM_START	0x80000000
 
 
-struct etnaviv_iommu_domain_pgtable {
-	u32 *pgtable;
-	dma_addr_t paddr;
+struct etnaviv_iommuv1_domain {
+	struct etnaviv_iommu_domain base;
+	u32 *pgtable_cpu;
+	dma_addr_t pgtable_dma;
 };
 };
 
 
-struct etnaviv_iommu_domain {
-	struct iommu_domain domain;
-	struct device *dev;
-	void *bad_page_cpu;
-	dma_addr_t bad_page_dma;
-	struct etnaviv_iommu_domain_pgtable pgtable;
-	spinlock_t map_lock;
-};
-
-static struct etnaviv_iommu_domain *to_etnaviv_domain(struct iommu_domain *domain)
-{
-	return container_of(domain, struct etnaviv_iommu_domain, domain);
-}
-
-static int pgtable_alloc(struct etnaviv_iommu_domain_pgtable *pgtable,
-			 size_t size)
-{
-	pgtable->pgtable = dma_alloc_coherent(NULL, size, &pgtable->paddr, GFP_KERNEL);
-	if (!pgtable->pgtable)
-		return -ENOMEM;
-
-	return 0;
-}
-
-static void pgtable_free(struct etnaviv_iommu_domain_pgtable *pgtable,
-			 size_t size)
+static struct etnaviv_iommuv1_domain *
+to_etnaviv_domain(struct etnaviv_iommu_domain *domain)
 {
 {
-	dma_free_coherent(NULL, size, pgtable->pgtable, pgtable->paddr);
-}
-
-static u32 pgtable_read(struct etnaviv_iommu_domain_pgtable *pgtable,
-			   unsigned long iova)
-{
-	/* calcuate index into page table */
-	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
-	phys_addr_t paddr;
-
-	paddr = pgtable->pgtable[index];
-
-	return paddr;
+	return container_of(domain, struct etnaviv_iommuv1_domain, base);
 }
 }
 
 
-static void pgtable_write(struct etnaviv_iommu_domain_pgtable *pgtable,
-			  unsigned long iova, phys_addr_t paddr)
-{
-	/* calcuate index into page table */
-	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
-
-	pgtable->pgtable[index] = paddr;
-}
-
-static int __etnaviv_iommu_init(struct etnaviv_iommu_domain *etnaviv_domain)
+static int __etnaviv_iommu_init(struct etnaviv_iommuv1_domain *etnaviv_domain)
 {
 {
 	u32 *p;
 	u32 *p;
-	int ret, i;
-
-	etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
-						  SZ_4K,
-						  &etnaviv_domain->bad_page_dma,
-						  GFP_KERNEL);
-	if (!etnaviv_domain->bad_page_cpu)
+	int i;
+
+	etnaviv_domain->base.bad_page_cpu = dma_alloc_coherent(
+						etnaviv_domain->base.dev,
+						SZ_4K,
+						&etnaviv_domain->base.bad_page_dma,
+						GFP_KERNEL);
+	if (!etnaviv_domain->base.bad_page_cpu)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	p = etnaviv_domain->bad_page_cpu;
+	p = etnaviv_domain->base.bad_page_cpu;
 	for (i = 0; i < SZ_4K / 4; i++)
 	for (i = 0; i < SZ_4K / 4; i++)
 		*p++ = 0xdead55aa;
 		*p++ = 0xdead55aa;
 
 
-	ret = pgtable_alloc(&etnaviv_domain->pgtable, PT_SIZE);
-	if (ret < 0) {
-		dma_free_coherent(etnaviv_domain->dev, SZ_4K,
-				  etnaviv_domain->bad_page_cpu,
-				  etnaviv_domain->bad_page_dma);
-		return ret;
+	etnaviv_domain->pgtable_cpu =
+			dma_alloc_coherent(etnaviv_domain->base.dev, PT_SIZE,
+					   &etnaviv_domain->pgtable_dma,
+					   GFP_KERNEL);
+	if (!etnaviv_domain->pgtable_cpu) {
+		dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
+				  etnaviv_domain->base.bad_page_cpu,
+				  etnaviv_domain->base.bad_page_dma);
+		return -ENOMEM;
 	}
 	}
 
 
 	for (i = 0; i < PT_ENTRIES; i++)
 	for (i = 0; i < PT_ENTRIES; i++)
-		etnaviv_domain->pgtable.pgtable[i] =
-			etnaviv_domain->bad_page_dma;
-
-	spin_lock_init(&etnaviv_domain->map_lock);
+		etnaviv_domain->pgtable_cpu[i] =
+				etnaviv_domain->base.bad_page_dma;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static void etnaviv_domain_free(struct iommu_domain *domain)
+static void etnaviv_iommuv1_domain_free(struct etnaviv_iommu_domain *domain)
 {
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+	struct etnaviv_iommuv1_domain *etnaviv_domain =
+			to_etnaviv_domain(domain);
 
 
-	pgtable_free(&etnaviv_domain->pgtable, PT_SIZE);
+	dma_free_coherent(etnaviv_domain->base.dev, PT_SIZE,
+			  etnaviv_domain->pgtable_cpu,
+			  etnaviv_domain->pgtable_dma);
 
 
-	dma_free_coherent(etnaviv_domain->dev, SZ_4K,
-			  etnaviv_domain->bad_page_cpu,
-			  etnaviv_domain->bad_page_dma);
+	dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
+			  etnaviv_domain->base.bad_page_cpu,
+			  etnaviv_domain->base.bad_page_dma);
 
 
 	kfree(etnaviv_domain);
 	kfree(etnaviv_domain);
 }
 }
 
 
-static int etnaviv_iommuv1_map(struct iommu_domain *domain, unsigned long iova,
-	   phys_addr_t paddr, size_t size, int prot)
+static int etnaviv_iommuv1_map(struct etnaviv_iommu_domain *domain,
+			       unsigned long iova, phys_addr_t paddr,
+			       size_t size, int prot)
 {
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+	struct etnaviv_iommuv1_domain *etnaviv_domain = to_etnaviv_domain(domain);
+	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
 
 
 	if (size != SZ_4K)
 	if (size != SZ_4K)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	spin_lock(&etnaviv_domain->map_lock);
-	pgtable_write(&etnaviv_domain->pgtable, iova, paddr);
-	spin_unlock(&etnaviv_domain->map_lock);
+	etnaviv_domain->pgtable_cpu[index] = paddr;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static size_t etnaviv_iommuv1_unmap(struct iommu_domain *domain,
+static size_t etnaviv_iommuv1_unmap(struct etnaviv_iommu_domain *domain,
 	unsigned long iova, size_t size)
 	unsigned long iova, size_t size)
 {
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+	struct etnaviv_iommuv1_domain *etnaviv_domain =
+			to_etnaviv_domain(domain);
+	unsigned int index = (iova - GPU_MEM_START) / SZ_4K;
 
 
 	if (size != SZ_4K)
 	if (size != SZ_4K)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	spin_lock(&etnaviv_domain->map_lock);
-	pgtable_write(&etnaviv_domain->pgtable, iova,
-		      etnaviv_domain->bad_page_dma);
-	spin_unlock(&etnaviv_domain->map_lock);
+	etnaviv_domain->pgtable_cpu[index] = etnaviv_domain->base.bad_page_dma;
 
 
 	return SZ_4K;
 	return SZ_4K;
 }
 }
 
 
-static phys_addr_t etnaviv_iommu_iova_to_phys(struct iommu_domain *domain,
-	dma_addr_t iova)
-{
-	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
-
-	return pgtable_read(&etnaviv_domain->pgtable, iova);
-}
-
-static size_t etnaviv_iommuv1_dump_size(struct iommu_domain *domain)
+static size_t etnaviv_iommuv1_dump_size(struct etnaviv_iommu_domain *domain)
 {
 {
 	return PT_SIZE;
 	return PT_SIZE;
 }
 }
 
 
-static void etnaviv_iommuv1_dump(struct iommu_domain *domain, void *buf)
+static void etnaviv_iommuv1_dump(struct etnaviv_iommu_domain *domain, void *buf)
 {
 {
-	struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+	struct etnaviv_iommuv1_domain *etnaviv_domain =
+			to_etnaviv_domain(domain);
 
 
-	memcpy(buf, etnaviv_domain->pgtable.pgtable, PT_SIZE);
+	memcpy(buf, etnaviv_domain->pgtable_cpu, PT_SIZE);
 }
 }
 
 
-static const struct etnaviv_iommu_ops etnaviv_iommu_ops = {
-	.ops = {
-		.domain_free = etnaviv_domain_free,
-		.map = etnaviv_iommuv1_map,
-		.unmap = etnaviv_iommuv1_unmap,
-		.iova_to_phys = etnaviv_iommu_iova_to_phys,
-		.pgsize_bitmap = SZ_4K,
-	},
-	.dump_size = etnaviv_iommuv1_dump_size,
-	.dump = etnaviv_iommuv1_dump,
-};
-
 void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
 void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
 {
 {
-	struct etnaviv_iommu_domain *etnaviv_domain =
+	struct etnaviv_iommuv1_domain *etnaviv_domain =
 			to_etnaviv_domain(gpu->mmu->domain);
 			to_etnaviv_domain(gpu->mmu->domain);
 	u32 pgtable;
 	u32 pgtable;
 
 
@@ -210,7 +150,7 @@ void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
 	gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
 
 
 	/* set page table address in MC */
 	/* set page table address in MC */
-	pgtable = (u32)etnaviv_domain->pgtable.paddr;
+	pgtable = (u32)etnaviv_domain->pgtable_dma;
 
 
 	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_FE_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_TX_PAGE_TABLE, pgtable);
@@ -219,28 +159,37 @@ void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
 	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
 	gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
 }
 }
 
 
-struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu)
+const struct etnaviv_iommu_domain_ops etnaviv_iommuv1_ops = {
+	.free = etnaviv_iommuv1_domain_free,
+	.map = etnaviv_iommuv1_map,
+	.unmap = etnaviv_iommuv1_unmap,
+	.dump_size = etnaviv_iommuv1_dump_size,
+	.dump = etnaviv_iommuv1_dump,
+};
+
+struct etnaviv_iommu_domain *
+etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu)
 {
 {
-	struct etnaviv_iommu_domain *etnaviv_domain;
+	struct etnaviv_iommuv1_domain *etnaviv_domain;
+	struct etnaviv_iommu_domain *domain;
 	int ret;
 	int ret;
 
 
 	etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
 	etnaviv_domain = kzalloc(sizeof(*etnaviv_domain), GFP_KERNEL);
 	if (!etnaviv_domain)
 	if (!etnaviv_domain)
 		return NULL;
 		return NULL;
 
 
-	etnaviv_domain->dev = gpu->dev;
+	domain = &etnaviv_domain->base;
 
 
-	etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
-	etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops;
-	etnaviv_domain->domain.pgsize_bitmap = SZ_4K;
-	etnaviv_domain->domain.geometry.aperture_start = GPU_MEM_START;
-	etnaviv_domain->domain.geometry.aperture_end = GPU_MEM_START + PT_ENTRIES * SZ_4K - 1;
+	domain->dev = gpu->dev;
+	domain->base = GPU_MEM_START;
+	domain->size = PT_ENTRIES * SZ_4K;
+	domain->ops = &etnaviv_iommuv1_ops;
 
 
 	ret = __etnaviv_iommu_init(etnaviv_domain);
 	ret = __etnaviv_iommu_init(etnaviv_domain);
 	if (ret)
 	if (ret)
 		goto out_free;
 		goto out_free;
 
 
-	return &etnaviv_domain->domain;
+	return &etnaviv_domain->base;
 
 
 out_free:
 out_free:
 	kfree(etnaviv_domain);
 	kfree(etnaviv_domain);

+ 5 - 2
drivers/gpu/drm/etnaviv/etnaviv_iommu.h

@@ -18,11 +18,14 @@
 #define __ETNAVIV_IOMMU_H__
 #define __ETNAVIV_IOMMU_H__
 
 
 struct etnaviv_gpu;
 struct etnaviv_gpu;
+struct etnaviv_iommu_domain;
 
 
-struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu);
+struct etnaviv_iommu_domain *
+etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu);
 void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu);
 void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu);
 
 
-struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu);
+struct etnaviv_iommu_domain *
+etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu);
 void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu);
 void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu);
 
 
 #endif /* __ETNAVIV_IOMMU_H__ */
 #endif /* __ETNAVIV_IOMMU_H__ */

+ 52 - 68
drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c

@@ -14,7 +14,6 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
  */
 
 
-#include <linux/iommu.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/sizes.h>
 #include <linux/sizes.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
@@ -40,10 +39,7 @@
 #define MMUv2_MAX_STLB_ENTRIES		1024
 #define MMUv2_MAX_STLB_ENTRIES		1024
 
 
 struct etnaviv_iommuv2_domain {
 struct etnaviv_iommuv2_domain {
-	struct iommu_domain domain;
-	struct device *dev;
-	void *bad_page_cpu;
-	dma_addr_t bad_page_dma;
+	struct etnaviv_iommu_domain base;
 	/* M(aster) TLB aka first level pagetable */
 	/* M(aster) TLB aka first level pagetable */
 	u32 *mtlb_cpu;
 	u32 *mtlb_cpu;
 	dma_addr_t mtlb_dma;
 	dma_addr_t mtlb_dma;
@@ -52,13 +48,15 @@ struct etnaviv_iommuv2_domain {
 	dma_addr_t stlb_dma[1024];
 	dma_addr_t stlb_dma[1024];
 };
 };
 
 
-static struct etnaviv_iommuv2_domain *to_etnaviv_domain(struct iommu_domain *domain)
+static struct etnaviv_iommuv2_domain *
+to_etnaviv_domain(struct etnaviv_iommu_domain *domain)
 {
 {
-	return container_of(domain, struct etnaviv_iommuv2_domain, domain);
+	return container_of(domain, struct etnaviv_iommuv2_domain, base);
 }
 }
 
 
-static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
-	   phys_addr_t paddr, size_t size, int prot)
+static int etnaviv_iommuv2_map(struct etnaviv_iommu_domain *domain,
+			       unsigned long iova, phys_addr_t paddr,
+			       size_t size, int prot)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 			to_etnaviv_domain(domain);
 			to_etnaviv_domain(domain);
@@ -68,7 +66,7 @@ static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
 	if (size != SZ_4K)
 	if (size != SZ_4K)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (prot & IOMMU_WRITE)
+	if (prot & ETNAVIV_PROT_WRITE)
 		entry |= MMUv2_PTE_WRITEABLE;
 		entry |= MMUv2_PTE_WRITEABLE;
 
 
 	mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
 	mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
@@ -79,8 +77,8 @@ static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
 	return 0;
 	return 0;
 }
 }
 
 
-static size_t etnaviv_iommuv2_unmap(struct iommu_domain *domain,
-	unsigned long iova, size_t size)
+static size_t etnaviv_iommuv2_unmap(struct etnaviv_iommu_domain *domain,
+				    unsigned long iova, size_t size)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 			to_etnaviv_domain(domain);
 			to_etnaviv_domain(domain);
@@ -97,38 +95,26 @@ static size_t etnaviv_iommuv2_unmap(struct iommu_domain *domain,
 	return SZ_4K;
 	return SZ_4K;
 }
 }
 
 
-static phys_addr_t etnaviv_iommuv2_iova_to_phys(struct iommu_domain *domain,
-	dma_addr_t iova)
-{
-	struct etnaviv_iommuv2_domain *etnaviv_domain =
-			to_etnaviv_domain(domain);
-	int mtlb_entry, stlb_entry;
-
-	mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
-	stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT;
-
-	return etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] & ~(SZ_4K - 1);
-}
-
 static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
 static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
 {
 {
 	u32 *p;
 	u32 *p;
 	int ret, i, j;
 	int ret, i, j;
 
 
 	/* allocate scratch page */
 	/* allocate scratch page */
-	etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
-						  SZ_4K,
-						  &etnaviv_domain->bad_page_dma,
-						  GFP_KERNEL);
-	if (!etnaviv_domain->bad_page_cpu) {
+	etnaviv_domain->base.bad_page_cpu = dma_alloc_coherent(
+						etnaviv_domain->base.dev,
+						SZ_4K,
+						&etnaviv_domain->base.bad_page_dma,
+						GFP_KERNEL);
+	if (!etnaviv_domain->base.bad_page_cpu) {
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto fail_mem;
 		goto fail_mem;
 	}
 	}
-	p = etnaviv_domain->bad_page_cpu;
+	p = etnaviv_domain->base.bad_page_cpu;
 	for (i = 0; i < SZ_4K / 4; i++)
 	for (i = 0; i < SZ_4K / 4; i++)
 		*p++ = 0xdead55aa;
 		*p++ = 0xdead55aa;
 
 
-	etnaviv_domain->mtlb_cpu = dma_alloc_coherent(etnaviv_domain->dev,
+	etnaviv_domain->mtlb_cpu = dma_alloc_coherent(etnaviv_domain->base.dev,
 						  SZ_4K,
 						  SZ_4K,
 						  &etnaviv_domain->mtlb_dma,
 						  &etnaviv_domain->mtlb_dma,
 						  GFP_KERNEL);
 						  GFP_KERNEL);
@@ -140,7 +126,7 @@ static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
 	/* pre-populate STLB pages (may want to switch to on-demand later) */
 	/* pre-populate STLB pages (may want to switch to on-demand later) */
 	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
 	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
 		etnaviv_domain->stlb_cpu[i] =
 		etnaviv_domain->stlb_cpu[i] =
-				dma_alloc_coherent(etnaviv_domain->dev,
+				dma_alloc_coherent(etnaviv_domain->base.dev,
 						   SZ_4K,
 						   SZ_4K,
 						   &etnaviv_domain->stlb_dma[i],
 						   &etnaviv_domain->stlb_dma[i],
 						   GFP_KERNEL);
 						   GFP_KERNEL);
@@ -159,19 +145,19 @@ static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
 	return 0;
 	return 0;
 
 
 fail_mem:
 fail_mem:
-	if (etnaviv_domain->bad_page_cpu)
-		dma_free_coherent(etnaviv_domain->dev, SZ_4K,
-				  etnaviv_domain->bad_page_cpu,
-				  etnaviv_domain->bad_page_dma);
+	if (etnaviv_domain->base.bad_page_cpu)
+		dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
+				  etnaviv_domain->base.bad_page_cpu,
+				  etnaviv_domain->base.bad_page_dma);
 
 
 	if (etnaviv_domain->mtlb_cpu)
 	if (etnaviv_domain->mtlb_cpu)
-		dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+		dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
 				  etnaviv_domain->mtlb_cpu,
 				  etnaviv_domain->mtlb_cpu,
 				  etnaviv_domain->mtlb_dma);
 				  etnaviv_domain->mtlb_dma);
 
 
 	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
 	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
 		if (etnaviv_domain->stlb_cpu[i])
 		if (etnaviv_domain->stlb_cpu[i])
-			dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+			dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
 					  etnaviv_domain->stlb_cpu[i],
 					  etnaviv_domain->stlb_cpu[i],
 					  etnaviv_domain->stlb_dma[i]);
 					  etnaviv_domain->stlb_dma[i]);
 	}
 	}
@@ -179,23 +165,23 @@ fail_mem:
 	return ret;
 	return ret;
 }
 }
 
 
-static void etnaviv_iommuv2_domain_free(struct iommu_domain *domain)
+static void etnaviv_iommuv2_domain_free(struct etnaviv_iommu_domain *domain)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 			to_etnaviv_domain(domain);
 			to_etnaviv_domain(domain);
 	int i;
 	int i;
 
 
-	dma_free_coherent(etnaviv_domain->dev, SZ_4K,
-			  etnaviv_domain->bad_page_cpu,
-			  etnaviv_domain->bad_page_dma);
+	dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
+			  etnaviv_domain->base.bad_page_cpu,
+			  etnaviv_domain->base.bad_page_dma);
 
 
-	dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+	dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
 			  etnaviv_domain->mtlb_cpu,
 			  etnaviv_domain->mtlb_cpu,
 			  etnaviv_domain->mtlb_dma);
 			  etnaviv_domain->mtlb_dma);
 
 
 	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
 	for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
 		if (etnaviv_domain->stlb_cpu[i])
 		if (etnaviv_domain->stlb_cpu[i])
-			dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+			dma_free_coherent(etnaviv_domain->base.dev, SZ_4K,
 					  etnaviv_domain->stlb_cpu[i],
 					  etnaviv_domain->stlb_cpu[i],
 					  etnaviv_domain->stlb_dma[i]);
 					  etnaviv_domain->stlb_dma[i]);
 	}
 	}
@@ -203,7 +189,7 @@ static void etnaviv_iommuv2_domain_free(struct iommu_domain *domain)
 	vfree(etnaviv_domain);
 	vfree(etnaviv_domain);
 }
 }
 
 
-static size_t etnaviv_iommuv2_dump_size(struct iommu_domain *domain)
+static size_t etnaviv_iommuv2_dump_size(struct etnaviv_iommu_domain *domain)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 			to_etnaviv_domain(domain);
 			to_etnaviv_domain(domain);
@@ -217,7 +203,7 @@ static size_t etnaviv_iommuv2_dump_size(struct iommu_domain *domain)
 	return dump_size;
 	return dump_size;
 }
 }
 
 
-static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf)
+static void etnaviv_iommuv2_dump(struct etnaviv_iommu_domain *domain, void *buf)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 			to_etnaviv_domain(domain);
 			to_etnaviv_domain(domain);
@@ -230,18 +216,6 @@ static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf)
 			memcpy(buf, etnaviv_domain->stlb_cpu[i], SZ_4K);
 			memcpy(buf, etnaviv_domain->stlb_cpu[i], SZ_4K);
 }
 }
 
 
-static const struct etnaviv_iommu_ops etnaviv_iommu_ops = {
-	.ops = {
-		.domain_free = etnaviv_iommuv2_domain_free,
-		.map = etnaviv_iommuv2_map,
-		.unmap = etnaviv_iommuv2_unmap,
-		.iova_to_phys = etnaviv_iommuv2_iova_to_phys,
-		.pgsize_bitmap = SZ_4K,
-	},
-	.dump_size = etnaviv_iommuv2_dump_size,
-	.dump = etnaviv_iommuv2_dump,
-};
-
 void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu)
 void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
 	struct etnaviv_iommuv2_domain *etnaviv_domain =
@@ -254,35 +228,45 @@ void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu)
 
 
 	prefetch = etnaviv_buffer_config_mmuv2(gpu,
 	prefetch = etnaviv_buffer_config_mmuv2(gpu,
 				(u32)etnaviv_domain->mtlb_dma,
 				(u32)etnaviv_domain->mtlb_dma,
-				(u32)etnaviv_domain->bad_page_dma);
+				(u32)etnaviv_domain->base.bad_page_dma);
 	etnaviv_gpu_start_fe(gpu, (u32)etnaviv_cmdbuf_get_pa(gpu->buffer),
 	etnaviv_gpu_start_fe(gpu, (u32)etnaviv_cmdbuf_get_pa(gpu->buffer),
 			     prefetch);
 			     prefetch);
 	etnaviv_gpu_wait_idle(gpu, 100);
 	etnaviv_gpu_wait_idle(gpu, 100);
 
 
 	gpu_write(gpu, VIVS_MMUv2_CONTROL, VIVS_MMUv2_CONTROL_ENABLE);
 	gpu_write(gpu, VIVS_MMUv2_CONTROL, VIVS_MMUv2_CONTROL_ENABLE);
 }
 }
-struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu)
+
+const struct etnaviv_iommu_domain_ops etnaviv_iommuv2_ops = {
+	.free = etnaviv_iommuv2_domain_free,
+	.map = etnaviv_iommuv2_map,
+	.unmap = etnaviv_iommuv2_unmap,
+	.dump_size = etnaviv_iommuv2_dump_size,
+	.dump = etnaviv_iommuv2_dump,
+};
+
+struct etnaviv_iommu_domain *
+etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu)
 {
 {
 	struct etnaviv_iommuv2_domain *etnaviv_domain;
 	struct etnaviv_iommuv2_domain *etnaviv_domain;
+	struct etnaviv_iommu_domain *domain;
 	int ret;
 	int ret;
 
 
 	etnaviv_domain = vzalloc(sizeof(*etnaviv_domain));
 	etnaviv_domain = vzalloc(sizeof(*etnaviv_domain));
 	if (!etnaviv_domain)
 	if (!etnaviv_domain)
 		return NULL;
 		return NULL;
 
 
-	etnaviv_domain->dev = gpu->dev;
+	domain = &etnaviv_domain->base;
 
 
-	etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
-	etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops;
-	etnaviv_domain->domain.pgsize_bitmap = SZ_4K;
-	etnaviv_domain->domain.geometry.aperture_start = 0;
-	etnaviv_domain->domain.geometry.aperture_end = ~0UL & ~(SZ_4K - 1);
+	domain->dev = gpu->dev;
+	domain->base = 0;
+	domain->size = (u64)SZ_1G * 4;
+	domain->ops = &etnaviv_iommuv2_ops;
 
 
 	ret = etnaviv_iommuv2_init(etnaviv_domain);
 	ret = etnaviv_iommuv2_init(etnaviv_domain);
 	if (ret)
 	if (ret)
 		goto out_free;
 		goto out_free;
 
 
-	return &etnaviv_domain->domain;
+	return &etnaviv_domain->base;
 
 
 out_free:
 out_free:
 	vfree(etnaviv_domain);
 	vfree(etnaviv_domain);

+ 68 - 38
drivers/gpu/drm/etnaviv/etnaviv_mmu.c

@@ -22,17 +22,64 @@
 #include "etnaviv_iommu.h"
 #include "etnaviv_iommu.h"
 #include "etnaviv_mmu.h"
 #include "etnaviv_mmu.h"
 
 
-static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
-		unsigned long iova, int flags, void *arg)
+static void etnaviv_domain_unmap(struct etnaviv_iommu_domain *domain,
+				 unsigned long iova, size_t size)
 {
 {
-	DBG("*** fault: iova=%08lx, flags=%d", iova, flags);
-	return 0;
+	size_t unmapped_page, unmapped = 0;
+	size_t pgsize = SZ_4K;
+
+	if (!IS_ALIGNED(iova | size, pgsize)) {
+		pr_err("unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
+		       iova, size, pgsize);
+		return;
+	}
+
+	while (unmapped < size) {
+		unmapped_page = domain->ops->unmap(domain, iova, pgsize);
+		if (!unmapped_page)
+			break;
+
+		iova += unmapped_page;
+		unmapped += unmapped_page;
+	}
 }
 }
 
 
-int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
-		struct sg_table *sgt, unsigned len, int prot)
+static int etnaviv_domain_map(struct etnaviv_iommu_domain *domain,
+			      unsigned long iova, phys_addr_t paddr,
+			      size_t size, int prot)
 {
 {
-	struct iommu_domain *domain = iommu->domain;
+	unsigned long orig_iova = iova;
+	size_t pgsize = SZ_4K;
+	size_t orig_size = size;
+	int ret = 0;
+
+	if (!IS_ALIGNED(iova | paddr | size, pgsize)) {
+		pr_err("unaligned: iova 0x%lx pa %pa size 0x%zx min_pagesz 0x%x\n",
+		       iova, &paddr, size, pgsize);
+		return -EINVAL;
+	}
+
+	while (size) {
+		ret = domain->ops->map(domain, iova, paddr, pgsize, prot);
+		if (ret)
+			break;
+
+		iova += pgsize;
+		paddr += pgsize;
+		size -= pgsize;
+	}
+
+	/* unroll mapping in case something went wrong */
+	if (ret)
+		etnaviv_domain_unmap(domain, orig_iova, orig_size - size);
+
+	return ret;
+}
+
+static int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
+			     struct sg_table *sgt, unsigned len, int prot)
+{
+	struct etnaviv_iommu_domain *domain = iommu->domain;
 	struct scatterlist *sg;
 	struct scatterlist *sg;
 	unsigned int da = iova;
 	unsigned int da = iova;
 	unsigned int i, j;
 	unsigned int i, j;
@@ -47,7 +94,7 @@ int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
 
 
 		VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
 		VERB("map[%d]: %08x %08x(%zx)", i, iova, pa, bytes);
 
 
-		ret = iommu_map(domain, da, pa, bytes, prot);
+		ret = etnaviv_domain_map(domain, da, pa, bytes, prot);
 		if (ret)
 		if (ret)
 			goto fail;
 			goto fail;
 
 
@@ -62,27 +109,24 @@ fail:
 	for_each_sg(sgt->sgl, sg, i, j) {
 	for_each_sg(sgt->sgl, sg, i, j) {
 		size_t bytes = sg_dma_len(sg) + sg->offset;
 		size_t bytes = sg_dma_len(sg) + sg->offset;
 
 
-		iommu_unmap(domain, da, bytes);
+		etnaviv_domain_unmap(domain, da, bytes);
 		da += bytes;
 		da += bytes;
 	}
 	}
 	return ret;
 	return ret;
 }
 }
 
 
-int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
-		struct sg_table *sgt, unsigned len)
+static void etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
+				struct sg_table *sgt, unsigned len)
 {
 {
-	struct iommu_domain *domain = iommu->domain;
+	struct etnaviv_iommu_domain *domain = iommu->domain;
 	struct scatterlist *sg;
 	struct scatterlist *sg;
 	unsigned int da = iova;
 	unsigned int da = iova;
 	int i;
 	int i;
 
 
 	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
 	for_each_sg(sgt->sgl, sg, sgt->nents, i) {
 		size_t bytes = sg_dma_len(sg) + sg->offset;
 		size_t bytes = sg_dma_len(sg) + sg->offset;
-		size_t unmapped;
 
 
-		unmapped = iommu_unmap(domain, da, bytes);
-		if (unmapped < bytes)
-			return unmapped;
+		etnaviv_domain_unmap(domain, da, bytes);
 
 
 		VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
 		VERB("unmap[%d]: %08x(%zx)", i, iova, bytes);
 
 
@@ -90,8 +134,6 @@ int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
 
 
 		da += bytes;
 		da += bytes;
 	}
 	}
-
-	return 0;
 }
 }
 
 
 static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu,
 static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu,
@@ -237,7 +279,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	mmu->last_iova = node->start + etnaviv_obj->base.size;
 	mmu->last_iova = node->start + etnaviv_obj->base.size;
 	mapping->iova = node->start;
 	mapping->iova = node->start;
 	ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size,
 	ret = etnaviv_iommu_map(mmu, node->start, sgt, etnaviv_obj->base.size,
-				IOMMU_READ | IOMMU_WRITE);
+				ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE);
 
 
 	if (ret < 0) {
 	if (ret < 0) {
 		drm_mm_remove_node(node);
 		drm_mm_remove_node(node);
@@ -271,7 +313,7 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
 void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
 {
 {
 	drm_mm_takedown(&mmu->mm);
 	drm_mm_takedown(&mmu->mm);
-	iommu_domain_free(mmu->domain);
+	mmu->domain->ops->free(mmu->domain);
 	kfree(mmu);
 	kfree(mmu);
 }
 }
 
 
@@ -303,11 +345,7 @@ struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu)
 	mutex_init(&mmu->lock);
 	mutex_init(&mmu->lock);
 	INIT_LIST_HEAD(&mmu->mappings);
 	INIT_LIST_HEAD(&mmu->mappings);
 
 
-	drm_mm_init(&mmu->mm, mmu->domain->geometry.aperture_start,
-		    mmu->domain->geometry.aperture_end -
-		    mmu->domain->geometry.aperture_start + 1);
-
-	iommu_set_fault_handler(mmu->domain, etnaviv_fault_handler, gpu->dev);
+	drm_mm_init(&mmu->mm, mmu->domain->base, mmu->domain->size);
 
 
 	return mmu;
 	return mmu;
 }
 }
@@ -338,8 +376,8 @@ int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr,
 			mutex_unlock(&mmu->lock);
 			mutex_unlock(&mmu->lock);
 			return ret;
 			return ret;
 		}
 		}
-		ret = iommu_map(mmu->domain, vram_node->start, paddr, size,
-				IOMMU_READ);
+		ret = etnaviv_domain_map(mmu->domain, vram_node->start, paddr,
+					 size, ETNAVIV_PROT_READ);
 		if (ret < 0) {
 		if (ret < 0) {
 			drm_mm_remove_node(vram_node);
 			drm_mm_remove_node(vram_node);
 			mutex_unlock(&mmu->lock);
 			mutex_unlock(&mmu->lock);
@@ -362,25 +400,17 @@ void etnaviv_iommu_put_suballoc_va(struct etnaviv_gpu *gpu,
 
 
 	if (mmu->version == ETNAVIV_IOMMU_V2) {
 	if (mmu->version == ETNAVIV_IOMMU_V2) {
 		mutex_lock(&mmu->lock);
 		mutex_lock(&mmu->lock);
-		iommu_unmap(mmu->domain,iova, size);
+		etnaviv_domain_unmap(mmu->domain, iova, size);
 		drm_mm_remove_node(vram_node);
 		drm_mm_remove_node(vram_node);
 		mutex_unlock(&mmu->lock);
 		mutex_unlock(&mmu->lock);
 	}
 	}
 }
 }
 size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu)
 size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu)
 {
 {
-	struct etnaviv_iommu_ops *ops;
-
-	ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops);
-
-	return ops->dump_size(iommu->domain);
+	return iommu->domain->ops->dump_size(iommu->domain);
 }
 }
 
 
 void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf)
 void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf)
 {
 {
-	struct etnaviv_iommu_ops *ops;
-
-	ops = container_of(iommu->domain->ops, struct etnaviv_iommu_ops, ops);
-
-	ops->dump(iommu->domain, buf);
+	iommu->domain->ops->dump(iommu->domain, buf);
 }
 }

+ 23 - 13
drivers/gpu/drm/etnaviv/etnaviv_mmu.h

@@ -17,7 +17,8 @@
 #ifndef __ETNAVIV_MMU_H__
 #ifndef __ETNAVIV_MMU_H__
 #define __ETNAVIV_MMU_H__
 #define __ETNAVIV_MMU_H__
 
 
-#include <linux/iommu.h>
+#define ETNAVIV_PROT_READ	(1 << 0)
+#define ETNAVIV_PROT_WRITE	(1 << 1)
 
 
 enum etnaviv_iommu_version {
 enum etnaviv_iommu_version {
 	ETNAVIV_IOMMU_V1 = 0,
 	ETNAVIV_IOMMU_V1 = 0,
@@ -26,16 +27,31 @@ enum etnaviv_iommu_version {
 
 
 struct etnaviv_gpu;
 struct etnaviv_gpu;
 struct etnaviv_vram_mapping;
 struct etnaviv_vram_mapping;
+struct etnaviv_iommu_domain;
 
 
-struct etnaviv_iommu_ops {
-	struct iommu_ops ops;
-	size_t (*dump_size)(struct iommu_domain *);
-	void (*dump)(struct iommu_domain *, void *);
+struct etnaviv_iommu_domain_ops {
+	void (*free)(struct etnaviv_iommu_domain *);
+	int (*map)(struct etnaviv_iommu_domain *domain, unsigned long iova,
+		   phys_addr_t paddr, size_t size, int prot);
+	size_t (*unmap)(struct etnaviv_iommu_domain *domain, unsigned long iova,
+			size_t size);
+	size_t (*dump_size)(struct etnaviv_iommu_domain *);
+	void (*dump)(struct etnaviv_iommu_domain *, void *);
+};
+
+struct etnaviv_iommu_domain {
+	struct device *dev;
+	void *bad_page_cpu;
+	dma_addr_t bad_page_dma;
+	u64 base;
+	u64 size;
+
+	const struct etnaviv_iommu_domain_ops *ops;
 };
 };
 
 
 struct etnaviv_iommu {
 struct etnaviv_iommu {
 	struct etnaviv_gpu *gpu;
 	struct etnaviv_gpu *gpu;
-	struct iommu_domain *domain;
+	struct etnaviv_iommu_domain *domain;
 
 
 	enum etnaviv_iommu_version version;
 	enum etnaviv_iommu_version version;
 
 
@@ -49,18 +65,11 @@ struct etnaviv_iommu {
 
 
 struct etnaviv_gem_object;
 struct etnaviv_gem_object;
 
 
-int etnaviv_iommu_attach(struct etnaviv_iommu *iommu, const char **names,
-	int cnt);
-int etnaviv_iommu_map(struct etnaviv_iommu *iommu, u32 iova,
-	struct sg_table *sgt, unsigned len, int prot);
-int etnaviv_iommu_unmap(struct etnaviv_iommu *iommu, u32 iova,
-	struct sg_table *sgt, unsigned len);
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
 	struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
 	struct etnaviv_vram_mapping *mapping);
 	struct etnaviv_vram_mapping *mapping);
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
 	struct etnaviv_vram_mapping *mapping);
 	struct etnaviv_vram_mapping *mapping);
-void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
 
 
 int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr,
 int etnaviv_iommu_get_suballoc_va(struct etnaviv_gpu *gpu, dma_addr_t paddr,
 				  struct drm_mm_node *vram_node, size_t size,
 				  struct drm_mm_node *vram_node, size_t size,
@@ -73,6 +82,7 @@ size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu);
 void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf);
 void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf);
 
 
 struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu);
 struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu);
+void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
 void etnaviv_iommu_restore(struct etnaviv_gpu *gpu);
 void etnaviv_iommu_restore(struct etnaviv_gpu *gpu);
 
 
 #endif /* __ETNAVIV_MMU_H__ */
 #endif /* __ETNAVIV_MMU_H__ */

+ 495 - 0
drivers/gpu/drm/etnaviv/etnaviv_perfmon.c

@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2017 Etnaviv Project
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "etnaviv_gpu.h"
+#include "etnaviv_perfmon.h"
+#include "state_hi.xml.h"
+
+struct etnaviv_pm_domain;
+
+struct etnaviv_pm_signal {
+	char name[64];
+	u32 data;
+
+	u32 (*sample)(struct etnaviv_gpu *gpu,
+	              const struct etnaviv_pm_domain *domain,
+	              const struct etnaviv_pm_signal *signal);
+};
+
+struct etnaviv_pm_domain {
+	char name[64];
+
+	/* profile register */
+	u32 profile_read;
+	u32 profile_config;
+
+	u8 nr_signals;
+	const struct etnaviv_pm_signal *signal;
+};
+
+struct etnaviv_pm_domain_meta {
+	const struct etnaviv_pm_domain *domains;
+	u32 nr_domains;
+};
+
+static u32 simple_reg_read(struct etnaviv_gpu *gpu,
+	const struct etnaviv_pm_domain *domain,
+	const struct etnaviv_pm_signal *signal)
+{
+	return gpu_read(gpu, signal->data);
+}
+
+static u32 perf_reg_read(struct etnaviv_gpu *gpu,
+	const struct etnaviv_pm_domain *domain,
+	const struct etnaviv_pm_signal *signal)
+{
+	gpu_write(gpu, domain->profile_config, signal->data);
+
+	return gpu_read(gpu, domain->profile_read);
+}
+
+static u32 pipe_reg_read(struct etnaviv_gpu *gpu,
+	const struct etnaviv_pm_domain *domain,
+	const struct etnaviv_pm_signal *signal)
+{
+	u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
+	u32 value = 0;
+	unsigned i;
+
+	for (i = 0; i < gpu->identity.pixel_pipes; i++) {
+		clock &= ~(VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK);
+		clock |= VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(i);
+		gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
+		gpu_write(gpu, domain->profile_config, signal->data);
+		value += gpu_read(gpu, domain->profile_read);
+	}
+
+	/* switch back to pixel pipe 0 to prevent GPU hang */
+	clock &= ~(VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK);
+	clock |= VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(0);
+	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
+
+	return value;
+}
+
+static const struct etnaviv_pm_domain doms_3d[] = {
+	{
+		.name = "HI",
+		.profile_read = VIVS_MC_PROFILE_HI_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG2,
+		.nr_signals = 5,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"TOTAL_CYCLES",
+				VIVS_HI_PROFILE_TOTAL_CYCLES,
+				&simple_reg_read
+			},
+			{
+				"IDLE_CYCLES",
+				VIVS_HI_PROFILE_IDLE_CYCLES,
+				&simple_reg_read
+			},
+			{
+				"AXI_CYCLES_READ_REQUEST_STALLED",
+				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED,
+				&perf_reg_read
+			},
+			{
+				"AXI_CYCLES_WRITE_REQUEST_STALLED",
+				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED,
+				&perf_reg_read
+			},
+			{
+				"AXI_CYCLES_WRITE_DATA_STALLED",
+				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED,
+				&perf_reg_read
+			}
+		}
+	},
+	{
+		.name = "PE",
+		.profile_read = VIVS_MC_PROFILE_PE_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG0,
+		.nr_signals = 5,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"PIXEL_COUNT_KILLED_BY_COLOR_PIPE",
+				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE,
+				&pipe_reg_read
+			},
+			{
+				"PIXEL_COUNT_KILLED_BY_DEPTH_PIPE",
+				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE,
+				&pipe_reg_read
+			},
+			{
+				"PIXEL_COUNT_DRAWN_BY_COLOR_PIPE",
+				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE,
+				&pipe_reg_read
+			},
+			{
+				"PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE",
+				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE,
+				&pipe_reg_read
+			}
+		}
+	},
+	{
+		.name = "SH",
+		.profile_read = VIVS_MC_PROFILE_SH_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG0,
+		.nr_signals = 9,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"SHADER_CYCLES",
+				VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES,
+				&perf_reg_read
+			},
+			{
+				"PS_INST_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"RENDERED_PIXEL_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"VS_INST_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"RENDERED_VERTICE_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"VTX_BRANCH_INST_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"VTX_TEXLD_INST_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"PXL_BRANCH_INST_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"PXL_TEXLD_INST_COUNTER",
+				VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER,
+				&pipe_reg_read
+			}
+		}
+	},
+	{
+		.name = "PA",
+		.profile_read = VIVS_MC_PROFILE_PA_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG1,
+		.nr_signals = 6,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"INPUT_VTX_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"INPUT_PRIM_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"OUTPUT_PRIM_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"DEPTH_CLIPPED_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"TRIVIAL_REJECTED_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER,
+				&pipe_reg_read
+			},
+			{
+				"CULLED_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER,
+				&pipe_reg_read
+			}
+		}
+	},
+	{
+		.name = "SE",
+		.profile_read = VIVS_MC_PROFILE_SE_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG1,
+		.nr_signals = 2,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"CULLED_TRIANGLE_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT,
+				&perf_reg_read
+			},
+			{
+				"CULLED_LINES_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT,
+				&perf_reg_read
+			}
+		}
+	},
+	{
+		.name = "RA",
+		.profile_read = VIVS_MC_PROFILE_RA_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG1,
+		.nr_signals = 7,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"VALID_PIXEL_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_QUAD_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT,
+				&perf_reg_read
+			},
+			{
+				"VALID_QUAD_COUNT_AFTER_EARLY_Z",
+				VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_PRIMITIVE_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT,
+				&perf_reg_read
+			},
+			{
+				"PIPE_CACHE_MISS_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"PREFETCH_CACHE_MISS_COUNTER",
+				VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER,
+				&perf_reg_read
+			},
+			{
+				"CULLED_QUAD_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT,
+				&perf_reg_read
+			}
+		}
+	},
+	{
+		.name = "TX",
+		.profile_read = VIVS_MC_PROFILE_TX_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG1,
+		.nr_signals = 9,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"TOTAL_BILINEAR_REQUESTS",
+				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_TRILINEAR_REQUESTS",
+				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_DISCARDED_TEXTURE_REQUESTS",
+				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_TEXTURE_REQUESTS",
+				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS,
+				&perf_reg_read
+			},
+			{
+				"MEM_READ_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT,
+				&perf_reg_read
+			},
+			{
+				"MEM_READ_IN_8B_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT,
+				&perf_reg_read
+			},
+			{
+				"CACHE_MISS_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT,
+				&perf_reg_read
+			},
+			{
+				"CACHE_HIT_TEXEL_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT,
+				&perf_reg_read
+			},
+			{
+				"CACHE_MISS_TEXEL_COUNT",
+				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT,
+				&perf_reg_read
+			}
+		}
+	},
+	{
+		.name = "MC",
+		.profile_read = VIVS_MC_PROFILE_MC_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG2,
+		.nr_signals = 3,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"TOTAL_READ_REQ_8B_FROM_PIPELINE",
+				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_READ_REQ_8B_FROM_IP",
+				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP,
+				&perf_reg_read
+			},
+			{
+				"TOTAL_WRITE_REQ_8B_FROM_PIPELINE",
+				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE,
+				&perf_reg_read
+			}
+		}
+	}
+};
+
+static const struct etnaviv_pm_domain doms_2d[] = {
+	{
+		.name = "PE",
+		.profile_read = VIVS_MC_PROFILE_PE_READ,
+		.profile_config = VIVS_MC_PROFILE_CONFIG0,
+		.nr_signals = 1,
+		.signal = (const struct etnaviv_pm_signal[]) {
+			{
+				"PIXELS_RENDERED_2D",
+				VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D,
+				&pipe_reg_read
+			}
+		}
+	}
+};
+
+static const struct etnaviv_pm_domain doms_vg[] = {
+};
+
+static const struct etnaviv_pm_domain_meta doms_meta[] = {
+	{
+		.nr_domains = ARRAY_SIZE(doms_3d),
+		.domains = &doms_3d[0]
+	},
+	{
+		.nr_domains = ARRAY_SIZE(doms_2d),
+		.domains = &doms_2d[0]
+	},
+	{
+		.nr_domains = ARRAY_SIZE(doms_vg),
+		.domains = &doms_vg[0]
+	}
+};
+
+int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
+	struct drm_etnaviv_pm_domain *domain)
+{
+	const struct etnaviv_pm_domain_meta *meta = &doms_meta[domain->pipe];
+	const struct etnaviv_pm_domain *dom;
+
+	if (domain->iter >= meta->nr_domains)
+		return -EINVAL;
+
+	dom = meta->domains + domain->iter;
+
+	domain->id = domain->iter;
+	domain->nr_signals = dom->nr_signals;
+	strncpy(domain->name, dom->name, sizeof(domain->name));
+
+	domain->iter++;
+	if (domain->iter == meta->nr_domains)
+		domain->iter = 0xff;
+
+	return 0;
+}
+
+int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
+	struct drm_etnaviv_pm_signal *signal)
+{
+	const struct etnaviv_pm_domain_meta *meta = &doms_meta[signal->pipe];
+	const struct etnaviv_pm_domain *dom;
+	const struct etnaviv_pm_signal *sig;
+
+	if (signal->domain >= meta->nr_domains)
+		return -EINVAL;
+
+	dom = meta->domains + signal->domain;
+
+	if (signal->iter > dom->nr_signals)
+		return -EINVAL;
+
+	sig = &dom->signal[signal->iter];
+
+	signal->id = signal->iter;
+	strncpy(signal->name, sig->name, sizeof(signal->name));
+
+	signal->iter++;
+	if (signal->iter == dom->nr_signals)
+		signal->iter = 0xffff;
+
+	return 0;
+}
+
+int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
+	u32 exec_state)
+{
+	const struct etnaviv_pm_domain_meta *meta = &doms_meta[exec_state];
+	const struct etnaviv_pm_domain *dom;
+
+	if (r->domain >= meta->nr_domains)
+		return -EINVAL;
+
+	dom = meta->domains + r->domain;
+
+	if (r->signal > dom->nr_signals)
+		return -EINVAL;
+
+	return 0;
+}
+
+void etnaviv_perfmon_process(struct etnaviv_gpu *gpu,
+	const struct etnaviv_perfmon_request *pmr)
+{
+	const struct etnaviv_pm_domain_meta *meta = &doms_meta[gpu->exec_state];
+	const struct etnaviv_pm_domain *dom;
+	const struct etnaviv_pm_signal *sig;
+	u32 *bo = pmr->bo_vma;
+	u32 val;
+
+	dom = meta->domains + pmr->domain;
+	sig = &dom->signal[pmr->signal];
+	val = sig->sample(gpu, dom, sig);
+
+	*(bo + pmr->offset) = val;
+}

+ 49 - 0
drivers/gpu/drm/etnaviv/etnaviv_perfmon.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 Etnaviv Project
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ETNAVIV_PERFMON_H__
+#define __ETNAVIV_PERFMON_H__
+
+struct etnaviv_gpu;
+struct drm_etnaviv_pm_domain;
+struct drm_etnaviv_pm_signal;
+
+struct etnaviv_perfmon_request
+{
+	u32 flags;
+	u8 domain;
+	u8 signal;
+	u32 sequence;
+
+	/* bo to store a value */
+	u32 *bo_vma;
+	u32 offset;
+};
+
+int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
+	struct drm_etnaviv_pm_domain *domain);
+
+int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
+	struct drm_etnaviv_pm_signal *signal);
+
+int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
+	u32 exec_state);
+
+void etnaviv_perfmon_process(struct etnaviv_gpu *gpu,
+	const struct etnaviv_perfmon_request *pmr);
+
+#endif /* __ETNAVIV_PERFMON_H__ */

+ 1 - 1
drivers/gpu/drm/gma500/mid_bios.c

@@ -237,7 +237,7 @@ static int mid_get_vbt_data_r10(struct drm_psb_private *dev_priv, u32 addr)
 
 
 	gct = kmalloc(sizeof(*gct) * vbt.panel_count, GFP_KERNEL);
 	gct = kmalloc(sizeof(*gct) * vbt.panel_count, GFP_KERNEL);
 	if (!gct)
 	if (!gct)
-		return -1;
+		return -ENOMEM;
 
 
 	gct_virtual = ioremap(addr + sizeof(vbt),
 	gct_virtual = ioremap(addr + sizeof(vbt),
 			sizeof(*gct) * vbt.panel_count);
 			sizeof(*gct) * vbt.panel_count);

+ 4 - 5
drivers/gpu/drm/gma500/psb_intel_sdvo.c

@@ -37,6 +37,7 @@
 #include "psb_drv.h"
 #include "psb_drv.h"
 #include "psb_intel_sdvo_regs.h"
 #include "psb_intel_sdvo_regs.h"
 #include "psb_intel_reg.h"
 #include "psb_intel_reg.h"
+#include <linux/kernel.h>
 
 
 #define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)
 #define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)
 #define SDVO_RGB_MASK  (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)
 #define SDVO_RGB_MASK  (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)
@@ -62,8 +63,6 @@ static const char *tv_format_names[] = {
 	"SECAM_60"
 	"SECAM_60"
 };
 };
 
 
-#define TV_FORMAT_NUM  (sizeof(tv_format_names) / sizeof(*tv_format_names))
-
 struct psb_intel_sdvo {
 struct psb_intel_sdvo {
 	struct gma_encoder base;
 	struct gma_encoder base;
 
 
@@ -148,7 +147,7 @@ struct psb_intel_sdvo_connector {
 	int force_audio;
 	int force_audio;
 
 
 	/* This contains all current supported TV format */
 	/* This contains all current supported TV format */
-	u8 tv_format_supported[TV_FORMAT_NUM];
+	u8 tv_format_supported[ARRAY_SIZE(tv_format_names)];
 	int   format_supported_num;
 	int   format_supported_num;
 	struct drm_property *tv_format;
 	struct drm_property *tv_format;
 
 
@@ -1709,7 +1708,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector,
 	}
 	}
 
 
 	if (property == psb_intel_sdvo_connector->tv_format) {
 	if (property == psb_intel_sdvo_connector->tv_format) {
-		if (val >= TV_FORMAT_NUM)
+		if (val >= ARRAY_SIZE(tv_format_names))
 			return -EINVAL;
 			return -EINVAL;
 
 
 		if (psb_intel_sdvo->tv_format_index ==
 		if (psb_intel_sdvo->tv_format_index ==
@@ -2269,7 +2268,7 @@ static bool psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_s
 		return false;
 		return false;
 
 
 	psb_intel_sdvo_connector->format_supported_num = 0;
 	psb_intel_sdvo_connector->format_supported_num = 0;
-	for (i = 0 ; i < TV_FORMAT_NUM; i++)
+	for (i = 0 ; i < ARRAY_SIZE(tv_format_names); i++)
 		if (format_map & (1 << i))
 		if (format_map & (1 << i))
 			psb_intel_sdvo_connector->tv_format_supported[psb_intel_sdvo_connector->format_supported_num++] = i;
 			psb_intel_sdvo_connector->tv_format_supported[psb_intel_sdvo_connector->format_supported_num++] = i;
 
 

+ 1 - 1
drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c

@@ -36,7 +36,7 @@ static int hibmc_connector_mode_valid(struct drm_connector *connector,
 static struct drm_encoder *
 static struct drm_encoder *
 hibmc_connector_best_encoder(struct drm_connector *connector)
 hibmc_connector_best_encoder(struct drm_connector *connector)
 {
 {
-	return drm_encoder_find(connector->dev, connector->encoder_ids[0]);
+	return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]);
 }
 }
 
 
 static const struct drm_connector_helper_funcs
 static const struct drm_connector_helper_funcs

+ 2 - 2
drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c

@@ -237,8 +237,8 @@ static int kirin_drm_platform_probe(struct platform_device *pdev)
 	}
 	}
 
 
 	remote = of_graph_get_remote_node(np, 0, 0);
 	remote = of_graph_get_remote_node(np, 0, 0);
-	if (IS_ERR(remote))
-		return PTR_ERR(remote);
+	if (!remote)
+		return -ENODEV;
 
 
 	drm_of_component_match_add(dev, &match, compare_of, remote);
 	drm_of_component_match_add(dev, &match, compare_of, remote);
 	of_node_put(remote);
 	of_node_put(remote);

+ 1 - 0
drivers/gpu/drm/i915/Kconfig

@@ -12,6 +12,7 @@ config DRM_I915
 	select DRM_PANEL
 	select DRM_PANEL
 	select DRM_MIPI_DSI
 	select DRM_MIPI_DSI
 	select RELAY
 	select RELAY
+	select IRQ_WORK
 	# i915 depends on ACPI_VIDEO when ACPI is enabled
 	# i915 depends on ACPI_VIDEO when ACPI is enabled
 	# but for select to work, need to select ACPI_VIDEO's dependencies, ick
 	# but for select to work, need to select ACPI_VIDEO's dependencies, ick
 	select BACKLIGHT_LCD_SUPPORT if ACPI
 	select BACKLIGHT_LCD_SUPPORT if ACPI

+ 2 - 1
drivers/gpu/drm/i915/Makefile

@@ -139,7 +139,8 @@ i915-y += i915_perf.o \
 	  i915_oa_bxt.o \
 	  i915_oa_bxt.o \
 	  i915_oa_kblgt2.o \
 	  i915_oa_kblgt2.o \
 	  i915_oa_kblgt3.o \
 	  i915_oa_kblgt3.o \
-	  i915_oa_glk.o
+	  i915_oa_glk.o \
+	  i915_oa_cflgt2.o
 
 
 ifeq ($(CONFIG_DRM_I915_GVT),y)
 ifeq ($(CONFIG_DRM_I915_GVT),y)
 i915-y += intel_gvt.o
 i915-y += intel_gvt.o

+ 22 - 8
drivers/gpu/drm/i915/gvt/cfg_space.c

@@ -101,7 +101,7 @@ int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset,
 	if (WARN_ON(bytes > 4))
 	if (WARN_ON(bytes > 4))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (WARN_ON(offset + bytes > INTEL_GVT_MAX_CFG_SPACE_SZ))
+	if (WARN_ON(offset + bytes > vgpu->gvt->device_info.cfg_space_size))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	memcpy(p_data, vgpu_cfg_space(vgpu) + offset, bytes);
 	memcpy(p_data, vgpu_cfg_space(vgpu) + offset, bytes);
@@ -110,13 +110,25 @@ int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset,
 
 
 static int map_aperture(struct intel_vgpu *vgpu, bool map)
 static int map_aperture(struct intel_vgpu *vgpu, bool map)
 {
 {
-	u64 first_gfn, first_mfn;
+	phys_addr_t aperture_pa = vgpu_aperture_pa_base(vgpu);
+	unsigned long aperture_sz = vgpu_aperture_sz(vgpu);
+	u64 first_gfn;
 	u64 val;
 	u64 val;
 	int ret;
 	int ret;
 
 
 	if (map == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked)
 	if (map == vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked)
 		return 0;
 		return 0;
 
 
+	if (map) {
+		vgpu->gm.aperture_va = memremap(aperture_pa, aperture_sz,
+						MEMREMAP_WC);
+		if (!vgpu->gm.aperture_va)
+			return -ENOMEM;
+	} else {
+		memunmap(vgpu->gm.aperture_va);
+		vgpu->gm.aperture_va = NULL;
+	}
+
 	val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_2];
 	val = vgpu_cfg_space(vgpu)[PCI_BASE_ADDRESS_2];
 	if (val & PCI_BASE_ADDRESS_MEM_TYPE_64)
 	if (val & PCI_BASE_ADDRESS_MEM_TYPE_64)
 		val = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
 		val = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
@@ -124,14 +136,16 @@ static int map_aperture(struct intel_vgpu *vgpu, bool map)
 		val = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
 		val = *(u32 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_2);
 
 
 	first_gfn = (val + vgpu_aperture_offset(vgpu)) >> PAGE_SHIFT;
 	first_gfn = (val + vgpu_aperture_offset(vgpu)) >> PAGE_SHIFT;
-	first_mfn = vgpu_aperture_pa_base(vgpu) >> PAGE_SHIFT;
 
 
 	ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, first_gfn,
 	ret = intel_gvt_hypervisor_map_gfn_to_mfn(vgpu, first_gfn,
-						  first_mfn,
-						  vgpu_aperture_sz(vgpu) >>
-						  PAGE_SHIFT, map);
-	if (ret)
+						  aperture_pa >> PAGE_SHIFT,
+						  aperture_sz >> PAGE_SHIFT,
+						  map);
+	if (ret) {
+		memunmap(vgpu->gm.aperture_va);
+		vgpu->gm.aperture_va = NULL;
 		return ret;
 		return ret;
+	}
 
 
 	vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked = map;
 	vgpu->cfg_space.bar[INTEL_GVT_PCI_BAR_APERTURE].tracked = map;
 	return 0;
 	return 0;
@@ -275,7 +289,7 @@ int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset,
 	if (WARN_ON(bytes > 4))
 	if (WARN_ON(bytes > 4))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (WARN_ON(offset + bytes > INTEL_GVT_MAX_CFG_SPACE_SZ))
+	if (WARN_ON(offset + bytes > vgpu->gvt->device_info.cfg_space_size))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	/* First check if it's PCI_COMMAND */
 	/* First check if it's PCI_COMMAND */

+ 24 - 13
drivers/gpu/drm/i915/gvt/cmd_parser.c

@@ -1576,11 +1576,11 @@ static int batch_buffer_needs_scan(struct parser_exec_state *s)
 	return 1;
 	return 1;
 }
 }
 
 
-static uint32_t find_bb_size(struct parser_exec_state *s)
+static int find_bb_size(struct parser_exec_state *s)
 {
 {
 	unsigned long gma = 0;
 	unsigned long gma = 0;
 	struct cmd_info *info;
 	struct cmd_info *info;
-	uint32_t bb_size = 0;
+	int bb_size = 0;
 	uint32_t cmd_len = 0;
 	uint32_t cmd_len = 0;
 	bool met_bb_end = false;
 	bool met_bb_end = false;
 	struct intel_vgpu *vgpu = s->vgpu;
 	struct intel_vgpu *vgpu = s->vgpu;
@@ -1637,6 +1637,8 @@ static int perform_bb_shadow(struct parser_exec_state *s)
 
 
 	/* get the size of the batch buffer */
 	/* get the size of the batch buffer */
 	bb_size = find_bb_size(s);
 	bb_size = find_bb_size(s);
+	if (bb_size < 0)
+		return -EINVAL;
 
 
 	/* allocate shadow batch buffer */
 	/* allocate shadow batch buffer */
 	entry_obj = kmalloc(sizeof(*entry_obj), GFP_KERNEL);
 	entry_obj = kmalloc(sizeof(*entry_obj), GFP_KERNEL);
@@ -2603,7 +2605,8 @@ static int shadow_workload_ring_buffer(struct intel_vgpu_workload *workload)
 {
 {
 	struct intel_vgpu *vgpu = workload->vgpu;
 	struct intel_vgpu *vgpu = workload->vgpu;
 	unsigned long gma_head, gma_tail, gma_top, guest_rb_size;
 	unsigned long gma_head, gma_tail, gma_top, guest_rb_size;
-	u32 *cs;
+	void *shadow_ring_buffer_va;
+	int ring_id = workload->ring_id;
 	int ret;
 	int ret;
 
 
 	guest_rb_size = _RING_CTL_BUF_SIZE(workload->rb_ctl);
 	guest_rb_size = _RING_CTL_BUF_SIZE(workload->rb_ctl);
@@ -2616,34 +2619,42 @@ static int shadow_workload_ring_buffer(struct intel_vgpu_workload *workload)
 	gma_tail = workload->rb_start + workload->rb_tail;
 	gma_tail = workload->rb_start + workload->rb_tail;
 	gma_top = workload->rb_start + guest_rb_size;
 	gma_top = workload->rb_start + guest_rb_size;
 
 
-	/* allocate shadow ring buffer */
-	cs = intel_ring_begin(workload->req, workload->rb_len / sizeof(u32));
-	if (IS_ERR(cs))
-		return PTR_ERR(cs);
+	if (workload->rb_len > vgpu->reserve_ring_buffer_size[ring_id]) {
+		void *va = vgpu->reserve_ring_buffer_va[ring_id];
+		/* realloc the new ring buffer if needed */
+		vgpu->reserve_ring_buffer_va[ring_id] =
+			krealloc(va, workload->rb_len, GFP_KERNEL);
+		if (!vgpu->reserve_ring_buffer_va[ring_id]) {
+			gvt_vgpu_err("fail to alloc reserve ring buffer\n");
+			return -ENOMEM;
+		}
+		vgpu->reserve_ring_buffer_size[ring_id] = workload->rb_len;
+	}
+
+	shadow_ring_buffer_va = vgpu->reserve_ring_buffer_va[ring_id];
 
 
 	/* get shadow ring buffer va */
 	/* get shadow ring buffer va */
-	workload->shadow_ring_buffer_va = cs;
+	workload->shadow_ring_buffer_va = shadow_ring_buffer_va;
 
 
 	/* head > tail --> copy head <-> top */
 	/* head > tail --> copy head <-> top */
 	if (gma_head > gma_tail) {
 	if (gma_head > gma_tail) {
 		ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm,
 		ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm,
-				      gma_head, gma_top, cs);
+				      gma_head, gma_top, shadow_ring_buffer_va);
 		if (ret < 0) {
 		if (ret < 0) {
 			gvt_vgpu_err("fail to copy guest ring buffer\n");
 			gvt_vgpu_err("fail to copy guest ring buffer\n");
 			return ret;
 			return ret;
 		}
 		}
-		cs += ret / sizeof(u32);
+		shadow_ring_buffer_va += ret;
 		gma_head = workload->rb_start;
 		gma_head = workload->rb_start;
 	}
 	}
 
 
 	/* copy head or start <-> tail */
 	/* copy head or start <-> tail */
-	ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm, gma_head, gma_tail, cs);
+	ret = copy_gma_to_hva(vgpu, vgpu->gtt.ggtt_mm, gma_head, gma_tail,
+				shadow_ring_buffer_va);
 	if (ret < 0) {
 	if (ret < 0) {
 		gvt_vgpu_err("fail to copy guest ring buffer\n");
 		gvt_vgpu_err("fail to copy guest ring buffer\n");
 		return ret;
 		return ret;
 	}
 	}
-	cs += ret / sizeof(u32);
-	intel_ring_advance(workload->req, cs);
 	return 0;
 	return 0;
 }
 }
 
 

+ 95 - 32
drivers/gpu/drm/i915/gvt/execlist.c

@@ -368,7 +368,7 @@ static void free_workload(struct intel_vgpu_workload *workload)
 #define get_desc_from_elsp_dwords(ed, i) \
 #define get_desc_from_elsp_dwords(ed, i) \
 	((struct execlist_ctx_descriptor_format *)&((ed)->data[i * 2]))
 	((struct execlist_ctx_descriptor_format *)&((ed)->data[i * 2]))
 
 
-static void prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
+static int prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
 {
 {
 	const int gmadr_bytes = workload->vgpu->gvt->device_info.gmadr_bytes_in_cmd;
 	const int gmadr_bytes = workload->vgpu->gvt->device_info.gmadr_bytes_in_cmd;
 	struct intel_shadow_bb_entry *entry_obj;
 	struct intel_shadow_bb_entry *entry_obj;
@@ -379,7 +379,7 @@ static void prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
 
 
 		vma = i915_gem_object_ggtt_pin(entry_obj->obj, NULL, 0, 4, 0);
 		vma = i915_gem_object_ggtt_pin(entry_obj->obj, NULL, 0, 4, 0);
 		if (IS_ERR(vma)) {
 		if (IS_ERR(vma)) {
-			return;
+			return PTR_ERR(vma);
 		}
 		}
 
 
 		/* FIXME: we are not tracking our pinned VMA leaving it
 		/* FIXME: we are not tracking our pinned VMA leaving it
@@ -392,6 +392,7 @@ static void prepare_shadow_batch_buffer(struct intel_vgpu_workload *workload)
 		if (gmadr_bytes == 8)
 		if (gmadr_bytes == 8)
 			entry_obj->bb_start_cmd_va[2] = 0;
 			entry_obj->bb_start_cmd_va[2] = 0;
 	}
 	}
+	return 0;
 }
 }
 
 
 static int update_wa_ctx_2_shadow_ctx(struct intel_shadow_wa_ctx *wa_ctx)
 static int update_wa_ctx_2_shadow_ctx(struct intel_shadow_wa_ctx *wa_ctx)
@@ -420,7 +421,7 @@ static int update_wa_ctx_2_shadow_ctx(struct intel_shadow_wa_ctx *wa_ctx)
 	return 0;
 	return 0;
 }
 }
 
 
-static void prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
+static int prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
 {
 {
 	struct i915_vma *vma;
 	struct i915_vma *vma;
 	unsigned char *per_ctx_va =
 	unsigned char *per_ctx_va =
@@ -428,12 +429,12 @@ static void prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
 		wa_ctx->indirect_ctx.size;
 		wa_ctx->indirect_ctx.size;
 
 
 	if (wa_ctx->indirect_ctx.size == 0)
 	if (wa_ctx->indirect_ctx.size == 0)
-		return;
+		return 0;
 
 
 	vma = i915_gem_object_ggtt_pin(wa_ctx->indirect_ctx.obj, NULL,
 	vma = i915_gem_object_ggtt_pin(wa_ctx->indirect_ctx.obj, NULL,
 				       0, CACHELINE_BYTES, 0);
 				       0, CACHELINE_BYTES, 0);
 	if (IS_ERR(vma)) {
 	if (IS_ERR(vma)) {
-		return;
+		return PTR_ERR(vma);
 	}
 	}
 
 
 	/* FIXME: we are not tracking our pinned VMA leaving it
 	/* FIXME: we are not tracking our pinned VMA leaving it
@@ -447,26 +448,7 @@ static void prepare_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
 	memset(per_ctx_va, 0, CACHELINE_BYTES);
 	memset(per_ctx_va, 0, CACHELINE_BYTES);
 
 
 	update_wa_ctx_2_shadow_ctx(wa_ctx);
 	update_wa_ctx_2_shadow_ctx(wa_ctx);
-}
-
-static int prepare_execlist_workload(struct intel_vgpu_workload *workload)
-{
-	struct intel_vgpu *vgpu = workload->vgpu;
-	struct execlist_ctx_descriptor_format ctx[2];
-	int ring_id = workload->ring_id;
-
-	intel_vgpu_pin_mm(workload->shadow_mm);
-	intel_vgpu_sync_oos_pages(workload->vgpu);
-	intel_vgpu_flush_post_shadow(workload->vgpu);
-	prepare_shadow_batch_buffer(workload);
-	prepare_shadow_wa_ctx(&workload->wa_ctx);
-	if (!workload->emulate_schedule_in)
-		return 0;
-
-	ctx[0] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 1);
-	ctx[1] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 0);
-
-	return emulate_execlist_schedule_in(&vgpu->execlist[ring_id], ctx);
+	return 0;
 }
 }
 
 
 static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload)
 static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload)
@@ -489,13 +471,62 @@ static void release_shadow_batch_buffer(struct intel_vgpu_workload *workload)
 	}
 	}
 }
 }
 
 
-static void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
+static int prepare_execlist_workload(struct intel_vgpu_workload *workload)
 {
 {
-	if (!wa_ctx->indirect_ctx.obj)
-		return;
+	struct intel_vgpu *vgpu = workload->vgpu;
+	struct execlist_ctx_descriptor_format ctx[2];
+	int ring_id = workload->ring_id;
+	int ret;
+
+	ret = intel_vgpu_pin_mm(workload->shadow_mm);
+	if (ret) {
+		gvt_vgpu_err("fail to vgpu pin mm\n");
+		goto out;
+	}
+
+	ret = intel_vgpu_sync_oos_pages(workload->vgpu);
+	if (ret) {
+		gvt_vgpu_err("fail to vgpu sync oos pages\n");
+		goto err_unpin_mm;
+	}
 
 
-	i915_gem_object_unpin_map(wa_ctx->indirect_ctx.obj);
-	i915_gem_object_put(wa_ctx->indirect_ctx.obj);
+	ret = intel_vgpu_flush_post_shadow(workload->vgpu);
+	if (ret) {
+		gvt_vgpu_err("fail to flush post shadow\n");
+		goto err_unpin_mm;
+	}
+
+	ret = prepare_shadow_batch_buffer(workload);
+	if (ret) {
+		gvt_vgpu_err("fail to prepare_shadow_batch_buffer\n");
+		goto err_unpin_mm;
+	}
+
+	ret = prepare_shadow_wa_ctx(&workload->wa_ctx);
+	if (ret) {
+		gvt_vgpu_err("fail to prepare_shadow_wa_ctx\n");
+		goto err_shadow_batch;
+	}
+
+	if (!workload->emulate_schedule_in)
+		return 0;
+
+	ctx[0] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 1);
+	ctx[1] = *get_desc_from_elsp_dwords(&workload->elsp_dwords, 0);
+
+	ret = emulate_execlist_schedule_in(&vgpu->execlist[ring_id], ctx);
+	if (!ret)
+		goto out;
+	else
+		gvt_vgpu_err("fail to emulate execlist schedule in\n");
+
+	release_shadow_wa_ctx(&workload->wa_ctx);
+err_shadow_batch:
+	release_shadow_batch_buffer(workload);
+err_unpin_mm:
+	intel_vgpu_unpin_mm(workload->shadow_mm);
+out:
+	return ret;
 }
 }
 
 
 static int complete_execlist_workload(struct intel_vgpu_workload *workload)
 static int complete_execlist_workload(struct intel_vgpu_workload *workload)
@@ -511,8 +542,10 @@ static int complete_execlist_workload(struct intel_vgpu_workload *workload)
 	gvt_dbg_el("complete workload %p status %d\n", workload,
 	gvt_dbg_el("complete workload %p status %d\n", workload,
 			workload->status);
 			workload->status);
 
 
-	release_shadow_batch_buffer(workload);
-	release_shadow_wa_ctx(&workload->wa_ctx);
+	if (!workload->status) {
+		release_shadow_batch_buffer(workload);
+		release_shadow_wa_ctx(&workload->wa_ctx);
+	}
 
 
 	if (workload->status || (vgpu->resetting_eng & ENGINE_MASK(ring_id))) {
 	if (workload->status || (vgpu->resetting_eng & ENGINE_MASK(ring_id))) {
 		/* if workload->status is not successful means HW GPU
 		/* if workload->status is not successful means HW GPU
@@ -820,10 +853,21 @@ static void clean_workloads(struct intel_vgpu *vgpu, unsigned long engine_mask)
 
 
 void intel_vgpu_clean_execlist(struct intel_vgpu *vgpu)
 void intel_vgpu_clean_execlist(struct intel_vgpu *vgpu)
 {
 {
+	enum intel_engine_id i;
+	struct intel_engine_cs *engine;
+
 	clean_workloads(vgpu, ALL_ENGINES);
 	clean_workloads(vgpu, ALL_ENGINES);
 	kmem_cache_destroy(vgpu->workloads);
 	kmem_cache_destroy(vgpu->workloads);
+
+	for_each_engine(engine, vgpu->gvt->dev_priv, i) {
+		kfree(vgpu->reserve_ring_buffer_va[i]);
+		vgpu->reserve_ring_buffer_va[i] = NULL;
+		vgpu->reserve_ring_buffer_size[i] = 0;
+	}
+
 }
 }
 
 
+#define RESERVE_RING_BUFFER_SIZE		((1 * PAGE_SIZE)/8)
 int intel_vgpu_init_execlist(struct intel_vgpu *vgpu)
 int intel_vgpu_init_execlist(struct intel_vgpu *vgpu)
 {
 {
 	enum intel_engine_id i;
 	enum intel_engine_id i;
@@ -843,7 +887,26 @@ int intel_vgpu_init_execlist(struct intel_vgpu *vgpu)
 	if (!vgpu->workloads)
 	if (!vgpu->workloads)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
+	/* each ring has a shadow ring buffer until vgpu destroyed */
+	for_each_engine(engine, vgpu->gvt->dev_priv, i) {
+		vgpu->reserve_ring_buffer_va[i] =
+			kmalloc(RESERVE_RING_BUFFER_SIZE, GFP_KERNEL);
+		if (!vgpu->reserve_ring_buffer_va[i]) {
+			gvt_vgpu_err("fail to alloc reserve ring buffer\n");
+			goto out;
+		}
+		vgpu->reserve_ring_buffer_size[i] = RESERVE_RING_BUFFER_SIZE;
+	}
 	return 0;
 	return 0;
+out:
+	for_each_engine(engine, vgpu->gvt->dev_priv, i) {
+		if (vgpu->reserve_ring_buffer_size[i]) {
+			kfree(vgpu->reserve_ring_buffer_va[i]);
+			vgpu->reserve_ring_buffer_va[i] = NULL;
+			vgpu->reserve_ring_buffer_size[i] = 0;
+		}
+	}
+	return -ENOMEM;
 }
 }
 
 
 void intel_vgpu_reset_execlist(struct intel_vgpu *vgpu,
 void intel_vgpu_reset_execlist(struct intel_vgpu *vgpu,

+ 2 - 3
drivers/gpu/drm/i915/gvt/gtt.c

@@ -1647,14 +1647,13 @@ int intel_vgpu_pin_mm(struct intel_vgpu_mm *mm)
 	if (WARN_ON(mm->type != INTEL_GVT_MM_PPGTT))
 	if (WARN_ON(mm->type != INTEL_GVT_MM_PPGTT))
 		return 0;
 		return 0;
 
 
-	atomic_inc(&mm->pincount);
-
 	if (!mm->shadowed) {
 	if (!mm->shadowed) {
 		ret = shadow_mm(mm);
 		ret = shadow_mm(mm);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 	}
 	}
 
 
+	atomic_inc(&mm->pincount);
 	list_del_init(&mm->lru_list);
 	list_del_init(&mm->lru_list);
 	list_add_tail(&mm->lru_list, &mm->vgpu->gvt->gtt.mm_lru_list_head);
 	list_add_tail(&mm->lru_list, &mm->vgpu->gvt->gtt.mm_lru_list_head);
 	return 0;
 	return 0;
@@ -1972,7 +1971,7 @@ static int alloc_scratch_pages(struct intel_vgpu *vgpu,
 		 */
 		 */
 		se.val64 |= _PAGE_PRESENT | _PAGE_RW;
 		se.val64 |= _PAGE_PRESENT | _PAGE_RW;
 		if (type == GTT_TYPE_PPGTT_PDE_PT)
 		if (type == GTT_TYPE_PPGTT_PDE_PT)
-			se.val64 |= PPAT_CACHED_INDEX;
+			se.val64 |= PPAT_CACHED;
 
 
 		for (i = 0; i < page_entry_num; i++)
 		for (i = 0; i < page_entry_num; i++)
 			ops->set_entry(scratch_pt, &se, i, false, 0, vgpu);
 			ops->set_entry(scratch_pt, &se, i, false, 0, vgpu);

+ 1 - 1
drivers/gpu/drm/i915/gvt/gvt.c

@@ -111,7 +111,7 @@ static void init_device_info(struct intel_gvt *gvt)
 	if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv)
 	if (IS_BROADWELL(gvt->dev_priv) || IS_SKYLAKE(gvt->dev_priv)
 		|| IS_KABYLAKE(gvt->dev_priv)) {
 		|| IS_KABYLAKE(gvt->dev_priv)) {
 		info->max_support_vgpus = 8;
 		info->max_support_vgpus = 8;
-		info->cfg_space_size = 256;
+		info->cfg_space_size = PCI_CFG_SPACE_EXP_SIZE;
 		info->mmio_size = 2 * 1024 * 1024;
 		info->mmio_size = 2 * 1024 * 1024;
 		info->mmio_bar = 0;
 		info->mmio_bar = 0;
 		info->gtt_start_offset = 8 * 1024 * 1024;
 		info->gtt_start_offset = 8 * 1024 * 1024;

+ 12 - 2
drivers/gpu/drm/i915/gvt/gvt.h

@@ -80,6 +80,7 @@ struct intel_gvt_device_info {
 struct intel_vgpu_gm {
 struct intel_vgpu_gm {
 	u64 aperture_sz;
 	u64 aperture_sz;
 	u64 hidden_sz;
 	u64 hidden_sz;
+	void *aperture_va;
 	struct drm_mm_node low_gm_node;
 	struct drm_mm_node low_gm_node;
 	struct drm_mm_node high_gm_node;
 	struct drm_mm_node high_gm_node;
 };
 };
@@ -99,7 +100,6 @@ struct intel_vgpu_mmio {
 	bool disable_warn_untrack;
 	bool disable_warn_untrack;
 };
 };
 
 
-#define INTEL_GVT_MAX_CFG_SPACE_SZ 256
 #define INTEL_GVT_MAX_BAR_NUM 4
 #define INTEL_GVT_MAX_BAR_NUM 4
 
 
 struct intel_vgpu_pci_bar {
 struct intel_vgpu_pci_bar {
@@ -108,7 +108,7 @@ struct intel_vgpu_pci_bar {
 };
 };
 
 
 struct intel_vgpu_cfg_space {
 struct intel_vgpu_cfg_space {
-	unsigned char virtual_cfg_space[INTEL_GVT_MAX_CFG_SPACE_SZ];
+	unsigned char virtual_cfg_space[PCI_CFG_SPACE_EXP_SIZE];
 	struct intel_vgpu_pci_bar bar[INTEL_GVT_MAX_BAR_NUM];
 	struct intel_vgpu_pci_bar bar[INTEL_GVT_MAX_BAR_NUM];
 };
 };
 
 
@@ -165,6 +165,9 @@ struct intel_vgpu {
 	struct list_head workload_q_head[I915_NUM_ENGINES];
 	struct list_head workload_q_head[I915_NUM_ENGINES];
 	struct kmem_cache *workloads;
 	struct kmem_cache *workloads;
 	atomic_t running_workload_num;
 	atomic_t running_workload_num;
+	/* 1/2K for each reserve ring buffer */
+	void *reserve_ring_buffer_va[I915_NUM_ENGINES];
+	int reserve_ring_buffer_size[I915_NUM_ENGINES];
 	DECLARE_BITMAP(tlb_handle_pending, I915_NUM_ENGINES);
 	DECLARE_BITMAP(tlb_handle_pending, I915_NUM_ENGINES);
 	struct i915_gem_context *shadow_ctx;
 	struct i915_gem_context *shadow_ctx;
 	DECLARE_BITMAP(shadow_ctx_desc_updated, I915_NUM_ENGINES);
 	DECLARE_BITMAP(shadow_ctx_desc_updated, I915_NUM_ENGINES);
@@ -474,6 +477,13 @@ int intel_vgpu_emulate_cfg_read(struct intel_vgpu *vgpu, unsigned int offset,
 int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset,
 int intel_vgpu_emulate_cfg_write(struct intel_vgpu *vgpu, unsigned int offset,
 		void *p_data, unsigned int bytes);
 		void *p_data, unsigned int bytes);
 
 
+static inline u64 intel_vgpu_get_bar_gpa(struct intel_vgpu *vgpu, int bar)
+{
+	/* We are 64bit bar. */
+	return (*(u64 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
+			PCI_BASE_ADDRESS_MEM_MASK;
+}
+
 void intel_gvt_clean_opregion(struct intel_gvt *gvt);
 void intel_gvt_clean_opregion(struct intel_gvt *gvt);
 int intel_gvt_init_opregion(struct intel_gvt *gvt);
 int intel_gvt_init_opregion(struct intel_gvt *gvt);
 
 

+ 26 - 18
drivers/gpu/drm/i915/gvt/kvmgt.c

@@ -609,21 +609,20 @@ static void intel_vgpu_release_work(struct work_struct *work)
 	__intel_vgpu_release(vgpu);
 	__intel_vgpu_release(vgpu);
 }
 }
 
 
-static uint64_t intel_vgpu_get_bar0_addr(struct intel_vgpu *vgpu)
+static uint64_t intel_vgpu_get_bar_addr(struct intel_vgpu *vgpu, int bar)
 {
 {
 	u32 start_lo, start_hi;
 	u32 start_lo, start_hi;
 	u32 mem_type;
 	u32 mem_type;
-	int pos = PCI_BASE_ADDRESS_0;
 
 
-	start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + pos)) &
+	start_lo = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
 			PCI_BASE_ADDRESS_MEM_MASK;
 			PCI_BASE_ADDRESS_MEM_MASK;
-	mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + pos)) &
+	mem_type = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space + bar)) &
 			PCI_BASE_ADDRESS_MEM_TYPE_MASK;
 			PCI_BASE_ADDRESS_MEM_TYPE_MASK;
 
 
 	switch (mem_type) {
 	switch (mem_type) {
 	case PCI_BASE_ADDRESS_MEM_TYPE_64:
 	case PCI_BASE_ADDRESS_MEM_TYPE_64:
 		start_hi = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space
 		start_hi = (*(u32 *)(vgpu->cfg_space.virtual_cfg_space
-						+ pos + 4));
+						+ bar + 4));
 		break;
 		break;
 	case PCI_BASE_ADDRESS_MEM_TYPE_32:
 	case PCI_BASE_ADDRESS_MEM_TYPE_32:
 	case PCI_BASE_ADDRESS_MEM_TYPE_1M:
 	case PCI_BASE_ADDRESS_MEM_TYPE_1M:
@@ -637,6 +636,21 @@ static uint64_t intel_vgpu_get_bar0_addr(struct intel_vgpu *vgpu)
 	return ((u64)start_hi << 32) | start_lo;
 	return ((u64)start_hi << 32) | start_lo;
 }
 }
 
 
+static int intel_vgpu_bar_rw(struct intel_vgpu *vgpu, int bar, uint64_t off,
+			     void *buf, unsigned int count, bool is_write)
+{
+	uint64_t bar_start = intel_vgpu_get_bar_addr(vgpu, bar);
+	int ret;
+
+	if (is_write)
+		ret = intel_gvt_ops->emulate_mmio_write(vgpu,
+					bar_start + off, buf, count);
+	else
+		ret = intel_gvt_ops->emulate_mmio_read(vgpu,
+					bar_start + off, buf, count);
+	return ret;
+}
+
 static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
 static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
 			size_t count, loff_t *ppos, bool is_write)
 			size_t count, loff_t *ppos, bool is_write)
 {
 {
@@ -661,20 +675,14 @@ static ssize_t intel_vgpu_rw(struct mdev_device *mdev, char *buf,
 						buf, count);
 						buf, count);
 		break;
 		break;
 	case VFIO_PCI_BAR0_REGION_INDEX:
 	case VFIO_PCI_BAR0_REGION_INDEX:
-	case VFIO_PCI_BAR1_REGION_INDEX:
-		if (is_write) {
-			uint64_t bar0_start = intel_vgpu_get_bar0_addr(vgpu);
-
-			ret = intel_gvt_ops->emulate_mmio_write(vgpu,
-						bar0_start + pos, buf, count);
-		} else {
-			uint64_t bar0_start = intel_vgpu_get_bar0_addr(vgpu);
-
-			ret = intel_gvt_ops->emulate_mmio_read(vgpu,
-						bar0_start + pos, buf, count);
-		}
+		ret = intel_vgpu_bar_rw(vgpu, PCI_BASE_ADDRESS_0, pos,
+					buf, count, is_write);
 		break;
 		break;
 	case VFIO_PCI_BAR2_REGION_INDEX:
 	case VFIO_PCI_BAR2_REGION_INDEX:
+		ret = intel_vgpu_bar_rw(vgpu, PCI_BASE_ADDRESS_2, pos,
+					buf, count, is_write);
+		break;
+	case VFIO_PCI_BAR1_REGION_INDEX:
 	case VFIO_PCI_BAR3_REGION_INDEX:
 	case VFIO_PCI_BAR3_REGION_INDEX:
 	case VFIO_PCI_BAR4_REGION_INDEX:
 	case VFIO_PCI_BAR4_REGION_INDEX:
 	case VFIO_PCI_BAR5_REGION_INDEX:
 	case VFIO_PCI_BAR5_REGION_INDEX:
@@ -970,7 +978,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
 		switch (info.index) {
 		switch (info.index) {
 		case VFIO_PCI_CONFIG_REGION_INDEX:
 		case VFIO_PCI_CONFIG_REGION_INDEX:
 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
 			info.offset = VFIO_PCI_INDEX_TO_OFFSET(info.index);
-			info.size = INTEL_GVT_MAX_CFG_SPACE_SZ;
+			info.size = vgpu->gvt->device_info.cfg_space_size;
 			info.flags = VFIO_REGION_INFO_FLAG_READ |
 			info.flags = VFIO_REGION_INFO_FLAG_READ |
 				     VFIO_REGION_INFO_FLAG_WRITE;
 				     VFIO_REGION_INFO_FLAG_WRITE;
 			break;
 			break;

+ 45 - 2
drivers/gpu/drm/i915/gvt/mmio.c

@@ -45,8 +45,7 @@
  */
  */
 int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
 int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
 {
 {
-	u64 gttmmio_gpa = *(u64 *)(vgpu_cfg_space(vgpu) + PCI_BASE_ADDRESS_0) &
-			  ~GENMASK(3, 0);
+	u64 gttmmio_gpa = intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_0);
 	return gpa - gttmmio_gpa;
 	return gpa - gttmmio_gpa;
 }
 }
 
 
@@ -57,6 +56,38 @@ int intel_vgpu_gpa_to_mmio_offset(struct intel_vgpu *vgpu, u64 gpa)
 	(reg >= gvt->device_info.gtt_start_offset \
 	(reg >= gvt->device_info.gtt_start_offset \
 	 && reg < gvt->device_info.gtt_start_offset + gvt_ggtt_sz(gvt))
 	 && reg < gvt->device_info.gtt_start_offset + gvt_ggtt_sz(gvt))
 
 
+static bool vgpu_gpa_is_aperture(struct intel_vgpu *vgpu, uint64_t gpa)
+{
+	u64 aperture_gpa = intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_2);
+	u64 aperture_sz = vgpu_aperture_sz(vgpu);
+
+	return gpa >= aperture_gpa && gpa < aperture_gpa + aperture_sz;
+}
+
+static int vgpu_aperture_rw(struct intel_vgpu *vgpu, uint64_t gpa,
+			    void *pdata, unsigned int size, bool is_read)
+{
+	u64 aperture_gpa = intel_vgpu_get_bar_gpa(vgpu, PCI_BASE_ADDRESS_2);
+	u64 offset = gpa - aperture_gpa;
+
+	if (!vgpu_gpa_is_aperture(vgpu, gpa + size - 1)) {
+		gvt_vgpu_err("Aperture rw out of range, offset %llx, size %d\n",
+			     offset, size);
+		return -EINVAL;
+	}
+
+	if (!vgpu->gm.aperture_va) {
+		gvt_vgpu_err("BAR is not enabled\n");
+		return -ENXIO;
+	}
+
+	if (is_read)
+		memcpy(pdata, vgpu->gm.aperture_va + offset, size);
+	else
+		memcpy(vgpu->gm.aperture_va + offset, pdata, size);
+	return 0;
+}
+
 static void failsafe_emulate_mmio_rw(struct intel_vgpu *vgpu, uint64_t pa,
 static void failsafe_emulate_mmio_rw(struct intel_vgpu *vgpu, uint64_t pa,
 		void *p_data, unsigned int bytes, bool read)
 		void *p_data, unsigned int bytes, bool read)
 {
 {
@@ -133,6 +164,12 @@ int intel_vgpu_emulate_mmio_read(struct intel_vgpu *vgpu, uint64_t pa,
 	}
 	}
 	mutex_lock(&gvt->lock);
 	mutex_lock(&gvt->lock);
 
 
+	if (vgpu_gpa_is_aperture(vgpu, pa)) {
+		ret = vgpu_aperture_rw(vgpu, pa, p_data, bytes, true);
+		mutex_unlock(&gvt->lock);
+		return ret;
+	}
+
 	if (atomic_read(&vgpu->gtt.n_write_protected_guest_page)) {
 	if (atomic_read(&vgpu->gtt.n_write_protected_guest_page)) {
 		struct intel_vgpu_guest_page *gp;
 		struct intel_vgpu_guest_page *gp;
 
 
@@ -224,6 +261,12 @@ int intel_vgpu_emulate_mmio_write(struct intel_vgpu *vgpu, uint64_t pa,
 
 
 	mutex_lock(&gvt->lock);
 	mutex_lock(&gvt->lock);
 
 
+	if (vgpu_gpa_is_aperture(vgpu, pa)) {
+		ret = vgpu_aperture_rw(vgpu, pa, p_data, bytes, false);
+		mutex_unlock(&gvt->lock);
+		return ret;
+	}
+
 	if (atomic_read(&vgpu->gtt.n_write_protected_guest_page)) {
 	if (atomic_read(&vgpu->gtt.n_write_protected_guest_page)) {
 		struct intel_vgpu_guest_page *gp;
 		struct intel_vgpu_guest_page *gp;
 
 

+ 1 - 1
drivers/gpu/drm/i915/gvt/render.c

@@ -293,7 +293,7 @@ static void switch_mmio_to_vgpu(struct intel_vgpu *vgpu, int ring_id)
 		 */
 		 */
 		if (mmio->in_context &&
 		if (mmio->in_context &&
 				((ctx_ctrl & inhibit_mask) != inhibit_mask) &&
 				((ctx_ctrl & inhibit_mask) != inhibit_mask) &&
-				i915.enable_execlists)
+				i915_modparams.enable_execlists)
 			continue;
 			continue;
 
 
 		if (mmio->mask)
 		if (mmio->mask)

+ 80 - 34
drivers/gpu/drm/i915/gvt/scheduler.c

@@ -87,7 +87,7 @@ static int populate_shadow_context(struct intel_vgpu_workload *workload)
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 
 
-		page = i915_gem_object_get_page(ctx_obj, LRC_PPHWSP_PN + i);
+		page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i);
 		dst = kmap(page);
 		dst = kmap(page);
 		intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst,
 		intel_gvt_hypervisor_read_gpa(vgpu, context_gpa, dst,
 				GTT_PAGE_SIZE);
 				GTT_PAGE_SIZE);
@@ -201,6 +201,43 @@ static void shadow_context_descriptor_update(struct i915_gem_context *ctx,
 	ce->lrc_desc = desc;
 	ce->lrc_desc = desc;
 }
 }
 
 
+static int copy_workload_to_ring_buffer(struct intel_vgpu_workload *workload)
+{
+	struct intel_vgpu *vgpu = workload->vgpu;
+	void *shadow_ring_buffer_va;
+	u32 *cs;
+
+	/* allocate shadow ring buffer */
+	cs = intel_ring_begin(workload->req, workload->rb_len / sizeof(u32));
+	if (IS_ERR(cs)) {
+		gvt_vgpu_err("fail to alloc size =%ld shadow  ring buffer\n",
+			workload->rb_len);
+		return PTR_ERR(cs);
+	}
+
+	shadow_ring_buffer_va = workload->shadow_ring_buffer_va;
+
+	/* get shadow ring buffer va */
+	workload->shadow_ring_buffer_va = cs;
+
+	memcpy(cs, shadow_ring_buffer_va,
+			workload->rb_len);
+
+	cs += workload->rb_len / sizeof(u32);
+	intel_ring_advance(workload->req, cs);
+
+	return 0;
+}
+
+void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx)
+{
+	if (!wa_ctx->indirect_ctx.obj)
+		return;
+
+	i915_gem_object_unpin_map(wa_ctx->indirect_ctx.obj);
+	i915_gem_object_put(wa_ctx->indirect_ctx.obj);
+}
+
 /**
 /**
  * intel_gvt_scan_and_shadow_workload - audit the workload by scanning and
  * intel_gvt_scan_and_shadow_workload - audit the workload by scanning and
  * shadow it as well, include ringbuffer,wa_ctx and ctx.
  * shadow it as well, include ringbuffer,wa_ctx and ctx.
@@ -214,8 +251,10 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload)
 	int ring_id = workload->ring_id;
 	int ring_id = workload->ring_id;
 	struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
 	struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
 	struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
 	struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
+	struct intel_engine_cs *engine = dev_priv->engine[ring_id];
 	struct drm_i915_gem_request *rq;
 	struct drm_i915_gem_request *rq;
 	struct intel_vgpu *vgpu = workload->vgpu;
 	struct intel_vgpu *vgpu = workload->vgpu;
+	struct intel_ring *ring;
 	int ret;
 	int ret;
 
 
 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
 	lockdep_assert_held(&dev_priv->drm.struct_mutex);
@@ -231,35 +270,56 @@ int intel_gvt_scan_and_shadow_workload(struct intel_vgpu_workload *workload)
 		shadow_context_descriptor_update(shadow_ctx,
 		shadow_context_descriptor_update(shadow_ctx,
 					dev_priv->engine[ring_id]);
 					dev_priv->engine[ring_id]);
 
 
-	rq = i915_gem_request_alloc(dev_priv->engine[ring_id], shadow_ctx);
-	if (IS_ERR(rq)) {
-		gvt_vgpu_err("fail to allocate gem request\n");
-		ret = PTR_ERR(rq);
-		goto out;
-	}
-
-	gvt_dbg_sched("ring id %d get i915 gem request %p\n", ring_id, rq);
-
-	workload->req = i915_gem_request_get(rq);
-
 	ret = intel_gvt_scan_and_shadow_ringbuffer(workload);
 	ret = intel_gvt_scan_and_shadow_ringbuffer(workload);
 	if (ret)
 	if (ret)
-		goto out;
+		goto err_scan;
 
 
 	if ((workload->ring_id == RCS) &&
 	if ((workload->ring_id == RCS) &&
 	    (workload->wa_ctx.indirect_ctx.size != 0)) {
 	    (workload->wa_ctx.indirect_ctx.size != 0)) {
 		ret = intel_gvt_scan_and_shadow_wa_ctx(&workload->wa_ctx);
 		ret = intel_gvt_scan_and_shadow_wa_ctx(&workload->wa_ctx);
 		if (ret)
 		if (ret)
-			goto out;
+			goto err_scan;
+	}
+
+	/* pin shadow context by gvt even the shadow context will be pinned
+	 * when i915 alloc request. That is because gvt will update the guest
+	 * context from shadow context when workload is completed, and at that
+	 * moment, i915 may already unpined the shadow context to make the
+	 * shadow_ctx pages invalid. So gvt need to pin itself. After update
+	 * the guest context, gvt can unpin the shadow_ctx safely.
+	 */
+	ring = engine->context_pin(engine, shadow_ctx);
+	if (IS_ERR(ring)) {
+		ret = PTR_ERR(ring);
+		gvt_vgpu_err("fail to pin shadow context\n");
+		goto err_shadow;
 	}
 	}
 
 
 	ret = populate_shadow_context(workload);
 	ret = populate_shadow_context(workload);
 	if (ret)
 	if (ret)
-		goto out;
+		goto err_unpin;
 
 
+	rq = i915_gem_request_alloc(dev_priv->engine[ring_id], shadow_ctx);
+	if (IS_ERR(rq)) {
+		gvt_vgpu_err("fail to allocate gem request\n");
+		ret = PTR_ERR(rq);
+		goto err_unpin;
+	}
+
+	gvt_dbg_sched("ring id %d get i915 gem request %p\n", ring_id, rq);
+
+	workload->req = i915_gem_request_get(rq);
+	ret = copy_workload_to_ring_buffer(workload);
+	if (ret)
+		goto err_unpin;
 	workload->shadowed = true;
 	workload->shadowed = true;
+	return 0;
 
 
-out:
+err_unpin:
+	engine->context_unpin(engine, shadow_ctx);
+err_shadow:
+	release_shadow_wa_ctx(&workload->wa_ctx);
+err_scan:
 	return ret;
 	return ret;
 }
 }
 
 
@@ -269,8 +329,6 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
 	struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
 	struct i915_gem_context *shadow_ctx = workload->vgpu->shadow_ctx;
 	struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
 	struct drm_i915_private *dev_priv = workload->vgpu->gvt->dev_priv;
 	struct intel_engine_cs *engine = dev_priv->engine[ring_id];
 	struct intel_engine_cs *engine = dev_priv->engine[ring_id];
-	struct intel_vgpu *vgpu = workload->vgpu;
-	struct intel_ring *ring;
 	int ret = 0;
 	int ret = 0;
 
 
 	gvt_dbg_sched("ring id %d prepare to dispatch workload %p\n",
 	gvt_dbg_sched("ring id %d prepare to dispatch workload %p\n",
@@ -284,22 +342,10 @@ static int dispatch_workload(struct intel_vgpu_workload *workload)
 
 
 	if (workload->prepare) {
 	if (workload->prepare) {
 		ret = workload->prepare(workload);
 		ret = workload->prepare(workload);
-		if (ret)
+		if (ret) {
+			engine->context_unpin(engine, shadow_ctx);
 			goto out;
 			goto out;
-	}
-
-	/* pin shadow context by gvt even the shadow context will be pinned
-	 * when i915 alloc request. That is because gvt will update the guest
-	 * context from shadow context when workload is completed, and at that
-	 * moment, i915 may already unpined the shadow context to make the
-	 * shadow_ctx pages invalid. So gvt need to pin itself. After update
-	 * the guest context, gvt can unpin the shadow_ctx safely.
-	 */
-	ring = engine->context_pin(engine, shadow_ctx);
-	if (IS_ERR(ring)) {
-		ret = PTR_ERR(ring);
-		gvt_vgpu_err("fail to pin shadow context\n");
-		goto out;
+		}
 	}
 	}
 
 
 out:
 out:
@@ -408,7 +454,7 @@ static void update_guest_context(struct intel_vgpu_workload *workload)
 			return;
 			return;
 		}
 		}
 
 
-		page = i915_gem_object_get_page(ctx_obj, LRC_PPHWSP_PN + i);
+		page = i915_gem_object_get_page(ctx_obj, LRC_HEADER_PAGES + i);
 		src = kmap(page);
 		src = kmap(page);
 		intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src,
 		intel_gvt_hypervisor_write_gpa(vgpu, context_gpa, src,
 				GTT_PAGE_SIZE);
 				GTT_PAGE_SIZE);

+ 1 - 0
drivers/gpu/drm/i915/gvt/scheduler.h

@@ -140,4 +140,5 @@ int intel_vgpu_init_gvt_context(struct intel_vgpu *vgpu);
 
 
 void intel_vgpu_clean_gvt_context(struct intel_vgpu *vgpu);
 void intel_vgpu_clean_gvt_context(struct intel_vgpu *vgpu);
 
 
+void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx);
 #endif
 #endif

+ 86 - 30
drivers/gpu/drm/i915/i915_debugfs.c

@@ -67,7 +67,7 @@ static int i915_capabilities(struct seq_file *m, void *data)
 #undef PRINT_FLAG
 #undef PRINT_FLAG
 
 
 	kernel_param_lock(THIS_MODULE);
 	kernel_param_lock(THIS_MODULE);
-#define PRINT_PARAM(T, x) seq_print_param(m, #x, #T, &i915.x);
+#define PRINT_PARAM(T, x, ...) seq_print_param(m, #x, #T, &i915_modparams.x);
 	I915_PARAMS_FOR_EACH(PRINT_PARAM);
 	I915_PARAMS_FOR_EACH(PRINT_PARAM);
 #undef PRINT_PARAM
 #undef PRINT_PARAM
 	kernel_param_unlock(THIS_MODULE);
 	kernel_param_unlock(THIS_MODULE);
@@ -1267,7 +1267,7 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused)
 	if (waitqueue_active(&dev_priv->gpu_error.reset_queue))
 	if (waitqueue_active(&dev_priv->gpu_error.reset_queue))
 		seq_puts(m, "struct_mutex blocked for reset\n");
 		seq_puts(m, "struct_mutex blocked for reset\n");
 
 
-	if (!i915.enable_hangcheck) {
+	if (!i915_modparams.enable_hangcheck) {
 		seq_puts(m, "Hangcheck disabled\n");
 		seq_puts(m, "Hangcheck disabled\n");
 		return 0;
 		return 0;
 	}
 	}
@@ -1422,6 +1422,9 @@ static int i915_forcewake_domains(struct seq_file *m, void *data)
 	struct intel_uncore_forcewake_domain *fw_domain;
 	struct intel_uncore_forcewake_domain *fw_domain;
 	unsigned int tmp;
 	unsigned int tmp;
 
 
+	seq_printf(m, "user.bypass_count = %u\n",
+		   i915->uncore.user_forcewake.count);
+
 	for_each_fw_domain(fw_domain, i915, tmp)
 	for_each_fw_domain(fw_domain, i915, tmp)
 		seq_printf(m, "%s.wake_count = %u\n",
 		seq_printf(m, "%s.wake_count = %u\n",
 			   intel_uncore_forcewake_domain_to_str(fw_domain->id),
 			   intel_uncore_forcewake_domain_to_str(fw_domain->id),
@@ -1699,7 +1702,7 @@ static int i915_ips_status(struct seq_file *m, void *unused)
 	intel_runtime_pm_get(dev_priv);
 	intel_runtime_pm_get(dev_priv);
 
 
 	seq_printf(m, "Enabled by kernel parameter: %s\n",
 	seq_printf(m, "Enabled by kernel parameter: %s\n",
-		   yesno(i915.enable_ips));
+		   yesno(i915_modparams.enable_ips));
 
 
 	if (INTEL_GEN(dev_priv) >= 8) {
 	if (INTEL_GEN(dev_priv) >= 8) {
 		seq_puts(m, "Currently: unknown\n");
 		seq_puts(m, "Currently: unknown\n");
@@ -2014,7 +2017,7 @@ static int i915_dump_lrc(struct seq_file *m, void *unused)
 	enum intel_engine_id id;
 	enum intel_engine_id id;
 	int ret;
 	int ret;
 
 
-	if (!i915.enable_execlists) {
+	if (!i915_modparams.enable_execlists) {
 		seq_printf(m, "Logical Ring Contexts are disabled\n");
 		seq_printf(m, "Logical Ring Contexts are disabled\n");
 		return 0;
 		return 0;
 	}
 	}
@@ -2443,12 +2446,8 @@ static void i915_guc_client_info(struct seq_file *m,
 
 
 	seq_printf(m, "\tPriority %d, GuC stage index: %u, PD offset 0x%x\n",
 	seq_printf(m, "\tPriority %d, GuC stage index: %u, PD offset 0x%x\n",
 		client->priority, client->stage_id, client->proc_desc_offset);
 		client->priority, client->stage_id, client->proc_desc_offset);
-	seq_printf(m, "\tDoorbell id %d, offset: 0x%lx, cookie 0x%x\n",
-		client->doorbell_id, client->doorbell_offset, client->doorbell_cookie);
-	seq_printf(m, "\tWQ size %d, offset: 0x%x, tail %d\n",
-		client->wq_size, client->wq_offset, client->wq_tail);
-
-	seq_printf(m, "\tWork queue full: %u\n", client->no_wq_space);
+	seq_printf(m, "\tDoorbell id %d, offset: 0x%lx\n",
+		client->doorbell_id, client->doorbell_offset);
 
 
 	for_each_engine(engine, dev_priv, id) {
 	for_each_engine(engine, dev_priv, id) {
 		u64 submissions = client->submissions[id];
 		u64 submissions = client->submissions[id];
@@ -2594,7 +2593,7 @@ static int i915_guc_log_control_get(void *data, u64 *val)
 	if (!dev_priv->guc.log.vma)
 	if (!dev_priv->guc.log.vma)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	*val = i915.guc_log_level;
+	*val = i915_modparams.guc_log_level;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -3312,7 +3311,9 @@ static int i915_engine_info(struct seq_file *m, void *unused)
 		seq_printf(m, "\tBBADDR: 0x%08x_%08x\n",
 		seq_printf(m, "\tBBADDR: 0x%08x_%08x\n",
 			   upper_32_bits(addr), lower_32_bits(addr));
 			   upper_32_bits(addr), lower_32_bits(addr));
 
 
-		if (i915.enable_execlists) {
+		if (i915_modparams.enable_execlists) {
+			const u32 *hws = &engine->status_page.page_addr[I915_HWS_CSB_BUF0_INDEX];
+			struct intel_engine_execlists * const execlists = &engine->execlists;
 			u32 ptr, read, write;
 			u32 ptr, read, write;
 			unsigned int idx;
 			unsigned int idx;
 
 
@@ -3323,8 +3324,10 @@ static int i915_engine_info(struct seq_file *m, void *unused)
 			ptr = I915_READ(RING_CONTEXT_STATUS_PTR(engine));
 			ptr = I915_READ(RING_CONTEXT_STATUS_PTR(engine));
 			read = GEN8_CSB_READ_PTR(ptr);
 			read = GEN8_CSB_READ_PTR(ptr);
 			write = GEN8_CSB_WRITE_PTR(ptr);
 			write = GEN8_CSB_WRITE_PTR(ptr);
-			seq_printf(m, "\tExeclist CSB read %d, write %d, interrupt posted? %s\n",
-				   read, write,
+			seq_printf(m, "\tExeclist CSB read %d [%d cached], write %d [%d from hws], interrupt posted? %s\n",
+				   read, execlists->csb_head,
+				   write,
+				   intel_read_status_page(engine, intel_hws_csb_write_index(engine->i915)),
 				   yesno(test_bit(ENGINE_IRQ_EXECLIST,
 				   yesno(test_bit(ENGINE_IRQ_EXECLIST,
 						  &engine->irq_posted)));
 						  &engine->irq_posted)));
 			if (read >= GEN8_CSB_ENTRIES)
 			if (read >= GEN8_CSB_ENTRIES)
@@ -3335,18 +3338,19 @@ static int i915_engine_info(struct seq_file *m, void *unused)
 				write += GEN8_CSB_ENTRIES;
 				write += GEN8_CSB_ENTRIES;
 			while (read < write) {
 			while (read < write) {
 				idx = ++read % GEN8_CSB_ENTRIES;
 				idx = ++read % GEN8_CSB_ENTRIES;
-				seq_printf(m, "\tExeclist CSB[%d]: 0x%08x, context: %d\n",
+				seq_printf(m, "\tExeclist CSB[%d]: 0x%08x [0x%08x in hwsp], context: %d [%d in hwsp]\n",
 					   idx,
 					   idx,
 					   I915_READ(RING_CONTEXT_STATUS_BUF_LO(engine, idx)),
 					   I915_READ(RING_CONTEXT_STATUS_BUF_LO(engine, idx)),
-					   I915_READ(RING_CONTEXT_STATUS_BUF_HI(engine, idx)));
+					   hws[idx * 2],
+					   I915_READ(RING_CONTEXT_STATUS_BUF_HI(engine, idx)),
+					   hws[idx * 2 + 1]);
 			}
 			}
 
 
 			rcu_read_lock();
 			rcu_read_lock();
-			for (idx = 0; idx < ARRAY_SIZE(engine->execlist_port); idx++) {
+			for (idx = 0; idx < execlists_num_ports(execlists); idx++) {
 				unsigned int count;
 				unsigned int count;
 
 
-				rq = port_unpack(&engine->execlist_port[idx],
-						 &count);
+				rq = port_unpack(&execlists->port[idx], &count);
 				if (rq) {
 				if (rq) {
 					seq_printf(m, "\t\tELSP[%d] count=%d, ",
 					seq_printf(m, "\t\tELSP[%d] count=%d, ",
 						   idx, count);
 						   idx, count);
@@ -3359,7 +3363,7 @@ static int i915_engine_info(struct seq_file *m, void *unused)
 			rcu_read_unlock();
 			rcu_read_unlock();
 
 
 			spin_lock_irq(&engine->timeline->lock);
 			spin_lock_irq(&engine->timeline->lock);
-			for (rb = engine->execlist_first; rb; rb = rb_next(rb)){
+			for (rb = execlists->first; rb; rb = rb_next(rb)) {
 				struct i915_priolist *p =
 				struct i915_priolist *p =
 					rb_entry(rb, typeof(*p), node);
 					rb_entry(rb, typeof(*p), node);
 
 
@@ -3403,7 +3407,7 @@ static int i915_semaphore_status(struct seq_file *m, void *unused)
 	enum intel_engine_id id;
 	enum intel_engine_id id;
 	int j, ret;
 	int j, ret;
 
 
-	if (!i915.semaphores) {
+	if (!i915_modparams.semaphores) {
 		seq_puts(m, "Semaphores are disabled\n");
 		seq_puts(m, "Semaphores are disabled\n");
 		return 0;
 		return 0;
 	}
 	}
@@ -3523,6 +3527,57 @@ static int i915_wa_registers(struct seq_file *m, void *unused)
 	return 0;
 	return 0;
 }
 }
 
 
+static int i915_ipc_status_show(struct seq_file *m, void *data)
+{
+	struct drm_i915_private *dev_priv = m->private;
+
+	seq_printf(m, "Isochronous Priority Control: %s\n",
+			yesno(dev_priv->ipc_enabled));
+	return 0;
+}
+
+static int i915_ipc_status_open(struct inode *inode, struct file *file)
+{
+	struct drm_i915_private *dev_priv = inode->i_private;
+
+	if (!HAS_IPC(dev_priv))
+		return -ENODEV;
+
+	return single_open(file, i915_ipc_status_show, dev_priv);
+}
+
+static ssize_t i915_ipc_status_write(struct file *file, const char __user *ubuf,
+				     size_t len, loff_t *offp)
+{
+	struct seq_file *m = file->private_data;
+	struct drm_i915_private *dev_priv = m->private;
+	int ret;
+	bool enable;
+
+	ret = kstrtobool_from_user(ubuf, len, &enable);
+	if (ret < 0)
+		return ret;
+
+	intel_runtime_pm_get(dev_priv);
+	if (!dev_priv->ipc_enabled && enable)
+		DRM_INFO("Enabling IPC: WM will be proper only after next commit\n");
+	dev_priv->wm.distrust_bios_wm = true;
+	dev_priv->ipc_enabled = enable;
+	intel_enable_ipc(dev_priv);
+	intel_runtime_pm_put(dev_priv);
+
+	return len;
+}
+
+static const struct file_operations i915_ipc_status_fops = {
+	.owner = THIS_MODULE,
+	.open = i915_ipc_status_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = i915_ipc_status_write
+};
+
 static int i915_ddb_info(struct seq_file *m, void *unused)
 static int i915_ddb_info(struct seq_file *m, void *unused)
 {
 {
 	struct drm_i915_private *dev_priv = node_to_i915(m->private);
 	struct drm_i915_private *dev_priv = node_to_i915(m->private);
@@ -4674,26 +4729,26 @@ static int i915_sseu_status(struct seq_file *m, void *unused)
 
 
 static int i915_forcewake_open(struct inode *inode, struct file *file)
 static int i915_forcewake_open(struct inode *inode, struct file *file)
 {
 {
-	struct drm_i915_private *dev_priv = inode->i_private;
+	struct drm_i915_private *i915 = inode->i_private;
 
 
-	if (INTEL_GEN(dev_priv) < 6)
+	if (INTEL_GEN(i915) < 6)
 		return 0;
 		return 0;
 
 
-	intel_runtime_pm_get(dev_priv);
-	intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+	intel_runtime_pm_get(i915);
+	intel_uncore_forcewake_user_get(i915);
 
 
 	return 0;
 	return 0;
 }
 }
 
 
 static int i915_forcewake_release(struct inode *inode, struct file *file)
 static int i915_forcewake_release(struct inode *inode, struct file *file)
 {
 {
-	struct drm_i915_private *dev_priv = inode->i_private;
+	struct drm_i915_private *i915 = inode->i_private;
 
 
-	if (INTEL_GEN(dev_priv) < 6)
+	if (INTEL_GEN(i915) < 6)
 		return 0;
 		return 0;
 
 
-	intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
-	intel_runtime_pm_put(dev_priv);
+	intel_uncore_forcewake_user_put(i915);
+	intel_runtime_pm_put(i915);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -4859,7 +4914,8 @@ static const struct i915_debugfs_files {
 	{"i915_dp_test_type", &i915_displayport_test_type_fops},
 	{"i915_dp_test_type", &i915_displayport_test_type_fops},
 	{"i915_dp_test_active", &i915_displayport_test_active_fops},
 	{"i915_dp_test_active", &i915_displayport_test_active_fops},
 	{"i915_guc_log_control", &i915_guc_log_control_fops},
 	{"i915_guc_log_control", &i915_guc_log_control_fops},
-	{"i915_hpd_storm_ctl", &i915_hpd_storm_ctl_fops}
+	{"i915_hpd_storm_ctl", &i915_hpd_storm_ctl_fops},
+	{"i915_ipc_status", &i915_ipc_status_fops}
 };
 };
 
 
 int i915_debugfs_register(struct drm_i915_private *dev_priv)
 int i915_debugfs_register(struct drm_i915_private *dev_priv)

+ 26 - 16
drivers/gpu/drm/i915/i915_drv.c

@@ -58,12 +58,12 @@ static unsigned int i915_load_fail_count;
 
 
 bool __i915_inject_load_failure(const char *func, int line)
 bool __i915_inject_load_failure(const char *func, int line)
 {
 {
-	if (i915_load_fail_count >= i915.inject_load_failure)
+	if (i915_load_fail_count >= i915_modparams.inject_load_failure)
 		return false;
 		return false;
 
 
-	if (++i915_load_fail_count == i915.inject_load_failure) {
+	if (++i915_load_fail_count == i915_modparams.inject_load_failure) {
 		DRM_INFO("Injecting failure at checkpoint %u [%s:%d]\n",
 		DRM_INFO("Injecting failure at checkpoint %u [%s:%d]\n",
-			 i915.inject_load_failure, func, line);
+			 i915_modparams.inject_load_failure, func, line);
 		return true;
 		return true;
 	}
 	}
 
 
@@ -106,8 +106,8 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level,
 
 
 static bool i915_error_injected(struct drm_i915_private *dev_priv)
 static bool i915_error_injected(struct drm_i915_private *dev_priv)
 {
 {
-	return i915.inject_load_failure &&
-	       i915_load_fail_count == i915.inject_load_failure;
+	return i915_modparams.inject_load_failure &&
+	       i915_load_fail_count == i915_modparams.inject_load_failure;
 }
 }
 
 
 #define i915_load_error(dev_priv, fmt, ...)				     \
 #define i915_load_error(dev_priv, fmt, ...)				     \
@@ -321,7 +321,7 @@ static int i915_getparam(struct drm_device *dev, void *data,
 		value = USES_PPGTT(dev_priv);
 		value = USES_PPGTT(dev_priv);
 		break;
 		break;
 	case I915_PARAM_HAS_SEMAPHORES:
 	case I915_PARAM_HAS_SEMAPHORES:
-		value = i915.semaphores;
+		value = i915_modparams.semaphores;
 		break;
 		break;
 	case I915_PARAM_HAS_SECURE_BATCHES:
 	case I915_PARAM_HAS_SECURE_BATCHES:
 		value = capable(CAP_SYS_ADMIN);
 		value = capable(CAP_SYS_ADMIN);
@@ -340,7 +340,8 @@ static int i915_getparam(struct drm_device *dev, void *data,
 			return -ENODEV;
 			return -ENODEV;
 		break;
 		break;
 	case I915_PARAM_HAS_GPU_RESET:
 	case I915_PARAM_HAS_GPU_RESET:
-		value = i915.enable_hangcheck && intel_has_gpu_reset(dev_priv);
+		value = i915_modparams.enable_hangcheck &&
+			intel_has_gpu_reset(dev_priv);
 		if (value && intel_has_reset_engine(dev_priv))
 		if (value && intel_has_reset_engine(dev_priv))
 			value = 2;
 			value = 2;
 		break;
 		break;
@@ -869,6 +870,10 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
 	memcpy(device_info, match_info, sizeof(*device_info));
 	memcpy(device_info, match_info, sizeof(*device_info));
 	device_info->device_id = dev_priv->drm.pdev->device;
 	device_info->device_id = dev_priv->drm.pdev->device;
 
 
+	BUILD_BUG_ON(INTEL_MAX_PLATFORMS >
+		     sizeof(device_info->platform_mask) * BITS_PER_BYTE);
+	device_info->platform_mask = BIT(device_info->platform);
+
 	BUG_ON(device_info->gen > sizeof(device_info->gen_mask) * BITS_PER_BYTE);
 	BUG_ON(device_info->gen > sizeof(device_info->gen_mask) * BITS_PER_BYTE);
 	device_info->gen_mask = BIT(device_info->gen - 1);
 	device_info->gen_mask = BIT(device_info->gen - 1);
 
 
@@ -1031,9 +1036,9 @@ static void i915_driver_cleanup_mmio(struct drm_i915_private *dev_priv)
 
 
 static void intel_sanitize_options(struct drm_i915_private *dev_priv)
 static void intel_sanitize_options(struct drm_i915_private *dev_priv)
 {
 {
-	i915.enable_execlists =
+	i915_modparams.enable_execlists =
 		intel_sanitize_enable_execlists(dev_priv,
 		intel_sanitize_enable_execlists(dev_priv,
-						i915.enable_execlists);
+						i915_modparams.enable_execlists);
 
 
 	/*
 	/*
 	 * i915.enable_ppgtt is read-only, so do an early pass to validate the
 	 * i915.enable_ppgtt is read-only, so do an early pass to validate the
@@ -1041,12 +1046,15 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv)
 	 * do this now so that we can print out any log messages once rather
 	 * do this now so that we can print out any log messages once rather
 	 * than every time we check intel_enable_ppgtt().
 	 * than every time we check intel_enable_ppgtt().
 	 */
 	 */
-	i915.enable_ppgtt =
-		intel_sanitize_enable_ppgtt(dev_priv, i915.enable_ppgtt);
-	DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt);
+	i915_modparams.enable_ppgtt =
+		intel_sanitize_enable_ppgtt(dev_priv,
+					    i915_modparams.enable_ppgtt);
+	DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915_modparams.enable_ppgtt);
 
 
-	i915.semaphores = intel_sanitize_semaphores(dev_priv, i915.semaphores);
-	DRM_DEBUG_DRIVER("use GPU semaphores? %s\n", yesno(i915.semaphores));
+	i915_modparams.semaphores =
+		intel_sanitize_semaphores(dev_priv, i915_modparams.semaphores);
+	DRM_DEBUG_DRIVER("use GPU semaphores? %s\n",
+			 yesno(i915_modparams.semaphores));
 
 
 	intel_uc_sanitize_options(dev_priv);
 	intel_uc_sanitize_options(dev_priv);
 
 
@@ -1277,7 +1285,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
 	int ret;
 	int ret;
 
 
 	/* Enable nuclear pageflip on ILK+ */
 	/* Enable nuclear pageflip on ILK+ */
-	if (!i915.nuclear_pageflip && match_info->gen < 5)
+	if (!i915_modparams.nuclear_pageflip && match_info->gen < 5)
 		driver.driver_features &= ~DRIVER_ATOMIC;
 		driver.driver_features &= ~DRIVER_ATOMIC;
 
 
 	ret = -ENOMEM;
 	ret = -ENOMEM;
@@ -1341,7 +1349,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent)
 
 
 	intel_runtime_pm_enable(dev_priv);
 	intel_runtime_pm_enable(dev_priv);
 
 
-	dev_priv->ipc_enabled = false;
+	intel_init_ipc(dev_priv);
 
 
 	if (IS_ENABLED(CONFIG_DRM_I915_DEBUG))
 	if (IS_ENABLED(CONFIG_DRM_I915_DEBUG))
 		DRM_INFO("DRM_I915_DEBUG enabled\n");
 		DRM_INFO("DRM_I915_DEBUG enabled\n");
@@ -2609,6 +2617,8 @@ static int intel_runtime_resume(struct device *kdev)
 	if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
 	if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
 		intel_hpd_init(dev_priv);
 		intel_hpd_init(dev_priv);
 
 
+	intel_enable_ipc(dev_priv);
+
 	enable_rpm_wakeref_asserts(dev_priv);
 	enable_rpm_wakeref_asserts(dev_priv);
 
 
 	if (ret)
 	if (ret)

+ 131 - 67
drivers/gpu/drm/i915/i915_drv.h

@@ -80,8 +80,8 @@
 
 
 #define DRIVER_NAME		"i915"
 #define DRIVER_NAME		"i915"
 #define DRIVER_DESC		"Intel Graphics"
 #define DRIVER_DESC		"Intel Graphics"
-#define DRIVER_DATE		"20170907"
-#define DRIVER_TIMESTAMP	1504772900
+#define DRIVER_DATE		"20170929"
+#define DRIVER_TIMESTAMP	1506682238
 
 
 /* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
 /* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
  * WARN_ON()) for hw state sanity checks to check for unexpected conditions
  * WARN_ON()) for hw state sanity checks to check for unexpected conditions
@@ -93,7 +93,7 @@
 #define I915_STATE_WARN(condition, format...) ({			\
 #define I915_STATE_WARN(condition, format...) ({			\
 	int __ret_warn_on = !!(condition);				\
 	int __ret_warn_on = !!(condition);				\
 	if (unlikely(__ret_warn_on))					\
 	if (unlikely(__ret_warn_on))					\
-		if (!WARN(i915.verbose_state_checks, format))		\
+		if (!WARN(i915_modparams.verbose_state_checks, format))	\
 			DRM_ERROR(format);				\
 			DRM_ERROR(format);				\
 	unlikely(__ret_warn_on);					\
 	unlikely(__ret_warn_on);					\
 })
 })
@@ -126,7 +126,7 @@ static inline uint_fixed_16_16_t u32_to_fixed16(uint32_t val)
 {
 {
 	uint_fixed_16_16_t fp;
 	uint_fixed_16_16_t fp;
 
 
-	WARN_ON(val >> 16);
+	WARN_ON(val > U16_MAX);
 
 
 	fp.val = val << 16;
 	fp.val = val << 16;
 	return fp;
 	return fp;
@@ -163,8 +163,8 @@ static inline uint_fixed_16_16_t max_fixed16(uint_fixed_16_16_t max1,
 static inline uint_fixed_16_16_t clamp_u64_to_fixed16(uint64_t val)
 static inline uint_fixed_16_16_t clamp_u64_to_fixed16(uint64_t val)
 {
 {
 	uint_fixed_16_16_t fp;
 	uint_fixed_16_16_t fp;
-	WARN_ON(val >> 32);
-	fp.val = clamp_t(uint32_t, val, 0, ~0);
+	WARN_ON(val > U32_MAX);
+	fp.val = (uint32_t) val;
 	return fp;
 	return fp;
 }
 }
 
 
@@ -181,8 +181,8 @@ static inline uint32_t mul_round_up_u32_fixed16(uint32_t val,
 
 
 	intermediate_val = (uint64_t) val * mul.val;
 	intermediate_val = (uint64_t) val * mul.val;
 	intermediate_val = DIV_ROUND_UP_ULL(intermediate_val, 1 << 16);
 	intermediate_val = DIV_ROUND_UP_ULL(intermediate_val, 1 << 16);
-	WARN_ON(intermediate_val >> 32);
-	return clamp_t(uint32_t, intermediate_val, 0, ~0);
+	WARN_ON(intermediate_val > U32_MAX);
+	return (uint32_t) intermediate_val;
 }
 }
 
 
 static inline uint_fixed_16_16_t mul_fixed16(uint_fixed_16_16_t val,
 static inline uint_fixed_16_16_t mul_fixed16(uint_fixed_16_16_t val,
@@ -211,8 +211,8 @@ static inline uint32_t div_round_up_u32_fixed16(uint32_t val,
 
 
 	interm_val = (uint64_t)val << 16;
 	interm_val = (uint64_t)val << 16;
 	interm_val = DIV_ROUND_UP_ULL(interm_val, d.val);
 	interm_val = DIV_ROUND_UP_ULL(interm_val, d.val);
-	WARN_ON(interm_val >> 32);
-	return clamp_t(uint32_t, interm_val, 0, ~0);
+	WARN_ON(interm_val > U32_MAX);
+	return (uint32_t) interm_val;
 }
 }
 
 
 static inline uint_fixed_16_16_t mul_u32_fixed16(uint32_t val,
 static inline uint_fixed_16_16_t mul_u32_fixed16(uint32_t val,
@@ -776,7 +776,6 @@ struct intel_csr {
 	func(has_fpga_dbg); \
 	func(has_fpga_dbg); \
 	func(has_full_ppgtt); \
 	func(has_full_ppgtt); \
 	func(has_full_48bit_ppgtt); \
 	func(has_full_48bit_ppgtt); \
-	func(has_gmbus_irq); \
 	func(has_gmch_display); \
 	func(has_gmch_display); \
 	func(has_guc); \
 	func(has_guc); \
 	func(has_guc_ct); \
 	func(has_guc_ct); \
@@ -797,7 +796,8 @@ struct intel_csr {
 	func(cursor_needs_physical); \
 	func(cursor_needs_physical); \
 	func(hws_needs_physical); \
 	func(hws_needs_physical); \
 	func(overlay_needs_physical); \
 	func(overlay_needs_physical); \
-	func(supports_tv);
+	func(supports_tv); \
+	func(has_ipc);
 
 
 struct sseu_dev_info {
 struct sseu_dev_info {
 	u8 slice_mask;
 	u8 slice_mask;
@@ -851,21 +851,28 @@ enum intel_platform {
 };
 };
 
 
 struct intel_device_info {
 struct intel_device_info {
-	u32 display_mmio_offset;
 	u16 device_id;
 	u16 device_id;
-	u8 num_pipes;
-	u8 num_sprites[I915_MAX_PIPES];
-	u8 num_scalers[I915_MAX_PIPES];
-	u8 gen;
 	u16 gen_mask;
 	u16 gen_mask;
-	enum intel_platform platform;
+
+	u8 gen;
 	u8 gt; /* GT number, 0 if undefined */
 	u8 gt; /* GT number, 0 if undefined */
-	u8 ring_mask; /* Rings supported by the HW */
 	u8 num_rings;
 	u8 num_rings;
+	u8 ring_mask; /* Rings supported by the HW */
+
+	enum intel_platform platform;
+	u32 platform_mask;
+
+	u32 display_mmio_offset;
+
+	u8 num_pipes;
+	u8 num_sprites[I915_MAX_PIPES];
+	u8 num_scalers[I915_MAX_PIPES];
+
 #define DEFINE_FLAG(name) u8 name:1
 #define DEFINE_FLAG(name) u8 name:1
 	DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG);
 	DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG);
 #undef DEFINE_FLAG
 #undef DEFINE_FLAG
 	u16 ddb_size; /* in blocks */
 	u16 ddb_size; /* in blocks */
+
 	/* Register offsets for the various display pipes and transcoders */
 	/* Register offsets for the various display pipes and transcoders */
 	int pipe_offsets[I915_MAX_TRANSCODERS];
 	int pipe_offsets[I915_MAX_TRANSCODERS];
 	int trans_offsets[I915_MAX_TRANSCODERS];
 	int trans_offsets[I915_MAX_TRANSCODERS];
@@ -1000,7 +1007,8 @@ struct i915_gpu_state {
 			u32 seqno;
 			u32 seqno;
 			u32 head;
 			u32 head;
 			u32 tail;
 			u32 tail;
-		} *requests, execlist[2];
+		} *requests, execlist[EXECLIST_MAX_PORTS];
+		unsigned int num_ports;
 
 
 		struct drm_i915_error_waiter {
 		struct drm_i915_error_waiter {
 			char comm[TASK_COMM_LEN];
 			char comm[TASK_COMM_LEN];
@@ -1178,6 +1186,14 @@ struct i915_psr {
 	bool y_cord_support;
 	bool y_cord_support;
 	bool colorimetry_support;
 	bool colorimetry_support;
 	bool alpm;
 	bool alpm;
+
+	void (*enable_source)(struct intel_dp *,
+			      const struct intel_crtc_state *);
+	void (*disable_source)(struct intel_dp *,
+			       const struct intel_crtc_state *);
+	void (*enable_sink)(struct intel_dp *);
+	void (*activate)(struct intel_dp *);
+	void (*setup_vsc)(struct intel_dp *, const struct intel_crtc_state *);
 };
 };
 
 
 enum intel_pch {
 enum intel_pch {
@@ -1836,6 +1852,20 @@ struct skl_wm_level {
 	uint8_t plane_res_l;
 	uint8_t plane_res_l;
 };
 };
 
 
+/* Stores plane specific WM parameters */
+struct skl_wm_params {
+	bool x_tiled, y_tiled;
+	bool rc_surface;
+	uint32_t width;
+	uint8_t cpp;
+	uint32_t plane_pixel_rate;
+	uint32_t y_min_scanlines;
+	uint32_t plane_bytes_per_line;
+	uint_fixed_16_16_t plane_blocks_per_line;
+	uint_fixed_16_16_t y_tile_minimum;
+	uint32_t linetime_us;
+};
+
 /*
 /*
  * This struct helps tracking the state needed for runtime PM, which puts the
  * This struct helps tracking the state needed for runtime PM, which puts the
  * device in PCI D3 state. Notice that when this happens, nothing on the
  * device in PCI D3 state. Notice that when this happens, nothing on the
@@ -2331,6 +2361,8 @@ struct drm_i915_private {
 	DECLARE_HASHTABLE(mm_structs, 7);
 	DECLARE_HASHTABLE(mm_structs, 7);
 	struct mutex mm_lock;
 	struct mutex mm_lock;
 
 
+	struct intel_ppat ppat;
+
 	/* Kernel Modesetting */
 	/* Kernel Modesetting */
 
 
 	struct intel_crtc *plane_to_crtc_mapping[I915_MAX_PIPES];
 	struct intel_crtc *plane_to_crtc_mapping[I915_MAX_PIPES];
@@ -2811,8 +2843,8 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg)
 #define for_each_sgt_dma(__dmap, __iter, __sgt)				\
 #define for_each_sgt_dma(__dmap, __iter, __sgt)				\
 	for ((__iter) = __sgt_iter((__sgt)->sgl, true);			\
 	for ((__iter) = __sgt_iter((__sgt)->sgl, true);			\
 	     ((__dmap) = (__iter).dma + (__iter).curr);			\
 	     ((__dmap) = (__iter).dma + (__iter).curr);			\
-	     (((__iter).curr += PAGE_SIZE) < (__iter).max) ||		\
-	     ((__iter) = __sgt_iter(__sg_next((__iter).sgp), true), 0))
+	     (((__iter).curr += PAGE_SIZE) >= (__iter).max) ?		\
+	     (__iter) = __sgt_iter(__sg_next((__iter).sgp), true), 0 : 0)
 
 
 /**
 /**
  * for_each_sgt_page - iterate over the pages of the given sg_table
  * for_each_sgt_page - iterate over the pages of the given sg_table
@@ -2824,8 +2856,23 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg)
 	for ((__iter) = __sgt_iter((__sgt)->sgl, false);		\
 	for ((__iter) = __sgt_iter((__sgt)->sgl, false);		\
 	     ((__pp) = (__iter).pfn == 0 ? NULL :			\
 	     ((__pp) = (__iter).pfn == 0 ? NULL :			\
 	      pfn_to_page((__iter).pfn + ((__iter).curr >> PAGE_SHIFT))); \
 	      pfn_to_page((__iter).pfn + ((__iter).curr >> PAGE_SHIFT))); \
-	     (((__iter).curr += PAGE_SIZE) < (__iter).max) ||		\
-	     ((__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0))
+	     (((__iter).curr += PAGE_SIZE) >= (__iter).max) ?		\
+	     (__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0 : 0)
+
+static inline unsigned int i915_sg_segment_size(void)
+{
+	unsigned int size = swiotlb_max_segment();
+
+	if (size == 0)
+		return SCATTERLIST_MAX_SEGMENT;
+
+	size = rounddown(size, PAGE_SIZE);
+	/* swiotlb_max_segment_size can return 1 byte when it means one page. */
+	if (size < PAGE_SIZE)
+		size = PAGE_SIZE;
+
+	return size;
+}
 
 
 static inline const struct intel_device_info *
 static inline const struct intel_device_info *
 intel_info(const struct drm_i915_private *dev_priv)
 intel_info(const struct drm_i915_private *dev_priv)
@@ -2842,23 +2889,21 @@ intel_info(const struct drm_i915_private *dev_priv)
 #define INTEL_REVID(dev_priv)	((dev_priv)->drm.pdev->revision)
 #define INTEL_REVID(dev_priv)	((dev_priv)->drm.pdev->revision)
 
 
 #define GEN_FOREVER (0)
 #define GEN_FOREVER (0)
+
+#define INTEL_GEN_MASK(s, e) ( \
+	BUILD_BUG_ON_ZERO(!__builtin_constant_p(s)) + \
+	BUILD_BUG_ON_ZERO(!__builtin_constant_p(e)) + \
+	GENMASK((e) != GEN_FOREVER ? (e) - 1 : BITS_PER_LONG - 1, \
+		(s) != GEN_FOREVER ? (s) - 1 : 0) \
+)
+
 /*
 /*
  * Returns true if Gen is in inclusive range [Start, End].
  * Returns true if Gen is in inclusive range [Start, End].
  *
  *
  * Use GEN_FOREVER for unbound start and or end.
  * Use GEN_FOREVER for unbound start and or end.
  */
  */
-#define IS_GEN(dev_priv, s, e) ({ \
-	unsigned int __s = (s), __e = (e); \
-	BUILD_BUG_ON(!__builtin_constant_p(s)); \
-	BUILD_BUG_ON(!__builtin_constant_p(e)); \
-	if ((__s) != GEN_FOREVER) \
-		__s = (s) - 1; \
-	if ((__e) == GEN_FOREVER) \
-		__e = BITS_PER_LONG - 1; \
-	else \
-		__e = (e) - 1; \
-	!!((dev_priv)->info.gen_mask & GENMASK((__e), (__s))); \
-})
+#define IS_GEN(dev_priv, s, e) \
+	(!!((dev_priv)->info.gen_mask & INTEL_GEN_MASK((s), (e))))
 
 
 /*
 /*
  * Return true if revision is in range [since,until] inclusive.
  * Return true if revision is in range [since,until] inclusive.
@@ -2868,37 +2913,39 @@ intel_info(const struct drm_i915_private *dev_priv)
 #define IS_REVID(p, since, until) \
 #define IS_REVID(p, since, until) \
 	(INTEL_REVID(p) >= (since) && INTEL_REVID(p) <= (until))
 	(INTEL_REVID(p) >= (since) && INTEL_REVID(p) <= (until))
 
 
-#define IS_I830(dev_priv)	((dev_priv)->info.platform == INTEL_I830)
-#define IS_I845G(dev_priv)	((dev_priv)->info.platform == INTEL_I845G)
-#define IS_I85X(dev_priv)	((dev_priv)->info.platform == INTEL_I85X)
-#define IS_I865G(dev_priv)	((dev_priv)->info.platform == INTEL_I865G)
-#define IS_I915G(dev_priv)	((dev_priv)->info.platform == INTEL_I915G)
-#define IS_I915GM(dev_priv)	((dev_priv)->info.platform == INTEL_I915GM)
-#define IS_I945G(dev_priv)	((dev_priv)->info.platform == INTEL_I945G)
-#define IS_I945GM(dev_priv)	((dev_priv)->info.platform == INTEL_I945GM)
-#define IS_I965G(dev_priv)	((dev_priv)->info.platform == INTEL_I965G)
-#define IS_I965GM(dev_priv)	((dev_priv)->info.platform == INTEL_I965GM)
-#define IS_G45(dev_priv)	((dev_priv)->info.platform == INTEL_G45)
-#define IS_GM45(dev_priv)	((dev_priv)->info.platform == INTEL_GM45)
+#define IS_PLATFORM(dev_priv, p) ((dev_priv)->info.platform_mask & BIT(p))
+
+#define IS_I830(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I830)
+#define IS_I845G(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I845G)
+#define IS_I85X(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I85X)
+#define IS_I865G(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I865G)
+#define IS_I915G(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I915G)
+#define IS_I915GM(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I915GM)
+#define IS_I945G(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I945G)
+#define IS_I945GM(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I945GM)
+#define IS_I965G(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I965G)
+#define IS_I965GM(dev_priv)	IS_PLATFORM(dev_priv, INTEL_I965GM)
+#define IS_G45(dev_priv)	IS_PLATFORM(dev_priv, INTEL_G45)
+#define IS_GM45(dev_priv)	IS_PLATFORM(dev_priv, INTEL_GM45)
 #define IS_G4X(dev_priv)	(IS_G45(dev_priv) || IS_GM45(dev_priv))
 #define IS_G4X(dev_priv)	(IS_G45(dev_priv) || IS_GM45(dev_priv))
 #define IS_PINEVIEW_G(dev_priv)	(INTEL_DEVID(dev_priv) == 0xa001)
 #define IS_PINEVIEW_G(dev_priv)	(INTEL_DEVID(dev_priv) == 0xa001)
 #define IS_PINEVIEW_M(dev_priv)	(INTEL_DEVID(dev_priv) == 0xa011)
 #define IS_PINEVIEW_M(dev_priv)	(INTEL_DEVID(dev_priv) == 0xa011)
-#define IS_PINEVIEW(dev_priv)	((dev_priv)->info.platform == INTEL_PINEVIEW)
-#define IS_G33(dev_priv)	((dev_priv)->info.platform == INTEL_G33)
+#define IS_PINEVIEW(dev_priv)	IS_PLATFORM(dev_priv, INTEL_PINEVIEW)
+#define IS_G33(dev_priv)	IS_PLATFORM(dev_priv, INTEL_G33)
 #define IS_IRONLAKE_M(dev_priv)	(INTEL_DEVID(dev_priv) == 0x0046)
 #define IS_IRONLAKE_M(dev_priv)	(INTEL_DEVID(dev_priv) == 0x0046)
-#define IS_IVYBRIDGE(dev_priv)	((dev_priv)->info.platform == INTEL_IVYBRIDGE)
+#define IS_IVYBRIDGE(dev_priv)	IS_PLATFORM(dev_priv, INTEL_IVYBRIDGE)
 #define IS_IVB_GT1(dev_priv)	(IS_IVYBRIDGE(dev_priv) && \
 #define IS_IVB_GT1(dev_priv)	(IS_IVYBRIDGE(dev_priv) && \
 				 (dev_priv)->info.gt == 1)
 				 (dev_priv)->info.gt == 1)
-#define IS_VALLEYVIEW(dev_priv)	((dev_priv)->info.platform == INTEL_VALLEYVIEW)
-#define IS_CHERRYVIEW(dev_priv)	((dev_priv)->info.platform == INTEL_CHERRYVIEW)
-#define IS_HASWELL(dev_priv)	((dev_priv)->info.platform == INTEL_HASWELL)
-#define IS_BROADWELL(dev_priv)	((dev_priv)->info.platform == INTEL_BROADWELL)
-#define IS_SKYLAKE(dev_priv)	((dev_priv)->info.platform == INTEL_SKYLAKE)
-#define IS_BROXTON(dev_priv)	((dev_priv)->info.platform == INTEL_BROXTON)
-#define IS_KABYLAKE(dev_priv)	((dev_priv)->info.platform == INTEL_KABYLAKE)
-#define IS_GEMINILAKE(dev_priv)	((dev_priv)->info.platform == INTEL_GEMINILAKE)
-#define IS_COFFEELAKE(dev_priv)	((dev_priv)->info.platform == INTEL_COFFEELAKE)
-#define IS_CANNONLAKE(dev_priv)	((dev_priv)->info.platform == INTEL_CANNONLAKE)
+#define IS_VALLEYVIEW(dev_priv)	IS_PLATFORM(dev_priv, INTEL_VALLEYVIEW)
+#define IS_CHERRYVIEW(dev_priv)	IS_PLATFORM(dev_priv, INTEL_CHERRYVIEW)
+#define IS_HASWELL(dev_priv)	IS_PLATFORM(dev_priv, INTEL_HASWELL)
+#define IS_BROADWELL(dev_priv)	IS_PLATFORM(dev_priv, INTEL_BROADWELL)
+#define IS_SKYLAKE(dev_priv)	IS_PLATFORM(dev_priv, INTEL_SKYLAKE)
+#define IS_BROXTON(dev_priv)	IS_PLATFORM(dev_priv, INTEL_BROXTON)
+#define IS_KABYLAKE(dev_priv)	IS_PLATFORM(dev_priv, INTEL_KABYLAKE)
+#define IS_GEMINILAKE(dev_priv)	IS_PLATFORM(dev_priv, INTEL_GEMINILAKE)
+#define IS_COFFEELAKE(dev_priv)	IS_PLATFORM(dev_priv, INTEL_COFFEELAKE)
+#define IS_CANNONLAKE(dev_priv)	IS_PLATFORM(dev_priv, INTEL_CANNONLAKE)
 #define IS_MOBILE(dev_priv)	((dev_priv)->info.is_mobile)
 #define IS_MOBILE(dev_priv)	((dev_priv)->info.is_mobile)
 #define IS_HSW_EARLY_SDV(dev_priv) (IS_HASWELL(dev_priv) && \
 #define IS_HSW_EARLY_SDV(dev_priv) (IS_HASWELL(dev_priv) && \
 				    (INTEL_DEVID(dev_priv) & 0xFF00) == 0x0C00)
 				    (INTEL_DEVID(dev_priv) & 0xFF00) == 0x0C00)
@@ -2946,6 +2993,8 @@ intel_info(const struct drm_i915_private *dev_priv)
 				 (dev_priv)->info.gt == 3)
 				 (dev_priv)->info.gt == 3)
 #define IS_CFL_ULT(dev_priv)	(IS_COFFEELAKE(dev_priv) && \
 #define IS_CFL_ULT(dev_priv)	(IS_COFFEELAKE(dev_priv) && \
 				 (INTEL_DEVID(dev_priv) & 0x00F0) == 0x00A0)
 				 (INTEL_DEVID(dev_priv) & 0x00F0) == 0x00A0)
+#define IS_CFL_GT2(dev_priv)	(IS_COFFEELAKE(dev_priv) && \
+				 (dev_priv)->info.gt == 2)
 
 
 #define IS_ALPHA_SUPPORT(intel_info) ((intel_info)->is_alpha_support)
 #define IS_ALPHA_SUPPORT(intel_info) ((intel_info)->is_alpha_support)
 
 
@@ -3036,9 +3085,9 @@ intel_info(const struct drm_i915_private *dev_priv)
 
 
 #define HAS_LOGICAL_RING_CONTEXTS(dev_priv) \
 #define HAS_LOGICAL_RING_CONTEXTS(dev_priv) \
 		((dev_priv)->info.has_logical_ring_contexts)
 		((dev_priv)->info.has_logical_ring_contexts)
-#define USES_PPGTT(dev_priv)		(i915.enable_ppgtt)
-#define USES_FULL_PPGTT(dev_priv)	(i915.enable_ppgtt >= 2)
-#define USES_FULL_48BIT_PPGTT(dev_priv)	(i915.enable_ppgtt == 3)
+#define USES_PPGTT(dev_priv)		(i915_modparams.enable_ppgtt)
+#define USES_FULL_PPGTT(dev_priv)	(i915_modparams.enable_ppgtt >= 2)
+#define USES_FULL_48BIT_PPGTT(dev_priv)	(i915_modparams.enable_ppgtt == 3)
 
 
 #define HAS_OVERLAY(dev_priv)		 ((dev_priv)->info.has_overlay)
 #define HAS_OVERLAY(dev_priv)		 ((dev_priv)->info.has_overlay)
 #define OVERLAY_NEEDS_PHYSICAL(dev_priv) \
 #define OVERLAY_NEEDS_PHYSICAL(dev_priv) \
@@ -3056,9 +3105,12 @@ intel_info(const struct drm_i915_private *dev_priv)
  * even when in MSI mode. This results in spurious interrupt warnings if the
  * even when in MSI mode. This results in spurious interrupt warnings if the
  * legacy irq no. is shared with another device. The kernel then disables that
  * legacy irq no. is shared with another device. The kernel then disables that
  * interrupt source and so prevents the other device from working properly.
  * interrupt source and so prevents the other device from working properly.
+ *
+ * Since we don't enable MSI anymore on gen4, we can always use GMBUS/AUX
+ * interrupts.
  */
  */
-#define HAS_AUX_IRQ(dev_priv)   ((dev_priv)->info.gen >= 5)
-#define HAS_GMBUS_IRQ(dev_priv) ((dev_priv)->info.has_gmbus_irq)
+#define HAS_AUX_IRQ(dev_priv)   true
+#define HAS_GMBUS_IRQ(dev_priv) (INTEL_GEN(dev_priv) >= 4)
 
 
 /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
 /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
  * rows, which changed the alignment requirements and fence programming.
  * rows, which changed the alignment requirements and fence programming.
@@ -3089,6 +3141,8 @@ intel_info(const struct drm_i915_private *dev_priv)
 #define HAS_RUNTIME_PM(dev_priv) ((dev_priv)->info.has_runtime_pm)
 #define HAS_RUNTIME_PM(dev_priv) ((dev_priv)->info.has_runtime_pm)
 #define HAS_64BIT_RELOC(dev_priv) ((dev_priv)->info.has_64bit_reloc)
 #define HAS_64BIT_RELOC(dev_priv) ((dev_priv)->info.has_64bit_reloc)
 
 
+#define HAS_IPC(dev_priv)		 ((dev_priv)->info.has_ipc)
+
 /*
 /*
  * For now, anything with a GuC requires uCode loading, and then supports
  * For now, anything with a GuC requires uCode loading, and then supports
  * command submission once loaded. But these are logically independent
  * command submission once loaded. But these are logically independent
@@ -3234,7 +3288,7 @@ static inline void i915_queue_hangcheck(struct drm_i915_private *dev_priv)
 {
 {
 	unsigned long delay;
 	unsigned long delay;
 
 
-	if (unlikely(!i915.enable_hangcheck))
+	if (unlikely(!i915_modparams.enable_hangcheck))
 		return;
 		return;
 
 
 	/* Don't continually defer the hangcheck so that it is always run at
 	/* Don't continually defer the hangcheck so that it is always run at
@@ -3267,6 +3321,8 @@ static inline bool intel_vgpu_active(struct drm_i915_private *dev_priv)
 	return dev_priv->vgpu.active;
 	return dev_priv->vgpu.active;
 }
 }
 
 
+u32 i915_pipestat_enable_mask(struct drm_i915_private *dev_priv,
+			      enum pipe pipe);
 void
 void
 i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
 i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
 		     u32 status_mask);
 		     u32 status_mask);
@@ -4360,4 +4416,12 @@ int remap_io_mapping(struct vm_area_struct *vma,
 		     unsigned long addr, unsigned long pfn, unsigned long size,
 		     unsigned long addr, unsigned long pfn, unsigned long size,
 		     struct io_mapping *iomap);
 		     struct io_mapping *iomap);
 
 
+static inline int intel_hws_csb_write_index(struct drm_i915_private *i915)
+{
+	if (INTEL_GEN(i915) >= 10)
+		return CNL_HWS_CSB_WRITE_INDEX;
+	else
+		return I915_HWS_CSB_WRITE_INDEX;
+}
+
 #endif
 #endif

+ 10 - 50
drivers/gpu/drm/i915/i915_gem.c

@@ -179,7 +179,7 @@ i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
 	 * the alignment of the buddy allocation will naturally match.
 	 * the alignment of the buddy allocation will naturally match.
 	 */
 	 */
 	phys = drm_pci_alloc(obj->base.dev,
 	phys = drm_pci_alloc(obj->base.dev,
-			     obj->base.size,
+			     roundup_pow_of_two(obj->base.size),
 			     roundup_pow_of_two(obj->base.size));
 			     roundup_pow_of_two(obj->base.size));
 	if (!phys)
 	if (!phys)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
@@ -694,10 +694,10 @@ flush_write_domain(struct drm_i915_gem_object *obj, unsigned int flush_domains)
 
 
 	switch (obj->base.write_domain) {
 	switch (obj->base.write_domain) {
 	case I915_GEM_DOMAIN_GTT:
 	case I915_GEM_DOMAIN_GTT:
-		if (INTEL_GEN(dev_priv) >= 6 && !HAS_LLC(dev_priv)) {
+		if (!HAS_LLC(dev_priv)) {
 			intel_runtime_pm_get(dev_priv);
 			intel_runtime_pm_get(dev_priv);
 			spin_lock_irq(&dev_priv->uncore.lock);
 			spin_lock_irq(&dev_priv->uncore.lock);
-			POSTING_READ_FW(RING_ACTHD(dev_priv->engine[RCS]->mmio_base));
+			POSTING_READ_FW(RING_HEAD(dev_priv->engine[RCS]->mmio_base));
 			spin_unlock_irq(&dev_priv->uncore.lock);
 			spin_unlock_irq(&dev_priv->uncore.lock);
 			intel_runtime_pm_put(dev_priv);
 			intel_runtime_pm_put(dev_priv);
 		}
 		}
@@ -2303,7 +2303,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
 	struct sgt_iter sgt_iter;
 	struct sgt_iter sgt_iter;
 	struct page *page;
 	struct page *page;
 	unsigned long last_pfn = 0;	/* suppress gcc warning */
 	unsigned long last_pfn = 0;	/* suppress gcc warning */
-	unsigned int max_segment;
+	unsigned int max_segment = i915_sg_segment_size();
 	gfp_t noreclaim;
 	gfp_t noreclaim;
 	int ret;
 	int ret;
 
 
@@ -2314,10 +2314,6 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
 	GEM_BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
 	GEM_BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS);
 	GEM_BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
 	GEM_BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS);
 
 
-	max_segment = swiotlb_max_segment();
-	if (!max_segment)
-		max_segment = rounddown(UINT_MAX, PAGE_SIZE);
-
 	st = kmalloc(sizeof(*st), GFP_KERNEL);
 	st = kmalloc(sizeof(*st), GFP_KERNEL);
 	if (st == NULL)
 	if (st == NULL)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
@@ -2819,8 +2815,8 @@ i915_gem_reset_prepare_engine(struct intel_engine_cs *engine)
 	 * Turning off the engine->irq_tasklet until the reset is over
 	 * Turning off the engine->irq_tasklet until the reset is over
 	 * prevents the race.
 	 * prevents the race.
 	 */
 	 */
-	tasklet_kill(&engine->irq_tasklet);
-	tasklet_disable(&engine->irq_tasklet);
+	tasklet_kill(&engine->execlists.irq_tasklet);
+	tasklet_disable(&engine->execlists.irq_tasklet);
 
 
 	if (engine->irq_seqno_barrier)
 	if (engine->irq_seqno_barrier)
 		engine->irq_seqno_barrier(engine);
 		engine->irq_seqno_barrier(engine);
@@ -2999,7 +2995,7 @@ void i915_gem_reset(struct drm_i915_private *dev_priv)
 
 
 void i915_gem_reset_finish_engine(struct intel_engine_cs *engine)
 void i915_gem_reset_finish_engine(struct intel_engine_cs *engine)
 {
 {
-	tasklet_enable(&engine->irq_tasklet);
+	tasklet_enable(&engine->execlists.irq_tasklet);
 	kthread_unpark(engine->breadcrumbs.signaler);
 	kthread_unpark(engine->breadcrumbs.signaler);
 }
 }
 
 
@@ -3026,9 +3022,6 @@ static void nop_submit_request(struct drm_i915_gem_request *request)
 
 
 static void engine_set_wedged(struct intel_engine_cs *engine)
 static void engine_set_wedged(struct intel_engine_cs *engine)
 {
 {
-	struct drm_i915_gem_request *request;
-	unsigned long flags;
-
 	/* We need to be sure that no thread is running the old callback as
 	/* We need to be sure that no thread is running the old callback as
 	 * we install the nop handler (otherwise we would submit a request
 	 * we install the nop handler (otherwise we would submit a request
 	 * to hardware that will never complete). In order to prevent this
 	 * to hardware that will never complete). In order to prevent this
@@ -3038,40 +3031,7 @@ static void engine_set_wedged(struct intel_engine_cs *engine)
 	engine->submit_request = nop_submit_request;
 	engine->submit_request = nop_submit_request;
 
 
 	/* Mark all executing requests as skipped */
 	/* Mark all executing requests as skipped */
-	spin_lock_irqsave(&engine->timeline->lock, flags);
-	list_for_each_entry(request, &engine->timeline->requests, link)
-		if (!i915_gem_request_completed(request))
-			dma_fence_set_error(&request->fence, -EIO);
-	spin_unlock_irqrestore(&engine->timeline->lock, flags);
-
-	/*
-	 * Clear the execlists queue up before freeing the requests, as those
-	 * are the ones that keep the context and ringbuffer backing objects
-	 * pinned in place.
-	 */
-
-	if (i915.enable_execlists) {
-		struct execlist_port *port = engine->execlist_port;
-		unsigned long flags;
-		unsigned int n;
-
-		spin_lock_irqsave(&engine->timeline->lock, flags);
-
-		for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++)
-			i915_gem_request_put(port_request(&port[n]));
-		memset(engine->execlist_port, 0, sizeof(engine->execlist_port));
-		engine->execlist_queue = RB_ROOT;
-		engine->execlist_first = NULL;
-
-		spin_unlock_irqrestore(&engine->timeline->lock, flags);
-
-		/* The port is checked prior to scheduling a tasklet, but
-		 * just in case we have suspended the tasklet to do the
-		 * wedging make sure that when it wakes, it decides there
-		 * is no work to do by clearing the irq_posted bit.
-		 */
-		clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
-	}
+	engine->cancel_requests(engine);
 
 
 	/* Mark all pending requests as complete so that any concurrent
 	/* Mark all pending requests as complete so that any concurrent
 	 * (lockless) lookup doesn't try and wait upon the request as we
 	 * (lockless) lookup doesn't try and wait upon the request as we
@@ -4778,7 +4738,7 @@ bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value)
 		return false;
 		return false;
 
 
 	/* TODO: make semaphores and Execlists play nicely together */
 	/* TODO: make semaphores and Execlists play nicely together */
-	if (i915.enable_execlists)
+	if (i915_modparams.enable_execlists)
 		return false;
 		return false;
 
 
 	if (value >= 0)
 	if (value >= 0)
@@ -4799,7 +4759,7 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
 
 
 	dev_priv->mm.unordered_timeline = dma_fence_context_alloc(1);
 	dev_priv->mm.unordered_timeline = dma_fence_context_alloc(1);
 
 
-	if (!i915.enable_execlists) {
+	if (!i915_modparams.enable_execlists) {
 		dev_priv->gt.resume = intel_legacy_submission_resume;
 		dev_priv->gt.resume = intel_legacy_submission_resume;
 		dev_priv->gt.cleanup_engine = intel_engine_cleanup;
 		dev_priv->gt.cleanup_engine = intel_engine_cleanup;
 	} else {
 	} else {

+ 6 - 6
drivers/gpu/drm/i915/i915_gem_context.c

@@ -314,7 +314,7 @@ __create_hw_context(struct drm_i915_private *dev_priv,
 	 * present or not in use we still need a small bias as ring wraparound
 	 * present or not in use we still need a small bias as ring wraparound
 	 * at offset 0 sometimes hangs. No idea why.
 	 * at offset 0 sometimes hangs. No idea why.
 	 */
 	 */
-	if (HAS_GUC(dev_priv) && i915.enable_guc_loading)
+	if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading)
 		ctx->ggtt_offset_bias = GUC_WOPCM_TOP;
 		ctx->ggtt_offset_bias = GUC_WOPCM_TOP;
 	else
 	else
 		ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
 		ctx->ggtt_offset_bias = I915_GTT_PAGE_SIZE;
@@ -407,7 +407,7 @@ i915_gem_context_create_gvt(struct drm_device *dev)
 	i915_gem_context_set_closed(ctx); /* not user accessible */
 	i915_gem_context_set_closed(ctx); /* not user accessible */
 	i915_gem_context_clear_bannable(ctx);
 	i915_gem_context_clear_bannable(ctx);
 	i915_gem_context_set_force_single_submission(ctx);
 	i915_gem_context_set_force_single_submission(ctx);
-	if (!i915.enable_guc_submission)
+	if (!i915_modparams.enable_guc_submission)
 		ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */
 		ctx->ring_size = 512 * PAGE_SIZE; /* Max ring buffer size */
 
 
 	GEM_BUG_ON(i915_gem_context_is_kernel(ctx));
 	GEM_BUG_ON(i915_gem_context_is_kernel(ctx));
@@ -431,7 +431,7 @@ int i915_gem_contexts_init(struct drm_i915_private *dev_priv)
 
 
 	if (intel_vgpu_active(dev_priv) &&
 	if (intel_vgpu_active(dev_priv) &&
 	    HAS_LOGICAL_RING_CONTEXTS(dev_priv)) {
 	    HAS_LOGICAL_RING_CONTEXTS(dev_priv)) {
-		if (!i915.enable_execlists) {
+		if (!i915_modparams.enable_execlists) {
 			DRM_INFO("Only EXECLIST mode is supported in vgpu.\n");
 			DRM_INFO("Only EXECLIST mode is supported in vgpu.\n");
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
@@ -483,7 +483,7 @@ void i915_gem_contexts_lost(struct drm_i915_private *dev_priv)
 	}
 	}
 
 
 	/* Force the GPU state to be restored on enabling */
 	/* Force the GPU state to be restored on enabling */
-	if (!i915.enable_execlists) {
+	if (!i915_modparams.enable_execlists) {
 		struct i915_gem_context *ctx;
 		struct i915_gem_context *ctx;
 
 
 		list_for_each_entry(ctx, &dev_priv->contexts.list, link) {
 		list_for_each_entry(ctx, &dev_priv->contexts.list, link) {
@@ -568,7 +568,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 flags)
 	enum intel_engine_id id;
 	enum intel_engine_id id;
 	const int num_rings =
 	const int num_rings =
 		/* Use an extended w/a on gen7 if signalling from other rings */
 		/* Use an extended w/a on gen7 if signalling from other rings */
-		(i915.semaphores && INTEL_GEN(dev_priv) == 7) ?
+		(i915_modparams.semaphores && INTEL_GEN(dev_priv) == 7) ?
 		INTEL_INFO(dev_priv)->num_rings - 1 :
 		INTEL_INFO(dev_priv)->num_rings - 1 :
 		0;
 		0;
 	int len;
 	int len;
@@ -837,7 +837,7 @@ int i915_switch_context(struct drm_i915_gem_request *req)
 	struct intel_engine_cs *engine = req->engine;
 	struct intel_engine_cs *engine = req->engine;
 
 
 	lockdep_assert_held(&req->i915->drm.struct_mutex);
 	lockdep_assert_held(&req->i915->drm.struct_mutex);
-	if (i915.enable_execlists)
+	if (i915_modparams.enable_execlists)
 		return 0;
 		return 0;
 
 
 	if (!req->ctx->engine[engine->id].state) {
 	if (!req->ctx->engine[engine->id].state) {

+ 7 - 8
drivers/gpu/drm/i915/i915_gem_execbuffer.c

@@ -58,6 +58,7 @@ enum {
 
 
 #define __EXEC_HAS_RELOC	BIT(31)
 #define __EXEC_HAS_RELOC	BIT(31)
 #define __EXEC_VALIDATED	BIT(30)
 #define __EXEC_VALIDATED	BIT(30)
+#define __EXEC_INTERNAL_FLAGS	(~0u << 30)
 #define UPDATE			PIN_OFFSET_FIXED
 #define UPDATE			PIN_OFFSET_FIXED
 
 
 #define BATCH_OFFSET_BIAS (256*1024)
 #define BATCH_OFFSET_BIAS (256*1024)
@@ -679,7 +680,7 @@ static int eb_select_context(struct i915_execbuffer *eb)
 static int eb_lookup_vmas(struct i915_execbuffer *eb)
 static int eb_lookup_vmas(struct i915_execbuffer *eb)
 {
 {
 	struct radix_tree_root *handles_vma = &eb->ctx->handles_vma;
 	struct radix_tree_root *handles_vma = &eb->ctx->handles_vma;
-	struct drm_i915_gem_object *uninitialized_var(obj);
+	struct drm_i915_gem_object *obj;
 	unsigned int i;
 	unsigned int i;
 	int err;
 	int err;
 
 
@@ -725,19 +726,17 @@ static int eb_lookup_vmas(struct i915_execbuffer *eb)
 			goto err_obj;
 			goto err_obj;
 		}
 		}
 
 
+		/* transfer ref to ctx */
 		vma->open_count++;
 		vma->open_count++;
 		list_add(&lut->obj_link, &obj->lut_list);
 		list_add(&lut->obj_link, &obj->lut_list);
 		list_add(&lut->ctx_link, &eb->ctx->handles_list);
 		list_add(&lut->ctx_link, &eb->ctx->handles_list);
 		lut->ctx = eb->ctx;
 		lut->ctx = eb->ctx;
 		lut->handle = handle;
 		lut->handle = handle;
 
 
-		/* transfer ref to ctx */
-		obj = NULL;
-
 add_vma:
 add_vma:
 		err = eb_add_vma(eb, i, vma);
 		err = eb_add_vma(eb, i, vma);
 		if (unlikely(err))
 		if (unlikely(err))
-			goto err_obj;
+			goto err_vma;
 
 
 		GEM_BUG_ON(vma != eb->vma[i]);
 		GEM_BUG_ON(vma != eb->vma[i]);
 		GEM_BUG_ON(vma->exec_flags != &eb->flags[i]);
 		GEM_BUG_ON(vma->exec_flags != &eb->flags[i]);
@@ -766,8 +765,7 @@ add_vma:
 	return eb_reserve(eb);
 	return eb_reserve(eb);
 
 
 err_obj:
 err_obj:
-	if (obj)
-		i915_gem_object_put(obj);
+	i915_gem_object_put(obj);
 err_vma:
 err_vma:
 	eb->vma[i] = NULL;
 	eb->vma[i] = NULL;
 	return err;
 	return err;
@@ -1587,7 +1585,7 @@ static int eb_prefault_relocations(const struct i915_execbuffer *eb)
 	const unsigned int count = eb->buffer_count;
 	const unsigned int count = eb->buffer_count;
 	unsigned int i;
 	unsigned int i;
 
 
-	if (unlikely(i915.prefault_disable))
+	if (unlikely(i915_modparams.prefault_disable))
 		return 0;
 		return 0;
 
 
 	for (i = 0; i < count; i++) {
 	for (i = 0; i < count; i++) {
@@ -2188,6 +2186,7 @@ i915_gem_do_execbuffer(struct drm_device *dev,
 	int out_fence_fd = -1;
 	int out_fence_fd = -1;
 	int err;
 	int err;
 
 
+	BUILD_BUG_ON(__EXEC_INTERNAL_FLAGS & ~__I915_EXEC_ILLEGAL_FLAGS);
 	BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS &
 	BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS &
 		     ~__EXEC_OBJECT_UNKNOWN_FLAGS);
 		     ~__EXEC_OBJECT_UNKNOWN_FLAGS);
 
 

+ 259 - 67
drivers/gpu/drm/i915/i915_gem_gtt.c

@@ -180,7 +180,7 @@ int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
 		return 0;
 		return 0;
 	}
 	}
 
 
-	if (INTEL_GEN(dev_priv) >= 8 && i915.enable_execlists) {
+	if (INTEL_GEN(dev_priv) >= 8 && i915_modparams.enable_execlists) {
 		if (has_full_48bit_ppgtt)
 		if (has_full_48bit_ppgtt)
 			return 3;
 			return 3;
 
 
@@ -230,13 +230,13 @@ static gen8_pte_t gen8_pte_encode(dma_addr_t addr,
 
 
 	switch (level) {
 	switch (level) {
 	case I915_CACHE_NONE:
 	case I915_CACHE_NONE:
-		pte |= PPAT_UNCACHED_INDEX;
+		pte |= PPAT_UNCACHED;
 		break;
 		break;
 	case I915_CACHE_WT:
 	case I915_CACHE_WT:
-		pte |= PPAT_DISPLAY_ELLC_INDEX;
+		pte |= PPAT_DISPLAY_ELLC;
 		break;
 		break;
 	default:
 	default:
-		pte |= PPAT_CACHED_INDEX;
+		pte |= PPAT_CACHED;
 		break;
 		break;
 	}
 	}
 
 
@@ -249,9 +249,9 @@ static gen8_pde_t gen8_pde_encode(const dma_addr_t addr,
 	gen8_pde_t pde = _PAGE_PRESENT | _PAGE_RW;
 	gen8_pde_t pde = _PAGE_PRESENT | _PAGE_RW;
 	pde |= addr;
 	pde |= addr;
 	if (level != I915_CACHE_NONE)
 	if (level != I915_CACHE_NONE)
-		pde |= PPAT_CACHED_PDE_INDEX;
+		pde |= PPAT_CACHED_PDE;
 	else
 	else
-		pde |= PPAT_UNCACHED_INDEX;
+		pde |= PPAT_UNCACHED;
 	return pde;
 	return pde;
 }
 }
 
 
@@ -481,10 +481,8 @@ static void fill_page_dma(struct i915_address_space *vm,
 			  const u64 val)
 			  const u64 val)
 {
 {
 	u64 * const vaddr = kmap_atomic(p->page);
 	u64 * const vaddr = kmap_atomic(p->page);
-	int i;
 
 
-	for (i = 0; i < 512; i++)
-		vaddr[i] = val;
+	memset64(vaddr, val, PAGE_SIZE / sizeof(val));
 
 
 	kunmap_atomic(vaddr);
 	kunmap_atomic(vaddr);
 }
 }
@@ -1168,19 +1166,22 @@ static int gen8_ppgtt_alloc_pd(struct i915_address_space *vm,
 	unsigned int pde;
 	unsigned int pde;
 
 
 	gen8_for_each_pde(pt, pd, start, length, pde) {
 	gen8_for_each_pde(pt, pd, start, length, pde) {
+		int count = gen8_pte_count(start, length);
+
 		if (pt == vm->scratch_pt) {
 		if (pt == vm->scratch_pt) {
 			pt = alloc_pt(vm);
 			pt = alloc_pt(vm);
 			if (IS_ERR(pt))
 			if (IS_ERR(pt))
 				goto unwind;
 				goto unwind;
 
 
-			gen8_initialize_pt(vm, pt);
+			if (count < GEN8_PTES)
+				gen8_initialize_pt(vm, pt);
 
 
 			gen8_ppgtt_set_pde(vm, pd, pt, pde);
 			gen8_ppgtt_set_pde(vm, pd, pt, pde);
 			pd->used_pdes++;
 			pd->used_pdes++;
 			GEM_BUG_ON(pd->used_pdes > I915_PDES);
 			GEM_BUG_ON(pd->used_pdes > I915_PDES);
 		}
 		}
 
 
-		pt->used_ptes += gen8_pte_count(start, length);
+		pt->used_ptes += count;
 	}
 	}
 	return 0;
 	return 0;
 
 
@@ -1969,7 +1970,7 @@ int i915_ppgtt_init_hw(struct drm_i915_private *dev_priv)
 	/* In the case of execlists, PPGTT is enabled by the context descriptor
 	/* In the case of execlists, PPGTT is enabled by the context descriptor
 	 * and the PDPs are contained within the context itself.  We don't
 	 * and the PDPs are contained within the context itself.  We don't
 	 * need to do anything here. */
 	 * need to do anything here. */
-	if (i915.enable_execlists)
+	if (i915_modparams.enable_execlists)
 		return 0;
 		return 0;
 
 
 	if (!USES_PPGTT(dev_priv))
 	if (!USES_PPGTT(dev_priv))
@@ -2816,41 +2817,209 @@ static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size)
 	return 0;
 	return 0;
 }
 }
 
 
-static void cnl_setup_private_ppat(struct drm_i915_private *dev_priv)
+static struct intel_ppat_entry *
+__alloc_ppat_entry(struct intel_ppat *ppat, unsigned int index, u8 value)
+{
+	struct intel_ppat_entry *entry = &ppat->entries[index];
+
+	GEM_BUG_ON(index >= ppat->max_entries);
+	GEM_BUG_ON(test_bit(index, ppat->used));
+
+	entry->ppat = ppat;
+	entry->value = value;
+	kref_init(&entry->ref);
+	set_bit(index, ppat->used);
+	set_bit(index, ppat->dirty);
+
+	return entry;
+}
+
+static void __free_ppat_entry(struct intel_ppat_entry *entry)
+{
+	struct intel_ppat *ppat = entry->ppat;
+	unsigned int index = entry - ppat->entries;
+
+	GEM_BUG_ON(index >= ppat->max_entries);
+	GEM_BUG_ON(!test_bit(index, ppat->used));
+
+	entry->value = ppat->clear_value;
+	clear_bit(index, ppat->used);
+	set_bit(index, ppat->dirty);
+}
+
+/**
+ * intel_ppat_get - get a usable PPAT entry
+ * @i915: i915 device instance
+ * @value: the PPAT value required by the caller
+ *
+ * The function tries to search if there is an existing PPAT entry which
+ * matches with the required value. If perfectly matched, the existing PPAT
+ * entry will be used. If only partially matched, it will try to check if
+ * there is any available PPAT index. If yes, it will allocate a new PPAT
+ * index for the required entry and update the HW. If not, the partially
+ * matched entry will be used.
+ */
+const struct intel_ppat_entry *
+intel_ppat_get(struct drm_i915_private *i915, u8 value)
 {
 {
+	struct intel_ppat *ppat = &i915->ppat;
+	struct intel_ppat_entry *entry;
+	unsigned int scanned, best_score;
+	int i;
+
+	GEM_BUG_ON(!ppat->max_entries);
+
+	scanned = best_score = 0;
+	for_each_set_bit(i, ppat->used, ppat->max_entries) {
+		unsigned int score;
+
+		score = ppat->match(ppat->entries[i].value, value);
+		if (score > best_score) {
+			entry = &ppat->entries[i];
+			if (score == INTEL_PPAT_PERFECT_MATCH) {
+				kref_get(&entry->ref);
+				return entry;
+			}
+			best_score = score;
+		}
+		scanned++;
+	}
+
+	if (scanned == ppat->max_entries) {
+		if (!best_score)
+			return ERR_PTR(-ENOSPC);
+
+		kref_get(&entry->ref);
+		return entry;
+	}
+
+	i = find_first_zero_bit(ppat->used, ppat->max_entries);
+	entry = __alloc_ppat_entry(ppat, i, value);
+	ppat->update_hw(i915);
+	return entry;
+}
+
+static void release_ppat(struct kref *kref)
+{
+	struct intel_ppat_entry *entry =
+		container_of(kref, struct intel_ppat_entry, ref);
+	struct drm_i915_private *i915 = entry->ppat->i915;
+
+	__free_ppat_entry(entry);
+	entry->ppat->update_hw(i915);
+}
+
+/**
+ * intel_ppat_put - put back the PPAT entry got from intel_ppat_get()
+ * @entry: an intel PPAT entry
+ *
+ * Put back the PPAT entry got from intel_ppat_get(). If the PPAT index of the
+ * entry is dynamically allocated, its reference count will be decreased. Once
+ * the reference count becomes into zero, the PPAT index becomes free again.
+ */
+void intel_ppat_put(const struct intel_ppat_entry *entry)
+{
+	struct intel_ppat *ppat = entry->ppat;
+	unsigned int index = entry - ppat->entries;
+
+	GEM_BUG_ON(!ppat->max_entries);
+
+	kref_put(&ppat->entries[index].ref, release_ppat);
+}
+
+static void cnl_private_pat_update_hw(struct drm_i915_private *dev_priv)
+{
+	struct intel_ppat *ppat = &dev_priv->ppat;
+	int i;
+
+	for_each_set_bit(i, ppat->dirty, ppat->max_entries) {
+		I915_WRITE(GEN10_PAT_INDEX(i), ppat->entries[i].value);
+		clear_bit(i, ppat->dirty);
+	}
+}
+
+static void bdw_private_pat_update_hw(struct drm_i915_private *dev_priv)
+{
+	struct intel_ppat *ppat = &dev_priv->ppat;
+	u64 pat = 0;
+	int i;
+
+	for (i = 0; i < ppat->max_entries; i++)
+		pat |= GEN8_PPAT(i, ppat->entries[i].value);
+
+	bitmap_clear(ppat->dirty, 0, ppat->max_entries);
+
+	I915_WRITE(GEN8_PRIVATE_PAT_LO, lower_32_bits(pat));
+	I915_WRITE(GEN8_PRIVATE_PAT_HI, upper_32_bits(pat));
+}
+
+static unsigned int bdw_private_pat_match(u8 src, u8 dst)
+{
+	unsigned int score = 0;
+	enum {
+		AGE_MATCH = BIT(0),
+		TC_MATCH = BIT(1),
+		CA_MATCH = BIT(2),
+	};
+
+	/* Cache attribute has to be matched. */
+	if (GEN8_PPAT_GET_CA(src) != GEN8_PPAT_GET_CA(dst))
+		return 0;
+
+	score |= CA_MATCH;
+
+	if (GEN8_PPAT_GET_TC(src) == GEN8_PPAT_GET_TC(dst))
+		score |= TC_MATCH;
+
+	if (GEN8_PPAT_GET_AGE(src) == GEN8_PPAT_GET_AGE(dst))
+		score |= AGE_MATCH;
+
+	if (score == (AGE_MATCH | TC_MATCH | CA_MATCH))
+		return INTEL_PPAT_PERFECT_MATCH;
+
+	return score;
+}
+
+static unsigned int chv_private_pat_match(u8 src, u8 dst)
+{
+	return (CHV_PPAT_GET_SNOOP(src) == CHV_PPAT_GET_SNOOP(dst)) ?
+		INTEL_PPAT_PERFECT_MATCH : 0;
+}
+
+static void cnl_setup_private_ppat(struct intel_ppat *ppat)
+{
+	ppat->max_entries = 8;
+	ppat->update_hw = cnl_private_pat_update_hw;
+	ppat->match = bdw_private_pat_match;
+	ppat->clear_value = GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3);
+
 	/* XXX: spec is unclear if this is still needed for CNL+ */
 	/* XXX: spec is unclear if this is still needed for CNL+ */
-	if (!USES_PPGTT(dev_priv)) {
-		I915_WRITE(GEN10_PAT_INDEX(0), GEN8_PPAT_UC);
+	if (!USES_PPGTT(ppat->i915)) {
+		__alloc_ppat_entry(ppat, 0, GEN8_PPAT_UC);
 		return;
 		return;
 	}
 	}
 
 
-	I915_WRITE(GEN10_PAT_INDEX(0), GEN8_PPAT_WB | GEN8_PPAT_LLC);
-	I915_WRITE(GEN10_PAT_INDEX(1), GEN8_PPAT_WC | GEN8_PPAT_LLCELLC);
-	I915_WRITE(GEN10_PAT_INDEX(2), GEN8_PPAT_WT | GEN8_PPAT_LLCELLC);
-	I915_WRITE(GEN10_PAT_INDEX(3), GEN8_PPAT_UC);
-	I915_WRITE(GEN10_PAT_INDEX(4), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0));
-	I915_WRITE(GEN10_PAT_INDEX(5), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1));
-	I915_WRITE(GEN10_PAT_INDEX(6), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2));
-	I915_WRITE(GEN10_PAT_INDEX(7), GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
+	__alloc_ppat_entry(ppat, 0, GEN8_PPAT_WB | GEN8_PPAT_LLC);
+	__alloc_ppat_entry(ppat, 1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC);
+	__alloc_ppat_entry(ppat, 2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC);
+	__alloc_ppat_entry(ppat, 3, GEN8_PPAT_UC);
+	__alloc_ppat_entry(ppat, 4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0));
+	__alloc_ppat_entry(ppat, 5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1));
+	__alloc_ppat_entry(ppat, 6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2));
+	__alloc_ppat_entry(ppat, 7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
 }
 }
 
 
 /* The GGTT and PPGTT need a private PPAT setup in order to handle cacheability
 /* The GGTT and PPGTT need a private PPAT setup in order to handle cacheability
  * bits. When using advanced contexts each context stores its own PAT, but
  * bits. When using advanced contexts each context stores its own PAT, but
  * writing this data shouldn't be harmful even in those cases. */
  * writing this data shouldn't be harmful even in those cases. */
-static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
+static void bdw_setup_private_ppat(struct intel_ppat *ppat)
 {
 {
-	u64 pat;
+	ppat->max_entries = 8;
+	ppat->update_hw = bdw_private_pat_update_hw;
+	ppat->match = bdw_private_pat_match;
+	ppat->clear_value = GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3);
 
 
-	pat = GEN8_PPAT(0, GEN8_PPAT_WB | GEN8_PPAT_LLC)     | /* for normal objects, no eLLC */
-	      GEN8_PPAT(1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC) | /* for something pointing to ptes? */
-	      GEN8_PPAT(2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC) | /* for scanout with eLLC */
-	      GEN8_PPAT(3, GEN8_PPAT_UC)                     | /* Uncached objects, mostly for scanout */
-	      GEN8_PPAT(4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0)) |
-	      GEN8_PPAT(5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1)) |
-	      GEN8_PPAT(6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2)) |
-	      GEN8_PPAT(7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
-
-	if (!USES_PPGTT(dev_priv))
+	if (!USES_PPGTT(ppat->i915)) {
 		/* Spec: "For GGTT, there is NO pat_sel[2:0] from the entry,
 		/* Spec: "For GGTT, there is NO pat_sel[2:0] from the entry,
 		 * so RTL will always use the value corresponding to
 		 * so RTL will always use the value corresponding to
 		 * pat_sel = 000".
 		 * pat_sel = 000".
@@ -2864,17 +3033,26 @@ static void bdw_setup_private_ppat(struct drm_i915_private *dev_priv)
 		 * So we can still hold onto all our assumptions wrt cpu
 		 * So we can still hold onto all our assumptions wrt cpu
 		 * clflushing on LLC machines.
 		 * clflushing on LLC machines.
 		 */
 		 */
-		pat = GEN8_PPAT(0, GEN8_PPAT_UC);
+		__alloc_ppat_entry(ppat, 0, GEN8_PPAT_UC);
+		return;
+	}
 
 
-	/* XXX: spec defines this as 2 distinct registers. It's unclear if a 64b
-	 * write would work. */
-	I915_WRITE(GEN8_PRIVATE_PAT_LO, pat);
-	I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
+	__alloc_ppat_entry(ppat, 0, GEN8_PPAT_WB | GEN8_PPAT_LLC);      /* for normal objects, no eLLC */
+	__alloc_ppat_entry(ppat, 1, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC);  /* for something pointing to ptes? */
+	__alloc_ppat_entry(ppat, 2, GEN8_PPAT_WT | GEN8_PPAT_LLCELLC);  /* for scanout with eLLC */
+	__alloc_ppat_entry(ppat, 3, GEN8_PPAT_UC);                      /* Uncached objects, mostly for scanout */
+	__alloc_ppat_entry(ppat, 4, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(0));
+	__alloc_ppat_entry(ppat, 5, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(1));
+	__alloc_ppat_entry(ppat, 6, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(2));
+	__alloc_ppat_entry(ppat, 7, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC | GEN8_PPAT_AGE(3));
 }
 }
 
 
-static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
+static void chv_setup_private_ppat(struct intel_ppat *ppat)
 {
 {
-	u64 pat;
+	ppat->max_entries = 8;
+	ppat->update_hw = bdw_private_pat_update_hw;
+	ppat->match = chv_private_pat_match;
+	ppat->clear_value = CHV_PPAT_SNOOP;
 
 
 	/*
 	/*
 	 * Map WB on BDW to snooped on CHV.
 	 * Map WB on BDW to snooped on CHV.
@@ -2894,17 +3072,15 @@ static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
 	 * Which means we must set the snoop bit in PAT entry 0
 	 * Which means we must set the snoop bit in PAT entry 0
 	 * in order to keep the global status page working.
 	 * in order to keep the global status page working.
 	 */
 	 */
-	pat = GEN8_PPAT(0, CHV_PPAT_SNOOP) |
-	      GEN8_PPAT(1, 0) |
-	      GEN8_PPAT(2, 0) |
-	      GEN8_PPAT(3, 0) |
-	      GEN8_PPAT(4, CHV_PPAT_SNOOP) |
-	      GEN8_PPAT(5, CHV_PPAT_SNOOP) |
-	      GEN8_PPAT(6, CHV_PPAT_SNOOP) |
-	      GEN8_PPAT(7, CHV_PPAT_SNOOP);
 
 
-	I915_WRITE(GEN8_PRIVATE_PAT_LO, pat);
-	I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
+	__alloc_ppat_entry(ppat, 0, CHV_PPAT_SNOOP);
+	__alloc_ppat_entry(ppat, 1, 0);
+	__alloc_ppat_entry(ppat, 2, 0);
+	__alloc_ppat_entry(ppat, 3, 0);
+	__alloc_ppat_entry(ppat, 4, CHV_PPAT_SNOOP);
+	__alloc_ppat_entry(ppat, 5, CHV_PPAT_SNOOP);
+	__alloc_ppat_entry(ppat, 6, CHV_PPAT_SNOOP);
+	__alloc_ppat_entry(ppat, 7, CHV_PPAT_SNOOP);
 }
 }
 
 
 static void gen6_gmch_remove(struct i915_address_space *vm)
 static void gen6_gmch_remove(struct i915_address_space *vm)
@@ -2915,6 +3091,31 @@ static void gen6_gmch_remove(struct i915_address_space *vm)
 	cleanup_scratch_page(vm);
 	cleanup_scratch_page(vm);
 }
 }
 
 
+static void setup_private_pat(struct drm_i915_private *dev_priv)
+{
+	struct intel_ppat *ppat = &dev_priv->ppat;
+	int i;
+
+	ppat->i915 = dev_priv;
+
+	if (INTEL_GEN(dev_priv) >= 10)
+		cnl_setup_private_ppat(ppat);
+	else if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv))
+		chv_setup_private_ppat(ppat);
+	else
+		bdw_setup_private_ppat(ppat);
+
+	GEM_BUG_ON(ppat->max_entries > INTEL_MAX_PPAT_ENTRIES);
+
+	for_each_clear_bit(i, ppat->used, ppat->max_entries) {
+		ppat->entries[i].value = ppat->clear_value;
+		ppat->entries[i].ppat = ppat;
+		set_bit(i, ppat->dirty);
+	}
+
+	ppat->update_hw(dev_priv);
+}
+
 static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 {
 {
 	struct drm_i915_private *dev_priv = ggtt->base.i915;
 	struct drm_i915_private *dev_priv = ggtt->base.i915;
@@ -2947,14 +3148,6 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 	}
 	}
 
 
 	ggtt->base.total = (size / sizeof(gen8_pte_t)) << PAGE_SHIFT;
 	ggtt->base.total = (size / sizeof(gen8_pte_t)) << PAGE_SHIFT;
-
-	if (INTEL_GEN(dev_priv) >= 10)
-		cnl_setup_private_ppat(dev_priv);
-	else if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv))
-		chv_setup_private_ppat(dev_priv);
-	else
-		bdw_setup_private_ppat(dev_priv);
-
 	ggtt->base.cleanup = gen6_gmch_remove;
 	ggtt->base.cleanup = gen6_gmch_remove;
 	ggtt->base.bind_vma = ggtt_bind_vma;
 	ggtt->base.bind_vma = ggtt_bind_vma;
 	ggtt->base.unbind_vma = ggtt_unbind_vma;
 	ggtt->base.unbind_vma = ggtt_unbind_vma;
@@ -2975,6 +3168,8 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 
 
 	ggtt->invalidate = gen6_ggtt_invalidate;
 	ggtt->invalidate = gen6_ggtt_invalidate;
 
 
+	setup_private_pat(dev_priv);
+
 	return ggtt_probe_common(ggtt, size);
 	return ggtt_probe_common(ggtt, size);
 }
 }
 
 
@@ -3095,7 +3290,7 @@ int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv)
 	 * currently don't have any bits spare to pass in this upper
 	 * currently don't have any bits spare to pass in this upper
 	 * restriction!
 	 * restriction!
 	 */
 	 */
-	if (HAS_GUC(dev_priv) && i915.enable_guc_loading) {
+	if (HAS_GUC(dev_priv) && i915_modparams.enable_guc_loading) {
 		ggtt->base.total = min_t(u64, ggtt->base.total, GUC_GGTT_TOP);
 		ggtt->base.total = min_t(u64, ggtt->base.total, GUC_GGTT_TOP);
 		ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total);
 		ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total);
 	}
 	}
@@ -3232,13 +3427,10 @@ void i915_gem_restore_gtt_mappings(struct drm_i915_private *dev_priv)
 	ggtt->base.closed = false;
 	ggtt->base.closed = false;
 
 
 	if (INTEL_GEN(dev_priv) >= 8) {
 	if (INTEL_GEN(dev_priv) >= 8) {
-		if (INTEL_GEN(dev_priv) >= 10)
-			cnl_setup_private_ppat(dev_priv);
-		else if (IS_CHERRYVIEW(dev_priv) || IS_GEN9_LP(dev_priv))
-			chv_setup_private_ppat(dev_priv);
-		else
-			bdw_setup_private_ppat(dev_priv);
+		struct intel_ppat *ppat = &dev_priv->ppat;
 
 
+		bitmap_set(ppat->dirty, 0, ppat->max_entries);
+		dev_priv->ppat.update_hw(dev_priv);
 		return;
 		return;
 	}
 	}
 
 

+ 41 - 5
drivers/gpu/drm/i915/i915_gem_gtt.h

@@ -126,13 +126,13 @@ typedef u64 gen8_ppgtt_pml4e_t;
  * tables */
  * tables */
 #define GEN8_PDPE_MASK			0x1ff
 #define GEN8_PDPE_MASK			0x1ff
 
 
-#define PPAT_UNCACHED_INDEX		(_PAGE_PWT | _PAGE_PCD)
-#define PPAT_CACHED_PDE_INDEX		0 /* WB LLC */
-#define PPAT_CACHED_INDEX		_PAGE_PAT /* WB LLCeLLC */
-#define PPAT_DISPLAY_ELLC_INDEX		_PAGE_PCD /* WT eLLC */
+#define PPAT_UNCACHED			(_PAGE_PWT | _PAGE_PCD)
+#define PPAT_CACHED_PDE			0 /* WB LLC */
+#define PPAT_CACHED			_PAGE_PAT /* WB LLCeLLC */
+#define PPAT_DISPLAY_ELLC		_PAGE_PCD /* WT eLLC */
 
 
 #define CHV_PPAT_SNOOP			(1<<6)
 #define CHV_PPAT_SNOOP			(1<<6)
-#define GEN8_PPAT_AGE(x)		(x<<4)
+#define GEN8_PPAT_AGE(x)		((x)<<4)
 #define GEN8_PPAT_LLCeLLC		(3<<2)
 #define GEN8_PPAT_LLCeLLC		(3<<2)
 #define GEN8_PPAT_LLCELLC		(2<<2)
 #define GEN8_PPAT_LLCELLC		(2<<2)
 #define GEN8_PPAT_LLC			(1<<2)
 #define GEN8_PPAT_LLC			(1<<2)
@@ -143,6 +143,11 @@ typedef u64 gen8_ppgtt_pml4e_t;
 #define GEN8_PPAT_ELLC_OVERRIDE		(0<<2)
 #define GEN8_PPAT_ELLC_OVERRIDE		(0<<2)
 #define GEN8_PPAT(i, x)			((u64)(x) << ((i) * 8))
 #define GEN8_PPAT(i, x)			((u64)(x) << ((i) * 8))
 
 
+#define GEN8_PPAT_GET_CA(x) ((x) & 3)
+#define GEN8_PPAT_GET_TC(x) ((x) & (3 << 2))
+#define GEN8_PPAT_GET_AGE(x) ((x) & (3 << 4))
+#define CHV_PPAT_GET_SNOOP(x) ((x) & (1 << 6))
+
 struct sg_table;
 struct sg_table;
 
 
 struct intel_rotation_info {
 struct intel_rotation_info {
@@ -536,6 +541,37 @@ i915_vm_to_ggtt(struct i915_address_space *vm)
 	return container_of(vm, struct i915_ggtt, base);
 	return container_of(vm, struct i915_ggtt, base);
 }
 }
 
 
+#define INTEL_MAX_PPAT_ENTRIES 8
+#define INTEL_PPAT_PERFECT_MATCH (~0U)
+
+struct intel_ppat;
+
+struct intel_ppat_entry {
+	struct intel_ppat *ppat;
+	struct kref ref;
+	u8 value;
+};
+
+struct intel_ppat {
+	struct intel_ppat_entry entries[INTEL_MAX_PPAT_ENTRIES];
+	DECLARE_BITMAP(used, INTEL_MAX_PPAT_ENTRIES);
+	DECLARE_BITMAP(dirty, INTEL_MAX_PPAT_ENTRIES);
+	unsigned int max_entries;
+	u8 clear_value;
+	/*
+	 * Return a score to show how two PPAT values match,
+	 * a INTEL_PPAT_PERFECT_MATCH indicates a perfect match
+	 */
+	unsigned int (*match)(u8 src, u8 dst);
+	void (*update_hw)(struct drm_i915_private *i915);
+
+	struct drm_i915_private *i915;
+};
+
+const struct intel_ppat_entry *
+intel_ppat_get(struct drm_i915_private *i915, u8 value);
+void intel_ppat_put(const struct intel_ppat_entry *entry);
+
 int i915_gem_init_aliasing_ppgtt(struct drm_i915_private *i915);
 int i915_gem_init_aliasing_ppgtt(struct drm_i915_private *i915);
 void i915_gem_fini_aliasing_ppgtt(struct drm_i915_private *i915);
 void i915_gem_fini_aliasing_ppgtt(struct drm_i915_private *i915);
 
 

+ 22 - 10
drivers/gpu/drm/i915/i915_gem_request.c

@@ -1021,12 +1021,28 @@ static bool busywait_stop(unsigned long timeout, unsigned int cpu)
 	return this_cpu != cpu;
 	return this_cpu != cpu;
 }
 }
 
 
-bool __i915_spin_request(const struct drm_i915_gem_request *req,
-			 u32 seqno, int state, unsigned long timeout_us)
+static bool __i915_spin_request(const struct drm_i915_gem_request *req,
+				u32 seqno, int state, unsigned long timeout_us)
 {
 {
 	struct intel_engine_cs *engine = req->engine;
 	struct intel_engine_cs *engine = req->engine;
 	unsigned int irq, cpu;
 	unsigned int irq, cpu;
 
 
+	GEM_BUG_ON(!seqno);
+
+	/*
+	 * Only wait for the request if we know it is likely to complete.
+	 *
+	 * We don't track the timestamps around requests, nor the average
+	 * request length, so we do not have a good indicator that this
+	 * request will complete within the timeout. What we do know is the
+	 * order in which requests are executed by the engine and so we can
+	 * tell if the request has started. If the request hasn't started yet,
+	 * it is a fair assumption that it will not complete within our
+	 * relatively short timeout.
+	 */
+	if (!i915_seqno_passed(intel_engine_get_seqno(engine), seqno - 1))
+		return false;
+
 	/* When waiting for high frequency requests, e.g. during synchronous
 	/* When waiting for high frequency requests, e.g. during synchronous
 	 * rendering split between the CPU and GPU, the finite amount of time
 	 * rendering split between the CPU and GPU, the finite amount of time
 	 * required to set up the irq and wait upon it limits the response
 	 * required to set up the irq and wait upon it limits the response
@@ -1040,12 +1056,8 @@ bool __i915_spin_request(const struct drm_i915_gem_request *req,
 	irq = atomic_read(&engine->irq_count);
 	irq = atomic_read(&engine->irq_count);
 	timeout_us += local_clock_us(&cpu);
 	timeout_us += local_clock_us(&cpu);
 	do {
 	do {
-		if (seqno != i915_gem_request_global_seqno(req))
-			break;
-
-		if (i915_seqno_passed(intel_engine_get_seqno(req->engine),
-				      seqno))
-			return true;
+		if (i915_seqno_passed(intel_engine_get_seqno(engine), seqno))
+			return seqno == i915_gem_request_global_seqno(req);
 
 
 		/* Seqno are meant to be ordered *before* the interrupt. If
 		/* Seqno are meant to be ordered *before* the interrupt. If
 		 * we see an interrupt without a corresponding seqno advance,
 		 * we see an interrupt without a corresponding seqno advance,
@@ -1156,7 +1168,7 @@ restart:
 	GEM_BUG_ON(!i915_sw_fence_signaled(&req->submit));
 	GEM_BUG_ON(!i915_sw_fence_signaled(&req->submit));
 
 
 	/* Optimistic short spin before touching IRQs */
 	/* Optimistic short spin before touching IRQs */
-	if (i915_spin_request(req, state, 5))
+	if (__i915_spin_request(req, wait.seqno, state, 5))
 		goto complete;
 		goto complete;
 
 
 	set_current_state(state);
 	set_current_state(state);
@@ -1213,7 +1225,7 @@ wakeup:
 			continue;
 			continue;
 
 
 		/* Only spin if we know the GPU is processing this request */
 		/* Only spin if we know the GPU is processing this request */
-		if (i915_spin_request(req, state, 2))
+		if (__i915_spin_request(req, wait.seqno, state, 2))
 			break;
 			break;
 
 
 		if (!intel_wait_check_request(&wait, req)) {
 		if (!intel_wait_check_request(&wait, req)) {

+ 0 - 35
drivers/gpu/drm/i915/i915_gem_request.h

@@ -312,26 +312,6 @@ static inline bool i915_seqno_passed(u32 seq1, u32 seq2)
 	return (s32)(seq1 - seq2) >= 0;
 	return (s32)(seq1 - seq2) >= 0;
 }
 }
 
 
-static inline bool
-__i915_gem_request_started(const struct drm_i915_gem_request *req, u32 seqno)
-{
-	GEM_BUG_ON(!seqno);
-	return i915_seqno_passed(intel_engine_get_seqno(req->engine),
-				 seqno - 1);
-}
-
-static inline bool
-i915_gem_request_started(const struct drm_i915_gem_request *req)
-{
-	u32 seqno;
-
-	seqno = i915_gem_request_global_seqno(req);
-	if (!seqno)
-		return false;
-
-	return __i915_gem_request_started(req, seqno);
-}
-
 static inline bool
 static inline bool
 __i915_gem_request_completed(const struct drm_i915_gem_request *req, u32 seqno)
 __i915_gem_request_completed(const struct drm_i915_gem_request *req, u32 seqno)
 {
 {
@@ -352,21 +332,6 @@ i915_gem_request_completed(const struct drm_i915_gem_request *req)
 	return __i915_gem_request_completed(req, seqno);
 	return __i915_gem_request_completed(req, seqno);
 }
 }
 
 
-bool __i915_spin_request(const struct drm_i915_gem_request *request,
-			 u32 seqno, int state, unsigned long timeout_us);
-static inline bool i915_spin_request(const struct drm_i915_gem_request *request,
-				     int state, unsigned long timeout_us)
-{
-	u32 seqno;
-
-	seqno = i915_gem_request_global_seqno(request);
-	if (!seqno)
-		return 0;
-
-	return (__i915_gem_request_started(request, seqno) &&
-		__i915_spin_request(request, seqno, state, timeout_us));
-}
-
 /* We treat requests as fences. This is not be to confused with our
 /* We treat requests as fences. This is not be to confused with our
  * "fence registers" but pipeline synchronisation objects ala GL_ARB_sync.
  * "fence registers" but pipeline synchronisation objects ala GL_ARB_sync.
  * We use the fences to synchronize access from the CPU with activity on the
  * We use the fences to synchronize access from the CPU with activity on the

+ 32 - 51
drivers/gpu/drm/i915/i915_gem_userptr.c

@@ -399,64 +399,42 @@ struct get_pages_work {
 	struct task_struct *task;
 	struct task_struct *task;
 };
 };
 
 
-#if IS_ENABLED(CONFIG_SWIOTLB)
-#define swiotlb_active() swiotlb_nr_tbl()
-#else
-#define swiotlb_active() 0
-#endif
-
-static int
-st_set_pages(struct sg_table **st, struct page **pvec, int num_pages)
-{
-	struct scatterlist *sg;
-	int ret, n;
-
-	*st = kmalloc(sizeof(**st), GFP_KERNEL);
-	if (*st == NULL)
-		return -ENOMEM;
-
-	if (swiotlb_active()) {
-		ret = sg_alloc_table(*st, num_pages, GFP_KERNEL);
-		if (ret)
-			goto err;
-
-		for_each_sg((*st)->sgl, sg, num_pages, n)
-			sg_set_page(sg, pvec[n], PAGE_SIZE, 0);
-	} else {
-		ret = sg_alloc_table_from_pages(*st, pvec, num_pages,
-						0, num_pages << PAGE_SHIFT,
-						GFP_KERNEL);
-		if (ret)
-			goto err;
-	}
-
-	return 0;
-
-err:
-	kfree(*st);
-	*st = NULL;
-	return ret;
-}
-
 static struct sg_table *
 static struct sg_table *
-__i915_gem_userptr_set_pages(struct drm_i915_gem_object *obj,
-			     struct page **pvec, int num_pages)
+__i915_gem_userptr_alloc_pages(struct drm_i915_gem_object *obj,
+			       struct page **pvec, int num_pages)
 {
 {
-	struct sg_table *pages;
+	unsigned int max_segment = i915_sg_segment_size();
+	struct sg_table *st;
 	int ret;
 	int ret;
 
 
-	ret = st_set_pages(&pages, pvec, num_pages);
-	if (ret)
+	st = kmalloc(sizeof(*st), GFP_KERNEL);
+	if (!st)
+		return ERR_PTR(-ENOMEM);
+
+alloc_table:
+	ret = __sg_alloc_table_from_pages(st, pvec, num_pages,
+					  0, num_pages << PAGE_SHIFT,
+					  max_segment,
+					  GFP_KERNEL);
+	if (ret) {
+		kfree(st);
 		return ERR_PTR(ret);
 		return ERR_PTR(ret);
+	}
 
 
-	ret = i915_gem_gtt_prepare_pages(obj, pages);
+	ret = i915_gem_gtt_prepare_pages(obj, st);
 	if (ret) {
 	if (ret) {
-		sg_free_table(pages);
-		kfree(pages);
+		sg_free_table(st);
+
+		if (max_segment > PAGE_SIZE) {
+			max_segment = PAGE_SIZE;
+			goto alloc_table;
+		}
+
+		kfree(st);
 		return ERR_PTR(ret);
 		return ERR_PTR(ret);
 	}
 	}
 
 
-	return pages;
+	return st;
 }
 }
 
 
 static int
 static int
@@ -540,7 +518,8 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
 		struct sg_table *pages = ERR_PTR(ret);
 		struct sg_table *pages = ERR_PTR(ret);
 
 
 		if (pinned == npages) {
 		if (pinned == npages) {
-			pages = __i915_gem_userptr_set_pages(obj, pvec, npages);
+			pages = __i915_gem_userptr_alloc_pages(obj, pvec,
+							       npages);
 			if (!IS_ERR(pages)) {
 			if (!IS_ERR(pages)) {
 				__i915_gem_object_set_pages(obj, pages);
 				__i915_gem_object_set_pages(obj, pages);
 				pinned = 0;
 				pinned = 0;
@@ -661,7 +640,7 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
 		pages = __i915_gem_userptr_get_pages_schedule(obj);
 		pages = __i915_gem_userptr_get_pages_schedule(obj);
 		active = pages == ERR_PTR(-EAGAIN);
 		active = pages == ERR_PTR(-EAGAIN);
 	} else {
 	} else {
-		pages = __i915_gem_userptr_set_pages(obj, pvec, num_pages);
+		pages = __i915_gem_userptr_alloc_pages(obj, pvec, num_pages);
 		active = !IS_ERR(pages);
 		active = !IS_ERR(pages);
 	}
 	}
 	if (active)
 	if (active)
@@ -834,7 +813,9 @@ int i915_gem_init_userptr(struct drm_i915_private *dev_priv)
 	hash_init(dev_priv->mm_structs);
 	hash_init(dev_priv->mm_structs);
 
 
 	dev_priv->mm.userptr_wq =
 	dev_priv->mm.userptr_wq =
-		alloc_workqueue("i915-userptr-acquire", WQ_HIGHPRI, 0);
+		alloc_workqueue("i915-userptr-acquire",
+				WQ_HIGHPRI | WQ_MEM_RECLAIM,
+				0);
 	if (!dev_priv->mm.userptr_wq)
 	if (!dev_priv->mm.userptr_wq)
 		return -ENOMEM;
 		return -ENOMEM;
 
 

+ 18 - 11
drivers/gpu/drm/i915/i915_gpu_error.c

@@ -396,6 +396,8 @@ static void error_print_context(struct drm_i915_error_state_buf *m,
 static void error_print_engine(struct drm_i915_error_state_buf *m,
 static void error_print_engine(struct drm_i915_error_state_buf *m,
 			       const struct drm_i915_error_engine *ee)
 			       const struct drm_i915_error_engine *ee)
 {
 {
+	int n;
+
 	err_printf(m, "%s command stream:\n", engine_str(ee->engine_id));
 	err_printf(m, "%s command stream:\n", engine_str(ee->engine_id));
 	err_printf(m, "  START: 0x%08x\n", ee->start);
 	err_printf(m, "  START: 0x%08x\n", ee->start);
 	err_printf(m, "  HEAD:  0x%08x [0x%08x]\n", ee->head, ee->rq_head);
 	err_printf(m, "  HEAD:  0x%08x [0x%08x]\n", ee->head, ee->rq_head);
@@ -465,8 +467,11 @@ static void error_print_engine(struct drm_i915_error_state_buf *m,
 		   jiffies_to_msecs(jiffies - ee->hangcheck_timestamp));
 		   jiffies_to_msecs(jiffies - ee->hangcheck_timestamp));
 	err_printf(m, "  engine reset count: %u\n", ee->reset_count);
 	err_printf(m, "  engine reset count: %u\n", ee->reset_count);
 
 
-	error_print_request(m, "  ELSP[0]: ", &ee->execlist[0]);
-	error_print_request(m, "  ELSP[1]: ", &ee->execlist[1]);
+	for (n = 0; n < ee->num_ports; n++) {
+		err_printf(m, "  ELSP[%d]:", n);
+		error_print_request(m, " ", &ee->execlist[n]);
+	}
+
 	error_print_context(m, "  Active context: ", &ee->context);
 	error_print_context(m, "  Active context: ", &ee->context);
 }
 }
 
 
@@ -567,7 +572,7 @@ static __always_inline void err_print_param(struct drm_i915_error_state_buf *m,
 static void err_print_params(struct drm_i915_error_state_buf *m,
 static void err_print_params(struct drm_i915_error_state_buf *m,
 			     const struct i915_params *p)
 			     const struct i915_params *p)
 {
 {
-#define PRINT(T, x) err_print_param(m, #x, #T, &p->x);
+#define PRINT(T, x, ...) err_print_param(m, #x, #T, &p->x);
 	I915_PARAMS_FOR_EACH(PRINT);
 	I915_PARAMS_FOR_EACH(PRINT);
 #undef PRINT
 #undef PRINT
 }
 }
@@ -861,7 +866,7 @@ void __i915_gpu_state_free(struct kref *error_ref)
 	kfree(error->overlay);
 	kfree(error->overlay);
 	kfree(error->display);
 	kfree(error->display);
 
 
-#define FREE(T, x) free_param(#T, &error->params.x);
+#define FREE(T, x, ...) free_param(#T, &error->params.x);
 	I915_PARAMS_FOR_EACH(FREE);
 	I915_PARAMS_FOR_EACH(FREE);
 #undef FREE
 #undef FREE
 
 
@@ -1327,17 +1332,19 @@ static void engine_record_requests(struct intel_engine_cs *engine,
 static void error_record_engine_execlists(struct intel_engine_cs *engine,
 static void error_record_engine_execlists(struct intel_engine_cs *engine,
 					  struct drm_i915_error_engine *ee)
 					  struct drm_i915_error_engine *ee)
 {
 {
-	const struct execlist_port *port = engine->execlist_port;
+	const struct intel_engine_execlists * const execlists = &engine->execlists;
 	unsigned int n;
 	unsigned int n;
 
 
-	for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++) {
-		struct drm_i915_gem_request *rq = port_request(&port[n]);
+	for (n = 0; n < execlists_num_ports(execlists); n++) {
+		struct drm_i915_gem_request *rq = port_request(&execlists->port[n]);
 
 
 		if (!rq)
 		if (!rq)
 			break;
 			break;
 
 
 		record_request(rq, &ee->execlist[n]);
 		record_request(rq, &ee->execlist[n]);
 	}
 	}
+
+	ee->num_ports = n;
 }
 }
 
 
 static void record_context(struct drm_i915_error_context *e,
 static void record_context(struct drm_i915_error_context *e,
@@ -1554,7 +1561,7 @@ static void i915_gem_capture_guc_log_buffer(struct drm_i915_private *dev_priv,
 					    struct i915_gpu_state *error)
 					    struct i915_gpu_state *error)
 {
 {
 	/* Capturing log buf contents won't be useful if logging was disabled */
 	/* Capturing log buf contents won't be useful if logging was disabled */
-	if (!dev_priv->guc.log.vma || (i915.guc_log_level < 0))
+	if (!dev_priv->guc.log.vma || (i915_modparams.guc_log_level < 0))
 		return;
 		return;
 
 
 	error->guc_log = i915_error_object_create(dev_priv,
 	error->guc_log = i915_error_object_create(dev_priv,
@@ -1696,8 +1703,8 @@ static int capture(void *data)
 		ktime_to_timeval(ktime_sub(ktime_get(),
 		ktime_to_timeval(ktime_sub(ktime_get(),
 					   error->i915->gt.last_init_time));
 					   error->i915->gt.last_init_time));
 
 
-	error->params = i915;
-#define DUP(T, x) dup_param(#T, &error->params.x);
+	error->params = i915_modparams;
+#define DUP(T, x, ...) dup_param(#T, &error->params.x);
 	I915_PARAMS_FOR_EACH(DUP);
 	I915_PARAMS_FOR_EACH(DUP);
 #undef DUP
 #undef DUP
 
 
@@ -1751,7 +1758,7 @@ void i915_capture_error_state(struct drm_i915_private *dev_priv,
 	struct i915_gpu_state *error;
 	struct i915_gpu_state *error;
 	unsigned long flags;
 	unsigned long flags;
 
 
-	if (!i915.error_capture)
+	if (!i915_modparams.error_capture)
 		return;
 		return;
 
 
 	if (READ_ONCE(dev_priv->gpu_error.first_error))
 	if (READ_ONCE(dev_priv->gpu_error.first_error))

+ 141 - 217
drivers/gpu/drm/i915/i915_guc_submission.c

@@ -192,13 +192,12 @@ static int __create_doorbell(struct i915_guc_client *client)
 
 
 	doorbell = __get_doorbell(client);
 	doorbell = __get_doorbell(client);
 	doorbell->db_status = GUC_DOORBELL_ENABLED;
 	doorbell->db_status = GUC_DOORBELL_ENABLED;
-	doorbell->cookie = client->doorbell_cookie;
+	doorbell->cookie = 0;
 
 
 	err = __guc_allocate_doorbell(client->guc, client->stage_id);
 	err = __guc_allocate_doorbell(client->guc, client->stage_id);
-	if (err) {
+	if (err)
 		doorbell->db_status = GUC_DOORBELL_DISABLED;
 		doorbell->db_status = GUC_DOORBELL_DISABLED;
-		doorbell->cookie = 0;
-	}
+
 	return err;
 	return err;
 }
 }
 
 
@@ -306,7 +305,7 @@ static void guc_proc_desc_init(struct intel_guc *guc,
 	desc->db_base_addr = 0;
 	desc->db_base_addr = 0;
 
 
 	desc->stage_id = client->stage_id;
 	desc->stage_id = client->stage_id;
-	desc->wq_size_bytes = client->wq_size;
+	desc->wq_size_bytes = GUC_WQ_SIZE;
 	desc->wq_status = WQ_STATUS_ACTIVE;
 	desc->wq_status = WQ_STATUS_ACTIVE;
 	desc->priority = client->priority;
 	desc->priority = client->priority;
 }
 }
@@ -391,8 +390,8 @@ static void guc_stage_desc_init(struct intel_guc *guc,
 	desc->db_trigger_cpu = (uintptr_t)__get_doorbell(client);
 	desc->db_trigger_cpu = (uintptr_t)__get_doorbell(client);
 	desc->db_trigger_uk = gfx_addr + client->doorbell_offset;
 	desc->db_trigger_uk = gfx_addr + client->doorbell_offset;
 	desc->process_desc = gfx_addr + client->proc_desc_offset;
 	desc->process_desc = gfx_addr + client->proc_desc_offset;
-	desc->wq_addr = gfx_addr + client->wq_offset;
-	desc->wq_size = client->wq_size;
+	desc->wq_addr = gfx_addr + GUC_DB_SIZE;
+	desc->wq_size = GUC_WQ_SIZE;
 
 
 	desc->desc_private = (uintptr_t)client;
 	desc->desc_private = (uintptr_t)client;
 }
 }
@@ -406,82 +405,23 @@ static void guc_stage_desc_fini(struct intel_guc *guc,
 	memset(desc, 0, sizeof(*desc));
 	memset(desc, 0, sizeof(*desc));
 }
 }
 
 
-/**
- * i915_guc_wq_reserve() - reserve space in the GuC's workqueue
- * @request:	request associated with the commands
- *
- * Return:	0 if space is available
- *		-EAGAIN if space is not currently available
- *
- * This function must be called (and must return 0) before a request
- * is submitted to the GuC via i915_guc_submit() below. Once a result
- * of 0 has been returned, it must be balanced by a corresponding
- * call to submit().
- *
- * Reservation allows the caller to determine in advance that space
- * will be available for the next submission before committing resources
- * to it, and helps avoid late failures with complicated recovery paths.
- */
-int i915_guc_wq_reserve(struct drm_i915_gem_request *request)
-{
-	const size_t wqi_size = sizeof(struct guc_wq_item);
-	struct i915_guc_client *client = request->i915->guc.execbuf_client;
-	struct guc_process_desc *desc = __get_process_desc(client);
-	u32 freespace;
-	int ret;
-
-	spin_lock_irq(&client->wq_lock);
-	freespace = CIRC_SPACE(client->wq_tail, desc->head, client->wq_size);
-	freespace -= client->wq_rsvd;
-	if (likely(freespace >= wqi_size)) {
-		client->wq_rsvd += wqi_size;
-		ret = 0;
-	} else {
-		client->no_wq_space++;
-		ret = -EAGAIN;
-	}
-	spin_unlock_irq(&client->wq_lock);
-
-	return ret;
-}
-
-static void guc_client_update_wq_rsvd(struct i915_guc_client *client, int size)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&client->wq_lock, flags);
-	client->wq_rsvd += size;
-	spin_unlock_irqrestore(&client->wq_lock, flags);
-}
-
-void i915_guc_wq_unreserve(struct drm_i915_gem_request *request)
-{
-	const int wqi_size = sizeof(struct guc_wq_item);
-	struct i915_guc_client *client = request->i915->guc.execbuf_client;
-
-	GEM_BUG_ON(READ_ONCE(client->wq_rsvd) < wqi_size);
-	guc_client_update_wq_rsvd(client, -wqi_size);
-}
-
 /* Construct a Work Item and append it to the GuC's Work Queue */
 /* Construct a Work Item and append it to the GuC's Work Queue */
 static void guc_wq_item_append(struct i915_guc_client *client,
 static void guc_wq_item_append(struct i915_guc_client *client,
 			       struct drm_i915_gem_request *rq)
 			       struct drm_i915_gem_request *rq)
 {
 {
 	/* wqi_len is in DWords, and does not include the one-word header */
 	/* wqi_len is in DWords, and does not include the one-word header */
 	const size_t wqi_size = sizeof(struct guc_wq_item);
 	const size_t wqi_size = sizeof(struct guc_wq_item);
-	const u32 wqi_len = wqi_size/sizeof(u32) - 1;
+	const u32 wqi_len = wqi_size / sizeof(u32) - 1;
 	struct intel_engine_cs *engine = rq->engine;
 	struct intel_engine_cs *engine = rq->engine;
+	struct i915_gem_context *ctx = rq->ctx;
 	struct guc_process_desc *desc = __get_process_desc(client);
 	struct guc_process_desc *desc = __get_process_desc(client);
 	struct guc_wq_item *wqi;
 	struct guc_wq_item *wqi;
-	u32 freespace, tail, wq_off;
+	u32 ring_tail, wq_off;
 
 
-	/* Free space is guaranteed, see i915_guc_wq_reserve() above */
-	freespace = CIRC_SPACE(client->wq_tail, desc->head, client->wq_size);
-	GEM_BUG_ON(freespace < wqi_size);
+	lockdep_assert_held(&client->wq_lock);
 
 
-	/* The GuC firmware wants the tail index in QWords, not bytes */
-	tail = intel_ring_set_tail(rq->ring, rq->tail) >> 3;
-	GEM_BUG_ON(tail > WQ_RING_TAIL_MAX);
+	ring_tail = intel_ring_set_tail(rq->ring, rq->tail) / sizeof(u64);
+	GEM_BUG_ON(ring_tail > WQ_RING_TAIL_MAX);
 
 
 	/* For now workqueue item is 4 DWs; workqueue buffer is 2 pages. So we
 	/* For now workqueue item is 4 DWs; workqueue buffer is 2 pages. So we
 	 * should not have the case where structure wqi is across page, neither
 	 * should not have the case where structure wqi is across page, neither
@@ -491,29 +431,29 @@ static void guc_wq_item_append(struct i915_guc_client *client,
 	 * workqueue buffer dw by dw.
 	 * workqueue buffer dw by dw.
 	 */
 	 */
 	BUILD_BUG_ON(wqi_size != 16);
 	BUILD_BUG_ON(wqi_size != 16);
-	GEM_BUG_ON(client->wq_rsvd < wqi_size);
 
 
-	/* postincrement WQ tail for next time */
-	wq_off = client->wq_tail;
+	/* Free space is guaranteed. */
+	wq_off = READ_ONCE(desc->tail);
+	GEM_BUG_ON(CIRC_SPACE(wq_off, READ_ONCE(desc->head),
+			      GUC_WQ_SIZE) < wqi_size);
 	GEM_BUG_ON(wq_off & (wqi_size - 1));
 	GEM_BUG_ON(wq_off & (wqi_size - 1));
-	client->wq_tail += wqi_size;
-	client->wq_tail &= client->wq_size - 1;
-	client->wq_rsvd -= wqi_size;
 
 
 	/* WQ starts from the page after doorbell / process_desc */
 	/* WQ starts from the page after doorbell / process_desc */
 	wqi = client->vaddr + wq_off + GUC_DB_SIZE;
 	wqi = client->vaddr + wq_off + GUC_DB_SIZE;
 
 
 	/* Now fill in the 4-word work queue item */
 	/* Now fill in the 4-word work queue item */
 	wqi->header = WQ_TYPE_INORDER |
 	wqi->header = WQ_TYPE_INORDER |
-			(wqi_len << WQ_LEN_SHIFT) |
-			(engine->guc_id << WQ_TARGET_SHIFT) |
-			WQ_NO_WCFLUSH_WAIT;
+		      (wqi_len << WQ_LEN_SHIFT) |
+		      (engine->guc_id << WQ_TARGET_SHIFT) |
+		      WQ_NO_WCFLUSH_WAIT;
 
 
-	/* The GuC wants only the low-order word of the context descriptor */
-	wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx, engine);
+	wqi->context_desc = lower_32_bits(intel_lr_context_descriptor(ctx, engine));
 
 
-	wqi->submit_element_info = tail << WQ_RING_TAIL_SHIFT;
+	wqi->submit_element_info = ring_tail << WQ_RING_TAIL_SHIFT;
 	wqi->fence_id = rq->global_seqno;
 	wqi->fence_id = rq->global_seqno;
+
+	/* Postincrement WQ tail for next time. */
+	WRITE_ONCE(desc->tail, (wq_off + wqi_size) & (GUC_WQ_SIZE - 1));
 }
 }
 
 
 static void guc_reset_wq(struct i915_guc_client *client)
 static void guc_reset_wq(struct i915_guc_client *client)
@@ -522,106 +462,64 @@ static void guc_reset_wq(struct i915_guc_client *client)
 
 
 	desc->head = 0;
 	desc->head = 0;
 	desc->tail = 0;
 	desc->tail = 0;
-
-	client->wq_tail = 0;
 }
 }
 
 
-static int guc_ring_doorbell(struct i915_guc_client *client)
+static void guc_ring_doorbell(struct i915_guc_client *client)
 {
 {
-	struct guc_process_desc *desc = __get_process_desc(client);
-	union guc_doorbell_qw db_cmp, db_exc, db_ret;
-	union guc_doorbell_qw *db;
-	int attempt = 2, ret = -EAGAIN;
-
-	/* Update the tail so it is visible to GuC */
-	desc->tail = client->wq_tail;
-
-	/* current cookie */
-	db_cmp.db_status = GUC_DOORBELL_ENABLED;
-	db_cmp.cookie = client->doorbell_cookie;
+	struct guc_doorbell_info *db;
+	u32 cookie;
 
 
-	/* cookie to be updated */
-	db_exc.db_status = GUC_DOORBELL_ENABLED;
-	db_exc.cookie = client->doorbell_cookie + 1;
-	if (db_exc.cookie == 0)
-		db_exc.cookie = 1;
+	lockdep_assert_held(&client->wq_lock);
 
 
 	/* pointer of current doorbell cacheline */
 	/* pointer of current doorbell cacheline */
-	db = (union guc_doorbell_qw *)__get_doorbell(client);
-
-	while (attempt--) {
-		/* lets ring the doorbell */
-		db_ret.value_qw = atomic64_cmpxchg((atomic64_t *)db,
-			db_cmp.value_qw, db_exc.value_qw);
-
-		/* if the exchange was successfully executed */
-		if (db_ret.value_qw == db_cmp.value_qw) {
-			/* db was successfully rung */
-			client->doorbell_cookie = db_exc.cookie;
-			ret = 0;
-			break;
-		}
-
-		/* XXX: doorbell was lost and need to acquire it again */
-		if (db_ret.db_status == GUC_DOORBELL_DISABLED)
-			break;
+	db = __get_doorbell(client);
 
 
-		DRM_WARN("Cookie mismatch. Expected %d, found %d\n",
-			 db_cmp.cookie, db_ret.cookie);
-
-		/* update the cookie to newly read cookie from GuC */
-		db_cmp.cookie = db_ret.cookie;
-		db_exc.cookie = db_ret.cookie + 1;
-		if (db_exc.cookie == 0)
-			db_exc.cookie = 1;
-	}
+	/* we're not expecting the doorbell cookie to change behind our back */
+	cookie = READ_ONCE(db->cookie);
+	WARN_ON_ONCE(xchg(&db->cookie, cookie + 1) != cookie);
 
 
-	return ret;
+	/* XXX: doorbell was lost and need to acquire it again */
+	GEM_BUG_ON(db->db_status != GUC_DOORBELL_ENABLED);
 }
 }
 
 
 /**
 /**
- * __i915_guc_submit() - Submit commands through GuC
- * @rq:		request associated with the commands
- *
- * The caller must have already called i915_guc_wq_reserve() above with
- * a result of 0 (success), guaranteeing that there is space in the work
- * queue for the new request, so enqueuing the item cannot fail.
- *
- * Bad Things Will Happen if the caller violates this protocol e.g. calls
- * submit() when _reserve() says there's no space, or calls _submit()
- * a different number of times from (successful) calls to _reserve().
+ * i915_guc_submit() - Submit commands through GuC
+ * @engine: engine associated with the commands
  *
  *
  * The only error here arises if the doorbell hardware isn't functioning
  * The only error here arises if the doorbell hardware isn't functioning
  * as expected, which really shouln't happen.
  * as expected, which really shouln't happen.
  */
  */
-static void __i915_guc_submit(struct drm_i915_gem_request *rq)
+static void i915_guc_submit(struct intel_engine_cs *engine)
 {
 {
-	struct drm_i915_private *dev_priv = rq->i915;
-	struct intel_engine_cs *engine = rq->engine;
-	unsigned int engine_id = engine->id;
-	struct intel_guc *guc = &rq->i915->guc;
+	struct drm_i915_private *dev_priv = engine->i915;
+	struct intel_guc *guc = &dev_priv->guc;
 	struct i915_guc_client *client = guc->execbuf_client;
 	struct i915_guc_client *client = guc->execbuf_client;
-	unsigned long flags;
-	int b_ret;
+	struct intel_engine_execlists * const execlists = &engine->execlists;
+	struct execlist_port *port = execlists->port;
+	const unsigned int engine_id = engine->id;
+	unsigned int n;
 
 
-	/* WA to flush out the pending GMADR writes to ring buffer. */
-	if (i915_vma_is_map_and_fenceable(rq->ring->vma))
-		POSTING_READ_FW(GUC_STATUS);
+	for (n = 0; n < ARRAY_SIZE(execlists->port); n++) {
+		struct drm_i915_gem_request *rq;
+		unsigned int count;
 
 
-	spin_lock_irqsave(&client->wq_lock, flags);
+		rq = port_unpack(&port[n], &count);
+		if (rq && count == 0) {
+			port_set(&port[n], port_pack(rq, ++count));
 
 
-	guc_wq_item_append(client, rq);
-	b_ret = guc_ring_doorbell(client);
+			if (i915_vma_is_map_and_fenceable(rq->ring->vma))
+				POSTING_READ_FW(GUC_STATUS);
 
 
-	client->submissions[engine_id] += 1;
+			spin_lock(&client->wq_lock);
 
 
-	spin_unlock_irqrestore(&client->wq_lock, flags);
-}
+			guc_wq_item_append(client, rq);
+			guc_ring_doorbell(client);
 
 
-static void i915_guc_submit(struct drm_i915_gem_request *rq)
-{
-	__i915_gem_request_submit(rq);
-	__i915_guc_submit(rq);
+			client->submissions[engine_id] += 1;
+
+			spin_unlock(&client->wq_lock);
+		}
+	}
 }
 }
 
 
 static void nested_enable_signaling(struct drm_i915_gem_request *rq)
 static void nested_enable_signaling(struct drm_i915_gem_request *rq)
@@ -655,27 +553,33 @@ static void port_assign(struct execlist_port *port,
 	if (port_isset(port))
 	if (port_isset(port))
 		i915_gem_request_put(port_request(port));
 		i915_gem_request_put(port_request(port));
 
 
-	port_set(port, i915_gem_request_get(rq));
+	port_set(port, port_pack(i915_gem_request_get(rq), port_count(port)));
 	nested_enable_signaling(rq);
 	nested_enable_signaling(rq);
 }
 }
 
 
-static bool i915_guc_dequeue(struct intel_engine_cs *engine)
+static void i915_guc_dequeue(struct intel_engine_cs *engine)
 {
 {
-	struct execlist_port *port = engine->execlist_port;
-	struct drm_i915_gem_request *last = port_request(port);
-	struct rb_node *rb;
+	struct intel_engine_execlists * const execlists = &engine->execlists;
+	struct execlist_port *port = execlists->port;
+	struct drm_i915_gem_request *last = NULL;
+	const struct execlist_port * const last_port =
+		&execlists->port[execlists->port_mask];
 	bool submit = false;
 	bool submit = false;
+	struct rb_node *rb;
+
+	if (port_isset(port))
+		port++;
 
 
 	spin_lock_irq(&engine->timeline->lock);
 	spin_lock_irq(&engine->timeline->lock);
-	rb = engine->execlist_first;
-	GEM_BUG_ON(rb_first(&engine->execlist_queue) != rb);
+	rb = execlists->first;
+	GEM_BUG_ON(rb_first(&execlists->queue) != rb);
 	while (rb) {
 	while (rb) {
 		struct i915_priolist *p = rb_entry(rb, typeof(*p), node);
 		struct i915_priolist *p = rb_entry(rb, typeof(*p), node);
 		struct drm_i915_gem_request *rq, *rn;
 		struct drm_i915_gem_request *rq, *rn;
 
 
 		list_for_each_entry_safe(rq, rn, &p->requests, priotree.link) {
 		list_for_each_entry_safe(rq, rn, &p->requests, priotree.link) {
 			if (last && rq->ctx != last->ctx) {
 			if (last && rq->ctx != last->ctx) {
-				if (port != engine->execlist_port) {
+				if (port == last_port) {
 					__list_del_many(&p->requests,
 					__list_del_many(&p->requests,
 							&rq->priotree.link);
 							&rq->priotree.link);
 					goto done;
 					goto done;
@@ -689,50 +593,48 @@ static bool i915_guc_dequeue(struct intel_engine_cs *engine)
 			INIT_LIST_HEAD(&rq->priotree.link);
 			INIT_LIST_HEAD(&rq->priotree.link);
 			rq->priotree.priority = INT_MAX;
 			rq->priotree.priority = INT_MAX;
 
 
-			i915_guc_submit(rq);
-			trace_i915_gem_request_in(rq, port_index(port, engine));
+			__i915_gem_request_submit(rq);
+			trace_i915_gem_request_in(rq, port_index(port, execlists));
 			last = rq;
 			last = rq;
 			submit = true;
 			submit = true;
 		}
 		}
 
 
 		rb = rb_next(rb);
 		rb = rb_next(rb);
-		rb_erase(&p->node, &engine->execlist_queue);
+		rb_erase(&p->node, &execlists->queue);
 		INIT_LIST_HEAD(&p->requests);
 		INIT_LIST_HEAD(&p->requests);
 		if (p->priority != I915_PRIORITY_NORMAL)
 		if (p->priority != I915_PRIORITY_NORMAL)
 			kmem_cache_free(engine->i915->priorities, p);
 			kmem_cache_free(engine->i915->priorities, p);
 	}
 	}
 done:
 done:
-	engine->execlist_first = rb;
-	if (submit)
+	execlists->first = rb;
+	if (submit) {
 		port_assign(port, last);
 		port_assign(port, last);
+		i915_guc_submit(engine);
+	}
 	spin_unlock_irq(&engine->timeline->lock);
 	spin_unlock_irq(&engine->timeline->lock);
-
-	return submit;
 }
 }
 
 
 static void i915_guc_irq_handler(unsigned long data)
 static void i915_guc_irq_handler(unsigned long data)
 {
 {
-	struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
-	struct execlist_port *port = engine->execlist_port;
+	struct intel_engine_cs * const engine = (struct intel_engine_cs *)data;
+	struct intel_engine_execlists * const execlists = &engine->execlists;
+	struct execlist_port *port = execlists->port;
+	const struct execlist_port * const last_port =
+		&execlists->port[execlists->port_mask];
 	struct drm_i915_gem_request *rq;
 	struct drm_i915_gem_request *rq;
-	bool submit;
 
 
-	do {
-		rq = port_request(&port[0]);
-		while (rq && i915_gem_request_completed(rq)) {
-			trace_i915_gem_request_out(rq);
-			i915_gem_request_put(rq);
+	rq = port_request(&port[0]);
+	while (rq && i915_gem_request_completed(rq)) {
+		trace_i915_gem_request_out(rq);
+		i915_gem_request_put(rq);
 
 
-			port[0] = port[1];
-			memset(&port[1], 0, sizeof(port[1]));
+		execlists_port_complete(execlists, port);
 
 
-			rq = port_request(&port[0]);
-		}
+		rq = port_request(&port[0]);
+	}
 
 
-		submit = false;
-		if (!port_count(&port[1]))
-			submit = i915_guc_dequeue(engine);
-	} while (submit);
+	if (!port_isset(last_port))
+		i915_guc_dequeue(engine);
 }
 }
 
 
 /*
 /*
@@ -913,8 +815,6 @@ guc_client_alloc(struct drm_i915_private *dev_priv,
 	client->engines = engines;
 	client->engines = engines;
 	client->priority = priority;
 	client->priority = priority;
 	client->doorbell_id = GUC_DOORBELL_INVALID;
 	client->doorbell_id = GUC_DOORBELL_INVALID;
-	client->wq_offset = GUC_DB_SIZE;
-	client->wq_size = GUC_WQ_SIZE;
 	spin_lock_init(&client->wq_lock);
 	spin_lock_init(&client->wq_lock);
 
 
 	ret = ida_simple_get(&guc->stage_ids, 0, GUC_MAX_STAGE_DESCRIPTORS,
 	ret = ida_simple_get(&guc->stage_ids, 0, GUC_MAX_STAGE_DESCRIPTORS,
@@ -996,28 +896,39 @@ static void guc_client_free(struct i915_guc_client *client)
 	kfree(client);
 	kfree(client);
 }
 }
 
 
+static void guc_policy_init(struct guc_policy *policy)
+{
+	policy->execution_quantum = POLICY_DEFAULT_EXECUTION_QUANTUM_US;
+	policy->preemption_time = POLICY_DEFAULT_PREEMPTION_TIME_US;
+	policy->fault_time = POLICY_DEFAULT_FAULT_TIME_US;
+	policy->policy_flags = 0;
+}
+
 static void guc_policies_init(struct guc_policies *policies)
 static void guc_policies_init(struct guc_policies *policies)
 {
 {
 	struct guc_policy *policy;
 	struct guc_policy *policy;
 	u32 p, i;
 	u32 p, i;
 
 
-	policies->dpc_promote_time = 500000;
+	policies->dpc_promote_time = POLICY_DEFAULT_DPC_PROMOTE_TIME_US;
 	policies->max_num_work_items = POLICY_MAX_NUM_WI;
 	policies->max_num_work_items = POLICY_MAX_NUM_WI;
 
 
 	for (p = 0; p < GUC_CLIENT_PRIORITY_NUM; p++) {
 	for (p = 0; p < GUC_CLIENT_PRIORITY_NUM; p++) {
 		for (i = GUC_RENDER_ENGINE; i < GUC_MAX_ENGINES_NUM; i++) {
 		for (i = GUC_RENDER_ENGINE; i < GUC_MAX_ENGINES_NUM; i++) {
 			policy = &policies->policy[p][i];
 			policy = &policies->policy[p][i];
 
 
-			policy->execution_quantum = 1000000;
-			policy->preemption_time = 500000;
-			policy->fault_time = 250000;
-			policy->policy_flags = 0;
+			guc_policy_init(policy);
 		}
 		}
 	}
 	}
 
 
 	policies->is_valid = 1;
 	policies->is_valid = 1;
 }
 }
 
 
+/*
+ * The first 80 dwords of the register state context, containing the
+ * execlists and ppgtt registers.
+ */
+#define LR_HW_CONTEXT_SIZE	(80 * sizeof(u32))
+
 static int guc_ads_create(struct intel_guc *guc)
 static int guc_ads_create(struct intel_guc *guc)
 {
 {
 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
 	struct drm_i915_private *dev_priv = guc_to_i915(guc);
@@ -1032,6 +943,8 @@ static int guc_ads_create(struct intel_guc *guc)
 	} __packed *blob;
 	} __packed *blob;
 	struct intel_engine_cs *engine;
 	struct intel_engine_cs *engine;
 	enum intel_engine_id id;
 	enum intel_engine_id id;
+	const u32 skipped_offset = LRC_HEADER_PAGES * PAGE_SIZE;
+	const u32 skipped_size = LRC_PPHWSP_SZ * PAGE_SIZE + LR_HW_CONTEXT_SIZE;
 	u32 base;
 	u32 base;
 
 
 	GEM_BUG_ON(guc->ads_vma);
 	GEM_BUG_ON(guc->ads_vma);
@@ -1062,13 +975,20 @@ static int guc_ads_create(struct intel_guc *guc)
 	 * engines after a reset. Here we use the Render ring default
 	 * engines after a reset. Here we use the Render ring default
 	 * context, which must already exist and be pinned in the GGTT,
 	 * context, which must already exist and be pinned in the GGTT,
 	 * so its address won't change after we've told the GuC where
 	 * so its address won't change after we've told the GuC where
-	 * to find it.
+	 * to find it. Note that we have to skip our header (1 page),
+	 * because our GuC shared data is there.
 	 */
 	 */
 	blob->ads.golden_context_lrca =
 	blob->ads.golden_context_lrca =
-		dev_priv->engine[RCS]->status_page.ggtt_offset;
+		guc_ggtt_offset(dev_priv->kernel_context->engine[RCS].state) + skipped_offset;
 
 
+	/*
+	 * The GuC expects us to exclude the portion of the context image that
+	 * it skips from the size it is to read. It starts reading from after
+	 * the execlist context (so skipping the first page [PPHWSP] and 80
+	 * dwords). Weird guc is weird.
+	 */
 	for_each_engine(engine, dev_priv, id)
 	for_each_engine(engine, dev_priv, id)
-		blob->ads.eng_state_size[engine->guc_id] = engine->context_size;
+		blob->ads.eng_state_size[engine->guc_id] = engine->context_size - skipped_size;
 
 
 	base = guc_ggtt_offset(vma);
 	base = guc_ggtt_offset(vma);
 	blob->ads.scheduler_policies = base + ptr_offset(blob, policies);
 	blob->ads.scheduler_policies = base + ptr_offset(blob, policies);
@@ -1221,6 +1141,19 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv)
 	enum intel_engine_id id;
 	enum intel_engine_id id;
 	int err;
 	int err;
 
 
+	/*
+	 * We're using GuC work items for submitting work through GuC. Since
+	 * we're coalescing multiple requests from a single context into a
+	 * single work item prior to assigning it to execlist_port, we can
+	 * never have more work items than the total number of ports (for all
+	 * engines). The GuC firmware is controlling the HEAD of work queue,
+	 * and it is guaranteed that it will remove the work item from the
+	 * queue before our request is completed.
+	 */
+	BUILD_BUG_ON(ARRAY_SIZE(engine->execlists.port) *
+		     sizeof(struct guc_wq_item) *
+		     I915_NUM_ENGINES > GUC_WQ_SIZE);
+
 	if (!client) {
 	if (!client) {
 		client = guc_client_alloc(dev_priv,
 		client = guc_client_alloc(dev_priv,
 					  INTEL_INFO(dev_priv)->ring_mask,
 					  INTEL_INFO(dev_priv)->ring_mask,
@@ -1248,24 +1181,15 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv)
 	guc_interrupts_capture(dev_priv);
 	guc_interrupts_capture(dev_priv);
 
 
 	for_each_engine(engine, dev_priv, id) {
 	for_each_engine(engine, dev_priv, id) {
-		const int wqi_size = sizeof(struct guc_wq_item);
-		struct drm_i915_gem_request *rq;
-
+		struct intel_engine_execlists * const execlists = &engine->execlists;
 		/* The tasklet was initialised by execlists, and may be in
 		/* The tasklet was initialised by execlists, and may be in
 		 * a state of flux (across a reset) and so we just want to
 		 * a state of flux (across a reset) and so we just want to
 		 * take over the callback without changing any other state
 		 * take over the callback without changing any other state
 		 * in the tasklet.
 		 * in the tasklet.
 		 */
 		 */
-		engine->irq_tasklet.func = i915_guc_irq_handler;
+		execlists->irq_tasklet.func = i915_guc_irq_handler;
 		clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
 		clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
-
-		/* Replay the current set of previously submitted requests */
-		spin_lock_irq(&engine->timeline->lock);
-		list_for_each_entry(rq, &engine->timeline->requests, link) {
-			guc_client_update_wq_rsvd(client, wqi_size);
-			__i915_guc_submit(rq);
-		}
-		spin_unlock_irq(&engine->timeline->lock);
+		tasklet_schedule(&execlists->irq_tasklet);
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -1310,7 +1234,7 @@ int intel_guc_suspend(struct drm_i915_private *dev_priv)
 	/* any value greater than GUC_POWER_D0 */
 	/* any value greater than GUC_POWER_D0 */
 	data[1] = GUC_POWER_D1;
 	data[1] = GUC_POWER_D1;
 	/* first page is shared data with GuC */
 	/* first page is shared data with GuC */
-	data[2] = guc_ggtt_offset(ctx->engine[RCS].state);
+	data[2] = guc_ggtt_offset(ctx->engine[RCS].state) + LRC_GUCSHR_PN * PAGE_SIZE;
 
 
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 }
 }
@@ -1328,7 +1252,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv)
 	if (guc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS)
 	if (guc->fw.load_status != INTEL_UC_FIRMWARE_SUCCESS)
 		return 0;
 		return 0;
 
 
-	if (i915.guc_log_level >= 0)
+	if (i915_modparams.guc_log_level >= 0)
 		gen9_enable_guc_interrupts(dev_priv);
 		gen9_enable_guc_interrupts(dev_priv);
 
 
 	ctx = dev_priv->kernel_context;
 	ctx = dev_priv->kernel_context;
@@ -1336,7 +1260,7 @@ int intel_guc_resume(struct drm_i915_private *dev_priv)
 	data[0] = INTEL_GUC_ACTION_EXIT_S_STATE;
 	data[0] = INTEL_GUC_ACTION_EXIT_S_STATE;
 	data[1] = GUC_POWER_D0;
 	data[1] = GUC_POWER_D0;
 	/* first page is shared data with GuC */
 	/* first page is shared data with GuC */
-	data[2] = guc_ggtt_offset(ctx->engine[RCS].state);
+	data[2] = guc_ggtt_offset(ctx->engine[RCS].state) + LRC_GUCSHR_PN * PAGE_SIZE;
 
 
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 	return intel_guc_send(guc, data, ARRAY_SIZE(data));
 }
 }

+ 417 - 544
drivers/gpu/drm/i915/i915_irq.c

@@ -126,7 +126,7 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = {
 	POSTING_READ(GEN8_##type##_IIR(which)); \
 	POSTING_READ(GEN8_##type##_IIR(which)); \
 } while (0)
 } while (0)
 
 
-#define GEN5_IRQ_RESET(type) do { \
+#define GEN3_IRQ_RESET(type) do { \
 	I915_WRITE(type##IMR, 0xffffffff); \
 	I915_WRITE(type##IMR, 0xffffffff); \
 	POSTING_READ(type##IMR); \
 	POSTING_READ(type##IMR); \
 	I915_WRITE(type##IER, 0); \
 	I915_WRITE(type##IER, 0); \
@@ -136,10 +136,20 @@ static const u32 hpd_bxt[HPD_NUM_PINS] = {
 	POSTING_READ(type##IIR); \
 	POSTING_READ(type##IIR); \
 } while (0)
 } while (0)
 
 
+#define GEN2_IRQ_RESET(type) do { \
+	I915_WRITE16(type##IMR, 0xffff); \
+	POSTING_READ16(type##IMR); \
+	I915_WRITE16(type##IER, 0); \
+	I915_WRITE16(type##IIR, 0xffff); \
+	POSTING_READ16(type##IIR); \
+	I915_WRITE16(type##IIR, 0xffff); \
+	POSTING_READ16(type##IIR); \
+} while (0)
+
 /*
 /*
  * We should clear IMR at preinstall/uninstall, and just check at postinstall.
  * We should clear IMR at preinstall/uninstall, and just check at postinstall.
  */
  */
-static void gen5_assert_iir_is_zero(struct drm_i915_private *dev_priv,
+static void gen3_assert_iir_is_zero(struct drm_i915_private *dev_priv,
 				    i915_reg_t reg)
 				    i915_reg_t reg)
 {
 {
 	u32 val = I915_READ(reg);
 	u32 val = I915_READ(reg);
@@ -155,20 +165,43 @@ static void gen5_assert_iir_is_zero(struct drm_i915_private *dev_priv,
 	POSTING_READ(reg);
 	POSTING_READ(reg);
 }
 }
 
 
+static void gen2_assert_iir_is_zero(struct drm_i915_private *dev_priv,
+				    i915_reg_t reg)
+{
+	u16 val = I915_READ16(reg);
+
+	if (val == 0)
+		return;
+
+	WARN(1, "Interrupt register 0x%x is not zero: 0x%08x\n",
+	     i915_mmio_reg_offset(reg), val);
+	I915_WRITE16(reg, 0xffff);
+	POSTING_READ16(reg);
+	I915_WRITE16(reg, 0xffff);
+	POSTING_READ16(reg);
+}
+
 #define GEN8_IRQ_INIT_NDX(type, which, imr_val, ier_val) do { \
 #define GEN8_IRQ_INIT_NDX(type, which, imr_val, ier_val) do { \
-	gen5_assert_iir_is_zero(dev_priv, GEN8_##type##_IIR(which)); \
+	gen3_assert_iir_is_zero(dev_priv, GEN8_##type##_IIR(which)); \
 	I915_WRITE(GEN8_##type##_IER(which), (ier_val)); \
 	I915_WRITE(GEN8_##type##_IER(which), (ier_val)); \
 	I915_WRITE(GEN8_##type##_IMR(which), (imr_val)); \
 	I915_WRITE(GEN8_##type##_IMR(which), (imr_val)); \
 	POSTING_READ(GEN8_##type##_IMR(which)); \
 	POSTING_READ(GEN8_##type##_IMR(which)); \
 } while (0)
 } while (0)
 
 
-#define GEN5_IRQ_INIT(type, imr_val, ier_val) do { \
-	gen5_assert_iir_is_zero(dev_priv, type##IIR); \
+#define GEN3_IRQ_INIT(type, imr_val, ier_val) do { \
+	gen3_assert_iir_is_zero(dev_priv, type##IIR); \
 	I915_WRITE(type##IER, (ier_val)); \
 	I915_WRITE(type##IER, (ier_val)); \
 	I915_WRITE(type##IMR, (imr_val)); \
 	I915_WRITE(type##IMR, (imr_val)); \
 	POSTING_READ(type##IMR); \
 	POSTING_READ(type##IMR); \
 } while (0)
 } while (0)
 
 
+#define GEN2_IRQ_INIT(type, imr_val, ier_val) do { \
+	gen2_assert_iir_is_zero(dev_priv, type##IIR); \
+	I915_WRITE16(type##IER, (ier_val)); \
+	I915_WRITE16(type##IMR, (imr_val)); \
+	POSTING_READ16(type##IMR); \
+} while (0)
+
 static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
 static void gen6_rps_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
 static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
 static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 pm_iir);
 
 
@@ -534,62 +567,16 @@ void ibx_display_interrupt_update(struct drm_i915_private *dev_priv,
 	POSTING_READ(SDEIMR);
 	POSTING_READ(SDEIMR);
 }
 }
 
 
-static void
-__i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
-		       u32 enable_mask, u32 status_mask)
+u32 i915_pipestat_enable_mask(struct drm_i915_private *dev_priv,
+			      enum pipe pipe)
 {
 {
-	i915_reg_t reg = PIPESTAT(pipe);
-	u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK;
-
-	lockdep_assert_held(&dev_priv->irq_lock);
-	WARN_ON(!intel_irqs_enabled(dev_priv));
-
-	if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
-		      status_mask & ~PIPESTAT_INT_STATUS_MASK,
-		      "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
-		      pipe_name(pipe), enable_mask, status_mask))
-		return;
-
-	if ((pipestat & enable_mask) == enable_mask)
-		return;
-
-	dev_priv->pipestat_irq_mask[pipe] |= status_mask;
-
-	/* Enable the interrupt, clear any pending status */
-	pipestat |= enable_mask | status_mask;
-	I915_WRITE(reg, pipestat);
-	POSTING_READ(reg);
-}
-
-static void
-__i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
-		        u32 enable_mask, u32 status_mask)
-{
-	i915_reg_t reg = PIPESTAT(pipe);
-	u32 pipestat = I915_READ(reg) & PIPESTAT_INT_ENABLE_MASK;
+	u32 status_mask = dev_priv->pipestat_irq_mask[pipe];
+	u32 enable_mask = status_mask << 16;
 
 
 	lockdep_assert_held(&dev_priv->irq_lock);
 	lockdep_assert_held(&dev_priv->irq_lock);
-	WARN_ON(!intel_irqs_enabled(dev_priv));
-
-	if (WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
-		      status_mask & ~PIPESTAT_INT_STATUS_MASK,
-		      "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
-		      pipe_name(pipe), enable_mask, status_mask))
-		return;
-
-	if ((pipestat & enable_mask) == 0)
-		return;
-
-	dev_priv->pipestat_irq_mask[pipe] &= ~status_mask;
 
 
-	pipestat &= ~enable_mask;
-	I915_WRITE(reg, pipestat);
-	POSTING_READ(reg);
-}
-
-static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask)
-{
-	u32 enable_mask = status_mask << 16;
+	if (INTEL_GEN(dev_priv) < 5)
+		goto out;
 
 
 	/*
 	/*
 	 * On pipe A we don't support the PSR interrupt yet,
 	 * On pipe A we don't support the PSR interrupt yet,
@@ -612,35 +599,59 @@ static u32 vlv_get_pipestat_enable_mask(struct drm_device *dev, u32 status_mask)
 	if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV)
 	if (status_mask & SPRITE1_FLIP_DONE_INT_STATUS_VLV)
 		enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV;
 		enable_mask |= SPRITE1_FLIP_DONE_INT_EN_VLV;
 
 
+out:
+	WARN_ONCE(enable_mask & ~PIPESTAT_INT_ENABLE_MASK ||
+		  status_mask & ~PIPESTAT_INT_STATUS_MASK,
+		  "pipe %c: enable_mask=0x%x, status_mask=0x%x\n",
+		  pipe_name(pipe), enable_mask, status_mask);
+
 	return enable_mask;
 	return enable_mask;
 }
 }
 
 
-void
-i915_enable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
-		     u32 status_mask)
+void i915_enable_pipestat(struct drm_i915_private *dev_priv,
+			  enum pipe pipe, u32 status_mask)
 {
 {
+	i915_reg_t reg = PIPESTAT(pipe);
 	u32 enable_mask;
 	u32 enable_mask;
 
 
-	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
-		enable_mask = vlv_get_pipestat_enable_mask(&dev_priv->drm,
-							   status_mask);
-	else
-		enable_mask = status_mask << 16;
-	__i915_enable_pipestat(dev_priv, pipe, enable_mask, status_mask);
+	WARN_ONCE(status_mask & ~PIPESTAT_INT_STATUS_MASK,
+		  "pipe %c: status_mask=0x%x\n",
+		  pipe_name(pipe), status_mask);
+
+	lockdep_assert_held(&dev_priv->irq_lock);
+	WARN_ON(!intel_irqs_enabled(dev_priv));
+
+	if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == status_mask)
+		return;
+
+	dev_priv->pipestat_irq_mask[pipe] |= status_mask;
+	enable_mask = i915_pipestat_enable_mask(dev_priv, pipe);
+
+	I915_WRITE(reg, enable_mask | status_mask);
+	POSTING_READ(reg);
 }
 }
 
 
-void
-i915_disable_pipestat(struct drm_i915_private *dev_priv, enum pipe pipe,
-		      u32 status_mask)
+void i915_disable_pipestat(struct drm_i915_private *dev_priv,
+			   enum pipe pipe, u32 status_mask)
 {
 {
+	i915_reg_t reg = PIPESTAT(pipe);
 	u32 enable_mask;
 	u32 enable_mask;
 
 
-	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
-		enable_mask = vlv_get_pipestat_enable_mask(&dev_priv->drm,
-							   status_mask);
-	else
-		enable_mask = status_mask << 16;
-	__i915_disable_pipestat(dev_priv, pipe, enable_mask, status_mask);
+	WARN_ONCE(status_mask & ~PIPESTAT_INT_STATUS_MASK,
+		  "pipe %c: status_mask=0x%x\n",
+		  pipe_name(pipe), status_mask);
+
+	lockdep_assert_held(&dev_priv->irq_lock);
+	WARN_ON(!intel_irqs_enabled(dev_priv));
+
+	if ((dev_priv->pipestat_irq_mask[pipe] & status_mask) == 0)
+		return;
+
+	dev_priv->pipestat_irq_mask[pipe] &= ~status_mask;
+	enable_mask = i915_pipestat_enable_mask(dev_priv, pipe);
+
+	I915_WRITE(reg, enable_mask | status_mask);
+	POSTING_READ(reg);
 }
 }
 
 
 /**
 /**
@@ -772,6 +783,57 @@ static u32 g4x_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
 	return I915_READ(PIPE_FRMCOUNT_G4X(pipe));
 	return I915_READ(PIPE_FRMCOUNT_G4X(pipe));
 }
 }
 
 
+/*
+ * On certain encoders on certain platforms, pipe
+ * scanline register will not work to get the scanline,
+ * since the timings are driven from the PORT or issues
+ * with scanline register updates.
+ * This function will use Framestamp and current
+ * timestamp registers to calculate the scanline.
+ */
+static u32 __intel_get_crtc_scanline_from_timestamp(struct intel_crtc *crtc)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	struct drm_vblank_crtc *vblank =
+		&crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
+	const struct drm_display_mode *mode = &vblank->hwmode;
+	u32 vblank_start = mode->crtc_vblank_start;
+	u32 vtotal = mode->crtc_vtotal;
+	u32 htotal = mode->crtc_htotal;
+	u32 clock = mode->crtc_clock;
+	u32 scanline, scan_prev_time, scan_curr_time, scan_post_time;
+
+	/*
+	 * To avoid the race condition where we might cross into the
+	 * next vblank just between the PIPE_FRMTMSTMP and TIMESTAMP_CTR
+	 * reads. We make sure we read PIPE_FRMTMSTMP and TIMESTAMP_CTR
+	 * during the same frame.
+	 */
+	do {
+		/*
+		 * This field provides read back of the display
+		 * pipe frame time stamp. The time stamp value
+		 * is sampled at every start of vertical blank.
+		 */
+		scan_prev_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe));
+
+		/*
+		 * The TIMESTAMP_CTR register has the current
+		 * time stamp value.
+		 */
+		scan_curr_time = I915_READ_FW(IVB_TIMESTAMP_CTR);
+
+		scan_post_time = I915_READ_FW(PIPE_FRMTMSTMP(crtc->pipe));
+	} while (scan_post_time != scan_prev_time);
+
+	scanline = div_u64(mul_u32_u32(scan_curr_time - scan_prev_time,
+					clock), 1000 * htotal);
+	scanline = min(scanline, vtotal - 1);
+	scanline = (scanline + vblank_start) % vtotal;
+
+	return scanline;
+}
+
 /* I915_READ_FW, only for fast reads of display block, no need for forcewake etc. */
 /* I915_READ_FW, only for fast reads of display block, no need for forcewake etc. */
 static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 {
 {
@@ -788,6 +850,9 @@ static int __intel_get_crtc_scanline(struct intel_crtc *crtc)
 	vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
 	vblank = &crtc->base.dev->vblank[drm_crtc_index(&crtc->base)];
 	mode = &vblank->hwmode;
 	mode = &vblank->hwmode;
 
 
+	if (mode->private_flags & I915_MODE_FLAG_GET_SCANLINE_FROM_TIMESTAMP)
+		return __intel_get_crtc_scanline_from_timestamp(crtc);
+
 	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;
@@ -1005,6 +1070,8 @@ static void notify_ring(struct intel_engine_cs *engine)
 	spin_lock(&engine->breadcrumbs.irq_lock);
 	spin_lock(&engine->breadcrumbs.irq_lock);
 	wait = engine->breadcrumbs.irq_wait;
 	wait = engine->breadcrumbs.irq_wait;
 	if (wait) {
 	if (wait) {
+		bool wakeup = engine->irq_seqno_barrier;
+
 		/* We use a callback from the dma-fence to submit
 		/* We use a callback from the dma-fence to submit
 		 * requests after waiting on our own requests. To
 		 * requests after waiting on our own requests. To
 		 * ensure minimum delay in queuing the next request to
 		 * ensure minimum delay in queuing the next request to
@@ -1017,12 +1084,18 @@ static void notify_ring(struct intel_engine_cs *engine)
 		 * and many waiters.
 		 * and many waiters.
 		 */
 		 */
 		if (i915_seqno_passed(intel_engine_get_seqno(engine),
 		if (i915_seqno_passed(intel_engine_get_seqno(engine),
-				      wait->seqno) &&
-		    !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
-			      &wait->request->fence.flags))
-			rq = i915_gem_request_get(wait->request);
+				      wait->seqno)) {
+			struct drm_i915_gem_request *waiter = wait->request;
+
+			wakeup = true;
+			if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT,
+				      &waiter->fence.flags) &&
+			    intel_wait_check_request(wait, waiter))
+				rq = i915_gem_request_get(waiter);
+		}
 
 
-		wake_up_process(wait->tsk);
+		if (wakeup)
+			wake_up_process(wait->tsk);
 	} else {
 	} else {
 		__intel_engine_disarm_breadcrumbs(engine);
 		__intel_engine_disarm_breadcrumbs(engine);
 	}
 	}
@@ -1305,10 +1378,11 @@ static void snb_gt_irq_handler(struct drm_i915_private *dev_priv,
 static void
 static void
 gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
 gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
 {
 {
+	struct intel_engine_execlists * const execlists = &engine->execlists;
 	bool tasklet = false;
 	bool tasklet = false;
 
 
 	if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
 	if (iir & (GT_CONTEXT_SWITCH_INTERRUPT << test_shift)) {
-		if (port_count(&engine->execlist_port[0])) {
+		if (port_count(&execlists->port[0])) {
 			__set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
 			__set_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted);
 			tasklet = true;
 			tasklet = true;
 		}
 		}
@@ -1316,11 +1390,11 @@ gen8_cs_irq_handler(struct intel_engine_cs *engine, u32 iir, int test_shift)
 
 
 	if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
 	if (iir & (GT_RENDER_USER_INTERRUPT << test_shift)) {
 		notify_ring(engine);
 		notify_ring(engine);
-		tasklet |= i915.enable_guc_submission;
+		tasklet |= i915_modparams.enable_guc_submission;
 	}
 	}
 
 
 	if (tasklet)
 	if (tasklet)
-		tasklet_hi_schedule(&engine->irq_tasklet);
+		tasklet_hi_schedule(&execlists->irq_tasklet);
 }
 }
 
 
 static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv,
 static irqreturn_t gen8_gt_irq_ack(struct drm_i915_private *dev_priv,
@@ -1573,11 +1647,11 @@ static void display_pipe_crc_irq_handler(struct drm_i915_private *dev_priv,
 		 * bonkers. So let's just wait for the next vblank and read
 		 * bonkers. So let's just wait for the next vblank and read
 		 * out the buggy result.
 		 * out the buggy result.
 		 *
 		 *
-		 * On CHV sometimes the second CRC is bonkers as well, so
+		 * On GEN8+ sometimes the second CRC is bonkers as well, so
 		 * don't trust that one either.
 		 * don't trust that one either.
 		 */
 		 */
 		if (pipe_crc->skipped == 0 ||
 		if (pipe_crc->skipped == 0 ||
-		    (IS_CHERRYVIEW(dev_priv) && pipe_crc->skipped == 1)) {
+		    (INTEL_GEN(dev_priv) >= 8 && pipe_crc->skipped == 1)) {
 			pipe_crc->skipped++;
 			pipe_crc->skipped++;
 			spin_unlock(&pipe_crc->lock);
 			spin_unlock(&pipe_crc->lock);
 			return;
 			return;
@@ -1706,8 +1780,21 @@ static void gen9_guc_irq_handler(struct drm_i915_private *dev_priv, u32 gt_iir)
 	}
 	}
 }
 }
 
 
-static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv,
-					u32 iir, u32 pipe_stats[I915_MAX_PIPES])
+static void i9xx_pipestat_irq_reset(struct drm_i915_private *dev_priv)
+{
+	enum pipe pipe;
+
+	for_each_pipe(dev_priv, pipe) {
+		I915_WRITE(PIPESTAT(pipe),
+			   PIPESTAT_INT_STATUS_MASK |
+			   PIPE_FIFO_UNDERRUN_STATUS);
+
+		dev_priv->pipestat_irq_mask[pipe] = 0;
+	}
+}
+
+static void i9xx_pipestat_irq_ack(struct drm_i915_private *dev_priv,
+				  u32 iir, u32 pipe_stats[I915_MAX_PIPES])
 {
 {
 	int pipe;
 	int pipe;
 
 
@@ -1720,7 +1807,7 @@ static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv,
 
 
 	for_each_pipe(dev_priv, pipe) {
 	for_each_pipe(dev_priv, pipe) {
 		i915_reg_t reg;
 		i915_reg_t reg;
-		u32 mask, iir_bit = 0;
+		u32 status_mask, enable_mask, iir_bit = 0;
 
 
 		/*
 		/*
 		 * PIPESTAT bits get signalled even when the interrupt is
 		 * PIPESTAT bits get signalled even when the interrupt is
@@ -1731,7 +1818,7 @@ static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv,
 		 */
 		 */
 
 
 		/* fifo underruns are filterered in the underrun handler. */
 		/* fifo underruns are filterered in the underrun handler. */
-		mask = PIPE_FIFO_UNDERRUN_STATUS;
+		status_mask = PIPE_FIFO_UNDERRUN_STATUS;
 
 
 		switch (pipe) {
 		switch (pipe) {
 		case PIPE_A:
 		case PIPE_A:
@@ -1745,25 +1832,92 @@ static void valleyview_pipestat_irq_ack(struct drm_i915_private *dev_priv,
 			break;
 			break;
 		}
 		}
 		if (iir & iir_bit)
 		if (iir & iir_bit)
-			mask |= dev_priv->pipestat_irq_mask[pipe];
+			status_mask |= dev_priv->pipestat_irq_mask[pipe];
 
 
-		if (!mask)
+		if (!status_mask)
 			continue;
 			continue;
 
 
 		reg = PIPESTAT(pipe);
 		reg = PIPESTAT(pipe);
-		mask |= PIPESTAT_INT_ENABLE_MASK;
-		pipe_stats[pipe] = I915_READ(reg) & mask;
+		pipe_stats[pipe] = I915_READ(reg) & status_mask;
+		enable_mask = i915_pipestat_enable_mask(dev_priv, pipe);
 
 
 		/*
 		/*
 		 * Clear the PIPE*STAT regs before the IIR
 		 * Clear the PIPE*STAT regs before the IIR
 		 */
 		 */
-		if (pipe_stats[pipe] & (PIPE_FIFO_UNDERRUN_STATUS |
-					PIPESTAT_INT_STATUS_MASK))
-			I915_WRITE(reg, pipe_stats[pipe]);
+		if (pipe_stats[pipe])
+			I915_WRITE(reg, enable_mask | pipe_stats[pipe]);
 	}
 	}
 	spin_unlock(&dev_priv->irq_lock);
 	spin_unlock(&dev_priv->irq_lock);
 }
 }
 
 
+static void i8xx_pipestat_irq_handler(struct drm_i915_private *dev_priv,
+				      u16 iir, u32 pipe_stats[I915_MAX_PIPES])
+{
+	enum pipe pipe;
+
+	for_each_pipe(dev_priv, pipe) {
+		if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
+			drm_handle_vblank(&dev_priv->drm, pipe);
+
+		if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+			i9xx_pipe_crc_irq_handler(dev_priv, pipe);
+
+		if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
+			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
+	}
+}
+
+static void i915_pipestat_irq_handler(struct drm_i915_private *dev_priv,
+				      u32 iir, u32 pipe_stats[I915_MAX_PIPES])
+{
+	bool blc_event = false;
+	enum pipe pipe;
+
+	for_each_pipe(dev_priv, pipe) {
+		if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
+			drm_handle_vblank(&dev_priv->drm, pipe);
+
+		if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
+			blc_event = true;
+
+		if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+			i9xx_pipe_crc_irq_handler(dev_priv, pipe);
+
+		if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
+			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
+	}
+
+	if (blc_event || (iir & I915_ASLE_INTERRUPT))
+		intel_opregion_asle_intr(dev_priv);
+}
+
+static void i965_pipestat_irq_handler(struct drm_i915_private *dev_priv,
+				      u32 iir, u32 pipe_stats[I915_MAX_PIPES])
+{
+	bool blc_event = false;
+	enum pipe pipe;
+
+	for_each_pipe(dev_priv, pipe) {
+		if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
+			drm_handle_vblank(&dev_priv->drm, pipe);
+
+		if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
+			blc_event = true;
+
+		if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
+			i9xx_pipe_crc_irq_handler(dev_priv, pipe);
+
+		if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
+			intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
+	}
+
+	if (blc_event || (iir & I915_ASLE_INTERRUPT))
+		intel_opregion_asle_intr(dev_priv);
+
+	if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
+		gmbus_irq_handler(dev_priv);
+}
+
 static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv,
 static void valleyview_pipestat_irq_handler(struct drm_i915_private *dev_priv,
 					    u32 pipe_stats[I915_MAX_PIPES])
 					    u32 pipe_stats[I915_MAX_PIPES])
 {
 {
@@ -1879,7 +2033,7 @@ static irqreturn_t valleyview_irq_handler(int irq, void *arg)
 
 
 		/* Call regardless, as some status bits might not be
 		/* Call regardless, as some status bits might not be
 		 * signalled in iir */
 		 * signalled in iir */
-		valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats);
+		i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
 
 
 		if (iir & (I915_LPE_PIPE_A_INTERRUPT |
 		if (iir & (I915_LPE_PIPE_A_INTERRUPT |
 			   I915_LPE_PIPE_B_INTERRUPT))
 			   I915_LPE_PIPE_B_INTERRUPT))
@@ -1963,7 +2117,7 @@ static irqreturn_t cherryview_irq_handler(int irq, void *arg)
 
 
 		/* Call regardless, as some status bits might not be
 		/* Call regardless, as some status bits might not be
 		 * signalled in iir */
 		 * signalled in iir */
-		valleyview_pipestat_irq_ack(dev_priv, iir, pipe_stats);
+		i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
 
 
 		if (iir & (I915_LPE_PIPE_A_INTERRUPT |
 		if (iir & (I915_LPE_PIPE_A_INTERRUPT |
 			   I915_LPE_PIPE_B_INTERRUPT |
 			   I915_LPE_PIPE_B_INTERRUPT |
@@ -2860,7 +3014,7 @@ static void ibx_irq_reset(struct drm_i915_private *dev_priv)
 	if (HAS_PCH_NOP(dev_priv))
 	if (HAS_PCH_NOP(dev_priv))
 		return;
 		return;
 
 
-	GEN5_IRQ_RESET(SDE);
+	GEN3_IRQ_RESET(SDE);
 
 
 	if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv))
 	if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv))
 		I915_WRITE(SERR_INT, 0xffffffff);
 		I915_WRITE(SERR_INT, 0xffffffff);
@@ -2888,15 +3042,13 @@ static void ibx_irq_pre_postinstall(struct drm_device *dev)
 
 
 static void gen5_gt_irq_reset(struct drm_i915_private *dev_priv)
 static void gen5_gt_irq_reset(struct drm_i915_private *dev_priv)
 {
 {
-	GEN5_IRQ_RESET(GT);
+	GEN3_IRQ_RESET(GT);
 	if (INTEL_GEN(dev_priv) >= 6)
 	if (INTEL_GEN(dev_priv) >= 6)
-		GEN5_IRQ_RESET(GEN6_PM);
+		GEN3_IRQ_RESET(GEN6_PM);
 }
 }
 
 
 static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
 static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
 {
 {
-	enum pipe pipe;
-
 	if (IS_CHERRYVIEW(dev_priv))
 	if (IS_CHERRYVIEW(dev_priv))
 		I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
 		I915_WRITE(DPINVGTT, DPINVGTT_STATUS_MASK_CHV);
 	else
 	else
@@ -2905,14 +3057,9 @@ static void vlv_display_irq_reset(struct drm_i915_private *dev_priv)
 	i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0);
 	i915_hotplug_interrupt_update_locked(dev_priv, 0xffffffff, 0);
 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 
 
-	for_each_pipe(dev_priv, pipe) {
-		I915_WRITE(PIPESTAT(pipe),
-			   PIPE_FIFO_UNDERRUN_STATUS |
-			   PIPESTAT_INT_STATUS_MASK);
-		dev_priv->pipestat_irq_mask[pipe] = 0;
-	}
+	i9xx_pipestat_irq_reset(dev_priv);
 
 
-	GEN5_IRQ_RESET(VLV_);
+	GEN3_IRQ_RESET(VLV_);
 	dev_priv->irq_mask = ~0;
 	dev_priv->irq_mask = ~0;
 }
 }
 
 
@@ -2922,8 +3069,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
 	u32 enable_mask;
 	u32 enable_mask;
 	enum pipe pipe;
 	enum pipe pipe;
 
 
-	pipestat_mask = PLANE_FLIP_DONE_INT_STATUS_VLV |
-			PIPE_CRC_DONE_INTERRUPT_STATUS;
+	pipestat_mask = PIPE_CRC_DONE_INTERRUPT_STATUS;
 
 
 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
 	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
 	for_each_pipe(dev_priv, pipe)
 	for_each_pipe(dev_priv, pipe)
@@ -2943,7 +3089,7 @@ static void vlv_display_irq_postinstall(struct drm_i915_private *dev_priv)
 
 
 	dev_priv->irq_mask = ~enable_mask;
 	dev_priv->irq_mask = ~enable_mask;
 
 
-	GEN5_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask);
+	GEN3_IRQ_INIT(VLV_, dev_priv->irq_mask, enable_mask);
 }
 }
 
 
 /* drm_dma.h hooks
 /* drm_dma.h hooks
@@ -2952,9 +3098,10 @@ static void ironlake_irq_reset(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 
 
-	I915_WRITE(HWSTAM, 0xffffffff);
+	if (IS_GEN5(dev_priv))
+		I915_WRITE(HWSTAM, 0xffffffff);
 
 
-	GEN5_IRQ_RESET(DE);
+	GEN3_IRQ_RESET(DE);
 	if (IS_GEN7(dev_priv))
 	if (IS_GEN7(dev_priv))
 		I915_WRITE(GEN7_ERR_INT, 0xffffffff);
 		I915_WRITE(GEN7_ERR_INT, 0xffffffff);
 
 
@@ -2963,7 +3110,7 @@ static void ironlake_irq_reset(struct drm_device *dev)
 	ibx_irq_reset(dev_priv);
 	ibx_irq_reset(dev_priv);
 }
 }
 
 
-static void valleyview_irq_preinstall(struct drm_device *dev)
+static void valleyview_irq_reset(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 
 
@@ -3001,9 +3148,9 @@ static void gen8_irq_reset(struct drm_device *dev)
 						   POWER_DOMAIN_PIPE(pipe)))
 						   POWER_DOMAIN_PIPE(pipe)))
 			GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
 			GEN8_IRQ_RESET_NDX(DE_PIPE, pipe);
 
 
-	GEN5_IRQ_RESET(GEN8_DE_PORT_);
-	GEN5_IRQ_RESET(GEN8_DE_MISC_);
-	GEN5_IRQ_RESET(GEN8_PCU_);
+	GEN3_IRQ_RESET(GEN8_DE_PORT_);
+	GEN3_IRQ_RESET(GEN8_DE_MISC_);
+	GEN3_IRQ_RESET(GEN8_PCU_);
 
 
 	if (HAS_PCH_SPLIT(dev_priv))
 	if (HAS_PCH_SPLIT(dev_priv))
 		ibx_irq_reset(dev_priv);
 		ibx_irq_reset(dev_priv);
@@ -3037,7 +3184,7 @@ void gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv,
 	synchronize_irq(dev_priv->drm.irq);
 	synchronize_irq(dev_priv->drm.irq);
 }
 }
 
 
-static void cherryview_irq_preinstall(struct drm_device *dev)
+static void cherryview_irq_reset(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 
 
@@ -3046,7 +3193,7 @@ static void cherryview_irq_preinstall(struct drm_device *dev)
 
 
 	gen8_gt_irq_reset(dev_priv);
 	gen8_gt_irq_reset(dev_priv);
 
 
-	GEN5_IRQ_RESET(GEN8_PCU_);
+	GEN3_IRQ_RESET(GEN8_PCU_);
 
 
 	spin_lock_irq(&dev_priv->irq_lock);
 	spin_lock_irq(&dev_priv->irq_lock);
 	if (dev_priv->display_irqs_enabled)
 	if (dev_priv->display_irqs_enabled)
@@ -3111,7 +3258,15 @@ static void ibx_hpd_irq_setup(struct drm_i915_private *dev_priv)
 
 
 static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv)
 static void spt_hpd_detection_setup(struct drm_i915_private *dev_priv)
 {
 {
-	u32 hotplug;
+	u32 val, hotplug;
+
+	/* Display WA #1179 WaHardHangonHotPlug: cnp */
+	if (HAS_PCH_CNP(dev_priv)) {
+		val = I915_READ(SOUTH_CHICKEN1);
+		val &= ~CHASSIS_CLK_REQ_DURATION_MASK;
+		val |= CHASSIS_CLK_REQ_DURATION(0xf);
+		I915_WRITE(SOUTH_CHICKEN1, val);
+	}
 
 
 	/* Enable digital hotplug on the PCH */
 	/* Enable digital hotplug on the PCH */
 	hotplug = I915_READ(PCH_PORT_HOTPLUG);
 	hotplug = I915_READ(PCH_PORT_HOTPLUG);
@@ -3238,10 +3393,12 @@ static void ibx_irq_postinstall(struct drm_device *dev)
 
 
 	if (HAS_PCH_IBX(dev_priv))
 	if (HAS_PCH_IBX(dev_priv))
 		mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON;
 		mask = SDE_GMBUS | SDE_AUX_MASK | SDE_POISON;
-	else
+	else if (HAS_PCH_CPT(dev_priv) || HAS_PCH_LPT(dev_priv))
 		mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
 		mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
+	else
+		mask = SDE_GMBUS_CPT;
 
 
-	gen5_assert_iir_is_zero(dev_priv, SDEIIR);
+	gen3_assert_iir_is_zero(dev_priv, SDEIIR);
 	I915_WRITE(SDEIMR, ~mask);
 	I915_WRITE(SDEIMR, ~mask);
 
 
 	if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) ||
 	if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) ||
@@ -3272,7 +3429,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
 		gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
 		gt_irqs |= GT_BLT_USER_INTERRUPT | GT_BSD_USER_INTERRUPT;
 	}
 	}
 
 
-	GEN5_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs);
+	GEN3_IRQ_INIT(GT, dev_priv->gt_irq_mask, gt_irqs);
 
 
 	if (INTEL_GEN(dev_priv) >= 6) {
 	if (INTEL_GEN(dev_priv) >= 6) {
 		/*
 		/*
@@ -3285,7 +3442,7 @@ static void gen5_gt_irq_postinstall(struct drm_device *dev)
 		}
 		}
 
 
 		dev_priv->pm_imr = 0xffffffff;
 		dev_priv->pm_imr = 0xffffffff;
-		GEN5_IRQ_INIT(GEN6_PM, dev_priv->pm_imr, pm_irqs);
+		GEN3_IRQ_INIT(GEN6_PM, dev_priv->pm_imr, pm_irqs);
 	}
 	}
 }
 }
 
 
@@ -3296,18 +3453,14 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
 
 
 	if (INTEL_GEN(dev_priv) >= 7) {
 	if (INTEL_GEN(dev_priv) >= 7) {
 		display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
 		display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
-				DE_PCH_EVENT_IVB | DE_PLANEC_FLIP_DONE_IVB |
-				DE_PLANEB_FLIP_DONE_IVB |
-				DE_PLANEA_FLIP_DONE_IVB | DE_AUX_CHANNEL_A_IVB);
+				DE_PCH_EVENT_IVB | DE_AUX_CHANNEL_A_IVB);
 		extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
 		extra_mask = (DE_PIPEC_VBLANK_IVB | DE_PIPEB_VBLANK_IVB |
 			      DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB |
 			      DE_PIPEA_VBLANK_IVB | DE_ERR_INT_IVB |
 			      DE_DP_A_HOTPLUG_IVB);
 			      DE_DP_A_HOTPLUG_IVB);
 	} else {
 	} else {
 		display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
 		display_mask = (DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
-				DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE |
-				DE_AUX_CHANNEL_A |
-				DE_PIPEB_CRC_DONE | DE_PIPEA_CRC_DONE |
-				DE_POISON);
+				DE_AUX_CHANNEL_A | DE_PIPEB_CRC_DONE |
+				DE_PIPEA_CRC_DONE | DE_POISON);
 		extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT |
 		extra_mask = (DE_PIPEA_VBLANK | DE_PIPEB_VBLANK | DE_PCU_EVENT |
 			      DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
 			      DE_PIPEB_FIFO_UNDERRUN | DE_PIPEA_FIFO_UNDERRUN |
 			      DE_DP_A_HOTPLUG);
 			      DE_DP_A_HOTPLUG);
@@ -3315,11 +3468,9 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
 
 
 	dev_priv->irq_mask = ~display_mask;
 	dev_priv->irq_mask = ~display_mask;
 
 
-	I915_WRITE(HWSTAM, 0xeffe);
-
 	ibx_irq_pre_postinstall(dev);
 	ibx_irq_pre_postinstall(dev);
 
 
-	GEN5_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask);
+	GEN3_IRQ_INIT(DE, dev_priv->irq_mask, display_mask | extra_mask);
 
 
 	gen5_gt_irq_postinstall(dev);
 	gen5_gt_irq_postinstall(dev);
 
 
@@ -3429,15 +3580,13 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
 	enum pipe pipe;
 	enum pipe pipe;
 
 
 	if (INTEL_GEN(dev_priv) >= 9) {
 	if (INTEL_GEN(dev_priv) >= 9) {
-		de_pipe_masked |= GEN9_PIPE_PLANE1_FLIP_DONE |
-				  GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
+		de_pipe_masked |= GEN9_DE_PIPE_IRQ_FAULT_ERRORS;
 		de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
 		de_port_masked |= GEN9_AUX_CHANNEL_B | GEN9_AUX_CHANNEL_C |
 				  GEN9_AUX_CHANNEL_D;
 				  GEN9_AUX_CHANNEL_D;
 		if (IS_GEN9_LP(dev_priv))
 		if (IS_GEN9_LP(dev_priv))
 			de_port_masked |= BXT_DE_PORT_GMBUS;
 			de_port_masked |= BXT_DE_PORT_GMBUS;
 	} else {
 	} else {
-		de_pipe_masked |= GEN8_PIPE_PRIMARY_FLIP_DONE |
-				  GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
+		de_pipe_masked |= GEN8_DE_PIPE_IRQ_FAULT_ERRORS;
 	}
 	}
 
 
 	de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
 	de_pipe_enables = de_pipe_masked | GEN8_PIPE_VBLANK |
@@ -3460,8 +3609,8 @@ static void gen8_de_irq_postinstall(struct drm_i915_private *dev_priv)
 					  dev_priv->de_irq_mask[pipe],
 					  dev_priv->de_irq_mask[pipe],
 					  de_pipe_enables);
 					  de_pipe_enables);
 
 
-	GEN5_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables);
-	GEN5_IRQ_INIT(GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked);
+	GEN3_IRQ_INIT(GEN8_DE_PORT_, ~de_port_masked, de_port_enables);
+	GEN3_IRQ_INIT(GEN8_DE_MISC_, ~de_misc_masked, de_misc_masked);
 
 
 	if (IS_GEN9_LP(dev_priv))
 	if (IS_GEN9_LP(dev_priv))
 		bxt_hpd_detection_setup(dev_priv);
 		bxt_hpd_detection_setup(dev_priv);
@@ -3505,98 +3654,36 @@ static int cherryview_irq_postinstall(struct drm_device *dev)
 	return 0;
 	return 0;
 }
 }
 
 
-static void gen8_irq_uninstall(struct drm_device *dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-
-	if (!dev_priv)
-		return;
-
-	gen8_irq_reset(dev);
-}
-
-static void valleyview_irq_uninstall(struct drm_device *dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-
-	if (!dev_priv)
-		return;
-
-	I915_WRITE(VLV_MASTER_IER, 0);
-	POSTING_READ(VLV_MASTER_IER);
-
-	gen5_gt_irq_reset(dev_priv);
-
-	I915_WRITE(HWSTAM, 0xffffffff);
-
-	spin_lock_irq(&dev_priv->irq_lock);
-	if (dev_priv->display_irqs_enabled)
-		vlv_display_irq_reset(dev_priv);
-	spin_unlock_irq(&dev_priv->irq_lock);
-}
-
-static void cherryview_irq_uninstall(struct drm_device *dev)
+static void i8xx_irq_reset(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 
 
-	if (!dev_priv)
-		return;
-
-	I915_WRITE(GEN8_MASTER_IRQ, 0);
-	POSTING_READ(GEN8_MASTER_IRQ);
-
-	gen8_gt_irq_reset(dev_priv);
-
-	GEN5_IRQ_RESET(GEN8_PCU_);
-
-	spin_lock_irq(&dev_priv->irq_lock);
-	if (dev_priv->display_irqs_enabled)
-		vlv_display_irq_reset(dev_priv);
-	spin_unlock_irq(&dev_priv->irq_lock);
-}
-
-static void ironlake_irq_uninstall(struct drm_device *dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
+	i9xx_pipestat_irq_reset(dev_priv);
 
 
-	if (!dev_priv)
-		return;
-
-	ironlake_irq_reset(dev);
-}
-
-static void i8xx_irq_preinstall(struct drm_device * dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-	int pipe;
+	I915_WRITE16(HWSTAM, 0xffff);
 
 
-	for_each_pipe(dev_priv, pipe)
-		I915_WRITE(PIPESTAT(pipe), 0);
-	I915_WRITE16(IMR, 0xffff);
-	I915_WRITE16(IER, 0x0);
-	POSTING_READ16(IER);
+	GEN2_IRQ_RESET();
 }
 }
 
 
 static int i8xx_irq_postinstall(struct drm_device *dev)
 static int i8xx_irq_postinstall(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
+	u16 enable_mask;
 
 
-	I915_WRITE16(EMR,
-		     ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
+	I915_WRITE16(EMR, ~(I915_ERROR_PAGE_TABLE |
+			    I915_ERROR_MEMORY_REFRESH));
 
 
 	/* Unmask the interrupts that we always want on. */
 	/* Unmask the interrupts that we always want on. */
 	dev_priv->irq_mask =
 	dev_priv->irq_mask =
 		~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
 		~(I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
-		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
-		  I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
-		  I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
-	I915_WRITE16(IMR, dev_priv->irq_mask);
+		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT);
+
+	enable_mask =
+		I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+		I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+		I915_USER_INTERRUPT;
 
 
-	I915_WRITE16(IER,
-		     I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
-		     I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
-		     I915_USER_INTERRUPT);
-	POSTING_READ16(IER);
+	GEN2_IRQ_INIT(, dev_priv->irq_mask, enable_mask);
 
 
 	/* Interrupt setup is already guaranteed to be single-threaded, this is
 	/* Interrupt setup is already guaranteed to be single-threaded, this is
 	 * just to make the assert_spin_locked check happy. */
 	 * just to make the assert_spin_locked check happy. */
@@ -3608,17 +3695,11 @@ static int i8xx_irq_postinstall(struct drm_device *dev)
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- * Returns true when a page flip has completed.
- */
 static irqreturn_t i8xx_irq_handler(int irq, void *arg)
 static irqreturn_t i8xx_irq_handler(int irq, void *arg)
 {
 {
 	struct drm_device *dev = arg;
 	struct drm_device *dev = arg;
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	u16 iir, new_iir;
-	u32 pipe_stats[2];
-	int pipe;
-	irqreturn_t ret;
+	irqreturn_t ret = IRQ_NONE;
 
 
 	if (!intel_irqs_enabled(dev_priv))
 	if (!intel_irqs_enabled(dev_priv))
 		return IRQ_NONE;
 		return IRQ_NONE;
@@ -3626,96 +3707,50 @@ static irqreturn_t i8xx_irq_handler(int irq, void *arg)
 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
 	disable_rpm_wakeref_asserts(dev_priv);
 	disable_rpm_wakeref_asserts(dev_priv);
 
 
-	ret = IRQ_NONE;
-	iir = I915_READ16(IIR);
-	if (iir == 0)
-		goto out;
+	do {
+		u32 pipe_stats[I915_MAX_PIPES] = {};
+		u16 iir;
 
 
-	while (iir) {
-		/* Can't rely on pipestat interrupt bit in iir as it might
-		 * have been cleared after the pipestat interrupt was received.
-		 * It doesn't set the bit in iir again, but it still produces
-		 * interrupts (for non-MSI).
-		 */
-		spin_lock(&dev_priv->irq_lock);
-		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
-			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
+		iir = I915_READ16(IIR);
+		if (iir == 0)
+			break;
 
 
-		for_each_pipe(dev_priv, pipe) {
-			i915_reg_t reg = PIPESTAT(pipe);
-			pipe_stats[pipe] = I915_READ(reg);
+		ret = IRQ_HANDLED;
 
 
-			/*
-			 * Clear the PIPE*STAT regs before the IIR
-			 */
-			if (pipe_stats[pipe] & 0x8000ffff)
-				I915_WRITE(reg, pipe_stats[pipe]);
-		}
-		spin_unlock(&dev_priv->irq_lock);
+		/* Call regardless, as some status bits might not be
+		 * signalled in iir */
+		i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
 
 
 		I915_WRITE16(IIR, iir);
 		I915_WRITE16(IIR, iir);
-		new_iir = I915_READ16(IIR); /* Flush posted writes */
 
 
 		if (iir & I915_USER_INTERRUPT)
 		if (iir & I915_USER_INTERRUPT)
 			notify_ring(dev_priv->engine[RCS]);
 			notify_ring(dev_priv->engine[RCS]);
 
 
-		for_each_pipe(dev_priv, pipe) {
-			int plane = pipe;
-			if (HAS_FBC(dev_priv))
-				plane = !plane;
-
-			if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
-				drm_handle_vblank(&dev_priv->drm, pipe);
-
-			if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
-				i9xx_pipe_crc_irq_handler(dev_priv, pipe);
-
-			if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
-				intel_cpu_fifo_underrun_irq_handler(dev_priv,
-								    pipe);
-		}
+		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
 
 
-		iir = new_iir;
-	}
-	ret = IRQ_HANDLED;
+		i8xx_pipestat_irq_handler(dev_priv, iir, pipe_stats);
+	} while (0);
 
 
-out:
 	enable_rpm_wakeref_asserts(dev_priv);
 	enable_rpm_wakeref_asserts(dev_priv);
 
 
 	return ret;
 	return ret;
 }
 }
 
 
-static void i8xx_irq_uninstall(struct drm_device * dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-	int pipe;
-
-	for_each_pipe(dev_priv, pipe) {
-		/* Clear enable bits; then clear status bits */
-		I915_WRITE(PIPESTAT(pipe), 0);
-		I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
-	}
-	I915_WRITE16(IMR, 0xffff);
-	I915_WRITE16(IER, 0x0);
-	I915_WRITE16(IIR, I915_READ16(IIR));
-}
-
-static void i915_irq_preinstall(struct drm_device * dev)
+static void i915_irq_reset(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	int pipe;
 
 
 	if (I915_HAS_HOTPLUG(dev_priv)) {
 	if (I915_HAS_HOTPLUG(dev_priv)) {
 		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
 		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
 		I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 		I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 	}
 	}
 
 
-	I915_WRITE16(HWSTAM, 0xeffe);
-	for_each_pipe(dev_priv, pipe)
-		I915_WRITE(PIPESTAT(pipe), 0);
-	I915_WRITE(IMR, 0xffffffff);
-	I915_WRITE(IER, 0x0);
-	POSTING_READ(IER);
+	i9xx_pipestat_irq_reset(dev_priv);
+
+	I915_WRITE(HWSTAM, 0xffffffff);
+
+	GEN3_IRQ_RESET();
 }
 }
 
 
 static int i915_irq_postinstall(struct drm_device *dev)
 static int i915_irq_postinstall(struct drm_device *dev)
@@ -3723,15 +3758,14 @@ static int i915_irq_postinstall(struct drm_device *dev)
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	u32 enable_mask;
 	u32 enable_mask;
 
 
-	I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE | I915_ERROR_MEMORY_REFRESH));
+	I915_WRITE(EMR, ~(I915_ERROR_PAGE_TABLE |
+			  I915_ERROR_MEMORY_REFRESH));
 
 
 	/* Unmask the interrupts that we always want on. */
 	/* Unmask the interrupts that we always want on. */
 	dev_priv->irq_mask =
 	dev_priv->irq_mask =
 		~(I915_ASLE_INTERRUPT |
 		~(I915_ASLE_INTERRUPT |
 		  I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
 		  I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
-		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
-		  I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
-		  I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
+		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT);
 
 
 	enable_mask =
 	enable_mask =
 		I915_ASLE_INTERRUPT |
 		I915_ASLE_INTERRUPT |
@@ -3740,20 +3774,13 @@ static int i915_irq_postinstall(struct drm_device *dev)
 		I915_USER_INTERRUPT;
 		I915_USER_INTERRUPT;
 
 
 	if (I915_HAS_HOTPLUG(dev_priv)) {
 	if (I915_HAS_HOTPLUG(dev_priv)) {
-		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
-		POSTING_READ(PORT_HOTPLUG_EN);
-
 		/* Enable in IER... */
 		/* Enable in IER... */
 		enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
 		enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
 		/* and unmask in IMR */
 		/* and unmask in IMR */
 		dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT;
 		dev_priv->irq_mask &= ~I915_DISPLAY_PORT_INTERRUPT;
 	}
 	}
 
 
-	I915_WRITE(IMR, dev_priv->irq_mask);
-	I915_WRITE(IER, enable_mask);
-	POSTING_READ(IER);
-
-	i915_enable_asle_pipestat(dev_priv);
+	GEN3_IRQ_INIT(, dev_priv->irq_mask, enable_mask);
 
 
 	/* Interrupt setup is already guaranteed to be single-threaded, this is
 	/* Interrupt setup is already guaranteed to be single-threaded, this is
 	 * just to make the assert_spin_locked check happy. */
 	 * just to make the assert_spin_locked check happy. */
@@ -3762,6 +3789,8 @@ static int i915_irq_postinstall(struct drm_device *dev)
 	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
 	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
 	spin_unlock_irq(&dev_priv->irq_lock);
 	spin_unlock_irq(&dev_priv->irq_lock);
 
 
+	i915_enable_asle_pipestat(dev_priv);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3769,8 +3798,7 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 {
 {
 	struct drm_device *dev = arg;
 	struct drm_device *dev = arg;
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	u32 iir, new_iir, pipe_stats[I915_MAX_PIPES];
-	int pipe, ret = IRQ_NONE;
+	irqreturn_t ret = IRQ_NONE;
 
 
 	if (!intel_irqs_enabled(dev_priv))
 	if (!intel_irqs_enabled(dev_priv))
 		return IRQ_NONE;
 		return IRQ_NONE;
@@ -3778,131 +3806,56 @@ static irqreturn_t i915_irq_handler(int irq, void *arg)
 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
 	disable_rpm_wakeref_asserts(dev_priv);
 	disable_rpm_wakeref_asserts(dev_priv);
 
 
-	iir = I915_READ(IIR);
 	do {
 	do {
-		bool irq_received = (iir) != 0;
-		bool blc_event = false;
-
-		/* Can't rely on pipestat interrupt bit in iir as it might
-		 * have been cleared after the pipestat interrupt was received.
-		 * It doesn't set the bit in iir again, but it still produces
-		 * interrupts (for non-MSI).
-		 */
-		spin_lock(&dev_priv->irq_lock);
-		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
-			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
-
-		for_each_pipe(dev_priv, pipe) {
-			i915_reg_t reg = PIPESTAT(pipe);
-			pipe_stats[pipe] = I915_READ(reg);
-
-			/* Clear the PIPE*STAT regs before the IIR */
-			if (pipe_stats[pipe] & 0x8000ffff) {
-				I915_WRITE(reg, pipe_stats[pipe]);
-				irq_received = true;
-			}
-		}
-		spin_unlock(&dev_priv->irq_lock);
+		u32 pipe_stats[I915_MAX_PIPES] = {};
+		u32 hotplug_status = 0;
+		u32 iir;
 
 
-		if (!irq_received)
+		iir = I915_READ(IIR);
+		if (iir == 0)
 			break;
 			break;
 
 
-		/* Consume port.  Then clear IIR or we'll miss events */
+		ret = IRQ_HANDLED;
+
 		if (I915_HAS_HOTPLUG(dev_priv) &&
 		if (I915_HAS_HOTPLUG(dev_priv) &&
-		    iir & I915_DISPLAY_PORT_INTERRUPT) {
-			u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
-			if (hotplug_status)
-				i9xx_hpd_irq_handler(dev_priv, hotplug_status);
-		}
+		    iir & I915_DISPLAY_PORT_INTERRUPT)
+			hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+
+		/* Call regardless, as some status bits might not be
+		 * signalled in iir */
+		i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
 
 
 		I915_WRITE(IIR, iir);
 		I915_WRITE(IIR, iir);
-		new_iir = I915_READ(IIR); /* Flush posted writes */
 
 
 		if (iir & I915_USER_INTERRUPT)
 		if (iir & I915_USER_INTERRUPT)
 			notify_ring(dev_priv->engine[RCS]);
 			notify_ring(dev_priv->engine[RCS]);
 
 
-		for_each_pipe(dev_priv, pipe) {
-			int plane = pipe;
-			if (HAS_FBC(dev_priv))
-				plane = !plane;
-
-			if (pipe_stats[pipe] & PIPE_VBLANK_INTERRUPT_STATUS)
-				drm_handle_vblank(&dev_priv->drm, pipe);
-
-			if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
-				blc_event = true;
-
-			if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
-				i9xx_pipe_crc_irq_handler(dev_priv, pipe);
-
-			if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
-				intel_cpu_fifo_underrun_irq_handler(dev_priv,
-								    pipe);
-		}
+		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
 
 
-		if (blc_event || (iir & I915_ASLE_INTERRUPT))
-			intel_opregion_asle_intr(dev_priv);
+		if (hotplug_status)
+			i9xx_hpd_irq_handler(dev_priv, hotplug_status);
 
 
-		/* With MSI, interrupts are only generated when iir
-		 * transitions from zero to nonzero.  If another bit got
-		 * set while we were handling the existing iir bits, then
-		 * we would never get another interrupt.
-		 *
-		 * This is fine on non-MSI as well, as if we hit this path
-		 * we avoid exiting the interrupt handler only to generate
-		 * another one.
-		 *
-		 * Note that for MSI this could cause a stray interrupt report
-		 * if an interrupt landed in the time between writing IIR and
-		 * the posting read.  This should be rare enough to never
-		 * trigger the 99% of 100,000 interrupts test for disabling
-		 * stray interrupts.
-		 */
-		ret = IRQ_HANDLED;
-		iir = new_iir;
-	} while (iir);
+		i915_pipestat_irq_handler(dev_priv, iir, pipe_stats);
+	} while (0);
 
 
 	enable_rpm_wakeref_asserts(dev_priv);
 	enable_rpm_wakeref_asserts(dev_priv);
 
 
 	return ret;
 	return ret;
 }
 }
 
 
-static void i915_irq_uninstall(struct drm_device * dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-	int pipe;
-
-	if (I915_HAS_HOTPLUG(dev_priv)) {
-		i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
-		I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
-	}
-
-	I915_WRITE16(HWSTAM, 0xffff);
-	for_each_pipe(dev_priv, pipe) {
-		/* Clear enable bits; then clear status bits */
-		I915_WRITE(PIPESTAT(pipe), 0);
-		I915_WRITE(PIPESTAT(pipe), I915_READ(PIPESTAT(pipe)));
-	}
-	I915_WRITE(IMR, 0xffffffff);
-	I915_WRITE(IER, 0x0);
-
-	I915_WRITE(IIR, I915_READ(IIR));
-}
-
-static void i965_irq_preinstall(struct drm_device * dev)
+static void i965_irq_reset(struct drm_device *dev)
 {
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	int pipe;
 
 
 	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
 	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 
 
-	I915_WRITE(HWSTAM, 0xeffe);
-	for_each_pipe(dev_priv, pipe)
-		I915_WRITE(PIPESTAT(pipe), 0);
-	I915_WRITE(IMR, 0xffffffff);
-	I915_WRITE(IER, 0x0);
-	POSTING_READ(IER);
+	i9xx_pipestat_irq_reset(dev_priv);
+
+	I915_WRITE(HWSTAM, 0xffffffff);
+
+	GEN3_IRQ_RESET();
 }
 }
 
 
 static int i965_irq_postinstall(struct drm_device *dev)
 static int i965_irq_postinstall(struct drm_device *dev)
@@ -3911,31 +3864,6 @@ static int i965_irq_postinstall(struct drm_device *dev)
 	u32 enable_mask;
 	u32 enable_mask;
 	u32 error_mask;
 	u32 error_mask;
 
 
-	/* Unmask the interrupts that we always want on. */
-	dev_priv->irq_mask = ~(I915_ASLE_INTERRUPT |
-			       I915_DISPLAY_PORT_INTERRUPT |
-			       I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
-			       I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
-			       I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
-			       I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT |
-			       I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
-
-	enable_mask = ~dev_priv->irq_mask;
-	enable_mask &= ~(I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT |
-			 I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT);
-	enable_mask |= I915_USER_INTERRUPT;
-
-	if (IS_G4X(dev_priv))
-		enable_mask |= I915_BSD_USER_INTERRUPT;
-
-	/* Interrupt setup is already guaranteed to be single-threaded, this is
-	 * just to make the assert_spin_locked check happy. */
-	spin_lock_irq(&dev_priv->irq_lock);
-	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
-	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
-	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
-	spin_unlock_irq(&dev_priv->irq_lock);
-
 	/*
 	/*
 	 * Enable some error detection, note the instruction error mask
 	 * Enable some error detection, note the instruction error mask
 	 * bit is reserved, so we leave it masked.
 	 * bit is reserved, so we leave it masked.
@@ -3951,12 +3879,34 @@ static int i965_irq_postinstall(struct drm_device *dev)
 	}
 	}
 	I915_WRITE(EMR, error_mask);
 	I915_WRITE(EMR, error_mask);
 
 
-	I915_WRITE(IMR, dev_priv->irq_mask);
-	I915_WRITE(IER, enable_mask);
-	POSTING_READ(IER);
+	/* Unmask the interrupts that we always want on. */
+	dev_priv->irq_mask =
+		~(I915_ASLE_INTERRUPT |
+		  I915_DISPLAY_PORT_INTERRUPT |
+		  I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+		  I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+		  I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
 
 
-	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
-	POSTING_READ(PORT_HOTPLUG_EN);
+	enable_mask =
+		I915_ASLE_INTERRUPT |
+		I915_DISPLAY_PORT_INTERRUPT |
+		I915_DISPLAY_PIPE_A_EVENT_INTERRUPT |
+		I915_DISPLAY_PIPE_B_EVENT_INTERRUPT |
+		I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT |
+		I915_USER_INTERRUPT;
+
+	if (IS_G4X(dev_priv))
+		enable_mask |= I915_BSD_USER_INTERRUPT;
+
+	GEN3_IRQ_INIT(, dev_priv->irq_mask, enable_mask);
+
+	/* Interrupt setup is already guaranteed to be single-threaded, this is
+	 * just to make the assert_spin_locked check happy. */
+	spin_lock_irq(&dev_priv->irq_lock);
+	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_GMBUS_INTERRUPT_STATUS);
+	i915_enable_pipestat(dev_priv, PIPE_A, PIPE_CRC_DONE_INTERRUPT_STATUS);
+	i915_enable_pipestat(dev_priv, PIPE_B, PIPE_CRC_DONE_INTERRUPT_STATUS);
+	spin_unlock_irq(&dev_priv->irq_lock);
 
 
 	i915_enable_asle_pipestat(dev_priv);
 	i915_enable_asle_pipestat(dev_priv);
 
 
@@ -3992,9 +3942,7 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
 {
 {
 	struct drm_device *dev = arg;
 	struct drm_device *dev = arg;
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	u32 iir, new_iir;
-	u32 pipe_stats[I915_MAX_PIPES];
-	int ret = IRQ_NONE, pipe;
+	irqreturn_t ret = IRQ_NONE;
 
 
 	if (!intel_irqs_enabled(dev_priv))
 	if (!intel_irqs_enabled(dev_priv))
 		return IRQ_NONE;
 		return IRQ_NONE;
@@ -4002,121 +3950,46 @@ static irqreturn_t i965_irq_handler(int irq, void *arg)
 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
 	/* IRQs are synced during runtime_suspend, we don't require a wakeref */
 	disable_rpm_wakeref_asserts(dev_priv);
 	disable_rpm_wakeref_asserts(dev_priv);
 
 
-	iir = I915_READ(IIR);
-
-	for (;;) {
-		bool irq_received = (iir) != 0;
-		bool blc_event = false;
-
-		/* Can't rely on pipestat interrupt bit in iir as it might
-		 * have been cleared after the pipestat interrupt was received.
-		 * It doesn't set the bit in iir again, but it still produces
-		 * interrupts (for non-MSI).
-		 */
-		spin_lock(&dev_priv->irq_lock);
-		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
-			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
-
-		for_each_pipe(dev_priv, pipe) {
-			i915_reg_t reg = PIPESTAT(pipe);
-			pipe_stats[pipe] = I915_READ(reg);
-
-			/*
-			 * Clear the PIPE*STAT regs before the IIR
-			 */
-			if (pipe_stats[pipe] & 0x8000ffff) {
-				I915_WRITE(reg, pipe_stats[pipe]);
-				irq_received = true;
-			}
-		}
-		spin_unlock(&dev_priv->irq_lock);
+	do {
+		u32 pipe_stats[I915_MAX_PIPES] = {};
+		u32 hotplug_status = 0;
+		u32 iir;
 
 
-		if (!irq_received)
+		iir = I915_READ(IIR);
+		if (iir == 0)
 			break;
 			break;
 
 
 		ret = IRQ_HANDLED;
 		ret = IRQ_HANDLED;
 
 
-		/* Consume port.  Then clear IIR or we'll miss events */
-		if (iir & I915_DISPLAY_PORT_INTERRUPT) {
-			u32 hotplug_status = i9xx_hpd_irq_ack(dev_priv);
-			if (hotplug_status)
-				i9xx_hpd_irq_handler(dev_priv, hotplug_status);
-		}
+		if (iir & I915_DISPLAY_PORT_INTERRUPT)
+			hotplug_status = i9xx_hpd_irq_ack(dev_priv);
+
+		/* Call regardless, as some status bits might not be
+		 * signalled in iir */
+		i9xx_pipestat_irq_ack(dev_priv, iir, pipe_stats);
 
 
 		I915_WRITE(IIR, iir);
 		I915_WRITE(IIR, iir);
-		new_iir = I915_READ(IIR); /* Flush posted writes */
 
 
 		if (iir & I915_USER_INTERRUPT)
 		if (iir & I915_USER_INTERRUPT)
 			notify_ring(dev_priv->engine[RCS]);
 			notify_ring(dev_priv->engine[RCS]);
+
 		if (iir & I915_BSD_USER_INTERRUPT)
 		if (iir & I915_BSD_USER_INTERRUPT)
 			notify_ring(dev_priv->engine[VCS]);
 			notify_ring(dev_priv->engine[VCS]);
 
 
-		for_each_pipe(dev_priv, pipe) {
-			if (pipe_stats[pipe] & PIPE_START_VBLANK_INTERRUPT_STATUS)
-				drm_handle_vblank(&dev_priv->drm, pipe);
-
-			if (pipe_stats[pipe] & PIPE_LEGACY_BLC_EVENT_STATUS)
-				blc_event = true;
-
-			if (pipe_stats[pipe] & PIPE_CRC_DONE_INTERRUPT_STATUS)
-				i9xx_pipe_crc_irq_handler(dev_priv, pipe);
-
-			if (pipe_stats[pipe] & PIPE_FIFO_UNDERRUN_STATUS)
-				intel_cpu_fifo_underrun_irq_handler(dev_priv, pipe);
-		}
-
-		if (blc_event || (iir & I915_ASLE_INTERRUPT))
-			intel_opregion_asle_intr(dev_priv);
+		if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+			DRM_DEBUG("Command parser error, iir 0x%08x\n", iir);
 
 
-		if (pipe_stats[0] & PIPE_GMBUS_INTERRUPT_STATUS)
-			gmbus_irq_handler(dev_priv);
+		if (hotplug_status)
+			i9xx_hpd_irq_handler(dev_priv, hotplug_status);
 
 
-		/* With MSI, interrupts are only generated when iir
-		 * transitions from zero to nonzero.  If another bit got
-		 * set while we were handling the existing iir bits, then
-		 * we would never get another interrupt.
-		 *
-		 * This is fine on non-MSI as well, as if we hit this path
-		 * we avoid exiting the interrupt handler only to generate
-		 * another one.
-		 *
-		 * Note that for MSI this could cause a stray interrupt report
-		 * if an interrupt landed in the time between writing IIR and
-		 * the posting read.  This should be rare enough to never
-		 * trigger the 99% of 100,000 interrupts test for disabling
-		 * stray interrupts.
-		 */
-		iir = new_iir;
-	}
+		i965_pipestat_irq_handler(dev_priv, iir, pipe_stats);
+	} while (0);
 
 
 	enable_rpm_wakeref_asserts(dev_priv);
 	enable_rpm_wakeref_asserts(dev_priv);
 
 
 	return ret;
 	return ret;
 }
 }
 
 
-static void i965_irq_uninstall(struct drm_device * dev)
-{
-	struct drm_i915_private *dev_priv = to_i915(dev);
-	int pipe;
-
-	if (!dev_priv)
-		return;
-
-	i915_hotplug_interrupt_update(dev_priv, 0xffffffff, 0);
-	I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
-
-	I915_WRITE(HWSTAM, 0xffffffff);
-	for_each_pipe(dev_priv, pipe)
-		I915_WRITE(PIPESTAT(pipe), 0);
-	I915_WRITE(IMR, 0xffffffff);
-	I915_WRITE(IER, 0x0);
-
-	for_each_pipe(dev_priv, pipe)
-		I915_WRITE(PIPESTAT(pipe),
-			   I915_READ(PIPESTAT(pipe)) & 0x8000ffff);
-	I915_WRITE(IIR, I915_READ(IIR));
-}
-
 /**
 /**
  * intel_irq_init - initializes irq support
  * intel_irq_init - initializes irq support
  * @dev_priv: i915 device instance
  * @dev_priv: i915 device instance
@@ -4197,17 +4070,17 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 
 
 	if (IS_CHERRYVIEW(dev_priv)) {
 	if (IS_CHERRYVIEW(dev_priv)) {
 		dev->driver->irq_handler = cherryview_irq_handler;
 		dev->driver->irq_handler = cherryview_irq_handler;
-		dev->driver->irq_preinstall = cherryview_irq_preinstall;
+		dev->driver->irq_preinstall = cherryview_irq_reset;
 		dev->driver->irq_postinstall = cherryview_irq_postinstall;
 		dev->driver->irq_postinstall = cherryview_irq_postinstall;
-		dev->driver->irq_uninstall = cherryview_irq_uninstall;
+		dev->driver->irq_uninstall = cherryview_irq_reset;
 		dev->driver->enable_vblank = i965_enable_vblank;
 		dev->driver->enable_vblank = i965_enable_vblank;
 		dev->driver->disable_vblank = i965_disable_vblank;
 		dev->driver->disable_vblank = i965_disable_vblank;
 		dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
 		dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
 	} else if (IS_VALLEYVIEW(dev_priv)) {
 	} else if (IS_VALLEYVIEW(dev_priv)) {
 		dev->driver->irq_handler = valleyview_irq_handler;
 		dev->driver->irq_handler = valleyview_irq_handler;
-		dev->driver->irq_preinstall = valleyview_irq_preinstall;
+		dev->driver->irq_preinstall = valleyview_irq_reset;
 		dev->driver->irq_postinstall = valleyview_irq_postinstall;
 		dev->driver->irq_postinstall = valleyview_irq_postinstall;
-		dev->driver->irq_uninstall = valleyview_irq_uninstall;
+		dev->driver->irq_uninstall = valleyview_irq_reset;
 		dev->driver->enable_vblank = i965_enable_vblank;
 		dev->driver->enable_vblank = i965_enable_vblank;
 		dev->driver->disable_vblank = i965_disable_vblank;
 		dev->driver->disable_vblank = i965_disable_vblank;
 		dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
 		dev_priv->display.hpd_irq_setup = i915_hpd_irq_setup;
@@ -4215,7 +4088,7 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 		dev->driver->irq_handler = gen8_irq_handler;
 		dev->driver->irq_handler = gen8_irq_handler;
 		dev->driver->irq_preinstall = gen8_irq_reset;
 		dev->driver->irq_preinstall = gen8_irq_reset;
 		dev->driver->irq_postinstall = gen8_irq_postinstall;
 		dev->driver->irq_postinstall = gen8_irq_postinstall;
-		dev->driver->irq_uninstall = gen8_irq_uninstall;
+		dev->driver->irq_uninstall = gen8_irq_reset;
 		dev->driver->enable_vblank = gen8_enable_vblank;
 		dev->driver->enable_vblank = gen8_enable_vblank;
 		dev->driver->disable_vblank = gen8_disable_vblank;
 		dev->driver->disable_vblank = gen8_disable_vblank;
 		if (IS_GEN9_LP(dev_priv))
 		if (IS_GEN9_LP(dev_priv))
@@ -4229,29 +4102,29 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
 		dev->driver->irq_handler = ironlake_irq_handler;
 		dev->driver->irq_handler = ironlake_irq_handler;
 		dev->driver->irq_preinstall = ironlake_irq_reset;
 		dev->driver->irq_preinstall = ironlake_irq_reset;
 		dev->driver->irq_postinstall = ironlake_irq_postinstall;
 		dev->driver->irq_postinstall = ironlake_irq_postinstall;
-		dev->driver->irq_uninstall = ironlake_irq_uninstall;
+		dev->driver->irq_uninstall = ironlake_irq_reset;
 		dev->driver->enable_vblank = ironlake_enable_vblank;
 		dev->driver->enable_vblank = ironlake_enable_vblank;
 		dev->driver->disable_vblank = ironlake_disable_vblank;
 		dev->driver->disable_vblank = ironlake_disable_vblank;
 		dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
 		dev_priv->display.hpd_irq_setup = ilk_hpd_irq_setup;
 	} else {
 	} else {
 		if (IS_GEN2(dev_priv)) {
 		if (IS_GEN2(dev_priv)) {
-			dev->driver->irq_preinstall = i8xx_irq_preinstall;
+			dev->driver->irq_preinstall = i8xx_irq_reset;
 			dev->driver->irq_postinstall = i8xx_irq_postinstall;
 			dev->driver->irq_postinstall = i8xx_irq_postinstall;
 			dev->driver->irq_handler = i8xx_irq_handler;
 			dev->driver->irq_handler = i8xx_irq_handler;
-			dev->driver->irq_uninstall = i8xx_irq_uninstall;
+			dev->driver->irq_uninstall = i8xx_irq_reset;
 			dev->driver->enable_vblank = i8xx_enable_vblank;
 			dev->driver->enable_vblank = i8xx_enable_vblank;
 			dev->driver->disable_vblank = i8xx_disable_vblank;
 			dev->driver->disable_vblank = i8xx_disable_vblank;
 		} else if (IS_GEN3(dev_priv)) {
 		} else if (IS_GEN3(dev_priv)) {
-			dev->driver->irq_preinstall = i915_irq_preinstall;
+			dev->driver->irq_preinstall = i915_irq_reset;
 			dev->driver->irq_postinstall = i915_irq_postinstall;
 			dev->driver->irq_postinstall = i915_irq_postinstall;
-			dev->driver->irq_uninstall = i915_irq_uninstall;
+			dev->driver->irq_uninstall = i915_irq_reset;
 			dev->driver->irq_handler = i915_irq_handler;
 			dev->driver->irq_handler = i915_irq_handler;
 			dev->driver->enable_vblank = i8xx_enable_vblank;
 			dev->driver->enable_vblank = i8xx_enable_vblank;
 			dev->driver->disable_vblank = i8xx_disable_vblank;
 			dev->driver->disable_vblank = i8xx_disable_vblank;
 		} else {
 		} else {
-			dev->driver->irq_preinstall = i965_irq_preinstall;
+			dev->driver->irq_preinstall = i965_irq_reset;
 			dev->driver->irq_postinstall = i965_irq_postinstall;
 			dev->driver->irq_postinstall = i965_irq_postinstall;
-			dev->driver->irq_uninstall = i965_irq_uninstall;
+			dev->driver->irq_uninstall = i965_irq_reset;
 			dev->driver->irq_handler = i965_irq_handler;
 			dev->driver->irq_handler = i965_irq_handler;
 			dev->driver->enable_vblank = i965_enable_vblank;
 			dev->driver->enable_vblank = i965_enable_vblank;
 			dev->driver->disable_vblank = i965_disable_vblank;
 			dev->driver->disable_vblank = i965_disable_vblank;

+ 109 - 0
drivers/gpu/drm/i915/i915_oa_cflgt2.c

@@ -0,0 +1,109 @@
+/*
+ * Autogenerated file by GPU Top : https://github.com/rib/gputop
+ * DO NOT EDIT manually!
+ *
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#include <linux/sysfs.h>
+
+#include "i915_drv.h"
+#include "i915_oa_cflgt2.h"
+
+static const struct i915_oa_reg b_counter_config_test_oa[] = {
+	{ _MMIO(0x2740), 0x00000000 },
+	{ _MMIO(0x2744), 0x00800000 },
+	{ _MMIO(0x2714), 0xf0800000 },
+	{ _MMIO(0x2710), 0x00000000 },
+	{ _MMIO(0x2724), 0xf0800000 },
+	{ _MMIO(0x2720), 0x00000000 },
+	{ _MMIO(0x2770), 0x00000004 },
+	{ _MMIO(0x2774), 0x00000000 },
+	{ _MMIO(0x2778), 0x00000003 },
+	{ _MMIO(0x277c), 0x00000000 },
+	{ _MMIO(0x2780), 0x00000007 },
+	{ _MMIO(0x2784), 0x00000000 },
+	{ _MMIO(0x2788), 0x00100002 },
+	{ _MMIO(0x278c), 0x0000fff7 },
+	{ _MMIO(0x2790), 0x00100002 },
+	{ _MMIO(0x2794), 0x0000ffcf },
+	{ _MMIO(0x2798), 0x00100082 },
+	{ _MMIO(0x279c), 0x0000ffef },
+	{ _MMIO(0x27a0), 0x001000c2 },
+	{ _MMIO(0x27a4), 0x0000ffe7 },
+	{ _MMIO(0x27a8), 0x00100001 },
+	{ _MMIO(0x27ac), 0x0000ffe7 },
+};
+
+static const struct i915_oa_reg flex_eu_config_test_oa[] = {
+};
+
+static const struct i915_oa_reg mux_config_test_oa[] = {
+	{ _MMIO(0x9840), 0x00000080 },
+	{ _MMIO(0x9888), 0x11810000 },
+	{ _MMIO(0x9888), 0x07810013 },
+	{ _MMIO(0x9888), 0x1f810000 },
+	{ _MMIO(0x9888), 0x1d810000 },
+	{ _MMIO(0x9888), 0x1b930040 },
+	{ _MMIO(0x9888), 0x07e54000 },
+	{ _MMIO(0x9888), 0x1f908000 },
+	{ _MMIO(0x9888), 0x11900000 },
+	{ _MMIO(0x9888), 0x37900000 },
+	{ _MMIO(0x9888), 0x53900000 },
+	{ _MMIO(0x9888), 0x45900000 },
+	{ _MMIO(0x9888), 0x33900000 },
+};
+
+static ssize_t
+show_test_oa_id(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "1\n");
+}
+
+void
+i915_perf_load_test_config_cflgt2(struct drm_i915_private *dev_priv)
+{
+	strncpy(dev_priv->perf.oa.test_config.uuid,
+		"74fb4902-d3d3-4237-9e90-cbdc68d0a446",
+		UUID_STRING_LEN);
+	dev_priv->perf.oa.test_config.id = 1;
+
+	dev_priv->perf.oa.test_config.mux_regs = mux_config_test_oa;
+	dev_priv->perf.oa.test_config.mux_regs_len = ARRAY_SIZE(mux_config_test_oa);
+
+	dev_priv->perf.oa.test_config.b_counter_regs = b_counter_config_test_oa;
+	dev_priv->perf.oa.test_config.b_counter_regs_len = ARRAY_SIZE(b_counter_config_test_oa);
+
+	dev_priv->perf.oa.test_config.flex_regs = flex_eu_config_test_oa;
+	dev_priv->perf.oa.test_config.flex_regs_len = ARRAY_SIZE(flex_eu_config_test_oa);
+
+	dev_priv->perf.oa.test_config.sysfs_metric.name = "74fb4902-d3d3-4237-9e90-cbdc68d0a446";
+	dev_priv->perf.oa.test_config.sysfs_metric.attrs = dev_priv->perf.oa.test_config.attrs;
+
+	dev_priv->perf.oa.test_config.attrs[0] = &dev_priv->perf.oa.test_config.sysfs_metric_id.attr;
+
+	dev_priv->perf.oa.test_config.sysfs_metric_id.attr.name = "id";
+	dev_priv->perf.oa.test_config.sysfs_metric_id.attr.mode = 0444;
+	dev_priv->perf.oa.test_config.sysfs_metric_id.show = show_test_oa_id;
+}

+ 34 - 0
drivers/gpu/drm/i915/i915_oa_cflgt2.h

@@ -0,0 +1,34 @@
+/*
+ * Autogenerated file by GPU Top : https://github.com/rib/gputop
+ * DO NOT EDIT manually!
+ *
+ *
+ * Copyright (c) 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __I915_OA_CFLGT2_H__
+#define __I915_OA_CFLGT2_H__
+
+extern void i915_perf_load_test_config_cflgt2(struct drm_i915_private *dev_priv);
+
+#endif

+ 72 - 136
drivers/gpu/drm/i915/i915_params.c

@@ -25,235 +25,171 @@
 #include "i915_params.h"
 #include "i915_params.h"
 #include "i915_drv.h"
 #include "i915_drv.h"
 
 
-struct i915_params i915 __read_mostly = {
-	.modeset = -1,
-	.panel_ignore_lid = 1,
-	.semaphores = -1,
-	.lvds_channel_mode = 0,
-	.panel_use_ssc = -1,
-	.vbt_sdvo_panel_type = -1,
-	.enable_rc6 = -1,
-	.enable_dc = -1,
-	.enable_fbc = -1,
-	.enable_execlists = -1,
-	.enable_hangcheck = true,
-	.enable_ppgtt = -1,
-	.enable_psr = -1,
-	.alpha_support = IS_ENABLED(CONFIG_DRM_I915_ALPHA_SUPPORT),
-	.disable_power_well = -1,
-	.enable_ips = 1,
-	.fastboot = 0,
-	.prefault_disable = 0,
-	.load_detect_test = 0,
-	.force_reset_modeset_test = 0,
-	.reset = 2,
-	.error_capture = true,
-	.invert_brightness = 0,
-	.disable_display = 0,
-	.enable_cmd_parser = true,
-	.use_mmio_flip = 0,
-	.mmio_debug = 0,
-	.verbose_state_checks = 1,
-	.nuclear_pageflip = 0,
-	.edp_vswing = 0,
-	.enable_guc_loading = 0,
-	.enable_guc_submission = 0,
-	.guc_log_level = -1,
-	.guc_firmware_path = NULL,
-	.huc_firmware_path = NULL,
-	.enable_dp_mst = true,
-	.inject_load_failure = 0,
-	.enable_dpcd_backlight = false,
-	.enable_gvt = false,
+#define i915_param_named(name, T, perm, desc) \
+	module_param_named(name, i915_modparams.name, T, perm); \
+	MODULE_PARM_DESC(name, desc)
+#define i915_param_named_unsafe(name, T, perm, desc) \
+	module_param_named_unsafe(name, i915_modparams.name, T, perm); \
+	MODULE_PARM_DESC(name, desc)
+
+struct i915_params i915_modparams __read_mostly = {
+#define MEMBER(T, member, value) .member = (value),
+	I915_PARAMS_FOR_EACH(MEMBER)
+#undef MEMBER
 };
 };
 
 
-module_param_named(modeset, i915.modeset, int, 0400);
-MODULE_PARM_DESC(modeset,
+i915_param_named(modeset, int, 0400,
 	"Use kernel modesetting [KMS] (0=disable, "
 	"Use kernel modesetting [KMS] (0=disable, "
 	"1=on, -1=force vga console preference [default])");
 	"1=on, -1=force vga console preference [default])");
 
 
-module_param_named_unsafe(panel_ignore_lid, i915.panel_ignore_lid, int, 0600);
-MODULE_PARM_DESC(panel_ignore_lid,
+i915_param_named_unsafe(panel_ignore_lid, int, 0600,
 	"Override lid status (0=autodetect, 1=autodetect disabled [default], "
 	"Override lid status (0=autodetect, 1=autodetect disabled [default], "
 	"-1=force lid closed, -2=force lid open)");
 	"-1=force lid closed, -2=force lid open)");
 
 
-module_param_named_unsafe(semaphores, i915.semaphores, int, 0400);
-MODULE_PARM_DESC(semaphores,
+i915_param_named_unsafe(semaphores, int, 0400,
 	"Use semaphores for inter-ring sync "
 	"Use semaphores for inter-ring sync "
 	"(default: -1 (use per-chip defaults))");
 	"(default: -1 (use per-chip defaults))");
 
 
-module_param_named_unsafe(enable_rc6, i915.enable_rc6, int, 0400);
-MODULE_PARM_DESC(enable_rc6,
+i915_param_named_unsafe(enable_rc6, int, 0400,
 	"Enable power-saving render C-state 6. "
 	"Enable power-saving render C-state 6. "
 	"Different stages can be selected via bitmask values "
 	"Different stages can be selected via bitmask values "
 	"(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). "
 	"(0 = disable; 1 = enable rc6; 2 = enable deep rc6; 4 = enable deepest rc6). "
 	"For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. "
 	"For example, 3 would enable rc6 and deep rc6, and 7 would enable everything. "
 	"default: -1 (use per-chip default)");
 	"default: -1 (use per-chip default)");
 
 
-module_param_named_unsafe(enable_dc, i915.enable_dc, int, 0400);
-MODULE_PARM_DESC(enable_dc,
+i915_param_named_unsafe(enable_dc, int, 0400,
 	"Enable power-saving display C-states. "
 	"Enable power-saving display C-states. "
 	"(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6)");
 	"(-1=auto [default]; 0=disable; 1=up to DC5; 2=up to DC6)");
 
 
-module_param_named_unsafe(enable_fbc, i915.enable_fbc, int, 0600);
-MODULE_PARM_DESC(enable_fbc,
+i915_param_named_unsafe(enable_fbc, int, 0600,
 	"Enable frame buffer compression for power savings "
 	"Enable frame buffer compression for power savings "
 	"(default: -1 (use per-chip default))");
 	"(default: -1 (use per-chip default))");
 
 
-module_param_named_unsafe(lvds_channel_mode, i915.lvds_channel_mode, int, 0400);
-MODULE_PARM_DESC(lvds_channel_mode,
+i915_param_named_unsafe(lvds_channel_mode, int, 0400,
 	 "Specify LVDS channel mode "
 	 "Specify LVDS channel mode "
 	 "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)");
 	 "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)");
 
 
-module_param_named_unsafe(lvds_use_ssc, i915.panel_use_ssc, int, 0600);
-MODULE_PARM_DESC(lvds_use_ssc,
+i915_param_named_unsafe(panel_use_ssc, int, 0600,
 	"Use Spread Spectrum Clock with panels [LVDS/eDP] "
 	"Use Spread Spectrum Clock with panels [LVDS/eDP] "
 	"(default: auto from VBT)");
 	"(default: auto from VBT)");
 
 
-module_param_named_unsafe(vbt_sdvo_panel_type, i915.vbt_sdvo_panel_type, int, 0400);
-MODULE_PARM_DESC(vbt_sdvo_panel_type,
+i915_param_named_unsafe(vbt_sdvo_panel_type, int, 0400,
 	"Override/Ignore selection of SDVO panel mode in the VBT "
 	"Override/Ignore selection of SDVO panel mode in the VBT "
 	"(-2=ignore, -1=auto [default], index in VBT BIOS table)");
 	"(-2=ignore, -1=auto [default], index in VBT BIOS table)");
 
 
-module_param_named_unsafe(reset, i915.reset, int, 0600);
-MODULE_PARM_DESC(reset, "Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])");
+i915_param_named_unsafe(reset, int, 0600,
+	"Attempt GPU resets (0=disabled, 1=full gpu reset, 2=engine reset [default])");
 
 
-module_param_named_unsafe(vbt_firmware, i915.vbt_firmware, charp, 0400);
-MODULE_PARM_DESC(vbt_firmware,
-		 "Load VBT from specified file under /lib/firmware");
+i915_param_named_unsafe(vbt_firmware, charp, 0400,
+	"Load VBT from specified file under /lib/firmware");
 
 
 #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
 #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR)
-module_param_named(error_capture, i915.error_capture, bool, 0600);
-MODULE_PARM_DESC(error_capture,
+i915_param_named(error_capture, bool, 0600,
 	"Record the GPU state following a hang. "
 	"Record the GPU state following a hang. "
 	"This information in /sys/class/drm/card<N>/error is vital for "
 	"This information in /sys/class/drm/card<N>/error is vital for "
 	"triaging and debugging hangs.");
 	"triaging and debugging hangs.");
 #endif
 #endif
 
 
-module_param_named_unsafe(enable_hangcheck, i915.enable_hangcheck, bool, 0644);
-MODULE_PARM_DESC(enable_hangcheck,
+i915_param_named_unsafe(enable_hangcheck, bool, 0644,
 	"Periodically check GPU activity for detecting hangs. "
 	"Periodically check GPU activity for detecting hangs. "
 	"WARNING: Disabling this can cause system wide hangs. "
 	"WARNING: Disabling this can cause system wide hangs. "
 	"(default: true)");
 	"(default: true)");
 
 
-module_param_named_unsafe(enable_ppgtt, i915.enable_ppgtt, int, 0400);
-MODULE_PARM_DESC(enable_ppgtt,
+i915_param_named_unsafe(enable_ppgtt, int, 0400,
 	"Override PPGTT usage. "
 	"Override PPGTT usage. "
 	"(-1=auto [default], 0=disabled, 1=aliasing, 2=full, 3=full with extended address space)");
 	"(-1=auto [default], 0=disabled, 1=aliasing, 2=full, 3=full with extended address space)");
 
 
-module_param_named_unsafe(enable_execlists, i915.enable_execlists, int, 0400);
-MODULE_PARM_DESC(enable_execlists,
+i915_param_named_unsafe(enable_execlists, int, 0400,
 	"Override execlists usage. "
 	"Override execlists usage. "
 	"(-1=auto [default], 0=disabled, 1=enabled)");
 	"(-1=auto [default], 0=disabled, 1=enabled)");
 
 
-module_param_named_unsafe(enable_psr, i915.enable_psr, int, 0600);
-MODULE_PARM_DESC(enable_psr, "Enable PSR "
-		 "(0=disabled, 1=enabled - link mode chosen per-platform, 2=force link-standby mode, 3=force link-off mode) "
-		 "Default: -1 (use per-chip default)");
+i915_param_named_unsafe(enable_psr, int, 0600,
+	"Enable PSR "
+	"(0=disabled, 1=enabled - link mode chosen per-platform, 2=force link-standby mode, 3=force link-off mode) "
+	"Default: -1 (use per-chip default)");
 
 
-module_param_named_unsafe(alpha_support, i915.alpha_support, bool, 0400);
-MODULE_PARM_DESC(alpha_support,
+i915_param_named_unsafe(alpha_support, bool, 0400,
 	"Enable alpha quality driver support for latest hardware. "
 	"Enable alpha quality driver support for latest hardware. "
 	"See also CONFIG_DRM_I915_ALPHA_SUPPORT.");
 	"See also CONFIG_DRM_I915_ALPHA_SUPPORT.");
 
 
-module_param_named_unsafe(disable_power_well, i915.disable_power_well, int, 0400);
-MODULE_PARM_DESC(disable_power_well,
+i915_param_named_unsafe(disable_power_well, int, 0400,
 	"Disable display power wells when possible "
 	"Disable display power wells when possible "
 	"(-1=auto [default], 0=power wells always on, 1=power wells disabled when possible)");
 	"(-1=auto [default], 0=power wells always on, 1=power wells disabled when possible)");
 
 
-module_param_named_unsafe(enable_ips, i915.enable_ips, int, 0600);
-MODULE_PARM_DESC(enable_ips, "Enable IPS (default: true)");
+i915_param_named_unsafe(enable_ips, int, 0600, "Enable IPS (default: true)");
 
 
-module_param_named(fastboot, i915.fastboot, bool, 0600);
-MODULE_PARM_DESC(fastboot,
+i915_param_named(fastboot, bool, 0600,
 	"Try to skip unnecessary mode sets at boot time (default: false)");
 	"Try to skip unnecessary mode sets at boot time (default: false)");
 
 
-module_param_named_unsafe(prefault_disable, i915.prefault_disable, bool, 0600);
-MODULE_PARM_DESC(prefault_disable,
+i915_param_named_unsafe(prefault_disable, bool, 0600,
 	"Disable page prefaulting for pread/pwrite/reloc (default:false). "
 	"Disable page prefaulting for pread/pwrite/reloc (default:false). "
 	"For developers only.");
 	"For developers only.");
 
 
-module_param_named_unsafe(load_detect_test, i915.load_detect_test, bool, 0600);
-MODULE_PARM_DESC(load_detect_test,
+i915_param_named_unsafe(load_detect_test, bool, 0600,
 	"Force-enable the VGA load detect code for testing (default:false). "
 	"Force-enable the VGA load detect code for testing (default:false). "
 	"For developers only.");
 	"For developers only.");
 
 
-module_param_named_unsafe(force_reset_modeset_test, i915.force_reset_modeset_test, bool, 0600);
-MODULE_PARM_DESC(force_reset_modeset_test,
+i915_param_named_unsafe(force_reset_modeset_test, bool, 0600,
 	"Force a modeset during gpu reset for testing (default:false). "
 	"Force a modeset during gpu reset for testing (default:false). "
 	"For developers only.");
 	"For developers only.");
 
 
-module_param_named_unsafe(invert_brightness, i915.invert_brightness, int, 0600);
-MODULE_PARM_DESC(invert_brightness,
+i915_param_named_unsafe(invert_brightness, int, 0600,
 	"Invert backlight brightness "
 	"Invert backlight brightness "
 	"(-1 force normal, 0 machine defaults, 1 force inversion), please "
 	"(-1 force normal, 0 machine defaults, 1 force inversion), please "
 	"report PCI device ID, subsystem vendor and subsystem device ID "
 	"report PCI device ID, subsystem vendor and subsystem device ID "
 	"to dri-devel@lists.freedesktop.org, if your machine needs it. "
 	"to dri-devel@lists.freedesktop.org, if your machine needs it. "
 	"It will then be included in an upcoming module version.");
 	"It will then be included in an upcoming module version.");
 
 
-module_param_named(disable_display, i915.disable_display, bool, 0400);
-MODULE_PARM_DESC(disable_display, "Disable display (default: false)");
+i915_param_named(disable_display, bool, 0400,
+	"Disable display (default: false)");
 
 
-module_param_named_unsafe(enable_cmd_parser, i915.enable_cmd_parser, bool, 0400);
-MODULE_PARM_DESC(enable_cmd_parser,
-		 "Enable command parsing (true=enabled [default], false=disabled)");
+i915_param_named_unsafe(enable_cmd_parser, bool, 0400,
+	"Enable command parsing (true=enabled [default], false=disabled)");
 
 
-module_param_named_unsafe(use_mmio_flip, i915.use_mmio_flip, int, 0600);
-MODULE_PARM_DESC(use_mmio_flip,
-		 "use MMIO flips (-1=never, 0=driver discretion [default], 1=always)");
+i915_param_named_unsafe(use_mmio_flip, int, 0600,
+	"use MMIO flips (-1=never, 0=driver discretion [default], 1=always)");
 
 
-module_param_named(mmio_debug, i915.mmio_debug, int, 0600);
-MODULE_PARM_DESC(mmio_debug,
+i915_param_named(mmio_debug, int, 0600,
 	"Enable the MMIO debug code for the first N failures (default: off). "
 	"Enable the MMIO debug code for the first N failures (default: off). "
 	"This may negatively affect performance.");
 	"This may negatively affect performance.");
 
 
-module_param_named(verbose_state_checks, i915.verbose_state_checks, bool, 0600);
-MODULE_PARM_DESC(verbose_state_checks,
+i915_param_named(verbose_state_checks, bool, 0600,
 	"Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions.");
 	"Enable verbose logs (ie. WARN_ON()) in case of unexpected hw state conditions.");
 
 
-module_param_named_unsafe(nuclear_pageflip, i915.nuclear_pageflip, bool, 0400);
-MODULE_PARM_DESC(nuclear_pageflip,
-		 "Force enable atomic functionality on platforms that don't have full support yet.");
+i915_param_named_unsafe(nuclear_pageflip, bool, 0400,
+	"Force enable atomic functionality on platforms that don't have full support yet.");
 
 
 /* WA to get away with the default setting in VBT for early platforms.Will be removed */
 /* WA to get away with the default setting in VBT for early platforms.Will be removed */
-module_param_named_unsafe(edp_vswing, i915.edp_vswing, int, 0400);
-MODULE_PARM_DESC(edp_vswing,
-		 "Ignore/Override vswing pre-emph table selection from VBT "
-		 "(0=use value from vbt [default], 1=low power swing(200mV),"
-		 "2=default swing(400mV))");
-
-module_param_named_unsafe(enable_guc_loading, i915.enable_guc_loading, int, 0400);
-MODULE_PARM_DESC(enable_guc_loading,
-		"Enable GuC firmware loading "
-		"(-1=auto, 0=never [default], 1=if available, 2=required)");
-
-module_param_named_unsafe(enable_guc_submission, i915.enable_guc_submission, int, 0400);
-MODULE_PARM_DESC(enable_guc_submission,
-		"Enable GuC submission "
-		"(-1=auto, 0=never [default], 1=if available, 2=required)");
-
-module_param_named(guc_log_level, i915.guc_log_level, int, 0400);
-MODULE_PARM_DESC(guc_log_level,
+i915_param_named_unsafe(edp_vswing, int, 0400,
+	"Ignore/Override vswing pre-emph table selection from VBT "
+	"(0=use value from vbt [default], 1=low power swing(200mV),"
+	"2=default swing(400mV))");
+
+i915_param_named_unsafe(enable_guc_loading, int, 0400,
+	"Enable GuC firmware loading "
+	"(-1=auto, 0=never [default], 1=if available, 2=required)");
+
+i915_param_named_unsafe(enable_guc_submission, int, 0400,
+	"Enable GuC submission "
+	"(-1=auto, 0=never [default], 1=if available, 2=required)");
+
+i915_param_named(guc_log_level, int, 0400,
 	"GuC firmware logging level (-1:disabled (default), 0-3:enabled)");
 	"GuC firmware logging level (-1:disabled (default), 0-3:enabled)");
 
 
-module_param_named_unsafe(guc_firmware_path, i915.guc_firmware_path, charp, 0400);
-MODULE_PARM_DESC(guc_firmware_path,
+i915_param_named_unsafe(guc_firmware_path, charp, 0400,
 	"GuC firmware path to use instead of the default one");
 	"GuC firmware path to use instead of the default one");
 
 
-module_param_named_unsafe(huc_firmware_path, i915.huc_firmware_path, charp, 0400);
-MODULE_PARM_DESC(huc_firmware_path,
+i915_param_named_unsafe(huc_firmware_path, charp, 0400,
 	"HuC firmware path to use instead of the default one");
 	"HuC firmware path to use instead of the default one");
 
 
-module_param_named_unsafe(enable_dp_mst, i915.enable_dp_mst, bool, 0600);
-MODULE_PARM_DESC(enable_dp_mst,
+i915_param_named_unsafe(enable_dp_mst, bool, 0600,
 	"Enable multi-stream transport (MST) for new DisplayPort sinks. (default: true)");
 	"Enable multi-stream transport (MST) for new DisplayPort sinks. (default: true)");
-module_param_named_unsafe(inject_load_failure, i915.inject_load_failure, uint, 0400);
-MODULE_PARM_DESC(inject_load_failure,
+
+i915_param_named_unsafe(inject_load_failure, uint, 0400,
 	"Force an error after a number of failure check points (0:disabled (default), N:force failure at the Nth failure check point)");
 	"Force an error after a number of failure check points (0:disabled (default), N:force failure at the Nth failure check point)");
-module_param_named(enable_dpcd_backlight, i915.enable_dpcd_backlight, bool, 0600);
-MODULE_PARM_DESC(enable_dpcd_backlight,
+
+i915_param_named(enable_dpcd_backlight, bool, 0600,
 	"Enable support for DPCD backlight control (default:false)");
 	"Enable support for DPCD backlight control (default:false)");
 
 
-module_param_named(enable_gvt, i915.enable_gvt, bool, 0400);
-MODULE_PARM_DESC(enable_gvt,
+i915_param_named(enable_gvt, bool, 0400,
 	"Enable support for Intel GVT-g graphics virtualization host support(default:false)");
 	"Enable support for Intel GVT-g graphics virtualization host support(default:false)");

+ 43 - 43
drivers/gpu/drm/i915/i915_params.h

@@ -27,56 +27,56 @@
 
 
 #include <linux/cache.h> /* for __read_mostly */
 #include <linux/cache.h> /* for __read_mostly */
 
 
-#define I915_PARAMS_FOR_EACH(func) \
-	func(char *, vbt_firmware); \
-	func(int, modeset); \
-	func(int, panel_ignore_lid); \
-	func(int, semaphores); \
-	func(int, lvds_channel_mode); \
-	func(int, panel_use_ssc); \
-	func(int, vbt_sdvo_panel_type); \
-	func(int, enable_rc6); \
-	func(int, enable_dc); \
-	func(int, enable_fbc); \
-	func(int, enable_ppgtt); \
-	func(int, enable_execlists); \
-	func(int, enable_psr); \
-	func(int, disable_power_well); \
-	func(int, enable_ips); \
-	func(int, invert_brightness); \
-	func(int, enable_guc_loading); \
-	func(int, enable_guc_submission); \
-	func(int, guc_log_level); \
-	func(char *, guc_firmware_path); \
-	func(char *, huc_firmware_path); \
-	func(int, use_mmio_flip); \
-	func(int, mmio_debug); \
-	func(int, edp_vswing); \
-	func(int, reset); \
-	func(unsigned int, inject_load_failure); \
+#define I915_PARAMS_FOR_EACH(param) \
+	param(char *, vbt_firmware, NULL) \
+	param(int, modeset, -1) \
+	param(int, panel_ignore_lid, 1) \
+	param(int, semaphores, -1) \
+	param(int, lvds_channel_mode, 0) \
+	param(int, panel_use_ssc, -1) \
+	param(int, vbt_sdvo_panel_type, -1) \
+	param(int, enable_rc6, -1) \
+	param(int, enable_dc, -1) \
+	param(int, enable_fbc, -1) \
+	param(int, enable_ppgtt, -1) \
+	param(int, enable_execlists, -1) \
+	param(int, enable_psr, -1) \
+	param(int, disable_power_well, -1) \
+	param(int, enable_ips, 1) \
+	param(int, invert_brightness, 0) \
+	param(int, enable_guc_loading, 0) \
+	param(int, enable_guc_submission, 0) \
+	param(int, guc_log_level, -1) \
+	param(char *, guc_firmware_path, NULL) \
+	param(char *, huc_firmware_path, NULL) \
+	param(int, use_mmio_flip, 0) \
+	param(int, mmio_debug, 0) \
+	param(int, edp_vswing, 0) \
+	param(int, reset, 2) \
+	param(unsigned int, inject_load_failure, 0) \
 	/* leave bools at the end to not create holes */ \
 	/* leave bools at the end to not create holes */ \
-	func(bool, alpha_support); \
-	func(bool, enable_cmd_parser); \
-	func(bool, enable_hangcheck); \
-	func(bool, fastboot); \
-	func(bool, prefault_disable); \
-	func(bool, load_detect_test); \
-	func(bool, force_reset_modeset_test); \
-	func(bool, error_capture); \
-	func(bool, disable_display); \
-	func(bool, verbose_state_checks); \
-	func(bool, nuclear_pageflip); \
-	func(bool, enable_dp_mst); \
-	func(bool, enable_dpcd_backlight); \
-	func(bool, enable_gvt)
+	param(bool, alpha_support, IS_ENABLED(CONFIG_DRM_I915_ALPHA_SUPPORT)) \
+	param(bool, enable_cmd_parser, true) \
+	param(bool, enable_hangcheck, true) \
+	param(bool, fastboot, false) \
+	param(bool, prefault_disable, false) \
+	param(bool, load_detect_test, false) \
+	param(bool, force_reset_modeset_test, false) \
+	param(bool, error_capture, true) \
+	param(bool, disable_display, false) \
+	param(bool, verbose_state_checks, true) \
+	param(bool, nuclear_pageflip, false) \
+	param(bool, enable_dp_mst, true) \
+	param(bool, enable_dpcd_backlight, false) \
+	param(bool, enable_gvt, false)
 
 
-#define MEMBER(T, member) T member
+#define MEMBER(T, member, ...) T member;
 struct i915_params {
 struct i915_params {
 	I915_PARAMS_FOR_EACH(MEMBER);
 	I915_PARAMS_FOR_EACH(MEMBER);
 };
 };
 #undef MEMBER
 #undef MEMBER
 
 
-extern struct i915_params i915 __read_mostly;
+extern struct i915_params i915_modparams __read_mostly;
 
 
 #endif
 #endif
 
 

+ 9 - 10
drivers/gpu/drm/i915/i915_pci.c

@@ -168,6 +168,7 @@ static const struct intel_device_info intel_i965g_info __initconst = {
 	.platform = INTEL_I965G,
 	.platform = INTEL_I965G,
 	.has_overlay = 1,
 	.has_overlay = 1,
 	.hws_needs_physical = 1,
 	.hws_needs_physical = 1,
+	.has_snoop = false,
 };
 };
 
 
 static const struct intel_device_info intel_i965gm_info __initconst = {
 static const struct intel_device_info intel_i965gm_info __initconst = {
@@ -177,6 +178,7 @@ static const struct intel_device_info intel_i965gm_info __initconst = {
 	.has_overlay = 1,
 	.has_overlay = 1,
 	.supports_tv = 1,
 	.supports_tv = 1,
 	.hws_needs_physical = 1,
 	.hws_needs_physical = 1,
+	.has_snoop = false,
 };
 };
 
 
 static const struct intel_device_info intel_g45_info __initconst = {
 static const struct intel_device_info intel_g45_info __initconst = {
@@ -198,7 +200,6 @@ static const struct intel_device_info intel_gm45_info __initconst = {
 #define GEN5_FEATURES \
 #define GEN5_FEATURES \
 	.gen = 5, .num_pipes = 2, \
 	.gen = 5, .num_pipes = 2, \
 	.has_hotplug = 1, \
 	.has_hotplug = 1, \
-	.has_gmbus_irq = 1, \
 	.ring_mask = RENDER_RING | BSD_RING, \
 	.ring_mask = RENDER_RING | BSD_RING, \
 	.has_snoop = true, \
 	.has_snoop = true, \
 	GEN_DEFAULT_PIPEOFFSETS, \
 	GEN_DEFAULT_PIPEOFFSETS, \
@@ -223,7 +224,6 @@ static const struct intel_device_info intel_ironlake_m_info __initconst = {
 	.has_llc = 1, \
 	.has_llc = 1, \
 	.has_rc6 = 1, \
 	.has_rc6 = 1, \
 	.has_rc6p = 1, \
 	.has_rc6p = 1, \
-	.has_gmbus_irq = 1, \
 	.has_aliasing_ppgtt = 1, \
 	.has_aliasing_ppgtt = 1, \
 	GEN_DEFAULT_PIPEOFFSETS, \
 	GEN_DEFAULT_PIPEOFFSETS, \
 	CURSOR_OFFSETS
 	CURSOR_OFFSETS
@@ -266,7 +266,6 @@ static const struct intel_device_info intel_sandybridge_m_gt2_info __initconst =
 	.has_llc = 1, \
 	.has_llc = 1, \
 	.has_rc6 = 1, \
 	.has_rc6 = 1, \
 	.has_rc6p = 1, \
 	.has_rc6p = 1, \
-	.has_gmbus_irq = 1, \
 	.has_aliasing_ppgtt = 1, \
 	.has_aliasing_ppgtt = 1, \
 	.has_full_ppgtt = 1, \
 	.has_full_ppgtt = 1, \
 	GEN_DEFAULT_PIPEOFFSETS, \
 	GEN_DEFAULT_PIPEOFFSETS, \
@@ -319,7 +318,6 @@ static const struct intel_device_info intel_valleyview_info __initconst = {
 	.has_psr = 1,
 	.has_psr = 1,
 	.has_runtime_pm = 1,
 	.has_runtime_pm = 1,
 	.has_rc6 = 1,
 	.has_rc6 = 1,
-	.has_gmbus_irq = 1,
 	.has_gmch_display = 1,
 	.has_gmch_display = 1,
 	.has_hotplug = 1,
 	.has_hotplug = 1,
 	.has_aliasing_ppgtt = 1,
 	.has_aliasing_ppgtt = 1,
@@ -410,7 +408,6 @@ static const struct intel_device_info intel_cherryview_info __initconst = {
 	.has_runtime_pm = 1,
 	.has_runtime_pm = 1,
 	.has_resource_streamer = 1,
 	.has_resource_streamer = 1,
 	.has_rc6 = 1,
 	.has_rc6 = 1,
-	.has_gmbus_irq = 1,
 	.has_logical_ring_contexts = 1,
 	.has_logical_ring_contexts = 1,
 	.has_gmch_display = 1,
 	.has_gmch_display = 1,
 	.has_aliasing_ppgtt = 1,
 	.has_aliasing_ppgtt = 1,
@@ -472,7 +469,6 @@ static const struct intel_device_info intel_skylake_gt4_info __initconst = {
 	.has_resource_streamer = 1, \
 	.has_resource_streamer = 1, \
 	.has_rc6 = 1, \
 	.has_rc6 = 1, \
 	.has_dp_mst = 1, \
 	.has_dp_mst = 1, \
-	.has_gmbus_irq = 1, \
 	.has_logical_ring_contexts = 1, \
 	.has_logical_ring_contexts = 1, \
 	.has_guc = 1, \
 	.has_guc = 1, \
 	.has_aliasing_ppgtt = 1, \
 	.has_aliasing_ppgtt = 1, \
@@ -480,6 +476,7 @@ static const struct intel_device_info intel_skylake_gt4_info __initconst = {
 	.has_full_48bit_ppgtt = 1, \
 	.has_full_48bit_ppgtt = 1, \
 	.has_reset_engine = 1, \
 	.has_reset_engine = 1, \
 	.has_snoop = true, \
 	.has_snoop = true, \
+	.has_ipc = 1, \
 	GEN_DEFAULT_PIPEOFFSETS, \
 	GEN_DEFAULT_PIPEOFFSETS, \
 	IVB_CURSOR_OFFSETS, \
 	IVB_CURSOR_OFFSETS, \
 	BDW_COLORS
 	BDW_COLORS
@@ -503,6 +500,7 @@ static const struct intel_device_info intel_geminilake_info __initconst = {
 	.platform = INTEL_KABYLAKE, \
 	.platform = INTEL_KABYLAKE, \
 	.has_csr = 1, \
 	.has_csr = 1, \
 	.has_guc = 1, \
 	.has_guc = 1, \
+	.has_ipc = 1, \
 	.ddb_size = 896
 	.ddb_size = 896
 
 
 static const struct intel_device_info intel_kabylake_gt1_info __initconst = {
 static const struct intel_device_info intel_kabylake_gt1_info __initconst = {
@@ -522,12 +520,12 @@ static const struct intel_device_info intel_kabylake_gt3_info __initconst = {
 };
 };
 
 
 #define CFL_PLATFORM \
 #define CFL_PLATFORM \
-	.is_alpha_support = 1, \
 	BDW_FEATURES, \
 	BDW_FEATURES, \
 	.gen = 9, \
 	.gen = 9, \
 	.platform = INTEL_COFFEELAKE, \
 	.platform = INTEL_COFFEELAKE, \
 	.has_csr = 1, \
 	.has_csr = 1, \
 	.has_guc = 1, \
 	.has_guc = 1, \
+	.has_ipc = 1, \
 	.ddb_size = 896
 	.ddb_size = 896
 
 
 static const struct intel_device_info intel_coffeelake_gt1_info __initconst = {
 static const struct intel_device_info intel_coffeelake_gt1_info __initconst = {
@@ -554,6 +552,7 @@ static const struct intel_device_info intel_cannonlake_gt2_info __initconst = {
 	.gt = 2,
 	.gt = 2,
 	.ddb_size = 1024,
 	.ddb_size = 1024,
 	.has_csr = 1,
 	.has_csr = 1,
+	.has_ipc = 1,
 	.color = { .degamma_lut_size = 0, .gamma_lut_size = 1024 }
 	.color = { .degamma_lut_size = 0, .gamma_lut_size = 1024 }
 };
 };
 
 
@@ -632,7 +631,7 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 		(struct intel_device_info *) ent->driver_data;
 		(struct intel_device_info *) ent->driver_data;
 	int err;
 	int err;
 
 
-	if (IS_ALPHA_SUPPORT(intel_info) && !i915.alpha_support) {
+	if (IS_ALPHA_SUPPORT(intel_info) && !i915_modparams.alpha_support) {
 		DRM_INFO("The driver support for your hardware in this kernel version is alpha quality\n"
 		DRM_INFO("The driver support for your hardware in this kernel version is alpha quality\n"
 			 "See CONFIG_DRM_I915_ALPHA_SUPPORT or i915.alpha_support module parameter\n"
 			 "See CONFIG_DRM_I915_ALPHA_SUPPORT or i915.alpha_support module parameter\n"
 			 "to enable support in this kernel version, or check for kernel updates.\n");
 			 "to enable support in this kernel version, or check for kernel updates.\n");
@@ -690,10 +689,10 @@ static int __init i915_init(void)
 	 * vga_text_mode_force boot option.
 	 * vga_text_mode_force boot option.
 	 */
 	 */
 
 
-	if (i915.modeset == 0)
+	if (i915_modparams.modeset == 0)
 		use_kms = false;
 		use_kms = false;
 
 
-	if (vgacon_text_force() && i915.modeset == -1)
+	if (vgacon_text_force() && i915_modparams.modeset == -1)
 		use_kms = false;
 		use_kms = false;
 
 
 	if (!use_kms) {
 	if (!use_kms) {

+ 9 - 5
drivers/gpu/drm/i915/i915_perf.c

@@ -206,6 +206,7 @@
 #include "i915_oa_kblgt2.h"
 #include "i915_oa_kblgt2.h"
 #include "i915_oa_kblgt3.h"
 #include "i915_oa_kblgt3.h"
 #include "i915_oa_glk.h"
 #include "i915_oa_glk.h"
+#include "i915_oa_cflgt2.h"
 
 
 /* HW requires this to be a power of two, between 128k and 16M, though driver
 /* HW requires this to be a power of two, between 128k and 16M, though driver
  * is currently generally designed assuming the largest 16M size is used such
  * is currently generally designed assuming the largest 16M size is used such
@@ -1213,7 +1214,7 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream)
 {
 {
 	struct drm_i915_private *dev_priv = stream->dev_priv;
 	struct drm_i915_private *dev_priv = stream->dev_priv;
 
 
-	if (i915.enable_execlists)
+	if (i915_modparams.enable_execlists)
 		dev_priv->perf.oa.specific_ctx_id = stream->ctx->hw_id;
 		dev_priv->perf.oa.specific_ctx_id = stream->ctx->hw_id;
 	else {
 	else {
 		struct intel_engine_cs *engine = dev_priv->engine[RCS];
 		struct intel_engine_cs *engine = dev_priv->engine[RCS];
@@ -1259,7 +1260,7 @@ static void oa_put_render_ctx_id(struct i915_perf_stream *stream)
 {
 {
 	struct drm_i915_private *dev_priv = stream->dev_priv;
 	struct drm_i915_private *dev_priv = stream->dev_priv;
 
 
-	if (i915.enable_execlists) {
+	if (i915_modparams.enable_execlists) {
 		dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID;
 		dev_priv->perf.oa.specific_ctx_id = INVALID_CTX_ID;
 	} else {
 	} else {
 		struct intel_engine_cs *engine = dev_priv->engine[RCS];
 		struct intel_engine_cs *engine = dev_priv->engine[RCS];
@@ -1850,8 +1851,7 @@ static int gen8_enable_metric_set(struct drm_i915_private *dev_priv,
 	 * be read back from automatically triggered reports, as part of the
 	 * be read back from automatically triggered reports, as part of the
 	 * RPT_ID field.
 	 * RPT_ID field.
 	 */
 	 */
-	if (IS_SKYLAKE(dev_priv) || IS_BROXTON(dev_priv) ||
-	    IS_KABYLAKE(dev_priv) || IS_GEMINILAKE(dev_priv)) {
+	if (IS_GEN9(dev_priv)) {
 		I915_WRITE(GEN8_OA_DEBUG,
 		I915_WRITE(GEN8_OA_DEBUG,
 			   _MASKED_BIT_ENABLE(GEN9_OA_DEBUG_DISABLE_CLK_RATIO_REPORTS |
 			   _MASKED_BIT_ENABLE(GEN9_OA_DEBUG_DISABLE_CLK_RATIO_REPORTS |
 					      GEN9_OA_DEBUG_INCLUDE_CLK_RATIO));
 					      GEN9_OA_DEBUG_INCLUDE_CLK_RATIO));
@@ -2927,6 +2927,9 @@ void i915_perf_register(struct drm_i915_private *dev_priv)
 			i915_perf_load_test_config_kblgt3(dev_priv);
 			i915_perf_load_test_config_kblgt3(dev_priv);
 	} else if (IS_GEMINILAKE(dev_priv)) {
 	} else if (IS_GEMINILAKE(dev_priv)) {
 		i915_perf_load_test_config_glk(dev_priv);
 		i915_perf_load_test_config_glk(dev_priv);
+	} else if (IS_COFFEELAKE(dev_priv)) {
+		if (IS_CFL_GT2(dev_priv))
+			i915_perf_load_test_config_cflgt2(dev_priv);
 	}
 	}
 
 
 	if (dev_priv->perf.oa.test_config.id == 0)
 	if (dev_priv->perf.oa.test_config.id == 0)
@@ -3405,7 +3408,7 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
 		dev_priv->perf.oa.timestamp_frequency = 12500000;
 		dev_priv->perf.oa.timestamp_frequency = 12500000;
 
 
 		dev_priv->perf.oa.oa_formats = hsw_oa_formats;
 		dev_priv->perf.oa.oa_formats = hsw_oa_formats;
-	} else if (i915.enable_execlists) {
+	} else if (i915_modparams.enable_execlists) {
 		/* Note: that although we could theoretically also support the
 		/* Note: that although we could theoretically also support the
 		 * legacy ringbuffer mode on BDW (and earlier iterations of
 		 * legacy ringbuffer mode on BDW (and earlier iterations of
 		 * this driver, before upstreaming did this) it didn't seem
 		 * this driver, before upstreaming did this) it didn't seem
@@ -3453,6 +3456,7 @@ void i915_perf_init(struct drm_i915_private *dev_priv)
 				break;
 				break;
 			case INTEL_SKYLAKE:
 			case INTEL_SKYLAKE:
 			case INTEL_KABYLAKE:
 			case INTEL_KABYLAKE:
+			case INTEL_COFFEELAKE:
 				dev_priv->perf.oa.timestamp_frequency = 12000000;
 				dev_priv->perf.oa.timestamp_frequency = 12000000;
 				break;
 				break;
 			default:
 			default:

+ 35 - 7
drivers/gpu/drm/i915/i915_reg.h

@@ -2336,7 +2336,7 @@ enum i915_power_well_id {
 #define DONE_REG		_MMIO(0x40b0)
 #define DONE_REG		_MMIO(0x40b0)
 #define GEN8_PRIVATE_PAT_LO	_MMIO(0x40e0)
 #define GEN8_PRIVATE_PAT_LO	_MMIO(0x40e0)
 #define GEN8_PRIVATE_PAT_HI	_MMIO(0x40e0 + 4)
 #define GEN8_PRIVATE_PAT_HI	_MMIO(0x40e0 + 4)
-#define GEN10_PAT_INDEX(index)	_MMIO(0x40e0 + index*4)
+#define GEN10_PAT_INDEX(index)	_MMIO(0x40e0 + (index)*4)
 #define BSD_HWS_PGA_GEN7	_MMIO(0x04180)
 #define BSD_HWS_PGA_GEN7	_MMIO(0x04180)
 #define BLT_HWS_PGA_GEN7	_MMIO(0x04280)
 #define BLT_HWS_PGA_GEN7	_MMIO(0x04280)
 #define VEBOX_HWS_PGA_GEN7	_MMIO(0x04380)
 #define VEBOX_HWS_PGA_GEN7	_MMIO(0x04380)
@@ -2730,6 +2730,11 @@ enum i915_power_well_id {
 #define   GEN9_F2_SS_DIS_SHIFT		20
 #define   GEN9_F2_SS_DIS_SHIFT		20
 #define   GEN9_F2_SS_DIS_MASK		(0xf << GEN9_F2_SS_DIS_SHIFT)
 #define   GEN9_F2_SS_DIS_MASK		(0xf << GEN9_F2_SS_DIS_SHIFT)
 
 
+#define   GEN10_F2_S_ENA_SHIFT		22
+#define   GEN10_F2_S_ENA_MASK		(0x3f << GEN10_F2_S_ENA_SHIFT)
+#define   GEN10_F2_SS_DIS_SHIFT		18
+#define   GEN10_F2_SS_DIS_MASK		(0xf << GEN10_F2_SS_DIS_SHIFT)
+
 #define GEN8_EU_DISABLE0		_MMIO(0x9134)
 #define GEN8_EU_DISABLE0		_MMIO(0x9134)
 #define   GEN8_EU_DIS0_S0_MASK		0xffffff
 #define   GEN8_EU_DIS0_S0_MASK		0xffffff
 #define   GEN8_EU_DIS0_S1_SHIFT		24
 #define   GEN8_EU_DIS0_S1_SHIFT		24
@@ -2745,6 +2750,9 @@ enum i915_power_well_id {
 
 
 #define GEN9_EU_DISABLE(slice)		_MMIO(0x9134 + (slice)*0x4)
 #define GEN9_EU_DISABLE(slice)		_MMIO(0x9134 + (slice)*0x4)
 
 
+#define GEN10_EU_DISABLE3		_MMIO(0x9140)
+#define   GEN10_EU_DIS_SS_MASK		0xff
+
 #define GEN6_BSD_SLEEP_PSMI_CONTROL	_MMIO(0x12050)
 #define GEN6_BSD_SLEEP_PSMI_CONTROL	_MMIO(0x12050)
 #define   GEN6_BSD_SLEEP_MSG_DISABLE	(1 << 0)
 #define   GEN6_BSD_SLEEP_MSG_DISABLE	(1 << 0)
 #define   GEN6_BSD_SLEEP_FLUSH_DISABLE	(1 << 2)
 #define   GEN6_BSD_SLEEP_FLUSH_DISABLE	(1 << 2)
@@ -4047,7 +4055,7 @@ enum {
 #define   EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4
 #define   EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4
 #define   EDP_PSR2_FRAME_BEFORE_SU_MASK	(0xf<<4)
 #define   EDP_PSR2_FRAME_BEFORE_SU_MASK	(0xf<<4)
 #define   EDP_PSR2_IDLE_MASK		0xf
 #define   EDP_PSR2_IDLE_MASK		0xf
-#define   EDP_FRAMES_BEFORE_SU_ENTRY   (1<<4)
+#define   EDP_PSR2_FRAME_BEFORE_SU(a)	((a)<<4)
 
 
 #define EDP_PSR2_STATUS_CTL            _MMIO(0x6f940)
 #define EDP_PSR2_STATUS_CTL            _MMIO(0x6f940)
 #define EDP_PSR2_STATUS_STATE_MASK     (0xf<<28)
 #define EDP_PSR2_STATUS_STATE_MASK     (0xf<<28)
@@ -6913,7 +6921,7 @@ enum {
 # define CHICKEN3_DGMG_DONE_FIX_DISABLE		(1 << 2)
 # define CHICKEN3_DGMG_DONE_FIX_DISABLE		(1 << 2)
 
 
 #define CHICKEN_PAR1_1		_MMIO(0x42080)
 #define CHICKEN_PAR1_1		_MMIO(0x42080)
-#define  SKL_RC_HASH_OUTSIDE	(1 << 15)
+#define  SKL_DE_COMPRESSED_HASH_MODE	(1 << 15)
 #define  DPA_MASK_VBLANK_SRD	(1 << 15)
 #define  DPA_MASK_VBLANK_SRD	(1 << 15)
 #define  FORCE_ARB_IDLE_PLANES	(1 << 14)
 #define  FORCE_ARB_IDLE_PLANES	(1 << 14)
 #define  SKL_EDP_PSR_FIX_RDWRAP	(1 << 3)
 #define  SKL_EDP_PSR_FIX_RDWRAP	(1 << 3)
@@ -6949,6 +6957,7 @@ enum {
 #define  DISP_FBC_WM_DIS		(1<<15)
 #define  DISP_FBC_WM_DIS		(1<<15)
 #define DISP_ARB_CTL2	_MMIO(0x45004)
 #define DISP_ARB_CTL2	_MMIO(0x45004)
 #define  DISP_DATA_PARTITION_5_6	(1<<6)
 #define  DISP_DATA_PARTITION_5_6	(1<<6)
+#define  DISP_IPC_ENABLE		(1<<3)
 #define DBUF_CTL	_MMIO(0x45008)
 #define DBUF_CTL	_MMIO(0x45008)
 #define  DBUF_POWER_REQUEST		(1<<31)
 #define  DBUF_POWER_REQUEST		(1<<31)
 #define  DBUF_POWER_STATE		(1<<30)
 #define  DBUF_POWER_STATE		(1<<30)
@@ -6990,6 +6999,7 @@ enum {
 # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC	((1<<10) | (1<<26))
 # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC	((1<<10) | (1<<26))
 # define GEN9_RHWO_OPTIMIZATION_DISABLE		(1<<14)
 # define GEN9_RHWO_OPTIMIZATION_DISABLE		(1<<14)
 #define COMMON_SLICE_CHICKEN2			_MMIO(0x7014)
 #define COMMON_SLICE_CHICKEN2			_MMIO(0x7014)
+# define GEN9_PBE_COMPRESSED_HASH_SELECTION	(1<<13)
 # define GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE (1<<12)
 # define GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE (1<<12)
 # define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8)
 # define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8)
 # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE	(1<<0)
 # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE	(1<<0)
@@ -7469,6 +7479,8 @@ enum {
 #define  FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2)))
 #define  FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2)))
 #define  FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2)))
 #define  FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2)))
 #define  FDI_BC_BIFURCATION_SELECT	(1 << 12)
 #define  FDI_BC_BIFURCATION_SELECT	(1 << 12)
+#define  CHASSIS_CLK_REQ_DURATION_MASK	(0xf << 8)
+#define  CHASSIS_CLK_REQ_DURATION(x)	((x) << 8)
 #define  SPT_PWM_GRANULARITY		(1<<0)
 #define  SPT_PWM_GRANULARITY		(1<<0)
 #define SOUTH_CHICKEN2		_MMIO(0xc2004)
 #define SOUTH_CHICKEN2		_MMIO(0xc2004)
 #define  FDI_MPHY_IOSFSB_RESET_STATUS	(1<<13)
 #define  FDI_MPHY_IOSFSB_RESET_STATUS	(1<<13)
@@ -7953,8 +7965,8 @@ enum {
 #define     GEN7_PCODE_TIMEOUT			0x2
 #define     GEN7_PCODE_TIMEOUT			0x2
 #define     GEN7_PCODE_ILLEGAL_DATA		0x3
 #define     GEN7_PCODE_ILLEGAL_DATA		0x3
 #define     GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE 0x10
 #define     GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE 0x10
-#define	  GEN6_PCODE_WRITE_RC6VIDS		0x4
-#define	  GEN6_PCODE_READ_RC6VIDS		0x5
+#define   GEN6_PCODE_WRITE_RC6VIDS		0x4
+#define   GEN6_PCODE_READ_RC6VIDS		0x5
 #define     GEN6_ENCODE_RC6_VID(mv)		(((mv) - 245) / 5)
 #define     GEN6_ENCODE_RC6_VID(mv)		(((mv) - 245) / 5)
 #define     GEN6_DECODE_RC6_VID(vids)		(((vids) * 5) + 245)
 #define     GEN6_DECODE_RC6_VID(vids)		(((vids) * 5) + 245)
 #define   BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ	0x18
 #define   BDW_PCODE_DISPLAY_FREQ_CHANGE_REQ	0x18
@@ -7973,7 +7985,9 @@ enum {
 #define   GEN6_PCODE_WRITE_D_COMP		0x11
 #define   GEN6_PCODE_WRITE_D_COMP		0x11
 #define   HSW_PCODE_DE_WRITE_FREQ_REQ		0x17
 #define   HSW_PCODE_DE_WRITE_FREQ_REQ		0x17
 #define   DISPLAY_IPS_CONTROL			0x19
 #define   DISPLAY_IPS_CONTROL			0x19
-#define	  HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL	0x1A
+            /* See also IPS_CTL */
+#define     IPS_PCODE_CONTROL			(1 << 30)
+#define   HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL	0x1A
 #define   GEN9_PCODE_SAGV_CONTROL		0x21
 #define   GEN9_PCODE_SAGV_CONTROL		0x21
 #define     GEN9_SAGV_DISABLE			0x0
 #define     GEN9_SAGV_DISABLE			0x0
 #define     GEN9_SAGV_IS_DISABLED		0x1
 #define     GEN9_SAGV_IS_DISABLED		0x1
@@ -8082,6 +8096,7 @@ enum {
 #define   GEN8_SAMPLER_POWER_BYPASS_DIS	(1<<1)
 #define   GEN8_SAMPLER_POWER_BYPASS_DIS	(1<<1)
 
 
 #define GEN9_HALF_SLICE_CHICKEN7	_MMIO(0xe194)
 #define GEN9_HALF_SLICE_CHICKEN7	_MMIO(0xe194)
+#define   GEN9_SAMPLER_HASH_COMPRESSED_READ_ADDR	(1<<8)
 #define   GEN9_ENABLE_YV12_BUGFIX	(1<<4)
 #define   GEN9_ENABLE_YV12_BUGFIX	(1<<4)
 #define   GEN9_ENABLE_GPGPU_PREEMPTION	(1<<2)
 #define   GEN9_ENABLE_GPGPU_PREEMPTION	(1<<2)
 
 
@@ -8594,7 +8609,7 @@ enum skl_power_gate {
 #define  DPLL_CFGCR0_LINK_RATE_3240	(6 << 25)
 #define  DPLL_CFGCR0_LINK_RATE_3240	(6 << 25)
 #define  DPLL_CFGCR0_LINK_RATE_4050	(7 << 25)
 #define  DPLL_CFGCR0_LINK_RATE_4050	(7 << 25)
 #define  DPLL_CFGCR0_DCO_FRACTION_MASK	(0x7fff << 10)
 #define  DPLL_CFGCR0_DCO_FRACTION_MASK	(0x7fff << 10)
-#define  DPLL_CFGCR0_DCO_FRAC_SHIFT	(10)
+#define  DPLL_CFGCR0_DCO_FRACTION_SHIFT	(10)
 #define  DPLL_CFGCR0_DCO_FRACTION(x)	((x) << 10)
 #define  DPLL_CFGCR0_DCO_FRACTION(x)	((x) << 10)
 #define  DPLL_CFGCR0_DCO_INTEGER_MASK	(0x3ff)
 #define  DPLL_CFGCR0_DCO_INTEGER_MASK	(0x3ff)
 #define CNL_DPLL_CFGCR0(pll)		_MMIO_PLL(pll, _CNL_DPLL0_CFGCR0, _CNL_DPLL1_CFGCR0)
 #define CNL_DPLL_CFGCR0(pll)		_MMIO_PLL(pll, _CNL_DPLL0_CFGCR0, _CNL_DPLL1_CFGCR0)
@@ -8801,6 +8816,15 @@ enum skl_power_gate {
 #define MIPIO_TXESC_CLK_DIV2			_MMIO(0x160008)
 #define MIPIO_TXESC_CLK_DIV2			_MMIO(0x160008)
 #define  GLK_TX_ESC_CLK_DIV2_MASK			0x3FF
 #define  GLK_TX_ESC_CLK_DIV2_MASK			0x3FF
 
 
+/* Gen4+ Timestamp and Pipe Frame time stamp registers */
+#define GEN4_TIMESTAMP		_MMIO(0x2358)
+#define ILK_TIMESTAMP_HI	_MMIO(0x70070)
+#define IVB_TIMESTAMP_CTR	_MMIO(0x44070)
+
+#define _PIPE_FRMTMSTMP_A		0x70048
+#define PIPE_FRMTMSTMP(pipe)		\
+			_MMIO_PIPE2(pipe, _PIPE_FRMTMSTMP_A)
+
 /* BXT MIPI clock controls */
 /* BXT MIPI clock controls */
 #define BXT_MAX_VAR_OUTPUT_KHZ			39500
 #define BXT_MAX_VAR_OUTPUT_KHZ			39500
 
 
@@ -9382,4 +9406,8 @@ enum skl_power_gate {
 #define   GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_SKL  0x67F1427F /*    "        " */
 #define   GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_SKL  0x67F1427F /*    "        " */
 #define   GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT  0x5FF101FF /*    "        " */
 #define   GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT  0x5FF101FF /*    "        " */
 
 
+#define MMCD_MISC_CTRL		_MMIO(0x4ddc) /* skl+ */
+#define  MMCD_PCLA		(1 << 31)
+#define  MMCD_HOTSPOT_EN	(1 << 27)
+
 #endif /* _I915_REG_H_ */
 #endif /* _I915_REG_H_ */

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