From fd965c7b7da4a0a21bd7750be8c3851be80a576f Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@st.com>
Date: Tue, 27 Nov 2018 09:39:21 +0100
Subject: [PATCH 18/52] ARM-stm32mp1-r0-rc2-DRM-KMS

---
 .../devicetree/bindings/display/bridge/sii902x.txt |   9 +
 drivers/gpu/drm/bridge/Kconfig                     |   1 +
 drivers/gpu/drm/bridge/sii902x.c                   | 844 +++++++++++++++++++--
 drivers/gpu/drm/drm_modes.c                        |  19 +-
 drivers/gpu/drm/panel/panel-orisetech-otm8009a.c   |  12 +-
 drivers/gpu/drm/panel/panel-raydium-rm68200.c      |  12 +-
 drivers/gpu/drm/stm/drv.c                          |  46 +-
 drivers/gpu/drm/stm/dw_mipi_dsi-stm.c              |  53 ++
 drivers/gpu/drm/stm/ltdc.c                         | 191 +++--
 drivers/gpu/drm/stm/ltdc.h                         |   6 +
 include/uapi/drm/drm_mode.h                        |   6 +
 11 files changed, 1060 insertions(+), 139 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/bridge/sii902x.txt b/Documentation/devicetree/bindings/display/bridge/sii902x.txt
index 72d2dc6..00e9e88 100644
--- a/Documentation/devicetree/bindings/display/bridge/sii902x.txt
+++ b/Documentation/devicetree/bindings/display/bridge/sii902x.txt
@@ -13,6 +13,8 @@ Optional subnodes:
 	- video input: this subnode can contain a video input port node
 	  to connect the bridge to a display controller output (See this
 	  documentation [1]).
+	- audio input: this subnode can contain an audio input port node
+	  to connect the bridge to an audio controller output.
 
 [1]: Documentation/devicetree/bindings/media/video-interfaces.txt
 
@@ -31,5 +33,12 @@ Example:
 					remote-endpoint = <&dc_out>;
 				};
 			};
+
+			port@1 {
+				reg = <1>;
+				codec_endpoint: endpoint {
+					remote-endpoint = <&i2s0_cpu_endpoint>;
+				};
+			};
 		};
 	};
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index bf6cad6..fe91c20 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -95,6 +95,7 @@ config DRM_SII902X
 	depends on OF
 	select DRM_KMS_HELPER
 	select REGMAP_I2C
+	select SND_SOC_HDMI_CODEC if SND_SOC
 	---help---
 	  Silicon Image sii902x bridge chip driver.
 
diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c
index e59a135..512eb03 100644
--- a/drivers/gpu/drm/bridge/sii902x.c
+++ b/drivers/gpu/drm/bridge/sii902x.c
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2018 Renesas Electronics
+ *
  * Copyright (C) 2016 Atmel
  *		      Bo Shen <voice.shen@atmel.com>
  *
@@ -20,16 +22,23 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/bitfield.h>
 #include <linux/gpio/consumer.h>
+#include <linux/i2c-mux.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 
+#include <sound/hdmi-codec.h>
+
 #define SII902X_TPI_VIDEO_DATA			0x0
 
 #define SII902X_TPI_PIXEL_REPETITION		0x8
@@ -71,23 +80,229 @@
 #define SII902X_AVI_POWER_STATE_MSK		GENMASK(1, 0)
 #define SII902X_AVI_POWER_STATE_D(l)		((l) & SII902X_AVI_POWER_STATE_MSK)
 
+#define SII902X_I2S_MAP				0x1f
+#define SII902X_I2S_MAP_SWITCH			BIT(7)
+#define SII902X_I2S_MAP_SD_MSK			GENMASK(5, 4)
+#define SII902X_I2S_MAP_SD(v)			\
+	FIELD_PREP(SII902X_I2S_MAP_SD_MSK, v)
+#define SII902X_I2S_MAP_DS			BIT(3)
+#define SII902X_I2S_MAP_SWAP			BIT(2)
+#define SII902X_I2S_MAP_FIFO_MSK		GENMASK(1, 0)
+#define SII902X_I2S_MAP_FIFO(v)			\
+	FIELD_PREP(SII902X_I2S_MAP_FIFO_MSK, v)
+
+#define SII902X_I2S_CONF			0x20
+#define SII902X_I2S_CONF_SCK_STROBING		BIT(7)
+#define SII902X_I2S_CONF_MCLK_RATIO_MSK		GENMASK(6, 4)
+#define SII902X_I2S_CONF_WS_STROBING		BIT(3)
+#define SII902X_I2S_CONF_JUSTIFY		BIT(2)
+#define SII902X_I2S_CONF_LSB_DIR		BIT(1)
+#define SII902X_I2S_CONF_NO_OFFSET		BIT(0)
+
+#define SII902X_I2S_CS0				0x21
+
+#define SII902X_I2S_CS1				0x22
+
+#define SII902X_I2S_CS2				0x23
+#define SII902X_I2S_CS2_SRC_MSK			GENMASK(3, 0)
+#define SII902X_I2S_CS2_CHAN_MSK		GENMASK(7, 4)
+
+#define SII902X_I2S_CS3				0x24
+#define SII902X_I2S_CS3_FS_MSK			GENMASK(3, 0)
+#define SII902X_I2S_CS3_FS(v)			\
+	FIELD_PREP(SII902X_I2S_CS3_FS_MSK, v)
+#define SII902X_I2S_CS3_ACC_MSK			GENMASK(7, 4)
+
+#define SII902X_I2S_CS4				0x25
+#define SII902X_I2S_CS4_WL_MSK			GENMASK(3, 0)
+#define SII902X_I2S_CS4_WL(v)			\
+	FIELD_PREP(SII902X_I2S_CS4_WL_MSK, v)
+
+#define SII902X_AIF				0x26
+#define SII902X_AIF_FMT_MSK			GENMASK(7, 6)
+#define SII902X_AIF_FMT(v)			\
+	FIELD_PREP(SII902X_AIF_FMT_MSK, v)
+#define SII902X_AIF_LAYOUT			BIT(5)
+#define SII902X_AIF_MUTE			BIT(4)
+#define SII902X_AIF_CODING_MSK			GENMASK(3, 0)
+#define SII902X_AIF_CODING(v)			\
+	FIELD_PREP(SII902X_AIF_CODING_MSK, v)
+
+#define SII902X_I2S_AUD_FMT			0x27
+#define SII902X_I2S_AUD_FMT_SZ_MSK		GENMASK(7, 6)
+#define SII902X_I2S_AUD_FMT_SZ(v)		\
+	FIELD_PREP(SII902X_I2S_AUD_FMT_SZ_MSK, v)
+#define SII902X_I2S_AUD_FMT_FS_MSK		GENMASK(5, 3)
+#define SII902X_I2S_AUD_FMT_FS(v)		\
+	FIELD_PREP(SII902X_I2S_AUD_FMT_FS_MSK, v)
+#define SII902X_I2S_AUD_FMT_FS_HBR		BIT(2)
+
 #define SII902X_INT_ENABLE			0x3c
 #define SII902X_INT_STATUS			0x3d
 #define SII902X_HOTPLUG_EVENT			BIT(0)
 #define SII902X_PLUGGED_STATUS			BIT(2)
 
+#define SII902X_PLL_R1				0x82
+#define SII902X_PLL_R1_TCLKSEL_MSK		GENMASK(6, 5)
+#define SII902X_PLL_R1_TCLKSEL(v)		\
+	FIELD_PREP(SII902X_PLL_R1_TCLKSEL_MSK, v)
+#define SII902X_PLL_R2				0x83
+#define SII902X_PLL_R2_CLKMUTLCTL_MSK		GENMASK(5, 4)
+#define SII902X_PLL_R2_CLKMUTLCTL(v)		\
+	FIELD_PREP(SII902X_PLL_R2_CLKMUTLCTL_MSK, v)
+#define SII902X_PLL_R3				0x84
+#define SII902X_PLL_R3_ACLKCNT_MSK		GENMASK(5, 4)
+#define SII902X_PLL_R3_ACLKCNT(v)		\
+	FIELD_PREP(SII902X_PLL_R3_ACLKCNT_MSK, v)
+#define SII902X_PLL_R3_HLFCLKEN			BIT(1)
+
+#define SII902X_INDEXED_REG_PAGE		0xbc
+#define SII902X_INDEXED_REG_IDX			0xbd
+#define SII902X_INDEXED_REG_ACCESS		0xbe
+
+#define SII902X_OTHER_IF			0xbf
+#define SII902X_OTHER_IF_SEL_MSK		GENMASK(2, 0)
+#define SII902X_OTHER_IF_SEL(v)			\
+	FIELD_PREP(SII902X_OTHER_IF_SEL_MSK, v)
+#define SII902X_OTHER_IF_REPEAT			BIT(6)
+#define SII902X_OTHER_IF_ENABLE			BIT(7)
+
 #define SII902X_REG_TPI_RQB			0xc7
 
 #define SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS	500
 
+#define SII902X_IF_AUDIO			2
+
+/* CEC device */
+#define SII902X_CEC_I2C_ADDR			0x30
+
+#define SII902X_CEC_SETUP			0x8e
+
+enum sii902x_i2s_map_sd {
+	SII902X_I2S_MAP_SD0,
+	SII902X_I2S_MAP_SD1,
+	SII902X_I2S_MAP_SD2,
+	SII902X_I2S_MAP_SD3,
+};
+
+enum sii902x_i2s_map_fifo {
+	SII902X_I2S_MAP_FIFO0,
+	SII902X_I2S_MAP_FIFO1,
+	SII902X_I2S_MAP_FIFO2,
+	SII902X_I2S_MAP_FIFO3,
+};
+
+enum sii902x_aif_format {
+	SII902X_AIF_FORMAT_SPDIF = 1,
+	SII902X_AIF_FORMAT_I2S,
+	SII902X_AIF_FORMAT_DSD,
+};
+
+enum sii902x_aif_coding {
+	SII902X_AIF_CODING_STREAM_HEADER,
+	SII902X_AIF_CODING_PCM,
+	SII902X_AIF_CODING_AC3,
+	SII902X_AIF_CODING_MPEG1,
+	SII902X_AIF_CODING_MP3,
+	SII902X_AIF_CODING_MPEG2,
+	SII902X_AIF_CODING_AAC,
+	SII902X_AIF_CODING_DTS,
+	SII902X_AIF_CODING_ATRAC,
+};
+
+enum sii902x_sample_rate {
+	SII902X_SAMPLE_RATE_32000 = 1,
+	SII902X_SAMPLE_RATE_44100,
+	SII902X_SAMPLE_RATE_48000,
+	SII902X_SAMPLE_RATE_88200,
+	SII902X_SAMPLE_RATE_96000,
+	SII902X_SAMPLE_RATE_176400,
+	SII902X_SAMPLE_RATE_192000,
+};
+
+enum sii902x_sample_width {
+	SII902X_SAMPLE_RATE_SIZE_16 = 1,
+	SII902X_SAMPLE_RATE_SIZE_20,
+	SII902X_SAMPLE_RATE_SIZE_24,
+};
+
+struct sii902x_audio_params {
+	unsigned int aes_size;
+	unsigned int aes_rate;
+};
+
 struct sii902x {
 	struct i2c_client *i2c;
 	struct regmap *regmap;
 	struct drm_bridge bridge;
 	struct drm_connector connector;
 	struct gpio_desc *reset_gpio;
+	struct i2c_mux_core *i2cmux;
+	struct regulator_bulk_data supplies[2];
+	struct platform_device *audio_pdev;
+	struct sii902x_audio_params audio;
+	struct edid *edid;
 };
 
+static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val)
+{
+	struct i2c_msg xfer[] = {
+		{
+			.addr = i2c->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &reg,
+		}, {
+			.addr = i2c->addr,
+			.flags = I2C_M_RD,
+			.len = 1,
+			.buf = val,
+		}
+	};
+	unsigned char xfers = ARRAY_SIZE(xfer);
+	int ret, retries = 5;
+
+	do {
+		ret = __i2c_transfer(i2c->adapter, xfer, xfers);
+		if (ret < 0)
+			return ret;
+	} while (ret != xfers && --retries);
+	return ret == xfers ? 0 : -1;
+}
+
+static int sii902x_write_unlocked(struct i2c_client *i2c, u8 reg, u8 val)
+{
+	u8 data[2] = {reg, val};
+	struct i2c_msg xfer = {
+		.addr = i2c->addr,
+		.flags = 0,
+		.len = sizeof(data),
+		.buf = data,
+	};
+	int ret, retries = 5;
+
+	do {
+		ret = __i2c_transfer(i2c->adapter, &xfer, 1);
+		if (ret < 0)
+			return ret;
+	} while (!ret && --retries);
+	return !ret ? -1 : 0;
+}
+
+static int sii902x_update_bits_unlocked(struct i2c_client *i2c, u8 reg, u8 mask,
+					u8 val)
+{
+	int ret;
+	u8 status;
+
+	ret = sii902x_read_unlocked(i2c, reg, &status);
+	if (ret)
+		return ret;
+	status &= ~mask;
+	status |= val & mask;
+	return sii902x_write_unlocked(i2c, reg, status);
+}
+
 static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge)
 {
 	return container_of(bridge, struct sii902x, bridge);
@@ -135,45 +350,18 @@ static const struct drm_connector_funcs sii902x_connector_funcs = {
 static int sii902x_get_modes(struct drm_connector *connector)
 {
 	struct sii902x *sii902x = connector_to_sii902x(connector);
-	struct regmap *regmap = sii902x->regmap;
 	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
-	struct device *dev = &sii902x->i2c->dev;
-	unsigned long timeout;
-	unsigned int retries;
-	unsigned int status;
 	struct edid *edid;
-	int num = 0;
-	int ret;
-
-	ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
-				 SII902X_SYS_CTRL_DDC_BUS_REQ,
-				 SII902X_SYS_CTRL_DDC_BUS_REQ);
-	if (ret)
-		return ret;
-
-	timeout = jiffies +
-		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
-	do {
-		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
-		if (ret)
-			return ret;
-	} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
-		 time_before(jiffies, timeout));
+	bool hdmi_mode = false;
+	int num = 0, ret;
 
-	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-		dev_err(dev, "failed to acquire the i2c bus\n");
-		return -ETIMEDOUT;
-	}
-
-	ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status);
-	if (ret)
-		return ret;
-
-	edid = drm_get_edid(connector, sii902x->i2c->adapter);
+	edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]);
 	drm_connector_update_edid_property(connector, edid);
 	if (edid) {
 		num = drm_add_edid_modes(connector, edid);
-		kfree(edid);
+		hdmi_mode = drm_detect_hdmi_monitor(edid);
+		kfree(sii902x->edid);
+		sii902x->edid = edid;
 	}
 
 	ret = drm_display_info_set_bus_formats(&connector->display_info,
@@ -181,41 +369,10 @@ static int sii902x_get_modes(struct drm_connector *connector)
 	if (ret)
 		return ret;
 
-	/*
-	 * Sometimes the I2C bus can stall after failure to use the
-	 * EDID channel. Retry a few times to see if things clear
-	 * up, else continue anyway.
-	 */
-	retries = 5;
-	do {
-		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA,
-				  &status);
-		retries--;
-	} while (ret && retries);
-	if (ret)
-		dev_err(dev, "failed to read status (%d)\n", ret);
-
-	ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA,
-				 SII902X_SYS_CTRL_DDC_BUS_REQ |
-				 SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
-	if (ret)
-		return ret;
-
-	timeout = jiffies +
-		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
-	do {
-		ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status);
-		if (ret)
-			return ret;
-	} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
-			   SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
-		 time_before(jiffies, timeout));
-
-	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
-		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
-		dev_err(dev, "failed to release the i2c bus\n");
-		return -ETIMEDOUT;
-	}
+	if (hdmi_mode)
+		regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
+				   SII902X_SYS_CTRL_OUTPUT_MODE,
+				   SII902X_SYS_CTRL_OUTPUT_HDMI);
 
 	return num;
 }
@@ -240,17 +397,29 @@ static void sii902x_bridge_disable(struct drm_bridge *bridge)
 	regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
 			   SII902X_SYS_CTRL_PWR_DWN,
 			   SII902X_SYS_CTRL_PWR_DWN);
+	pinctrl_pm_select_sleep_state(&sii902x->i2c->dev);
 }
 
 static void sii902x_bridge_enable(struct drm_bridge *bridge)
 {
 	struct sii902x *sii902x = bridge_to_sii902x(bridge);
+	bool hdmi_mode;
 
+	pinctrl_pm_select_default_state(&sii902x->i2c->dev);
 	regmap_update_bits(sii902x->regmap, SII902X_PWR_STATE_CTRL,
 			   SII902X_AVI_POWER_STATE_MSK,
 			   SII902X_AVI_POWER_STATE_D(0));
 	regmap_update_bits(sii902x->regmap, SII902X_SYS_CTRL_DATA,
 			   SII902X_SYS_CTRL_PWR_DWN, 0);
+
+	if(sii902x->edid) {
+		hdmi_mode = drm_detect_hdmi_monitor(sii902x->edid);
+		if (hdmi_mode)
+			regmap_update_bits(sii902x->regmap,
+					   SII902X_SYS_CTRL_DATA,
+					   SII902X_SYS_CTRL_OUTPUT_MODE,
+					   SII902X_SYS_CTRL_OUTPUT_HDMI);
+	}
 }
 
 static void sii902x_bridge_mode_set(struct drm_bridge *bridge,
@@ -329,6 +498,267 @@ static int sii902x_bridge_attach(struct drm_bridge *bridge)
 	return 0;
 }
 
+static int sii902x_audio_infoframe_config(struct sii902x *sii902x,
+					  struct hdmi_codec_params *params)
+{
+	u8 buf[HDMI_INFOFRAME_SIZE(AUDIO)];
+	int ret;
+
+	ret = hdmi_audio_infoframe_init(&params->cea);
+	if (ret) {
+		DRM_ERROR("Failed to init audio infoframe\n");
+		return ret;
+	}
+
+	ret = hdmi_audio_infoframe_pack(&params->cea, buf, sizeof(buf));
+	if (ret < 0) {
+		DRM_ERROR("failed to pack audio infoframe: %d\n", ret);
+		return ret;
+	}
+
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_OTHER_IF,
+			   SII902X_OTHER_IF_SEL_MSK |
+			   SII902X_OTHER_IF_REPEAT |
+			   SII902X_OTHER_IF_ENABLE,
+			   SII902X_OTHER_IF_SEL(SII902X_IF_AUDIO) |
+			   SII902X_OTHER_IF_REPEAT |
+			   SII902X_OTHER_IF_ENABLE);
+
+	return regmap_bulk_write(sii902x->regmap, SII902X_OTHER_IF + 1, buf,
+				 HDMI_INFOFRAME_SIZE(AUDIO) +
+				 HDMI_INFOFRAME_HEADER_SIZE - 1);
+}
+
+static int sii902x_audio_iec60958_config(struct sii902x *sii902x)
+{
+	/* Bytes 0,1,2 are let to default setting. Configure bytes 3&4 */
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_I2S_CS3,
+			   SII902X_I2S_CS3_FS_MSK,
+			   SII902X_I2S_CS3_FS(sii902x->audio.aes_rate));
+
+	return regmap_update_bits(sii902x->regmap,
+				  SII902X_I2S_CS4,
+				  SII902X_I2S_CS4_WL_MSK,
+				  SII902X_I2S_CS4_WL(sii902x->audio.aes_size));
+}
+
+static int sii902x_i2s_configure(struct sii902x *sii902x,
+				 struct hdmi_codec_params *params,
+				 struct hdmi_codec_daifmt *fmt)
+{
+	unsigned int rate, size, val, mask;
+
+	/* Configure audio interface */
+	regmap_update_bits(sii902x->regmap, SII902X_AIF,
+			   SII902X_AIF_FMT_MSK |
+			   SII902X_AIF_LAYOUT |
+			   SII902X_AIF_MUTE |
+			   SII902X_AIF_CODING_MSK,
+			   SII902X_AIF_FMT(SII902X_AIF_FORMAT_I2S) |
+			   SII902X_AIF_MUTE |
+			   SII902X_AIF_CODING(SII902X_AIF_CODING_PCM));
+
+	switch (fmt->fmt) {
+	case HDMI_I2S:
+		val = SII902X_I2S_CONF_SCK_STROBING;
+		break;
+	case HDMI_LEFT_J:
+		val = SII902X_I2S_CONF_SCK_STROBING |
+		      SII902X_I2S_CONF_WS_STROBING |
+		      SII902X_I2S_CONF_NO_OFFSET;
+		break;
+	case HDMI_RIGHT_J:
+		val = SII902X_I2S_CONF_SCK_STROBING |
+		      SII902X_I2S_CONF_WS_STROBING |
+		      SII902X_I2S_CONF_NO_OFFSET |
+		      SII902X_I2S_CONF_JUSTIFY;
+		break;
+	default:
+		DRM_ERROR("Unknown protocol %#x\n", fmt->fmt);
+		return -EINVAL;
+	}
+	mask = SII902X_I2S_CONF_NO_OFFSET | SII902X_I2S_CONF_WS_STROBING |
+	       SII902X_I2S_CONF_JUSTIFY | SII902X_I2S_CONF_LSB_DIR |
+	       SII902X_I2S_CONF_SCK_STROBING;
+
+	if (fmt->frame_clk_inv)
+		val ^= SII902X_I2S_CONF_WS_STROBING;
+
+	if (fmt->bit_clk_inv)
+		val ^= SII902X_I2S_CONF_SCK_STROBING;
+
+	/* Configure i2s interface */
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_I2S_CONF, mask, val);
+
+	/*
+	 * Configure i2s interface mapping
+	 * Assume that only SD0 is used and connected to FIFO0
+	 */
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_I2S_MAP,
+			   SII902X_I2S_MAP_SWITCH |
+			   SII902X_I2S_MAP_SD_MSK | SII902X_I2S_MAP_FIFO_MSK,
+			   SII902X_I2S_MAP_SWITCH |
+			   SII902X_I2S_MAP_SD0 | SII902X_I2S_MAP_FIFO0);
+
+	switch (params->sample_rate) {
+	case 32000:
+		rate = SII902X_SAMPLE_RATE_32000;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		rate = SII902X_SAMPLE_RATE_44100;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		rate = SII902X_SAMPLE_RATE_48000;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		rate = SII902X_SAMPLE_RATE_88200;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		rate = SII902X_SAMPLE_RATE_96000;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_96000;
+		break;
+	case 176400:
+		rate = SII902X_SAMPLE_RATE_176400;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_176400;
+		break;
+	case 192000:
+		rate = SII902X_SAMPLE_RATE_192000;
+		sii902x->audio.aes_rate = IEC958_AES3_CON_FS_192000;
+		break;
+	default:
+		DRM_ERROR("Unknown sampling rate %d\n", params->sample_rate);
+		return -EINVAL;
+	}
+
+	switch (params->sample_width) {
+	case 16:
+		size = SII902X_SAMPLE_RATE_SIZE_16;
+		sii902x->audio.aes_size = IEC958_AES4_CON_WORDLEN_20_16;
+		break;
+	case 20:
+		size = SII902X_SAMPLE_RATE_SIZE_20;
+		sii902x->audio.aes_size = IEC958_AES4_CON_WORDLEN_24_20;
+		break;
+	case 24:
+	case 32:
+		size = SII902X_SAMPLE_RATE_SIZE_24;
+		sii902x->audio.aes_size = IEC958_AES4_CON_WORDLEN_24_20 |
+					  IEC958_AES4_CON_MAX_WORDLEN_24;
+		break;
+	default:
+		DRM_ERROR("Unknown sample width %d\n", params->sample_width);
+		return -EINVAL;
+	}
+
+	/* Configure i2s interface rate and input/output word length */
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_INDEXED_REG_PAGE, 0xff, 0x2);
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_INDEXED_REG_IDX, 0xff, 0x24);
+	regmap_update_bits(sii902x->regmap,
+			   SII902X_INDEXED_REG_ACCESS, 0x0f,
+			   sii902x->audio.aes_size);
+
+	return regmap_update_bits(sii902x->regmap,
+				  SII902X_I2S_AUD_FMT,
+				  SII902X_I2S_AUD_FMT_FS_MSK |
+				  SII902X_I2S_AUD_FMT_SZ_MSK,
+				  SII902X_I2S_AUD_FMT_FS(rate) |
+				  SII902X_I2S_AUD_FMT_SZ(size));
+}
+
+static int sii902x_audio_hw_params(struct device *dev, void *data,
+				   struct hdmi_codec_daifmt *fmt,
+				   struct hdmi_codec_params *params)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+	int ret;
+
+	if (fmt->bit_clk_master || fmt->frame_clk_master) {
+		DRM_ERROR("Master mode not supported\n");
+		return -EINVAL;
+	}
+
+	if (fmt->fmt == HDMI_I2S || fmt->fmt == HDMI_RIGHT_J ||
+	    fmt->fmt == HDMI_LEFT_J) {
+		/* Configure i2s interface */
+		ret = sii902x_i2s_configure(sii902x, params, fmt);
+		if (ret)
+			return ret;
+
+		/* Configure iec958 channel status */
+		ret = sii902x_audio_iec60958_config(sii902x);
+		if (ret)
+			return ret;
+	} else {
+		DRM_ERROR("Unsupported format 0x%x\n", fmt->fmt);
+		return -EINVAL;
+	}
+
+	/* Configure audio infoframes */
+	return sii902x_audio_infoframe_config(sii902x, params);
+}
+
+static int sii902x_audio_digital_mute(struct device *dev,
+				      void *data, bool enable)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+	int ret;
+
+	DRM_DEBUG("%s audio\n", enable ? "mute" : "unmute");
+
+	if (enable)
+		ret = regmap_update_bits(sii902x->regmap, SII902X_AIF,
+					 SII902X_AIF_MUTE, SII902X_AIF_MUTE);
+	else
+		ret = regmap_update_bits(sii902x->regmap, SII902X_AIF,
+					 SII902X_AIF_MUTE, 0);
+
+	return ret;
+}
+
+static int sii902x_audio_get_eld(struct device *dev, void *data,
+				 u8 *buf, size_t len)
+{
+	struct sii902x *sii902x = dev_get_drvdata(dev);
+	struct drm_connector *connector = &sii902x->connector;
+
+	if (!sii902x->edid)
+		return -ENODEV;
+
+	memcpy(buf, connector->eld, min(sizeof(connector->eld), len));
+
+	return 0;
+}
+
+static void sii902x_audio_shutdown(struct device *dev, void *data)
+{}
+
+static int sii902x_audio_get_dai_id(struct snd_soc_component *component,
+				    struct device_node *endpoint)
+{
+	struct of_endpoint of_ep;
+	int ret;
+
+	ret = of_graph_parse_endpoint(endpoint, &of_ep);
+	if (ret < 0)
+		return ret;
+
+	/* HDMI sound should be located at reg = <1> */
+	if (of_ep.port == 1)
+		return 0;
+
+	return -EINVAL;
+}
+
 static const struct drm_bridge_funcs sii902x_bridge_funcs = {
 	.attach = sii902x_bridge_attach,
 	.mode_set = sii902x_bridge_mode_set,
@@ -348,10 +778,39 @@ static const struct regmap_access_table sii902x_volatile_table = {
 static const struct regmap_config sii902x_regmap_config = {
 	.reg_bits = 8,
 	.val_bits = 8,
+	/* map up to infoframe data registers. 0xbf-0xde */
+	.max_register = 0xde,
 	.volatile_table = &sii902x_volatile_table,
 	.cache_type = REGCACHE_NONE,
 };
 
+static const struct hdmi_codec_ops sii902x_codec_ops = {
+	.hw_params = sii902x_audio_hw_params,
+	.audio_shutdown = sii902x_audio_shutdown,
+	.get_dai_id = sii902x_audio_get_dai_id,
+	.digital_mute = sii902x_audio_digital_mute,
+	.get_eld = sii902x_audio_get_eld,
+};
+
+static int sii902x_register_audio_driver(struct device *dev,
+					 struct sii902x *sii902x)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.ops = &sii902x_codec_ops,
+		.max_i2s_channels = 2,
+		.i2s = 1,
+	};
+
+	sii902x->audio_pdev = platform_device_register_data(
+		dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+		&codec_data, sizeof(codec_data));
+
+	if (IS_ERR(sii902x->audio_pdev))
+		return PTR_ERR(sii902x->audio_pdev);
+
+	return 0;
+}
+
 static irqreturn_t sii902x_interrupt(int irq, void *data)
 {
 	struct sii902x *sii902x = data;
@@ -366,15 +825,134 @@ static irqreturn_t sii902x_interrupt(int irq, void *data)
 	return IRQ_HANDLED;
 }
 
+/*
+ * The purpose of sii902x_i2c_bypass_select is to enable the pass through
+ * mode of the HDMI transmitter. Do not use regmap from within this function,
+ * only use sii902x_*_unlocked functions to read/modify/write registers.
+ * We are holding the parent adapter lock here, keep this in mind before
+ * adding more i2c transactions.
+ */
+static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
+{
+	struct sii902x *sii902x = i2c_mux_priv(mux);
+	struct device *dev = &sii902x->i2c->dev;
+	unsigned long timeout;
+	u8 status;
+	int ret;
+
+	ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					   SII902X_SYS_CTRL_DDC_BUS_REQ,
+					   SII902X_SYS_CTRL_DDC_BUS_REQ);
+
+	timeout = jiffies +
+		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
+	do {
+		ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					    &status);
+		if (ret)
+			return ret;
+	} while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
+		 time_before(jiffies, timeout));
+
+	if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
+		dev_err(dev, "Failed to acquire the i2c bus\n");
+		return -ETIMEDOUT;
+	}
+
+	ret = sii902x_write_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+				     status);
+	if (ret)
+		return ret;
+	return 0;
+}
+
+/*
+ * The purpose of sii902x_i2c_bypass_deselect is to disable the pass through
+ * mode of the HDMI transmitter. Do not use regmap from within this function,
+ * only use sii902x_*_unlocked functions to read/modify/write registers.
+ * We are holding the parent adapter lock here, keep this in mind before
+ * adding more i2c transactions.
+ */
+static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
+{
+	struct sii902x *sii902x = i2c_mux_priv(mux);
+	struct device *dev = &sii902x->i2c->dev;
+	unsigned long timeout;
+	unsigned int retries;
+	u8 status;
+	int ret;
+
+	/*
+	 * When the HDMI transmitter is in pass through mode, we need an
+	 * (undocumented) additional delay between STOP and START conditions
+	 * to guarantee the bus won't get stuck.
+	 */
+	udelay(30);
+
+	/*
+	 * Sometimes the I2C bus can stall after failure to use the
+	 * EDID channel. Retry a few times to see if things clear
+	 * up, else continue anyway.
+	 */
+	retries = 5;
+	do {
+		ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					    &status);
+		retries--;
+	} while (ret && retries);
+	if (ret) {
+		dev_err(dev, "failed to read status (%d)\n", ret);
+		return ret;
+	}
+
+	ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					   SII902X_SYS_CTRL_DDC_BUS_REQ |
+					   SII902X_SYS_CTRL_DDC_BUS_GRTD, 0);
+	if (ret)
+		return ret;
+
+	timeout = jiffies +
+		  msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS);
+	do {
+		ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA,
+					    &status);
+		if (ret)
+			return ret;
+	} while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
+			   SII902X_SYS_CTRL_DDC_BUS_GRTD) &&
+		 time_before(jiffies, timeout));
+
+	if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ |
+		      SII902X_SYS_CTRL_DDC_BUS_GRTD)) {
+		dev_err(dev, "failed to release the i2c bus\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
 static int sii902x_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
 	struct device *dev = &client->dev;
 	unsigned int status = 0;
 	struct sii902x *sii902x;
+	unsigned char data[2] = { SII902X_CEC_SETUP, 0};
+	struct i2c_msg msg = {
+		.addr	= SII902X_CEC_I2C_ADDR << 1,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
 	u8 chipid[4];
 	int ret;
 
+	ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+	if (!ret) {
+		dev_err(dev, "I2C adapter not suitable\n");
+		return -EIO;
+	}
+
 	sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL);
 	if (!sii902x)
 		return -ENOMEM;
@@ -392,39 +970,67 @@ static int sii902x_probe(struct i2c_client *client,
 		return PTR_ERR(sii902x->reset_gpio);
 	}
 
+	sii902x->supplies[0].supply = "iovcc";
+	sii902x->supplies[1].supply = "cvcc12";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sii902x->supplies),
+				      sii902x->supplies);
+	if (ret) {
+		if(ret != -EPROBE_DEFER)
+			dev_err(dev, "regulator_bulk_get failed\n");
+		return ret;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies),
+				    sii902x->supplies);
+	if (ret) {
+		dev_err(dev, "regulator_bulk_enable failed\n");
+		return ret;
+	}
+
+	pinctrl_pm_select_sleep_state(&sii902x->i2c->dev);
 	sii902x_reset(sii902x);
 
 	ret = regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x0);
 	if (ret)
-		return ret;
+		goto err_disable_regulator;
 
 	ret = regmap_bulk_read(sii902x->regmap, SII902X_REG_CHIPID(0),
 			       &chipid, 4);
 	if (ret) {
 		dev_err(dev, "regmap_read failed %d\n", ret);
-		return ret;
+		goto err_disable_regulator;
 	}
 
 	if (chipid[0] != 0xb0) {
 		dev_err(dev, "Invalid chipid: %02x (expecting 0xb0)\n",
 			chipid[0]);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_disable_regulator;
 	}
 
+	/*
+	 * By default, CEC must be disabled to allow other CEC devives
+	 * to bypass the bridge.
+	 */
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		dev_warn(&client->dev, "Failed to disable CEC device!\n");
+
 	/* Clear all pending interrupts */
 	regmap_read(sii902x->regmap, SII902X_INT_STATUS, &status);
 	regmap_write(sii902x->regmap, SII902X_INT_STATUS, status);
 
 	if (client->irq > 0) {
-		regmap_write(sii902x->regmap, SII902X_INT_ENABLE,
-			     SII902X_HOTPLUG_EVENT);
+		regmap_update_bits(sii902x->regmap, SII902X_INT_ENABLE,
+				   SII902X_HOTPLUG_EVENT,
+				   SII902X_HOTPLUG_EVENT);
 
 		ret = devm_request_threaded_irq(dev, client->irq, NULL,
 						sii902x_interrupt,
 						IRQF_ONESHOT, dev_name(dev),
 						sii902x);
 		if (ret)
-			return ret;
+			goto err_disable_regulator;
 	}
 
 	sii902x->bridge.funcs = &sii902x_bridge_funcs;
@@ -433,7 +1039,31 @@ static int sii902x_probe(struct i2c_client *client,
 
 	i2c_set_clientdata(client, sii902x);
 
+	sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev,
+					1, 0, I2C_MUX_GATE,
+					sii902x_i2c_bypass_select,
+					sii902x_i2c_bypass_deselect);
+	if (!sii902x->i2cmux) {
+		dev_err(dev, "failed to allocate I2C mux\n");
+		return -ENOMEM;
+	}
+
+	sii902x->i2cmux->priv = sii902x;
+	ret = i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0);
+	if (ret) {
+		dev_err(dev, "Couldn't add i2c mux adapter\n");
+		return ret;
+	}
+
+	sii902x_register_audio_driver(dev, sii902x);
+
 	return 0;
+
+err_disable_regulator:
+	regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),
+			       sii902x->supplies);
+
+	return ret;
 }
 
 static int sii902x_remove(struct i2c_client *client)
@@ -441,11 +1071,76 @@ static int sii902x_remove(struct i2c_client *client)
 {
 	struct sii902x *sii902x = i2c_get_clientdata(client);
 
+	if (sii902x->i2cmux)
+		i2c_mux_del_adapters(sii902x->i2cmux);
+
 	drm_bridge_remove(&sii902x->bridge);
 
+	regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),
+			       sii902x->supplies);
+
+	return 0;
+}
+
+static int sii902x_pm_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sii902x *sii902x = i2c_get_clientdata(client);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (sii902x->reset_gpio)
+		gpiod_set_value(sii902x->reset_gpio, 1);
+
+	regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies),
+			       sii902x->supplies);
+
 	return 0;
 }
 
+static int sii902x_pm_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct sii902x *sii902x = i2c_get_clientdata(client);
+	unsigned char data[2] = { SII902X_CEC_SETUP, 0};
+	struct i2c_msg msg = {
+		.addr	= SII902X_CEC_I2C_ADDR << 1,
+		.flags	= 0,
+		.len	= 2,
+		.buf	= data,
+	};
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies),
+				    sii902x->supplies);
+	if (ret) {
+		DRM_ERROR("regulator_bulk_enable failed\n");
+		return ret;
+	}
+
+	if (sii902x->reset_gpio)
+		gpiod_set_value(sii902x->reset_gpio, 0);
+
+	regmap_write(sii902x->regmap, SII902X_REG_TPI_RQB, 0x00);
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret < 0)
+		DRM_ERROR("Failed to disable CEC device!\n");
+
+	if (client->irq > 0)
+		regmap_update_bits(sii902x->regmap, SII902X_INT_ENABLE,
+				   SII902X_HOTPLUG_EVENT,
+				   SII902X_HOTPLUG_EVENT);
+
+	return 0;
+}
+
+static const struct dev_pm_ops sii902x_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sii902x_pm_suspend, sii902x_pm_resume)
+};
+
 static const struct of_device_id sii902x_dt_ids[] = {
 	{ .compatible = "sil,sii9022", },
 	{ }
@@ -464,6 +1159,7 @@ static struct i2c_driver sii902x_driver = {
 	.driver = {
 		.name = "sii902x",
 		.of_match_table = sii902x_dt_ids,
+		.pm = &sii902x_pm_ops,
 	},
 	.id_table = sii902x_i2c_ids,
 };
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 02db9ac..d9d9ad9 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -130,7 +130,7 @@ EXPORT_SYMBOL(drm_mode_probed_add);
  * according to the hdisplay, vdisplay, vrefresh.
  * It is based from the VESA(TM) Coordinated Video Timing Generator by
  * Graham Loveridge April 9, 2003 available at
- * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls 
+ * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls
  *
  * And it is copied from xf86CVTmode in xserver/hw/xfree86/modes/xf86cvt.c.
  * What I have done is to translate it by using integer calculation.
@@ -611,6 +611,15 @@ void drm_display_mode_from_videomode(const struct videomode *vm,
 		dmode->flags |= DRM_MODE_FLAG_DBLSCAN;
 	if (vm->flags & DISPLAY_FLAGS_DOUBLECLK)
 		dmode->flags |= DRM_MODE_FLAG_DBLCLK;
+	if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+		dmode->flags |= DRM_MODE_FLAG_PPIXDATA;
+	else if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+		dmode->flags |= DRM_MODE_FLAG_NPIXDATA;
+	if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
+		dmode->flags |= DRM_MODE_FLAG_PDE;
+	else if (vm->flags & DISPLAY_FLAGS_DE_LOW)
+		dmode->flags |= DRM_MODE_FLAG_NDE;
+
 	drm_mode_set_name(dmode);
 }
 EXPORT_SYMBOL_GPL(drm_display_mode_from_videomode);
@@ -652,6 +661,14 @@ void drm_display_mode_to_videomode(const struct drm_display_mode *dmode,
 		vm->flags |= DISPLAY_FLAGS_DOUBLESCAN;
 	if (dmode->flags & DRM_MODE_FLAG_DBLCLK)
 		vm->flags |= DISPLAY_FLAGS_DOUBLECLK;
+	if (dmode->flags & DRM_MODE_FLAG_PPIXDATA)
+		vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+	else if (dmode->flags & DRM_MODE_FLAG_NPIXDATA)
+		vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+	if (dmode->flags & DRM_MODE_FLAG_PDE)
+		vm->flags |= DISPLAY_FLAGS_DE_HIGH;
+	else if (dmode->flags & DRM_MODE_FLAG_NDE)
+		vm->flags |= DISPLAY_FLAGS_DE_LOW;
 }
 EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode);
 
diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
index 87fa316..a76d03a 100644
--- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
+++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c
@@ -67,15 +67,15 @@ struct otm8009a {
 };
 
 static const struct drm_display_mode default_mode = {
-	.clock = 32729,
+	.clock = 33000,
 	.hdisplay = 480,
 	.hsync_start = 480 + 120,
-	.hsync_end = 480 + 120 + 63,
-	.htotal = 480 + 120 + 63 + 120,
+	.hsync_end = 480 + 120 + 64,
+	.htotal = 480 + 120 + 64 + 120,
 	.vdisplay = 800,
-	.vsync_start = 800 + 12,
-	.vsync_end = 800 + 12 + 12,
-	.vtotal = 800 + 12 + 12 + 12,
+	.vsync_start = 800 + 14,
+	.vsync_end = 800 + 14 + 14,
+	.vtotal = 800 + 14 + 14 + 14,
 	.vrefresh = 50,
 	.flags = 0,
 	.width_mm = 52,
diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c
index 7759353..94436ea 100644
--- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c
+++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c
@@ -81,15 +81,15 @@ struct rm68200 {
 };
 
 static const struct drm_display_mode default_mode = {
-	.clock = 52582,
+	.clock = 54000,
 	.hdisplay = 720,
-	.hsync_start = 720 + 38,
-	.hsync_end = 720 + 38 + 8,
-	.htotal = 720 + 38 + 8 + 38,
+	.hsync_start = 720 + 48,
+	.hsync_end = 720 + 48 + 9,
+	.htotal = 720 + 48 + 9 + 48,
 	.vdisplay = 1280,
 	.vsync_start = 1280 + 12,
-	.vsync_end = 1280 + 12 + 4,
-	.vtotal = 1280 + 12 + 4 + 12,
+	.vsync_end = 1280 + 12 + 5,
+	.vtotal = 1280 + 12 + 5 + 12,
 	.vrefresh = 50,
 	.flags = 0,
 	.width_mm = 68,
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c
index f2021b2..cf61875 100644
--- a/drivers/gpu/drm/stm/drv.c
+++ b/drivers/gpu/drm/stm/drv.c
@@ -26,7 +26,6 @@
 
 static const struct drm_mode_config_funcs drv_mode_config_funcs = {
 	.fb_create = drm_gem_fb_create,
-	.output_poll_changed = drm_fb_helper_output_poll_changed,
 	.atomic_check = drm_atomic_helper_check,
 	.atomic_commit = drm_atomic_helper_commit,
 };
@@ -52,7 +51,6 @@ DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
 static struct drm_driver drv_driver = {
 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
 			   DRIVER_ATOMIC,
-	.lastclose = drm_fb_helper_lastclose,
 	.name = "stm",
 	.desc = "STMicroelectronics SoC DRM",
 	.date = "20170330",
@@ -72,6 +70,8 @@ static struct drm_driver drv_driver = {
 	.gem_prime_vmap = drm_gem_cma_prime_vmap,
 	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
 	.gem_prime_mmap = drm_gem_cma_prime_mmap,
+	.get_scanout_position = ltdc_crtc_scanoutpos,
+	.get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
 };
 
 static int drv_load(struct drm_device *ddev)
@@ -108,12 +108,6 @@ static int drv_load(struct drm_device *ddev)
 	drm_mode_config_reset(ddev);
 	drm_kms_helper_poll_init(ddev);
 
-	if (ddev->mode_config.num_connector) {
-		ret = drm_fb_cma_fbdev_init(ddev, 16, 0);
-		if (ret)
-			DRM_DEBUG("Warning: fails to create fbdev\n");
-	}
-
 	platform_set_drvdata(pdev, ddev);
 
 	return 0;
@@ -126,12 +120,43 @@ static void drv_unload(struct drm_device *ddev)
 {
 	DRM_DEBUG("%s\n", __func__);
 
-	drm_fb_cma_fbdev_fini(ddev);
 	drm_kms_helper_poll_fini(ddev);
 	ltdc_unload(ddev);
 	drm_mode_config_cleanup(ddev);
 }
 
+static __maybe_unused int drv_suspend(struct device *dev)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct ltdc_device *ldev = ddev->dev_private;
+	struct drm_atomic_state *state;
+
+	drm_kms_helper_poll_disable(ddev);
+	state = drm_atomic_helper_suspend(ddev);
+	if (IS_ERR(state)) {
+		drm_kms_helper_poll_enable(ddev);
+		return PTR_ERR(state);
+	}
+	ldev->suspend_state = state;
+
+	return 0;
+}
+
+static __maybe_unused int drv_resume(struct device *dev)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct ltdc_device *ldev = ddev->dev_private;
+
+	drm_atomic_helper_resume(ddev, ldev->suspend_state);
+	drm_kms_helper_poll_enable(ddev);
+
+	return 0;
+}
+
+static const struct dev_pm_ops drv_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(drv_suspend, drv_resume)
+};
+
 static int stm_drm_platform_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -154,6 +179,8 @@ static int stm_drm_platform_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_put;
 
+	drm_fbdev_generic_setup(ddev, 16);
+
 	return 0;
 
 err_put:
@@ -187,6 +214,7 @@ static struct platform_driver stm_drm_platform_driver = {
 	.driver = {
 		.name = "stm32-display",
 		.of_match_table = drv_dt_ids,
+		.pm = &drv_pm_ops,
 	},
 };
 
diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
index a514b59..a6edd86 100644
--- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
+++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c
@@ -9,6 +9,7 @@
 #include <linux/clk.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
+#include <linux/regulator/consumer.h>
 #include <drm/drmP.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/bridge/dw_mipi_dsi.h>
@@ -76,6 +77,7 @@ struct dw_mipi_dsi_stm {
 	u32 hw_version;
 	int lane_min_kbps;
 	int lane_max_kbps;
+	struct regulator *vdd_supply;
 };
 
 static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val)
@@ -318,16 +320,30 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
 		return PTR_ERR(dsi->base);
 	}
 
+	dsi->vdd_supply = devm_regulator_get(dev, "phy-dsi");
+	if (IS_ERR(dsi->vdd_supply)) {
+		DRM_ERROR("can't get power supply\n");
+		return PTR_ERR(dsi->vdd_supply);
+	}
+
+	ret = regulator_enable(dsi->vdd_supply);
+	if (ret) {
+		DRM_ERROR("can't enable power supply\n");
+		return ret;
+	}
+
 	dsi->pllref_clk = devm_clk_get(dev, "ref");
 	if (IS_ERR(dsi->pllref_clk)) {
 		ret = PTR_ERR(dsi->pllref_clk);
 		dev_err(dev, "Unable to get pll reference clock: %d\n", ret);
+		regulator_disable(dsi->vdd_supply);
 		return ret;
 	}
 
 	ret = clk_prepare_enable(dsi->pllref_clk);
 	if (ret) {
 		dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
+		regulator_disable(dsi->vdd_supply);
 		return ret;
 	}
 
@@ -339,6 +355,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
 	dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
 	if (IS_ERR(dsi->dsi)) {
 		DRM_ERROR("Failed to initialize mipi dsi host\n");
+		regulator_disable(dsi->vdd_supply);
 		clk_disable_unprepare(dsi->pllref_clk);
 		return PTR_ERR(dsi->dsi);
 	}
@@ -351,17 +368,53 @@ static int dw_mipi_dsi_stm_remove(struct platform_device *pdev)
 	struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev);
 
 	clk_disable_unprepare(dsi->pllref_clk);
+	regulator_disable(dsi->vdd_supply);
 	dw_mipi_dsi_remove(dsi->dsi);
 
 	return 0;
 }
 
+static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev)
+{
+	struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	clk_disable_unprepare(dsi->pllref_clk);
+	regulator_disable(dsi->vdd_supply);
+
+	return 0;
+}
+
+static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev)
+{
+	struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	ret = regulator_enable(dsi->vdd_supply);
+	if (ret) {
+		DRM_ERROR("can't enable power supply\n");
+		return ret;
+	}
+	clk_prepare_enable(dsi->pllref_clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend,
+				dw_mipi_dsi_stm_resume)
+};
+
 static struct platform_driver dw_mipi_dsi_stm_driver = {
 	.probe		= dw_mipi_dsi_stm_probe,
 	.remove		= dw_mipi_dsi_stm_remove,
 	.driver		= {
 		.of_match_table = dw_mipi_dsi_stm_dt_ids,
 		.name	= "stm32-display-dsi",
+		.pm = &dw_mipi_dsi_stm_pm_ops,
 	},
 };
 
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 808d9fb..599d2f8 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -148,6 +148,8 @@
 #define IER_TERRIE	BIT(2)		/* Transfer ERRor Interrupt Enable */
 #define IER_RRIE	BIT(3)		/* Register Reload Interrupt enable */
 
+#define CPSR_CYPOS	GENMASK(15, 0)	/* Current Y position */
+
 #define ISR_LIF		BIT(0)		/* Line Interrupt Flag */
 #define ISR_FUIF	BIT(1)		/* Fifo Underrun Interrupt Flag */
 #define ISR_TERRIF	BIT(2)		/* Transfer ERRor Interrupt Flag */
@@ -348,6 +350,33 @@ static inline u32 get_pixelformat_without_alpha(u32 drm)
 	}
 }
 
+static int ltdc_power_up(struct ltdc_device *ldev)
+{
+	int ret;
+
+	DRM_DEBUG_DRIVER("\n");
+
+	if (!ldev->power_on) {
+		ret = clk_prepare_enable(ldev->pixel_clk);
+		if (ret) {
+			DRM_ERROR("failed to enable pixel clock (%d)\n",
+				  ret);
+			return ret;
+		}
+		ldev->power_on = true;
+	}
+
+	return 0;
+}
+
+static void ltdc_power_down(struct ltdc_device *ldev)
+{
+	DRM_DEBUG_DRIVER("\n");
+
+	clk_disable_unprepare(ldev->pixel_clk);
+	ldev->power_on = false;
+}
+
 static irqreturn_t ltdc_irq_thread(int irq, void *arg)
 {
 	struct drm_device *ddev = arg;
@@ -408,9 +437,14 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
 				    struct drm_crtc_state *old_state)
 {
 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+	int ret;
 
 	DRM_DEBUG_DRIVER("\n");
 
+	ret = ltdc_power_up(ldev);
+	if (ret)
+		return;
+
 	/* Sets the background color value */
 	reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
 
@@ -443,6 +477,8 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
 
 	/* immediately commit disable of layers before switching off LTDC */
 	reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+
+	ltdc_power_down(ldev);
 }
 
 #define CLK_TOLERANCE_HZ 50
@@ -497,13 +533,10 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc,
 	 * TODO clk_round_rate() does not work yet. When ready, it can
 	 * be used instead of clk_set_rate() then clk_get_rate().
 	 */
-
-	clk_disable(ldev->pixel_clk);
 	if (clk_set_rate(ldev->pixel_clk, rate) < 0) {
 		DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate);
 		return false;
 	}
-	clk_enable(ldev->pixel_clk);
 
 	adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000;
 
@@ -518,6 +551,11 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
 	u32 total_width, total_height;
 	u32 val;
+	int ret;
+
+	ret = ltdc_power_up(ldev);
+	if (ret)
+		return;
 
 	drm_display_mode_to_videomode(mode, &vm);
 
@@ -546,10 +584,10 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH)
 		val |= GCR_VSPOL;
 
-	if (vm.flags & DISPLAY_FLAGS_DE_HIGH)
+	if (vm.flags & DISPLAY_FLAGS_DE_LOW)
 		val |= GCR_DEPOL;
 
-	if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+	if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
 		val |= GCR_PCPOL;
 
 	reg_update_bits(ldev->regs, LTDC_GCR,
@@ -611,8 +649,14 @@ static const struct drm_crtc_helper_funcs ltdc_crtc_helper_funcs = {
 static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
 {
 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+	int ret;
 
 	DRM_DEBUG_DRIVER("\n");
+
+	ret = ltdc_power_up(ldev);
+	if (ret)
+		return ret;
+
 	reg_set(ldev->regs, LTDC_IER, IER_LIE);
 
 	return 0;
@@ -623,9 +667,58 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
 	struct ltdc_device *ldev = crtc_to_ltdc(crtc);
 
 	DRM_DEBUG_DRIVER("\n");
+
+	if (!ldev->power_on) {
+		DRM_WARN("power is already down\n");
+		return;
+	}
+
 	reg_clear(ldev->regs, LTDC_IER, IER_LIE);
 }
 
+bool ltdc_crtc_scanoutpos(struct drm_device *ddev, unsigned int pipe,
+			  bool in_vblank_irq, int *vpos, int *hpos,
+			  ktime_t *stime, ktime_t *etime,
+			  const struct drm_display_mode *mode)
+{
+	struct ltdc_device *ldev = ddev->dev_private;
+	int line, vactive_start, vactive_end, vtotal;
+
+	if (stime)
+		*stime = ktime_get();
+
+	/* The active area starts after vsync + front porch and ends
+	 * at vsync + front porc + display size.
+	 * The total height also include back porch.
+	 * We have 3 possible cases to handle:
+	 * - line < vactive_start: vpos = line - vactive_start and will be
+	 * negative
+	 * - vactive_start < line < vactive_end: vpos = line - vactive_start
+	 * and will be positive
+	 * - line > vactive_end: vpos = line - vtotal - vactive_start
+	 * and will negative
+	 *
+	 * Computation for the two first cases are identical so we can
+	 * simplify the code and only test if line > vactive_end
+	 */
+	line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS;
+	vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP;
+	vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH;
+	vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH;
+
+	if (line > vactive_end)
+		*vpos = line - vtotal - vactive_start;
+	else
+		*vpos = line - vactive_start;
+
+	*hpos = 0;
+
+	if (etime)
+		*etime = ktime_get();
+
+	return true;
+}
+
 static const struct drm_crtc_funcs ltdc_crtc_funcs = {
 	.destroy = drm_crtc_cleanup,
 	.set_config = drm_atomic_helper_set_config,
@@ -646,24 +739,44 @@ static int ltdc_plane_atomic_check(struct drm_plane *plane,
 				   struct drm_plane_state *state)
 {
 	struct drm_framebuffer *fb = state->fb;
-	u32 src_x, src_y, src_w, src_h;
+	struct drm_crtc_state *crtc_state;
+	struct drm_rect *src = &state->src;
+	struct drm_rect *dst = &state->dst;
 
 	DRM_DEBUG_DRIVER("\n");
 
 	if (!fb)
 		return 0;
 
-	/* convert src_ from 16:16 format */
-	src_x = state->src_x >> 16;
-	src_y = state->src_y >> 16;
-	src_w = state->src_w >> 16;
-	src_h = state->src_h >> 16;
+	/* convert src from 16:16 format */
+	src->x1 = state->src_x >> 16;
+	src->y1 = state->src_y >> 16;
+	src->x2 = (state->src_w >> 16) + src->x1 - 1;
+	src->y2 = (state->src_h >> 16) + src->y1 - 1;
+
+	dst->x1 = state->crtc_x;
+	dst->y1 = state->crtc_y;
+	dst->x2 = state->crtc_w + dst->x1 - 1;
+	dst->y2 = state->crtc_h + dst->y1 - 1;
 
-	/* Reject scaling */
-	if (src_w != state->crtc_w || src_h != state->crtc_h) {
-		DRM_ERROR("Scaling is not supported");
+	DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
+			 plane->base.id, fb->base.id,
+			 src->x2 - src->x1 + 1, src->y2 - src->y1 + 1,
+			 src->x1, src->y1,
+			 dst->x2 - dst->x1 + 1, dst->y2 - dst->y1 + 1,
+			 dst->x1, dst->y1);
+
+	crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+							state->crtc);
+	/* destination coordinates do not have to exceed display sizes */
+	if (crtc_state && (crtc_state->mode.hdisplay <= dst->x2 ||
+			   crtc_state->mode.vdisplay <= dst->y2))
+		return -EINVAL;
+
+	/* source sizes do not have to exceed destination sizes */
+	if (dst->x2 - dst->x1 < src->x2 - src->x1 ||
+	    dst->y2 - dst->y1 < src->y2 - src->y1)
 		return -EINVAL;
-	}
 
 	return 0;
 }
@@ -673,44 +786,36 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
 {
 	struct ltdc_device *ldev = plane_to_ltdc(plane);
 	struct drm_plane_state *state = plane->state;
+	struct drm_rect *src = &state->src;
+	struct drm_rect *dst = &state->dst;
 	struct drm_framebuffer *fb = state->fb;
 	u32 lofs = plane->index * LAY_OFS;
-	u32 x0 = state->crtc_x;
-	u32 x1 = state->crtc_x + state->crtc_w - 1;
-	u32 y0 = state->crtc_y;
-	u32 y1 = state->crtc_y + state->crtc_h - 1;
-	u32 src_x, src_y, src_w, src_h;
 	u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
 	enum ltdc_pix_fmt pf;
+	struct drm_rect dr;
 
 	if (!state->crtc || !fb) {
 		DRM_DEBUG_DRIVER("fb or crtc NULL");
 		return;
 	}
 
-	/* convert src_ from 16:16 format */
-	src_x = state->src_x >> 16;
-	src_y = state->src_y >> 16;
-	src_w = state->src_w >> 16;
-	src_h = state->src_h >> 16;
-
-	DRM_DEBUG_DRIVER("plane:%d fb:%d (%dx%d)@(%d,%d) -> (%dx%d)@(%d,%d)\n",
-			 plane->base.id, fb->base.id,
-			 src_w, src_h, src_x, src_y,
-			 state->crtc_w, state->crtc_h,
-			 state->crtc_x, state->crtc_y);
+	/* compute final coordinates of frame buffer */
+	dr.x1 = src->x1 + dst->x1;
+	dr.y1 = src->y1 + dst->y1;
+	dr.x2 = src->x2 + dst->x1;
+	dr.y2 = src->y2 + dst->y1;
 
 	bpcr = reg_read(ldev->regs, LTDC_BPCR);
 	ahbp = (bpcr & BPCR_AHBP) >> 16;
 	avbp = bpcr & BPCR_AVBP;
 
 	/* Configures the horizontal start and stop position */
-	val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
+	val = ((dr.x2 + 1 + ahbp) << 16) + (dr.x1 + 1 + ahbp);
 	reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
 			LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
 
 	/* Configures the vertical start and stop position */
-	val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
+	val = ((dr.y2 + 1 + avbp) << 16) + (dr.y1 + 1 + avbp);
 	reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
 			LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
 
@@ -730,7 +835,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
 	/* Configures the color frame buffer pitch in bytes & line length */
 	pitch_in_bytes = fb->pitches[0];
 	line_length = drm_format_plane_cpp(fb->format->format, 0) *
-		      (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
+		      (dr.x2 - dr.x1 + 1) + (ldev->caps.bus_width >> 3) - 1;
 	val = ((pitch_in_bytes << 16) | line_length);
 	reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
 			LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
@@ -753,7 +858,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
 			LXBFCR_BF2 | LXBFCR_BF1, val);
 
 	/* Configures the frame buffer line number */
-	val = y1 - y0 + 1;
+	val = dr.y2 - dr.y1 + 1;
 	reg_update_bits(ldev->regs, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val);
 
 	/* Sets the FB address */
@@ -772,11 +877,11 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
 
 	mutex_lock(&ldev->err_lock);
 	if (ldev->error_status & ISR_FUIF) {
-		DRM_DEBUG_DRIVER("Fifo underrun\n");
+		DRM_WARN("ltdc fifo underrun: please verify display mode\n");
 		ldev->error_status &= ~ISR_FUIF;
 	}
 	if (ldev->error_status & ISR_TERRIF) {
-		DRM_DEBUG_DRIVER("Transfer error\n");
+		DRM_WARN("ltdc transfer error\n");
 		ldev->error_status &= ~ISR_TERRIF;
 	}
 	mutex_unlock(&ldev->err_lock);
@@ -1055,10 +1160,10 @@ int ltdc_load(struct drm_device *ddev)
 		return -ENODEV;
 	}
 
-	if (clk_prepare_enable(ldev->pixel_clk)) {
-		DRM_ERROR("Unable to prepare pixel clock\n");
-		return -ENODEV;
-	}
+	ldev->power_on = false;
+	ret = ltdc_power_up(ldev);
+	if (ret)
+		return ret;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	ldev->regs = devm_ioremap_resource(dev, res);
@@ -1150,7 +1255,7 @@ int ltdc_load(struct drm_device *ddev)
 	for (i = 0; i < MAX_ENDPOINTS; i++)
 		drm_panel_bridge_remove(bridge[i]);
 
-	clk_disable_unprepare(ldev->pixel_clk);
+	ltdc_power_down(ldev);
 
 	return ret;
 }
@@ -1165,7 +1270,7 @@ void ltdc_unload(struct drm_device *ddev)
 	for (i = 0; i < MAX_ENDPOINTS; i++)
 		drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i);
 
-	clk_disable_unprepare(ldev->pixel_clk);
+	ltdc_power_down(ldev);
 }
 
 MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
index d5afb89..08bd69d 100644
--- a/drivers/gpu/drm/stm/ltdc.h
+++ b/drivers/gpu/drm/stm/ltdc.h
@@ -36,8 +36,14 @@ struct ltdc_device {
 	u32 error_status;
 	u32 irq_status;
 	struct fps_info plane_fpsi[LTDC_MAX_LAYER];
+	struct drm_atomic_state *suspend_state;
+	bool power_on;
 };
 
+bool ltdc_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
+			  bool in_vblank_irq, int *vpos, int *hpos,
+			  ktime_t *stime, ktime_t *etime,
+			  const struct drm_display_mode *mode);
 int ltdc_load(struct drm_device *ddev);
 void ltdc_unload(struct drm_device *ddev);
 
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
index 8d67243..c20c4c3 100644
--- a/include/uapi/drm/drm_mode.h
+++ b/include/uapi/drm/drm_mode.h
@@ -89,6 +89,12 @@ extern "C" {
 #define  DRM_MODE_FLAG_3D_TOP_AND_BOTTOM	(7<<14)
 #define  DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF	(8<<14)
 
+/* flags for polarity clock & data enable polarities */
+#define DRM_MODE_FLAG_PPIXDATA			(1 << 19)
+#define DRM_MODE_FLAG_NPIXDATA			(1 << 20)
+#define DRM_MODE_FLAG_PDE			(1 << 21)
+#define DRM_MODE_FLAG_NDE			(1 << 22)
+
 /* Picture aspect ratio options */
 #define DRM_MODE_PICTURE_ASPECT_NONE		0
 #define DRM_MODE_PICTURE_ASPECT_4_3		1
-- 
2.7.4

