diff --git a/Documentation/devicetree/bindings/display/imx/ldb.txt b/Documentation/devicetree/bindings/display/imx/ldb.txt
index 38c637fa39ddf4efaef296a3878859dfb598e82c..2c3a5ac54250396dd0c19c02ddc362ecc2d9b013 100644
--- a/Documentation/devicetree/bindings/display/imx/ldb.txt
+++ b/Documentation/devicetree/bindings/display/imx/ldb.txt
@@ -9,10 +9,14 @@ nodes describing each of the two LVDS encoder channels of the bridge.
 Required properties:
  - #address-cells : should be <1>
  - #size-cells : should be <0>
- - compatible : should be "fsl,imx53-ldb" or "fsl,imx6q-ldb".
-                Both LDB versions are similar, but i.MX6 has an additional
-                multiplexer in the front to select any of the four IPU display
-                interfaces as input for each LVDS channel.
+ - compatible : should be "fsl,imx53-ldb" or "fsl,imx6q-ldb" or
+                "fsl,imx8qm-ldb".
+                All LDB versions are similar.
+                i.MX6q/dl has an additional multiplexer in the front to select
+                any of the two or four IPU display interfaces as input for each
+                LVDS channel.
+                i.MX8qm LDB supports 10bit RGB input and needs an additional
+                phy.
  - gpr : should be <&gpr> on i.MX53 and i.MX6q.
          The phandle points to the iomuxc-gpr region containing the LVDS
          control register.
@@ -29,14 +33,18 @@ Required properties:
         On i.MX6q the following additional clocks are needed:
                 "di2_sel" - IPU2 DI0 mux
                 "di3_sel" - IPU2 DI1 mux
+        The following clocks are expected on i.MX8qm:
+                "pixel" - pixel clock
+                "bypass" - bypass clock
         The needed clock numbers for each are documented in
         Documentation/devicetree/bindings/clock/imx5-clock.txt, and in
         Documentation/devicetree/bindings/clock/imx6q-clock.txt.
+- power-domains : phandle pointing to power domain, only required by i.MX8qm.
 
 Optional properties:
- - pinctrl-names : should be "default" on i.MX53, not used on i.MX6q
+ - pinctrl-names : should be "default" on i.MX53, not used on i.MX6q and i.MX8qm
  - pinctrl-0 : a phandle pointing to LVDS pin settings on i.MX53,
-               not used on i.MX6q
+               not used on i.MX6q and i.MX8qm
  - fsl,dual-channel : boolean. if it exists, only LVDS channel 0 should
    be configured - one input will be distributed on both outputs in dual
    channel mode
@@ -57,9 +65,13 @@ Required properties:
    (lvds-channel@[0,1], respectively).
    On i.MX6, there should be four input ports (port@[0-3]) that correspond
    to the four LVDS multiplexer inputs.
-   A single output port (port@2 on i.MX5, port@4 on i.MX6) must be connected
-   to a panel input port. Optionally, the output port can be left out if
-   display-timings are used instead.
+   On i.MX8qm, the two channels of LDB connect to one display interface of DPU.
+   A single output port (port@2 on i.MX5, port@4 on i.MX6, port@1 on i.MX8qm)
+   must be connected to a panel input port or a bridge input port.
+   Optionally, the output port can be left out if display-timings are used
+   instead.
+ - phys: the phandle for the LVDS PHY device. Valid only on i.MX8qm.
+ - phy-names: should be "ldb_phy". Valid only on i.MX8qm.
 
 Optional properties (required if display-timings are used):
  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
@@ -69,6 +81,7 @@ Optional properties (required if display-timings are used):
                       This describes how the color bits are laid out in the
                       serialized LVDS signal.
  - fsl,data-width : should be <18> or <24>
+                    Additionally, <30> for i.MX8qm.
 
 example:
 
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index e4c18ef08d347d5fb1416497fed84b4d51ef448f..b94628b524e59d4142b88ba71638e36158128a08 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -28,7 +28,7 @@ config DRM_IMX_LDB
 	select DRM_PANEL
 	help
 	  Choose this to enable the internal LVDS Display Bridge (LDB)
-	  found on i.MX53 and i.MX6 processors.
+	  found on i.MX53, i.MX6 and i.MX8 processors.
 
 config DRM_IMX_IPUV3
 	tristate
diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
index b3c4b5f2b8b9198885d878cb99d46dbaffcd6b11..7d183b6e651bf40a2e0c40a0c57f5dd2d5c17b1f 100644
--- a/drivers/gpu/drm/imx/imx-ldb.c
+++ b/drivers/gpu/drm/imx/imx-ldb.c
@@ -29,6 +29,8 @@
 #include <linux/of_graph.h>
 #include <video/of_display_timing.h>
 #include <video/of_videomode.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mixel-lvds.h>
 #include <linux/regmap.h>
 #include <linux/videodev2.h>
 
@@ -50,6 +52,12 @@
 #define LDB_DI0_VS_POL_ACT_LOW		(1 << 9)
 #define LDB_DI1_VS_POL_ACT_LOW		(1 << 10)
 #define LDB_BGREF_RMODE_INT		(1 << 15)
+#define LDB_CH0_10BIT_EN		(1 << 22)
+#define LDB_CH1_10BIT_EN		(1 << 23)
+#define LDB_CH0_DATA_WIDTH_24BIT	(1 << 24)
+#define LDB_CH1_DATA_WIDTH_24BIT	(1 << 26)
+#define LDB_CH0_DATA_WIDTH_30BIT	(2 << 24)
+#define LDB_CH1_DATA_WIDTH_30BIT	(2 << 26)
 
 struct imx_ldb;
 
@@ -62,6 +70,9 @@ struct imx_ldb_channel {
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 
+	struct phy *phy;
+	bool phy_is_on;
+
 	struct device_node *child;
 	struct i2c_adapter *ddc;
 	int chno;
@@ -89,6 +100,21 @@ struct bus_mux {
 	int mask;
 };
 
+struct devtype {
+	int ctrl_reg;
+	struct bus_mux *bus_mux;
+	bool capable_10bit;
+	bool visible_phy;
+	bool has_mux;
+	bool is_imx8;
+	bool use_mixel_phy;
+	bool padding_quirks;
+
+	/* pixel rate in KHz */
+	unsigned int max_prate_single_mode;
+	unsigned int max_prate_dual_mode;
+};
+
 struct imx_ldb {
 	struct regmap *regmap;
 	struct device *dev;
@@ -97,8 +123,21 @@ struct imx_ldb {
 	struct clk *clk_sel[4]; /* parent of display clock */
 	struct clk *clk_parent[4]; /* original parent of clk_sel */
 	struct clk *clk_pll[2]; /* upstream clock we can adjust */
+	struct clk *clk_pixel;
+	struct clk *clk_bypass;
+	u32 ldb_ctrl_reg;
 	u32 ldb_ctrl;
 	const struct bus_mux *lvds_mux;
+	bool capable_10bit;
+	bool visible_phy;
+	bool has_mux;
+	bool is_imx8;
+	bool use_mixel_phy;
+	bool padding_quirks;
+
+	/* pixel rate in KHz */
+	unsigned int max_prate_single_mode;
+	unsigned int max_prate_dual_mode;
 };
 
 static void imx_ldb_ch_set_bus_format(struct imx_ldb_channel *imx_ldb_ch,
@@ -112,16 +151,38 @@ static void imx_ldb_ch_set_bus_format(struct imx_ldb_channel *imx_ldb_ch,
 		break;
 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
 		if (imx_ldb_ch->chno == 0 || dual)
-			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
+			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
+					 LDB_CH0_DATA_WIDTH_24BIT;
 		if (imx_ldb_ch->chno == 1 || dual)
-			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
+			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
+					 LDB_CH1_DATA_WIDTH_24BIT;
 		break;
 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
 		if (imx_ldb_ch->chno == 0 || dual)
 			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
+					 LDB_CH0_DATA_WIDTH_24BIT |
 					 LDB_BIT_MAP_CH0_JEIDA;
 		if (imx_ldb_ch->chno == 1 || dual)
 			ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
+					 LDB_CH1_DATA_WIDTH_24BIT |
+					 LDB_BIT_MAP_CH1_JEIDA;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG:
+		if (imx_ldb_ch->chno == 0 || dual)
+			ldb->ldb_ctrl |= LDB_CH0_10BIT_EN |
+					 LDB_CH0_DATA_WIDTH_30BIT;
+		if (imx_ldb_ch->chno == 1 || dual)
+			ldb->ldb_ctrl |= LDB_CH1_10BIT_EN |
+					 LDB_CH1_DATA_WIDTH_30BIT;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA:
+		if (imx_ldb_ch->chno == 0 || dual)
+			ldb->ldb_ctrl |= LDB_CH0_10BIT_EN |
+					 LDB_CH0_DATA_WIDTH_30BIT |
+					 LDB_BIT_MAP_CH0_JEIDA;
+		if (imx_ldb_ch->chno == 1 || dual)
+			ldb->ldb_ctrl |= LDB_CH1_10BIT_EN |
+					 LDB_CH1_DATA_WIDTH_30BIT |
 					 LDB_BIT_MAP_CH1_JEIDA;
 		break;
 	}
@@ -176,6 +237,12 @@ static void imx_ldb_set_clock(struct imx_ldb *ldb, int mux, int chno,
 {
 	int ret;
 
+	if (ldb->is_imx8) {
+		clk_set_rate(ldb->clk_bypass, di_clk);
+		clk_set_rate(ldb->clk_pixel, di_clk);
+		return;
+	}
+
 	dev_dbg(ldb->dev, "%s: now: %ld want: %ld\n", __func__,
 			clk_get_rate(ldb->clk_pll[chno]), serial_clk);
 	clk_set_rate(ldb->clk_pll[chno], serial_clk);
@@ -208,14 +275,22 @@ static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
 
 	drm_panel_prepare(imx_ldb_ch->panel);
 
-	if (dual) {
-		clk_set_parent(ldb->clk_sel[mux], ldb->clk[0]);
-		clk_set_parent(ldb->clk_sel[mux], ldb->clk[1]);
+	if (ldb->is_imx8) {
+		clk_prepare_enable(ldb->clk_pixel);
+		clk_prepare_enable(ldb->clk_bypass);
+	}
 
-		clk_prepare_enable(ldb->clk[0]);
-		clk_prepare_enable(ldb->clk[1]);
-	} else {
-		clk_set_parent(ldb->clk_sel[mux], ldb->clk[imx_ldb_ch->chno]);
+	if (ldb->has_mux) {
+		if (dual) {
+			clk_set_parent(ldb->clk_sel[mux], ldb->clk[0]);
+			clk_set_parent(ldb->clk_sel[mux], ldb->clk[1]);
+
+			clk_prepare_enable(ldb->clk[0]);
+			clk_prepare_enable(ldb->clk[1]);
+		} else {
+			clk_set_parent(ldb->clk_sel[mux],
+				       ldb->clk[imx_ldb_ch->chno]);
+		}
 	}
 
 	if (imx_ldb_ch == &ldb->channel[0] || dual) {
@@ -245,7 +320,19 @@ static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
 				   mux << lvds_mux->shift);
 	}
 
-	regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
+	regmap_write(ldb->regmap, ldb->ldb_ctrl_reg, ldb->ldb_ctrl);
+
+	if (dual) {
+		phy_power_on(ldb->channel[0].phy);
+		phy_power_on(ldb->channel[1].phy);
+
+		ldb->channel[0].phy_is_on = true;
+		ldb->channel[1].phy_is_on = true;
+	} else {
+		phy_power_on(imx_ldb_ch->phy);
+
+		imx_ldb_ch->phy_is_on = true;
+	}
 
 	drm_panel_enable(imx_ldb_ch->panel);
 }
@@ -264,23 +351,35 @@ imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder,
 	int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder);
 	u32 bus_format = imx_ldb_ch->bus_format;
 
-	if (mode->clock > 170000) {
+	if (mode->clock > ldb->max_prate_dual_mode) {
 		dev_warn(ldb->dev,
-			 "%s: mode exceeds 170 MHz pixel clock\n", __func__);
+			 "%s: mode exceeds %u MHz pixel clock\n", __func__,
+			 ldb->max_prate_dual_mode / 1000);
 	}
-	if (mode->clock > 85000 && !dual) {
+	if (mode->clock > ldb->max_prate_single_mode && !dual) {
 		dev_warn(ldb->dev,
-			 "%s: mode exceeds 85 MHz pixel clock\n", __func__);
+			 "%s: mode exceeds %u MHz pixel clock\n", __func__,
+			 ldb->max_prate_single_mode / 1000);
 	}
 
 	if (dual) {
 		serial_clk = 3500UL * mode->clock;
 		imx_ldb_set_clock(ldb, mux, 0, serial_clk, di_clk);
 		imx_ldb_set_clock(ldb, mux, 1, serial_clk, di_clk);
+
+		if (ldb->use_mixel_phy) {
+			mixel_phy_lvds_set_phy_speed(ldb->channel[0].phy,
+						     di_clk / 2);
+			mixel_phy_lvds_set_phy_speed(ldb->channel[1].phy,
+						     di_clk / 2);
+		}
 	} else {
 		serial_clk = 7000UL * mode->clock;
 		imx_ldb_set_clock(ldb, mux, imx_ldb_ch->chno, serial_clk,
 				  di_clk);
+
+		if (ldb->use_mixel_phy)
+			mixel_phy_lvds_set_phy_speed(imx_ldb_ch->phy, di_clk);
 	}
 
 	/* FIXME - assumes straight connections DI0 --> CH0, DI1 --> CH1 */
@@ -297,6 +396,52 @@ imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder,
 			ldb->ldb_ctrl &= ~LDB_DI1_VS_POL_ACT_LOW;
 	}
 
+	if (dual) {
+		if (ldb->use_mixel_phy) {
+			/* VSYNC */
+			if (mode->flags & DRM_MODE_FLAG_NVSYNC) {
+				mixel_phy_lvds_set_vsync_pol(
+					ldb->channel[0].phy, false);
+				mixel_phy_lvds_set_vsync_pol(
+					ldb->channel[1].phy, false);
+			} else if (mode->flags & DRM_MODE_FLAG_PVSYNC) {
+				mixel_phy_lvds_set_vsync_pol(
+					ldb->channel[0].phy, true);
+				mixel_phy_lvds_set_vsync_pol(
+					ldb->channel[1].phy, true);
+			}
+			/* HSYNC */
+			if (mode->flags & DRM_MODE_FLAG_NHSYNC) {
+				mixel_phy_lvds_set_hsync_pol(
+					ldb->channel[0].phy, false);
+				mixel_phy_lvds_set_hsync_pol(
+					ldb->channel[1].phy, false);
+			} else if (mode->flags & DRM_MODE_FLAG_PHSYNC) {
+				mixel_phy_lvds_set_hsync_pol(
+					ldb->channel[0].phy, true);
+				mixel_phy_lvds_set_hsync_pol(
+					ldb->channel[1].phy, true);
+			}
+		}
+	} else {
+		if (ldb->use_mixel_phy) {
+			/* VSYNC */
+			if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+				mixel_phy_lvds_set_vsync_pol(imx_ldb_ch->phy,
+								false);
+			else if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+				mixel_phy_lvds_set_vsync_pol(imx_ldb_ch->phy,
+								true);
+			/* HSYNC */
+			if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+				mixel_phy_lvds_set_hsync_pol(imx_ldb_ch->phy,
+								false);
+			else if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+				mixel_phy_lvds_set_hsync_pol(imx_ldb_ch->phy,
+								true);
+		}
+	}
+
 	if (!bus_format) {
 		struct drm_connector *connector = connector_state->connector;
 		struct drm_display_info *di = &connector->display_info;
@@ -311,22 +456,43 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
 {
 	struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
 	struct imx_ldb *ldb = imx_ldb_ch->ldb;
+	int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
 	int mux, ret;
 
 	drm_panel_disable(imx_ldb_ch->panel);
 
+	if (dual) {
+		phy_power_off(ldb->channel[0].phy);
+		phy_power_off(ldb->channel[1].phy);
+
+		ldb->channel[0].phy_is_on = false;
+		ldb->channel[1].phy_is_on = false;
+	} else {
+		phy_power_off(imx_ldb_ch->phy);
+
+		imx_ldb_ch->phy_is_on = false;
+	}
+
 	if (imx_ldb_ch == &ldb->channel[0])
 		ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
 	else if (imx_ldb_ch == &ldb->channel[1])
 		ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
 
-	regmap_write(ldb->regmap, IOMUXC_GPR2, ldb->ldb_ctrl);
+	regmap_write(ldb->regmap, ldb->ldb_ctrl_reg, ldb->ldb_ctrl);
 
-	if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
-		clk_disable_unprepare(ldb->clk[0]);
-		clk_disable_unprepare(ldb->clk[1]);
+	if (ldb->is_imx8) {
+		clk_disable_unprepare(ldb->clk_bypass);
+		clk_disable_unprepare(ldb->clk_pixel);
+	} else {
+		if (ldb->ldb_ctrl & LDB_SPLIT_MODE_EN) {
+			clk_disable_unprepare(ldb->clk[0]);
+			clk_disable_unprepare(ldb->clk[1]);
+		}
 	}
 
+	if (!ldb->has_mux)
+		goto unprepare_panel;
+
 	if (ldb->lvds_mux) {
 		const struct bus_mux *lvds_mux = NULL;
 
@@ -349,6 +515,7 @@ static void imx_ldb_encoder_disable(struct drm_encoder *encoder)
 			"unable to set di%d parent clock to original parent\n",
 			mux);
 
+unprepare_panel:
 	drm_panel_unprepare(imx_ldb_ch->panel);
 }
 
@@ -358,6 +525,7 @@ static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
 {
 	struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state);
 	struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
+	struct imx_ldb *ldb = imx_ldb_ch->ldb;
 	struct drm_display_info *di = &conn_state->connector->display_info;
 	u32 bus_format = imx_ldb_ch->bus_format;
 
@@ -371,11 +539,23 @@ static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
 	}
 	switch (bus_format) {
 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
-		imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
+		if (ldb->padding_quirks)
+			imx_crtc_state->bus_format =
+					MEDIA_BUS_FMT_RGB666_1X30_PADLO;
+		else
+			imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
 		break;
 	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
-		imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+		if (ldb->padding_quirks)
+			imx_crtc_state->bus_format =
+					MEDIA_BUS_FMT_RGB888_1X30_PADLO;
+		else
+			imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+		break;
+	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG:
+	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA:
+		imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB101010_1X30;
 		break;
 	default:
 		return -EINVAL;
@@ -416,6 +596,9 @@ static int imx_ldb_get_clk(struct imx_ldb *ldb, int chno)
 {
 	char clkname[16];
 
+	if (ldb->is_imx8)
+		return 0;
+
 	snprintf(clkname, sizeof(clkname), "di%d", chno);
 	ldb->clk[chno] = devm_clk_get(ldb->dev, clkname);
 	if (IS_ERR(ldb->clk[chno]))
@@ -496,12 +679,15 @@ struct imx_ldb_bit_mapping {
 };
 
 static const struct imx_ldb_bit_mapping imx_ldb_bit_mappings[] = {
-	{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,  18, "spwg" },
-	{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,  24, "spwg" },
-	{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, "jeida" },
+	{ MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,     18, "spwg" },
+	{ MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,     24, "spwg" },
+	{ MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,    24, "jeida" },
+	{ MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG,  30, "spwg" },
+	{ MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA, 30, "jeida" },
 };
 
-static u32 of_get_bus_format(struct device *dev, struct device_node *np)
+static u32 of_get_bus_format(struct device *dev, struct imx_ldb *ldb,
+			     struct device_node *np)
 {
 	const char *bm;
 	u32 datawidth = 0;
@@ -513,6 +699,11 @@ static u32 of_get_bus_format(struct device *dev, struct device_node *np)
 
 	of_property_read_u32(np, "fsl,data-width", &datawidth);
 
+	if (!ldb->capable_10bit && datawidth == 30) {
+		dev_err(dev, "invalid data width: %d-bit\n", datawidth);
+		return -ENOENT;
+	}
+
 	for (i = 0; i < ARRAY_SIZE(imx_ldb_bit_mappings); i++) {
 		if (!strcasecmp(bm, imx_ldb_bit_mappings[i].mapping) &&
 		    datawidth == imx_ldb_bit_mappings[i].datawidth)
@@ -524,6 +715,16 @@ static u32 of_get_bus_format(struct device *dev, struct device_node *np)
 	return -ENOENT;
 }
 
+static struct devtype imx53_ldb_devtype = {
+	.ctrl_reg = IOMUXC_GPR2,
+	.bus_mux = NULL,
+	.capable_10bit = false,
+	.visible_phy = false,
+	.has_mux = true,
+	.max_prate_single_mode = 85000,
+	.max_prate_dual_mode = 150000,
+};
+
 static struct bus_mux imx6q_lvds_mux[2] = {
 	{
 		.reg = IOMUXC_GPR3,
@@ -536,15 +737,39 @@ static struct bus_mux imx6q_lvds_mux[2] = {
 	}
 };
 
+static struct devtype imx6q_ldb_devtype = {
+	.ctrl_reg = IOMUXC_GPR2,
+	.bus_mux = imx6q_lvds_mux,
+	.capable_10bit = false,
+	.visible_phy = false,
+	.has_mux = true,
+	.max_prate_single_mode = 85000,
+	.max_prate_dual_mode = 170000,
+};
+
+static struct devtype imx8qm_ldb_devtype = {
+	.ctrl_reg = 0x10e0,
+	.bus_mux = NULL,
+	.capable_10bit = true,
+	.visible_phy = true,
+	.is_imx8 = true,
+	.use_mixel_phy = true,
+	.padding_quirks = true,
+	.max_prate_single_mode = 150000,
+	.max_prate_dual_mode = 300000,
+};
+
 /*
- * For a device declaring compatible = "fsl,imx6q-ldb", "fsl,imx53-ldb",
- * of_match_device will walk through this list and take the first entry
- * matching any of its compatible values. Therefore, the more generic
- * entries (in this case fsl,imx53-ldb) need to be ordered last.
+ * For a device declaring compatible = "fsl,imx8qm-ldb", "fsl,imx6q-ldb",
+ * "fsl,imx53-ldb", of_match_device will walk through this list and take the
+ * first entry matching any of its compatible values.
+ * Therefore, the more generic entries (in this case fsl,imx53-ldb) need
+ * to be ordered last.
  */
 static const struct of_device_id imx_ldb_dt_ids[] = {
-	{ .compatible = "fsl,imx6q-ldb", .data = imx6q_lvds_mux, },
-	{ .compatible = "fsl,imx53-ldb", .data = NULL, },
+	{ .compatible = "fsl,imx8qm-ldb", .data = &imx8qm_ldb_devtype, },
+	{ .compatible = "fsl,imx6q-ldb", .data = &imx6q_ldb_devtype, },
+	{ .compatible = "fsl,imx53-ldb", .data = &imx53_ldb_devtype, },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
@@ -595,6 +820,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 	struct device_node *np = dev->of_node;
 	const struct of_device_id *of_id =
 			of_match_device(imx_ldb_dt_ids, dev);
+	const struct devtype *devtype = of_id->data;
 	struct device_node *child;
 	struct imx_ldb *imx_ldb;
 	int dual;
@@ -612,40 +838,64 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 	}
 
 	imx_ldb->dev = dev;
-
-	if (of_id)
-		imx_ldb->lvds_mux = of_id->data;
+	imx_ldb->ldb_ctrl_reg = devtype->ctrl_reg;
+	imx_ldb->lvds_mux = devtype->bus_mux;
+	imx_ldb->capable_10bit = devtype->capable_10bit;
+	imx_ldb->visible_phy = devtype->visible_phy;
+	imx_ldb->has_mux = devtype->has_mux;
+	imx_ldb->is_imx8 = devtype->is_imx8;
+	imx_ldb->use_mixel_phy = devtype->use_mixel_phy;
+	imx_ldb->padding_quirks = devtype->padding_quirks;
+	imx_ldb->max_prate_single_mode = devtype->max_prate_single_mode;
+	imx_ldb->max_prate_dual_mode = devtype->max_prate_dual_mode;
 
 	dual = of_property_read_bool(np, "fsl,dual-channel");
 	if (dual)
 		imx_ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN;
 
-	/*
-	 * There are three different possible clock mux configurations:
-	 * i.MX53:  ipu1_di0_sel, ipu1_di1_sel
-	 * i.MX6q:  ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel, ipu2_di1_sel
-	 * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel
-	 * Map them all to di0_sel...di3_sel.
-	 */
-	for (i = 0; i < 4; i++) {
-		char clkname[16];
-
-		sprintf(clkname, "di%d_sel", i);
-		imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev, clkname);
-		if (IS_ERR(imx_ldb->clk_sel[i])) {
-			ret = PTR_ERR(imx_ldb->clk_sel[i]);
-			imx_ldb->clk_sel[i] = NULL;
-			break;
-		}
+	if (imx_ldb->is_imx8) {
+		imx_ldb->clk_pixel = devm_clk_get(imx_ldb->dev, "pixel");
+		if (IS_ERR(imx_ldb->clk_pixel))
+			return PTR_ERR(imx_ldb->clk_pixel);
 
-		imx_ldb->clk_parent[i] = clk_get_parent(imx_ldb->clk_sel[i]);
+		imx_ldb->clk_bypass = devm_clk_get(imx_ldb->dev, "bypass");
+		if (IS_ERR(imx_ldb->clk_bypass))
+			return PTR_ERR(imx_ldb->clk_bypass);
+	}
+
+	if (imx_ldb->has_mux) {
+		/*
+		 * There are three different possible clock mux configurations:
+		 * i.MX53:  ipu1_di0_sel, ipu1_di1_sel
+		 * i.MX6q:  ipu1_di0_sel, ipu1_di1_sel, ipu2_di0_sel,
+		 *          ipu2_di1_sel
+		 * i.MX6dl: ipu1_di0_sel, ipu1_di1_sel, lcdif_sel
+		 * Map them all to di0_sel...di3_sel.
+		 */
+		for (i = 0; i < 4; i++) {
+			char clkname[16];
+
+			sprintf(clkname, "di%d_sel", i);
+			imx_ldb->clk_sel[i] = devm_clk_get(imx_ldb->dev,
+								clkname);
+			if (IS_ERR(imx_ldb->clk_sel[i])) {
+				ret = PTR_ERR(imx_ldb->clk_sel[i]);
+				imx_ldb->clk_sel[i] = NULL;
+				break;
+			}
+
+			imx_ldb->clk_parent[i] =
+					clk_get_parent(imx_ldb->clk_sel[i]);
+		}
+		if (i == 0)
+			return ret;
 	}
-	if (i == 0)
-		return ret;
 
 	for_each_child_of_node(np, child) {
 		struct imx_ldb_channel *channel;
 		int bus_format;
+		int port_reg;
+		bool auxiliary_ch = false;
 
 		ret = of_property_read_u32(child, "reg", &i);
 		if (ret || i < 0 || i > 1) {
@@ -653,6 +903,12 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 			goto free_child;
 		}
 
+		if (dual && imx_ldb->use_mixel_phy && i > 0) {
+			auxiliary_ch = true;
+			channel = &imx_ldb->channel[i];
+			goto get_phy;
+		}
+
 		if (!of_device_is_available(child))
 			continue;
 
@@ -667,10 +923,15 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 
 		/*
 		 * The output port is port@4 with an external 4-port mux or
-		 * port@2 with the internal 2-port mux.
+		 * port@2 with the internal 2-port mux or port@1 without mux.
 		 */
+		if (imx_ldb->has_mux)
+			port_reg = imx_ldb->lvds_mux ? 4 : 2;
+		else
+			port_reg = 1;
+
 		ret = drm_of_find_panel_or_bridge(child,
-						  imx_ldb->lvds_mux ? 4 : 2, 0,
+						  port_reg, 0,
 						  &channel->panel, &channel->bridge);
 		if (ret && ret != -ENODEV)
 			goto free_child;
@@ -682,7 +943,7 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 				goto free_child;
 		}
 
-		bus_format = of_get_bus_format(dev, child);
+		bus_format = of_get_bus_format(dev, imx_ldb, child);
 		if (bus_format == -EINVAL) {
 			/*
 			 * If no bus format was specified in the device tree,
@@ -701,6 +962,33 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 		channel->bus_format = bus_format;
 		channel->child = child;
 
+get_phy:
+		if (imx_ldb->visible_phy) {
+			channel->phy = devm_of_phy_get(dev, child, "ldb_phy");
+			if (IS_ERR(channel->phy)) {
+				ret = PTR_ERR(channel->phy);
+				if (ret == -EPROBE_DEFER) {
+					return ret;
+				} else {
+					dev_err(dev,
+						"can't get channel%d phy: %d\n",
+							channel->chno, ret);
+					return ret;
+				}
+			}
+
+			ret = phy_init(channel->phy);
+			if (ret < 0) {
+				dev_err(dev,
+					"failed to initialize channel%d phy: %d\n",
+					channel->chno, ret);
+				return ret;
+			}
+
+			if (auxiliary_ch)
+				return 0;
+		}
+
 		ret = imx_ldb_register(drm, channel);
 		if (ret) {
 			channel->child = NULL;
@@ -726,6 +1014,11 @@ static void imx_ldb_unbind(struct device *dev, struct device *master,
 	for (i = 0; i < 2; i++) {
 		struct imx_ldb_channel *channel = &imx_ldb->channel[i];
 
+		if (channel->phy_is_on)
+			phy_power_off(channel->phy);
+
+		phy_exit(channel->phy);
+
 		if (channel->panel)
 			drm_panel_detach(channel->panel);