Browse Source

Merge remote-tracking branch 'airlied/drm-next' into drm-intel-next

Pull in drm-next with Dave's DP MST support so that I can merge some
conflicting patches which also touch the driver load sequencing around
interrupt handling.

Conflicts:
	drivers/gpu/drm/i915/intel_display.c
	drivers/gpu/drm/i915/intel_dp.c

Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Daniel Vetter 11 years ago
parent
commit
4dac3edfe6
100 changed files with 4975 additions and 560 deletions
  1. 9 3
      Documentation/DocBook/drm.tmpl
  2. 30 0
      Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt
  3. 157 35
      drivers/base/component.c
  4. 2 1
      drivers/gpu/drm/Makefile
  5. 10 13
      drivers/gpu/drm/armada/armada_510.c
  6. 164 23
      drivers/gpu/drm/armada/armada_crtc.c
  7. 9 2
      drivers/gpu/drm/armada/armada_crtc.h
  8. 5 8
      drivers/gpu/drm/armada/armada_drm.h
  9. 172 73
      drivers/gpu/drm/armada/armada_drv.c
  10. 2 2
      drivers/gpu/drm/armada/armada_fbdev.c
  11. 2 2
      drivers/gpu/drm/armada/armada_output.c
  12. 1 1
      drivers/gpu/drm/ast/ast_drv.h
  13. 4 2
      drivers/gpu/drm/ast/ast_fb.c
  14. 4 12
      drivers/gpu/drm/ast/ast_mode.c
  15. 2 0
      drivers/gpu/drm/bochs/bochs_drv.c
  16. 4 3
      drivers/gpu/drm/bochs/bochs_fbdev.c
  17. 4 13
      drivers/gpu/drm/bochs/bochs_kms.c
  18. 1 1
      drivers/gpu/drm/bochs/bochs_mm.c
  19. 1 1
      drivers/gpu/drm/bridge/ptn3460.c
  20. 2 0
      drivers/gpu/drm/cirrus/cirrus_drv.c
  21. 1 1
      drivers/gpu/drm/cirrus/cirrus_drv.h
  22. 4 2
      drivers/gpu/drm/cirrus/cirrus_fbdev.c
  23. 2 12
      drivers/gpu/drm/cirrus/cirrus_mode.c
  24. 1 5
      drivers/gpu/drm/drm_buffer.c
  25. 86 4
      drivers/gpu/drm/drm_crtc.c
  26. 1 0
      drivers/gpu/drm/drm_crtc_helper.c
  27. 182 0
      drivers/gpu/drm/drm_debugfs.c
  28. 2715 0
      drivers/gpu/drm/drm_dp_mst_topology.c
  29. 1 1
      drivers/gpu/drm/drm_drv.c
  30. 1 0
      drivers/gpu/drm/drm_edid.c
  31. 3 2
      drivers/gpu/drm/drm_fb_cma_helper.c
  32. 95 20
      drivers/gpu/drm/drm_fb_helper.c
  33. 20 9
      drivers/gpu/drm/drm_gem.c
  34. 1 1
      drivers/gpu/drm/drm_gem_cma_helper.c
  35. 0 2
      drivers/gpu/drm/drm_ioctl.c
  36. 67 0
      drivers/gpu/drm/drm_of.c
  37. 4 3
      drivers/gpu/drm/drm_plane_helper.c
  38. 8 1
      drivers/gpu/drm/drm_probe_helper.c
  39. 1 13
      drivers/gpu/drm/drm_stub.c
  40. 0 2
      drivers/gpu/drm/drm_sysfs.c
  41. 1 1
      drivers/gpu/drm/exynos/exynos_dp_core.c
  42. 4 17
      drivers/gpu/drm/exynos/exynos_drm_connector.c
  43. 2 2
      drivers/gpu/drm/exynos/exynos_drm_dpi.c
  44. 0 4
      drivers/gpu/drm/exynos/exynos_drm_drv.c
  45. 0 2
      drivers/gpu/drm/exynos/exynos_drm_drv.h
  46. 1 1
      drivers/gpu/drm/exynos/exynos_drm_dsi.c
  47. 3 2
      drivers/gpu/drm/exynos/exynos_drm_fbdev.c
  48. 1 1
      drivers/gpu/drm/exynos/exynos_drm_vidi.c
  49. 1 1
      drivers/gpu/drm/exynos/exynos_hdmi.c
  50. 2 2
      drivers/gpu/drm/gma500/cdv_intel_crt.c
  51. 2 2
      drivers/gpu/drm/gma500/cdv_intel_dp.c
  52. 2 2
      drivers/gpu/drm/gma500/cdv_intel_hdmi.c
  53. 2 2
      drivers/gpu/drm/gma500/cdv_intel_lvds.c
  54. 3 2
      drivers/gpu/drm/gma500/framebuffer.c
  55. 1 1
      drivers/gpu/drm/gma500/gtt.c
  56. 2 2
      drivers/gpu/drm/gma500/mdfld_dsi_output.c
  57. 1 1
      drivers/gpu/drm/gma500/oaktrail_hdmi.c
  58. 1 1
      drivers/gpu/drm/gma500/oaktrail_lvds.c
  59. 2 2
      drivers/gpu/drm/gma500/psb_intel_lvds.c
  60. 2 2
      drivers/gpu/drm/gma500/psb_intel_sdvo.c
  61. 1 2
      drivers/gpu/drm/i2c/tda998x_drv.c
  62. 1 0
      drivers/gpu/drm/i915/Makefile
  63. 32 7
      drivers/gpu/drm/i915/i915_debugfs.c
  64. 10 3
      drivers/gpu/drm/i915/i915_dma.c
  65. 17 8
      drivers/gpu/drm/i915/i915_drv.c
  66. 9 1
      drivers/gpu/drm/i915/i915_drv.h
  67. 8 6
      drivers/gpu/drm/i915/i915_irq.c
  68. 9 2
      drivers/gpu/drm/i915/i915_reg.h
  69. 1 1
      drivers/gpu/drm/i915/intel_crt.c
  70. 73 20
      drivers/gpu/drm/i915/intel_ddi.c
  71. 78 33
      drivers/gpu/drm/i915/intel_display.c
  72. 220 18
      drivers/gpu/drm/i915/intel_dp.c
  73. 548 0
      drivers/gpu/drm/i915/intel_dp_mst.c
  74. 44 1
      drivers/gpu/drm/i915/intel_drv.h
  75. 1 1
      drivers/gpu/drm/i915/intel_dsi.c
  76. 1 1
      drivers/gpu/drm/i915/intel_dvo.c
  77. 3 2
      drivers/gpu/drm/i915/intel_fbdev.c
  78. 1 1
      drivers/gpu/drm/i915/intel_hdmi.c
  79. 1 1
      drivers/gpu/drm/i915/intel_lvds.c
  80. 1 0
      drivers/gpu/drm/i915/intel_opregion.c
  81. 4 5
      drivers/gpu/drm/i915/intel_overlay.c
  82. 5 5
      drivers/gpu/drm/i915/intel_sdvo.c
  83. 4 8
      drivers/gpu/drm/i915/intel_sprite.c
  84. 1 1
      drivers/gpu/drm/i915/intel_tv.c
  85. 1 1
      drivers/gpu/drm/mgag200/mgag200_drv.h
  86. 3 2
      drivers/gpu/drm/mgag200/mgag200_fb.c
  87. 3 13
      drivers/gpu/drm/mgag200/mgag200_mode.c
  88. 2 2
      drivers/gpu/drm/msm/hdmi/hdmi_connector.c
  89. 35 48
      drivers/gpu/drm/msm/msm_drv.c
  90. 2 2
      drivers/gpu/drm/msm/msm_fbdev.c
  91. 1 1
      drivers/gpu/drm/msm/msm_gem.c
  92. 3 3
      drivers/gpu/drm/nouveau/nouveau_bo.c
  93. 10 10
      drivers/gpu/drm/nouveau/nouveau_connector.c
  94. 3 2
      drivers/gpu/drm/nouveau/nouveau_fbcon.c
  95. 2 2
      drivers/gpu/drm/nouveau/nouveau_gem.c
  96. 3 0
      drivers/gpu/drm/nouveau/nouveau_ttm.c
  97. 2 2
      drivers/gpu/drm/omapdrm/omap_connector.c
  98. 4 2
      drivers/gpu/drm/omapdrm/omap_dmm_tiler.c
  99. 2 2
      drivers/gpu/drm/omapdrm/omap_fbdev.c
  100. 15 18
      drivers/gpu/drm/omapdrm/omap_gem.c

+ 9 - 3
Documentation/DocBook/drm.tmpl

@@ -1610,7 +1610,7 @@ int max_width, max_height;</synopsis>
           The connector is then registered with a call to
           The connector is then registered with a call to
           <function>drm_connector_init</function> with a pointer to the connector
           <function>drm_connector_init</function> with a pointer to the connector
           functions and a connector type, and exposed through sysfs with a call to
           functions and a connector type, and exposed through sysfs with a call to
-          <function>drm_sysfs_connector_add</function>.
+          <function>drm_connector_register</function>.
         </para>
         </para>
         <para>
         <para>
           Supported connector types are
           Supported connector types are
@@ -1768,7 +1768,7 @@ int max_width, max_height;</synopsis>
 	(<function>drm_encoder_cleanup</function>) and connectors
 	(<function>drm_encoder_cleanup</function>) and connectors
 	(<function>drm_connector_cleanup</function>). Furthermore, connectors
 	(<function>drm_connector_cleanup</function>). Furthermore, connectors
 	that have been added to sysfs must be removed by a call to
 	that have been added to sysfs must be removed by a call to
-	<function>drm_sysfs_connector_remove</function> before calling
+	<function>drm_connector_unregister</function> before calling
 	<function>drm_connector_cleanup</function>.
 	<function>drm_connector_cleanup</function>.
       </para>
       </para>
       <para>
       <para>
@@ -1813,7 +1813,7 @@ void intel_crt_init(struct drm_device *dev)
 	drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
 	drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
 	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 }]]></programlisting>
 }]]></programlisting>
       <para>
       <para>
         In the example above (taken from the i915 driver), a CRTC, connector and
         In the example above (taken from the i915 driver), a CRTC, connector and
@@ -2336,6 +2336,12 @@ void intel_crt_init(struct drm_device *dev)
 !Pdrivers/gpu/drm/drm_dp_helper.c dp helpers
 !Pdrivers/gpu/drm/drm_dp_helper.c dp helpers
 !Iinclude/drm/drm_dp_helper.h
 !Iinclude/drm/drm_dp_helper.h
 !Edrivers/gpu/drm/drm_dp_helper.c
 !Edrivers/gpu/drm/drm_dp_helper.c
+    </sect2>
+    <sect2>
+      <title>Display Port MST Helper Functions Reference</title>
+!Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper
+!Iinclude/drm/drm_dp_mst_helper.h
+!Edrivers/gpu/drm/drm_dp_mst_topology.c
     </sect2>
     </sect2>
     <sect2>
     <sect2>
       <title>EDID Helper Functions Reference</title>
       <title>EDID Helper Functions Reference</title>

+ 30 - 0
Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt

@@ -0,0 +1,30 @@
+Device Tree bindings for Armada DRM CRTC driver
+
+Required properties:
+ - compatible: value should be "marvell,dove-lcd".
+ - reg: base address and size of the LCD controller
+ - interrupts: single interrupt number for the LCD controller
+ - port: video output port with endpoints, as described by graph.txt
+
+Optional properties:
+
+ - clocks: as described by clock-bindings.txt
+ - clock-names: as described by clock-bindings.txt
+	"axiclk" - axi bus clock for pixel clock
+	"plldivider" - pll divider clock for pixel clock
+	"ext_ref_clk0" - external clock 0 for pixel clock
+	"ext_ref_clk1" - external clock 1 for pixel clock
+
+Note: all clocks are optional but at least one must be specified.
+Further clocks may be added in the future according to requirements of
+different SoCs.
+
+Example:
+
+	lcd0: lcd-controller@820000 {
+		compatible = "marvell,dove-lcd";
+		reg = <0x820000 0x1000>;
+		interrupts = <47>;
+		clocks = <&si5351 0>;
+		clock-names = "ext_ref_clk_1";
+	};

+ 157 - 35
drivers/base/component.c

@@ -18,6 +18,15 @@
 #include <linux/mutex.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 
 
+struct component_match {
+	size_t alloc;
+	size_t num;
+	struct {
+		void *data;
+		int (*fn)(struct device *, void *);
+	} compare[0];
+};
+
 struct master {
 struct master {
 	struct list_head node;
 	struct list_head node;
 	struct list_head components;
 	struct list_head components;
@@ -25,6 +34,7 @@ struct master {
 
 
 	const struct component_master_ops *ops;
 	const struct component_master_ops *ops;
 	struct device *dev;
 	struct device *dev;
+	struct component_match *match;
 };
 };
 
 
 struct component {
 struct component {
@@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c)
 	c->master = NULL;
 	c->master = NULL;
 }
 }
 
 
+/*
+ * Add a component to a master, finding the component via the compare
+ * function and compare data.  This is safe to call for duplicate matches
+ * and will not result in the same component being added multiple times.
+ */
 int component_master_add_child(struct master *master,
 int component_master_add_child(struct master *master,
 	int (*compare)(struct device *, void *), void *compare_data)
 	int (*compare)(struct device *, void *), void *compare_data)
 {
 {
@@ -76,11 +91,12 @@ int component_master_add_child(struct master *master,
 	int ret = -ENXIO;
 	int ret = -ENXIO;
 
 
 	list_for_each_entry(c, &component_list, node) {
 	list_for_each_entry(c, &component_list, node) {
-		if (c->master)
+		if (c->master && c->master != master)
 			continue;
 			continue;
 
 
 		if (compare(c->dev, compare_data)) {
 		if (compare(c->dev, compare_data)) {
-			component_attach_master(master, c);
+			if (!c->master)
+				component_attach_master(master, c);
 			ret = 0;
 			ret = 0;
 			break;
 			break;
 		}
 		}
@@ -90,6 +106,34 @@ int component_master_add_child(struct master *master,
 }
 }
 EXPORT_SYMBOL_GPL(component_master_add_child);
 EXPORT_SYMBOL_GPL(component_master_add_child);
 
 
+static int find_components(struct master *master)
+{
+	struct component_match *match = master->match;
+	size_t i;
+	int ret = 0;
+
+	if (!match) {
+		/*
+		 * Search the list of components, looking for components that
+		 * belong to this master, and attach them to the master.
+		 */
+		return master->ops->add_components(master->dev, master);
+	}
+
+	/*
+	 * Scan the array of match functions and attach
+	 * any components which are found to this master.
+	 */
+	for (i = 0; i < match->num; i++) {
+		ret = component_master_add_child(master,
+						 match->compare[i].fn,
+						 match->compare[i].data);
+		if (ret)
+			break;
+	}
+	return ret;
+}
+
 /* Detach all attached components from this master */
 /* Detach all attached components from this master */
 static void master_remove_components(struct master *master)
 static void master_remove_components(struct master *master)
 {
 {
@@ -113,44 +157,44 @@ static void master_remove_components(struct master *master)
 static int try_to_bring_up_master(struct master *master,
 static int try_to_bring_up_master(struct master *master,
 	struct component *component)
 	struct component *component)
 {
 {
-	int ret = 0;
+	int ret;
 
 
-	if (!master->bound) {
-		/*
-		 * Search the list of components, looking for components that
-		 * belong to this master, and attach them to the master.
-		 */
-		if (master->ops->add_components(master->dev, master)) {
-			/* Failed to find all components */
-			master_remove_components(master);
-			ret = 0;
-			goto out;
-		}
+	if (master->bound)
+		return 0;
 
 
-		if (component && component->master != master) {
-			master_remove_components(master);
-			ret = 0;
-			goto out;
-		}
+	/*
+	 * Search the list of components, looking for components that
+	 * belong to this master, and attach them to the master.
+	 */
+	if (find_components(master)) {
+		/* Failed to find all components */
+		ret = 0;
+		goto out;
+	}
 
 
-		if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
-			ret = -ENOMEM;
-			goto out;
-		}
+	if (component && component->master != master) {
+		ret = 0;
+		goto out;
+	}
 
 
-		/* Found all components */
-		ret = master->ops->bind(master->dev);
-		if (ret < 0) {
-			devres_release_group(master->dev, NULL);
-			dev_info(master->dev, "master bind failed: %d\n", ret);
-			master_remove_components(master);
-			goto out;
-		}
+	if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
+		ret = -ENOMEM;
+		goto out;
+	}
 
 
-		master->bound = true;
-		ret = 1;
+	/* Found all components */
+	ret = master->ops->bind(master->dev);
+	if (ret < 0) {
+		devres_release_group(master->dev, NULL);
+		dev_info(master->dev, "master bind failed: %d\n", ret);
+		goto out;
 	}
 	}
+
+	master->bound = true;
+	return 1;
+
 out:
 out:
+	master_remove_components(master);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -180,18 +224,89 @@ static void take_down_master(struct master *master)
 	master_remove_components(master);
 	master_remove_components(master);
 }
 }
 
 
-int component_master_add(struct device *dev,
-	const struct component_master_ops *ops)
+static size_t component_match_size(size_t num)
+{
+	return offsetof(struct component_match, compare[num]);
+}
+
+static struct component_match *component_match_realloc(struct device *dev,
+	struct component_match *match, size_t num)
+{
+	struct component_match *new;
+
+	if (match && match->alloc == num)
+		return match;
+
+	new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
+	if (!new)
+		return ERR_PTR(-ENOMEM);
+
+	if (match) {
+		memcpy(new, match, component_match_size(min(match->num, num)));
+		devm_kfree(dev, match);
+	} else {
+		new->num = 0;
+	}
+
+	new->alloc = num;
+
+	return new;
+}
+
+/*
+ * Add a component to be matched.
+ *
+ * The match array is first created or extended if necessary.
+ */
+void component_match_add(struct device *dev, struct component_match **matchptr,
+	int (*compare)(struct device *, void *), void *compare_data)
+{
+	struct component_match *match = *matchptr;
+
+	if (IS_ERR(match))
+		return;
+
+	if (!match || match->num == match->alloc) {
+		size_t new_size = match ? match->alloc + 16 : 15;
+
+		match = component_match_realloc(dev, match, new_size);
+
+		*matchptr = match;
+
+		if (IS_ERR(match))
+			return;
+	}
+
+	match->compare[match->num].fn = compare;
+	match->compare[match->num].data = compare_data;
+	match->num++;
+}
+EXPORT_SYMBOL(component_match_add);
+
+int component_master_add_with_match(struct device *dev,
+	const struct component_master_ops *ops,
+	struct component_match *match)
 {
 {
 	struct master *master;
 	struct master *master;
 	int ret;
 	int ret;
 
 
+	if (ops->add_components && match)
+		return -EINVAL;
+
+	if (match) {
+		/* Reallocate the match array for its true size */
+		match = component_match_realloc(dev, match, match->num);
+		if (IS_ERR(match))
+			return PTR_ERR(match);
+	}
+
 	master = kzalloc(sizeof(*master), GFP_KERNEL);
 	master = kzalloc(sizeof(*master), GFP_KERNEL);
 	if (!master)
 	if (!master)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	master->dev = dev;
 	master->dev = dev;
 	master->ops = ops;
 	master->ops = ops;
+	master->match = match;
 	INIT_LIST_HEAD(&master->components);
 	INIT_LIST_HEAD(&master->components);
 
 
 	/* Add to the list of available masters. */
 	/* Add to the list of available masters. */
@@ -209,6 +324,13 @@ int component_master_add(struct device *dev,
 
 
 	return ret < 0 ? ret : 0;
 	return ret < 0 ? ret : 0;
 }
 }
+EXPORT_SYMBOL_GPL(component_master_add_with_match);
+
+int component_master_add(struct device *dev,
+	const struct component_master_ops *ops)
+{
+	return component_master_add_with_match(dev, ops, NULL);
+}
 EXPORT_SYMBOL_GPL(component_master_add);
 EXPORT_SYMBOL_GPL(component_master_add);
 
 
 void component_master_del(struct device *dev,
 void component_master_del(struct device *dev,

+ 2 - 1
drivers/gpu/drm/Makefile

@@ -20,11 +20,12 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
 drm-$(CONFIG_PCI) += ati_pcigart.o
 drm-$(CONFIG_PCI) += ati_pcigart.o
 drm-$(CONFIG_DRM_PANEL) += drm_panel.o
 drm-$(CONFIG_DRM_PANEL) += drm_panel.o
+drm-$(CONFIG_OF) += drm_of.o
 
 
 drm-usb-y   := drm_usb.o
 drm-usb-y   := drm_usb.o
 
 
 drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
 drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
-		drm_plane_helper.o
+		drm_plane_helper.o drm_dp_mst_topology.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
 drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o

+ 10 - 13
drivers/gpu/drm/armada/armada_510.c

@@ -15,20 +15,19 @@
 #include "armada_drm.h"
 #include "armada_drm.h"
 #include "armada_hw.h"
 #include "armada_hw.h"
 
 
-static int armada510_init(struct armada_private *priv, struct device *dev)
+static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
 {
 {
-	priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
+	struct clk *clk;
 
 
-	if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
-		priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
+	clk = devm_clk_get(dev, "ext_ref_clk1");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk);
 
 
-	return PTR_RET(priv->extclk[0]);
-}
+	dcrtc->extclk[0] = clk;
 
 
-static int armada510_crtc_init(struct armada_crtc *dcrtc)
-{
 	/* Lower the watermark so to eliminate jitter at higher bandwidths */
 	/* Lower the watermark so to eliminate jitter at higher bandwidths */
 	armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
 	armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc)
 static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
 static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
 	const struct drm_display_mode *mode, uint32_t *sclk)
 	const struct drm_display_mode *mode, uint32_t *sclk)
 {
 {
-	struct armada_private *priv = dcrtc->crtc.dev->dev_private;
-	struct clk *clk = priv->extclk[0];
+	struct clk *clk = dcrtc->extclk[0];
 	int ret;
 	int ret;
 
 
 	if (dcrtc->num == 1)
 	if (dcrtc->num == 1)
@@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
 const struct armada_variant armada510_ops = {
 const struct armada_variant armada510_ops = {
 	.has_spu_adv_reg = true,
 	.has_spu_adv_reg = true,
 	.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
 	.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
-	.init = armada510_init,
-	.crtc_init = armada510_crtc_init,
-	.crtc_compute_clock = armada510_crtc_compute_clock,
+	.init = armada510_crtc_init,
+	.compute_clock = armada510_crtc_compute_clock,
 };
 };

+ 164 - 23
drivers/gpu/drm/armada/armada_crtc.c

@@ -7,6 +7,9 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  */
  */
 #include <linux/clk.h>
 #include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
 #include <drm/drmP.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include "armada_crtc.h"
 #include "armada_crtc.h"
@@ -332,24 +335,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc)
 static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
 static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
 	const struct drm_display_mode *mode, struct drm_display_mode *adj)
 	const struct drm_display_mode *mode, struct drm_display_mode *adj)
 {
 {
-	struct armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	int ret;
 	int ret;
 
 
 	/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
 	/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
-	if (!priv->variant->has_spu_adv_reg &&
+	if (!dcrtc->variant->has_spu_adv_reg &&
 	    adj->flags & DRM_MODE_FLAG_INTERLACE)
 	    adj->flags & DRM_MODE_FLAG_INTERLACE)
 		return false;
 		return false;
 
 
 	/* Check whether the display mode is possible */
 	/* Check whether the display mode is possible */
-	ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
+	ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL);
 	if (ret)
 	if (ret)
 		return false;
 		return false;
 
 
 	return true;
 	return true;
 }
 }
 
 
-void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
+static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 {
 {
 	struct armada_vbl_event *e, *n;
 	struct armada_vbl_event *e, *n;
 	void __iomem *base = dcrtc->base;
 	void __iomem *base = dcrtc->base;
@@ -410,6 +412,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
 	}
 	}
 }
 }
 
 
+static irqreturn_t armada_drm_irq(int irq, void *arg)
+{
+	struct armada_crtc *dcrtc = arg;
+	u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
+
+	/*
+	 * This is rediculous - rather than writing bits to clear, we
+	 * have to set the actual status register value.  This is racy.
+	 */
+	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+	/* Mask out those interrupts we haven't enabled */
+	v = stat & dcrtc->irq_ena;
+
+	if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
+		armada_drm_crtc_irq(dcrtc, stat);
+		return IRQ_HANDLED;
+	}
+	return IRQ_NONE;
+}
+
 /* These are locked by dev->vbl_lock */
 /* These are locked by dev->vbl_lock */
 void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
 void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
 {
 {
@@ -470,7 +493,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	struct drm_display_mode *mode, struct drm_display_mode *adj,
 	struct drm_display_mode *mode, struct drm_display_mode *adj,
 	int x, int y, struct drm_framebuffer *old_fb)
 	int x, int y, struct drm_framebuffer *old_fb)
 {
 {
-	struct armada_private *priv = crtc->dev->dev_private;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_regs regs[17];
 	struct armada_regs regs[17];
 	uint32_t lm, rm, tm, bm, val, sclk;
 	uint32_t lm, rm, tm, bm, val, sclk;
@@ -515,7 +537,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	}
 	}
 
 
 	/* Now compute the divider for real */
 	/* Now compute the divider for real */
-	priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
+	dcrtc->variant->compute_clock(dcrtc, adj, &sclk);
 
 
 	/* Ensure graphic fifo is enabled */
 	/* Ensure graphic fifo is enabled */
 	armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
 	armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
@@ -537,7 +559,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	dcrtc->v[1].spu_v_porch = tm << 16 | bm;
 	dcrtc->v[1].spu_v_porch = tm << 16 | bm;
 	val = adj->crtc_hsync_start;
 	val = adj->crtc_hsync_start;
 	dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
 	dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
-		priv->variant->spu_adv_reg;
+		dcrtc->variant->spu_adv_reg;
 
 
 	if (interlaced) {
 	if (interlaced) {
 		/* Odd interlaced frame */
 		/* Odd interlaced frame */
@@ -546,7 +568,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 		dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
 		dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
 		val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
 		val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
 		dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
 		dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
-			priv->variant->spu_adv_reg;
+			dcrtc->variant->spu_adv_reg;
 	} else {
 	} else {
 		dcrtc->v[0] = dcrtc->v[1];
 		dcrtc->v[0] = dcrtc->v[1];
 	}
 	}
@@ -561,7 +583,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
 	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
 	armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
 			   LCD_SPUT_V_H_TOTAL);
 			   LCD_SPUT_V_H_TOTAL);
 
 
-	if (priv->variant->has_spu_adv_reg) {
+	if (dcrtc->variant->has_spu_adv_reg) {
 		armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
 		armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
 				     ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
 				     ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
 				     ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
 				     ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
@@ -805,12 +827,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
 {
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_device *dev = crtc->dev;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
-	struct armada_private *priv = crtc->dev->dev_private;
 	struct armada_gem_object *obj = NULL;
 	struct armada_gem_object *obj = NULL;
 	int ret;
 	int ret;
 
 
 	/* If no cursor support, replicate drm's return value */
 	/* If no cursor support, replicate drm's return value */
-	if (!priv->variant->has_spu_adv_reg)
+	if (!dcrtc->variant->has_spu_adv_reg)
 		return -ENXIO;
 		return -ENXIO;
 
 
 	if (handle && w > 0 && h > 0) {
 	if (handle && w > 0 && h > 0) {
@@ -858,11 +879,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
 {
 {
 	struct drm_device *dev = crtc->dev;
 	struct drm_device *dev = crtc->dev;
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
 	struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
-	struct armada_private *priv = crtc->dev->dev_private;
 	int ret;
 	int ret;
 
 
 	/* If no cursor support, replicate drm's return value */
 	/* If no cursor support, replicate drm's return value */
-	if (!priv->variant->has_spu_adv_reg)
+	if (!dcrtc->variant->has_spu_adv_reg)
 		return -EFAULT;
 		return -EFAULT;
 
 
 	mutex_lock(&dev->struct_mutex);
 	mutex_lock(&dev->struct_mutex);
@@ -888,6 +908,10 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
 	if (!IS_ERR(dcrtc->clk))
 	if (!IS_ERR(dcrtc->clk))
 		clk_disable_unprepare(dcrtc->clk);
 		clk_disable_unprepare(dcrtc->clk);
 
 
+	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA);
+
+	of_node_put(dcrtc->crtc.port);
+
 	kfree(dcrtc);
 	kfree(dcrtc);
 }
 }
 
 
@@ -1027,19 +1051,20 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
 	return 0;
 	return 0;
 }
 }
 
 
-int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
-	struct resource *res)
+int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
+	struct resource *res, int irq, const struct armada_variant *variant,
+	struct device_node *port)
 {
 {
-	struct armada_private *priv = dev->dev_private;
+	struct armada_private *priv = drm->dev_private;
 	struct armada_crtc *dcrtc;
 	struct armada_crtc *dcrtc;
 	void __iomem *base;
 	void __iomem *base;
 	int ret;
 	int ret;
 
 
-	ret = armada_drm_crtc_create_properties(dev);
+	ret = armada_drm_crtc_create_properties(drm);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
-	base = devm_request_and_ioremap(dev->dev, res);
+	base = devm_request_and_ioremap(dev, res);
 	if (!base) {
 	if (!base) {
 		DRM_ERROR("failed to ioremap register\n");
 		DRM_ERROR("failed to ioremap register\n");
 		return -ENOMEM;
 		return -ENOMEM;
@@ -1051,8 +1076,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
+	if (dev != drm->dev)
+		dev_set_drvdata(dev, dcrtc);
+
+	dcrtc->variant = variant;
 	dcrtc->base = base;
 	dcrtc->base = base;
-	dcrtc->num = num;
+	dcrtc->num = drm->mode_config.num_crtc;
 	dcrtc->clk = ERR_PTR(-EINVAL);
 	dcrtc->clk = ERR_PTR(-EINVAL);
 	dcrtc->csc_yuv_mode = CSC_AUTO;
 	dcrtc->csc_yuv_mode = CSC_AUTO;
 	dcrtc->csc_rgb_mode = CSC_AUTO;
 	dcrtc->csc_rgb_mode = CSC_AUTO;
@@ -1074,9 +1103,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 		       CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
 		       CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
 	writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
 	writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
 	writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
 	writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
+	writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
+	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
+
+	ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc",
+			       dcrtc);
+	if (ret < 0) {
+		kfree(dcrtc);
+		return ret;
+	}
 
 
-	if (priv->variant->crtc_init) {
-		ret = priv->variant->crtc_init(dcrtc);
+	if (dcrtc->variant->init) {
+		ret = dcrtc->variant->init(dcrtc, dev);
 		if (ret) {
 		if (ret) {
 			kfree(dcrtc);
 			kfree(dcrtc);
 			return ret;
 			return ret;
@@ -1088,7 +1126,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 
 
 	priv->dcrtc[dcrtc->num] = dcrtc;
 	priv->dcrtc[dcrtc->num] = dcrtc;
 
 
-	drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
+	dcrtc->crtc.port = port;
+	drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
 	drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
 	drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
 
 
 	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
 	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
@@ -1096,5 +1135,107 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
 	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
 	drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
 				   dcrtc->csc_rgb_mode);
 				   dcrtc->csc_rgb_mode);
 
 
-	return armada_overlay_plane_create(dev, 1 << dcrtc->num);
+	return armada_overlay_plane_create(drm, 1 << dcrtc->num);
+}
+
+static int
+armada_lcd_bind(struct device *dev, struct device *master, void *data)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct drm_device *drm = data;
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	int irq = platform_get_irq(pdev, 0);
+	const struct armada_variant *variant;
+	struct device_node *port = NULL;
+
+	if (irq < 0)
+		return irq;
+
+	if (!dev->of_node) {
+		const struct platform_device_id *id;
+
+		id = platform_get_device_id(pdev);
+		if (!id)
+			return -ENXIO;
+
+		variant = (const struct armada_variant *)id->driver_data;
+	} else {
+		const struct of_device_id *match;
+		struct device_node *np, *parent = dev->of_node;
+
+		match = of_match_device(dev->driver->of_match_table, dev);
+		if (!match)
+			return -ENXIO;
+
+		np = of_get_child_by_name(parent, "ports");
+		if (np)
+			parent = np;
+		port = of_get_child_by_name(parent, "port");
+		of_node_put(np);
+		if (!port) {
+			dev_err(dev, "no port node found in %s\n",
+				parent->full_name);
+			return -ENXIO;
+		}
+
+		variant = match->data;
+	}
+
+	return armada_drm_crtc_create(drm, dev, res, irq, variant, port);
+}
+
+static void
+armada_lcd_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct armada_crtc *dcrtc = dev_get_drvdata(dev);
+
+	armada_drm_crtc_destroy(&dcrtc->crtc);
 }
 }
+
+static const struct component_ops armada_lcd_ops = {
+	.bind = armada_lcd_bind,
+	.unbind = armada_lcd_unbind,
+};
+
+static int armada_lcd_probe(struct platform_device *pdev)
+{
+	return component_add(&pdev->dev, &armada_lcd_ops);
+}
+
+static int armada_lcd_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &armada_lcd_ops);
+	return 0;
+}
+
+static struct of_device_id armada_lcd_of_match[] = {
+	{
+		.compatible	= "marvell,dove-lcd",
+		.data		= &armada510_ops,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, armada_lcd_of_match);
+
+static const struct platform_device_id armada_lcd_platform_ids[] = {
+	{
+		.name		= "armada-lcd",
+		.driver_data	= (unsigned long)&armada510_ops,
+	}, {
+		.name		= "armada-510-lcd",
+		.driver_data	= (unsigned long)&armada510_ops,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids);
+
+struct platform_driver armada_lcd_platform_driver = {
+	.probe	= armada_lcd_probe,
+	.remove	= armada_lcd_remove,
+	.driver = {
+		.name	= "armada-lcd",
+		.owner	=  THIS_MODULE,
+		.of_match_table = armada_lcd_of_match,
+	},
+	.id_table = armada_lcd_platform_ids,
+};

+ 9 - 2
drivers/gpu/drm/armada/armada_crtc.h

@@ -32,12 +32,15 @@ struct armada_regs {
 	armada_reg_queue_mod(_r, _i, 0, 0, ~0)
 	armada_reg_queue_mod(_r, _i, 0, 0, ~0)
 
 
 struct armada_frame_work;
 struct armada_frame_work;
+struct armada_variant;
 
 
 struct armada_crtc {
 struct armada_crtc {
 	struct drm_crtc		crtc;
 	struct drm_crtc		crtc;
+	const struct armada_variant *variant;
 	unsigned		num;
 	unsigned		num;
 	void __iomem		*base;
 	void __iomem		*base;
 	struct clk		*clk;
 	struct clk		*clk;
+	struct clk		*extclk[2];
 	struct {
 	struct {
 		uint32_t	spu_v_h_total;
 		uint32_t	spu_v_h_total;
 		uint32_t	spu_v_porch;
 		uint32_t	spu_v_porch;
@@ -72,12 +75,16 @@ struct armada_crtc {
 };
 };
 #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
 #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
 
 
-int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
+struct device_node;
+int armada_drm_crtc_create(struct drm_device *, struct device *,
+	struct resource *, int, const struct armada_variant *,
+	struct device_node *);
 void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
 void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
 void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
 void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
-void armada_drm_crtc_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
 void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
 void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
 
 
+extern struct platform_driver armada_lcd_platform_driver;
+
 #endif
 #endif

+ 5 - 8
drivers/gpu/drm/armada/armada_drm.h

@@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
 struct armada_private;
 struct armada_private;
 
 
 struct armada_variant {
 struct armada_variant {
-	bool	has_spu_adv_reg;
+	bool has_spu_adv_reg;
 	uint32_t spu_adv_reg;
 	uint32_t spu_adv_reg;
-	int (*init)(struct armada_private *, struct device *);
-	int (*crtc_init)(struct armada_crtc *);
-	int (*crtc_compute_clock)(struct armada_crtc *,
-				  const struct drm_display_mode *,
-				  uint32_t *);
+	int (*init)(struct armada_crtc *, struct device *);
+	int (*compute_clock)(struct armada_crtc *,
+			     const struct drm_display_mode *,
+			     uint32_t *);
 };
 };
 
 
 /* Variant ops */
 /* Variant ops */
 extern const struct armada_variant armada510_ops;
 extern const struct armada_variant armada510_ops;
 
 
 struct armada_private {
 struct armada_private {
-	const struct armada_variant *variant;
 	struct work_struct	fb_unref_work;
 	struct work_struct	fb_unref_work;
 	DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
 	DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
 	struct drm_fb_helper	*fbdev;
 	struct drm_fb_helper	*fbdev;
 	struct armada_crtc	*dcrtc[2];
 	struct armada_crtc	*dcrtc[2];
 	struct drm_mm		linear;
 	struct drm_mm		linear;
-	struct clk		*extclk[2];
 	struct drm_property	*csc_yuv_prop;
 	struct drm_property	*csc_yuv_prop;
 	struct drm_property	*csc_rgb_prop;
 	struct drm_property	*csc_rgb_prop;
 	struct drm_property	*colorkey_prop;
 	struct drm_property	*colorkey_prop;

+ 172 - 73
drivers/gpu/drm/armada/armada_drv.c

@@ -6,7 +6,9 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  */
  */
 #include <linux/clk.h>
 #include <linux/clk.h>
+#include <linux/component.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/of_graph.h>
 #include <drm/drmP.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include "armada_crtc.h"
 #include "armada_crtc.h"
@@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = {
 };
 };
 #endif
 #endif
 
 
+static bool is_componentized(struct device *dev)
+{
+	return dev->of_node || dev->platform_data;
+}
+
 static void armada_drm_unref_work(struct work_struct *work)
 static void armada_drm_unref_work(struct work_struct *work)
 {
 {
 	struct armada_private *priv =
 	struct armada_private *priv =
@@ -85,6 +92,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
 static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 {
 {
 	const struct platform_device_id *id;
 	const struct platform_device_id *id;
+	const struct armada_variant *variant;
 	struct armada_private *priv;
 	struct armada_private *priv;
 	struct resource *res[ARRAY_SIZE(priv->dcrtc)];
 	struct resource *res[ARRAY_SIZE(priv->dcrtc)];
 	struct resource *mem = NULL;
 	struct resource *mem = NULL;
@@ -107,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 			return -EINVAL;
 			return -EINVAL;
 	}
 	}
 
 
-	if (!res[0] || !mem)
+	if (!mem)
 		return -ENXIO;
 		return -ENXIO;
 
 
 	if (!devm_request_mem_region(dev->dev, mem->start,
 	if (!devm_request_mem_region(dev->dev, mem->start,
@@ -128,11 +136,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 	if (!id)
 	if (!id)
 		return -ENXIO;
 		return -ENXIO;
 
 
-	priv->variant = (struct armada_variant *)id->driver_data;
-
-	ret = priv->variant->init(priv, dev->dev);
-	if (ret)
-		return ret;
+	variant = (const struct armada_variant *)id->driver_data;
 
 
 	INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
 	INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
 	INIT_KFIFO(priv->fb_unref);
 	INIT_KFIFO(priv->fb_unref);
@@ -155,40 +159,50 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
 
 
 	/* Create all LCD controllers */
 	/* Create all LCD controllers */
 	for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
 	for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
+		int irq;
+
 		if (!res[n])
 		if (!res[n])
 			break;
 			break;
 
 
-		ret = armada_drm_crtc_create(dev, n, res[n]);
+		irq = platform_get_irq(dev->platformdev, n);
+		if (irq < 0)
+			goto err_kms;
+
+		ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq,
+					     variant, NULL);
 		if (ret)
 		if (ret)
 			goto err_kms;
 			goto err_kms;
 	}
 	}
 
 
+	if (is_componentized(dev->dev)) {
+		ret = component_bind_all(dev->dev, dev);
+		if (ret)
+			goto err_kms;
+	} else {
 #ifdef CONFIG_DRM_ARMADA_TDA1998X
 #ifdef CONFIG_DRM_ARMADA_TDA1998X
-	ret = armada_drm_connector_slave_create(dev, &tda19988_config);
-	if (ret)
-		goto err_kms;
+		ret = armada_drm_connector_slave_create(dev, &tda19988_config);
+		if (ret)
+			goto err_kms;
 #endif
 #endif
+	}
 
 
-	ret = drm_vblank_init(dev, n);
-	if (ret)
-		goto err_kms;
-
-	ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
+	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
 	if (ret)
 	if (ret)
-		goto err_kms;
+		goto err_comp;
 
 
 	dev->vblank_disable_allowed = 1;
 	dev->vblank_disable_allowed = 1;
 
 
 	ret = armada_fbdev_init(dev);
 	ret = armada_fbdev_init(dev);
 	if (ret)
 	if (ret)
-		goto err_irq;
+		goto err_comp;
 
 
 	drm_kms_helper_poll_init(dev);
 	drm_kms_helper_poll_init(dev);
 
 
 	return 0;
 	return 0;
 
 
- err_irq:
-	drm_irq_uninstall(dev);
+ err_comp:
+	if (is_componentized(dev->dev))
+		component_unbind_all(dev->dev, dev);
  err_kms:
  err_kms:
 	drm_mode_config_cleanup(dev);
 	drm_mode_config_cleanup(dev);
 	drm_mm_takedown(&priv->linear);
 	drm_mm_takedown(&priv->linear);
@@ -203,7 +217,10 @@ static int armada_drm_unload(struct drm_device *dev)
 
 
 	drm_kms_helper_poll_fini(dev);
 	drm_kms_helper_poll_fini(dev);
 	armada_fbdev_fini(dev);
 	armada_fbdev_fini(dev);
-	drm_irq_uninstall(dev);
+
+	if (is_componentized(dev->dev))
+		component_unbind_all(dev->dev, dev);
+
 	drm_mode_config_cleanup(dev);
 	drm_mode_config_cleanup(dev);
 	drm_mm_takedown(&priv->linear);
 	drm_mm_takedown(&priv->linear);
 	flush_work(&priv->fb_unref_work);
 	flush_work(&priv->fb_unref_work);
@@ -259,52 +276,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
 	armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
 	armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
 }
 }
 
 
-static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
-{
-	struct drm_device *dev = arg;
-	struct armada_private *priv = dev->dev_private;
-	struct armada_crtc *dcrtc = priv->dcrtc[0];
-	uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
-	irqreturn_t handled = IRQ_NONE;
-
-	/*
-	 * This is rediculous - rather than writing bits to clear, we
-	 * have to set the actual status register value.  This is racy.
-	 */
-	writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
-
-	/* Mask out those interrupts we haven't enabled */
-	v = stat & dcrtc->irq_ena;
-
-	if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
-		armada_drm_crtc_irq(dcrtc, stat);
-		handled = IRQ_HANDLED;
-	}
-
-	return handled;
-}
-
-static int armada_drm_irq_postinstall(struct drm_device *dev)
-{
-	struct armada_private *priv = dev->dev_private;
-	struct armada_crtc *dcrtc = priv->dcrtc[0];
-
-	spin_lock_irq(&dev->vbl_lock);
-	writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
-	writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
-	spin_unlock_irq(&dev->vbl_lock);
-
-	return 0;
-}
-
-static void armada_drm_irq_uninstall(struct drm_device *dev)
-{
-	struct armada_private *priv = dev->dev_private;
-	struct armada_crtc *dcrtc = priv->dcrtc[0];
-
-	writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
-}
-
 static struct drm_ioctl_desc armada_ioctls[] = {
 static struct drm_ioctl_desc armada_ioctls[] = {
 	DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
 	DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
 		DRM_UNLOCKED),
 		DRM_UNLOCKED),
@@ -340,9 +311,6 @@ static struct drm_driver armada_drm_driver = {
 	.get_vblank_counter	= drm_vblank_count,
 	.get_vblank_counter	= drm_vblank_count,
 	.enable_vblank		= armada_drm_enable_vblank,
 	.enable_vblank		= armada_drm_enable_vblank,
 	.disable_vblank		= armada_drm_disable_vblank,
 	.disable_vblank		= armada_drm_disable_vblank,
-	.irq_handler		= armada_drm_irq_handler,
-	.irq_postinstall	= armada_drm_irq_postinstall,
-	.irq_uninstall		= armada_drm_irq_uninstall,
 #ifdef CONFIG_DEBUG_FS
 #ifdef CONFIG_DEBUG_FS
 	.debugfs_init		= armada_drm_debugfs_init,
 	.debugfs_init		= armada_drm_debugfs_init,
 	.debugfs_cleanup	= armada_drm_debugfs_cleanup,
 	.debugfs_cleanup	= armada_drm_debugfs_cleanup,
@@ -362,19 +330,140 @@ static struct drm_driver armada_drm_driver = {
 	.desc			= "Armada SoC DRM",
 	.desc			= "Armada SoC DRM",
 	.date			= "20120730",
 	.date			= "20120730",
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
-				  DRIVER_HAVE_IRQ | DRIVER_PRIME,
+				  DRIVER_PRIME,
 	.ioctls			= armada_ioctls,
 	.ioctls			= armada_ioctls,
 	.fops			= &armada_drm_fops,
 	.fops			= &armada_drm_fops,
 };
 };
 
 
+static int armada_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&armada_drm_driver, to_platform_device(dev));
+}
+
+static void armada_drm_unbind(struct device *dev)
+{
+	drm_put_dev(dev_get_drvdata(dev));
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int compare_dev_name(struct device *dev, void *data)
+{
+	const char *name = data;
+	return !strcmp(dev_name(dev), name);
+}
+
+static void armada_add_endpoints(struct device *dev,
+	struct component_match **match, struct device_node *port)
+{
+	struct device_node *ep, *remote;
+
+	for_each_child_of_node(port, ep) {
+		remote = of_graph_get_remote_port_parent(ep);
+		if (!remote || !of_device_is_available(remote)) {
+			of_node_put(remote);
+			continue;
+		} else if (!of_device_is_available(remote->parent)) {
+			dev_warn(dev, "parent device of %s is not available\n",
+				 remote->full_name);
+			of_node_put(remote);
+			continue;
+		}
+
+		component_match_add(dev, match, compare_of, remote);
+		of_node_put(remote);
+	}
+}
+
+static int armada_drm_find_components(struct device *dev,
+	struct component_match **match)
+{
+	struct device_node *port;
+	int i;
+
+	if (dev->of_node) {
+		struct device_node *np = dev->of_node;
+
+		for (i = 0; ; i++) {
+			port = of_parse_phandle(np, "ports", i);
+			if (!port)
+				break;
+
+			component_match_add(dev, match, compare_of, port);
+			of_node_put(port);
+		}
+
+		if (i == 0) {
+			dev_err(dev, "missing 'ports' property\n");
+			return -ENODEV;
+		}
+
+		for (i = 0; ; i++) {
+			port = of_parse_phandle(np, "ports", i);
+			if (!port)
+				break;
+
+			armada_add_endpoints(dev, match, port);
+			of_node_put(port);
+		}
+	} else if (dev->platform_data) {
+		char **devices = dev->platform_data;
+		struct device *d;
+
+		for (i = 0; devices[i]; i++)
+			component_match_add(dev, match, compare_dev_name,
+					    devices[i]);
+
+		if (i == 0) {
+			dev_err(dev, "missing 'ports' property\n");
+			return -ENODEV;
+		}
+
+		for (i = 0; devices[i]; i++) {
+			d = bus_find_device_by_name(&platform_bus_type, NULL,
+					devices[i]);
+			if (d && d->of_node) {
+				for_each_child_of_node(d->of_node, port)
+					armada_add_endpoints(dev, match, port);
+			}
+			put_device(d);
+		}
+	}
+
+	return 0;
+}
+
+static const struct component_master_ops armada_master_ops = {
+	.bind = armada_drm_bind,
+	.unbind = armada_drm_unbind,
+};
+
 static int armada_drm_probe(struct platform_device *pdev)
 static int armada_drm_probe(struct platform_device *pdev)
 {
 {
-	return drm_platform_init(&armada_drm_driver, pdev);
+	if (is_componentized(&pdev->dev)) {
+		struct component_match *match = NULL;
+		int ret;
+
+		ret = armada_drm_find_components(&pdev->dev, &match);
+		if (ret < 0)
+			return ret;
+
+		return component_master_add_with_match(&pdev->dev,
+				&armada_master_ops, match);
+	} else {
+		return drm_platform_init(&armada_drm_driver, pdev);
+	}
 }
 }
 
 
 static int armada_drm_remove(struct platform_device *pdev)
 static int armada_drm_remove(struct platform_device *pdev)
 {
 {
-	drm_put_dev(platform_get_drvdata(pdev));
+	if (is_componentized(&pdev->dev))
+		component_master_del(&pdev->dev, &armada_master_ops);
+	else
+		drm_put_dev(platform_get_drvdata(pdev));
 	return 0;
 	return 0;
 }
 }
 
 
@@ -402,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = {
 
 
 static int __init armada_drm_init(void)
 static int __init armada_drm_init(void)
 {
 {
+	int ret;
+
 	armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
 	armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
-	return platform_driver_register(&armada_drm_platform_driver);
+
+	ret = platform_driver_register(&armada_lcd_platform_driver);
+	if (ret)
+		return ret;
+	ret = platform_driver_register(&armada_drm_platform_driver);
+	if (ret)
+		platform_driver_unregister(&armada_lcd_platform_driver);
+	return ret;
 }
 }
 module_init(armada_drm_init);
 module_init(armada_drm_init);
 
 
 static void __exit armada_drm_exit(void)
 static void __exit armada_drm_exit(void)
 {
 {
 	platform_driver_unregister(&armada_drm_platform_driver);
 	platform_driver_unregister(&armada_drm_platform_driver);
+	platform_driver_unregister(&armada_lcd_platform_driver);
 }
 }
 module_exit(armada_drm_exit);
 module_exit(armada_drm_exit);
 
 

+ 2 - 2
drivers/gpu/drm/armada/armada_fbdev.c

@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
 	return ret;
 	return ret;
 }
 }
 
 
-static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
 	.gamma_set	= armada_drm_crtc_gamma_set,
 	.gamma_set	= armada_drm_crtc_gamma_set,
 	.gamma_get	= armada_drm_crtc_gamma_get,
 	.gamma_get	= armada_drm_crtc_gamma_get,
 	.fb_probe	= armada_fb_probe,
 	.fb_probe	= armada_fb_probe,
@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
 
 
 	priv->fbdev = fbh;
 	priv->fbdev = fbh;
 
 
-	fbh->funcs = &armada_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
 
 
 	ret = drm_fb_helper_init(dev, fbh, 1, 1);
 	ret = drm_fb_helper_init(dev, fbh, 1, 1);
 	if (ret) {
 	if (ret) {

+ 2 - 2
drivers/gpu/drm/armada/armada_output.c

@@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
 {
 {
 	struct armada_connector *dconn = drm_to_armada_conn(conn);
 	struct armada_connector *dconn = drm_to_armada_conn(conn);
 
 
-	drm_sysfs_connector_remove(conn);
+	drm_connector_unregister(conn);
 	drm_connector_cleanup(conn);
 	drm_connector_cleanup(conn);
 	kfree(dconn);
 	kfree(dconn);
 }
 }
@@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev,
 	if (ret)
 	if (ret)
 		goto err_conn;
 		goto err_conn;
 
 
-	ret = drm_sysfs_connector_add(&dconn->conn);
+	ret = drm_connector_register(&dconn->conn);
 	if (ret)
 	if (ret)
 		goto err_sysfs;
 		goto err_sysfs;
 
 

+ 1 - 1
drivers/gpu/drm/ast/ast_drv.h

@@ -362,7 +362,7 @@ static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+	ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
 	if (ret) {
 	if (ret) {
 		if (ret != -ERESTARTSYS && ret != -EBUSY)
 		if (ret != -ERESTARTSYS && ret != -EBUSY)
 			DRM_ERROR("reserve failed %p\n", bo);
 			DRM_ERROR("reserve failed %p\n", bo);

+ 4 - 2
drivers/gpu/drm/ast/ast_fb.c

@@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue = ast_crtc->lut_b[regno] << 8;
 	*blue = ast_crtc->lut_b[regno] << 8;
 }
 }
 
 
-static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
 	.gamma_set = ast_fb_gamma_set,
 	.gamma_set = ast_fb_gamma_set,
 	.gamma_get = ast_fb_gamma_get,
 	.gamma_get = ast_fb_gamma_get,
 	.fb_probe = astfb_create,
 	.fb_probe = astfb_create,
@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	ast->fbdev = afbdev;
 	ast->fbdev = afbdev;
-	afbdev->helper.funcs = &ast_fb_helper_funcs;
 	spin_lock_init(&afbdev->dirty_lock);
 	spin_lock_init(&afbdev->dirty_lock);
+
+	drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, &afbdev->helper,
 	ret = drm_fb_helper_init(dev, &afbdev->helper,
 				 1, 1);
 				 1, 1);
 	if (ret) {
 	if (ret) {

+ 4 - 12
drivers/gpu/drm/ast/ast_mode.c

@@ -667,17 +667,9 @@ static void ast_encoder_destroy(struct drm_encoder *encoder)
 static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector)
 static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector)
 {
 {
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
-	struct drm_mode_object *obj;
-	struct drm_encoder *encoder;
-
 	/* pick the encoder ids */
 	/* pick the encoder ids */
-	if (enc_id) {
-		obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
-		if (!obj)
-			return NULL;
-		encoder = obj_to_encoder(obj);
-		return encoder;
-	}
+	if (enc_id)
+		return drm_encoder_find(connector->dev, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -829,7 +821,7 @@ static void ast_connector_destroy(struct drm_connector *connector)
 {
 {
 	struct ast_connector *ast_connector = to_ast_connector(connector);
 	struct ast_connector *ast_connector = to_ast_connector(connector);
 	ast_i2c_destroy(ast_connector->i2c);
 	ast_i2c_destroy(ast_connector->i2c);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -871,7 +863,7 @@ static int ast_connector_init(struct drm_device *dev)
 	connector->interlace_allowed = 0;
 	connector->interlace_allowed = 0;
 	connector->doublescan_allowed = 0;
 	connector->doublescan_allowed = 0;
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
 	connector->polled = DRM_CONNECTOR_POLL_CONNECT;
 
 

+ 2 - 0
drivers/gpu/drm/bochs/bochs_drv.c

@@ -97,6 +97,7 @@ static struct drm_driver bochs_driver = {
 /* ---------------------------------------------------------------------- */
 /* ---------------------------------------------------------------------- */
 /* pm interface                                                           */
 /* pm interface                                                           */
 
 
+#ifdef CONFIG_PM_SLEEP
 static int bochs_pm_suspend(struct device *dev)
 static int bochs_pm_suspend(struct device *dev)
 {
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct pci_dev *pdev = to_pci_dev(dev);
@@ -131,6 +132,7 @@ static int bochs_pm_resume(struct device *dev)
 	drm_kms_helper_poll_enable(drm_dev);
 	drm_kms_helper_poll_enable(drm_dev);
 	return 0;
 	return 0;
 }
 }
+#endif
 
 
 static const struct dev_pm_ops bochs_pm_ops = {
 static const struct dev_pm_ops bochs_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,
 	SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,

+ 4 - 3
drivers/gpu/drm/bochs/bochs_fbdev.c

@@ -72,7 +72,7 @@ static int bochsfb_create(struct drm_fb_helper *helper,
 
 
 	bo = gem_to_bochs_bo(gobj);
 	bo = gem_to_bochs_bo(gobj);
 
 
-	ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
+	ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue  = regno;
 	*blue  = regno;
 }
 }
 
 
-static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
 	.gamma_set = bochs_fb_gamma_set,
 	.gamma_set = bochs_fb_gamma_set,
 	.gamma_get = bochs_fb_gamma_get,
 	.gamma_get = bochs_fb_gamma_get,
 	.fb_probe = bochsfb_create,
 	.fb_probe = bochsfb_create,
@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
 {
 {
 	int ret;
 	int ret;
 
 
-	bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
+	drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
+			      &bochs_fb_helper_funcs);
 
 
 	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
 	ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
 				 1, 1);
 				 1, 1);

+ 4 - 13
drivers/gpu/drm/bochs/bochs_kms.c

@@ -53,7 +53,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 	if (old_fb) {
 	if (old_fb) {
 		bochs_fb = to_bochs_framebuffer(old_fb);
 		bochs_fb = to_bochs_framebuffer(old_fb);
 		bo = gem_to_bochs_bo(bochs_fb->obj);
 		bo = gem_to_bochs_bo(bochs_fb->obj);
-		ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
+		ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
 		if (ret) {
 		if (ret) {
 			DRM_ERROR("failed to reserve old_fb bo\n");
 			DRM_ERROR("failed to reserve old_fb bo\n");
 		} else {
 		} else {
@@ -67,7 +67,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
 
 
 	bochs_fb = to_bochs_framebuffer(crtc->primary->fb);
 	bochs_fb = to_bochs_framebuffer(crtc->primary->fb);
 	bo = gem_to_bochs_bo(bochs_fb->obj);
 	bo = gem_to_bochs_bo(bochs_fb->obj);
-	ret = ttm_bo_reserve(&bo->bo, true, false, false, 0);
+	ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
@@ -216,18 +216,9 @@ static struct drm_encoder *
 bochs_connector_best_encoder(struct drm_connector *connector)
 bochs_connector_best_encoder(struct drm_connector *connector)
 {
 {
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
-	struct drm_mode_object *obj;
-	struct drm_encoder *encoder;
-
 	/* pick the encoder ids */
 	/* pick the encoder ids */
-	if (enc_id) {
-		obj = drm_mode_object_find(connector->dev, enc_id,
-					   DRM_MODE_OBJECT_ENCODER);
-		if (!obj)
-			return NULL;
-		encoder = obj_to_encoder(obj);
-		return encoder;
-	}
+	if (enc_id)
+		return drm_encoder_find(connector->dev, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 

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

@@ -387,7 +387,7 @@ int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel,
 
 
 	*obj = NULL;
 	*obj = NULL;
 
 
-	size = ALIGN(size, PAGE_SIZE);
+	size = PAGE_ALIGN(size);
 	if (size == 0)
 	if (size == 0)
 		return -EINVAL;
 		return -EINVAL;
 
 

+ 1 - 1
drivers/gpu/drm/bridge/ptn3460.c

@@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
 	}
 	}
 	drm_connector_helper_add(&ptn_bridge->connector,
 	drm_connector_helper_add(&ptn_bridge->connector,
 			&ptn3460_connector_helper_funcs);
 			&ptn3460_connector_helper_funcs);
-	drm_sysfs_connector_add(&ptn_bridge->connector);
+	drm_connector_register(&ptn_bridge->connector);
 	drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
 	drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
 
 
 	return 0;
 	return 0;

+ 2 - 0
drivers/gpu/drm/cirrus/cirrus_drv.c

@@ -76,6 +76,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev)
 	drm_put_dev(dev);
 	drm_put_dev(dev);
 }
 }
 
 
+#ifdef CONFIG_PM_SLEEP
 static int cirrus_pm_suspend(struct device *dev)
 static int cirrus_pm_suspend(struct device *dev)
 {
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct pci_dev *pdev = to_pci_dev(dev);
@@ -110,6 +111,7 @@ static int cirrus_pm_resume(struct device *dev)
 	drm_kms_helper_poll_enable(drm_dev);
 	drm_kms_helper_poll_enable(drm_dev);
 	return 0;
 	return 0;
 }
 }
+#endif
 
 
 static const struct file_operations cirrus_driver_fops = {
 static const struct file_operations cirrus_driver_fops = {
 	.owner = THIS_MODULE,
 	.owner = THIS_MODULE,

+ 1 - 1
drivers/gpu/drm/cirrus/cirrus_drv.h

@@ -241,7 +241,7 @@ static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+	ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
 	if (ret) {
 	if (ret) {
 		if (ret != -ERESTARTSYS && ret != -EBUSY)
 		if (ret != -ERESTARTSYS && ret != -EBUSY)
 			DRM_ERROR("reserve failed %p\n", bo);
 			DRM_ERROR("reserve failed %p\n", bo);

+ 4 - 2
drivers/gpu/drm/cirrus/cirrus_fbdev.c

@@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
 	return 0;
 	return 0;
 }
 }
 
 
-static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
 	.gamma_set = cirrus_crtc_fb_gamma_set,
 	.gamma_set = cirrus_crtc_fb_gamma_set,
 	.gamma_get = cirrus_crtc_fb_gamma_get,
 	.gamma_get = cirrus_crtc_fb_gamma_get,
 	.fb_probe = cirrusfb_create,
 	.fb_probe = cirrusfb_create,
@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	cdev->mode_info.gfbdev = gfbdev;
 	cdev->mode_info.gfbdev = gfbdev;
-	gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
 	spin_lock_init(&gfbdev->dirty_lock);
 	spin_lock_init(&gfbdev->dirty_lock);
 
 
+	drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
+			      &cirrus_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
 	ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
 				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
 				 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
 	if (ret) {
 	if (ret) {

+ 2 - 12
drivers/gpu/drm/cirrus/cirrus_mode.c

@@ -509,19 +509,9 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
 						  *connector)
 						  *connector)
 {
 {
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
-	struct drm_mode_object *obj;
-	struct drm_encoder *encoder;
-
 	/* pick the encoder ids */
 	/* pick the encoder ids */
-	if (enc_id) {
-		obj =
-		    drm_mode_object_find(connector->dev, enc_id,
-					 DRM_MODE_OBJECT_ENCODER);
-		if (!obj)
-			return NULL;
-		encoder = obj_to_encoder(obj);
-		return encoder;
-	}
+	if (enc_id)
+		return drm_encoder_find(connector->dev, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 

+ 1 - 5
drivers/gpu/drm/drm_buffer.c

@@ -80,11 +80,7 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size)
 
 
 error_out:
 error_out:
 
 
-	/* Only last element can be null pointer so check for it first. */
-	if ((*buf)->data[idx])
-		kfree((*buf)->data[idx]);
-
-	for (--idx; idx >= 0; --idx)
+	for (; idx >= 0; --idx)
 		kfree((*buf)->data[idx]);
 		kfree((*buf)->data[idx]);
 
 
 	kfree(*buf);
 	kfree(*buf);

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

@@ -894,6 +894,8 @@ int drm_connector_init(struct drm_device *dev,
 	drm_object_attach_property(&connector->base,
 	drm_object_attach_property(&connector->base,
 				      dev->mode_config.dpms_property, 0);
 				      dev->mode_config.dpms_property, 0);
 
 
+	connector->debugfs_entry = NULL;
+
 out_put:
 out_put:
 	if (ret)
 	if (ret)
 		drm_mode_object_put(dev, &connector->base);
 		drm_mode_object_put(dev, &connector->base);
@@ -933,6 +935,47 @@ void drm_connector_cleanup(struct drm_connector *connector)
 }
 }
 EXPORT_SYMBOL(drm_connector_cleanup);
 EXPORT_SYMBOL(drm_connector_cleanup);
 
 
+/**
+ * drm_connector_register - register a connector
+ * @connector: the connector to register
+ *
+ * Register userspace interfaces for a connector
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_register(struct drm_connector *connector)
+{
+	int ret;
+
+	ret = drm_sysfs_connector_add(connector);
+	if (ret)
+		return ret;
+
+	ret = drm_debugfs_connector_add(connector);
+	if (ret) {
+		drm_sysfs_connector_remove(connector);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_connector_register);
+
+/**
+ * drm_connector_unregister - unregister a connector
+ * @connector: the connector to unregister
+ *
+ * Unregister userspace interfaces for a connector
+ */
+void drm_connector_unregister(struct drm_connector *connector)
+{
+	drm_sysfs_connector_remove(connector);
+	drm_debugfs_connector_remove(connector);
+}
+EXPORT_SYMBOL(drm_connector_unregister);
+
+
 /**
 /**
  * drm_connector_unplug_all - unregister connector userspace interfaces
  * drm_connector_unplug_all - unregister connector userspace interfaces
  * @dev: drm device
  * @dev: drm device
@@ -947,7 +990,7 @@ void drm_connector_unplug_all(struct drm_device *dev)
 
 
 	/* taking the mode config mutex ends up in a clash with sysfs */
 	/* taking the mode config mutex ends up in a clash with sysfs */
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-		drm_sysfs_connector_remove(connector);
+		drm_connector_unregister(connector);
 
 
 }
 }
 EXPORT_SYMBOL(drm_connector_unplug_all);
 EXPORT_SYMBOL(drm_connector_unplug_all);
@@ -1227,6 +1270,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 {
 {
 	struct drm_property *edid;
 	struct drm_property *edid;
 	struct drm_property *dpms;
 	struct drm_property *dpms;
+	struct drm_property *dev_path;
 
 
 	/*
 	/*
 	 * Standard properties (apply to all connectors)
 	 * Standard properties (apply to all connectors)
@@ -1241,6 +1285,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
 				   ARRAY_SIZE(drm_dpms_enum_list));
 				   ARRAY_SIZE(drm_dpms_enum_list));
 	dev->mode_config.dpms_property = dpms;
 	dev->mode_config.dpms_property = dpms;
 
 
+	dev_path = drm_property_create(dev,
+				       DRM_MODE_PROP_BLOB |
+				       DRM_MODE_PROP_IMMUTABLE,
+				       "PATH", 0);
+	dev->mode_config.path_property = dev_path;
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1510,6 +1560,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
 }
 }
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
 
 
+void drm_reinit_primary_mode_group(struct drm_device *dev)
+{
+	drm_modeset_lock_all(dev);
+	drm_mode_group_destroy(&dev->primary->mode_group);
+	drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
+	drm_modeset_unlock_all(dev);
+}
+EXPORT_SYMBOL(drm_reinit_primary_mode_group);
+
 /**
 /**
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
  * @out: drm_mode_modeinfo struct to return to the user
  * @out: drm_mode_modeinfo struct to return to the user
@@ -3362,7 +3421,7 @@ fail:
 EXPORT_SYMBOL(drm_property_create);
 EXPORT_SYMBOL(drm_property_create);
 
 
 /**
 /**
- * drm_property_create - create a new enumeration property type
+ * drm_property_create_enum - create a new enumeration property type
  * @dev: drm device
  * @dev: drm device
  * @flags: flags specifying the property type
  * @flags: flags specifying the property type
  * @name: name of the property
  * @name: name of the property
@@ -3408,7 +3467,7 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
 EXPORT_SYMBOL(drm_property_create_enum);
 EXPORT_SYMBOL(drm_property_create_enum);
 
 
 /**
 /**
- * drm_property_create - create a new bitmask property type
+ * drm_property_create_bitmask - create a new bitmask property type
  * @dev: drm device
  * @dev: drm device
  * @flags: flags specifying the property type
  * @flags: flags specifying the property type
  * @name: name of the property
  * @name: name of the property
@@ -3479,7 +3538,7 @@ static struct drm_property *property_create_range(struct drm_device *dev,
 }
 }
 
 
 /**
 /**
- * drm_property_create - create a new ranged property type
+ * drm_property_create_range - create a new ranged property type
  * @dev: drm device
  * @dev: drm device
  * @flags: flags specifying the property type
  * @flags: flags specifying the property type
  * @name: name of the property
  * @name: name of the property
@@ -3898,6 +3957,25 @@ done:
 	return ret;
 	return ret;
 }
 }
 
 
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+					 char *path)
+{
+	struct drm_device *dev = connector->dev;
+	int ret, size;
+	size = strlen(path) + 1;
+
+	connector->path_blob_ptr = drm_property_create_blob(connector->dev,
+							    size, path);
+	if (!connector->path_blob_ptr)
+		return -EINVAL;
+
+	ret = drm_object_property_set_value(&connector->base,
+					    dev->mode_config.path_property,
+					    connector->path_blob_ptr->base.id);
+	return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_path_property);
+
 /**
 /**
  * drm_mode_connector_update_edid_property - update the edid property of a connector
  * drm_mode_connector_update_edid_property - update the edid property of a connector
  * @connector: drm connector
  * @connector: drm connector
@@ -3915,6 +3993,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
 	struct drm_device *dev = connector->dev;
 	struct drm_device *dev = connector->dev;
 	int ret, size;
 	int ret, size;
 
 
+	/* ignore requests to set edid when overridden */
+	if (connector->override_edid)
+		return 0;
+
 	if (connector->edid_blob_ptr)
 	if (connector->edid_blob_ptr)
 		drm_property_destroy_blob(dev, connector->edid_blob_ptr);
 		drm_property_destroy_blob(dev, connector->edid_blob_ptr);
 
 

+ 1 - 0
drivers/gpu/drm/drm_crtc_helper.c

@@ -818,6 +818,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
 	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
 	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
 				    &fb->bits_per_pixel);
 				    &fb->bits_per_pixel);
 	fb->pixel_format = mode_cmd->pixel_format;
 	fb->pixel_format = mode_cmd->pixel_format;
+	fb->flags = mode_cmd->flags;
 }
 }
 EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
 EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
 
 

+ 182 - 0
drivers/gpu/drm/drm_debugfs.c

@@ -35,6 +35,7 @@
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/export.h>
 #include <drm/drmP.h>
 #include <drm/drmP.h>
+#include <drm/drm_edid.h>
 
 
 #if defined(CONFIG_DEBUG_FS)
 #if defined(CONFIG_DEBUG_FS)
 
 
@@ -237,5 +238,186 @@ int drm_debugfs_cleanup(struct drm_minor *minor)
 	return 0;
 	return 0;
 }
 }
 
 
+static int connector_show(struct seq_file *m, void *data)
+{
+	struct drm_connector *connector = m->private;
+	const char *status;
+
+	switch (connector->force) {
+	case DRM_FORCE_ON:
+		status = "on\n";
+		break;
+
+	case DRM_FORCE_ON_DIGITAL:
+		status = "digital\n";
+		break;
+
+	case DRM_FORCE_OFF:
+		status = "off\n";
+		break;
+
+	case DRM_FORCE_UNSPECIFIED:
+		status = "unspecified\n";
+		break;
+
+	default:
+		return 0;
+	}
+
+	seq_puts(m, status);
+
+	return 0;
+}
+
+static int connector_open(struct inode *inode, struct file *file)
+{
+	struct drm_connector *dev = inode->i_private;
+
+	return single_open(file, connector_show, dev);
+}
+
+static ssize_t connector_write(struct file *file, const char __user *ubuf,
+			       size_t len, loff_t *offp)
+{
+	struct seq_file *m = file->private_data;
+	struct drm_connector *connector = m->private;
+	char buf[12];
+
+	if (len > sizeof(buf) - 1)
+		return -EINVAL;
+
+	if (copy_from_user(buf, ubuf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (!strcmp(buf, "on"))
+		connector->force = DRM_FORCE_ON;
+	else if (!strcmp(buf, "digital"))
+		connector->force = DRM_FORCE_ON_DIGITAL;
+	else if (!strcmp(buf, "off"))
+		connector->force = DRM_FORCE_OFF;
+	else if (!strcmp(buf, "unspecified"))
+		connector->force = DRM_FORCE_UNSPECIFIED;
+	else
+		return -EINVAL;
+
+	return len;
+}
+
+static int edid_show(struct seq_file *m, void *data)
+{
+	struct drm_connector *connector = m->private;
+	struct drm_property_blob *edid = connector->edid_blob_ptr;
+
+	if (connector->override_edid && edid)
+		seq_write(m, edid->data, edid->length);
+
+	return 0;
+}
+
+static int edid_open(struct inode *inode, struct file *file)
+{
+	struct drm_connector *dev = inode->i_private;
+
+	return single_open(file, edid_show, dev);
+}
+
+static ssize_t edid_write(struct file *file, const char __user *ubuf,
+			  size_t len, loff_t *offp)
+{
+	struct seq_file *m = file->private_data;
+	struct drm_connector *connector = m->private;
+	char *buf;
+	struct edid *edid;
+	int ret;
+
+	buf = memdup_user(ubuf, len);
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	edid = (struct edid *) buf;
+
+	if (len == 5 && !strncmp(buf, "reset", 5)) {
+		connector->override_edid = false;
+		ret = drm_mode_connector_update_edid_property(connector, NULL);
+	} else if (len < EDID_LENGTH ||
+		   EDID_LENGTH * (1 + edid->extensions) > len)
+		ret = -EINVAL;
+	else {
+		connector->override_edid = false;
+		ret = drm_mode_connector_update_edid_property(connector, edid);
+		if (!ret)
+			connector->override_edid = true;
+	}
+
+	kfree(buf);
+
+	return (ret) ? ret : len;
+}
+
+static const struct file_operations drm_edid_fops = {
+	.owner = THIS_MODULE,
+	.open = edid_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = edid_write
+};
+
+
+static const struct file_operations drm_connector_fops = {
+	.owner = THIS_MODULE,
+	.open = connector_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = connector_write
+};
+
+int drm_debugfs_connector_add(struct drm_connector *connector)
+{
+	struct drm_minor *minor = connector->dev->primary;
+	struct dentry *root, *ent;
+
+	if (!minor->debugfs_root)
+		return -1;
+
+	root = debugfs_create_dir(connector->name, minor->debugfs_root);
+	if (!root)
+		return -ENOMEM;
+
+	connector->debugfs_entry = root;
+
+	/* force */
+	ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector,
+				  &drm_connector_fops);
+	if (!ent)
+		goto error;
+
+	/* edid */
+	ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root,
+				  connector, &drm_edid_fops);
+	if (!ent)
+		goto error;
+
+	return 0;
+
+error:
+	debugfs_remove_recursive(connector->debugfs_entry);
+	connector->debugfs_entry = NULL;
+	return -ENOMEM;
+}
+
+void drm_debugfs_connector_remove(struct drm_connector *connector)
+{
+	if (!connector->debugfs_entry)
+		return;
+
+	debugfs_remove_recursive(connector->debugfs_entry);
+
+	connector->debugfs_entry = NULL;
+}
+
 #endif /* CONFIG_DEBUG_FS */
 #endif /* CONFIG_DEBUG_FS */
 
 

+ 2715 - 0
drivers/gpu/drm/drm_dp_mst_topology.c

@@ -0,0 +1,2715 @@
+/*
+ * Copyright © 2014 Red Hat
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/i2c.h>
+#include <drm/drm_dp_mst_helper.h>
+#include <drm/drmP.h>
+
+#include <drm/drm_fixed.h>
+
+/**
+ * DOC: dp mst helper
+ *
+ * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
+ * protocol. The helpers contain a topology manager and bandwidth manager.
+ * The helpers encapsulate the sending and received of sideband msgs.
+ */
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+				  char *buf);
+static int test_calc_pbn_mode(void);
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+				     int id,
+				     struct drm_dp_payload *payload);
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+				  struct drm_dp_mst_port *port,
+				  int offset, int size, u8 *bytes);
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+				    struct drm_dp_mst_branch *mstb);
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+					   struct drm_dp_mst_branch *mstb,
+					   struct drm_dp_mst_port *port);
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+				 u8 *guid);
+
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
+/* sideband msg handling */
+static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
+{
+	u8 bitmask = 0x80;
+	u8 bitshift = 7;
+	u8 array_index = 0;
+	int number_of_bits = num_nibbles * 4;
+	u8 remainder = 0;
+
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		remainder |= (data[array_index] & bitmask) >> bitshift;
+		bitmask >>= 1;
+		bitshift--;
+		if (bitmask == 0) {
+			bitmask = 0x80;
+			bitshift = 7;
+			array_index++;
+		}
+		if ((remainder & 0x10) == 0x10)
+			remainder ^= 0x13;
+	}
+
+	number_of_bits = 4;
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		if ((remainder & 0x10) != 0)
+			remainder ^= 0x13;
+	}
+
+	return remainder;
+}
+
+static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
+{
+	u8 bitmask = 0x80;
+	u8 bitshift = 7;
+	u8 array_index = 0;
+	int number_of_bits = number_of_bytes * 8;
+	u16 remainder = 0;
+
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		remainder |= (data[array_index] & bitmask) >> bitshift;
+		bitmask >>= 1;
+		bitshift--;
+		if (bitmask == 0) {
+			bitmask = 0x80;
+			bitshift = 7;
+			array_index++;
+		}
+		if ((remainder & 0x100) == 0x100)
+			remainder ^= 0xd5;
+	}
+
+	number_of_bits = 8;
+	while (number_of_bits != 0) {
+		number_of_bits--;
+		remainder <<= 1;
+		if ((remainder & 0x100) != 0)
+			remainder ^= 0xd5;
+	}
+
+	return remainder & 0xff;
+}
+static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
+{
+	u8 size = 3;
+	size += (hdr->lct / 2);
+	return size;
+}
+
+static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+					   u8 *buf, int *len)
+{
+	int idx = 0;
+	int i;
+	u8 crc4;
+	buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
+	for (i = 0; i < (hdr->lct / 2); i++)
+		buf[idx++] = hdr->rad[i];
+	buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
+		(hdr->msg_len & 0x3f);
+	buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
+
+	crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
+	buf[idx - 1] |= (crc4 & 0xf);
+
+	*len = idx;
+}
+
+static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
+					   u8 *buf, int buflen, u8 *hdrlen)
+{
+	u8 crc4;
+	u8 len;
+	int i;
+	u8 idx;
+	if (buf[0] == 0)
+		return false;
+	len = 3;
+	len += ((buf[0] & 0xf0) >> 4) / 2;
+	if (len > buflen)
+		return false;
+	crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
+
+	if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
+		DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
+		return false;
+	}
+
+	hdr->lct = (buf[0] & 0xf0) >> 4;
+	hdr->lcr = (buf[0] & 0xf);
+	idx = 1;
+	for (i = 0; i < (hdr->lct / 2); i++)
+		hdr->rad[i] = buf[idx++];
+	hdr->broadcast = (buf[idx] >> 7) & 0x1;
+	hdr->path_msg = (buf[idx] >> 6) & 0x1;
+	hdr->msg_len = buf[idx] & 0x3f;
+	idx++;
+	hdr->somt = (buf[idx] >> 7) & 0x1;
+	hdr->eomt = (buf[idx] >> 6) & 0x1;
+	hdr->seqno = (buf[idx] >> 4) & 0x1;
+	idx++;
+	*hdrlen = idx;
+	return true;
+}
+
+static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
+				       struct drm_dp_sideband_msg_tx *raw)
+{
+	int idx = 0;
+	int i;
+	u8 *buf = raw->msg;
+	buf[idx++] = req->req_type & 0x7f;
+
+	switch (req->req_type) {
+	case DP_ENUM_PATH_RESOURCES:
+		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
+		idx++;
+		break;
+	case DP_ALLOCATE_PAYLOAD:
+		buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
+			(req->u.allocate_payload.number_sdp_streams & 0xf);
+		idx++;
+		buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
+		idx++;
+		buf[idx] = (req->u.allocate_payload.pbn >> 8);
+		idx++;
+		buf[idx] = (req->u.allocate_payload.pbn & 0xff);
+		idx++;
+		for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
+			buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
+				(req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
+			idx++;
+		}
+		if (req->u.allocate_payload.number_sdp_streams & 1) {
+			i = req->u.allocate_payload.number_sdp_streams - 1;
+			buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
+			idx++;
+		}
+		break;
+	case DP_QUERY_PAYLOAD:
+		buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
+		idx++;
+		buf[idx] = (req->u.query_payload.vcpi & 0x7f);
+		idx++;
+		break;
+	case DP_REMOTE_DPCD_READ:
+		buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
+		buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
+		idx++;
+		buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
+		idx++;
+		buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
+		idx++;
+		buf[idx] = (req->u.dpcd_read.num_bytes);
+		idx++;
+		break;
+
+	case DP_REMOTE_DPCD_WRITE:
+		buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
+		buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
+		idx++;
+		buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
+		idx++;
+		buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
+		idx++;
+		buf[idx] = (req->u.dpcd_write.num_bytes);
+		idx++;
+		memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
+		idx += req->u.dpcd_write.num_bytes;
+		break;
+	case DP_REMOTE_I2C_READ:
+		buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
+		buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
+		idx++;
+		for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
+			buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
+			idx++;
+			buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
+			idx++;
+			memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
+			idx += req->u.i2c_read.transactions[i].num_bytes;
+
+			buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5;
+			buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
+			idx++;
+		}
+		buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
+		idx++;
+		buf[idx] = (req->u.i2c_read.num_bytes_read);
+		idx++;
+		break;
+
+	case DP_REMOTE_I2C_WRITE:
+		buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
+		idx++;
+		buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
+		idx++;
+		buf[idx] = (req->u.i2c_write.num_bytes);
+		idx++;
+		memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
+		idx += req->u.i2c_write.num_bytes;
+		break;
+	}
+	raw->cur_len = idx;
+}
+
+static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
+{
+	u8 crc4;
+	crc4 = drm_dp_msg_data_crc4(msg, len);
+	msg[len] = crc4;
+}
+
+static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
+					 struct drm_dp_sideband_msg_tx *raw)
+{
+	int idx = 0;
+	u8 *buf = raw->msg;
+
+	buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
+
+	raw->cur_len = idx;
+}
+
+/* this adds a chunk of msg to the builder to get the final msg */
+static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg,
+				      u8 *replybuf, u8 replybuflen, bool hdr)
+{
+	int ret;
+	u8 crc4;
+
+	if (hdr) {
+		u8 hdrlen;
+		struct drm_dp_sideband_msg_hdr recv_hdr;
+		ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen);
+		if (ret == false) {
+			print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false);
+			return false;
+		}
+
+		/* get length contained in this portion */
+		msg->curchunk_len = recv_hdr.msg_len;
+		msg->curchunk_hdrlen = hdrlen;
+
+		/* we have already gotten an somt - don't bother parsing */
+		if (recv_hdr.somt && msg->have_somt)
+			return false;
+
+		if (recv_hdr.somt) {
+			memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr));
+			msg->have_somt = true;
+		}
+		if (recv_hdr.eomt)
+			msg->have_eomt = true;
+
+		/* copy the bytes for the remainder of this header chunk */
+		msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen));
+		memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx);
+	} else {
+		memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
+		msg->curchunk_idx += replybuflen;
+	}
+
+	if (msg->curchunk_idx >= msg->curchunk_len) {
+		/* do CRC */
+		crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
+		/* copy chunk into bigger msg */
+		memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
+		msg->curlen += msg->curchunk_len - 1;
+	}
+	return true;
+}
+
+static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw,
+					       struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	int i;
+	memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
+	idx += 16;
+	repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	for (i = 0; i < repmsg->u.link_addr.nports; i++) {
+		if (raw->msg[idx] & 0x80)
+			repmsg->u.link_addr.ports[i].input_port = 1;
+
+		repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
+		repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
+
+		idx++;
+		if (idx > raw->curlen)
+			goto fail_len;
+		repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
+		repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
+		if (repmsg->u.link_addr.ports[i].input_port == 0)
+			repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+		idx++;
+		if (idx > raw->curlen)
+			goto fail_len;
+		if (repmsg->u.link_addr.ports[i].input_port == 0) {
+			repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
+			idx++;
+			if (idx > raw->curlen)
+				goto fail_len;
+			memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
+			idx += 16;
+			if (idx > raw->curlen)
+				goto fail_len;
+			repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
+			repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
+			idx++;
+
+		}
+		if (idx > raw->curlen)
+			goto fail_len;
+	}
+
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
+						   struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
+						      struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
+						      struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+
+	repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
+	idx++;
+	/* TODO check */
+	memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
+							  struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+							  struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.allocate_payload.vcpi = raw->msg[idx];
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
+						    struct drm_dp_sideband_msg_reply_body *repmsg)
+{
+	int idx = 1;
+	repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+	repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+	idx += 2;
+	if (idx > raw->curlen)
+		goto fail_len;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
+					struct drm_dp_sideband_msg_reply_body *msg)
+{
+	memset(msg, 0, sizeof(*msg));
+	msg->reply_type = (raw->msg[0] & 0x80) >> 7;
+	msg->req_type = (raw->msg[0] & 0x7f);
+
+	if (msg->reply_type) {
+		memcpy(msg->u.nak.guid, &raw->msg[1], 16);
+		msg->u.nak.reason = raw->msg[17];
+		msg->u.nak.nak_data = raw->msg[18];
+		return false;
+	}
+
+	switch (msg->req_type) {
+	case DP_LINK_ADDRESS:
+		return drm_dp_sideband_parse_link_address(raw, msg);
+	case DP_QUERY_PAYLOAD:
+		return drm_dp_sideband_parse_query_payload_ack(raw, msg);
+	case DP_REMOTE_DPCD_READ:
+		return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
+	case DP_REMOTE_DPCD_WRITE:
+		return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
+	case DP_REMOTE_I2C_READ:
+		return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
+	case DP_ENUM_PATH_RESOURCES:
+		return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
+	case DP_ALLOCATE_PAYLOAD:
+		return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
+	default:
+		DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
+		return false;
+	}
+}
+
+static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw,
+							   struct drm_dp_sideband_msg_req_body *msg)
+{
+	int idx = 1;
+
+	msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
+	idx += 16;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
+	msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
+	msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
+	msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
+	msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
+	idx++;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw,
+							   struct drm_dp_sideband_msg_req_body *msg)
+{
+	int idx = 1;
+
+	msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
+	idx++;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
+	idx += 16;
+	if (idx > raw->curlen)
+		goto fail_len;
+
+	msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
+	idx++;
+	return true;
+fail_len:
+	DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen);
+	return false;
+}
+
+static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw,
+				      struct drm_dp_sideband_msg_req_body *msg)
+{
+	memset(msg, 0, sizeof(*msg));
+	msg->req_type = (raw->msg[0] & 0x7f);
+
+	switch (msg->req_type) {
+	case DP_CONNECTION_STATUS_NOTIFY:
+		return drm_dp_sideband_parse_connection_status_notify(raw, msg);
+	case DP_RESOURCE_STATUS_NOTIFY:
+		return drm_dp_sideband_parse_resource_status_notify(raw, msg);
+	default:
+		DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type);
+		return false;
+	}
+}
+
+static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_REMOTE_DPCD_WRITE;
+	req.u.dpcd_write.port_number = port_num;
+	req.u.dpcd_write.dpcd_address = offset;
+	req.u.dpcd_write.num_bytes = num_bytes;
+	req.u.dpcd_write.bytes = bytes;
+	drm_dp_encode_sideband_req(&req, msg);
+
+	return 0;
+}
+
+static int build_link_address(struct drm_dp_sideband_msg_tx *msg)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_LINK_ADDRESS;
+	drm_dp_encode_sideband_req(&req, msg);
+	return 0;
+}
+
+static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_ENUM_PATH_RESOURCES;
+	req.u.port_num.port_number = port_num;
+	drm_dp_encode_sideband_req(&req, msg);
+	msg->path_msg = true;
+	return 0;
+}
+
+static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num,
+				  u8 vcpi, uint16_t pbn)
+{
+	struct drm_dp_sideband_msg_req_body req;
+	memset(&req, 0, sizeof(req));
+	req.req_type = DP_ALLOCATE_PAYLOAD;
+	req.u.allocate_payload.port_number = port_num;
+	req.u.allocate_payload.vcpi = vcpi;
+	req.u.allocate_payload.pbn = pbn;
+	drm_dp_encode_sideband_req(&req, msg);
+	msg->path_msg = true;
+	return 0;
+}
+
+static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+					struct drm_dp_vcpi *vcpi)
+{
+	int ret;
+
+	mutex_lock(&mgr->payload_lock);
+	ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
+	if (ret > mgr->max_payloads) {
+		ret = -EINVAL;
+		DRM_DEBUG_KMS("out of payload ids %d\n", ret);
+		goto out_unlock;
+	}
+
+	set_bit(ret, &mgr->payload_mask);
+	vcpi->vcpi = ret;
+	mgr->proposed_vcpis[ret - 1] = vcpi;
+out_unlock:
+	mutex_unlock(&mgr->payload_lock);
+	return ret;
+}
+
+static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
+				      int id)
+{
+	if (id == 0)
+		return;
+
+	mutex_lock(&mgr->payload_lock);
+	DRM_DEBUG_KMS("putting payload %d\n", id);
+	clear_bit(id, &mgr->payload_mask);
+	mgr->proposed_vcpis[id - 1] = NULL;
+	mutex_unlock(&mgr->payload_lock);
+}
+
+static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
+			      struct drm_dp_sideband_msg_tx *txmsg)
+{
+	bool ret;
+	mutex_lock(&mgr->qlock);
+	ret = (txmsg->state == DRM_DP_SIDEBAND_TX_RX ||
+	       txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT);
+	mutex_unlock(&mgr->qlock);
+	return ret;
+}
+
+static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
+				    struct drm_dp_sideband_msg_tx *txmsg)
+{
+	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
+	int ret;
+
+	ret = wait_event_timeout(mgr->tx_waitq,
+				 check_txmsg_state(mgr, txmsg),
+				 (4 * HZ));
+	mutex_lock(&mstb->mgr->qlock);
+	if (ret > 0) {
+		if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
+			ret = -EIO;
+			goto out;
+		}
+	} else {
+		DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno);
+
+		/* dump some state */
+		ret = -EIO;
+
+		/* remove from q */
+		if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
+		    txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) {
+			list_del(&txmsg->next);
+		}
+
+		if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
+		    txmsg->state == DRM_DP_SIDEBAND_TX_SENT) {
+			mstb->tx_slots[txmsg->seqno] = NULL;
+		}
+	}
+out:
+	mutex_unlock(&mgr->qlock);
+
+	return ret;
+}
+
+static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
+{
+	struct drm_dp_mst_branch *mstb;
+
+	mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
+	if (!mstb)
+		return NULL;
+
+	mstb->lct = lct;
+	if (lct > 1)
+		memcpy(mstb->rad, rad, lct / 2);
+	INIT_LIST_HEAD(&mstb->ports);
+	kref_init(&mstb->kref);
+	return mstb;
+}
+
+static void drm_dp_destroy_mst_branch_device(struct kref *kref)
+{
+	struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
+	struct drm_dp_mst_port *port, *tmp;
+	bool wake_tx = false;
+
+	cancel_work_sync(&mstb->mgr->work);
+
+	/*
+	 * destroy all ports - don't need lock
+	 * as there are no more references to the mst branch
+	 * device at this point.
+	 */
+	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
+		list_del(&port->next);
+		drm_dp_put_port(port);
+	}
+
+	/* drop any tx slots msg */
+	mutex_lock(&mstb->mgr->qlock);
+	if (mstb->tx_slots[0]) {
+		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		mstb->tx_slots[0] = NULL;
+		wake_tx = true;
+	}
+	if (mstb->tx_slots[1]) {
+		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		mstb->tx_slots[1] = NULL;
+		wake_tx = true;
+	}
+	mutex_unlock(&mstb->mgr->qlock);
+
+	if (wake_tx)
+		wake_up(&mstb->mgr->tx_waitq);
+	kfree(mstb);
+}
+
+static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
+{
+	kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device);
+}
+
+
+static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
+{
+	switch (old_pdt) {
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+	case DP_PEER_DEVICE_SST_SINK:
+		/* remove i2c over sideband */
+		drm_dp_mst_unregister_i2c_bus(&port->aux);
+		break;
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		drm_dp_put_mst_branch_device(port->mstb);
+		port->mstb = NULL;
+		break;
+	}
+}
+
+static void drm_dp_destroy_port(struct kref *kref)
+{
+	struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
+	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+	if (!port->input) {
+		port->vcpi.num_slots = 0;
+		if (port->connector)
+			(*port->mgr->cbs->destroy_connector)(mgr, port->connector);
+		drm_dp_port_teardown_pdt(port, port->pdt);
+
+		if (!port->input && port->vcpi.vcpi > 0)
+			drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+	}
+	kfree(port);
+
+	(*mgr->cbs->hotplug)(mgr);
+}
+
+static void drm_dp_put_port(struct drm_dp_mst_port *port)
+{
+	kref_put(&port->kref, drm_dp_destroy_port);
+}
+
+static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find)
+{
+	struct drm_dp_mst_port *port;
+	struct drm_dp_mst_branch *rmstb;
+	if (to_find == mstb) {
+		kref_get(&mstb->kref);
+		return mstb;
+	}
+	list_for_each_entry(port, &mstb->ports, next) {
+		if (port->mstb) {
+			rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find);
+			if (rmstb)
+				return rmstb;
+		}
+	}
+	return NULL;
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb)
+{
+	struct drm_dp_mst_branch *rmstb = NULL;
+	mutex_lock(&mgr->lock);
+	if (mgr->mst_primary)
+		rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb);
+	mutex_unlock(&mgr->lock);
+	return rmstb;
+}
+
+static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find)
+{
+	struct drm_dp_mst_port *port, *mport;
+
+	list_for_each_entry(port, &mstb->ports, next) {
+		if (port == to_find) {
+			kref_get(&port->kref);
+			return port;
+		}
+		if (port->mstb) {
+			mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find);
+			if (mport)
+				return mport;
+		}
+	}
+	return NULL;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	struct drm_dp_mst_port *rport = NULL;
+	mutex_lock(&mgr->lock);
+	if (mgr->mst_primary)
+		rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port);
+	mutex_unlock(&mgr->lock);
+	return rport;
+}
+
+static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
+{
+	struct drm_dp_mst_port *port;
+
+	list_for_each_entry(port, &mstb->ports, next) {
+		if (port->port_num == port_num) {
+			kref_get(&port->kref);
+			return port;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * calculate a new RAD for this MST branch device
+ * if parent has an LCT of 2 then it has 1 nibble of RAD,
+ * if parent has an LCT of 3 then it has 2 nibbles of RAD,
+ */
+static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
+				 u8 *rad)
+{
+	int lct = port->parent->lct;
+	int shift = 4;
+	int idx = lct / 2;
+	if (lct > 1) {
+		memcpy(rad, port->parent->rad, idx);
+		shift = (lct % 2) ? 4 : 0;
+	} else
+		rad[0] = 0;
+
+	rad[idx] |= port->port_num << shift;
+	return lct + 1;
+}
+
+/*
+ * return sends link address for new mstb
+ */
+static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
+{
+	int ret;
+	u8 rad[6], lct;
+	bool send_link = false;
+	switch (port->pdt) {
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+	case DP_PEER_DEVICE_SST_SINK:
+		/* add i2c over sideband */
+		ret = drm_dp_mst_register_i2c_bus(&port->aux);
+		break;
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		lct = drm_dp_calculate_rad(port, rad);
+
+		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
+		port->mstb->mgr = port->mgr;
+		port->mstb->port_parent = port;
+
+		send_link = true;
+		break;
+	}
+	return send_link;
+}
+
+static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb,
+				   struct drm_dp_mst_port *port)
+{
+	int ret;
+	if (port->dpcd_rev >= 0x12) {
+		port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid);
+		if (!port->guid_valid) {
+			ret = drm_dp_send_dpcd_write(mstb->mgr,
+						     port,
+						     DP_GUID,
+						     16, port->guid);
+			port->guid_valid = true;
+		}
+	}
+}
+
+static void build_mst_prop_path(struct drm_dp_mst_port *port,
+				struct drm_dp_mst_branch *mstb,
+				char *proppath)
+{
+	int i;
+	char temp[8];
+	snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id);
+	for (i = 0; i < (mstb->lct - 1); i++) {
+		int shift = (i % 2) ? 0 : 4;
+		int port_num = mstb->rad[i / 2] >> shift;
+		snprintf(temp, 8, "-%d", port_num);
+		strncat(proppath, temp, 255);
+	}
+	snprintf(temp, 8, "-%d", port->port_num);
+	strncat(proppath, temp, 255);
+}
+
+static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
+			    struct device *dev,
+			    struct drm_dp_link_addr_reply_port *port_msg)
+{
+	struct drm_dp_mst_port *port;
+	bool ret;
+	bool created = false;
+	int old_pdt = 0;
+	int old_ddps = 0;
+	port = drm_dp_get_port(mstb, port_msg->port_number);
+	if (!port) {
+		port = kzalloc(sizeof(*port), GFP_KERNEL);
+		if (!port)
+			return;
+		kref_init(&port->kref);
+		port->parent = mstb;
+		port->port_num = port_msg->port_number;
+		port->mgr = mstb->mgr;
+		port->aux.name = "DPMST";
+		port->aux.dev = dev;
+		created = true;
+	} else {
+		old_pdt = port->pdt;
+		old_ddps = port->ddps;
+	}
+
+	port->pdt = port_msg->peer_device_type;
+	port->input = port_msg->input_port;
+	port->mcs = port_msg->mcs;
+	port->ddps = port_msg->ddps;
+	port->ldps = port_msg->legacy_device_plug_status;
+	port->dpcd_rev = port_msg->dpcd_revision;
+	port->num_sdp_streams = port_msg->num_sdp_streams;
+	port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
+	memcpy(port->guid, port_msg->peer_guid, 16);
+
+	/* manage mstb port lists with mgr lock - take a reference
+	   for this list */
+	if (created) {
+		mutex_lock(&mstb->mgr->lock);
+		kref_get(&port->kref);
+		list_add(&port->next, &mstb->ports);
+		mutex_unlock(&mstb->mgr->lock);
+	}
+
+	if (old_ddps != port->ddps) {
+		if (port->ddps) {
+			drm_dp_check_port_guid(mstb, port);
+			if (!port->input)
+				drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
+		} else {
+			port->guid_valid = false;
+			port->available_pbn = 0;
+			}
+	}
+
+	if (old_pdt != port->pdt && !port->input) {
+		drm_dp_port_teardown_pdt(port, old_pdt);
+
+		ret = drm_dp_port_setup_pdt(port);
+		if (ret == true) {
+			drm_dp_send_link_address(mstb->mgr, port->mstb);
+			port->mstb->link_address_sent = true;
+		}
+	}
+
+	if (created && !port->input) {
+		char proppath[255];
+		build_mst_prop_path(port, mstb, proppath);
+		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);
+	}
+
+	/* put reference to this port */
+	drm_dp_put_port(port);
+}
+
+static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
+			       struct drm_dp_connection_status_notify *conn_stat)
+{
+	struct drm_dp_mst_port *port;
+	int old_pdt;
+	int old_ddps;
+	bool dowork = false;
+	port = drm_dp_get_port(mstb, conn_stat->port_number);
+	if (!port)
+		return;
+
+	old_ddps = port->ddps;
+	old_pdt = port->pdt;
+	port->pdt = conn_stat->peer_device_type;
+	port->mcs = conn_stat->message_capability_status;
+	port->ldps = conn_stat->legacy_device_plug_status;
+	port->ddps = conn_stat->displayport_device_plug_status;
+
+	if (old_ddps != port->ddps) {
+		if (port->ddps) {
+			drm_dp_check_port_guid(mstb, port);
+			dowork = true;
+		} else {
+			port->guid_valid = false;
+			port->available_pbn = 0;
+		}
+	}
+	if (old_pdt != port->pdt && !port->input) {
+		drm_dp_port_teardown_pdt(port, old_pdt);
+
+		if (drm_dp_port_setup_pdt(port))
+			dowork = true;
+	}
+
+	drm_dp_put_port(port);
+	if (dowork)
+		queue_work(system_long_wq, &mstb->mgr->work);
+
+}
+
+static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
+							       u8 lct, u8 *rad)
+{
+	struct drm_dp_mst_branch *mstb;
+	struct drm_dp_mst_port *port;
+	int i;
+	/* find the port by iterating down */
+	mstb = mgr->mst_primary;
+
+	for (i = 0; i < lct - 1; i++) {
+		int shift = (i % 2) ? 0 : 4;
+		int port_num = rad[i / 2] >> shift;
+
+		list_for_each_entry(port, &mstb->ports, next) {
+			if (port->port_num == port_num) {
+				if (!port->mstb) {
+					DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]);
+					return NULL;
+				}
+
+				mstb = port->mstb;
+				break;
+			}
+		}
+	}
+	kref_get(&mstb->kref);
+	return mstb;
+}
+
+static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+					       struct drm_dp_mst_branch *mstb)
+{
+	struct drm_dp_mst_port *port;
+
+	if (!mstb->link_address_sent) {
+		drm_dp_send_link_address(mgr, mstb);
+		mstb->link_address_sent = true;
+	}
+	list_for_each_entry(port, &mstb->ports, next) {
+		if (port->input)
+			continue;
+
+		if (!port->ddps)
+			continue;
+
+		if (!port->available_pbn)
+			drm_dp_send_enum_path_resources(mgr, mstb, port);
+
+		if (port->mstb)
+			drm_dp_check_and_send_link_address(mgr, port->mstb);
+	}
+}
+
+static void drm_dp_mst_link_probe_work(struct work_struct *work)
+{
+	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work);
+
+	drm_dp_check_and_send_link_address(mgr, mgr->mst_primary);
+
+}
+
+static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
+				 u8 *guid)
+{
+	static u8 zero_guid[16];
+
+	if (!memcmp(guid, zero_guid, 16)) {
+		u64 salt = get_jiffies_64();
+		memcpy(&guid[0], &salt, sizeof(u64));
+		memcpy(&guid[8], &salt, sizeof(u64));
+		return false;
+	}
+	return true;
+}
+
+#if 0
+static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
+{
+	struct drm_dp_sideband_msg_req_body req;
+
+	req.req_type = DP_REMOTE_DPCD_READ;
+	req.u.dpcd_read.port_number = port_num;
+	req.u.dpcd_read.dpcd_address = offset;
+	req.u.dpcd_read.num_bytes = num_bytes;
+	drm_dp_encode_sideband_req(&req, msg);
+
+	return 0;
+}
+#endif
+
+static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
+				    bool up, u8 *msg, int len)
+{
+	int ret;
+	int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
+	int tosend, total, offset;
+	int retries = 0;
+
+retry:
+	total = len;
+	offset = 0;
+	do {
+		tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total);
+
+		ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
+					&msg[offset],
+					tosend);
+		if (ret != tosend) {
+			if (ret == -EIO && retries < 5) {
+				retries++;
+				goto retry;
+			}
+			DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret);
+			WARN(1, "fail\n");
+
+			return -EIO;
+		}
+		offset += tosend;
+		total -= tosend;
+	} while (total > 0);
+	return 0;
+}
+
+static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
+				  struct drm_dp_sideband_msg_tx *txmsg)
+{
+	struct drm_dp_mst_branch *mstb = txmsg->dst;
+
+	/* both msg slots are full */
+	if (txmsg->seqno == -1) {
+		if (mstb->tx_slots[0] && mstb->tx_slots[1]) {
+			DRM_DEBUG_KMS("%s: failed to find slot\n", __func__);
+			return -EAGAIN;
+		}
+		if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) {
+			txmsg->seqno = mstb->last_seqno;
+			mstb->last_seqno ^= 1;
+		} else if (mstb->tx_slots[0] == NULL)
+			txmsg->seqno = 0;
+		else
+			txmsg->seqno = 1;
+		mstb->tx_slots[txmsg->seqno] = txmsg;
+	}
+	hdr->broadcast = 0;
+	hdr->path_msg = txmsg->path_msg;
+	hdr->lct = mstb->lct;
+	hdr->lcr = mstb->lct - 1;
+	if (mstb->lct > 1)
+		memcpy(hdr->rad, mstb->rad, mstb->lct / 2);
+	hdr->seqno = txmsg->seqno;
+	return 0;
+}
+/*
+ * process a single block of the next message in the sideband queue
+ */
+static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
+				   struct drm_dp_sideband_msg_tx *txmsg,
+				   bool up)
+{
+	u8 chunk[48];
+	struct drm_dp_sideband_msg_hdr hdr;
+	int len, space, idx, tosend;
+	int ret;
+
+	memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
+
+	if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) {
+		txmsg->seqno = -1;
+		txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
+	}
+
+	/* make hdr from dst mst - for replies use seqno
+	   otherwise assign one */
+	ret = set_hdr_from_dst_qlock(&hdr, txmsg);
+	if (ret < 0)
+		return ret;
+
+	/* amount left to send in this message */
+	len = txmsg->cur_len - txmsg->cur_offset;
+
+	/* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
+	space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
+
+	tosend = min(len, space);
+	if (len == txmsg->cur_len)
+		hdr.somt = 1;
+	if (space >= len)
+		hdr.eomt = 1;
+
+
+	hdr.msg_len = tosend + 1;
+	drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
+	memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
+	/* add crc at end */
+	drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
+	idx += tosend + 1;
+
+	ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
+	if (ret) {
+		DRM_DEBUG_KMS("sideband msg failed to send\n");
+		return ret;
+	}
+
+	txmsg->cur_offset += tosend;
+	if (txmsg->cur_offset == txmsg->cur_len) {
+		txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
+		return 1;
+	}
+	return 0;
+}
+
+/* must be called holding qlock */
+static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+
+	/* construct a chunk from the first msg in the tx_msg queue */
+	if (list_empty(&mgr->tx_msg_downq)) {
+		mgr->tx_down_in_progress = false;
+		return;
+	}
+	mgr->tx_down_in_progress = true;
+
+	txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next);
+	ret = process_single_tx_qlock(mgr, txmsg, false);
+	if (ret == 1) {
+		/* txmsg is sent it should be in the slots now */
+		list_del(&txmsg->next);
+	} else if (ret) {
+		DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
+		list_del(&txmsg->next);
+		if (txmsg->seqno != -1)
+			txmsg->dst->tx_slots[txmsg->seqno] = NULL;
+		txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
+		wake_up(&mgr->tx_waitq);
+	}
+	if (list_empty(&mgr->tx_msg_downq)) {
+		mgr->tx_down_in_progress = false;
+		return;
+	}
+}
+
+/* called holding qlock */
+static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+
+	/* construct a chunk from the first msg in the tx_msg queue */
+	if (list_empty(&mgr->tx_msg_upq)) {
+		mgr->tx_up_in_progress = false;
+		return;
+	}
+
+	txmsg = list_first_entry(&mgr->tx_msg_upq, struct drm_dp_sideband_msg_tx, next);
+	ret = process_single_tx_qlock(mgr, txmsg, true);
+	if (ret == 1) {
+		/* up txmsgs aren't put in slots - so free after we send it */
+		list_del(&txmsg->next);
+		kfree(txmsg);
+	} else if (ret)
+		DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
+	mgr->tx_up_in_progress = true;
+}
+
+static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
+				 struct drm_dp_sideband_msg_tx *txmsg)
+{
+	mutex_lock(&mgr->qlock);
+	list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
+	if (!mgr->tx_down_in_progress)
+		process_single_down_tx_qlock(mgr);
+	mutex_unlock(&mgr->qlock);
+}
+
+static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
+				    struct drm_dp_mst_branch *mstb)
+{
+	int len;
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = mstb;
+	len = build_link_address(txmsg);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+		int i;
+
+		if (txmsg->reply.reply_type == 1)
+			DRM_DEBUG_KMS("link address nak received\n");
+		else {
+			DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
+			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+				DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
+				       txmsg->reply.u.link_addr.ports[i].input_port,
+				       txmsg->reply.u.link_addr.ports[i].peer_device_type,
+				       txmsg->reply.u.link_addr.ports[i].port_number,
+				       txmsg->reply.u.link_addr.ports[i].dpcd_revision,
+				       txmsg->reply.u.link_addr.ports[i].mcs,
+				       txmsg->reply.u.link_addr.ports[i].ddps,
+				       txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
+				       txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
+				       txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
+			}
+			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
+				drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
+			}
+			(*mgr->cbs->hotplug)(mgr);
+		}
+	} else
+		DRM_DEBUG_KMS("link address failed %d\n", ret);
+
+	kfree(txmsg);
+	return 0;
+}
+
+static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
+					   struct drm_dp_mst_branch *mstb,
+					   struct drm_dp_mst_port *port)
+{
+	int len;
+	struct drm_dp_sideband_msg_tx *txmsg;
+	int ret;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = mstb;
+	len = build_enum_path_resources(txmsg, port->port_num);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+		if (txmsg->reply.reply_type == 1)
+			DRM_DEBUG_KMS("enum path resources nak received\n");
+		else {
+			if (port->port_num != txmsg->reply.u.path_resources.port_number)
+				DRM_ERROR("got incorrect port in response\n");
+			DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number,
+			       txmsg->reply.u.path_resources.avail_payload_bw_number);
+			port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number;
+		}
+	}
+
+	kfree(txmsg);
+	return 0;
+}
+
+static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
+				   struct drm_dp_mst_port *port,
+				   int id,
+				   int pbn)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+	struct drm_dp_mst_branch *mstb;
+	int len, ret;
+
+	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+	if (!mstb)
+		return -EINVAL;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg) {
+		ret = -ENOMEM;
+		goto fail_put;
+	}
+
+	txmsg->dst = mstb;
+	len = build_allocate_payload(txmsg, port->port_num,
+				     id,
+				     pbn);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+		if (txmsg->reply.reply_type == 1) {
+			ret = -EINVAL;
+		} else
+			ret = 0;
+	}
+	kfree(txmsg);
+fail_put:
+	drm_dp_put_mst_branch_device(mstb);
+	return ret;
+}
+
+static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+				       int id,
+				       struct drm_dp_payload *payload)
+{
+	int ret;
+
+	ret = drm_dp_dpcd_write_payload(mgr, id, payload);
+	if (ret < 0) {
+		payload->payload_state = 0;
+		return ret;
+	}
+	payload->payload_state = DP_PAYLOAD_LOCAL;
+	return 0;
+}
+
+static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+				       struct drm_dp_mst_port *port,
+				       int id,
+				       struct drm_dp_payload *payload)
+{
+	int ret;
+	ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
+	if (ret < 0)
+		return ret;
+	payload->payload_state = DP_PAYLOAD_REMOTE;
+	return ret;
+}
+
+static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
+					struct drm_dp_mst_port *port,
+					int id,
+					struct drm_dp_payload *payload)
+{
+	DRM_DEBUG_KMS("\n");
+	/* its okay for these to fail */
+	if (port) {
+		drm_dp_payload_send_msg(mgr, port, id, 0);
+	}
+
+	drm_dp_dpcd_write_payload(mgr, id, payload);
+	payload->payload_state = 0;
+	return 0;
+}
+
+static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
+					int id,
+					struct drm_dp_payload *payload)
+{
+	payload->payload_state = 0;
+	return 0;
+}
+
+/**
+ * drm_dp_update_payload_part1() - Execute payload update part 1
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step just writes the VCPI to the MST device. For slots->0
+ * transitions, this writes the updated VCPIs and removes the
+ * remote VC payloads.
+ *
+ * after calling this the driver should generate ACT and payload
+ * packets.
+ */
+int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int i;
+	int cur_slots = 1;
+	struct drm_dp_payload req_payload;
+	struct drm_dp_mst_port *port;
+
+	mutex_lock(&mgr->payload_lock);
+	for (i = 0; i < mgr->max_payloads; i++) {
+		/* solve the current payloads - compare to the hw ones
+		   - update the hw view */
+		req_payload.start_slot = cur_slots;
+		if (mgr->proposed_vcpis[i]) {
+			port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+			req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
+		} else {
+			port = NULL;
+			req_payload.num_slots = 0;
+		}
+		/* work out what is required to happen with this payload */
+		if (mgr->payloads[i].start_slot != req_payload.start_slot ||
+		    mgr->payloads[i].num_slots != req_payload.num_slots) {
+
+			/* need to push an update for this payload */
+			if (req_payload.num_slots) {
+				drm_dp_create_payload_step1(mgr, i + 1, &req_payload);
+				mgr->payloads[i].num_slots = req_payload.num_slots;
+			} else if (mgr->payloads[i].num_slots) {
+				mgr->payloads[i].num_slots = 0;
+				drm_dp_destroy_payload_step1(mgr, port, i + 1, &mgr->payloads[i]);
+				req_payload.payload_state = mgr->payloads[i].payload_state;
+			} else
+				req_payload.payload_state = 0;
+
+			mgr->payloads[i].start_slot = req_payload.start_slot;
+			mgr->payloads[i].payload_state = req_payload.payload_state;
+		}
+		cur_slots += req_payload.num_slots;
+	}
+	mutex_unlock(&mgr->payload_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part1);
+
+/**
+ * drm_dp_update_payload_part2() - Execute payload update part 2
+ * @mgr: manager to use.
+ *
+ * This iterates over all proposed virtual channels, and tries to
+ * allocate space in the link for them. For 0->slots transitions,
+ * this step writes the remote VC payload commands. For slots->0
+ * this just resets some internal state.
+ */
+int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct drm_dp_mst_port *port;
+	int i;
+	int ret = 0;
+	mutex_lock(&mgr->payload_lock);
+	for (i = 0; i < mgr->max_payloads; i++) {
+
+		if (!mgr->proposed_vcpis[i])
+			continue;
+
+		port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+
+		DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state);
+		if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
+			ret = drm_dp_create_payload_step2(mgr, port, i + 1, &mgr->payloads[i]);
+		} else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
+			ret = drm_dp_destroy_payload_step2(mgr, i + 1, &mgr->payloads[i]);
+		}
+		if (ret) {
+			mutex_unlock(&mgr->payload_lock);
+			return ret;
+		}
+	}
+	mutex_unlock(&mgr->payload_lock);
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_update_payload_part2);
+
+#if 0 /* unused as of yet */
+static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
+				 struct drm_dp_mst_port *port,
+				 int offset, int size)
+{
+	int len;
+	struct drm_dp_sideband_msg_tx *txmsg;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	len = build_dpcd_read(txmsg, port->port_num, 0, 8);
+	txmsg->dst = port->parent;
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	return 0;
+}
+#endif
+
+static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
+				  struct drm_dp_mst_port *port,
+				  int offset, int size, u8 *bytes)
+{
+	int len;
+	int ret;
+	struct drm_dp_sideband_msg_tx *txmsg;
+	struct drm_dp_mst_branch *mstb;
+
+	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+	if (!mstb)
+		return -EINVAL;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg) {
+		ret = -ENOMEM;
+		goto fail_put;
+	}
+
+	len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
+	txmsg->dst = mstb;
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+		if (txmsg->reply.reply_type == 1) {
+			ret = -EINVAL;
+		} else
+			ret = 0;
+	}
+	kfree(txmsg);
+fail_put:
+	drm_dp_put_mst_branch_device(mstb);
+	return ret;
+}
+
+static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
+{
+	struct drm_dp_sideband_msg_reply_body reply;
+
+	reply.reply_type = 1;
+	reply.req_type = req_type;
+	drm_dp_encode_sideband_reply(&reply, msg);
+	return 0;
+}
+
+static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
+				    struct drm_dp_mst_branch *mstb,
+				    int req_type, int seqno, bool broadcast)
+{
+	struct drm_dp_sideband_msg_tx *txmsg;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg)
+		return -ENOMEM;
+
+	txmsg->dst = mstb;
+	txmsg->seqno = seqno;
+	drm_dp_encode_up_ack_reply(txmsg, req_type);
+
+	mutex_lock(&mgr->qlock);
+	list_add_tail(&txmsg->next, &mgr->tx_msg_upq);
+	if (!mgr->tx_up_in_progress) {
+		process_single_up_tx_qlock(mgr);
+	}
+	mutex_unlock(&mgr->qlock);
+	return 0;
+}
+
+static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count)
+{
+	switch (dp_link_bw) {
+	case DP_LINK_BW_1_62:
+		return 3 * dp_link_count;
+	case DP_LINK_BW_2_7:
+		return 5 * dp_link_count;
+	case DP_LINK_BW_5_4:
+		return 10 * dp_link_count;
+	}
+	return 0;
+}
+
+/**
+ * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
+ * @mgr: manager to set state for
+ * @mst_state: true to enable MST on this connector - false to disable.
+ *
+ * This is called by the driver when it detects an MST capable device plugged
+ * into a DP MST capable port, or when a DP MST capable device is unplugged.
+ */
+int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
+{
+	int ret = 0;
+	struct drm_dp_mst_branch *mstb = NULL;
+
+	mutex_lock(&mgr->lock);
+	if (mst_state == mgr->mst_state)
+		goto out_unlock;
+
+	mgr->mst_state = mst_state;
+	/* set the device into MST mode */
+	if (mst_state) {
+		WARN_ON(mgr->mst_primary);
+
+		/* get dpcd info */
+		ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+		if (ret != DP_RECEIVER_CAP_SIZE) {
+			DRM_DEBUG_KMS("failed to read DPCD\n");
+			goto out_unlock;
+		}
+
+		mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK);
+		mgr->total_pbn = 2560;
+		mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div);
+		mgr->avail_slots = mgr->total_slots;
+
+		/* add initial branch device at LCT 1 */
+		mstb = drm_dp_add_mst_branch_device(1, NULL);
+		if (mstb == NULL) {
+			ret = -ENOMEM;
+			goto out_unlock;
+		}
+		mstb->mgr = mgr;
+
+		/* give this the main reference */
+		mgr->mst_primary = mstb;
+		kref_get(&mgr->mst_primary->kref);
+
+		{
+			struct drm_dp_payload reset_pay;
+			reset_pay.start_slot = 0;
+			reset_pay.num_slots = 0x3f;
+			drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
+		}
+
+		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+					 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+		if (ret < 0) {
+			goto out_unlock;
+		}
+
+
+		/* sort out guid */
+		ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16);
+		if (ret != 16) {
+			DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret);
+			goto out_unlock;
+		}
+
+		mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid);
+		if (!mgr->guid_valid) {
+			ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16);
+			mgr->guid_valid = true;
+		}
+
+		queue_work(system_long_wq, &mgr->work);
+
+		ret = 0;
+	} else {
+		/* disable MST on the device */
+		mstb = mgr->mst_primary;
+		mgr->mst_primary = NULL;
+		/* this can fail if the device is gone */
+		drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
+		ret = 0;
+		memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload));
+		mgr->payload_mask = 0;
+		set_bit(0, &mgr->payload_mask);
+	}
+
+out_unlock:
+	mutex_unlock(&mgr->lock);
+	if (mstb)
+		drm_dp_put_mst_branch_device(mstb);
+	return ret;
+
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
+
+/**
+ * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
+ * @mgr: manager to suspend
+ *
+ * This function tells the MST device that we can't handle UP messages
+ * anymore. This should stop it from sending any since we are suspended.
+ */
+void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
+{
+	mutex_lock(&mgr->lock);
+	drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
+	mutex_unlock(&mgr->lock);
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
+
+/**
+ * drm_dp_mst_topology_mgr_resume() - resume the MST manager
+ * @mgr: manager to resume
+ *
+ * This will fetch DPCD and see if the device is still there,
+ * if it is, it will rewrite the MSTM control bits, and return.
+ *
+ * if the device fails this returns -1, and the driver should do
+ * a full MST reprobe, in case we were undocked.
+ */
+int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int ret = 0;
+
+	mutex_lock(&mgr->lock);
+
+	if (mgr->mst_primary) {
+		int sret;
+		sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
+		if (sret != DP_RECEIVER_CAP_SIZE) {
+			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
+			ret = -1;
+			goto out_unlock;
+		}
+
+		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
+					 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
+		if (ret < 0) {
+			DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
+			ret = -1;
+			goto out_unlock;
+		}
+		ret = 0;
+	} else
+		ret = -1;
+
+out_unlock:
+	mutex_unlock(&mgr->lock);
+	return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
+
+static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
+{
+	int len;
+	u8 replyblock[32];
+	int replylen, origlen, curreply;
+	int ret;
+	struct drm_dp_sideband_msg_rx *msg;
+	int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE;
+	msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv;
+
+	len = min(mgr->max_dpcd_transaction_bytes, 16);
+	ret = drm_dp_dpcd_read(mgr->aux, basereg,
+			       replyblock, len);
+	if (ret != len) {
+		DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret);
+		return;
+	}
+	ret = drm_dp_sideband_msg_build(msg, replyblock, len, true);
+	if (!ret) {
+		DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]);
+		return;
+	}
+	replylen = msg->curchunk_len + msg->curchunk_hdrlen;
+
+	origlen = replylen;
+	replylen -= len;
+	curreply = len;
+	while (replylen > 0) {
+		len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16);
+		ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
+				    replyblock, len);
+		if (ret != len) {
+			DRM_DEBUG_KMS("failed to read a chunk\n");
+		}
+		ret = drm_dp_sideband_msg_build(msg, replyblock, len, false);
+		if (ret == false)
+			DRM_DEBUG_KMS("failed to build sideband msg\n");
+		curreply += len;
+		replylen -= len;
+	}
+}
+
+static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int ret = 0;
+
+	drm_dp_get_one_sb_msg(mgr, false);
+
+	if (mgr->down_rep_recv.have_eomt) {
+		struct drm_dp_sideband_msg_tx *txmsg;
+		struct drm_dp_mst_branch *mstb;
+		int slot = -1;
+		mstb = drm_dp_get_mst_branch_device(mgr,
+						    mgr->down_rep_recv.initial_hdr.lct,
+						    mgr->down_rep_recv.initial_hdr.rad);
+
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct);
+			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
+
+		/* find the message */
+		slot = mgr->down_rep_recv.initial_hdr.seqno;
+		mutex_lock(&mgr->qlock);
+		txmsg = mstb->tx_slots[slot];
+		/* remove from slots */
+		mutex_unlock(&mgr->qlock);
+
+		if (!txmsg) {
+			DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
+			       mstb,
+			       mgr->down_rep_recv.initial_hdr.seqno,
+			       mgr->down_rep_recv.initial_hdr.lct,
+				      mgr->down_rep_recv.initial_hdr.rad[0],
+				      mgr->down_rep_recv.msg[0]);
+			drm_dp_put_mst_branch_device(mstb);
+			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
+
+		drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
+		if (txmsg->reply.reply_type == 1) {
+			DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data);
+		}
+
+		memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+		drm_dp_put_mst_branch_device(mstb);
+
+		mutex_lock(&mgr->qlock);
+		txmsg->state = DRM_DP_SIDEBAND_TX_RX;
+		mstb->tx_slots[slot] = NULL;
+		mutex_unlock(&mgr->qlock);
+
+		wake_up(&mgr->tx_waitq);
+	}
+	return ret;
+}
+
+static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
+{
+	int ret = 0;
+	drm_dp_get_one_sb_msg(mgr, true);
+
+	if (mgr->up_req_recv.have_eomt) {
+		struct drm_dp_sideband_msg_req_body msg;
+		struct drm_dp_mst_branch *mstb;
+		bool seqno;
+		mstb = drm_dp_get_mst_branch_device(mgr,
+						    mgr->up_req_recv.initial_hdr.lct,
+						    mgr->up_req_recv.initial_hdr.rad);
+		if (!mstb) {
+			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
+			memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+			return 0;
+		}
+
+		seqno = mgr->up_req_recv.initial_hdr.seqno;
+		drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
+
+		if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
+			drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+			drm_dp_update_port(mstb, &msg.u.conn_stat);
+			DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
+			(*mgr->cbs->hotplug)(mgr);
+
+		} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
+			drm_dp_send_up_ack_reply(mgr, mstb, msg.req_type, seqno, false);
+			DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
+		}
+
+		drm_dp_put_mst_branch_device(mstb);
+		memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
+	}
+	return ret;
+}
+
+/**
+ * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify
+ * @mgr: manager to notify irq for.
+ * @esi: 4 bytes from SINK_COUNT_ESI
+ *
+ * This should be called from the driver when it detects a short IRQ,
+ * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The
+ * topology manager will process the sideband messages received as a result
+ * of this.
+ */
+int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled)
+{
+	int ret = 0;
+	int sc;
+	*handled = false;
+	sc = esi[0] & 0x3f;
+
+	if (sc != mgr->sink_count) {
+		mgr->sink_count = sc;
+		*handled = true;
+	}
+
+	if (esi[1] & DP_DOWN_REP_MSG_RDY) {
+		ret = drm_dp_mst_handle_down_rep(mgr);
+		*handled = true;
+	}
+
+	if (esi[1] & DP_UP_REQ_MSG_RDY) {
+		ret |= drm_dp_mst_handle_up_req(mgr);
+		*handled = true;
+	}
+
+	drm_dp_mst_kick_tx(mgr);
+	return ret;
+}
+EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
+
+/**
+ * drm_dp_mst_detect_port() - get connection status for an MST port
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port
+ *
+ * This returns the current connection state for a port. It validates the
+ * port pointer still exists so the caller doesn't require a reference
+ */
+enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	enum drm_connector_status status = connector_status_disconnected;
+
+	/* we need to search for the port in the mgr in case its gone */
+	port = drm_dp_get_validated_port_ref(mgr, port);
+	if (!port)
+		return connector_status_disconnected;
+
+	if (!port->ddps)
+		goto out;
+
+	switch (port->pdt) {
+	case DP_PEER_DEVICE_NONE:
+	case DP_PEER_DEVICE_MST_BRANCHING:
+		break;
+
+	case DP_PEER_DEVICE_SST_SINK:
+		status = connector_status_connected;
+		break;
+	case DP_PEER_DEVICE_DP_LEGACY_CONV:
+		if (port->ldps)
+			status = connector_status_connected;
+		break;
+	}
+out:
+	drm_dp_put_port(port);
+	return status;
+}
+EXPORT_SYMBOL(drm_dp_mst_detect_port);
+
+/**
+ * drm_dp_mst_get_edid() - get EDID for an MST port
+ * @connector: toplevel connector to get EDID for
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This returns an EDID for the port connected to a connector,
+ * It validates the pointer still exists so the caller doesn't require a
+ * reference.
+ */
+struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	struct edid *edid = NULL;
+
+	/* we need to search for the port in the mgr in case its gone */
+	port = drm_dp_get_validated_port_ref(mgr, port);
+	if (!port)
+		return NULL;
+
+	edid = drm_get_edid(connector, &port->aux.ddc);
+	drm_dp_put_port(port);
+	return edid;
+}
+EXPORT_SYMBOL(drm_dp_mst_get_edid);
+
+/**
+ * drm_dp_find_vcpi_slots() - find slots for this PBN value
+ * @mgr: manager to use
+ * @pbn: payload bandwidth to convert into slots.
+ */
+int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
+			   int pbn)
+{
+	int num_slots;
+
+	num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	if (num_slots > mgr->avail_slots)
+		return -ENOSPC;
+	return num_slots;
+}
+EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
+
+static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+			    struct drm_dp_vcpi *vcpi, int pbn)
+{
+	int num_slots;
+	int ret;
+
+	num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
+
+	if (num_slots > mgr->avail_slots)
+		return -ENOSPC;
+
+	vcpi->pbn = pbn;
+	vcpi->aligned_pbn = num_slots * mgr->pbn_div;
+	vcpi->num_slots = num_slots;
+
+	ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+/**
+ * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
+ * @mgr: manager for this port
+ * @port: port to allocate a virtual channel for.
+ * @pbn: payload bandwidth number to request
+ * @slots: returned number of slots for this PBN.
+ */
+bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, int pbn, int *slots)
+{
+	int ret;
+
+	port = drm_dp_get_validated_port_ref(mgr, port);
+	if (!port)
+		return false;
+
+	if (port->vcpi.vcpi > 0) {
+		DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn);
+		if (pbn == port->vcpi.pbn) {
+			*slots = port->vcpi.num_slots;
+			return true;
+		}
+	}
+
+	ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn);
+	if (ret) {
+		DRM_DEBUG_KMS("failed to init vcpi %d %d %d\n", DIV_ROUND_UP(pbn, mgr->pbn_div), mgr->avail_slots, ret);
+		goto out;
+	}
+	DRM_DEBUG_KMS("initing vcpi for %d %d\n", pbn, port->vcpi.num_slots);
+	*slots = port->vcpi.num_slots;
+
+	drm_dp_put_port(port);
+	return true;
+out:
+	return false;
+}
+EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
+
+/**
+ * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
+ * @mgr: manager for this port
+ * @port: unverified pointer to a port.
+ *
+ * This just resets the number of slots for the ports VCPI for later programming.
+ */
+void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	port = drm_dp_get_validated_port_ref(mgr, port);
+	if (!port)
+		return;
+	port->vcpi.num_slots = 0;
+	drm_dp_put_port(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
+
+/**
+ * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
+ * @mgr: manager for this port
+ * @port: unverified port to deallocate vcpi for
+ */
+void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+{
+	port = drm_dp_get_validated_port_ref(mgr, port);
+	if (!port)
+		return;
+
+	drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
+	port->vcpi.num_slots = 0;
+	port->vcpi.pbn = 0;
+	port->vcpi.aligned_pbn = 0;
+	port->vcpi.vcpi = 0;
+	drm_dp_put_port(port);
+}
+EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
+
+static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
+				     int id, struct drm_dp_payload *payload)
+{
+	u8 payload_alloc[3], status;
+	int ret;
+	int retries = 0;
+
+	drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
+			   DP_PAYLOAD_TABLE_UPDATED);
+
+	payload_alloc[0] = id;
+	payload_alloc[1] = payload->start_slot;
+	payload_alloc[2] = payload->num_slots;
+
+	ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
+	if (ret != 3) {
+		DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret);
+		goto fail;
+	}
+
+retry:
+	ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+	if (ret < 0) {
+		DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+		goto fail;
+	}
+
+	if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
+		retries++;
+		if (retries < 20) {
+			usleep_range(10000, 20000);
+			goto retry;
+		}
+		DRM_DEBUG_KMS("status not set after read payload table status %d\n", status);
+		ret = -EINVAL;
+		goto fail;
+	}
+	ret = 0;
+fail:
+	return ret;
+}
+
+
+/**
+ * drm_dp_check_act_status() - Check ACT handled status.
+ * @mgr: manager to use
+ *
+ * Check the payload status bits in the DPCD for ACT handled completion.
+ */
+int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
+{
+	u8 status;
+	int ret;
+	int count = 0;
+
+	do {
+		ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
+
+		if (ret < 0) {
+			DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
+			goto fail;
+		}
+
+		if (status & DP_PAYLOAD_ACT_HANDLED)
+			break;
+		count++;
+		udelay(100);
+
+	} while (count < 30);
+
+	if (!(status & DP_PAYLOAD_ACT_HANDLED)) {
+		DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count);
+		ret = -EINVAL;
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+EXPORT_SYMBOL(drm_dp_check_act_status);
+
+/**
+ * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
+ * @clock: dot clock for the mode
+ * @bpp: bpp for the mode.
+ *
+ * This uses the formula in the spec to calculate the PBN value for a mode.
+ */
+int drm_dp_calc_pbn_mode(int clock, int bpp)
+{
+	fixed20_12 pix_bw;
+	fixed20_12 fbpp;
+	fixed20_12 result;
+	fixed20_12 margin, tmp;
+	u32 res;
+
+	pix_bw.full = dfixed_const(clock);
+	fbpp.full = dfixed_const(bpp);
+	tmp.full = dfixed_const(8);
+	fbpp.full = dfixed_div(fbpp, tmp);
+
+	result.full = dfixed_mul(pix_bw, fbpp);
+	margin.full = dfixed_const(54);
+	tmp.full = dfixed_const(64);
+	margin.full = dfixed_div(margin, tmp);
+	result.full = dfixed_div(result, margin);
+
+	margin.full = dfixed_const(1006);
+	tmp.full = dfixed_const(1000);
+	margin.full = dfixed_div(margin, tmp);
+	result.full = dfixed_mul(result, margin);
+
+	result.full = dfixed_div(result, tmp);
+	result.full = dfixed_ceil(result);
+	res = dfixed_trunc(result);
+	return res;
+}
+EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
+
+static int test_calc_pbn_mode(void)
+{
+	int ret;
+	ret = drm_dp_calc_pbn_mode(154000, 30);
+	if (ret != 689)
+		return -EINVAL;
+	ret = drm_dp_calc_pbn_mode(234000, 30);
+	if (ret != 1047)
+		return -EINVAL;
+	return 0;
+}
+
+/* we want to kick the TX after we've ack the up/down IRQs. */
+static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
+{
+	queue_work(system_long_wq, &mgr->tx_work);
+}
+
+static void drm_dp_mst_dump_mstb(struct seq_file *m,
+				 struct drm_dp_mst_branch *mstb)
+{
+	struct drm_dp_mst_port *port;
+	int tabs = mstb->lct;
+	char prefix[10];
+	int i;
+
+	for (i = 0; i < tabs; i++)
+		prefix[i] = '\t';
+	prefix[i] = '\0';
+
+	seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports);
+	list_for_each_entry(port, &mstb->ports, next) {
+		seq_printf(m, "%sport: %d: ddps: %d ldps: %d, %p, conn: %p\n", prefix, port->port_num, port->ddps, port->ldps, port, port->connector);
+		if (port->mstb)
+			drm_dp_mst_dump_mstb(m, port->mstb);
+	}
+}
+
+static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
+				  char *buf)
+{
+	int ret;
+	int i;
+	for (i = 0; i < 4; i++) {
+		ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16);
+		if (ret != 16)
+			break;
+	}
+	if (i == 4)
+		return true;
+	return false;
+}
+
+/**
+ * drm_dp_mst_dump_topology(): dump topology to seq file.
+ * @m: seq_file to dump output to
+ * @mgr: manager to dump current topology for.
+ *
+ * helper to dump MST topology to a seq file for debugfs.
+ */
+void drm_dp_mst_dump_topology(struct seq_file *m,
+			      struct drm_dp_mst_topology_mgr *mgr)
+{
+	int i;
+	struct drm_dp_mst_port *port;
+	mutex_lock(&mgr->lock);
+	if (mgr->mst_primary)
+		drm_dp_mst_dump_mstb(m, mgr->mst_primary);
+
+	/* dump VCPIs */
+	mutex_unlock(&mgr->lock);
+
+	mutex_lock(&mgr->payload_lock);
+	seq_printf(m, "vcpi: %lx\n", mgr->payload_mask);
+
+	for (i = 0; i < mgr->max_payloads; i++) {
+		if (mgr->proposed_vcpis[i]) {
+			port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
+			seq_printf(m, "vcpi %d: %d %d %d\n", i, port->port_num, port->vcpi.vcpi, port->vcpi.num_slots);
+		} else
+			seq_printf(m, "vcpi %d:unsed\n", i);
+	}
+	for (i = 0; i < mgr->max_payloads; i++) {
+		seq_printf(m, "payload %d: %d, %d, %d\n",
+			   i,
+			   mgr->payloads[i].payload_state,
+			   mgr->payloads[i].start_slot,
+			   mgr->payloads[i].num_slots);
+
+
+	}
+	mutex_unlock(&mgr->payload_lock);
+
+	mutex_lock(&mgr->lock);
+	if (mgr->mst_primary) {
+		u8 buf[64];
+		bool bret;
+		int ret;
+		ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
+		seq_printf(m, "dpcd: ");
+		for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++)
+			seq_printf(m, "%02x ", buf[i]);
+		seq_printf(m, "\n");
+		ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2);
+		seq_printf(m, "faux/mst: ");
+		for (i = 0; i < 2; i++)
+			seq_printf(m, "%02x ", buf[i]);
+		seq_printf(m, "\n");
+		ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1);
+		seq_printf(m, "mst ctrl: ");
+		for (i = 0; i < 1; i++)
+			seq_printf(m, "%02x ", buf[i]);
+		seq_printf(m, "\n");
+
+		bret = dump_dp_payload_table(mgr, buf);
+		if (bret == true) {
+			seq_printf(m, "payload table: ");
+			for (i = 0; i < 63; i++)
+				seq_printf(m, "%02x ", buf[i]);
+			seq_printf(m, "\n");
+		}
+
+	}
+
+	mutex_unlock(&mgr->lock);
+
+}
+EXPORT_SYMBOL(drm_dp_mst_dump_topology);
+
+static void drm_dp_tx_work(struct work_struct *work)
+{
+	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);
+
+	mutex_lock(&mgr->qlock);
+	if (mgr->tx_down_in_progress)
+		process_single_down_tx_qlock(mgr);
+	mutex_unlock(&mgr->qlock);
+}
+
+/**
+ * drm_dp_mst_topology_mgr_init - initialise a topology manager
+ * @mgr: manager struct to initialise
+ * @dev: device providing this structure - for i2c addition.
+ * @aux: DP helper aux channel to talk to this device
+ * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
+ * @max_payloads: maximum number of payloads this GPU can source
+ * @conn_base_id: the connector object ID the MST device is connected to.
+ *
+ * Return 0 for success, or negative error code on failure
+ */
+int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+				 struct device *dev, struct drm_dp_aux *aux,
+				 int max_dpcd_transaction_bytes,
+				 int max_payloads, int conn_base_id)
+{
+	mutex_init(&mgr->lock);
+	mutex_init(&mgr->qlock);
+	mutex_init(&mgr->payload_lock);
+	INIT_LIST_HEAD(&mgr->tx_msg_upq);
+	INIT_LIST_HEAD(&mgr->tx_msg_downq);
+	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
+	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
+	init_waitqueue_head(&mgr->tx_waitq);
+	mgr->dev = dev;
+	mgr->aux = aux;
+	mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
+	mgr->max_payloads = max_payloads;
+	mgr->conn_base_id = conn_base_id;
+	mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
+	if (!mgr->payloads)
+		return -ENOMEM;
+	mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
+	if (!mgr->proposed_vcpis)
+		return -ENOMEM;
+	set_bit(0, &mgr->payload_mask);
+	test_calc_pbn_mode();
+	return 0;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
+
+/**
+ * drm_dp_mst_topology_mgr_destroy() - destroy topology manager.
+ * @mgr: manager to destroy
+ */
+void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
+{
+	mutex_lock(&mgr->payload_lock);
+	kfree(mgr->payloads);
+	mgr->payloads = NULL;
+	kfree(mgr->proposed_vcpis);
+	mgr->proposed_vcpis = NULL;
+	mutex_unlock(&mgr->payload_lock);
+	mgr->dev = NULL;
+	mgr->aux = NULL;
+}
+EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
+
+/* I2C device */
+static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
+			       int num)
+{
+	struct drm_dp_aux *aux = adapter->algo_data;
+	struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux);
+	struct drm_dp_mst_branch *mstb;
+	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
+	unsigned int i;
+	bool reading = false;
+	struct drm_dp_sideband_msg_req_body msg;
+	struct drm_dp_sideband_msg_tx *txmsg = NULL;
+	int ret;
+
+	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+	if (!mstb)
+		return -EREMOTEIO;
+
+	/* construct i2c msg */
+	/* see if last msg is a read */
+	if (msgs[num - 1].flags & I2C_M_RD)
+		reading = true;
+
+	if (!reading) {
+		DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
+		ret = -EIO;
+		goto out;
+	}
+
+	msg.req_type = DP_REMOTE_I2C_READ;
+	msg.u.i2c_read.num_transactions = num - 1;
+	msg.u.i2c_read.port_number = port->port_num;
+	for (i = 0; i < num - 1; i++) {
+		msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
+		msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
+		msg.u.i2c_read.transactions[i].bytes = msgs[i].buf;
+	}
+	msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
+	msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
+
+	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+	if (!txmsg) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	txmsg->dst = mstb;
+	drm_dp_encode_sideband_req(&msg, txmsg);
+
+	drm_dp_queue_down_tx(mgr, txmsg);
+
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret > 0) {
+
+		if (txmsg->reply.reply_type == 1) { /* got a NAK back */
+			ret = -EREMOTEIO;
+			goto out;
+		}
+		if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
+			ret = -EIO;
+			goto out;
+		}
+		memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
+		ret = num;
+	}
+out:
+	kfree(txmsg);
+	drm_dp_put_mst_branch_device(mstb);
+	return ret;
+}
+
+static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+	       I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
+	.functionality = drm_dp_mst_i2c_functionality,
+	.master_xfer = drm_dp_mst_i2c_xfer,
+};
+
+/**
+ * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
+ * @aux: DisplayPort AUX channel
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
+{
+	aux->ddc.algo = &drm_dp_mst_i2c_algo;
+	aux->ddc.algo_data = aux;
+	aux->ddc.retries = 3;
+
+	aux->ddc.class = I2C_CLASS_DDC;
+	aux->ddc.owner = THIS_MODULE;
+	aux->ddc.dev.parent = aux->dev;
+	aux->ddc.dev.of_node = aux->dev->of_node;
+
+	strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
+		sizeof(aux->ddc.name));
+
+	return i2c_add_adapter(&aux->ddc);
+}
+
+/**
+ * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
+ * @aux: DisplayPort AUX channel
+ */
+static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
+{
+	i2c_del_adapter(&aux->ddc);
+}

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

@@ -233,7 +233,7 @@ module_exit(drm_core_exit);
 /**
 /**
  * Copy and IOCTL return string to user space
  * Copy and IOCTL return string to user space
  */
  */
-static int drm_copy_field(char *buf, size_t *buf_len, const char *value)
+static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value)
 {
 {
 	int len;
 	int len;
 
 

+ 1 - 0
drivers/gpu/drm/drm_edid.c

@@ -3305,6 +3305,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
 	struct drm_device *dev = encoder->dev;
 	struct drm_device *dev = encoder->dev;
 
 
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 	WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
 
 
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head)
 		if (connector->encoder == encoder && connector->eld[0])
 		if (connector->encoder == encoder && connector->eld[0])

+ 3 - 2
drivers/gpu/drm/drm_fb_cma_helper.c

@@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
 	return ret;
 	return ret;
 }
 }
 
 
-static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
+static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
 	.fb_probe = drm_fbdev_cma_create,
 	.fb_probe = drm_fbdev_cma_create,
 };
 };
 
 
@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 	}
 	}
 
 
-	fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
 	helper = &fbdev_cma->fb_helper;
 	helper = &fbdev_cma->fb_helper;
 
 
+	drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
+
 	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
 	ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
 		dev_err(dev->dev, "Failed to initialize drm fb helper.\n");

+ 95 - 20
drivers/gpu/drm/drm_fb_helper.c

@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
  * helper functions used by many drivers to implement the kernel mode setting
  * helper functions used by many drivers to implement the kernel mode setting
  * interfaces.
  * interfaces.
  *
  *
- * Initialization is done as a three-step process with drm_fb_helper_init(),
- * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
- * Drivers with fancier requirements than the default behaviour can override the
- * second step with their own code.  Teardown is done with drm_fb_helper_fini().
+ * Initialization is done as a four-step process with drm_fb_helper_prepare(),
+ * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
+ * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
+ * default behaviour can override the third step with their own code.
+ * Teardown is done with drm_fb_helper_fini().
  *
  *
  * At runtime drivers should restore the fbdev console by calling
  * At runtime drivers should restore the fbdev console by calling
  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
  * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
@@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
  *
  *
  * All other functions exported by the fb helper library can be used to
  * All other functions exported by the fb helper library can be used to
  * implement the fbdev driver interface by the driver.
  * implement the fbdev driver interface by the driver.
+ *
+ * It is possible, though perhaps somewhat tricky, to implement race-free
+ * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
+ * helper must be called first to initialize the minimum required to make
+ * hotplug detection work. Drivers also need to make sure to properly set up
+ * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
+ * it is safe to enable interrupts and start processing hotplug events. At the
+ * same time, drivers should initialize all modeset objects such as CRTCs,
+ * encoders and connectors. To finish up the fbdev helper initialization, the
+ * drm_fb_helper_init() function is called. To probe for all attached displays
+ * and set up an initial configuration using the detected hardware, drivers
+ * should call drm_fb_helper_single_add_all_connectors() followed by
+ * drm_fb_helper_initial_config().
  */
  */
 
 
 /**
 /**
@@ -105,6 +119,58 @@ fail:
 }
 }
 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
 EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
 
 
+int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
+{
+	struct drm_fb_helper_connector **temp;
+	struct drm_fb_helper_connector *fb_helper_connector;
+
+	WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
+	if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
+		temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector) * (fb_helper->connector_count + 1), GFP_KERNEL);
+		if (!temp)
+			return -ENOMEM;
+
+		fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
+		fb_helper->connector_info = temp;
+	}
+
+
+	fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
+	if (!fb_helper_connector)
+		return -ENOMEM;
+
+	fb_helper_connector->connector = connector;
+	fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
+	return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
+
+int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
+				       struct drm_connector *connector)
+{
+	struct drm_fb_helper_connector *fb_helper_connector;
+	int i, j;
+
+	WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
+
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		if (fb_helper->connector_info[i]->connector == connector)
+			break;
+	}
+
+	if (i == fb_helper->connector_count)
+		return -EINVAL;
+	fb_helper_connector = fb_helper->connector_info[i];
+
+	for (j = i + 1; j < fb_helper->connector_count; j++) {
+		fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
+	}
+	fb_helper->connector_count--;
+	kfree(fb_helper_connector);
+	return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
+
 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
 static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
 {
 {
 	struct drm_fb_helper_connector *fb_helper_conn;
 	struct drm_fb_helper_connector *fb_helper_conn;
@@ -199,9 +265,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
 	struct drm_crtc_helper_funcs *funcs;
 	struct drm_crtc_helper_funcs *funcs;
 	int i;
 	int i;
 
 
-	if (list_empty(&kernel_fb_helper_list))
-		return false;
-
 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
 	list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
 		for (i = 0; i < helper->crtc_count; i++) {
 		for (i = 0; i < helper->crtc_count; i++) {
 			struct drm_mode_set *mode_set =
 			struct drm_mode_set *mode_set =
@@ -530,6 +593,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 	kfree(helper->crtc_info);
 	kfree(helper->crtc_info);
 }
 }
 
 
+/**
+ * drm_fb_helper_prepare - setup a drm_fb_helper structure
+ * @dev: DRM device
+ * @helper: driver-allocated fbdev helper structure to set up
+ * @funcs: pointer to structure of functions associate with this helper
+ *
+ * Sets up the bare minimum to make the framebuffer helper usable. This is
+ * useful to implement race-free initialization of the polling helpers.
+ */
+void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
+			   const struct drm_fb_helper_funcs *funcs)
+{
+	INIT_LIST_HEAD(&helper->kernel_fb_list);
+	helper->funcs = funcs;
+	helper->dev = dev;
+}
+EXPORT_SYMBOL(drm_fb_helper_prepare);
+
 /**
 /**
  * drm_fb_helper_init - initialize a drm_fb_helper structure
  * drm_fb_helper_init - initialize a drm_fb_helper structure
  * @dev: drm device
  * @dev: drm device
@@ -542,8 +623,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
  * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
  * to allow driver writes more control over the exact init sequence.
  * to allow driver writes more control over the exact init sequence.
  *
  *
- * Drivers must set fb_helper->funcs before calling
- * drm_fb_helper_initial_config().
+ * Drivers must call drm_fb_helper_prepare() before calling this function.
  *
  *
  * RETURNS:
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
  * Zero if everything went ok, nonzero otherwise.
@@ -558,10 +638,6 @@ int drm_fb_helper_init(struct drm_device *dev,
 	if (!max_conn_count)
 	if (!max_conn_count)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	fb_helper->dev = dev;
-
-	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
-
 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 	if (!fb_helper->crtc_info)
 	if (!fb_helper->crtc_info)
 		return -ENOMEM;
 		return -ENOMEM;
@@ -572,6 +648,7 @@ int drm_fb_helper_init(struct drm_device *dev,
 		kfree(fb_helper->crtc_info);
 		kfree(fb_helper->crtc_info);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
+	fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
 	fb_helper->connector_count = 0;
 	fb_helper->connector_count = 0;
 
 
 	for (i = 0; i < crtc_count; i++) {
 	for (i = 0; i < crtc_count; i++) {
@@ -1056,7 +1133,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 	info->fix.ypanstep = 1; /* doing it in hw */
 	info->fix.ypanstep = 1; /* doing it in hw */
 	info->fix.ywrapstep = 0;
 	info->fix.ywrapstep = 0;
 	info->fix.accel = FB_ACCEL_NONE;
 	info->fix.accel = FB_ACCEL_NONE;
-	info->fix.type_aux = 0;
 
 
 	info->fix.line_length = pitch;
 	info->fix.line_length = pitch;
 	return;
 	return;
@@ -1613,8 +1689,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  * either the output polling work or a work item launched from the driver's
  * either the output polling work or a work item launched from the driver's
  * hotplug interrupt).
  * hotplug interrupt).
  *
  *
- * Note that the driver must ensure that this is only called _after_ the fb has
- * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
+ * Note that drivers may call this even before calling
+ * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
+ * for a race-free fbcon setup and will make sure that the fbdev emulation will
+ * not miss any hotplug events.
  *
  *
  * RETURNS:
  * RETURNS:
  * 0 on success and a non-zero error code otherwise.
  * 0 on success and a non-zero error code otherwise.
@@ -1624,11 +1702,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_device *dev = fb_helper->dev;
 	u32 max_width, max_height;
 	u32 max_width, max_height;
 
 
-	if (!fb_helper->fb)
-		return 0;
-
 	mutex_lock(&fb_helper->dev->mode_config.mutex);
 	mutex_lock(&fb_helper->dev->mode_config.mutex);
-	if (!drm_fb_helper_is_bound(fb_helper)) {
+	if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
 		fb_helper->delayed_hotplug = true;
 		fb_helper->delayed_hotplug = true;
 		mutex_unlock(&fb_helper->dev->mode_config.mutex);
 		mutex_unlock(&fb_helper->dev->mode_config.mutex);
 		return 0;
 		return 0;

+ 20 - 9
drivers/gpu/drm/drm_gem.c

@@ -441,18 +441,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset);
  * drm_gem_get_pages - helper to allocate backing pages for a GEM object
  * drm_gem_get_pages - helper to allocate backing pages for a GEM object
  * from shmem
  * from shmem
  * @obj: obj in question
  * @obj: obj in question
- * @gfpmask: gfp mask of requested pages
+ *
+ * This reads the page-array of the shmem-backing storage of the given gem
+ * object. An array of pages is returned. If a page is not allocated or
+ * swapped-out, this will allocate/swap-in the required pages. Note that the
+ * whole object is covered by the page-array and pinned in memory.
+ *
+ * Use drm_gem_put_pages() to release the array and unpin all pages.
+ *
+ * This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()).
+ * If you require other GFP-masks, you have to do those allocations yourself.
+ *
+ * Note that you are not allowed to change gfp-zones during runtime. That is,
+ * shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as
+ * set during initialization. If you have special zone constraints, set them
+ * after drm_gem_init_object() via mapping_set_gfp_mask(). shmem-core takes care
+ * to keep pages in the required zone during swap-in.
  */
  */
-struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
+struct page **drm_gem_get_pages(struct drm_gem_object *obj)
 {
 {
-	struct inode *inode;
 	struct address_space *mapping;
 	struct address_space *mapping;
 	struct page *p, **pages;
 	struct page *p, **pages;
 	int i, npages;
 	int i, npages;
 
 
 	/* This is the shared memory object that backs the GEM resource */
 	/* This is the shared memory object that backs the GEM resource */
-	inode = file_inode(obj->filp);
-	mapping = inode->i_mapping;
+	mapping = file_inode(obj->filp)->i_mapping;
 
 
 	/* We already BUG_ON() for non-page-aligned sizes in
 	/* We already BUG_ON() for non-page-aligned sizes in
 	 * drm_gem_object_init(), so we should never hit this unless
 	 * drm_gem_object_init(), so we should never hit this unless
@@ -466,10 +479,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
 	if (pages == NULL)
 	if (pages == NULL)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 
 
-	gfpmask |= mapping_gfp_mask(mapping);
-
 	for (i = 0; i < npages; i++) {
 	for (i = 0; i < npages; i++) {
-		p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+		p = shmem_read_mapping_page(mapping, i);
 		if (IS_ERR(p))
 		if (IS_ERR(p))
 			goto fail;
 			goto fail;
 		pages[i] = p;
 		pages[i] = p;
@@ -479,7 +490,7 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
 		 * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping)
 		 * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping)
 		 * so shmem can relocate pages during swapin if required.
 		 * so shmem can relocate pages during swapin if required.
 		 */
 		 */
-		BUG_ON((gfpmask & __GFP_DMA32) &&
+		BUG_ON((mapping_gfp_mask(mapping) & __GFP_DMA32) &&
 				(page_to_pfn(p) >= 0x00100000UL));
 				(page_to_pfn(p) >= 0x00100000UL));
 	}
 	}
 
 

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

@@ -327,7 +327,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
 	/* Create a CMA GEM buffer. */
 	/* Create a CMA GEM buffer. */
 	cma_obj = __drm_gem_cma_create(dev, size);
 	cma_obj = __drm_gem_cma_create(dev, size);
 	if (IS_ERR(cma_obj))
 	if (IS_ERR(cma_obj))
-		return ERR_PTR(PTR_ERR(cma_obj));
+		return ERR_CAST(cma_obj);
 
 
 	cma_obj->paddr = sg_dma_address(sgt->sgl);
 	cma_obj->paddr = sg_dma_address(sgt->sgl);
 	cma_obj->sgt = sgt;
 	cma_obj->sgt = sgt;

+ 0 - 2
drivers/gpu/drm/drm_ioctl.c

@@ -342,8 +342,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
 		file_priv->stereo_allowed = req->value;
 		file_priv->stereo_allowed = req->value;
 		break;
 		break;
 	case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
 	case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
-		if (!drm_universal_planes)
-			return -EINVAL;
 		if (req->value > 1)
 		if (req->value > 1)
 			return -EINVAL;
 			return -EINVAL;
 		file_priv->universal_planes = req->value;
 		file_priv->universal_planes = req->value;

+ 67 - 0
drivers/gpu/drm/drm_of.c

@@ -0,0 +1,67 @@
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/of_graph.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_of.h>
+
+/**
+ * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
+ * @dev: DRM device
+ * @port: port OF node
+ *
+ * Given a port OF node, return the possible mask of the corresponding
+ * CRTC within a device's list of CRTCs.  Returns zero if not found.
+ */
+static uint32_t drm_crtc_port_mask(struct drm_device *dev,
+				   struct device_node *port)
+{
+	unsigned int index = 0;
+	struct drm_crtc *tmp;
+
+	list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
+		if (tmp->port == port)
+			return 1 << index;
+
+		index++;
+	}
+
+	return 0;
+}
+
+/**
+ * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
+ * @dev: DRM device
+ * @port: encoder port to scan for endpoints
+ *
+ * Scan all endpoints attached to a port, locate their attached CRTCs,
+ * and generate the DRM mask of CRTCs which may be attached to this
+ * encoder.
+ *
+ * See Documentation/devicetree/bindings/graph.txt for the bindings.
+ */
+uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
+				    struct device_node *port)
+{
+	struct device_node *remote_port, *ep = NULL;
+	uint32_t possible_crtcs = 0;
+
+	do {
+		ep = of_graph_get_next_endpoint(port, ep);
+		if (!ep)
+			break;
+
+		remote_port = of_graph_get_remote_port(ep);
+		if (!remote_port) {
+			of_node_put(ep);
+			return 0;
+		}
+
+		possible_crtcs |= drm_crtc_port_mask(dev, remote_port);
+
+		of_node_put(remote_port);
+	} while (1);
+
+	return possible_crtcs;
+}
+EXPORT_SYMBOL(drm_of_find_possible_crtcs);

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

@@ -335,9 +335,10 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
 	}
 	}
 
 
 	/* possible_crtc's will be filled in later by crtc_init */
 	/* possible_crtc's will be filled in later by crtc_init */
-	ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs,
-			     formats, num_formats,
-			     DRM_PLANE_TYPE_PRIMARY);
+	ret = drm_universal_plane_init(dev, primary, 0,
+				       &drm_primary_helper_funcs,
+				       formats, num_formats,
+				       DRM_PLANE_TYPE_PRIMARY);
 	if (ret) {
 	if (ret) {
 		kfree(primary);
 		kfree(primary);
 		primary = NULL;
 		primary = NULL;

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

@@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
 	count = drm_load_edid_firmware(connector);
 	count = drm_load_edid_firmware(connector);
 	if (count == 0)
 	if (count == 0)
 #endif
 #endif
-		count = (*connector_funcs->get_modes)(connector);
+	{
+		if (connector->override_edid) {
+			struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
+
+			count = drm_add_edid_modes(connector, edid);
+		} else
+			count = (*connector_funcs->get_modes)(connector);
+	}
 
 
 	if (count == 0 && connector->status == connector_status_connected)
 	if (count == 0 && connector->status == connector_status_connected)
 		count = drm_add_modes_noedid(connector, 1024, 768);
 		count = drm_add_modes_noedid(connector, 1024, 768);

+ 1 - 13
drivers/gpu/drm/drm_stub.c

@@ -37,18 +37,9 @@
 unsigned int drm_debug = 0;	/* 1 to enable debug output */
 unsigned int drm_debug = 0;	/* 1 to enable debug output */
 EXPORT_SYMBOL(drm_debug);
 EXPORT_SYMBOL(drm_debug);
 
 
-unsigned int drm_rnodes = 0;	/* 1 to enable experimental render nodes API */
-EXPORT_SYMBOL(drm_rnodes);
-
-/* 1 to allow user space to request universal planes (experimental) */
-unsigned int drm_universal_planes = 0;
-EXPORT_SYMBOL(drm_universal_planes);
-
 unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
 unsigned int drm_vblank_offdelay = 5000;    /* Default to 5000 msecs. */
-EXPORT_SYMBOL(drm_vblank_offdelay);
 
 
 unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
 unsigned int drm_timestamp_precision = 20;  /* Default to 20 usecs. */
-EXPORT_SYMBOL(drm_timestamp_precision);
 
 
 /*
 /*
  * Default to use monotonic timestamps for wait-for-vblank and page-flip
  * Default to use monotonic timestamps for wait-for-vblank and page-flip
@@ -60,14 +51,11 @@ MODULE_AUTHOR(CORE_AUTHOR);
 MODULE_DESCRIPTION(CORE_DESC);
 MODULE_DESCRIPTION(CORE_DESC);
 MODULE_LICENSE("GPL and additional rights");
 MODULE_LICENSE("GPL and additional rights");
 MODULE_PARM_DESC(debug, "Enable debug output");
 MODULE_PARM_DESC(debug, "Enable debug output");
-MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API");
 MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
 MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
 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");
 MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
 
 
 module_param_named(debug, drm_debug, int, 0600);
 module_param_named(debug, drm_debug, int, 0600);
-module_param_named(rnodes, drm_rnodes, int, 0600);
-module_param_named(universal_planes, drm_universal_planes, int, 0600);
 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_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
@@ -588,7 +576,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
 			goto err_minors;
 			goto err_minors;
 	}
 	}
 
 
-	if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) {
+	if (drm_core_check_feature(dev, DRIVER_RENDER)) {
 		ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
 		ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
 		if (ret)
 		if (ret)
 			goto err_minors;
 			goto err_minors;

+ 0 - 2
drivers/gpu/drm/drm_sysfs.c

@@ -438,7 +438,6 @@ err_out_files:
 out:
 out:
 	return ret;
 	return ret;
 }
 }
-EXPORT_SYMBOL(drm_sysfs_connector_add);
 
 
 /**
 /**
  * drm_sysfs_connector_remove - remove an connector device from sysfs
  * drm_sysfs_connector_remove - remove an connector device from sysfs
@@ -468,7 +467,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
 	device_unregister(connector->kdev);
 	device_unregister(connector->kdev);
 	connector->kdev = NULL;
 	connector->kdev = NULL;
 }
 }
-EXPORT_SYMBOL(drm_sysfs_connector_remove);
 
 
 /**
 /**
  * drm_sysfs_hotplug_event - generate a DRM uevent
  * drm_sysfs_hotplug_event - generate a DRM uevent

+ 1 - 1
drivers/gpu/drm/exynos/exynos_dp_core.c

@@ -1018,7 +1018,7 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
 	}
 	}
 
 
 	drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
 	drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
 
 	return 0;
 	return 0;

+ 4 - 17
drivers/gpu/drm/exynos/exynos_drm_connector.c

@@ -117,20 +117,7 @@ static struct drm_encoder *exynos_drm_best_encoder(
 	struct drm_device *dev = connector->dev;
 	struct drm_device *dev = connector->dev;
 	struct exynos_drm_connector *exynos_connector =
 	struct exynos_drm_connector *exynos_connector =
 					to_exynos_connector(connector);
 					to_exynos_connector(connector);
-	struct drm_mode_object *obj;
-	struct drm_encoder *encoder;
-
-	obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
-				   DRM_MODE_OBJECT_ENCODER);
-	if (!obj) {
-		DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
-				exynos_connector->encoder_id);
-		return NULL;
-	}
-
-	encoder = obj_to_encoder(obj);
-
-	return encoder;
+	return drm_encoder_find(dev, exynos_connector->encoder_id);
 }
 }
 
 
 static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
 static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
@@ -185,7 +172,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
 	struct exynos_drm_connector *exynos_connector =
 	struct exynos_drm_connector *exynos_connector =
 		to_exynos_connector(connector);
 		to_exynos_connector(connector);
 
 
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(exynos_connector);
 	kfree(exynos_connector);
 }
 }
@@ -230,7 +217,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
 	drm_connector_init(dev, connector, &exynos_connector_funcs, type);
 	drm_connector_init(dev, connector, &exynos_connector_funcs, type);
 	drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
 	drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
 
 
-	err = drm_sysfs_connector_add(connector);
+	err = drm_connector_register(connector);
 	if (err)
 	if (err)
 		goto err_connector;
 		goto err_connector;
 
 
@@ -250,7 +237,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
 	return connector;
 	return connector;
 
 
 err_sysfs:
 err_sysfs:
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 err_connector:
 err_connector:
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(exynos_connector);
 	kfree(exynos_connector);

+ 2 - 2
drivers/gpu/drm/exynos/exynos_drm_dpi.c

@@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
 
 
 static void exynos_dpi_connector_destroy(struct drm_connector *connector)
 static void exynos_dpi_connector_destroy(struct drm_connector *connector)
 {
 {
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 }
 }
 
 
@@ -117,7 +117,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
 	}
 	}
 
 
 	drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
 	drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
 
 	return 0;
 	return 0;

+ 0 - 4
drivers/gpu/drm/exynos/exynos_drm_drv.c

@@ -39,8 +39,6 @@
 #define DRIVER_MAJOR	1
 #define DRIVER_MAJOR	1
 #define DRIVER_MINOR	0
 #define DRIVER_MINOR	0
 
 
-#define VBLANK_OFF_DELAY	50000
-
 static struct platform_device *exynos_drm_pdev;
 static struct platform_device *exynos_drm_pdev;
 
 
 static DEFINE_MUTEX(drm_component_lock);
 static DEFINE_MUTEX(drm_component_lock);
@@ -103,8 +101,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
 	/* setup possible_clones. */
 	/* setup possible_clones. */
 	exynos_drm_encoder_setup(dev);
 	exynos_drm_encoder_setup(dev);
 
 
-	drm_vblank_offdelay = VBLANK_OFF_DELAY;
-
 	platform_set_drvdata(dev->platformdev, dev);
 	platform_set_drvdata(dev->platformdev, dev);
 
 
 	/* Try to bind all sub drivers. */
 	/* Try to bind all sub drivers. */

+ 0 - 2
drivers/gpu/drm/exynos/exynos_drm_drv.h

@@ -40,8 +40,6 @@ struct drm_device;
 struct exynos_drm_overlay;
 struct exynos_drm_overlay;
 struct drm_connector;
 struct drm_connector;
 
 
-extern unsigned int drm_vblank_offdelay;
-
 /* This enumerates device type. */
 /* This enumerates device type. */
 enum exynos_drm_device_type {
 enum exynos_drm_device_type {
 	EXYNOS_DEVICE_TYPE_NONE,
 	EXYNOS_DEVICE_TYPE_NONE,

+ 1 - 1
drivers/gpu/drm/exynos/exynos_drm_dsi.c

@@ -1246,7 +1246,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,
 	}
 	}
 
 
 	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
 	drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
 
 	return 0;
 	return 0;

+ 3 - 2
drivers/gpu/drm/exynos/exynos_drm_fbdev.c

@@ -225,7 +225,7 @@ out:
 	return ret;
 	return ret;
 }
 }
 
 
-static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
 	.fb_probe =	exynos_drm_fbdev_create,
 	.fb_probe =	exynos_drm_fbdev_create,
 };
 };
 
 
@@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	private->fb_helper = helper = &fbdev->drm_fb_helper;
 	private->fb_helper = helper = &fbdev->drm_fb_helper;
-	helper->funcs = &exynos_drm_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
 
 
 	num_crtc = dev->mode_config.num_crtc;
 	num_crtc = dev->mode_config.num_crtc;
 
 

+ 1 - 1
drivers/gpu/drm/exynos/exynos_drm_vidi.c

@@ -562,7 +562,7 @@ static int vidi_create_connector(struct exynos_drm_display *display,
 	}
 	}
 
 
 	drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
 	drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
 
 	return 0;
 	return 0;

+ 1 - 1
drivers/gpu/drm/exynos/exynos_hdmi.c

@@ -1129,7 +1129,7 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
 	}
 	}
 
 
 	drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
 	drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	drm_mode_connector_attach_encoder(connector, encoder);
 	drm_mode_connector_attach_encoder(connector, encoder);
 
 
 	return 0;
 	return 0;

+ 2 - 2
drivers/gpu/drm/gma500/cdv_intel_crt.c

@@ -192,7 +192,7 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector)
 	struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
 	struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
 
 
 	psb_intel_i2c_destroy(gma_encoder->ddc_bus);
 	psb_intel_i2c_destroy(gma_encoder->ddc_bus);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -304,7 +304,7 @@ void cdv_intel_crt_init(struct drm_device *dev,
 	drm_connector_helper_add(connector,
 	drm_connector_helper_add(connector,
 					&cdv_intel_crt_connector_helper_funcs);
 					&cdv_intel_crt_connector_helper_funcs);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	return;
 	return;
 failed_ddc:
 failed_ddc:

+ 2 - 2
drivers/gpu/drm/gma500/cdv_intel_dp.c

@@ -1713,7 +1713,7 @@ cdv_intel_dp_destroy(struct drm_connector *connector)
 		}
 		}
 	}
 	}
 	i2c_del_adapter(&intel_dp->adapter);
 	i2c_del_adapter(&intel_dp->adapter);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -1847,7 +1847,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
 	connector->interlace_allowed = false;
 	connector->interlace_allowed = false;
 	connector->doublescan_allowed = false;
 	connector->doublescan_allowed = false;
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	/* Set up the DDC bus. */
 	/* Set up the DDC bus. */
 	switch (output_reg) {
 	switch (output_reg) {

+ 2 - 2
drivers/gpu/drm/gma500/cdv_intel_hdmi.c

@@ -248,7 +248,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector)
 
 
 	if (gma_encoder->i2c_bus)
 	if (gma_encoder->i2c_bus)
 		psb_intel_i2c_destroy(gma_encoder->i2c_bus);
 		psb_intel_i2c_destroy(gma_encoder->i2c_bus);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -356,7 +356,7 @@ void cdv_hdmi_init(struct drm_device *dev,
 
 
 	hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter);
 	hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter);
 	hdmi_priv->dev = dev;
 	hdmi_priv->dev = dev;
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	return;
 	return;
 
 
 failed_ddc:
 failed_ddc:

+ 2 - 2
drivers/gpu/drm/gma500/cdv_intel_lvds.c

@@ -446,7 +446,7 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector)
 
 
 	if (gma_encoder->i2c_bus)
 	if (gma_encoder->i2c_bus)
 		psb_intel_i2c_destroy(gma_encoder->i2c_bus);
 		psb_intel_i2c_destroy(gma_encoder->i2c_bus);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -774,7 +774,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
 
 
 out:
 out:
 	mutex_unlock(&dev->mode_config.mutex);
 	mutex_unlock(&dev->mode_config.mutex);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	return;
 	return;
 
 
 failed_find:
 failed_find:

+ 3 - 2
drivers/gpu/drm/gma500/framebuffer.c

@@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
 	return psbfb_create(psb_fbdev, sizes);
 	return psbfb_create(psb_fbdev, sizes);
 }
 }
 
 
-static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
 	.gamma_set = psbfb_gamma_set,
 	.gamma_set = psbfb_gamma_set,
 	.gamma_get = psbfb_gamma_get,
 	.gamma_get = psbfb_gamma_get,
 	.fb_probe = psbfb_probe,
 	.fb_probe = psbfb_probe,
@@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
 	}
 	}
 
 
 	dev_priv->fbdev = fbdev;
 	dev_priv->fbdev = fbdev;
-	fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
 
 
 	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
 	drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
 							INTELFB_CONN_LIMIT);
 							INTELFB_CONN_LIMIT);

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

@@ -206,7 +206,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt)
 
 
 	WARN_ON(gt->pages);
 	WARN_ON(gt->pages);
 
 
-	pages = drm_gem_get_pages(&gt->gem, 0);
+	pages = drm_gem_get_pages(&gt->gem);
 	if (IS_ERR(pages))
 	if (IS_ERR(pages))
 		return PTR_ERR(pages);
 		return PTR_ERR(pages);
 
 

+ 2 - 2
drivers/gpu/drm/gma500/mdfld_dsi_output.c

@@ -318,7 +318,7 @@ static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
 
 
 	if (!dsi_connector)
 	if (!dsi_connector)
 		return;
 		return;
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	sender = dsi_connector->pkg_sender;
 	sender = dsi_connector->pkg_sender;
 	mdfld_dsi_pkg_sender_destroy(sender);
 	mdfld_dsi_pkg_sender_destroy(sender);
@@ -597,7 +597,7 @@ void mdfld_dsi_output_init(struct drm_device *dev,
 	dsi_config->encoder = encoder;
 	dsi_config->encoder = encoder;
 	encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
 	encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
 		INTEL_OUTPUT_MIPI2;
 		INTEL_OUTPUT_MIPI2;
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	return;
 	return;
 
 
 	/*TODO: add code to destroy outputs on error*/
 	/*TODO: add code to destroy outputs on error*/

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

@@ -665,7 +665,7 @@ void oaktrail_hdmi_init(struct drm_device *dev,
 	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
 	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
 	connector->interlace_allowed = false;
 	connector->interlace_allowed = false;
 	connector->doublescan_allowed = false;
 	connector->doublescan_allowed = false;
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	dev_info(dev->dev, "HDMI initialised.\n");
 	dev_info(dev->dev, "HDMI initialised.\n");
 
 
 	return;
 	return;

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

@@ -404,7 +404,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
 out:
 out:
 	mutex_unlock(&dev->mode_config.mutex);
 	mutex_unlock(&dev->mode_config.mutex);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	return;
 	return;
 
 
 failed_find:
 failed_find:

+ 2 - 2
drivers/gpu/drm/gma500/psb_intel_lvds.c

@@ -563,7 +563,7 @@ void psb_intel_lvds_destroy(struct drm_connector *connector)
 
 
 	if (lvds_priv->ddc_bus)
 	if (lvds_priv->ddc_bus)
 		psb_intel_i2c_destroy(lvds_priv->ddc_bus);
 		psb_intel_i2c_destroy(lvds_priv->ddc_bus);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -829,7 +829,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
 	 */
 	 */
 out:
 out:
 	mutex_unlock(&dev->mode_config.mutex);
 	mutex_unlock(&dev->mode_config.mutex);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	return;
 	return;
 
 
 failed_find:
 failed_find:

+ 2 - 2
drivers/gpu/drm/gma500/psb_intel_sdvo.c

@@ -1682,7 +1682,7 @@ static void psb_intel_sdvo_destroy(struct drm_connector *connector)
 				     psb_intel_sdvo_connector->tv_format);
 				     psb_intel_sdvo_connector->tv_format);
 
 
 	psb_intel_sdvo_destroy_enhance_property(connector);
 	psb_intel_sdvo_destroy_enhance_property(connector);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(connector);
 	kfree(connector);
 }
 }
@@ -2071,7 +2071,7 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector,
 	connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
 	connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
 
 
 	gma_connector_attach_encoder(&connector->base, &encoder->base);
 	gma_connector_attach_encoder(&connector->base, &encoder->base);
-	drm_sysfs_connector_add(&connector->base.base);
+	drm_connector_register(&connector->base.base);
 }
 }
 
 
 static void
 static void

+ 1 - 2
drivers/gpu/drm/i2c/tda998x_drv.c

@@ -1196,8 +1196,7 @@ tda998x_encoder_destroy(struct drm_encoder *encoder)
 	if (priv->hdmi->irq)
 	if (priv->hdmi->irq)
 		free_irq(priv->hdmi->irq, priv);
 		free_irq(priv->hdmi->irq, priv);
 
 
-	if (priv->cec)
-		i2c_unregister_device(priv->cec);
+	i2c_unregister_device(priv->cec);
 	drm_i2c_encoder_destroy(encoder);
 	drm_i2c_encoder_destroy(encoder);
 	kfree(priv);
 	kfree(priv);
 }
 }

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

@@ -59,6 +59,7 @@ i915-y += dvo_ch7017.o \
 	  intel_crt.o \
 	  intel_crt.o \
 	  intel_ddi.o \
 	  intel_ddi.o \
 	  intel_dp.o \
 	  intel_dp.o \
+	  intel_dp_mst.o \
 	  intel_dsi_cmd.o \
 	  intel_dsi_cmd.o \
 	  intel_dsi.o \
 	  intel_dsi.o \
 	  intel_dsi_pll.o \
 	  intel_dsi_pll.o \

+ 32 - 7
drivers/gpu/drm/i915/i915_debugfs.c

@@ -2216,13 +2216,15 @@ static void intel_connector_info(struct seq_file *m,
 		seq_printf(m, "\tCEA rev: %d\n",
 		seq_printf(m, "\tCEA rev: %d\n",
 			   connector->display_info.cea_rev);
 			   connector->display_info.cea_rev);
 	}
 	}
-	if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
-	    intel_encoder->type == INTEL_OUTPUT_EDP)
-		intel_dp_info(m, intel_connector);
-	else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
-		intel_hdmi_info(m, intel_connector);
-	else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
-		intel_lvds_info(m, intel_connector);
+	if (intel_encoder) {
+		if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
+		    intel_encoder->type == INTEL_OUTPUT_EDP)
+			intel_dp_info(m, intel_connector);
+		else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
+			intel_hdmi_info(m, intel_connector);
+		else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
+			intel_lvds_info(m, intel_connector);
+	}
 
 
 	seq_printf(m, "\tmodes:\n");
 	seq_printf(m, "\tmodes:\n");
 	list_for_each_entry(mode, &connector->modes, head)
 	list_for_each_entry(mode, &connector->modes, head)
@@ -2410,6 +2412,28 @@ struct pipe_crc_info {
 	enum pipe pipe;
 	enum pipe pipe;
 };
 };
 
 
+static int i915_dp_mst_info(struct seq_file *m, void *unused)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_device *dev = node->minor->dev;
+	struct drm_encoder *encoder;
+	struct intel_encoder *intel_encoder;
+	struct intel_digital_port *intel_dig_port;
+	drm_modeset_lock_all(dev);
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		intel_encoder = to_intel_encoder(encoder);
+		if (intel_encoder->type != INTEL_OUTPUT_DISPLAYPORT)
+			continue;
+		intel_dig_port = enc_to_dig_port(encoder);
+		if (!intel_dig_port->dp.can_mst)
+			continue;
+
+		drm_dp_mst_dump_topology(m, &intel_dig_port->dp.mst_mgr);
+	}
+	drm_modeset_unlock_all(dev);
+	return 0;
+}
+
 static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
 static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
 {
 {
 	struct pipe_crc_info *info = inode->i_private;
 	struct pipe_crc_info *info = inode->i_private;
@@ -3911,6 +3935,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
 	{"i915_display_info", i915_display_info, 0},
 	{"i915_display_info", i915_display_info, 0},
 	{"i915_semaphore_status", i915_semaphore_status, 0},
 	{"i915_semaphore_status", i915_semaphore_status, 0},
 	{"i915_shared_dplls_info", i915_shared_dplls_info, 0},
 	{"i915_shared_dplls_info", i915_shared_dplls_info, 0},
+	{"i915_dp_mst_info", i915_dp_mst_info, 0},
 };
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
 

+ 10 - 3
drivers/gpu/drm/i915/i915_dma.c

@@ -1379,9 +1379,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
 	 */
 	 */
 	intel_fbdev_initial_config(dev);
 	intel_fbdev_initial_config(dev);
 
 
-	/* Only enable hotplug handling once the fbdev is fully set up. */
-	dev_priv->enable_hotplug_processing = true;
-
 	drm_kms_helper_poll_init(dev);
 	drm_kms_helper_poll_init(dev);
 
 
 	return 0;
 	return 0;
@@ -1730,6 +1727,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 		goto out_mtrrfree;
 		goto out_mtrrfree;
 	}
 	}
 
 
+	dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
+	if (dev_priv->dp_wq == NULL) {
+		DRM_ERROR("Failed to create our dp workqueue.\n");
+		ret = -ENOMEM;
+		goto out_freewq;
+	}
+
 	intel_irq_init(dev);
 	intel_irq_init(dev);
 	intel_uncore_sanitize(dev);
 	intel_uncore_sanitize(dev);
 
 
@@ -1805,6 +1809,8 @@ out_gem_unload:
 	intel_teardown_gmbus(dev);
 	intel_teardown_gmbus(dev);
 	intel_teardown_mchbar(dev);
 	intel_teardown_mchbar(dev);
 	pm_qos_remove_request(&dev_priv->pm_qos);
 	pm_qos_remove_request(&dev_priv->pm_qos);
+	destroy_workqueue(dev_priv->dp_wq);
+out_freewq:
 	destroy_workqueue(dev_priv->wq);
 	destroy_workqueue(dev_priv->wq);
 out_mtrrfree:
 out_mtrrfree:
 	arch_phys_wc_del(dev_priv->gtt.mtrr);
 	arch_phys_wc_del(dev_priv->gtt.mtrr);
@@ -1905,6 +1911,7 @@ int i915_driver_unload(struct drm_device *dev)
 	intel_teardown_gmbus(dev);
 	intel_teardown_gmbus(dev);
 	intel_teardown_mchbar(dev);
 	intel_teardown_mchbar(dev);
 
 
+	destroy_workqueue(dev_priv->dp_wq);
 	destroy_workqueue(dev_priv->wq);
 	destroy_workqueue(dev_priv->wq);
 	pm_qos_remove_request(&dev_priv->pm_qos);
 	pm_qos_remove_request(&dev_priv->pm_qos);
 
 

+ 17 - 8
drivers/gpu/drm/i915/i915_drv.c

@@ -520,13 +520,6 @@ static int i915_drm_freeze(struct drm_device *dev)
 			return error;
 			return error;
 		}
 		}
 
 
-		flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
-		intel_runtime_pm_disable_interrupts(dev);
-		dev_priv->enable_hotplug_processing = false;
-
-		intel_suspend_gt_powersave(dev);
-
 		/*
 		/*
 		 * Disable CRTCs directly since we want to preserve sw state
 		 * Disable CRTCs directly since we want to preserve sw state
 		 * for _thaw. Also, power gate the CRTC power wells.
 		 * for _thaw. Also, power gate the CRTC power wells.
@@ -536,6 +529,14 @@ static int i915_drm_freeze(struct drm_device *dev)
 			intel_crtc_control(crtc, false);
 			intel_crtc_control(crtc, false);
 		drm_modeset_unlock_all(dev);
 		drm_modeset_unlock_all(dev);
 
 
+		intel_dp_mst_suspend(dev);
+
+		flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+
+		intel_runtime_pm_disable_interrupts(dev);
+
+		intel_suspend_gt_powersave(dev);
+
 		intel_modeset_suspend_hw(dev);
 		intel_modeset_suspend_hw(dev);
 	}
 	}
 
 
@@ -650,6 +651,15 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 
 
 		intel_modeset_init_hw(dev);
 		intel_modeset_init_hw(dev);
 
 
+		{
+			unsigned long irqflags;
+			spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+			if (dev_priv->display.hpd_irq_setup)
+				dev_priv->display.hpd_irq_setup(dev);
+			spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
+		}
+
+		intel_dp_mst_resume(dev);
 		drm_modeset_lock_all(dev);
 		drm_modeset_lock_all(dev);
 		intel_modeset_setup_hw_state(dev, true);
 		intel_modeset_setup_hw_state(dev, true);
 		drm_modeset_unlock_all(dev);
 		drm_modeset_unlock_all(dev);
@@ -661,7 +671,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
 		 * notifications.
 		 * notifications.
 		 * */
 		 * */
 		intel_hpd_init(dev);
 		intel_hpd_init(dev);
-		dev_priv->enable_hotplug_processing = true;
 		/* Config may have changed between suspend and resume */
 		/* Config may have changed between suspend and resume */
 		drm_helper_hpd_irq_event(dev);
 		drm_helper_hpd_irq_event(dev);
 	}
 	}

+ 9 - 1
drivers/gpu/drm/i915/i915_drv.h

@@ -1446,7 +1446,6 @@ struct drm_i915_private {
 	u32 pipestat_irq_mask[I915_MAX_PIPES];
 	u32 pipestat_irq_mask[I915_MAX_PIPES];
 
 
 	struct work_struct hotplug_work;
 	struct work_struct hotplug_work;
-	bool enable_hotplug_processing;
 	struct {
 	struct {
 		unsigned long hpd_last_jiffies;
 		unsigned long hpd_last_jiffies;
 		int hpd_cnt;
 		int hpd_cnt;
@@ -1604,6 +1603,15 @@ struct drm_i915_private {
 	u32 short_hpd_port_mask;
 	u32 short_hpd_port_mask;
 	struct work_struct dig_port_work;
 	struct work_struct dig_port_work;
 
 
+	/*
+	 * if we get a HPD irq from DP and a HPD irq from non-DP
+	 * the non-DP HPD could block the workqueue on a mode config
+	 * mutex getting, that userspace may have taken. However
+	 * userspace is waiting on the DP workqueue to run which is
+	 * blocked behind the non-DP one.
+	 */
+	struct workqueue_struct *dp_wq;
+
 	/* Old dri1 support infrastructure, beware the dragons ya fools entering
 	/* Old dri1 support infrastructure, beware the dragons ya fools entering
 	 * here! */
 	 * here! */
 	struct i915_dri1_state dri1;
 	struct i915_dri1_state dri1;

+ 8 - 6
drivers/gpu/drm/i915/i915_irq.c

@@ -1156,10 +1156,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	bool changed = false;
 	bool changed = false;
 	u32 hpd_event_bits;
 	u32 hpd_event_bits;
 
 
-	/* HPD irq before everything is fully set up. */
-	if (!dev_priv->enable_hotplug_processing)
-		return;
-
 	mutex_lock(&mode_config->mutex);
 	mutex_lock(&mode_config->mutex);
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
 
 
@@ -1169,6 +1165,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	dev_priv->hpd_event_bits = 0;
 	dev_priv->hpd_event_bits = 0;
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 		intel_connector = to_intel_connector(connector);
 		intel_connector = to_intel_connector(connector);
+		if (!intel_connector->encoder)
+			continue;
 		intel_encoder = intel_connector->encoder;
 		intel_encoder = intel_connector->encoder;
 		if (intel_encoder->hpd_pin > HPD_NONE &&
 		if (intel_encoder->hpd_pin > HPD_NONE &&
 		    dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
 		    dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
@@ -1199,6 +1197,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
 
 
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 		intel_connector = to_intel_connector(connector);
 		intel_connector = to_intel_connector(connector);
+		if (!intel_connector->encoder)
+			continue;
 		intel_encoder = intel_connector->encoder;
 		intel_encoder = intel_connector->encoder;
 		if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
 		if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
 			if (intel_encoder->hot_plug)
 			if (intel_encoder->hot_plug)
@@ -1846,7 +1846,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
 	 * deadlock.
 	 * deadlock.
 	 */
 	 */
 	if (queue_dig)
 	if (queue_dig)
-		schedule_work(&dev_priv->dig_port_work);
+		queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work);
 	if (queue_hp)
 	if (queue_hp)
 		schedule_work(&dev_priv->hotplug_work);
 		schedule_work(&dev_priv->hotplug_work);
 }
 }
@@ -4759,7 +4759,9 @@ void intel_hpd_init(struct drm_device *dev)
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 		struct intel_connector *intel_connector = to_intel_connector(connector);
 		struct intel_connector *intel_connector = to_intel_connector(connector);
 		connector->polled = intel_connector->polled;
 		connector->polled = intel_connector->polled;
-		if (!connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
+		if (connector->encoder && !connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
+			connector->polled = DRM_CONNECTOR_POLL_HPD;
+		if (intel_connector->mst_port)
 			connector->polled = DRM_CONNECTOR_POLL_HPD;
 			connector->polled = DRM_CONNECTOR_POLL_HPD;
 	}
 	}
 
 

+ 9 - 2
drivers/gpu/drm/i915/i915_reg.h

@@ -5821,6 +5821,7 @@ enum punit_power_well {
 #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
 #define  TRANS_DDI_EDP_INPUT_A_ONOFF	(4<<12)
 #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
 #define  TRANS_DDI_EDP_INPUT_B_ONOFF	(5<<12)
 #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
 #define  TRANS_DDI_EDP_INPUT_C_ONOFF	(6<<12)
+#define  TRANS_DDI_DP_VC_PAYLOAD_ALLOC	(1<<8)
 #define  TRANS_DDI_BFI_ENABLE		(1<<4)
 #define  TRANS_DDI_BFI_ENABLE		(1<<4)
 
 
 /* DisplayPort Transport Control */
 /* DisplayPort Transport Control */
@@ -5830,6 +5831,7 @@ enum punit_power_well {
 #define  DP_TP_CTL_ENABLE			(1<<31)
 #define  DP_TP_CTL_ENABLE			(1<<31)
 #define  DP_TP_CTL_MODE_SST			(0<<27)
 #define  DP_TP_CTL_MODE_SST			(0<<27)
 #define  DP_TP_CTL_MODE_MST			(1<<27)
 #define  DP_TP_CTL_MODE_MST			(1<<27)
+#define  DP_TP_CTL_FORCE_ACT			(1<<25)
 #define  DP_TP_CTL_ENHANCED_FRAME_ENABLE	(1<<18)
 #define  DP_TP_CTL_ENHANCED_FRAME_ENABLE	(1<<18)
 #define  DP_TP_CTL_FDI_AUTOTRAIN		(1<<15)
 #define  DP_TP_CTL_FDI_AUTOTRAIN		(1<<15)
 #define  DP_TP_CTL_LINK_TRAIN_MASK		(7<<8)
 #define  DP_TP_CTL_LINK_TRAIN_MASK		(7<<8)
@@ -5844,8 +5846,13 @@ enum punit_power_well {
 #define DP_TP_STATUS_A			0x64044
 #define DP_TP_STATUS_A			0x64044
 #define DP_TP_STATUS_B			0x64144
 #define DP_TP_STATUS_B			0x64144
 #define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
 #define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
-#define  DP_TP_STATUS_IDLE_DONE		(1<<25)
-#define  DP_TP_STATUS_AUTOTRAIN_DONE	(1<<12)
+#define  DP_TP_STATUS_IDLE_DONE			(1<<25)
+#define  DP_TP_STATUS_ACT_SENT			(1<<24)
+#define  DP_TP_STATUS_MODE_STATUS_MST		(1<<23)
+#define  DP_TP_STATUS_AUTOTRAIN_DONE		(1<<12)
+#define  DP_TP_STATUS_PAYLOAD_MAPPING_VC2	(3 << 8)
+#define  DP_TP_STATUS_PAYLOAD_MAPPING_VC1	(3 << 4)
+#define  DP_TP_STATUS_PAYLOAD_MAPPING_VC0	(3 << 0)
 
 
 /* DDI Buffer Control */
 /* DDI Buffer Control */
 #define DDI_BUF_CTL_A				0x64000
 #define DDI_BUF_CTL_A				0x64000

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

@@ -895,7 +895,7 @@ void intel_crt_init(struct drm_device *dev)
 
 
 	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	if (!I915_HAS_HOTPLUG(dev))
 	if (!I915_HAS_HOTPLUG(dev))
 		intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
 		intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;

+ 73 - 20
drivers/gpu/drm/i915/intel_ddi.c

@@ -116,7 +116,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
 	struct drm_encoder *encoder = &intel_encoder->base;
 	struct drm_encoder *encoder = &intel_encoder->base;
 	int type = intel_encoder->type;
 	int type = intel_encoder->type;
 
 
-	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
+	if (type == INTEL_OUTPUT_DP_MST) {
+		struct intel_digital_port *intel_dig_port = enc_to_mst(encoder)->primary;
+		return intel_dig_port->port;
+	} else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
 	    type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
 	    type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
 		struct intel_digital_port *intel_dig_port =
 		struct intel_digital_port *intel_dig_port =
 			enc_to_dig_port(encoder);
 			enc_to_dig_port(encoder);
@@ -365,6 +368,18 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
 	DRM_ERROR("FDI link training failed!\n");
 	DRM_ERROR("FDI link training failed!\n");
 }
 }
 
 
+void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder)
+{
+	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+	struct intel_digital_port *intel_dig_port =
+		enc_to_dig_port(&encoder->base);
+
+	intel_dp->DP = intel_dig_port->saved_port_bits |
+		DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
+	intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
+
+}
+
 static struct intel_encoder *
 static struct intel_encoder *
 intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
 intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
 {
 {
@@ -572,8 +587,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
 	return (refclk * n * 100) / (p * r);
 	return (refclk * n * 100) / (p * r);
 }
 }
 
 
-static void intel_ddi_clock_get(struct intel_encoder *encoder,
-				struct intel_crtc_config *pipe_config)
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+			 struct intel_crtc_config *pipe_config)
 {
 {
 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
 	struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
 	int link_clock = 0;
 	int link_clock = 0;
@@ -743,8 +758,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
 	int type = intel_encoder->type;
 	int type = intel_encoder->type;
 	uint32_t temp;
 	uint32_t temp;
 
 
-	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
-
+	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP_MST) {
 		temp = TRANS_MSA_SYNC_CLK;
 		temp = TRANS_MSA_SYNC_CLK;
 		switch (intel_crtc->config.pipe_bpp) {
 		switch (intel_crtc->config.pipe_bpp) {
 		case 18:
 		case 18:
@@ -766,6 +780,21 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
 	}
 	}
 }
 }
 
 
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state)
+{
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
+	uint32_t temp;
+	temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+	if (state == true)
+		temp |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
+	else
+		temp &= ~TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
+	I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
+}
+
 void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 {
 {
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -845,7 +874,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 		   type == INTEL_OUTPUT_EDP) {
 		   type == INTEL_OUTPUT_EDP) {
 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
 
-		temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+		if (intel_dp->is_mst) {
+			temp |= TRANS_DDI_MODE_SELECT_DP_MST;
+		} else
+			temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+
+		temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
+	} else if (type == INTEL_OUTPUT_DP_MST) {
+		struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp;
+
+		if (intel_dp->is_mst) {
+			temp |= TRANS_DDI_MODE_SELECT_DP_MST;
+		} else
+			temp |= TRANS_DDI_MODE_SELECT_DP_SST;
 
 
 		temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
 		temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
 	} else {
 	} else {
@@ -862,7 +903,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
 	uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
 	uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
 	uint32_t val = I915_READ(reg);
 	uint32_t val = I915_READ(reg);
 
 
-	val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
+	val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK | TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
 	val |= TRANS_DDI_PORT_NONE;
 	val |= TRANS_DDI_PORT_NONE;
 	I915_WRITE(reg, val);
 	I915_WRITE(reg, val);
 }
 }
@@ -901,8 +942,11 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
 	case TRANS_DDI_MODE_SELECT_DP_SST:
 	case TRANS_DDI_MODE_SELECT_DP_SST:
 		if (type == DRM_MODE_CONNECTOR_eDP)
 		if (type == DRM_MODE_CONNECTOR_eDP)
 			return true;
 			return true;
-	case TRANS_DDI_MODE_SELECT_DP_MST:
 		return (type == DRM_MODE_CONNECTOR_DisplayPort);
 		return (type == DRM_MODE_CONNECTOR_DisplayPort);
+	case TRANS_DDI_MODE_SELECT_DP_MST:
+		/* if the transcoder is in MST state then
+		 * connector isn't connected */
+		return false;
 
 
 	case TRANS_DDI_MODE_SELECT_FDI:
 	case TRANS_DDI_MODE_SELECT_FDI:
 		return (type == DRM_MODE_CONNECTOR_VGA);
 		return (type == DRM_MODE_CONNECTOR_VGA);
@@ -954,6 +998,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
 
 
 			if ((tmp & TRANS_DDI_PORT_MASK)
 			if ((tmp & TRANS_DDI_PORT_MASK)
 			    == TRANS_DDI_SELECT_PORT(port)) {
 			    == TRANS_DDI_SELECT_PORT(port)) {
+				if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST)
+					return false;
+
 				*pipe = i;
 				*pipe = i;
 				return true;
 				return true;
 			}
 			}
@@ -1015,12 +1062,8 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
 
 
 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
 	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-		struct intel_digital_port *intel_dig_port =
-			enc_to_dig_port(encoder);
 
 
-		intel_dp->DP = intel_dig_port->saved_port_bits |
-			       DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
-		intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
+		intel_ddi_init_dp_buf_reg(intel_encoder);
 
 
 		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
 		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
 		intel_dp_start_link_train(intel_dp);
 		intel_dp_start_link_train(intel_dp);
@@ -1264,10 +1307,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
 			intel_wait_ddi_buf_idle(dev_priv, port);
 			intel_wait_ddi_buf_idle(dev_priv, port);
 	}
 	}
 
 
-	val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
+	val = DP_TP_CTL_ENABLE |
 	      DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
 	      DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
-	if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
-		val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+	if (intel_dp->is_mst)
+		val |= DP_TP_CTL_MODE_MST;
+	else {
+		val |= DP_TP_CTL_MODE_SST;
+		if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
+			val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+	}
 	I915_WRITE(DP_TP_CTL(port), val);
 	I915_WRITE(DP_TP_CTL(port), val);
 	POSTING_READ(DP_TP_CTL(port));
 	POSTING_READ(DP_TP_CTL(port));
 
 
@@ -1306,11 +1354,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc)
 
 
 static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
 static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
 {
 {
-	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
-	int type = intel_encoder->type;
+	struct intel_digital_port *intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+	int type = intel_dig_port->base.type;
+
+	if (type != INTEL_OUTPUT_DISPLAYPORT &&
+	    type != INTEL_OUTPUT_EDP &&
+	    type != INTEL_OUTPUT_UNKNOWN) {
+		return;
+	}
 
 
-	if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP)
-		intel_dp_check_link_status(intel_dp);
+	intel_dp_hot_plug(intel_encoder);
 }
 }
 
 
 void intel_ddi_get_config(struct intel_encoder *encoder,
 void intel_ddi_get_config(struct intel_encoder *encoder,

+ 78 - 33
drivers/gpu/drm/i915/intel_display.c

@@ -101,6 +101,14 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc);
 static void intel_set_pipe_csc(struct drm_crtc *crtc);
 static void intel_set_pipe_csc(struct drm_crtc *crtc);
 static void vlv_prepare_pll(struct intel_crtc *crtc);
 static void vlv_prepare_pll(struct intel_crtc *crtc);
 
 
+static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe)
+{
+	if (!connector->mst_port)
+		return connector->encoder;
+	else
+		return &connector->mst_port->mst_encoders[pipe]->base;
+}
+
 typedef struct {
 typedef struct {
 	int	min, max;
 	int	min, max;
 } intel_range_t;
 } intel_range_t;
@@ -4130,6 +4138,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
 	if (intel_crtc->config.has_pch_encoder)
 	if (intel_crtc->config.has_pch_encoder)
 		lpt_pch_enable(crtc);
 		lpt_pch_enable(crtc);
 
 
+	if (intel_crtc->config.dp_encoder_is_mst)
+		intel_ddi_set_vc_payload_alloc(crtc, true);
+
 	for_each_encoder_on_crtc(dev, crtc, encoder) {
 	for_each_encoder_on_crtc(dev, crtc, encoder) {
 		encoder->enable(encoder);
 		encoder->enable(encoder);
 		intel_opregion_notify_encoder(encoder, true);
 		intel_opregion_notify_encoder(encoder, true);
@@ -4178,6 +4189,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
 
 
 	intel_disable_pipe(dev_priv, pipe);
 	intel_disable_pipe(dev_priv, pipe);
 
 
+	if (intel_crtc->config.dp_encoder_is_mst)
+		intel_ddi_set_vc_payload_alloc(crtc, false);
+
 	ironlake_pfit_disable(intel_crtc);
 	ironlake_pfit_disable(intel_crtc);
 
 
 	for_each_encoder_on_crtc(dev, crtc, encoder)
 	for_each_encoder_on_crtc(dev, crtc, encoder)
@@ -4300,6 +4314,27 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc)
 	I915_WRITE(BCLRPAT(crtc->pipe), 0);
 	I915_WRITE(BCLRPAT(crtc->pipe), 0);
 }
 }
 
 
+static enum intel_display_power_domain port_to_power_domain(enum port port)
+{
+	switch (port) {
+	case PORT_A:
+		return POWER_DOMAIN_PORT_DDI_A_4_LANES;
+	case PORT_B:
+		return POWER_DOMAIN_PORT_DDI_B_4_LANES;
+	case PORT_C:
+		return POWER_DOMAIN_PORT_DDI_C_4_LANES;
+	case PORT_D:
+		return POWER_DOMAIN_PORT_DDI_D_4_LANES;
+	default:
+		WARN_ON_ONCE(1);
+		return POWER_DOMAIN_PORT_OTHER;
+	}
+}
+
+#define for_each_power_domain(domain, mask)				\
+	for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++)	\
+		if ((1 << (domain)) & (mask))
+
 enum intel_display_power_domain
 enum intel_display_power_domain
 intel_display_port_power_domain(struct intel_encoder *intel_encoder)
 intel_display_port_power_domain(struct intel_encoder *intel_encoder)
 {
 {
@@ -4314,19 +4349,10 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder)
 	case INTEL_OUTPUT_HDMI:
 	case INTEL_OUTPUT_HDMI:
 	case INTEL_OUTPUT_EDP:
 	case INTEL_OUTPUT_EDP:
 		intel_dig_port = enc_to_dig_port(&intel_encoder->base);
 		intel_dig_port = enc_to_dig_port(&intel_encoder->base);
-		switch (intel_dig_port->port) {
-		case PORT_A:
-			return POWER_DOMAIN_PORT_DDI_A_4_LANES;
-		case PORT_B:
-			return POWER_DOMAIN_PORT_DDI_B_4_LANES;
-		case PORT_C:
-			return POWER_DOMAIN_PORT_DDI_C_4_LANES;
-		case PORT_D:
-			return POWER_DOMAIN_PORT_DDI_D_4_LANES;
-		default:
-			WARN_ON_ONCE(1);
-			return POWER_DOMAIN_PORT_OTHER;
-		}
+		return port_to_power_domain(intel_dig_port->port);
+	case INTEL_OUTPUT_DP_MST:
+		intel_dig_port = enc_to_mst(&intel_encoder->base)->primary;
+		return port_to_power_domain(intel_dig_port->port);
 	case INTEL_OUTPUT_ANALOG:
 	case INTEL_OUTPUT_ANALOG:
 		return POWER_DOMAIN_PORT_CRT;
 		return POWER_DOMAIN_PORT_CRT;
 	case INTEL_OUTPUT_DSI:
 	case INTEL_OUTPUT_DSI:
@@ -4990,24 +5016,31 @@ static void intel_connector_check_state(struct intel_connector *connector)
 			      connector->base.base.id,
 			      connector->base.base.id,
 			      connector->base.name);
 			      connector->base.name);
 
 
+		/* there is no real hw state for MST connectors */
+		if (connector->mst_port)
+			return;
+
 		WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
 		WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
 		     "wrong connector dpms state\n");
 		     "wrong connector dpms state\n");
 		WARN(connector->base.encoder != &encoder->base,
 		WARN(connector->base.encoder != &encoder->base,
 		     "active connector not linked to encoder\n");
 		     "active connector not linked to encoder\n");
-		WARN(!encoder->connectors_active,
-		     "encoder->connectors_active not set\n");
 
 
-		encoder_enabled = encoder->get_hw_state(encoder, &pipe);
-		WARN(!encoder_enabled, "encoder not enabled\n");
-		if (WARN_ON(!encoder->base.crtc))
-			return;
+		if (encoder) {
+			WARN(!encoder->connectors_active,
+			     "encoder->connectors_active not set\n");
+
+			encoder_enabled = encoder->get_hw_state(encoder, &pipe);
+			WARN(!encoder_enabled, "encoder not enabled\n");
+			if (WARN_ON(!encoder->base.crtc))
+				return;
 
 
-		crtc = encoder->base.crtc;
+			crtc = encoder->base.crtc;
 
 
-		WARN(!crtc->enabled, "crtc not enabled\n");
-		WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
-		WARN(pipe != to_intel_crtc(crtc)->pipe,
-		     "encoder active on the wrong pipe\n");
+			WARN(!crtc->enabled, "crtc not enabled\n");
+			WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
+			WARN(pipe != to_intel_crtc(crtc)->pipe,
+			     "encoder active on the wrong pipe\n");
+		}
 	}
 	}
 }
 }
 
 
@@ -10508,6 +10541,14 @@ check_encoder_state(struct drm_device *dev)
 			if (connector->base.dpms != DRM_MODE_DPMS_OFF)
 			if (connector->base.dpms != DRM_MODE_DPMS_OFF)
 				active = true;
 				active = true;
 		}
 		}
+		/*
+		 * for MST connectors if we unplug the connector is gone
+		 * away but the encoder is still connected to a crtc
+		 * until a modeset happens in response to the hotplug.
+		 */
+		if (!enabled && encoder->base.encoder_type == DRM_MODE_ENCODER_DPMST)
+			continue;
+
 		WARN(!!encoder->base.crtc != enabled,
 		WARN(!!encoder->base.crtc != enabled,
 		     "encoder's enabled state mismatch "
 		     "encoder's enabled state mismatch "
 		     "(expected %i, found %i)\n",
 		     "(expected %i, found %i)\n",
@@ -11053,7 +11094,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
 		 * for them. */
 		 * for them. */
 		for (ro = 0; ro < set->num_connectors; ro++) {
 		for (ro = 0; ro < set->num_connectors; ro++) {
 			if (set->connectors[ro] == &connector->base) {
 			if (set->connectors[ro] == &connector->base) {
-				connector->new_encoder = connector->encoder;
+				connector->new_encoder = intel_find_encoder(connector, to_intel_crtc(set->crtc)->pipe);
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -11099,7 +11140,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
 					 new_crtc)) {
 					 new_crtc)) {
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
-		connector->encoder->new_crtc = to_intel_crtc(new_crtc);
+		connector->new_encoder->new_crtc = to_intel_crtc(new_crtc);
 
 
 		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
 		DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
 			connector->base.base.id,
 			connector->base.base.id,
@@ -11133,7 +11174,12 @@ intel_modeset_stage_output_state(struct drm_device *dev,
 		}
 		}
 	}
 	}
 	/* Now we've also updated encoder->new_crtc for all encoders. */
 	/* Now we've also updated encoder->new_crtc for all encoders. */
-
+	list_for_each_entry(connector, &dev->mode_config.connector_list,
+			    base.head) {
+		if (connector->new_encoder)
+			if (connector->new_encoder != connector->encoder)
+				connector->encoder = connector->new_encoder;
+	}
 	for_each_intel_crtc(dev, crtc) {
 	for_each_intel_crtc(dev, crtc) {
 		crtc->new_enabled = false;
 		crtc->new_enabled = false;
 
 
@@ -11784,21 +11830,20 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
 				struct drm_file *file)
 				struct drm_file *file)
 {
 {
 	struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
 	struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
-	struct drm_mode_object *drmmode_obj;
+	struct drm_crtc *drmmode_crtc;
 	struct intel_crtc *crtc;
 	struct intel_crtc *crtc;
 
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -ENODEV;
 		return -ENODEV;
 
 
-	drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id,
-			DRM_MODE_OBJECT_CRTC);
+	drmmode_crtc = drm_crtc_find(dev, pipe_from_crtc_id->crtc_id);
 
 
-	if (!drmmode_obj) {
+	if (!drmmode_crtc) {
 		DRM_ERROR("no such CRTC id\n");
 		DRM_ERROR("no such CRTC id\n");
 		return -ENOENT;
 		return -ENOENT;
 	}
 	}
 
 
-	crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
+	crtc = to_intel_crtc(drmmode_crtc);
 	pipe_from_crtc_id->pipe = crtc->pipe;
 	pipe_from_crtc_id->pipe = crtc->pipe;
 
 
 	return 0;
 	return 0;
@@ -13031,7 +13076,7 @@ void intel_connector_unregister(struct intel_connector *intel_connector)
 	struct drm_connector *connector = &intel_connector->base;
 	struct drm_connector *connector = &intel_connector->base;
 
 
 	intel_panel_destroy_backlight(connector);
 	intel_panel_destroy_backlight(connector);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 }
 }
 
 
 void intel_modeset_cleanup(struct drm_device *dev)
 void intel_modeset_cleanup(struct drm_device *dev)

+ 220 - 18
drivers/gpu/drm/i915/intel_dp.c

@@ -112,7 +112,7 @@ static void intel_dp_link_down(struct intel_dp *intel_dp);
 static bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
 static bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
 static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
 
 
-static int
+int
 intel_dp_max_link_bw(struct intel_dp *intel_dp)
 intel_dp_max_link_bw(struct intel_dp *intel_dp)
 {
 {
 	int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
 	int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
@@ -740,8 +740,9 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector)
 {
 {
 	struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
 	struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
 
 
-	sysfs_remove_link(&intel_connector->base.kdev->kobj,
-			  intel_dp->aux.ddc.dev.kobj.name);
+	if (!intel_connector->mst_port)
+		sysfs_remove_link(&intel_connector->base.kdev->kobj,
+				  intel_dp->aux.ddc.dev.kobj.name);
 	intel_connector_unregister(intel_connector);
 	intel_connector_unregister(intel_connector);
 }
 }
 
 
@@ -3348,6 +3349,33 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
 	edp_panel_vdd_off(intel_dp, false);
 	edp_panel_vdd_off(intel_dp, false);
 }
 }
 
 
+static bool
+intel_dp_probe_mst(struct intel_dp *intel_dp)
+{
+	u8 buf[1];
+
+	if (!intel_dp->can_mst)
+		return false;
+
+	if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
+		return false;
+
+	_edp_panel_vdd_on(intel_dp);
+	if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) {
+		if (buf[0] & DP_MST_CAP) {
+			DRM_DEBUG_KMS("Sink is MST capable\n");
+			intel_dp->is_mst = true;
+		} else {
+			DRM_DEBUG_KMS("Sink is not MST capable\n");
+			intel_dp->is_mst = false;
+		}
+	}
+	edp_panel_vdd_off(intel_dp, false);
+
+	drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+	return intel_dp->is_mst;
+}
+
 int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
 int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
 {
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -3385,6 +3413,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
 				       sink_irq_vector, 1) == 1;
 				       sink_irq_vector, 1) == 1;
 }
 }
 
 
+static bool
+intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector)
+{
+	int ret;
+
+	ret = intel_dp_dpcd_read_wake(&intel_dp->aux,
+					     DP_SINK_COUNT_ESI,
+					     sink_irq_vector, 14);
+	if (ret != 14)
+		return false;
+
+	return true;
+}
+
 static void
 static void
 intel_dp_handle_test_request(struct intel_dp *intel_dp)
 intel_dp_handle_test_request(struct intel_dp *intel_dp)
 {
 {
@@ -3392,6 +3434,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
 	drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK);
 	drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK);
 }
 }
 
 
+static int
+intel_dp_check_mst_status(struct intel_dp *intel_dp)
+{
+	bool bret;
+
+	if (intel_dp->is_mst) {
+		u8 esi[16] = { 0 };
+		int ret = 0;
+		int retry;
+		bool handled;
+		bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+go_again:
+		if (bret == true) {
+
+			/* check link status - esi[10] = 0x200c */
+			if (intel_dp->active_mst_links && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) {
+				DRM_DEBUG_KMS("channel EQ not ok, retraining\n");
+				intel_dp_start_link_train(intel_dp);
+				intel_dp_complete_link_train(intel_dp);
+				intel_dp_stop_link_train(intel_dp);
+			}
+
+			DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], esi[1], esi[2]);
+			ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, &handled);
+
+			if (handled) {
+				for (retry = 0; retry < 3; retry++) {
+					int wret;
+					wret = drm_dp_dpcd_write(&intel_dp->aux,
+								 DP_SINK_COUNT_ESI+1,
+								 &esi[1], 3);
+					if (wret == 3) {
+						break;
+					}
+				}
+
+				bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
+				if (bret == true) {
+					DRM_DEBUG_KMS("got esi2 %02x %02x %02x\n", esi[0], esi[1], esi[2]);
+					goto go_again;
+				}
+			} else
+				ret = 0;
+
+			return ret;
+		} else {
+			struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+			DRM_DEBUG_KMS("failed to get ESI - device may have failed\n");
+			intel_dp->is_mst = false;
+			drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+			/* send a hotplug event */
+			drm_kms_helper_hotplug_event(intel_dig_port->base.base.dev);
+		}
+	}
+	return -EINVAL;
+}
+
 /*
 /*
  * According to DP spec
  * According to DP spec
  * 5.1.2:
  * 5.1.2:
@@ -3400,7 +3499,6 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
  *  3. Use Link Training from 2.5.3.3 and 3.5.1.3
  *  3. Use Link Training from 2.5.3.3 and 3.5.1.3
  *  4. Check link status on receipt of hot-plug interrupt
  *  4. Check link status on receipt of hot-plug interrupt
  */
  */
-
 void
 void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
 intel_dp_check_link_status(struct intel_dp *intel_dp)
 {
 {
@@ -3620,6 +3718,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 	enum drm_connector_status status;
 	enum drm_connector_status status;
 	enum intel_display_power_domain power_domain;
 	enum intel_display_power_domain power_domain;
 	struct edid *edid = NULL;
 	struct edid *edid = NULL;
+	bool ret;
 
 
 	power_domain = intel_display_port_power_domain(intel_encoder);
 	power_domain = intel_display_port_power_domain(intel_encoder);
 	intel_display_power_get(dev_priv, power_domain);
 	intel_display_power_get(dev_priv, power_domain);
@@ -3627,6 +3726,14 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
 		      connector->base.id, connector->name);
 		      connector->base.id, connector->name);
 
 
+	if (intel_dp->is_mst) {
+		/* MST devices are disconnected from a monitor POV */
+		if (intel_encoder->type != INTEL_OUTPUT_EDP)
+			intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+		status = connector_status_disconnected;
+		goto out;
+	}
+
 	intel_dp->has_audio = false;
 	intel_dp->has_audio = false;
 
 
 	if (HAS_PCH_SPLIT(dev))
 	if (HAS_PCH_SPLIT(dev))
@@ -3639,6 +3746,16 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 
 
 	intel_dp_probe_oui(intel_dp);
 	intel_dp_probe_oui(intel_dp);
 
 
+	ret = intel_dp_probe_mst(intel_dp);
+	if (ret) {
+		/* if we are in MST mode then this connector
+		   won't appear connected or have anything with EDID on it */
+		if (intel_encoder->type != INTEL_OUTPUT_EDP)
+			intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
+		status = connector_status_disconnected;
+		goto out;
+	}
+
 	if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
 	if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
 		intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
 		intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
 	} else {
 	} else {
@@ -3831,6 +3948,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
 
 
 	drm_dp_aux_unregister(&intel_dp->aux);
 	drm_dp_aux_unregister(&intel_dp->aux);
+	intel_dp_mst_encoder_cleanup(intel_dig_port);
 	drm_encoder_cleanup(encoder);
 	drm_encoder_cleanup(encoder);
 	if (is_edp(intel_dp)) {
 	if (is_edp(intel_dp)) {
 		cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
 		cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
@@ -3859,28 +3977,62 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = {
 	.destroy = intel_dp_encoder_destroy,
 	.destroy = intel_dp_encoder_destroy,
 };
 };
 
 
-static void
+void
 intel_dp_hot_plug(struct intel_encoder *intel_encoder)
 intel_dp_hot_plug(struct intel_encoder *intel_encoder)
 {
 {
-	struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
-
-	intel_dp_check_link_status(intel_dp);
+	return;
 }
 }
 
 
 bool
 bool
 intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
 intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
 {
 {
 	struct intel_dp *intel_dp = &intel_dig_port->dp;
 	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int ret;
+	if (intel_dig_port->base.type != INTEL_OUTPUT_EDP)
+		intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT;
 
 
-	if (long_hpd)
-		return true;
+	DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port,
+		      long_hpd ? "long" : "short");
 
 
-	/*
-	 * we'll check the link status via the normal hot plug path later -
-	 * but for short hpds we should check it now
-	 */
-	intel_dp_check_link_status(intel_dp);
+	if (long_hpd) {
+		if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
+			goto mst_fail;
+
+		if (!intel_dp_get_dpcd(intel_dp)) {
+			goto mst_fail;
+		}
+
+		intel_dp_probe_oui(intel_dp);
+
+		if (!intel_dp_probe_mst(intel_dp))
+			goto mst_fail;
+
+	} else {
+		if (intel_dp->is_mst) {
+			ret = intel_dp_check_mst_status(intel_dp);
+			if (ret == -EINVAL)
+				goto mst_fail;
+		}
+
+		if (!intel_dp->is_mst) {
+			/*
+			 * we'll check the link status via the normal hot plug path later -
+			 * but for short hpds we should check it now
+			 */
+			intel_dp_check_link_status(intel_dp);
+		}
+	}
 	return false;
 	return false;
+mst_fail:
+	/* if we were in MST mode, and device is not there get out of MST mode */
+	if (intel_dp->is_mst) {
+		DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n", intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
+		intel_dp->is_mst = false;
+		drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
+	}
+	return true;
 }
 }
 
 
 /* Return which DP Port should be selected for Transcoder DP control */
 /* Return which DP Port should be selected for Transcoder DP control */
@@ -3931,7 +4083,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port)
 	return false;
 	return false;
 }
 }
 
 
-static void
+void
 intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
 intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
 {
 {
 	struct intel_connector *intel_connector = to_intel_connector(connector);
 	struct intel_connector *intel_connector = to_intel_connector(connector);
@@ -4397,7 +4549,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 			  edp_panel_vdd_work);
 			  edp_panel_vdd_work);
 
 
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	if (HAS_DDI(dev))
 	if (HAS_DDI(dev))
 		intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
 		intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
@@ -4430,6 +4582,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 
 
 	intel_dp_aux_init(intel_dp, intel_connector);
 	intel_dp_aux_init(intel_dp, intel_connector);
 
 
+	/* init MST on ports that can support it */
+	if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+		if (port == PORT_B || port == PORT_C || port == PORT_D) {
+			intel_dp_mst_encoder_init(intel_dig_port, intel_connector->base.base.id);
+		}
+	}
+
 	if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
 	if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
 		drm_dp_aux_unregister(&intel_dp->aux);
 		drm_dp_aux_unregister(&intel_dp->aux);
 		if (is_edp(intel_dp)) {
 		if (is_edp(intel_dp)) {
@@ -4438,7 +4597,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 			edp_panel_vdd_off_sync(intel_dp);
 			edp_panel_vdd_off_sync(intel_dp);
 			drm_modeset_unlock(&dev->mode_config.connection_mutex);
 			drm_modeset_unlock(&dev->mode_config.connection_mutex);
 		}
 		}
-		drm_sysfs_connector_remove(connector);
+		drm_connector_unregister(connector);
 		drm_connector_cleanup(connector);
 		drm_connector_cleanup(connector);
 		return false;
 		return false;
 	}
 	}
@@ -4526,3 +4685,46 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
 		kfree(intel_connector);
 		kfree(intel_connector);
 	}
 	}
 }
 }
+
+void intel_dp_mst_suspend(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int i;
+
+	/* disable MST */
+	for (i = 0; i < I915_MAX_PORTS; i++) {
+		struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i];
+		if (!intel_dig_port)
+			continue;
+
+		if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+			if (!intel_dig_port->dp.can_mst)
+				continue;
+			if (intel_dig_port->dp.is_mst)
+				drm_dp_mst_topology_mgr_suspend(&intel_dig_port->dp.mst_mgr);
+		}
+	}
+}
+
+void intel_dp_mst_resume(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	int i;
+
+	for (i = 0; i < I915_MAX_PORTS; i++) {
+		struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i];
+		if (!intel_dig_port)
+			continue;
+		if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
+			int ret;
+
+			if (!intel_dig_port->dp.can_mst)
+				continue;
+
+			ret = drm_dp_mst_topology_mgr_resume(&intel_dig_port->dp.mst_mgr);
+			if (ret != 0) {
+				intel_dp_check_mst_status(&intel_dig_port->dp);
+			}
+		}
+	}
+}

+ 548 - 0
drivers/gpu/drm/i915/intel_dp_mst.c

@@ -0,0 +1,548 @@
+/*
+ * Copyright © 2008 Intel Corporation
+ *             2014 Red Hat Inc.
+ *
+ * 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 <drm/drmP.h>
+#include "i915_drv.h"
+#include "intel_drv.h"
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
+					struct intel_crtc_config *pipe_config)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_digital_port *intel_dig_port = intel_mst->primary;
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	struct drm_device *dev = encoder->base.dev;
+	int bpp;
+	int lane_count, slots;
+	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
+	struct intel_connector *found = NULL, *intel_connector;
+	int mst_pbn;
+
+	pipe_config->dp_encoder_is_mst = true;
+	pipe_config->has_pch_encoder = false;
+	pipe_config->has_dp_encoder = true;
+	bpp = 24;
+	/*
+	 * for MST we always configure max link bw - the spec doesn't
+	 * seem to suggest we should do otherwise.
+	 */
+	lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
+	intel_dp->link_bw = intel_dp_max_link_bw(intel_dp);
+	intel_dp->lane_count = lane_count;
+
+	pipe_config->pipe_bpp = 24;
+	pipe_config->port_clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
+
+	list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+		if (intel_connector->new_encoder == encoder) {
+			found = intel_connector;
+			break;
+		}
+	}
+
+	if (!found) {
+		DRM_ERROR("can't find connector\n");
+		return false;
+	}
+
+	mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp);
+
+	pipe_config->pbn = mst_pbn;
+	slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
+
+	intel_link_compute_m_n(bpp, lane_count,
+			       adjusted_mode->crtc_clock,
+			       pipe_config->port_clock,
+			       &pipe_config->dp_m_n);
+
+	pipe_config->dp_m_n.tu = slots;
+	return true;
+
+}
+
+static void intel_mst_disable_dp(struct intel_encoder *encoder)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_digital_port *intel_dig_port = intel_mst->primary;
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	int ret;
+
+	DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+	drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->port);
+
+	ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+	if (ret) {
+		DRM_ERROR("failed to update payload %d\n", ret);
+	}
+}
+
+static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_digital_port *intel_dig_port = intel_mst->primary;
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+	DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+	/* this can fail */
+	drm_dp_check_act_status(&intel_dp->mst_mgr);
+	/* and this can also fail */
+	drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+
+	drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->port);
+
+	intel_dp->active_mst_links--;
+	intel_mst->port = NULL;
+	if (intel_dp->active_mst_links == 0) {
+		intel_dig_port->base.post_disable(&intel_dig_port->base);
+		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
+	}
+}
+
+static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_digital_port *intel_dig_port = intel_mst->primary;
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	struct drm_device *dev = encoder->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	enum port port = intel_dig_port->port;
+	int ret;
+	uint32_t temp;
+	struct intel_connector *found = NULL, *intel_connector;
+	int slots;
+	struct drm_crtc *crtc = encoder->base.crtc;
+	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+	list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
+		if (intel_connector->new_encoder == encoder) {
+			found = intel_connector;
+			break;
+		}
+	}
+
+	if (!found) {
+		DRM_ERROR("can't find connector\n");
+		return;
+	}
+
+	DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+	intel_mst->port = found->port;
+
+	if (intel_dp->active_mst_links == 0) {
+		enum port port = intel_ddi_get_encoder_port(encoder);
+
+		I915_WRITE(PORT_CLK_SEL(port), intel_crtc->config.ddi_pll_sel);
+
+		intel_ddi_init_dp_buf_reg(&intel_dig_port->base);
+
+		intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+
+
+		intel_dp_start_link_train(intel_dp);
+		intel_dp_complete_link_train(intel_dp);
+		intel_dp_stop_link_train(intel_dp);
+	}
+
+	ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
+				       intel_mst->port, intel_crtc->config.pbn, &slots);
+	if (ret == false) {
+		DRM_ERROR("failed to allocate vcpi\n");
+		return;
+	}
+
+
+	intel_dp->active_mst_links++;
+	temp = I915_READ(DP_TP_STATUS(port));
+	I915_WRITE(DP_TP_STATUS(port), temp);
+
+	ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
+}
+
+static void intel_mst_enable_dp(struct intel_encoder *encoder)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_digital_port *intel_dig_port = intel_mst->primary;
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	enum port port = intel_dig_port->port;
+	int ret;
+
+	DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
+
+	if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT),
+		     1))
+		DRM_ERROR("Timed out waiting for ACT sent\n");
+
+	ret = drm_dp_check_act_status(&intel_dp->mst_mgr);
+
+	ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+}
+
+static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
+				      enum pipe *pipe)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	*pipe = intel_mst->pipe;
+	if (intel_mst->port)
+		return true;
+	return false;
+}
+
+static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
+					struct intel_crtc_config *pipe_config)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+	struct intel_digital_port *intel_dig_port = intel_mst->primary;
+	struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+	struct drm_device *dev = encoder->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
+	u32 temp, flags = 0;
+
+	pipe_config->has_dp_encoder = true;
+
+	temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+	if (temp & TRANS_DDI_PHSYNC)
+		flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		flags |= DRM_MODE_FLAG_NHSYNC;
+	if (temp & TRANS_DDI_PVSYNC)
+		flags |= DRM_MODE_FLAG_PVSYNC;
+	else
+		flags |= DRM_MODE_FLAG_NVSYNC;
+
+	switch (temp & TRANS_DDI_BPC_MASK) {
+	case TRANS_DDI_BPC_6:
+		pipe_config->pipe_bpp = 18;
+		break;
+	case TRANS_DDI_BPC_8:
+		pipe_config->pipe_bpp = 24;
+		break;
+	case TRANS_DDI_BPC_10:
+		pipe_config->pipe_bpp = 30;
+		break;
+	case TRANS_DDI_BPC_12:
+		pipe_config->pipe_bpp = 36;
+		break;
+	default:
+		break;
+	}
+	pipe_config->adjusted_mode.flags |= flags;
+	intel_dp_get_m_n(crtc, pipe_config);
+
+	intel_ddi_clock_get(&intel_dig_port->base, pipe_config);
+}
+
+static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
+{
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+	struct intel_dp *intel_dp = intel_connector->mst_port;
+	struct edid *edid;
+	int ret;
+
+	edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
+	if (!edid)
+		return 0;
+
+	ret = intel_connector_update_modes(connector, edid);
+	kfree(edid);
+
+	return ret;
+}
+
+static enum drm_connector_status
+intel_mst_port_dp_detect(struct drm_connector *connector)
+{
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+	struct intel_dp *intel_dp = intel_connector->mst_port;
+
+	return drm_dp_mst_detect_port(&intel_dp->mst_mgr, intel_connector->port);
+}
+
+static enum drm_connector_status
+intel_dp_mst_detect(struct drm_connector *connector, bool force)
+{
+	enum drm_connector_status status;
+	status = intel_mst_port_dp_detect(connector);
+	return status;
+}
+
+static int
+intel_dp_mst_set_property(struct drm_connector *connector,
+			  struct drm_property *property,
+			  uint64_t val)
+{
+	return 0;
+}
+
+static void
+intel_dp_mst_connector_destroy(struct drm_connector *connector)
+{
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+
+	if (!IS_ERR_OR_NULL(intel_connector->edid))
+		kfree(intel_connector->edid);
+
+	drm_connector_cleanup(connector);
+	kfree(connector);
+}
+
+static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
+	.dpms = intel_connector_dpms,
+	.detect = intel_dp_mst_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.set_property = intel_dp_mst_set_property,
+	.destroy = intel_dp_mst_connector_destroy,
+};
+
+static int intel_dp_mst_get_modes(struct drm_connector *connector)
+{
+	return intel_dp_mst_get_ddc_modes(connector);
+}
+
+static enum drm_mode_status
+intel_dp_mst_mode_valid(struct drm_connector *connector,
+			struct drm_display_mode *mode)
+{
+	/* TODO - validate mode against available PBN for link */
+	if (mode->clock < 10000)
+		return MODE_CLOCK_LOW;
+
+	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+		return MODE_H_ILLEGAL;
+
+	return MODE_OK;
+}
+
+static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector)
+{
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+	struct intel_dp *intel_dp = intel_connector->mst_port;
+	return &intel_dp->mst_encoders[0]->base.base;
+}
+
+static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
+	.get_modes = intel_dp_mst_get_modes,
+	.mode_valid = intel_dp_mst_mode_valid,
+	.best_encoder = intel_mst_best_encoder,
+};
+
+static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
+{
+	struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
+
+	drm_encoder_cleanup(encoder);
+	kfree(intel_mst);
+}
+
+static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
+	.destroy = intel_dp_mst_encoder_destroy,
+};
+
+static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
+{
+	if (connector->encoder) {
+		enum pipe pipe;
+		if (!connector->encoder->get_hw_state(connector->encoder, &pipe))
+			return false;
+		return true;
+	}
+	return false;
+}
+
+static void intel_connector_add_to_fbdev(struct intel_connector *connector)
+{
+#ifdef CONFIG_DRM_I915_FBDEV
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+	drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, &connector->base);
+#endif
+}
+
+static void intel_connector_remove_from_fbdev(struct intel_connector *connector)
+{
+#ifdef CONFIG_DRM_I915_FBDEV
+	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+	drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, &connector->base);
+#endif
+}
+
+static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop)
+{
+	struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	struct intel_connector *intel_connector;
+	struct drm_connector *connector;
+	int i;
+
+	intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
+	if (!intel_connector)
+		return NULL;
+
+	connector = &intel_connector->base;
+	drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
+	drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
+
+	intel_connector->unregister = intel_connector_unregister;
+	intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
+	intel_connector->mst_port = intel_dp;
+	intel_connector->port = port;
+
+	for (i = PIPE_A; i <= PIPE_C; i++) {
+		drm_mode_connector_attach_encoder(&intel_connector->base,
+						  &intel_dp->mst_encoders[i]->base.base);
+	}
+	intel_dp_add_properties(intel_dp, connector);
+
+	drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
+	drm_mode_connector_set_path_property(connector, pathprop);
+	drm_reinit_primary_mode_group(dev);
+	mutex_lock(&dev->mode_config.mutex);
+	intel_connector_add_to_fbdev(intel_connector);
+	mutex_unlock(&dev->mode_config.mutex);
+	drm_connector_register(&intel_connector->base);
+	return connector;
+}
+
+static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
+					   struct drm_connector *connector)
+{
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+	struct drm_device *dev = connector->dev;
+	/* need to nuke the connector */
+	mutex_lock(&dev->mode_config.mutex);
+	intel_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	mutex_unlock(&dev->mode_config.mutex);
+
+	intel_connector->unregister(intel_connector);
+
+	mutex_lock(&dev->mode_config.mutex);
+	intel_connector_remove_from_fbdev(intel_connector);
+	drm_connector_cleanup(connector);
+	mutex_unlock(&dev->mode_config.mutex);
+
+	drm_reinit_primary_mode_group(dev);
+
+	kfree(intel_connector);
+	DRM_DEBUG_KMS("\n");
+}
+
+static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
+{
+	struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+
+	drm_kms_helper_hotplug_event(dev);
+}
+
+static struct drm_dp_mst_topology_cbs mst_cbs = {
+	.add_connector = intel_dp_add_mst_connector,
+	.destroy_connector = intel_dp_destroy_mst_connector,
+	.hotplug = intel_dp_mst_hotplug,
+};
+
+static struct intel_dp_mst_encoder *
+intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
+{
+	struct intel_dp_mst_encoder *intel_mst;
+	struct intel_encoder *intel_encoder;
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+
+	intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
+
+	if (!intel_mst)
+		return NULL;
+
+	intel_mst->pipe = pipe;
+	intel_encoder = &intel_mst->base;
+	intel_mst->primary = intel_dig_port;
+
+	drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
+			 DRM_MODE_ENCODER_DPMST);
+
+	intel_encoder->type = INTEL_OUTPUT_DP_MST;
+	intel_encoder->crtc_mask = 0x7;
+	intel_encoder->cloneable = 0;
+
+	intel_encoder->compute_config = intel_dp_mst_compute_config;
+	intel_encoder->disable = intel_mst_disable_dp;
+	intel_encoder->post_disable = intel_mst_post_disable_dp;
+	intel_encoder->pre_enable = intel_mst_pre_enable_dp;
+	intel_encoder->enable = intel_mst_enable_dp;
+	intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
+	intel_encoder->get_config = intel_dp_mst_enc_get_config;
+
+	return intel_mst;
+
+}
+
+static bool
+intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
+{
+	int i;
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+	for (i = PIPE_A; i <= PIPE_C; i++)
+		intel_dp->mst_encoders[i] = intel_dp_create_fake_mst_encoder(intel_dig_port, i);
+	return true;
+}
+
+int
+intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
+{
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+	struct drm_device *dev = intel_dig_port->base.base.dev;
+	int ret;
+
+	intel_dp->can_mst = true;
+	intel_dp->mst_mgr.cbs = &mst_cbs;
+
+	/* create encoders */
+	intel_dp_create_fake_mst_encoders(intel_dig_port);
+	ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3, conn_base_id);
+	if (ret) {
+		intel_dp->can_mst = false;
+		return ret;
+	}
+	return 0;
+}
+
+void
+intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
+{
+	struct intel_dp *intel_dp = &intel_dig_port->dp;
+
+	if (!intel_dp->can_mst)
+		return;
+
+	drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
+	/* encoders will get killed by normal cleanup */
+}

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

@@ -32,7 +32,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fb_helper.h>
-#include <drm/drm_dp_helper.h>
+#include <drm/drm_dp_mst_helper.h>
 
 
 /**
 /**
  * _wait_for - magic (register) wait macro
  * _wait_for - magic (register) wait macro
@@ -100,6 +100,7 @@
 #define INTEL_OUTPUT_EDP 8
 #define INTEL_OUTPUT_EDP 8
 #define INTEL_OUTPUT_DSI 9
 #define INTEL_OUTPUT_DSI 9
 #define INTEL_OUTPUT_UNKNOWN 10
 #define INTEL_OUTPUT_UNKNOWN 10
+#define INTEL_OUTPUT_DP_MST 11
 
 
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_NONE 0
 #define INTEL_DVO_CHIP_LVDS 1
 #define INTEL_DVO_CHIP_LVDS 1
@@ -208,6 +209,10 @@ struct intel_connector {
 	/* since POLL and HPD connectors may use the same HPD line keep the native
 	/* since POLL and HPD connectors may use the same HPD line keep the native
 	   state of connector->polled in case hotplug storm detection changes it */
 	   state of connector->polled in case hotplug storm detection changes it */
 	u8 polled;
 	u8 polled;
+
+	void *port; /* store this opaque as its illegal to dereference it */
+
+	struct intel_dp *mst_port;
 };
 };
 
 
 typedef struct dpll {
 typedef struct dpll {
@@ -352,6 +357,9 @@ struct intel_crtc_config {
 	bool ips_enabled;
 	bool ips_enabled;
 
 
 	bool double_wide;
 	bool double_wide;
+
+	bool dp_encoder_is_mst;
+	int pbn;
 };
 };
 
 
 struct intel_pipe_wm {
 struct intel_pipe_wm {
@@ -509,6 +517,7 @@ struct intel_hdmi {
 			       struct drm_display_mode *adjusted_mode);
 			       struct drm_display_mode *adjusted_mode);
 };
 };
 
 
+struct intel_dp_mst_encoder;
 #define DP_MAX_DOWNSTREAM_PORTS		0x10
 #define DP_MAX_DOWNSTREAM_PORTS		0x10
 
 
 /**
 /**
@@ -548,8 +557,16 @@ struct intel_dp {
 	unsigned long last_power_on;
 	unsigned long last_power_on;
 	unsigned long last_backlight_off;
 	unsigned long last_backlight_off;
 	bool use_tps3;
 	bool use_tps3;
+	bool can_mst; /* this port supports mst */
+	bool is_mst;
+	int active_mst_links;
+	/* connector directly attached - won't be use for modeset in mst world */
 	struct intel_connector *attached_connector;
 	struct intel_connector *attached_connector;
 
 
+	/* mst connector list */
+	struct intel_dp_mst_encoder *mst_encoders[I915_MAX_PIPES];
+	struct drm_dp_mst_topology_mgr mst_mgr;
+
 	uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
 	uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
 	/*
 	/*
 	 * This function returns the value we have to program the AUX_CTL
 	 * This function returns the value we have to program the AUX_CTL
@@ -576,6 +593,13 @@ struct intel_digital_port {
 	bool (*hpd_pulse)(struct intel_digital_port *, bool);
 	bool (*hpd_pulse)(struct intel_digital_port *, bool);
 };
 };
 
 
+struct intel_dp_mst_encoder {
+	struct intel_encoder base;
+	enum pipe pipe;
+	struct intel_digital_port *primary;
+	void *port; /* store this opaque as its illegal to dereference it */
+};
+
 static inline int
 static inline int
 vlv_dport_to_channel(struct intel_digital_port *dport)
 vlv_dport_to_channel(struct intel_digital_port *dport)
 {
 {
@@ -660,6 +684,12 @@ enc_to_dig_port(struct drm_encoder *encoder)
 	return container_of(encoder, struct intel_digital_port, base.base);
 	return container_of(encoder, struct intel_digital_port, base.base);
 }
 }
 
 
+static inline struct intel_dp_mst_encoder *
+enc_to_mst(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct intel_dp_mst_encoder, base.base);
+}
+
 static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
 static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
 {
 {
 	return &enc_to_dig_port(encoder)->dp;
 	return &enc_to_dig_port(encoder)->dp;
@@ -730,6 +760,10 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc);
 void intel_ddi_get_config(struct intel_encoder *encoder,
 void intel_ddi_get_config(struct intel_encoder *encoder,
 			  struct intel_crtc_config *pipe_config);
 			  struct intel_crtc_config *pipe_config);
 
 
+void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder);
+void intel_ddi_clock_get(struct intel_encoder *encoder,
+			 struct intel_crtc_config *pipe_config);
+void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
 
 
 /* intel_display.c */
 /* intel_display.c */
 const char *intel_output_name(int output);
 const char *intel_output_name(int output);
@@ -886,6 +920,15 @@ void intel_edp_psr_flush(struct drm_device *dev,
 			 unsigned frontbuffer_bits);
 			 unsigned frontbuffer_bits);
 void intel_edp_psr_init(struct drm_device *dev);
 void intel_edp_psr_init(struct drm_device *dev);
 
 
+int intel_dp_handle_hpd_irq(struct intel_digital_port *digport, bool long_hpd);
+void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector);
+void intel_dp_mst_suspend(struct drm_device *dev);
+void intel_dp_mst_resume(struct drm_device *dev);
+int intel_dp_max_link_bw(struct intel_dp *intel_dp);
+void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
+/* intel_dp_mst.c */
+int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
+void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
 /* intel_dsi.c */
 /* intel_dsi.c */
 void intel_dsi_init(struct drm_device *dev);
 void intel_dsi_init(struct drm_device *dev);
 
 

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

@@ -742,7 +742,7 @@ void intel_dsi_init(struct drm_device *dev)
 
 
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev);
 	fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev);
 	if (!fixed_mode) {
 	if (!fixed_mode) {

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

@@ -566,7 +566,7 @@ void intel_dvo_init(struct drm_device *dev)
 			intel_dvo->panel_wants_dither = true;
 			intel_dvo->panel_wants_dither = true;
 		}
 		}
 
 
-		drm_sysfs_connector_add(connector);
+		drm_connector_register(connector);
 		return;
 		return;
 	}
 	}
 
 

+ 3 - 2
drivers/gpu/drm/i915/intel_fbdev.c

@@ -478,7 +478,7 @@ out:
 	return true;
 	return true;
 }
 }
 
 
-static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
 	.initial_config = intel_fb_initial_config,
 	.initial_config = intel_fb_initial_config,
 	.gamma_set = intel_crtc_fb_gamma_set,
 	.gamma_set = intel_crtc_fb_gamma_set,
 	.gamma_get = intel_crtc_fb_gamma_get,
 	.gamma_get = intel_crtc_fb_gamma_get,
@@ -649,7 +649,8 @@ int intel_fbdev_init(struct drm_device *dev)
 	if (ifbdev == NULL)
 	if (ifbdev == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
+
 	if (!intel_fbdev_init_bios(dev, ifbdev))
 	if (!intel_fbdev_init_bios(dev, ifbdev))
 		ifbdev->preferred_bpp = 32;
 		ifbdev->preferred_bpp = 32;
 
 

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

@@ -1585,7 +1585,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
 	intel_hdmi_add_properties(intel_hdmi, connector);
 	intel_hdmi_add_properties(intel_hdmi, connector);
 
 
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
 	intel_connector_attach_encoder(intel_connector, intel_encoder);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
 	/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
 	 * 0xd.  Failure to do so will result in spurious interrupts being
 	 * 0xd.  Failure to do so will result in spurious interrupts being

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

@@ -1107,7 +1107,7 @@ out:
 		DRM_DEBUG_KMS("lid notifier registration failed\n");
 		DRM_DEBUG_KMS("lid notifier registration failed\n");
 		lvds_connector->lid_notifier.notifier_call = NULL;
 		lvds_connector->lid_notifier.notifier_call = NULL;
 	}
 	}
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
 	intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
 	intel_panel_setup_backlight(connector);
 	intel_panel_setup_backlight(connector);

+ 1 - 0
drivers/gpu/drm/i915/intel_opregion.c

@@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
 	case INTEL_OUTPUT_UNKNOWN:
 	case INTEL_OUTPUT_UNKNOWN:
 	case INTEL_OUTPUT_DISPLAYPORT:
 	case INTEL_OUTPUT_DISPLAYPORT:
 	case INTEL_OUTPUT_HDMI:
 	case INTEL_OUTPUT_HDMI:
+	case INTEL_OUTPUT_DP_MST:
 		type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
 		type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
 		break;
 		break;
 	case INTEL_OUTPUT_EDP:
 	case INTEL_OUTPUT_EDP:

+ 4 - 5
drivers/gpu/drm/i915/intel_overlay.c

@@ -1039,7 +1039,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
 	struct drm_intel_overlay_put_image *put_image_rec = data;
 	struct drm_intel_overlay_put_image *put_image_rec = data;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_overlay *overlay;
 	struct intel_overlay *overlay;
-	struct drm_mode_object *drmmode_obj;
+	struct drm_crtc *drmmode_crtc;
 	struct intel_crtc *crtc;
 	struct intel_crtc *crtc;
 	struct drm_i915_gem_object *new_bo;
 	struct drm_i915_gem_object *new_bo;
 	struct put_image_params *params;
 	struct put_image_params *params;
@@ -1068,13 +1068,12 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
 	if (!params)
 	if (!params)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
-					   DRM_MODE_OBJECT_CRTC);
-	if (!drmmode_obj) {
+	drmmode_crtc = drm_crtc_find(dev, put_image_rec->crtc_id);
+	if (!drmmode_crtc) {
 		ret = -ENOENT;
 		ret = -ENOENT;
 		goto out_free;
 		goto out_free;
 	}
 	}
-	crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
+	crtc = to_intel_crtc(drmmode_crtc);
 
 
 	new_bo = to_intel_bo(drm_gem_object_lookup(dev, file_priv,
 	new_bo = to_intel_bo(drm_gem_object_lookup(dev, file_priv,
 						   put_image_rec->bo_handle));
 						   put_image_rec->bo_handle));

+ 5 - 5
drivers/gpu/drm/i915/intel_sdvo.c

@@ -2433,7 +2433,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
 	connector->base.unregister = intel_sdvo_connector_unregister;
 	connector->base.unregister = intel_sdvo_connector_unregister;
 
 
 	intel_connector_attach_encoder(&connector->base, &encoder->base);
 	intel_connector_attach_encoder(&connector->base, &encoder->base);
-	ret = drm_sysfs_connector_add(drm_connector);
+	ret = drm_connector_register(drm_connector);
 	if (ret < 0)
 	if (ret < 0)
 		goto err1;
 		goto err1;
 
 
@@ -2446,7 +2446,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
 	return 0;
 	return 0;
 
 
 err2:
 err2:
-	drm_sysfs_connector_remove(drm_connector);
+	drm_connector_unregister(drm_connector);
 err1:
 err1:
 	drm_connector_cleanup(drm_connector);
 	drm_connector_cleanup(drm_connector);
 
 
@@ -2559,7 +2559,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
 	return true;
 	return true;
 
 
 err:
 err:
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	intel_sdvo_destroy(connector);
 	intel_sdvo_destroy(connector);
 	return false;
 	return false;
 }
 }
@@ -2638,7 +2638,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
 	return true;
 	return true;
 
 
 err:
 err:
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	intel_sdvo_destroy(connector);
 	intel_sdvo_destroy(connector);
 	return false;
 	return false;
 }
 }
@@ -2711,7 +2711,7 @@ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo)
 	list_for_each_entry_safe(connector, tmp,
 	list_for_each_entry_safe(connector, tmp,
 				 &dev->mode_config.connector_list, head) {
 				 &dev->mode_config.connector_list, head) {
 		if (intel_attached_encoder(connector) == &intel_sdvo->base) {
 		if (intel_attached_encoder(connector) == &intel_sdvo->base) {
-			drm_sysfs_connector_remove(connector);
+			drm_connector_unregister(connector);
 			intel_sdvo_destroy(connector);
 			intel_sdvo_destroy(connector);
 		}
 		}
 	}
 	}

+ 4 - 8
drivers/gpu/drm/i915/intel_sprite.c

@@ -1126,7 +1126,6 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
 			      struct drm_file *file_priv)
 			      struct drm_file *file_priv)
 {
 {
 	struct drm_intel_sprite_colorkey *set = data;
 	struct drm_intel_sprite_colorkey *set = data;
-	struct drm_mode_object *obj;
 	struct drm_plane *plane;
 	struct drm_plane *plane;
 	struct intel_plane *intel_plane;
 	struct intel_plane *intel_plane;
 	int ret = 0;
 	int ret = 0;
@@ -1140,13 +1139,12 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
 
 
 	drm_modeset_lock_all(dev);
 	drm_modeset_lock_all(dev);
 
 
-	obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);
-	if (!obj) {
+	plane = drm_plane_find(dev, set->plane_id);
+	if (!plane) {
 		ret = -ENOENT;
 		ret = -ENOENT;
 		goto out_unlock;
 		goto out_unlock;
 	}
 	}
 
 
-	plane = obj_to_plane(obj);
 	intel_plane = to_intel_plane(plane);
 	intel_plane = to_intel_plane(plane);
 	ret = intel_plane->update_colorkey(plane, set);
 	ret = intel_plane->update_colorkey(plane, set);
 
 
@@ -1159,7 +1157,6 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
 			      struct drm_file *file_priv)
 			      struct drm_file *file_priv)
 {
 {
 	struct drm_intel_sprite_colorkey *get = data;
 	struct drm_intel_sprite_colorkey *get = data;
-	struct drm_mode_object *obj;
 	struct drm_plane *plane;
 	struct drm_plane *plane;
 	struct intel_plane *intel_plane;
 	struct intel_plane *intel_plane;
 	int ret = 0;
 	int ret = 0;
@@ -1169,13 +1166,12 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
 
 
 	drm_modeset_lock_all(dev);
 	drm_modeset_lock_all(dev);
 
 
-	obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);
-	if (!obj) {
+	plane = drm_plane_find(dev, get->plane_id);
+	if (!plane) {
 		ret = -ENOENT;
 		ret = -ENOENT;
 		goto out_unlock;
 		goto out_unlock;
 	}
 	}
 
 
-	plane = obj_to_plane(obj);
 	intel_plane = to_intel_plane(plane);
 	intel_plane = to_intel_plane(plane);
 	intel_plane->get_colorkey(plane, get);
 	intel_plane->get_colorkey(plane, get);
 
 

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

@@ -1680,5 +1680,5 @@ intel_tv_init(struct drm_device *dev)
 	drm_object_attach_property(&connector->base,
 	drm_object_attach_property(&connector->base,
 				   dev->mode_config.tv_bottom_margin_property,
 				   dev->mode_config.tv_bottom_margin_property,
 				   intel_tv->margin[TV_MARGIN_BOTTOM]);
 				   intel_tv->margin[TV_MARGIN_BOTTOM]);
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 }
 }

+ 1 - 1
drivers/gpu/drm/mgag200/mgag200_drv.h

@@ -280,7 +280,7 @@ static inline int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+	ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
 	if (ret) {
 	if (ret) {
 		if (ret != -ERESTARTSYS && ret != -EBUSY)
 		if (ret != -ERESTARTSYS && ret != -EBUSY)
 			DRM_ERROR("reserve failed %p\n", bo);
 			DRM_ERROR("reserve failed %p\n", bo);

+ 3 - 2
drivers/gpu/drm/mgag200/mgag200_fb.c

@@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
 	return 0;
 	return 0;
 }
 }
 
 
-static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
 	.gamma_set = mga_crtc_fb_gamma_set,
 	.gamma_set = mga_crtc_fb_gamma_set,
 	.gamma_get = mga_crtc_fb_gamma_get,
 	.gamma_get = mga_crtc_fb_gamma_get,
 	.fb_probe = mgag200fb_create,
 	.fb_probe = mgag200fb_create,
@@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	mdev->mfbdev = mfbdev;
 	mdev->mfbdev = mfbdev;
-	mfbdev->helper.funcs = &mga_fb_helper_funcs;
 	spin_lock_init(&mfbdev->dirty_lock);
 	spin_lock_init(&mfbdev->dirty_lock);
 
 
+	drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
+
 	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
 	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
 				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
 				 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
 	if (ret)
 	if (ret)

+ 3 - 13
drivers/gpu/drm/mgag200/mgag200_mode.c

@@ -1562,19 +1562,9 @@ static struct drm_encoder *mga_connector_best_encoder(struct drm_connector
 						  *connector)
 						  *connector)
 {
 {
 	int enc_id = connector->encoder_ids[0];
 	int enc_id = connector->encoder_ids[0];
-	struct drm_mode_object *obj;
-	struct drm_encoder *encoder;
-
 	/* pick the encoder ids */
 	/* pick the encoder ids */
-	if (enc_id) {
-		obj =
-		    drm_mode_object_find(connector->dev, enc_id,
-					 DRM_MODE_OBJECT_ENCODER);
-		if (!obj)
-			return NULL;
-		encoder = obj_to_encoder(obj);
-		return encoder;
-	}
+	if (enc_id)
+		return drm_encoder_find(connector->dev, enc_id);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -1621,7 +1611,7 @@ static struct drm_connector *mga_vga_init(struct drm_device *dev)
 
 
 	drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
 	drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	mga_connector->i2c = mgag200_i2c_create(dev);
 	mga_connector->i2c = mgag200_i2c_create(dev);
 	if (!mga_connector->i2c)
 	if (!mga_connector->i2c)

+ 2 - 2
drivers/gpu/drm/msm/hdmi/hdmi_connector.c

@@ -306,7 +306,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector)
 
 
 	hdp_disable(hdmi_connector);
 	hdp_disable(hdmi_connector);
 
 
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 
 
 	hdmi_unreference(hdmi_connector->hdmi);
 	hdmi_unreference(hdmi_connector->hdmi);
@@ -416,7 +416,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
 	connector->interlace_allowed = 1;
 	connector->interlace_allowed = 1;
 	connector->doublescan_allowed = 0;
 	connector->doublescan_allowed = 0;
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	ret = hpd_enable(hdmi_connector);
 	ret = hpd_enable(hdmi_connector);
 	if (ret) {
 	if (ret) {

+ 35 - 48
drivers/gpu/drm/msm/msm_drv.c

@@ -905,12 +905,41 @@ static int compare_of(struct device *dev, void *data)
 {
 {
 	return dev->of_node == data;
 	return dev->of_node == data;
 }
 }
+#else
+static int compare_dev(struct device *dev, void *data)
+{
+	return dev == data;
+}
+#endif
+
+static int msm_drm_bind(struct device *dev)
+{
+	return drm_platform_init(&msm_driver, to_platform_device(dev));
+}
+
+static void msm_drm_unbind(struct device *dev)
+{
+	drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
+}
+
+static const struct component_master_ops msm_drm_ops = {
+	.bind = msm_drm_bind,
+	.unbind = msm_drm_unbind,
+};
+
+/*
+ * Platform driver:
+ */
 
 
-static int msm_drm_add_components(struct device *master, struct master *m)
+static int msm_pdev_probe(struct platform_device *pdev)
 {
 {
-	struct device_node *np = master->of_node;
+	struct component_match *match = NULL;
+#ifdef CONFIG_OF
+	/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx
+	 * (or probably any other).. so probably some room for some helpers
+	 */
+	struct device_node *np = pdev->dev.of_node;
 	unsigned i;
 	unsigned i;
-	int ret;
 
 
 	for (i = 0; ; i++) {
 	for (i = 0; ; i++) {
 		struct device_node *node;
 		struct device_node *node;
@@ -919,22 +948,9 @@ static int msm_drm_add_components(struct device *master, struct master *m)
 		if (!node)
 		if (!node)
 			break;
 			break;
 
 
-		ret = component_master_add_child(m, compare_of, node);
-		of_node_put(node);
-
-		if (ret)
-			return ret;
+		component_match_add(&pdev->dev, &match, compare_of, node);
 	}
 	}
-	return 0;
-}
 #else
 #else
-static int compare_dev(struct device *dev, void *data)
-{
-	return dev == data;
-}
-
-static int msm_drm_add_components(struct device *master, struct master *m)
-{
 	/* For non-DT case, it kinda sucks.  We don't actually have a way
 	/* For non-DT case, it kinda sucks.  We don't actually have a way
 	 * to know whether or not we are waiting for certain devices (or if
 	 * to know whether or not we are waiting for certain devices (or if
 	 * they are simply not present).  But for non-DT we only need to
 	 * they are simply not present).  But for non-DT we only need to
@@ -958,41 +974,12 @@ static int msm_drm_add_components(struct device *master, struct master *m)
 			return -EPROBE_DEFER;
 			return -EPROBE_DEFER;
 		}
 		}
 
 
-		ret = component_master_add_child(m, compare_dev, dev);
-		if (ret) {
-			DBG("could not add child: %d", ret);
-			return ret;
-		}
+		component_match_add(&pdev->dev, &match, compare_dev, dev);
 	}
 	}
-
-	return 0;
-}
 #endif
 #endif
 
 
-static int msm_drm_bind(struct device *dev)
-{
-	return drm_platform_init(&msm_driver, to_platform_device(dev));
-}
-
-static void msm_drm_unbind(struct device *dev)
-{
-	drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
-}
-
-static const struct component_master_ops msm_drm_ops = {
-		.add_components = msm_drm_add_components,
-		.bind = msm_drm_bind,
-		.unbind = msm_drm_unbind,
-};
-
-/*
- * Platform driver:
- */
-
-static int msm_pdev_probe(struct platform_device *pdev)
-{
 	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
 	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
-	return component_master_add(&pdev->dev, &msm_drm_ops);
+	return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
 }
 }
 
 
 static int msm_pdev_remove(struct platform_device *pdev)
 static int msm_pdev_remove(struct platform_device *pdev)

+ 2 - 2
drivers/gpu/drm/msm/msm_fbdev.c

@@ -177,7 +177,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
 	DBG("fbdev: get gamma");
 	DBG("fbdev: get gamma");
 }
 }
 
 
-static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
 	.gamma_set = msm_crtc_fb_gamma_set,
 	.gamma_set = msm_crtc_fb_gamma_set,
 	.gamma_get = msm_crtc_fb_gamma_get,
 	.gamma_get = msm_crtc_fb_gamma_get,
 	.fb_probe = msm_fbdev_create,
 	.fb_probe = msm_fbdev_create,
@@ -197,7 +197,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
 
 
 	helper = &fbdev->base;
 	helper = &fbdev->base;
 
 
-	helper->funcs = &msm_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
 
 
 	ret = drm_fb_helper_init(dev, helper,
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
 			priv->num_crtcs, priv->num_connectors);

+ 1 - 1
drivers/gpu/drm/msm/msm_gem.c

@@ -73,7 +73,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
 		int npages = obj->size >> PAGE_SHIFT;
 		int npages = obj->size >> PAGE_SHIFT;
 
 
 		if (iommu_present(&platform_bus_type))
 		if (iommu_present(&platform_bus_type))
-			p = drm_gem_get_pages(obj, 0);
+			p = drm_gem_get_pages(obj);
 		else
 		else
 			p = get_pages_vram(obj, npages);
 			p = get_pages_vram(obj, npages);
 
 

+ 3 - 3
drivers/gpu/drm/nouveau/nouveau_bo.c

@@ -309,7 +309,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
 	struct ttm_buffer_object *bo = &nvbo->bo;
 	struct ttm_buffer_object *bo = &nvbo->bo;
 	int ret;
 	int ret;
 
 
-	ret = ttm_bo_reserve(bo, false, false, false, 0);
+	ret = ttm_bo_reserve(bo, false, false, false, NULL);
 	if (ret)
 	if (ret)
 		goto out;
 		goto out;
 
 
@@ -350,7 +350,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
 	struct ttm_buffer_object *bo = &nvbo->bo;
 	struct ttm_buffer_object *bo = &nvbo->bo;
 	int ret, ref;
 	int ret, ref;
 
 
-	ret = ttm_bo_reserve(bo, false, false, false, 0);
+	ret = ttm_bo_reserve(bo, false, false, false, NULL);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
@@ -385,7 +385,7 @@ nouveau_bo_map(struct nouveau_bo *nvbo)
 {
 {
 	int ret;
 	int ret;
 
 
-	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 

+ 10 - 10
drivers/gpu/drm/nouveau/nouveau_connector.c

@@ -63,7 +63,7 @@ find_encoder(struct drm_connector *connector, int type)
 {
 {
 	struct drm_device *dev = connector->dev;
 	struct drm_device *dev = connector->dev;
 	struct nouveau_encoder *nv_encoder;
 	struct nouveau_encoder *nv_encoder;
-	struct drm_mode_object *obj;
+	struct drm_encoder *enc;
 	int i, id;
 	int i, id;
 
 
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
@@ -71,10 +71,10 @@ find_encoder(struct drm_connector *connector, int type)
 		if (!id)
 		if (!id)
 			break;
 			break;
 
 
-		obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
-		if (!obj)
+		enc = drm_encoder_find(dev, id);
+		if (!enc)
 			continue;
 			continue;
-		nv_encoder = nouveau_encoder(obj_to_encoder(obj));
+		nv_encoder = nouveau_encoder(enc);
 
 
 		if (type == DCB_OUTPUT_ANY ||
 		if (type == DCB_OUTPUT_ANY ||
 		    (nv_encoder->dcb && nv_encoder->dcb->type == type))
 		    (nv_encoder->dcb && nv_encoder->dcb->type == type))
@@ -104,7 +104,7 @@ nouveau_connector_destroy(struct drm_connector *connector)
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
 	nouveau_event_ref(NULL, &nv_connector->hpd);
 	nouveau_event_ref(NULL, &nv_connector->hpd);
 	kfree(nv_connector->edid);
 	kfree(nv_connector->edid);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	if (nv_connector->aux.transfer)
 	if (nv_connector->aux.transfer)
 		drm_dp_aux_unregister(&nv_connector->aux);
 		drm_dp_aux_unregister(&nv_connector->aux);
@@ -119,7 +119,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
 	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nouveau_drm *drm = nouveau_drm(dev);
 	struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
 	struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
 	struct nouveau_encoder *nv_encoder;
 	struct nouveau_encoder *nv_encoder;
-	struct drm_mode_object *obj;
+	struct drm_encoder *encoder;
 	int i, panel = -ENODEV;
 	int i, panel = -ENODEV;
 
 
 	/* eDP panels need powering on by us (if the VBIOS doesn't default it
 	/* eDP panels need powering on by us (if the VBIOS doesn't default it
@@ -139,10 +139,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
 		if (id == 0)
 		if (id == 0)
 			break;
 			break;
 
 
-		obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
-		if (!obj)
+		encoder = drm_encoder_find(dev, id);
+		if (!encoder)
 			continue;
 			continue;
-		nv_encoder = nouveau_encoder(obj_to_encoder(obj));
+		nv_encoder = nouveau_encoder(encoder);
 
 
 		if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
 		if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
 			int ret = nouveau_dp_detect(nv_encoder);
 			int ret = nouveau_dp_detect(nv_encoder);
@@ -1236,6 +1236,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
 
 
 	INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work);
 	INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work);
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 	return connector;
 	return connector;
 }
 }

+ 3 - 2
drivers/gpu/drm/nouveau/nouveau_fbcon.c

@@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 	info->flags |= FBINFO_HWACCEL_DISABLED;
 	info->flags |= FBINFO_HWACCEL_DISABLED;
 }
 }
 
 
-static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
 	.gamma_set = nouveau_fbcon_gamma_set,
 	.gamma_set = nouveau_fbcon_gamma_set,
 	.gamma_get = nouveau_fbcon_gamma_get,
 	.gamma_get = nouveau_fbcon_gamma_get,
 	.fb_probe = nouveau_fbcon_create,
 	.fb_probe = nouveau_fbcon_create,
@@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
 
 
 	fbcon->dev = dev;
 	fbcon->dev = dev;
 	drm->fbcon = fbcon;
 	drm->fbcon = fbcon;
-	fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
+
+	drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
 
 
 	ret = drm_fb_helper_init(dev, &fbcon->helper,
 	ret = drm_fb_helper_init(dev, &fbcon->helper,
 				 dev->mode_config.num_crtc, 4);
 				 dev->mode_config.num_crtc, 4);

+ 2 - 2
drivers/gpu/drm/nouveau/nouveau_gem.c

@@ -61,7 +61,7 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
 	if (!cli->base.vm)
 	if (!cli->base.vm)
 		return 0;
 		return 0;
 
 
-	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
 
 
@@ -132,7 +132,7 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
 	if (!cli->base.vm)
 	if (!cli->base.vm)
 		return;
 		return;
 
 
-	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
 	if (ret)
 	if (ret)
 		return;
 		return;
 
 

+ 3 - 0
drivers/gpu/drm/nouveau/nouveau_ttm.c

@@ -76,6 +76,7 @@ static int
 nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
 nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
 			 struct ttm_buffer_object *bo,
 			 struct ttm_buffer_object *bo,
 			 struct ttm_placement *placement,
 			 struct ttm_placement *placement,
+			 uint32_t flags,
 			 struct ttm_mem_reg *mem)
 			 struct ttm_mem_reg *mem)
 {
 {
 	struct nouveau_drm *drm = nouveau_bdev(man->bdev);
 	struct nouveau_drm *drm = nouveau_bdev(man->bdev);
@@ -162,6 +163,7 @@ static int
 nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
 nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
 			 struct ttm_buffer_object *bo,
 			 struct ttm_buffer_object *bo,
 			 struct ttm_placement *placement,
 			 struct ttm_placement *placement,
+			 uint32_t flags,
 			 struct ttm_mem_reg *mem)
 			 struct ttm_mem_reg *mem)
 {
 {
 	struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
 	struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
@@ -242,6 +244,7 @@ static int
 nv04_gart_manager_new(struct ttm_mem_type_manager *man,
 nv04_gart_manager_new(struct ttm_mem_type_manager *man,
 		      struct ttm_buffer_object *bo,
 		      struct ttm_buffer_object *bo,
 		      struct ttm_placement *placement,
 		      struct ttm_placement *placement,
+		      uint32_t flags,
 		      struct ttm_mem_reg *mem)
 		      struct ttm_mem_reg *mem)
 {
 {
 	struct nouveau_mem *node;
 	struct nouveau_mem *node;

+ 2 - 2
drivers/gpu/drm/omapdrm/omap_connector.c

@@ -130,7 +130,7 @@ static void omap_connector_destroy(struct drm_connector *connector)
 	struct omap_dss_device *dssdev = omap_connector->dssdev;
 	struct omap_dss_device *dssdev = omap_connector->dssdev;
 
 
 	DBG("%s", omap_connector->dssdev->name);
 	DBG("%s", omap_connector->dssdev->name);
-	drm_sysfs_connector_remove(connector);
+	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
 	drm_connector_cleanup(connector);
 	kfree(omap_connector);
 	kfree(omap_connector);
 
 
@@ -307,7 +307,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
 	connector->interlace_allowed = 1;
 	connector->interlace_allowed = 1;
 	connector->doublescan_allowed = 0;
 	connector->doublescan_allowed = 0;
 
 
-	drm_sysfs_connector_add(connector);
+	drm_connector_register(connector);
 
 
 	return connector;
 	return connector;
 
 

+ 4 - 2
drivers/gpu/drm/omapdrm/omap_dmm_tiler.c

@@ -199,7 +199,7 @@ static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm)
 static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
 static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
 		struct page **pages, uint32_t npages, uint32_t roll)
 		struct page **pages, uint32_t npages, uint32_t roll)
 {
 {
-	dma_addr_t pat_pa = 0;
+	dma_addr_t pat_pa = 0, data_pa = 0;
 	uint32_t *data;
 	uint32_t *data;
 	struct pat *pat;
 	struct pat *pat;
 	struct refill_engine *engine = txn->engine_handle;
 	struct refill_engine *engine = txn->engine_handle;
@@ -223,7 +223,9 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
 			.lut_id = engine->tcm->lut_id,
 			.lut_id = engine->tcm->lut_id,
 		};
 		};
 
 
-	data = alloc_dma(txn, 4*i, &pat->data_pa);
+	data = alloc_dma(txn, 4*i, &data_pa);
+	/* FIXME: what if data_pa is more than 32-bit ? */
+	pat->data_pa = data_pa;
 
 
 	while (i--) {
 	while (i--) {
 		int n = i + roll;
 		int n = i + roll;

+ 2 - 2
drivers/gpu/drm/omapdrm/omap_fbdev.c

@@ -281,7 +281,7 @@ fail:
 	return ret;
 	return ret;
 }
 }
 
 
-static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
 	.fb_probe = omap_fbdev_create,
 	.fb_probe = omap_fbdev_create,
 };
 };
 
 
@@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
 
 
 	helper = &fbdev->base;
 	helper = &fbdev->base;
 
 
-	helper->funcs = &omap_fb_helper_funcs;
+	drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
 
 
 	ret = drm_fb_helper_init(dev, helper,
 	ret = drm_fb_helper_init(dev, helper,
 			priv->num_crtcs, priv->num_connectors);
 			priv->num_crtcs, priv->num_connectors);

+ 15 - 18
drivers/gpu/drm/omapdrm/omap_gem.c

@@ -233,11 +233,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
 
 
 	WARN_ON(omap_obj->pages);
 	WARN_ON(omap_obj->pages);
 
 
-	/* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the
-	 * mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably
-	 * we actually want CMA memory for it all anyways..
-	 */
-	pages = drm_gem_get_pages(obj, GFP_KERNEL);
+	pages = drm_gem_get_pages(obj);
 	if (IS_ERR(pages)) {
 	if (IS_ERR(pages)) {
 		dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages));
 		dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages));
 		return PTR_ERR(pages);
 		return PTR_ERR(pages);
@@ -791,7 +787,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj,
 			omap_obj->paddr = tiler_ssptr(block);
 			omap_obj->paddr = tiler_ssptr(block);
 			omap_obj->block = block;
 			omap_obj->block = block;
 
 
-			DBG("got paddr: %08x", omap_obj->paddr);
+			DBG("got paddr: %pad", &omap_obj->paddr);
 		}
 		}
 
 
 		omap_obj->paddr_cnt++;
 		omap_obj->paddr_cnt++;
@@ -985,9 +981,9 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
 
 
 	off = drm_vma_node_start(&obj->vma_node);
 	off = drm_vma_node_start(&obj->vma_node);
 
 
-	seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
+	seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d",
 			omap_obj->flags, obj->name, obj->refcount.refcount.counter,
 			omap_obj->flags, obj->name, obj->refcount.refcount.counter,
-			off, omap_obj->paddr, omap_obj->paddr_cnt,
+			off, &omap_obj->paddr, omap_obj->paddr_cnt,
 			omap_obj->vaddr, omap_obj->roll);
 			omap_obj->vaddr, omap_obj->roll);
 
 
 	if (omap_obj->flags & OMAP_BO_TILED) {
 	if (omap_obj->flags & OMAP_BO_TILED) {
@@ -1183,9 +1179,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
 			}
 			}
 		}
 		}
 		spin_unlock(&sync_lock);
 		spin_unlock(&sync_lock);
-
-		if (waiter)
-			kfree(waiter);
+		kfree(waiter);
 	}
 	}
 	return ret;
 	return ret;
 }
 }
@@ -1347,6 +1341,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
 	struct omap_drm_private *priv = dev->dev_private;
 	struct omap_drm_private *priv = dev->dev_private;
 	struct omap_gem_object *omap_obj;
 	struct omap_gem_object *omap_obj;
 	struct drm_gem_object *obj = NULL;
 	struct drm_gem_object *obj = NULL;
+	struct address_space *mapping;
 	size_t size;
 	size_t size;
 	int ret;
 	int ret;
 
 
@@ -1404,14 +1399,16 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
 		omap_obj->height = gsize.tiled.height;
 		omap_obj->height = gsize.tiled.height;
 	}
 	}
 
 
-	ret = 0;
-	if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM))
+	if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
 		drm_gem_private_object_init(dev, obj, size);
 		drm_gem_private_object_init(dev, obj, size);
-	else
+	} else {
 		ret = drm_gem_object_init(dev, obj, size);
 		ret = drm_gem_object_init(dev, obj, size);
+		if (ret)
+			goto fail;
 
 
-	if (ret)
-		goto fail;
+		mapping = file_inode(obj->filp)->i_mapping;
+		mapping_set_gfp_mask(mapping, GFP_USER | __GFP_DMA32);
+	}
 
 
 	return obj;
 	return obj;
 
 
@@ -1467,8 +1464,8 @@ void omap_gem_init(struct drm_device *dev)
 			entry->paddr = tiler_ssptr(block);
 			entry->paddr = tiler_ssptr(block);
 			entry->block = block;
 			entry->block = block;
 
 
-			DBG("%d:%d: %dx%d: paddr=%08x stride=%d", i, j, w, h,
-					entry->paddr,
+			DBG("%d:%d: %dx%d: paddr=%pad stride=%d", i, j, w, h,
+					&entry->paddr,
 					usergart[i].stride_pfn << PAGE_SHIFT);
 					usergart[i].stride_pfn << PAGE_SHIFT);
 		}
 		}
 	}
 	}

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