diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 4359497..29ea1d1 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -388,6 +388,8 @@ source "drivers/gpu/drm/mcde/Kconfig"
 
 source "drivers/gpu/drm/tidss/Kconfig"
 
+source "drivers/gpu/drm/ipk/Kconfig"
+
 # Keep legacy drivers last
 
 menuconfig DRM_LEGACY
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index f34d08c..b15f2ea 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -124,3 +124,4 @@ obj-$(CONFIG_DRM_PANFROST) += panfrost/
 obj-$(CONFIG_DRM_ASPEED_GFX) += aspeed/
 obj-$(CONFIG_DRM_MCDE) += mcde/
 obj-$(CONFIG_DRM_TIDSS) += tidss/
+obj-$(CONFIG_DRM_IPK) += ipk/
diff --git a/drivers/gpu/drm/ipk/Kconfig b/drivers/gpu/drm/ipk/Kconfig
new file mode 100644
index 0000000..1f87444
--- /dev/null
+++ b/drivers/gpu/drm/ipk/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config DRM_IPK
+	tristate "DRM Support for Synopsys DesignWare IPK DSI"
+	depends on DRM
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL_BRIDGE
+	select VIDEOMODE_HELPERS
+	help
+	  Enable support for the Synopsys DesignWare DRM DSI.
+	  To compile this driver as a module, choose M here: the module
+	  will be called ipk-drm.
diff --git a/drivers/gpu/drm/ipk/Makefile b/drivers/gpu/drm/ipk/Makefile
new file mode 100644
index 0000000..6a1a911
--- /dev/null
+++ b/drivers/gpu/drm/ipk/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+ipk-drm-y := dw-drv.o dw-vpg.o
+
+obj-$(CONFIG_DRM_IPK) += ipk-drm.o
diff --git a/drivers/gpu/drm/ipk/dw-drv.c b/drivers/gpu/drm/ipk/dw-drv.c
new file mode 100644
index 0000000..5ff6cbf
--- /dev/null
+++ b/drivers/gpu/drm/ipk/dw-drv.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare MIPI DSI DRM driver
+ *
+ * Author: Angelo Ribeiro <angelo.ribeiro@synopsys.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+
+#include "dw-ipk.h"
+#include "dw-vpg.h"
+
+static const struct drm_mode_config_funcs dw_ipk_drm_modecfg_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static int dw_ipk_load(struct drm_device *drm)
+{
+	int ret;
+
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+
+	/* To handle orientation */
+	drm->mode_config.max_width = 2048;
+	drm->mode_config.max_height = 2048;
+
+	drm->mode_config.funcs = &dw_ipk_drm_modecfg_funcs;
+
+	/* TODO
+	 * Optional framebuffer memory resources allocation
+	 */
+
+	ret = vpg_load(drm);
+	if (ret)
+		return ret;
+
+	/* Calls all the crtc's, encoder's and connector's reset */
+	drm_mode_config_reset(drm);
+
+	return ret;
+}
+
+static void dw_ipk_unload(struct drm_device *drm)
+{
+	drm_dbg(drm, "\n");
+	vpg_unload(drm);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(ipk_drm_driver_fops);
+
+static int dw_ipk_gem_cma_dumb_create(struct drm_file *file,
+				      struct drm_device *dev,
+				      struct drm_mode_create_dumb *args)
+{
+	unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	int err;
+
+	/*
+	 * In order to optimize data transfer, pitch is aligned on
+	 * 128 bytes, height is aligned on 4 bytes
+	 */
+	args->pitch = roundup(min_pitch, 128);
+	args->height = roundup(args->height, 4);
+
+	err = drm_gem_cma_dumb_create_internal(file, dev, args);
+	if (err)
+		drm_err(dev, "dumb_create failed %d\n", err);
+
+	return err;
+}
+
+static struct drm_driver dw_ipk_drm_driver = {
+	.gem_create_object = drm_cma_gem_create_object_default_funcs,
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.dumb_create = dw_ipk_gem_cma_dumb_create,
+
+	.major = 1,
+	.minor = 0,
+	.patchlevel = 0,
+	.name = "dw_ipk",
+	.desc = "DW IPK DSI Host Controller",
+	.date = "20190725",
+
+	.driver_features = DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
+
+	.fops = &ipk_drm_driver_fops,
+};
+
+static int dw_ipk_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct drm_device *drm;
+	struct ipk_device *ipk;
+	int ret;
+
+	ipk = devm_drm_dev_alloc(dev, &dw_ipk_drm_driver,
+				 struct ipk_device, drm);
+	if (IS_ERR(ipk))
+		return PTR_ERR(ipk);
+
+	ipk->platform = pdev;
+	drm = &ipk->drm;
+
+	platform_set_drvdata(pdev, drm);
+
+	ret = dw_ipk_load(drm);
+	if (ret)
+		return ret;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret)
+		return ret;
+
+	drm_fbdev_generic_setup(drm, 24);
+
+	return ret;
+}
+
+static int dw_ipk_drm_platform_remove(struct platform_device *pdev)
+{
+	struct drm_device *drm = platform_get_drvdata(pdev);
+
+	drm_dev_unregister(drm);
+	dw_ipk_unload(drm);
+
+	return 0;
+}
+
+static const struct of_device_id dw_ipk_dt_ids[] = {
+	{.compatible = "snps,dw-ipk-vpg"},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, dw_ipk_dt_ids);
+
+static struct platform_driver dw_ipk_drm_platform_driver = {
+	.probe = dw_ipk_drm_platform_probe,
+	.remove = dw_ipk_drm_platform_remove,
+	.driver = {
+		.name = "dw-ipk-drm",
+		.of_match_table = dw_ipk_dt_ids,
+	},
+};
+
+module_platform_driver(dw_ipk_drm_platform_driver);
+
+MODULE_DESCRIPTION("Synopsys DesignWare IPK DRM driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Angelo Ribeiro <angelo.ribeiro@synopsys.com>");
diff --git a/drivers/gpu/drm/ipk/dw-ipk.h b/drivers/gpu/drm/ipk/dw-ipk.h
new file mode 100644
index 0000000..8d3fd12
--- /dev/null
+++ b/drivers/gpu/drm/ipk/dw-ipk.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare IPK MIPI DSI DRM Driver
+ */
+
+#ifndef _DW_IPK_H
+#define _DW_IPK_H
+
+#include <drm/drm_simple_kms_helper.h>
+
+struct vpg_device;
+
+struct ipk_device {
+	struct drm_device drm;
+	struct platform_device *platform;
+	struct drm_simple_display_pipe pipe;
+	struct vpg_device *vpg;
+};
+
+#define drm_dev_to_ipk_dev(target) \
+	container_of(target, struct ipk_device, drm)
+#define display_pipe_to_ipk_dev(target) \
+	container_of(target, struct ipk_device, pipe)
+
+#endif /* _DW_IPK_H */
diff --git a/drivers/gpu/drm/ipk/dw-vpg.c b/drivers/gpu/drm/ipk/dw-vpg.c
new file mode 100644
index 0000000..b7575fa
--- /dev/null
+++ b/drivers/gpu/drm/ipk/dw-vpg.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare MIPI DSI controller
+ *
+ * Author: Angelo Ribeiro <angelor@synopsys.com>
+ * Author: Luis Oliveira <luis.oliveira@synopsys.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_graph.h>
+#include <linux/platform_data/simplefb.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <video/videomode.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "dw-ipk.h"
+#include "dw-vpg.h"
+
+struct vpg_device {
+	void __iomem *base;
+	void __iomem *base_mmcm;
+};
+
+enum vpg_pixel_fmt {
+	VIF_COLOR_CODE_16BIT_CONFIG1 = 0,
+	VIF_COLOR_CODE_16BIT_CONFIG2,
+	VIF_COLOR_CODE_16BIT_CONFIG3,
+	VIF_COLOR_CODE_18BIT_CONFIG1,
+	VIF_COLOR_CODE_18BIT_CONFIG2,
+	VIF_COLOR_CODE_24BIT,
+	VIF_COLOR_CODE_MAX
+};
+
+static enum vpg_pixel_fmt to_vpg_pixel_format(u32 drm_fmt)
+{
+	enum vpg_pixel_fmt pf;
+
+	switch (drm_fmt) {
+	case DRM_FORMAT_RGB888:
+		pf = VIF_COLOR_CODE_24BIT;
+		break;
+	case DRM_FORMAT_RGB565:
+		pf = VIF_COLOR_CODE_16BIT_CONFIG1;
+		break;
+	default:
+		pf = VIF_COLOR_CODE_MAX;
+		break;
+	}
+	return pf;
+}
+
+static u32 vpg_read(struct vpg_device *dev, u32 reg)
+{
+	return readl(dev->base + reg);
+}
+
+static void vpg_write(struct vpg_device *dev, u32 reg, u32 val)
+{
+	writel(val, dev->base + reg);
+}
+
+static void vpg_write_part(struct vpg_device *dev, u32 reg,
+			   u32 val, u8 shift, u8 width)
+{
+	u32 mask = (1 << width) - 1;
+
+	vpg_write(dev, reg, (vpg_read(dev, reg) &
+		  ~(mask << shift)) | ((val & mask) << shift));
+}
+
+static u32 mmcm_read(struct vpg_device *dev, u32 reg)
+{
+	return readl(dev->base_mmcm + reg);
+}
+
+static void mmcm_write(struct vpg_device *dev, u32 reg, u32 value)
+{
+	writel(value, dev->base_mmcm + reg);
+}
+
+static void mmcm_configure(struct drm_device *drm,
+			   struct drm_display_mode *mode)
+{
+	struct ipk_device *ipk = drm_dev_to_ipk_dev(drm);
+	int div,  mul, cur_freq, cur_deviat, temp;
+	struct vpg_device *vpg = ipk->vpg;
+	int out_freq = mode->clock;
+	int in_freq = 100000;
+	int deviat = 1000;	/* Deviation from desired master clock */
+	int best_div = 0;	/* Divider for PLL */
+	int best_mul = 0;	/* Multiplier for PLL */
+
+	drm_dbg(drm, "out_freq = %d\n", out_freq);
+
+	for (div = 1; div < 150 && deviat > 50; div++) {
+		for (mul = 1; mul <= 10 && deviat > 50; mul++) {
+			cur_freq = in_freq * mul / div;
+			cur_deviat = abs(out_freq - cur_freq);
+			if (cur_deviat < deviat) {
+				best_div = div;
+				best_mul = mul;
+				deviat = cur_deviat;
+			}
+		}
+	}
+
+	drm_dbg(drm, "deviat = %d\n best_div = %d\n best_mul = %d\n",
+		deviat, best_div, best_mul);
+
+	temp = mmcm_read(vpg, DW_MMCM_CLKOUT0_REG_1) & DW_MMCM_MASK;
+	mmcm_write(vpg, DW_MMCM_CLKOUT0_REG_1, temp | (best_div << 6) |
+		   best_div);
+
+	temp = mmcm_read(vpg, DW_MMCM_CLKFBOUT_REG_1) & DW_MMCM_MASK;
+	mmcm_write(vpg, DW_MMCM_CLKFBOUT_REG_1, temp | (best_mul << 6) |
+		   best_mul);
+}
+
+static void vpg_set_fps(struct vpg_device *vpg,
+			const struct drm_display_mode *mode)
+{
+	/* DW_VPG_FREQ is in KHz */
+	u32 line_time = (1000 * DW_VPG_FREQ) / (mode->vrefresh * mode->vtotal);
+
+	vpg_write(vpg, DW_VPG_LINE_TIME, line_time);
+}
+
+static void vpg_select_pattern(struct vpg_device *vpg, unsigned int value)
+{
+	vpg_write(vpg, DW_VPG_TESTMODE, value);
+}
+
+static void vpg_vertical_set(struct vpg_device *vpg,
+			     struct drm_display_mode *mode)
+{
+	u32 v_sync = mode->vsync_end - mode->vsync_start;
+	u32 v_back_porch = mode->vtotal - mode->vsync_end;
+	u32 v_front_porch = mode->vsync_start - mode->vdisplay;
+
+	vpg_write_part(vpg, DW_VPG_CANVAS, mode->vdisplay, 16, 16);
+	vpg_write_part(vpg, DW_VPG_VBP_VFP_VSA, v_sync, 0, 8);
+	vpg_write_part(vpg, DW_VPG_VBP_VFP_VSA, v_back_porch, 20, 12);
+	vpg_write_part(vpg, DW_VPG_VBP_VFP_VSA, v_front_porch, 8, 11);
+}
+
+static void vpg_horizontal_set(struct vpg_device *vpg,
+			       struct drm_display_mode *mode)
+{
+	u32 h_sync = mode->hsync_end - mode->hsync_start;
+	u32 h_back_porch = mode->htotal - mode->hsync_end;
+
+	vpg_write_part(vpg, DW_VPG_CANVAS, mode->hdisplay, 0, 16);
+	vpg_write_part(vpg, DW_VPG_HBP_HSA, h_sync, 0, 16);
+	vpg_write_part(vpg, DW_VPG_HBP_HSA, h_back_porch, 16, 16);
+}
+
+static ssize_t show_pattern(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+	struct ipk_device *ipk = drm_dev_to_ipk_dev(drm);
+	struct vpg_device *vpg = ipk->vpg;
+
+	return sprintf(buf, "%d\n", vpg_read(vpg, DW_VPG_TESTMODE));
+}
+
+static ssize_t store_pattern(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct drm_device *drm = dev_get_drvdata(dev);
+	struct ipk_device *ipk = drm_dev_to_ipk_dev(drm);
+	struct vpg_device *vpg = ipk->vpg;
+	long pattern;
+	int ret;
+
+	ret = kstrtol(buf, 0, &pattern);
+	if (ret < 0)
+		return ret;
+
+	vpg_select_pattern(vpg, pattern);
+
+	return count;
+}
+
+static struct kobj_attribute vpg_pattern = __ATTR(pattern, 0660,
+						  (void *)show_pattern,
+						  (void *)store_pattern);
+
+static struct attribute *vpg_attr[] = {
+	&vpg_pattern.attr,
+	NULL,
+};
+
+static const struct attribute_group vpg_attr_group = {
+	.attrs = vpg_attr,
+};
+
+static int vpg_sysfs_register(struct ipk_device *ipk)
+{
+	struct platform_device *pdev = ipk->platform;
+	int ret;
+
+	ret = sysfs_create_group(&pdev->dev.kobj, &vpg_attr_group);
+	if (ret)
+		drm_err(&ipk->drm, "Failed to create sysfs entries");
+
+	return ret;
+}
+
+static void vpg_sysfs_remove(struct ipk_device *ipk)
+{
+	struct platform_device *pdev = ipk->platform;
+
+	sysfs_remove_group(&pdev->dev.kobj, &vpg_attr_group);
+}
+
+static void vpg_pipe_update(struct drm_simple_display_pipe *pipe,
+			    struct drm_plane_state *old_state)
+{
+	struct ipk_device *ipk = display_pipe_to_ipk_dev(pipe);
+	struct drm_plane_state *state = pipe->plane.state;
+	struct drm_framebuffer *fb = state->fb;
+	struct vpg_device *vpg = ipk->vpg;
+	enum vpg_pixel_fmt pixel_format;
+
+	if (!fb)
+		return;
+
+	pixel_format = to_vpg_pixel_format(fb->format->format);
+
+	vpg_write(vpg, DW_VPG_DPICOLORMODE, pixel_format);
+}
+
+static void vpg_pipe_enable(struct drm_simple_display_pipe *pipe,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_plane_state *plane_state)
+{
+	struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+	struct ipk_device *ipk = display_pipe_to_ipk_dev(pipe);
+	struct drm_device *drm = &ipk->drm;
+	struct vpg_device *vpg = ipk->vpg;
+
+	drm_dbg(drm, "Video mode: %dx%d\n", mode->hdisplay, mode->vdisplay);
+
+	mmcm_configure(drm, mode);
+
+	vpg_write(vpg, DW_VPG_SEL_DATA, 0x01);
+	vpg_write(vpg, DW_VPG_SEL_DATA, 0x03);
+
+	vpg_horizontal_set(vpg, mode);
+
+	vpg_vertical_set(vpg, mode);
+
+	vpg_set_fps(vpg, mode);
+
+	vpg_select_pattern(vpg, 0);
+}
+
+static const struct drm_simple_display_pipe_funcs drm_display_pipe = {
+	.enable = vpg_pipe_enable,
+	.update = vpg_pipe_update,
+};
+
+static int vpg_init_pipe(struct drm_device *drm, struct drm_panel *panel,
+			 struct drm_bridge *bridge)
+{
+	struct ipk_device *ipk = drm_dev_to_ipk_dev(drm);
+	static const u64 modifiers[] = {
+		DRM_FORMAT_MOD_LINEAR,
+		DRM_FORMAT_MOD_INVALID,
+	};
+	static const u32 formats[] = {
+		DRM_FORMAT_RGB565,
+		DRM_FORMAT_RGB888,
+	};
+	int ret;
+
+	ret = drm_simple_display_pipe_init(drm, &ipk->pipe, &drm_display_pipe,
+					   formats, ARRAY_SIZE(formats),
+					   modifiers, NULL);
+	if (ret)
+		return ret;
+
+	if (panel) {
+		bridge = devm_drm_panel_bridge_add_typed(&ipk->platform->dev,
+						 panel, DRM_MODE_CONNECTOR_DSI);
+		if (IS_ERR(bridge))
+			return PTR_ERR(bridge);
+	}
+
+	ret = drm_simple_display_pipe_attach_bridge(&ipk->pipe, bridge);
+	if (ret)
+		return ret;
+
+	ret = vpg_sysfs_register(ipk);
+
+	return ret;
+}
+
+int vpg_load(struct drm_device *drm)
+{
+	struct ipk_device *ipk = drm_dev_to_ipk_dev(drm);
+	struct platform_device *pdev = ipk->platform;
+	struct device_node *np = drm->dev->of_node;
+	int ret, endpoint_not_ready = -ENODEV;
+	struct reset_control *vpg_rst, *mmcm_rst;
+	struct drm_bridge *bridge = NULL;
+	struct drm_panel *panel = NULL;
+	struct device *dev = drm->dev;
+	struct vpg_device *vpg;
+	struct resource *res;
+
+	drm_dbg(drm, "\n");
+
+	vpg = devm_kzalloc(&pdev->dev, sizeof(*vpg), GFP_KERNEL);
+	if (!vpg)
+		return -ENOMEM;
+
+	ipk->vpg = vpg;
+
+	/* Get endpoints if any */
+	ret = drm_of_find_panel_or_bridge(np, 0, 0, &panel, &bridge);
+	if (ret == -EPROBE_DEFER)
+		return ret;
+	else if (!ret)
+		endpoint_not_ready = 0;
+
+	if (endpoint_not_ready)
+		return endpoint_not_ready;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpg");
+	vpg->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vpg->base)) {
+		drm_err(drm, "Unable to get VPG registers\n");
+		ret = PTR_ERR(vpg->base);
+		goto err;
+	}
+
+	vpg_rst = devm_reset_control_get_optional_exclusive(dev, "vpg");
+	if (IS_ERR(vpg_rst)) {
+		ret = PTR_ERR(vpg_rst);
+		if (ret != -EPROBE_DEFER)
+			drm_err(drm, "Unable to get reset control: %d\n", ret);
+		goto err;
+	}
+
+	if (vpg_rst) {
+		reset_control_assert(vpg_rst);
+		usleep_range(10, 20);
+		reset_control_deassert(vpg_rst);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmcm");
+	vpg->base_mmcm = devm_ioremap_resource(dev, res);
+	if (IS_ERR(vpg->base)) {
+		ret = PTR_ERR(vpg->base);
+		goto err;
+	}
+
+	mmcm_rst = devm_reset_control_get_optional_exclusive(dev, "mmcm");
+	if (IS_ERR(mmcm_rst)) {
+		ret = PTR_ERR(mmcm_rst);
+		if (ret != -EPROBE_DEFER)
+			drm_err(drm, "Unable to get reset control: %d\n", ret);
+		goto err;
+	}
+
+	if (mmcm_rst) {
+		reset_control_assert(mmcm_rst);
+		usleep_range(10, 20);
+		reset_control_deassert(mmcm_rst);
+	}
+
+	/* Init DRM Simple Pipeline */
+	ret = vpg_init_pipe(drm, panel, bridge);
+	if (ret) {
+		drm_err(drm, "Failed to init simple pipe\n");
+		goto err;
+	}
+
+	return ret;
+
+err:
+	drm_panel_bridge_remove(bridge);
+	return ret;
+}
+
+void vpg_unload(struct drm_device *drm)
+{
+	struct ipk_device *ipk = drm_dev_to_ipk_dev(drm);
+
+	drm_dbg(drm, "\n");
+	vpg_sysfs_remove(ipk);
+	drm_of_panel_bridge_remove(drm->dev->of_node, 0, 0);
+}
diff --git a/drivers/gpu/drm/ipk/dw-vpg.h b/drivers/gpu/drm/ipk/dw-vpg.h
new file mode 100644
index 0000000..bd1969e
--- /dev/null
+++ b/drivers/gpu/drm/ipk/dw-vpg.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2020 Synopsys, Inc. and/or its affiliates.
+ * Synopsys DesignWare VPG
+ */
+
+#ifndef _VPG_H_
+#define _VPG_H_
+
+#define DW_VPG_PANX				0x00
+#define DW_VPG_PANY				0x04
+#define DW_VPG_PHASE_SEL			0x08
+#define DW_VPG_SEL_DATA				0x0C
+#define DW_VPG_SWAP_BAY				0x10
+#define DW_VPG_TESTMODE				0x14
+#define DW_VPG_ZOOM_OUT				0x18
+#define DW_VPG_EDPICTRL				0x1C
+#define DW_VPG_CANVAS				0x20
+#define DW_VPG_HBP_HSA				0x24
+#define DW_VPG_LINE_TIME			0x28
+#define DW_VPG_VBP_VFP_VSA			0x2C
+#define DW_VPG_DPICOLORMODE			0x30
+#define DW_VPG_VERSION				0x34
+#define DW_VPG_EXTRNAL_EDPI			0x38
+#define DW_VPG_PVO_CEAVID			0x3C
+#define DW_VPG_PAN_VALID			0x40
+#define DW_VPG_TECONTROL			0x44
+#define DW_VPG_IMAGE_START_POSITION_X		0x4C
+#define DW_VPG_IMAGE_START_POSITION_Y		0x50
+#define DW_VPG_IMAGE_Y				0x54
+#define DW_VPG_IMAGE_WORD_COUNT			0x58
+#define DW_VPG_IMAGE_DATA_TYPE			0x5C
+#define DW_VPG_LINE_PIXS_CNT			0x60
+#define DW_VPG_FRAME_LINES_CNT			0x64
+
+#define DW_MMCM_MASK				0x1000
+#define DW_MMCM_CLKOUT0_REG_1			0x20
+#define DW_MMCM_CLKOUT0_REG_2			0x24
+#define DW_MMCM_CLKFBOUT_REG_1			0x50
+#define DW_MMCM_CLKFBOUT_REG_2			0x54
+#define DW_MMCM_POWER_REG			0xA0
+
+#define DW_VPG_FREQ	25000	/* [KHz] */
+
+int vpg_load(struct drm_device *drm);
+void vpg_unload(struct drm_device *drm);
+
+#endif /* _VPG_H_ */
