diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 2a141ad4145e451ddcf2cf602e1e5d9bb01ad40b..99ba230d89f3c32132e985f65f100b4197482ed4 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -90,6 +90,7 @@ config DRM_IMX_SEC_DSIM
 	  Choose this to enable the internal SEC MIPI DSIM controller
 	  found on i.MX platform.
 
+source "drivers/gpu/drm/imx/dcnano/Kconfig"
 source "drivers/gpu/drm/imx/dpu/Kconfig"
 source "drivers/gpu/drm/imx/dcss/Kconfig"
 source "drivers/gpu/drm/imx/mhdp/Kconfig"
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index 82aaa99b036c64b9d4514a26cf77ff3d9575c5c4..5aad8b074155a8291d5abf026d9776c65f9d6dfb 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o
 obj-$(CONFIG_DRM_IMX8QXP_LDB) += imx8qxp-ldb.o
 obj-$(CONFIG_DRM_IMX8MP_LDB) += imx8mp-ldb.o
 
+obj-$(CONFIG_DRM_IMX_DCNANO) += dcnano/
 obj-$(CONFIG_DRM_IMX_DPU) += dpu/
 obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3/
 obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
diff --git a/drivers/gpu/drm/imx/dcnano/Kconfig b/drivers/gpu/drm/imx/dcnano/Kconfig
new file mode 100644
index 0000000000000000000000000000000000000000..ec3ba3ad7b00c8737a57ec009e29dddd942a2e24
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/Kconfig
@@ -0,0 +1,10 @@
+config DRM_IMX_DCNANO
+	tristate "DRM support for NXP i.MX DCNANO Graphics"
+	select DRM_KMS_HELPER
+	select VIDEOMODE_HELPERS
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	depends on DRM && OF && ARCH_MXC
+	depends on COMMON_CLK
+	help
+	  enable NXP i.MX DCNANO graphics support
diff --git a/drivers/gpu/drm/imx/dcnano/Makefile b/drivers/gpu/drm/imx/dcnano/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..b08d6712861fdd4d7b4abf7399e17581d1c5f157
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+imx-dcnano-drm-objs := dcnano-crtc.o dcnano-drv.o dcnano-kms.o dcnano-plane.o
+
+obj-$(CONFIG_DRM_IMX_DCNANO) += imx-dcnano-drm.o
diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-crtc.c b/drivers/gpu/drm/imx/dcnano/dcnano-crtc.c
new file mode 100644
index 0000000000000000000000000000000000000000..f6018b62a9f84fbf73e14c138743d7c5f418e79e
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/dcnano-crtc.c
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020,2021 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/irq.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+
+#include "dcnano-drv.h"
+#include "dcnano-reg.h"
+
+#define DCNANO_CRTC_PLL_MIN_RATE	271500000
+#define DCNANO_CRTC_PLL_MAX_RATE	792000000
+
+#define DCNANO_CRTC_PLL_MIN_DIV		1
+#define DCNANO_CRTC_PLL_MAX_DIV		64
+
+#define dcnano_crtc_dbg(crtc, fmt, ...)					\
+	drm_dbg_kms((crtc)->dev, "[CRTC:%d:%s] " fmt,			\
+		    (crtc)->base.id, (crtc)->name, ##__VA_ARGS__)
+
+#define dcnano_crtc_err(crtc, fmt, ...)					\
+	drm_err((crtc)->dev, "[CRTC:%d:%s] " fmt,			\
+		(crtc)->base.id, (crtc)->name, ##__VA_ARGS__)
+
+static inline struct dcnano_dev *crtc_to_dcnano_dev(struct drm_crtc *crtc)
+{
+	return to_dcnano_dev(crtc->dev);
+}
+
+static void dcnano_crtc_mode_set_nofb_dpi(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+	struct drm_display_mode *adj = &crtc->state->adjusted_mode;
+	u32 val;
+
+	/* select output bus */
+	dcnano_write(dcnano, DCNANO_DBICONFIG, DBICFG_BUS_OUTPUT_SEL_DPI);
+
+	/* set bus format */
+	dcnano_write(dcnano, DCNANO_DPICONFIG, DPICFG_DATA_FORMAT_D24);
+
+	/* horizontal timing */
+	val = HDISPLAY_END(adj->crtc_hdisplay) |
+	      HDISPLAY_TOTAL(adj->crtc_htotal);
+	dcnano_write(dcnano, DCNANO_HDISPLAY, val);
+
+	val = HSYNC_START(adj->crtc_hsync_start) |
+	      HSYNC_END(adj->crtc_hsync_end) | HSYNC_PULSE_ENABLE;
+	if (adj->flags & DRM_MODE_FLAG_PHSYNC)
+		val |= HSYNC_POL_POSITIVE;
+	else
+		val |= HSYNC_POL_NEGATIVE;
+	dcnano_write(dcnano, DCNANO_HSYNC, val);
+
+	/* vertical timing */
+	val = VDISPLAY_END(adj->crtc_vdisplay) |
+	      VDISPLAY_TOTAL(adj->crtc_vtotal);
+	dcnano_write(dcnano, DCNANO_VDISPLAY, val);
+
+	val = VSYNC_START(adj->crtc_vsync_start) |
+	      VSYNC_END(adj->crtc_vsync_end) | VSYNC_PULSE_ENABLE;
+	if (adj->flags & DRM_MODE_FLAG_PVSYNC)
+		val |= VSYNC_POL_POSITIVE;
+	else
+		val |= VSYNC_POL_NEGATIVE;
+	dcnano_write(dcnano, DCNANO_VSYNC, val);
+
+	/* panel configuration */
+	val = PANELCFG_DE_ENABLE | PANELCFG_DE_POL_POSITIVE |
+	      PANELCFG_DATA_ENABLE | PANELCFG_DATA_POL_POSITIVE |
+	      PANELCFG_CLOCK_ENABLE | PANELCFG_CLOCK_POL_POSITIVE |
+	      PANELCFG_SEQUENCING_SOFTWARE;
+	dcnano_write(dcnano, DCNANO_PANELCONFIG, val);
+}
+
+static bool dcnano_crtc_pll_clock_rate_is_valid(unsigned long pll_clk_rate)
+{
+	return pll_clk_rate >= DCNANO_CRTC_PLL_MIN_RATE &&
+	       pll_clk_rate <= DCNANO_CRTC_PLL_MAX_RATE;
+}
+
+static unsigned long
+dcnano_crtc_find_pll_clock_rate(struct drm_crtc *crtc,
+				const struct drm_display_mode *mode)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+	unsigned long pll_clk_rate, rounded_pll_clk_rate;
+	int i;
+
+	for (i = DCNANO_CRTC_PLL_MIN_DIV; i <= DCNANO_CRTC_PLL_MAX_DIV; i++) {
+		pll_clk_rate = mode->clock * 1000 * i;
+
+		if (!dcnano_crtc_pll_clock_rate_is_valid(pll_clk_rate))
+			continue;
+
+		rounded_pll_clk_rate = clk_round_rate(dcnano->pll_clk,
+						      pll_clk_rate);
+		if (rounded_pll_clk_rate != pll_clk_rate) {
+			dcnano_crtc_dbg(crtc,
+					"rounded pll clock rate %lu, expected %lu\n",
+					rounded_pll_clk_rate, pll_clk_rate);
+			continue;
+		}
+
+		dcnano_crtc_dbg(crtc, "find pll clock rate %lu with div %d\n",
+				pll_clk_rate, i);
+
+		return pll_clk_rate;
+	}
+
+	dcnano_crtc_dbg(crtc, "failed to find pll clock rate\n");
+
+	return 0;
+}
+
+static void dcnano_crtc_set_pixel_clock(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+	struct drm_display_mode *adj = &crtc->state->adjusted_mode;
+	struct clk *parent;
+	unsigned long pixel_clk_rate = adj->crtc_clock * 1000;
+	unsigned long pll_clk_rate;
+	int ret;
+
+	parent = clk_get_parent(dcnano->pixel_clk);
+	if (!parent) {
+		dcnano_crtc_err(crtc, "%s: no pixel clock's parent\n", __func__);
+	} else if (IS_ERR(parent)) {
+		ret = PTR_ERR(parent);
+		dcnano_crtc_err(crtc,
+				"%s: failed to get pixel clock's parent: %d\n",
+				__func__, ret);
+		return;
+	}
+
+	pll_clk_rate = dcnano_crtc_find_pll_clock_rate(crtc, adj);
+	if (pll_clk_rate == 0)
+		dcnano_crtc_err(crtc, "%s: failed to find pll clock rate\n",
+				__func__);
+
+	ret = clk_set_rate(dcnano->pll_clk, pll_clk_rate);
+	if (ret)
+		dcnano_crtc_err(crtc, "%s: failed to set pll clock rate: %d\n",
+				__func__, ret);
+
+	/* FIXME: The rate of pixel clock's parent is pixel clock rate. */
+	ret = clk_set_rate(parent, pixel_clk_rate);
+	if (ret)
+		dcnano_crtc_err(crtc,
+				"%s: failed to set pixel clock's parent rate: %d\n",
+				__func__, ret);
+
+	ret = clk_set_rate(dcnano->pixel_clk, pixel_clk_rate);
+	if (ret)
+		dcnano_crtc_err(crtc, "%s: failed to set pixel clock rate: %d\n",
+				__func__, ret);
+
+	ret = clk_prepare_enable(dcnano->pixel_clk);
+	if (ret)
+		dcnano_crtc_err(crtc, "%s: failed to enable pixel clock: %d\n",
+				__func__, ret);
+
+	dcnano_crtc_dbg(crtc, "%s: get pll clock rate: %lu\n",
+			__func__, clk_get_rate(dcnano->pll_clk));
+
+	dcnano_crtc_dbg(crtc, "%s: get rate of pixel clock's parent: %lu\n",
+			__func__, clk_get_rate(parent));
+
+	dcnano_crtc_dbg(crtc, "%s: get pixel clock rate %lu\n",
+			__func__, clk_get_rate(dcnano->pixel_clk));
+}
+
+static enum drm_mode_status
+dcnano_crtc_mode_valid(struct drm_crtc *crtc,
+		       const struct drm_display_mode *mode)
+{
+	dcnano_crtc_dbg(crtc, "validating mode " DRM_MODE_FMT "\n",
+			DRM_MODE_ARG(mode));
+
+	if (dcnano_crtc_find_pll_clock_rate(crtc, mode) == 0)
+		return MODE_NOCLOCK;
+
+	return MODE_OK;
+}
+
+static void dcnano_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+	struct drm_display_mode *adj = &crtc->state->adjusted_mode;
+
+	dcnano_crtc_dbg(crtc, "mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(adj));
+
+	clk_prepare_enable(dcnano->axi_clk);
+	clk_prepare_enable(dcnano->ahb_clk);
+
+	dcnano_crtc_set_pixel_clock(crtc);
+
+	if (dcnano->port == DCNANO_DPI_PORT)
+		dcnano_crtc_mode_set_nofb_dpi(crtc);
+}
+
+static void dcnano_crtc_queue_state_event(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+	if (crtc->state->event) {
+		WARN_ON(drm_crtc_vblank_get(crtc));
+		WARN_ON(dcnano->event);
+		dcnano->event = crtc->state->event;
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static int dcnano_crtc_atomic_check(struct drm_crtc *crtc,
+				    struct drm_crtc_state *state)
+{
+	bool has_primary = state->plane_mask & drm_plane_mask(crtc->primary);
+
+	if (state->active && !has_primary)
+		return -EINVAL;
+
+	if (state->active_changed && state->active)
+		state->mode_changed = true;
+
+	return 0;
+}
+
+static void dcnano_crtc_atomic_begin(struct drm_crtc *crtc,
+				     struct drm_crtc_state *old_crtc_state)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	/*
+	 * Enable clocks by ourselves in case the plane callbacks
+	 * need to access registers.
+	 */
+	clk_prepare_enable(dcnano->ahb_clk);
+	clk_prepare_enable(dcnano->pixel_clk);
+}
+
+static void dcnano_crtc_atomic_flush(struct drm_crtc *crtc,
+				     struct drm_crtc_state *old_crtc_state)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+
+	if (!crtc->state->active && !old_crtc_state->active)
+		return;
+
+	if (!drm_atomic_crtc_needs_modeset(crtc->state))
+		dcnano_crtc_queue_state_event(crtc);
+}
+
+static void dcnano_crtc_atomic_enable(struct drm_crtc *crtc,
+				      struct drm_crtc_state *old_crtc_state)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+	struct drm_plane *plane;
+	struct drm_plane_state *new_plane_state;
+	int i;
+	u32 primary_fb_fmt = 0;
+	u32 val;
+
+	drm_crtc_vblank_on(crtc);
+
+	for_each_new_plane_in_state(old_crtc_state->state,
+				    plane, new_plane_state, i) {
+		if (!new_plane_state->fb)
+			continue;
+
+		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
+			continue;
+
+		switch (new_plane_state->fb->format->format) {
+		case DRM_FORMAT_RGB565:
+			primary_fb_fmt = FBCFG_FORMAT_R5G6B5;
+			break;
+		case DRM_FORMAT_XRGB8888:
+			primary_fb_fmt = FBCFG_FORMAT_R8G8B8;
+			break;
+		}
+	}
+
+	val = FBCFG_OUTPUT_ENABLE | primary_fb_fmt;
+
+	/* enable DPI timing and start a DPI transfer, if needed */
+	if (dcnano->port == DCNANO_DPI_PORT)
+		val |= FBCFG_RESET_ENABLE;
+
+	dcnano_write(dcnano, DCNANO_FRAMEBUFFERCONFIG, val);
+
+	dcnano_crtc_queue_state_event(crtc);
+}
+
+static void dcnano_crtc_atomic_disable(struct drm_crtc *crtc,
+				       struct drm_crtc_state *old_crtc_state)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	/* simply write '0' to the framebuffer and timing control register */
+	dcnano_write(dcnano, DCNANO_FRAMEBUFFERCONFIG, 0);
+
+	drm_crtc_vblank_off(crtc);
+
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+	clk_disable_unprepare(dcnano->axi_clk);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+	if (crtc->state->event && !crtc->state->active) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&crtc->dev->event_lock);
+}
+
+static bool
+dcnano_crtc_get_scanout_position(struct drm_crtc *crtc,
+				 bool in_vblank_irq,
+				 int *vpos, int *hpos,
+				 ktime_t *stime, ktime_t *etime,
+				 const struct drm_display_mode *mode)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+	int hdisplay = mode->crtc_hdisplay;
+	int htotal = mode->crtc_htotal;
+	int vdisplay = mode->crtc_vdisplay;
+	int vtotal = mode->crtc_vtotal;
+	int x, y;
+	u32 val;
+	bool reliable;
+
+	if (stime)
+		*stime = ktime_get();
+
+	val = dcnano_read(dcnano, DCNANO_DISPLAYCURRENTLOCATION);
+
+	x = CURRENTLOCATION_X_GET(val);
+	y = CURRENTLOCATION_Y_GET(val);
+
+	if (x < hdisplay)
+		*hpos = x + 1; /* active scanout area - positive */
+	else
+		*hpos = x - (htotal - 1); /* inside vblank - negative */
+
+	if (y < vdisplay)
+		*vpos = y + 1; /* active scanout area - positive */
+	else
+		*vpos = y - (vtotal - 1); /* inside vblank - negative */
+
+	reliable = true;
+
+	if (etime)
+		*etime = ktime_get();
+
+	return reliable;
+}
+
+static const struct drm_crtc_helper_funcs dcnano_crtc_helper_funcs = {
+	.mode_valid		= dcnano_crtc_mode_valid,
+	.mode_set_nofb		= dcnano_crtc_mode_set_nofb,
+	.atomic_check		= dcnano_crtc_atomic_check,
+	.atomic_begin		= dcnano_crtc_atomic_begin,
+	.atomic_flush		= dcnano_crtc_atomic_flush,
+	.atomic_enable		= dcnano_crtc_atomic_enable,
+	.atomic_disable		= dcnano_crtc_atomic_disable,
+	.get_scanout_position	= dcnano_crtc_get_scanout_position,
+};
+
+static u32 dcnano_crtc_get_vblank_counter(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	dcnano_write(dcnano, DCNANO_DEBUGCOUNTERSELECT, TOTAL_FRAME_CNT);
+	return dcnano_read(dcnano, DCNANO_DEBUGCOUNTERVALUE);
+}
+
+static int dcnano_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	dcnano_write(dcnano, DCNANO_DISPLAYINTRENABLE, DISPLAYINTR_DISP0);
+
+	return 0;
+}
+
+static void dcnano_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct dcnano_dev *dcnano = crtc_to_dcnano_dev(crtc);
+
+	dcnano_write(dcnano, DCNANO_DISPLAYINTRENABLE, 0);
+}
+
+static const struct drm_crtc_funcs dcnano_crtc_funcs = {
+	.reset			= drm_atomic_helper_crtc_reset,
+	.destroy		= drm_crtc_cleanup,
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
+	.get_vblank_counter	= dcnano_crtc_get_vblank_counter,
+	.enable_vblank		= dcnano_crtc_enable_vblank,
+	.disable_vblank		= dcnano_crtc_disable_vblank,
+	.get_vblank_timestamp	= drm_crtc_vblank_helper_get_vblank_timestamp,
+};
+
+irqreturn_t dcnano_irq_handler(int irq, void *data)
+{
+	struct drm_device *drm = data;
+	struct dcnano_dev *dcnano = to_dcnano_dev(drm);
+	unsigned long flags;
+
+	/* DCNANO_DISPLAYINTR will automatically clear after a read. */
+	dcnano_read(dcnano, DCNANO_DISPLAYINTR);
+
+	drm_crtc_handle_vblank(&dcnano->crtc);
+
+	spin_lock_irqsave(&drm->event_lock, flags);
+	if (dcnano->event) {
+		drm_crtc_send_vblank_event(&dcnano->crtc, dcnano->event);
+		dcnano->event = NULL;
+		drm_crtc_vblank_put(&dcnano->crtc);
+	}
+	spin_unlock_irqrestore(&drm->event_lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int dcnano_get_pll_clock(struct dcnano_dev *dcnano)
+{
+	int ret, i;
+
+	/*
+	 * There are one divider clock and gate clock between the pixel
+	 * clock and the pll clock, so walk through the clock tree 3 levels
+	 * up to get the pll clock.
+	 */
+	dcnano->pll_clk = dcnano->pixel_clk;
+	for (i = 0; i < 3; i++) {
+		dcnano->pll_clk = clk_get_parent(dcnano->pll_clk);
+		if (IS_ERR(dcnano->pll_clk)) {
+			ret = PTR_ERR(dcnano->pll_clk);
+			drm_err(&dcnano->base,
+				"failed to get pll clock: %d\n", ret);
+			return ret;
+		} else if (!dcnano->pll_clk) {
+			drm_err(&dcnano->base, "no pll clock\n");
+			return -ENODEV;
+		}
+	}
+
+	return 0;
+}
+
+static void dcnano_reset_all_debug_counters(struct dcnano_dev *dcnano)
+{
+	clk_prepare_enable(dcnano->ahb_clk);
+	clk_prepare_enable(dcnano->pixel_clk);
+	dcnano_write(dcnano, DCNANO_DEBUGCOUNTERSELECT, RESET_ALL_CNTS);
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+}
+
+int dcnano_crtc_init(struct dcnano_dev *dcnano)
+{
+	int ret;
+
+	ret = dcnano_plane_init(dcnano);
+	if (ret)
+		return ret;
+
+	drm_crtc_helper_add(&dcnano->crtc, &dcnano_crtc_helper_funcs);
+	ret = drm_crtc_init_with_planes(&dcnano->base, &dcnano->crtc,
+					&dcnano->primary, NULL,
+					&dcnano_crtc_funcs, NULL);
+	if (ret) {
+		drm_err(&dcnano->base, "failed to initialize CRTC: %d\n", ret);
+		return ret;
+	}
+
+	ret = dcnano_get_pll_clock(dcnano);
+	if (ret)
+		return ret;
+
+	dcnano_reset_all_debug_counters(dcnano);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-drv.c b/drivers/gpu/drm/imx/dcnano/dcnano-drv.c
new file mode 100644
index 0000000000000000000000000000000000000000..d8524e574a8665e9993a66ac8470a906b612cc90
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/dcnano-drv.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020,2021 NXP
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_irq.h>
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "dcnano-drv.h"
+#include "dcnano-reg.h"
+
+#define DRIVER_NAME     "imx-dcnano-drm"
+
+static int legacyfb_depth = 32;
+module_param(legacyfb_depth, uint, 0444);
+
+DEFINE_DRM_GEM_CMA_FOPS(dcnano_driver_fops);
+
+static struct drm_driver dcnano_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
+	.irq_handler		= dcnano_irq_handler,
+	DRM_GEM_CMA_DRIVER_OPS,
+	.fops			= &dcnano_driver_fops,
+	.name			= "imx-dcnano",
+	.desc			= "i.MX DCNANO DRM graphics",
+	.date			= "20201221",
+	.major			= 1,
+	.minor			= 0,
+	.patchlevel		= 0,
+};
+
+static int dcnano_check_chip_info(struct dcnano_dev *dcnano)
+{
+	struct drm_device *drm = &dcnano->base;
+	u32 val;
+	int ret = 0;
+
+	clk_prepare_enable(dcnano->ahb_clk);
+	clk_prepare_enable(dcnano->pixel_clk);
+
+	val = dcnano_read(dcnano, DCNANO_DCCHIPREV);
+	if (val != DCCHIPREV) {
+		DRM_DEV_ERROR(drm->dev, "invalid chip revision(0x%08x)\n", val);
+		ret = -ENODEV;
+		goto err;
+	}
+	DRM_DEV_DEBUG(drm->dev, "chip revision is 0x%08x\n", val);
+
+	val = dcnano_read(dcnano, DCNANO_DCCHIPDATE);
+	if (val != DCCHIPDATE) {
+		DRM_DEV_ERROR(drm->dev, "invalid chip date(0x%08x)\n", val);
+		ret = -ENODEV;
+		goto err;
+	}
+	DRM_DEV_DEBUG(drm->dev, "chip date is 0x%08x\n", val);
+
+	val = dcnano_read(dcnano, DCNANO_DCCHIPPATCHREV);
+	if (val != DCCHIPPATCHREV) {
+		DRM_DEV_ERROR(drm->dev,
+			      "invalid chip patch revision(0x%08x)\n", val);
+		ret = -ENODEV;
+		goto err;
+	}
+	DRM_DEV_DEBUG(drm->dev, "chip patch revision is 0x%08x\n", val);
+err:
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+	return ret;
+}
+
+static int dcnano_probe(struct platform_device *pdev)
+{
+	struct dcnano_dev *dcnano;
+	struct drm_device *drm;
+	int irq, ret;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	dcnano = devm_drm_dev_alloc(&pdev->dev, &dcnano_driver,
+				    struct dcnano_dev, base);
+	if (IS_ERR(dcnano))
+		return PTR_ERR(dcnano);
+
+	drm = &dcnano->base;
+	dev_set_drvdata(&pdev->dev, dcnano);
+
+	dcnano->mmio_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(dcnano->mmio_base))
+		return PTR_ERR(dcnano->mmio_base);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	dcnano->axi_clk = devm_clk_get(drm->dev, "axi");
+	if (IS_ERR(dcnano->axi_clk)) {
+		ret = PTR_ERR(dcnano->axi_clk);
+		if (ret != -EPROBE_DEFER)
+			DRM_DEV_ERROR(drm->dev,
+				      "failed to get axi clk: %d\n", ret);
+		return ret;
+	}
+
+	dcnano->ahb_clk = devm_clk_get(drm->dev, "ahb");
+	if (IS_ERR(dcnano->ahb_clk)) {
+		ret = PTR_ERR(dcnano->ahb_clk);
+		if (ret != -EPROBE_DEFER)
+			DRM_DEV_ERROR(drm->dev,
+				      "failed to get ahb clk: %d\n", ret);
+		return ret;
+	}
+
+	dcnano->pixel_clk = devm_clk_get(drm->dev, "pixel");
+	if (IS_ERR(dcnano->pixel_clk)) {
+		ret = PTR_ERR(dcnano->pixel_clk);
+		if (ret != -EPROBE_DEFER)
+			DRM_DEV_ERROR(drm->dev,
+				      "failed to get pixel clk: %d\n", ret);
+		return ret;
+	}
+
+	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
+	if (ret) {
+		DRM_DEV_ERROR(drm->dev,
+			      "failed to set dma mask and coherent: %d\n", ret);
+		return ret;
+	}
+
+	clk_prepare_enable(dcnano->ahb_clk);
+	clk_prepare_enable(dcnano->pixel_clk);
+	ret = drm_irq_install(drm, irq);
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+
+	if (ret < 0) {
+		DRM_DEV_ERROR(drm->dev,
+			      "failed to install IRQ handler: %d\n", ret);
+		return ret;
+	}
+
+	ret = dcnano_check_chip_info(dcnano);
+	if (ret)
+		goto err_check_chip_info;
+
+	ret = dcnano_kms_prepare(dcnano);
+	if (ret)
+		goto err_kms_prepare;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret) {
+		DRM_DEV_ERROR(drm->dev,
+			      "failed to register drm device: %d\n", ret);
+		goto err_register;
+	}
+
+	if (legacyfb_depth != 16 && legacyfb_depth != 32) {
+		DRM_DEV_INFO(drm->dev,
+			     "Invalid legacyfb_depth.  Defaulting to 32bpp\n");
+		legacyfb_depth = 32;
+	}
+
+	drm_fbdev_generic_setup(drm, legacyfb_depth);
+
+	return 0;
+
+err_register:
+	drm_kms_helper_poll_fini(drm);
+err_kms_prepare:
+err_check_chip_info:
+	clk_prepare_enable(dcnano->ahb_clk);
+	clk_prepare_enable(dcnano->pixel_clk);
+	drm_irq_uninstall(drm);
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+	return ret;
+}
+
+static int dcnano_remove(struct platform_device *pdev)
+{
+	struct dcnano_dev *dcnano = dev_get_drvdata(&pdev->dev);
+	struct drm_device *drm = &dcnano->base;
+
+	drm_dev_unregister(drm);
+
+	drm_kms_helper_poll_fini(drm);
+
+	drm_atomic_helper_shutdown(drm);
+
+	clk_prepare_enable(dcnano->ahb_clk);
+	clk_prepare_enable(dcnano->pixel_clk);
+	drm_irq_uninstall(drm);
+	clk_disable_unprepare(dcnano->pixel_clk);
+	clk_disable_unprepare(dcnano->ahb_clk);
+
+	return 0;
+}
+
+static int __maybe_unused dcnano_suspend(struct device *dev)
+{
+	struct dcnano_dev *dcnano = dev_get_drvdata(dev);
+
+	return drm_mode_config_helper_suspend(&dcnano->base);
+}
+
+static int __maybe_unused dcnano_resume(struct device *dev)
+{
+	struct dcnano_dev *dcnano = dev_get_drvdata(dev);
+
+	return drm_mode_config_helper_resume(&dcnano->base);
+}
+
+static const struct dev_pm_ops dcnano_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(dcnano_suspend, dcnano_resume)
+};
+
+static const struct of_device_id dcnano_dt_ids[] = {
+	{ .compatible = "nxp,imx8ulp-dcnano", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dcnano_dt_ids);
+
+static struct platform_driver dcnano_platform_driver = {
+	.probe	= dcnano_probe,
+	.remove	= dcnano_remove,
+	.driver	= {
+		.name		= DRIVER_NAME,
+		.of_match_table	= dcnano_dt_ids,
+		.pm		= &dcnano_pm_ops,
+	},
+};
+module_platform_driver(dcnano_platform_driver);
+
+MODULE_AUTHOR("NXP Semiconductor");
+MODULE_DESCRIPTION("i.MX DCNANO DRM driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-drv.h b/drivers/gpu/drm/imx/dcnano/dcnano-drv.h
new file mode 100644
index 0000000000000000000000000000000000000000..287f8b3f0c60c050a6804ec605b1fcec2343f098
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/dcnano-drv.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Copyright 2020,2021 NXP
+ */
+
+#ifndef __DCNANO_DRV_H__
+#define __DCNANO_DRV_H__
+
+#include <linux/clk.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_vblank.h>
+
+enum dcnano_port {
+	DCNANO_DPI_PORT,
+	DCNANO_DBI_PORT,
+	DCNANO_PORT_NUM,
+};
+
+struct dcnano_dev {
+	struct drm_device base;
+	void __iomem *mmio_base;
+
+	struct clk *axi_clk;
+	struct clk *ahb_clk;
+	struct clk *pixel_clk;
+	struct clk *pll_clk;
+
+	struct drm_crtc crtc;
+	struct drm_plane primary;
+	struct drm_encoder encoder;
+
+	struct drm_pending_vblank_event *event;
+
+	enum dcnano_port port;
+};
+
+static inline struct dcnano_dev *to_dcnano_dev(struct drm_device *drm)
+{
+	return container_of(drm, struct dcnano_dev, base);
+}
+
+irqreturn_t dcnano_irq_handler(int irq, void *data);
+
+int dcnano_crtc_init(struct dcnano_dev *dcnano);
+
+int dcnano_plane_init(struct dcnano_dev *dcnano);
+
+int dcnano_kms_prepare(struct dcnano_dev *dcnano);
+
+#endif
diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-kms.c b/drivers/gpu/drm/imx/dcnano/dcnano-kms.c
new file mode 100644
index 0000000000000000000000000000000000000000..223795fa0aa8f1aea3ee74a928e1d63736de8f49
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/dcnano-kms.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020,2021 NXP
+ */
+
+#include <linux/of.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_vblank.h>
+
+#include "dcnano-drv.h"
+#include "dcnano-reg.h"
+
+static int dcnano_kms_init(struct dcnano_dev *dcnano)
+{
+	struct drm_device *drm = &dcnano->base;
+	struct drm_panel *panel;
+	struct drm_bridge *bridge;
+	struct drm_connector *connector;
+	struct device_node *np = drm->dev->of_node;
+	struct device_node *port, *ep, *remote;
+	struct of_endpoint endpoint;
+	u32 port_id;
+	bool found_ep = false;
+	int ret;
+
+	ret = dcnano_crtc_init(dcnano);
+	if (ret)
+		return ret;
+
+	for (port_id = 0; port_id < DCNANO_PORT_NUM; port_id++) {
+		port = of_graph_get_port_by_id(np, port_id);
+		if (!port) {
+			drm_err(drm, "failed to get output port%u\n", port_id);
+			return -EINVAL;
+		}
+
+		for_each_child_of_node(port, ep) {
+			remote = of_graph_get_remote_port_parent(ep);
+			if (!remote || !of_device_is_available(remote) ||
+			    !of_device_is_available(remote->parent)) {
+				of_node_put(remote);
+				continue;
+			}
+
+			of_node_put(remote);
+
+			ret = of_graph_parse_endpoint(ep, &endpoint);
+			if (ret) {
+				drm_err(drm,
+					"failed to parse endpoint of port%u: %d\n",
+					port_id, ret);
+				of_node_put(ep);
+				of_node_put(port);
+				return ret;
+			}
+
+			ret = drm_of_find_panel_or_bridge(np,
+							  port_id, endpoint.id,
+							  &panel, &bridge);
+			if (ret) {
+				if (ret == -ENODEV) {
+					drm_dbg(drm,
+						"no panel or bridge on port%u ep%d\n",
+						port_id, endpoint.id);
+					continue;
+				} else if (ret != -EPROBE_DEFER) {
+					drm_err(drm,
+						"failed to find panel or bridge on port%u ep%d: %d\n",
+						port_id, endpoint.id, ret);
+				}
+				of_node_put(ep);
+				of_node_put(port);
+				return ret;
+			}
+
+			found_ep = true;
+			break;
+		}
+
+		of_node_put(port);
+		dcnano->port = port_id;
+
+		if (found_ep) {
+			drm_dbg(drm, "found valid endpoint%d @ port%u\n",
+				endpoint.id, port_id);
+			break;
+		}
+	}
+
+	if (!found_ep) {
+		drm_info(drm, "no valid endpoint\n");
+		return 0;
+	}
+
+	if (panel) {
+		bridge = devm_drm_panel_bridge_add(drm->dev, panel);
+		if (IS_ERR(bridge)) {
+			ret = PTR_ERR(bridge);
+			drm_err(drm,
+				"failed to add panel bridge on port%u ep%d: %d\n",
+				port_id, endpoint.id, ret);
+			goto err;
+		}
+	}
+
+	dcnano->encoder.possible_crtcs = drm_crtc_mask(&dcnano->crtc);
+	ret = drm_simple_encoder_init(drm, &dcnano->encoder,
+				      DRM_MODE_ENCODER_NONE);
+	if (ret) {
+		drm_err(drm, "failed to initialize encoder on port%u ep%d: %d\n",
+			port_id, endpoint.id, ret);
+		goto err;
+	}
+
+	ret = drm_bridge_attach(&dcnano->encoder, bridge, NULL,
+				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+	if (ret) {
+		drm_err(drm,
+			"failed to attach bridge to encoder on port%u ep%d: %d\n",
+			port_id, endpoint.id, ret);
+		goto err;
+	}
+
+	connector = drm_bridge_connector_init(drm, &dcnano->encoder);
+	if (IS_ERR(connector)) {
+		ret = PTR_ERR(connector);
+		drm_err(drm,
+			"failed to initialize bridge connector on port%u ep%d: %d\n",
+			port_id, endpoint.id, ret);
+		goto err;
+	}
+
+	ret = drm_connector_attach_encoder(connector, &dcnano->encoder);
+	if (ret)
+		drm_err(drm,
+			"failed to attach encoder to connector on port%u ep%d: %d\n",
+			port_id, endpoint.id, ret);
+err:
+	return ret;
+}
+
+static const struct drm_mode_config_funcs dcnano_mode_config_funcs = {
+	.fb_create     = drm_gem_fb_create,
+	.atomic_check  = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+int dcnano_kms_prepare(struct dcnano_dev *dcnano)
+{
+	struct drm_device *drm = &dcnano->base;
+	int ret;
+
+	ret = drmm_mode_config_init(drm);
+	if (ret)
+		return ret;
+
+	ret = dcnano_kms_init(dcnano);
+	if (ret)
+		return ret;
+
+	drm->mode_config.min_width	= 32;
+	drm->mode_config.min_height	= 32;
+	drm->mode_config.max_width	= 1280;
+	drm->mode_config.max_height	= 1280;
+	drm->mode_config.funcs		= &dcnano_mode_config_funcs;
+	drm->max_vblank_count		= DEBUGCOUNTERVALUE_MAX;
+
+	ret = drm_vblank_init(drm, 1);
+	if (ret < 0) {
+		drm_err(drm, "failed to initialize vblank: %d\n", ret);
+		return ret;
+	}
+
+	drm_mode_config_reset(drm);
+
+	drm_kms_helper_poll_init(drm);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-plane.c b/drivers/gpu/drm/imx/dcnano/dcnano-plane.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb332f2bb54c54cb3d2daac562606e3e17a69167
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/dcnano-plane.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020,2021 NXP
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_rect.h>
+
+#include "dcnano-drv.h"
+#include "dcnano-reg.h"
+
+#define DCNANO_FB_PITCH_ALIGN	128	/* in byte */
+
+#define dcnano_plane_dbg(plane, fmt, ...)				\
+	drm_dbg_kms((plane)->dev, "[PLANE:%d:%s] " fmt,			\
+		    (plane)->base.id, (plane)->name, ##__VA_ARGS__)
+
+/* primary plane formats */
+static const u32 dcnano_primary_plane_formats[] = {
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_XRGB8888,
+};
+
+static unsigned int
+dcnano_primary_plane_format_count = ARRAY_SIZE(dcnano_primary_plane_formats);
+
+static inline struct dcnano_dev *plane_to_dcnano_dev(struct drm_plane *plane)
+{
+	return to_dcnano_dev(plane->dev);
+}
+
+static inline dma_addr_t
+drm_plane_state_to_baseaddr(struct drm_plane_state *state)
+{
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_gem_cma_object *cma_obj;
+	unsigned int x = state->src.x1 >> 16;
+	unsigned int y = state->src.y1 >> 16;
+
+	cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+
+	return cma_obj->paddr + fb->offsets[0] + fb->pitches[0] * y +
+	       fb->format->cpp[0] * x;
+}
+
+/***************************/
+/* primary plane functions */
+/***************************/
+
+static int dcnano_primary_plane_atomic_check(struct drm_plane *plane,
+					     struct drm_plane_state *state)
+{
+	struct dcnano_dev *dcnano = plane_to_dcnano_dev(plane);
+	struct drm_crtc_state *crtc_state;
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_framebuffer *old_fb = plane->state->fb;
+	u32 src_w;
+	unsigned int pitch_no_padding;
+	int ret;
+
+	/* ok to disable */
+	if (!fb)
+		return 0;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state->state, &dcnano->crtc);
+	if (WARN_ON(!crtc_state))
+		return -EINVAL;
+
+	ret = drm_atomic_helper_check_plane_state(state, crtc_state,
+						  DRM_PLANE_HELPER_NO_SCALING,
+						  DRM_PLANE_HELPER_NO_SCALING,
+						  false, true);
+	if (ret)
+		return ret;
+
+	if (fb->pitches[0] > FBSTRIDE_MAX) {
+		dcnano_plane_dbg(plane, "fb pitches[0] 0x%08x is out of range\n",
+				 fb->pitches[0]);
+		return -EINVAL;
+	}
+
+	/*
+	 * The primary plane's stride value in register has to be 128byte
+	 * aligned, _but_ no dedicated padding is allowed.
+	 */
+	src_w = drm_rect_width(&state->src) >> 16;
+	pitch_no_padding = fb->format->cpp[0] * src_w;
+	if (fb->pitches[0] != pitch_no_padding) {
+		dcnano_plane_dbg(plane,
+				 "fb pitches[0] 0x%08x should be no padding - 0x%08x\n",
+				 fb->pitches[0], pitch_no_padding);
+		return -EINVAL;
+	}
+
+	/*
+	 * Force CRTC mode change if framebuffer stride or pixel format
+	 * are changed.
+	 */
+	if (old_fb &&
+	    (fb->pitches[0] != old_fb->pitches[0] ||
+	     fb->format->format != old_fb->format->format))
+		crtc_state->mode_changed = true;
+
+	return 0;
+}
+
+static void dcnano_primary_plane_atomic_update(struct drm_plane *plane,
+					       struct drm_plane_state *old_state)
+{
+	struct dcnano_dev *dcnano = plane_to_dcnano_dev(plane);
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	dma_addr_t baseaddr;
+
+	if (!fb) {
+		dcnano_plane_dbg(plane, "no fb\n");
+		return;
+	}
+
+	baseaddr = drm_plane_state_to_baseaddr(state);
+
+	dcnano_plane_dbg(plane, "fb address %pad, pitch 0x%08x\n",
+			 &baseaddr, fb->pitches[0]);
+
+	dcnano_write(dcnano, DCNANO_FRAMEBUFFERADDRESS, baseaddr);
+
+	dcnano_write(dcnano, DCNANO_FRAMEBUFFERSTRIDE,
+		     ALIGN(fb->pitches[0], DCNANO_FB_PITCH_ALIGN));
+}
+
+static const struct drm_plane_helper_funcs dcnano_primary_plane_helper_funcs = {
+	.prepare_fb	= drm_gem_fb_prepare_fb,
+	.atomic_check	= dcnano_primary_plane_atomic_check,
+	.atomic_update	= dcnano_primary_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs dcnano_plane_funcs = {
+	.update_plane		= drm_atomic_helper_update_plane,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.destroy		= drm_plane_cleanup,
+	.reset			= drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
+};
+
+int dcnano_plane_init(struct dcnano_dev *dcnano)
+{
+	int ret;
+
+	/* primary plane */
+	drm_plane_helper_add(&dcnano->primary,
+			     &dcnano_primary_plane_helper_funcs);
+	ret = drm_universal_plane_init(&dcnano->base, &dcnano->primary, 0,
+				       &dcnano_plane_funcs,
+				       dcnano_primary_plane_formats,
+				       dcnano_primary_plane_format_count,
+				       NULL,
+				       DRM_PLANE_TYPE_PRIMARY, NULL);
+	if (ret)
+		drm_err(&dcnano->base,
+			"failed to initialize primary plane: %d\n", ret);
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/imx/dcnano/dcnano-reg.h b/drivers/gpu/drm/imx/dcnano/dcnano-reg.h
new file mode 100644
index 0000000000000000000000000000000000000000..aa31adcc2935f6025dd9a0fea4f4871f01b58ef4
--- /dev/null
+++ b/drivers/gpu/drm/imx/dcnano/dcnano-reg.h
@@ -0,0 +1,438 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Copyright 2020,2021 NXP
+ */
+
+#ifndef _DCNANO_REG_H_
+#define _DCNANO_REG_H_
+
+#include <linux/bitfield.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+
+#define DCNANO_FRAMEBUFFERCONFIG	0x1240
+/* double buffered */
+#define  FBCFG_FORMAT_MASK		GENMASK(2, 0)
+#define  FBCFG_FORMAT_NONE		FIELD_PREP(FBCFG_FORMAT_MASK, 0)
+#define  FBCFG_FORMAT_R4G4B4		FIELD_PREP(FBCFG_FORMAT_MASK, 1)
+#define  FBCFG_FORMAT_R5G5B5		FIELD_PREP(FBCFG_FORMAT_MASK, 2)
+#define  FBCFG_FORMAT_R5G6B5		FIELD_PREP(FBCFG_FORMAT_MASK, 3)
+#define  FBCFG_FORMAT_R8G8B8		FIELD_PREP(FBCFG_FORMAT_MASK, 4)
+/* double buffered */
+#define  FBCFG_MODE_LINEAR		0
+#define  FBCFG_MODE_TILE4X4		BIT(4)
+/* double buffered */
+#define  FBCFG_OUTPUT_MASK		BIT(8)
+#define  FBCFG_OUTPUT_DISABLE		0
+#define  FBCFG_OUTPUT_ENABLE		BIT(8)
+/* double buffered */
+#define  FBCFG_SWITCHPANEL_DISABLE	0
+#define  FBCFG_SWITCHPANEL_ENABLE	BIT(9)
+/* double buffered */
+#define  FBCFG_GAMMA_DISABLE		0
+#define  FBCFG_GAMMA_ENABLE		BIT(12)
+#define  FBCFG_VALID_WORKING		0
+#define  FBCFG_VALID_PENDING		BIT(16)
+#define  FBCFG_RESET_MASK		BIT(20)
+#define  FBCFG_RESET_DISABLE		0
+#define  FBCFG_RESET_ENABLE		BIT(20)
+#define  FBCFG_UNDERFLOW_NO		0
+#define  FBCFG_UNDERFLOW_YES		BIT(24)
+#define  FBCFG_FLIP_INPROGRSS_NO	0
+#define  FBCFG_FLIP_INPROGRSS_YES	BIT(28)
+#define  FBCFG_BACK_PRES_DISABLE_NO	0
+#define  FBCFG_BACK_PRES_DISABLE_YES	BIT(29)
+
+/* double buffered */
+#define DCNANO_FRAMEBUFFERADDRESS	0x1260
+#define  FBADDRESS_MASK			GENMASK(31, 0)
+#define  FBADDRESS(x)			FIELD_PREP(FBADDRESS_MASK, (x))
+#define  FBADDRESS_TYPE_SYSTEM		0
+#define  FBADDRESS_TYPE_VIRTUAL		BIT(31)
+
+/* double buffered */
+#define DCNANO_FRAMEBUFFERSTRIDE	0x1280
+#define  FBSTRIDE_MASK			GENMASK(16, 0)
+#define  FBSTRIDE(x)			FIELD_PREP(FBSTRIDE_MASK, (x))
+#define  FBSTRIDE_MAX			FIELD_MAX(FBSTRIDE_MASK)
+
+#define DCNANO_DISPLAYDITHERCONFIG	0x1360
+#define  DITHERCFG_BLUESIZE_MASK	GENMASK(3, 0)
+#define  DITHERCFG_BLUESIZE(x)		FIELD_PREP(DITHERCFG_BLUESIZE_MASK, (x))
+#define  DITHERCFG_GREENSIZE_MASK	GENMASK(11, 8)
+#define  DITHERCFG_GREENSIZE(x)		FIELD_PREP(DITHERCFG_GREENSIZE_MASK, (x))
+#define  DITHERCFG_REDSIZE_MASK		GENMASK(19, 16)
+#define  DITHERCFG_REDSIZE(x)		FIELD_PREP(DITHERCFG_REDSIZE_MASK, (x))
+#define  DITHERCFG_DISABLE		0
+/* double buffered */
+#define  DITHERCFG_ENABLE		BIT(31)
+
+#define DCNANO_DISPLAYDITHERTABLELOW	0x1380
+#define  DITHERTLB_LOW_Y0X0_MASK	GENMASK(3, 0)
+#define  DITHERTLB_LOW_Y0X0(x)		FIELD_PREP(DITHERTLB_LOW_Y0X0_MASK, (x))
+#define  DITHERTLB_LOW_Y0X1_MASK	GENMASK(7, 4)
+#define  DITHERTLB_LOW_Y0X1(x)		FIELD_PREP(DITHERTLB_LOW_Y0X1_MASK, (x))
+#define  DITHERTLB_LOW_Y0X2_MASK	GENMASK(11, 8)
+#define  DITHERTLB_LOW_Y0X2(x)		FIELD_PREP(DITHERTLB_LOW_Y0X2_MASK, (x))
+#define  DITHERTLB_LOW_Y0X3_MASK	GENMASK(15, 12)
+#define  DITHERTLB_LOW_Y0X3(x)		FIELD_PREP(DITHERTLB_LOW_Y0X3_MASK, (x))
+#define  DITHERTLB_LOW_Y1X0_MASK	GENMASK(19, 16)
+#define  DITHERTLB_LOW_Y1X0(x)		FIELD_PREP(DITHERTLB_LOW_Y1X0_MASK, (x))
+#define  DITHERTLB_LOW_Y1X1_MASK	GENMASK(23, 20)
+#define  DITHERTLB_LOW_Y1X1(x)		FIELD_PREP(DITHERTLB_LOW_Y1X1_MASK, (x))
+#define  DITHERTLB_LOW_Y1X2_MASK	GENMASK(27, 24)
+#define  DITHERTLB_LOW_Y1X2(x)		FIELD_PREP(DITHERTLB_LOW_Y1X2_MASK, (x))
+#define  DITHERTLB_LOW_Y1X3_MASK	GENMASK(31, 28)
+#define  DITHERTLB_LOW_Y1X3(x)		FIELD_PREP(DITHERTLB_LOW_Y1X3_MASK, (x))
+
+#define DCNANO_DISPLAYDITHERTABLEHIGH	0x13a0
+#define  DITHERTLB_HIGH_Y2X0_MASK	GENMASK(3, 0)
+#define  DITHERTLB_HIGH_Y2X0(x)		FIELD_PREP(DITHERTLB_HIGH_Y2X0_MASK, (x))
+#define  DITHERTLB_HIGH_Y2X1_MASK	GENMASK(7, 4)
+#define  DITHERTLB_HIGH_Y2X1(x)		FIELD_PREP(DITHERTLB_HIGH_Y2X1_MASK, (x))
+#define  DITHERTLB_HIGH_Y2X2_MASK	GENMASK(11, 8)
+#define  DITHERTLB_HIGH_Y2X2(x)		FIELD_PREP(DITHERTLB_HIGH_Y2X2_MASK, (x))
+#define  DITHERTLB_HIGH_Y2X3_MASK	GENMASK(15, 12)
+#define  DITHERTLB_HIGH_Y2X3(x)		FIELD_PREP(DITHERTLB_HIGH_Y2X3_MASK, (x))
+#define  DITHERTLB_HIGH_Y3X0_MASK	GENMASK(19, 16)
+#define  DITHERTLB_HIGH_Y3X0(x)		FIELD_PREP(DITHERTLB_HIGH_Y3X0_MASK, (x))
+#define  DITHERTLB_HIGH_Y3X1_MASK	GENMASK(23, 20)
+#define  DITHERTLB_HIGH_Y3X1(x)		FIELD_PREP(DITHERTLB_HIGH_Y3X1_MASK, (x))
+#define  DITHERTLB_HIGH_Y3X2_MASK	GENMASK(27, 24)
+#define  DITHERTLB_HIGH_Y3X2(x)		FIELD_PREP(DITHERTLB_HIGH_Y3X2_MASK, (x))
+#define  DITHERTLB_HIGH_Y3X3_MASK	GENMASK(31, 28)
+#define  DITHERTLB_HIGH_Y3X3(x)		FIELD_PREP(DITHERTLB_HIGH_Y3X3_MASK, (x))
+
+#define DCNANO_PANELCONFIG		0x13c0
+#define  PANELCFG_DE_DISABLE		0
+#define  PANELCFG_DE_ENABLE		BIT(0)
+#define  PANELCFG_DE_POL_POSITIVE	0
+#define  PANELCFG_DE_POL_NEGATIVE	BIT(1)
+/* double buffered? */
+#define  PANELCFG_DATA_DISABLE		0
+#define  PANELCFG_DATA_ENABLE		BIT(4)
+#define  PANELCFG_DATA_POL_POSITIVE	0
+#define  PANELCFG_DATA_POL_NEGATIVE	BIT(5)
+#define  PANELCFG_CLOCK_DISABLE		0
+#define  PANELCFG_CLOCK_ENABLE		BIT(8)
+#define  PANELCFG_CLOCK_POL_POSITIVE	0
+#define  PANELCFG_CLOCK_POL_NEGATIVE	BIT(9)
+#define  PANELCFG_SEQUENCING_HARDWARE	0
+#define  PANELCFG_SEQUENCING_SOFTWARE	BIT(31)
+
+#define DCNANO_PANELTIMING		0x13e0
+#define  PANELTIMING_POWER_ENABLE_MASK		GENMASK(3, 0)
+#define  PANELTIMING_POWER_ENABLE(x)		\
+			FIELD_PREP(PANELTIMING_POWER_ENABLE_MASK, (x))
+#define  PANELTIMING_BACKLIGHT_ENABLE_MASK	GENMASK(7, 4)
+#define  PANELTIMING_BACKLIGHT_ENABLE(x)	\
+			FIELD_PREP(PANELTIMING_BACKLIGHT_ENABLE_MASK, (x))
+#define  PANELTIMING_CLOCK_ENABLE_MASK		GENMASK(11, 8)
+#define  PANELTIMING_CLOCK_ENABLE(x)		\
+			FIELD_PREP(PANELTIMING_CLOCK_ENABLE_MASK, (x))
+#define  PANELTIMING_DATA_ENABLE_MASK		GENMASK(15, 12)
+#define  PANELTIMING_DATA_ENABLE(x)		\
+			FIELD_PREP(PANELTIMING_DATA_ENABLE_MASK, (x))
+#define  PANELTIMING_DATA_DISABLE_MASK		GENMASK(19, 16)
+#define  PANELTIMING_DATA_DISABLE(x)		\
+			FIELD_PREP(PANELTIMING_DATA_DISABLE_MASK, (x))
+#define  PANELTIMING_CLOCK_DISABLE_MASK		GENMASK(23, 20)
+#define  PANELTIMING_CLOCK_DISABLE(x)		\
+			FIELD_PREP(PANELTIMING_CLOCK_DISABLE_MASK, (x))
+#define  PANELTIMING_BACKLIGHT_DISABLE_MASK	GENMASK(27, 24)
+#define  PANELTIMING_BACKLIGHT_DISABLE(x)	\
+			FIELD_PREP(PANELTIMING_BACKLIGHT_DISABLE_MASK, (x))
+#define  PANELTIMING_POWER_DISABLE_MASK		GENMASK(31, 28)
+#define  PANELTIMING_POWER_DISABLE(x)		\
+			FIELD_PREP(PANELTIMING_POWER_DISABLE_MASK, (x))
+
+#define DCNANO_HDISPLAY			0x1400
+#define  HDISPLAY_END_MASK		GENMASK(12, 0)
+#define  HDISPLAY_END(x)		FIELD_PREP(HDISPLAY_END_MASK, (x))
+#define  HDISPLAY_TOTAL_MASK		GENMASK(28, 16)
+#define  HDISPLAY_TOTAL(x)		FIELD_PREP(HDISPLAY_TOTAL_MASK, (x))
+
+#define DCNANO_HSYNC			0x1420
+#define  HSYNC_START_MASK		GENMASK(12, 0)
+#define  HSYNC_START(x)			FIELD_PREP(HSYNC_START_MASK, (x))
+#define  HSYNC_END_MASK			GENMASK(28, 16)
+#define  HSYNC_END(x)			FIELD_PREP(HSYNC_END_MASK, (x))
+/* double buffered? */
+#define  HSYNC_PULSE_DISABLE		0
+#define  HSYNC_PULSE_ENABLE		BIT(30)
+#define  HSYNC_POL_MASK			BIT(31)
+#define  HSYNC_POL_POSITIVE		0
+#define  HSYNC_POL_NEGATIVE		BIT(31)
+
+#define DCNANO_VDISPLAY			0x1480
+#define  VDISPLAY_END_MASK		GENMASK(11, 0)
+#define  VDISPLAY_END(x)		FIELD_PREP(VDISPLAY_END_MASK, (x))
+#define  VDISPLAY_TOTAL_MASK		GENMASK(27, 16)
+#define  VDISPLAY_TOTAL(x)		FIELD_PREP(VDISPLAY_TOTAL_MASK, (x))
+
+#define DCNANO_VSYNC			0x14a0
+#define  VSYNC_START_MASK		GENMASK(11, 0)
+#define  VSYNC_START(x)			FIELD_PREP(VSYNC_START_MASK, (x))
+#define  VSYNC_END_MASK			GENMASK(27, 16)
+#define  VSYNC_END(x)			FIELD_PREP(VSYNC_END_MASK, (x))
+/* double buffered? */
+#define  VSYNC_PULSE_DISABLE		0
+#define  VSYNC_PULSE_ENABLE		BIT(30)
+#define  VSYNC_POL_MASK			BIT(31)
+#define  VSYNC_POL_POSITIVE		0
+#define  VSYNC_POL_NEGATIVE		BIT(31)
+
+#define DCNANO_DISPLAYCURRENTLOCATION	0x14c0
+#define  CURRENTLOCATION_X_MASK		GENMASK(15, 0)
+#define  CURRENTLOCATION_X(x)		FIELD_PREP(CURRENTLOCATION_X_MASK, (x))
+#define  CURRENTLOCATION_X_GET(x)	FIELD_GET(CURRENTLOCATION_X_MASK, (x))
+#define  CURRENTLOCATION_Y_MASK		GENMASK(31, 16)
+#define  CURRENTLOCATION_Y(x)		FIELD_PREP(CURRENTLOCATION_Y_MASK, (x))
+#define  CURRENTLOCATION_Y_GET(x)	FIELD_GET(CURRENTLOCATION_Y_MASK, (x))
+
+#define DCNANO_GAMMAINDEX		0x14e0
+#define  GAMMAINDEX_MASK		GENMASK(7, 0)
+#define  GAMMAINDEX(x)			FIELD_PREP(GAMMAINDEX_MASK, (x))
+
+#define DCNANO_GAMMADATA		0x1500
+#define  GAMMADATA_BLUE_MASK		GENMASK(7, 0)
+#define  GAMMADATA_BLUE(x)		FIELD_PREP(GAMMADATA_BLUE_MASK, (x))
+#define  GAMMADATA_GREEN_MASK		GENMASK(15, 8)
+#define  GAMMADATA_GREEN(x)		FIELD_PREP(GAMMADATA_GREEN_MASK, (x))
+#define  GAMMADATA_RED_MASK		GENMASK(23, 16)
+#define  GAMMADATA_RED(x)		FIELD_PREP(GAMMADATA_RED_MASK, (x))
+
+#define DCNANO_CURSORCONFIG		0x1520
+/* double buffered */
+#define  CURSORCFG_FORMAT_MASK		GENMASK(1, 0)
+#define  CURSORCFG_FORMAT_NONE		FIELD_PREP(CURSORCFG_FORMAT_MASK, 0)
+#define  CURSORCFG_FORMAT_MASKED	FIELD_PREP(CURSORCFG_FORMAT_MASK, 1)
+#define  CURSORCFG_FORMAT_A8R8G8B8	FIELD_PREP(CURSORCFG_FORMAT_MASK, 2)
+#define  CURSORCFG_DISPLAY_MASK		BIT(4)
+#define  CURSORCFG_DISPLAY0		0
+#define  CURSORCFG_DISPLAY1		BIT(4)
+/* double buffered */
+#define  CURSORCFG_HOTSPOT_Y_MASK	GENMASK(12, 8)
+#define  CURSORCFG_HOTSPOT_Y(x)		FIELD_PREP(CURSORCFG_HOTSPOT_Y_MASK, 0)
+/* double buffered */
+#define  CURSORCFG_HOTSPOT_X_MASK	GENMASK(20, 16)
+#define  CURSORCFG_HOTSPOT_X(x)		FIELD_PREP(CURSORCFG_HOTSPOT_X_MASK, 0)
+#define  CURSORCFG_FLIP_INPROGRSS_NO	0
+#define  CURSORCFG_FLIP_INPROGRSS_YES	BIT(31)
+
+/* double buffered */
+#define DCNANO_CURSORADDRESS		0x1530
+#define  CURSORADDRESS_MASK		GENMASK(31, 0)
+#define  CURSORADDRESS(x)		FIELD_PREP(CURSORADDRESS_MASK, (x))
+#define  CURSORADDRESS_TYPE_SYSTEM	0
+#define  CURSORADDRESS_TYPE_VIRTUAL	BIT(31)
+
+/* double buffered */
+#define DCNANO_CURSORLOCATION		0x1540
+#define  CURSORLOCATION_X_MASK		GENMASK(12, 0)
+#define  CURSORLOCATION_X(x)		FIELD_PREP(CURSORLOCATION_X_MASK, (x))
+#define  CURSORLOCATION_X_MAX		FIELD_MAX(CURSORLOCATION_X_MASK)
+#define  CURSORLOCATION_Y_MASK		GENMASK(27, 16)
+#define  CURSORLOCATION_Y(x)		FIELD_PREP(CURSORLOCATION_Y_MASK, (x))
+#define  CURSORLOCATION_Y_MAX		FIELD_MAX(CURSORLOCATION_Y_MASK)
+
+/* double buffered */
+#define DCNANO_CURSORBACKGROUND		0x1550
+/* double buffered */
+#define DCNANO_CURSORFOREGROUND		0x1560
+#define  CURSOR_BLUE_MASK		GENMASK(7, 0)
+#define  CURSOR_BLUE(x)			FIELD_PREP(CURSOR_BLUE_MASK, (x))
+#define  CURSOR_GREEN_MASK		GENMASK(15, 8)
+#define  CURSOR_GREEN(x)		FIELD_PREP(CURSOR_GREEN_MASK, (x))
+#define  CURSOR_RED_MASK		GENMASK(23, 16)
+#define  CURSOR_RED(x)			FIELD_PREP(CURSOR_RED_MASK, (x))
+
+#define DCNANO_DISPLAYINTR		0x1600
+#define DCNANO_DISPLAYINTRENABLE	0x1610
+#define  DISPLAYINTR_DISP0		BIT(0)
+
+#define DCNANO_DBICONFIG		0x1620
+#define  DBICFG_DBI_TYPE_MASK		GENMASK(1, 0)
+#define  DBICFG_DBI_TYPE_A_FIXED_E	FIELD_PREP(DBICFG_DBI_TYPE_MASK, 0)
+#define  DBICFG_DBI_TYPE_A_CLOCK_E	FIELD_PREP(DBICFG_DBI_TYPE_MASK, 1)
+#define  DBICFG_DBI_TYPE_B		FIELD_PREP(DBICFG_DBI_TYPE_MASK, 2)
+#define  DBICFG_DBI_TYPE_C		FIELD_PREP(DBICFG_DBI_TYPE_MASK, 3)
+#define  DBICFG_DATA_FORMAT_MASK	GENMASK(5, 2)
+
+/* 8bit  data bus - D[7 : 0] */
+/* 8bpp */
+#define  DBICFG_DATA_FORMAT_D8R3G3B2	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 0)
+/* 12bpp */
+#define  DBICFG_DATA_FORMAT_D8R4G4B4	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 1)
+/* 16bpp */
+#define  DBICFG_DATA_FORMAT_D8R5G6B5	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 2)
+/* 18bpp */
+#define  DBICFG_DATA_FORMAT_D8R6G6B6	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 3)
+/* 24bpp */
+#define  DBICFG_DATA_FORMAT_D8R8G8B8	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 4)
+
+/* 9bit  data bus - D[8 : 0] */
+/* 18bpp */
+#define  DBICFG_DATA_FORMAT_D9R6G6B6	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 5)
+
+/* 16bit data bus - D[15 : 0] */
+/* 8bpp */
+#define  DBICFG_DATA_FORMAT_D16R3G3B2	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 6)
+/* 12bpp */
+#define  DBICFG_DATA_FORMAT_D16R4G4B4	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 7)
+/* 16bpp */
+#define  DBICFG_DATA_FORMAT_D16R5G6B5	 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 8)
+/* 18bpp */
+#define  DBICFG_DATA_FORMAT_D16R6G6B6OP1 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 9)
+#define  DBICFG_DATA_FORMAT_D16R6G6B6OP2 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 10)
+/* 24bpp */
+#define  DBICFG_DATA_FORMAT_D16R8G8B8OP1 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 11)
+#define  DBICFG_DATA_FORMAT_D16R8G8B8OP2 FIELD_PREP(DBICFG_DATA_FORMAT_MASK, 12)
+
+#define  DBICFG_BUS_OUTPUT_SEL_DPI	0
+#define  DBICFG_BUS_OUTPUT_SEL_DBI	BIT(6)
+#define  DBICFG_DBIX_POLARITY_DEFAULT	0
+#define  DBICFG_DBIX_POLARITY_REVERSE	BIT(7)
+#define  DBICFG_DBI_AC_TIME_UNIT_MASK	GENMASK(11, 8)
+#define  DBICFG_DBI_AC_TIME_UNIT(x)	\
+				FIELD_PREP(DBICFG_DBI_AC_TIME_UNIT_MASK, (x))
+#define  DBICFG_DBI_TYPEC_OPT_MASK	GENMASK(13, 12)
+#define  DBICFG_DBI_TYPEC_OPT1		FIELD_PREP(DBICFG_DBI_TYPEC_OPT_MASK, 0)
+#define  DBICFG_DBI_TYPEC_OPT2		FIELD_PREP(DBICFG_DBI_TYPEC_OPT_MASK, 1)
+#define  DBICFG_DBI_TYPEC_OPT3		FIELD_PREP(DBICFG_DBI_TYPEC_OPT_MASK, 2)
+
+#define DCNANO_DBIIFRESET		0x1640
+#define  DBIIF_LEVEL_NO_RESET		0
+#define  DBIIF_LEVEL_RESET		BIT(0)
+
+#define DCNANO_DBIWRCHAR1		0x1660
+#define  DBIWR_PERIOD_MASK		GENMASK(7, 0)
+#define  DBIWR_PERIOD(x)		FIELD_PREP(DBIWR_PERIOD_MASK, (x))
+#define  DBIWR_EOR_WR_ASSERT_MASK	GENMASK(11, 8)
+#define  DBIWR_EOR_WR_ASSERT(x)		FIELD_PREP(DBIWR_EOR_WR_ASSERT_MASK, (x))
+#define  DBIWR_CS_ASSERT_MASK		GENMASK(15, 12)
+#define  DBIWR_CS_ASSERT(x)		FIELD_PREP(DBIWR_CS_ASSERT_MASK, (x))
+
+#define DCNANO_DBIWRCHAR2		0x1680
+#define  DBIWR_EOR_WR_DE_ASRT_MASK	GENMASK(7, 0)
+#define  DBIWR_EOR_WR_DE_ASRT(x)	\
+				FIELD_PREP(DBIWR_EOR_WR_DE_ASRT_MASK, (x))
+#define  DBIWR_CS_DE_ASRT_MASK		GENMASK(15, 8)
+#define  DBIWR_CS_DE_ASRT(x)		FIELD_PREP(DBIWR_CS_DE_ASRT_MASK, (x))
+
+#define DCNANO_DBICMD			0x16a0
+#define  DBICMD_WORD_MASK		GENMASK(15, 0)
+#define  DBICMD_WORD(x)			FIELD_PREP(DBICMD_WORD_MASK, (x))
+#define  DBICMD_FLAG_MASK		GENMASK(31, 30)
+#define  DBICMD_FLAG_ADDRESS		FIELD_PREP(DBICMD_FLAG_MASK, 0)
+#define  DBICMD_FLAG_WRITE_MEM_START	FIELD_PREP(DBICMD_FLAG_MASK, 1)
+#define  DBICMD_FLAG_PARAMETER_OR_DATA	FIELD_PREP(DBICMD_FLAG_MASK, 2)
+/* Read is unused. */
+#define  DBICMD_FLAG_READ		FIELD_PREP(DBICMD_FLAG_MASK, 3)
+
+#define DCNANO_DPICONFIG		0x16c0
+#define  DPICFG_DATA_FORMAT_MASK	GENMASK(2, 0)
+#define  DPICFG_DATA_FORMAT_D16CFG1	FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 0)
+#define  DPICFG_DATA_FORMAT_D16CFG2	FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 1)
+#define  DPICFG_DATA_FORMAT_D16CFG3	FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 2)
+#define  DPICFG_DATA_FORMAT_D18CFG1	FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 3)
+#define  DPICFG_DATA_FORMAT_D18CFG2	FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 4)
+#define  DPICFG_DATA_FORMAT_D24		FIELD_PREP(DPICFG_DATA_FORMAT_MASK, 5)
+
+#define DCNANO_DCCHIPREV		0x16f0
+#define  DCCHIPREV_MASK			GENMASK(31, 0)
+#define  DCCHIPREV			0x00005543
+
+#define DCNANO_DCCHIPDATE		0x1700
+#define  DCCHIPDATE_MASK		GENMASK(31, 0)
+#define  DCCHIPDATE			0x20180612
+
+#define DCNANO_DCCHIPPATCHREV		0x1720
+#define  DCCHIPPATCHREV_MASK		GENMASK(31, 0)
+#define  DCCHIPPATCHREV			0x00000003
+
+#define DCNANO_DCTILEINCFG		0x1740
+/* double buffered */
+#define  DCTILEINCFG_TILE_FORMAT_MASK	GENMASK(1, 0)
+#define  DCTILEINCFG_TILE_FORMAT_NONE		\
+				FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 0)
+#define  DCTILEINCFG_TILE_FORMAT_ARGB8888	\
+				FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 1)
+#define  DCTILEINCFG_TILE_FORMAT_YUY2		\
+				FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 2)
+#define  DCTILEINCFG_TILE_FORMAT_NV12		\
+				FIELD_PREP(DCTILEINCFG_TILE_FORMAT_MASK, 3)
+#define  DCTILEINCFG_YUV_STANDARD_MASK	GENMASK(3, 2)
+/* double buffered */
+#define  DCTILEINCFG_YUV_BT601			\
+				FIELD_PREP(DCTILEINCFG_YUV_STANDARD_MASK, 0)
+#define  DCTILEINCFG_YUV_BT709			\
+				FIELD_PREP(DCTILEINCFG_YUV_STANDARD_MASK, 1)
+/* double buffered */
+#define  DCTILEINCFG_YUV2_RGB_EN_MASK	BIT(4)
+#define  DCTILEINCFG_YUV2_RGB_ENABLE	BIT(4)
+#define  DCTILEINCFG_YUV2_RGB_DISABLE	0
+#define  DCTILEINCFG_CFG_MODE_EN	BIT(5)
+#define  DCTILEINCFG_CFG_MODE_ENABLE	BIT(5)
+#define  DCTILEINCFG_CFG_MODE_DISABLE	0
+
+/* double buffered */
+#define DCNANO_DCTILEUVFRAMEBUFFERADR	0x1760
+#define  DCTILEUVFB_ADDRESS_MASK	GENMASK(31, 0)
+#define  DCTILEUVFB_ADDRESS(x)		FIELD_PREP(DCTILEUVFB_ADDRESS_MASK, (x))
+#define  DCTILEUVFB_ADDRESS_MAX		FIELD_MAX(DCTILEUVFB_ADDRESS_MASK)
+
+/* double buffered */
+#define DCNANO_DCTILEUVFRAMEBUFFERSTR	0x1780
+#define  DCTILEUVFB_STRIDE_MASK		GENMASK(15, 0)
+#define  DCTILEUVFB_STRIDE(x)		FIELD_PREP(DCTILEUVFB_STRIDE_MASK, (x))
+#define  DCTILEUVFB_STRIDE_MAX		FIELD_MAX(DCTILEUVFB_STRIDE_MASK)
+
+#define DCNANO_DCPRODUCTID		0x17b0
+#define  DCPRODUCTID_MASK		GENMASK(31, 0)
+#define  DCPRODUCTID			0x02000361
+
+#define DCNANO_DCSTATUS			0x1800
+#define  DCSTATUS_DBI_TYPEC_FIFO_FULL	BIT(0)
+
+#define DCNANO_DEBUGCOUNTERSELECT	0x1820
+#define  DEBUGCOUNTERSELECT_MASK	GENMASK(7, 0)
+#define  TOTAL_AXI_RD_REQ_CNT		FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 0)
+#define  TOTAL_AXI_RD_LAST_CNT		FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 1)
+#define  TOTAL_AXI_REQ_BURST_CNT	FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 2)
+#define  TOTAL_AXI_RD_BURST_CNT		FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 3)
+#define  TOTAL_PIXEL_CNT		FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 4)
+#define  TOTAL_FRAME_CNT		FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 5)
+#define  TOTAL_INPUT_DBI_CMD_CNT	FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 6)
+#define  TOTAL_OUTPUT_DBI_CMD_CNT	FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 7)
+#define  DEBUG_SIGNALS0			FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 8)
+#define  RESET_ALL_CNTS			FIELD_PREP(DEBUGCOUNTERSELECT_MASK, 0xFF)
+
+#define DCNANO_DEBUGCOUNTERVALUE	0x1840
+#define  DEBUGCOUNTERVALUE_MASK		GENMASK(31, 0)
+#define  DEBUGCOUNTERVALUE(x)		FIELD_PREP(DEBUGCOUNTERVALUE_MASK, (x))
+#define  DEBUGCOUNTERVALUE_MAX		FIELD_MAX(DEBUGCOUNTERVALUE_MASK)
+
+static inline u32 dcnano_read(struct dcnano_dev *dcnano, unsigned int reg)
+{
+	return readl(dcnano->mmio_base + reg);
+}
+
+static inline void dcnano_write(struct dcnano_dev *dcnano,
+				unsigned int reg, u32 value)
+{
+	writel(value, dcnano->mmio_base + reg);
+}
+
+static inline void dcnano_write_mask(struct dcnano_dev *dcnano,
+				     unsigned int reg, u32 mask, u32 value)
+{
+	u32 tmp;
+
+	tmp = dcnano_read(dcnano, reg);
+	tmp &= ~mask;
+	dcnano_write(dcnano, reg, tmp | value);
+}
+
+#endif