[PATCH 1/3] drm/db9000: Add Digital Blocks DB9000 LCD Controller

Gareth Williams gareth.williams.jx at renesas.com
Mon Apr 27 08:21:47 UTC 2020


Add DRM support for the Digital Blocks DB9000 LCD Controller
with the Renesas RZ/N1 specific changes.

Signed-off-by: Gareth Williams <gareth.williams.jx at renesas.com>
---
 drivers/gpu/drm/Kconfig                    |   2 +
 drivers/gpu/drm/Makefile                   |   1 +
 drivers/gpu/drm/digital-blocks/Kconfig     |  13 +
 drivers/gpu/drm/digital-blocks/Makefile    |   3 +
 drivers/gpu/drm/digital-blocks/db9000-du.c | 953 +++++++++++++++++++++++++++++
 drivers/gpu/drm/digital-blocks/db9000-du.h | 192 ++++++
 6 files changed, 1164 insertions(+)
 create mode 100644 drivers/gpu/drm/digital-blocks/Kconfig
 create mode 100644 drivers/gpu/drm/digital-blocks/Makefile
 create mode 100644 drivers/gpu/drm/digital-blocks/db9000-du.c
 create mode 100644 drivers/gpu/drm/digital-blocks/db9000-du.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 3c88420..159832d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -280,6 +280,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
 
 source "drivers/gpu/drm/cirrus/Kconfig"
 
+source "drivers/gpu/drm/digital-blocks/Kconfig"
+
 source "drivers/gpu/drm/armada/Kconfig"
 
 source "drivers/gpu/drm/atmel-hlcdc/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 9f0d2ee..f525807 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_DRM_MGAG200) += mgag200/
 obj-$(CONFIG_DRM_V3D)  += v3d/
 obj-$(CONFIG_DRM_VC4)  += vc4/
 obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
+obj-$(CONFIG_DRM_DB9000) += digital-blocks/
 obj-$(CONFIG_DRM_SIS)   += sis/
 obj-$(CONFIG_DRM_SAVAGE)+= savage/
 obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
diff --git a/drivers/gpu/drm/digital-blocks/Kconfig b/drivers/gpu/drm/digital-blocks/Kconfig
new file mode 100644
index 0000000..436a7c0
--- /dev/null
+++ b/drivers/gpu/drm/digital-blocks/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+config DRM_DB9000
+	bool "DRM Support for DB9000 LCD Controller"
+	depends on DRM && (ARCH_MULTIPLATFORM || COMPILE_TEST)
+	select DRM_KMS_HELPER
+	select DRM_GEM_CMA_HELPER
+	select DRM_KMS_CMA_HELPER
+	select DRM_PANEL_BRIDGE
+	select VIDEOMODE_HELPERS
+	select FB_PROVIDE_GET_FB_UNMAPPED_AREA if FB
+
+	help
+	  Enable DRM support for the DB9000 LCD controller.
diff --git a/drivers/gpu/drm/digital-blocks/Makefile b/drivers/gpu/drm/digital-blocks/Makefile
new file mode 100644
index 0000000..9f78492
--- /dev/null
+++ b/drivers/gpu/drm/digital-blocks/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DRM_DB9000) += db9000-du.o
diff --git a/drivers/gpu/drm/digital-blocks/db9000-du.c b/drivers/gpu/drm/digital-blocks/db9000-du.c
new file mode 100644
index 0000000..d84d446
--- /dev/null
+++ b/drivers/gpu/drm/digital-blocks/db9000-du.c
@@ -0,0 +1,953 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2019 Renesas Electronics Europe Ltd.
+ *
+ * Author: Gareth Williams <gareth.williams.jx at renesas.com>
+ *
+ * Based on ltdc.c
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu at st.com>
+ *          Yannick Fertre <yannick.fertre at st.com>
+ *          Fabien Dessenne <fabien.dessenne at st.com>
+ *          Mickael Reulier <mickael.reulier at st.com>
+ */
+#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_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_drv.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pwm.h>
+#include <linux/reset.h>
+
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
+#include "db9000-du.h"
+
+#define NR_CRTC		1
+#define DB9000_FB_MAX_WIDTH	4095
+#define DB9000_FB_MAX_HEIGHT	4095
+#define RZN1_REGS		((void *) 1)
+
+static const u32 db9000_fmts[] = {
+	DRM_FORMAT_RGB888,
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_XRGB1555,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_BGR888,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_XBGR1555,
+	DRM_FORMAT_XBGR8888
+};
+
+static u32 reg_read(void __iomem *base, u32 reg)
+{
+	return readl(base + reg);
+}
+
+static void reg_write(void __iomem *base, u32 reg, u32 val)
+{
+	writel(val, base + reg);
+}
+
+static struct db9000_device *crtc_to_db9000(struct drm_crtc *crtc)
+{
+	return container_of(crtc->dev, struct db9000_device, drm);
+}
+
+static struct db9000_device *plane_to_db9000(struct drm_plane *plane)
+{
+	return container_of(plane->dev, struct db9000_device, drm);
+}
+
+static struct db9000_device *pwm_chip_to_db9000(struct pwm_chip *chip)
+{
+	return container_of(chip, struct db9000_device, pwm);
+}
+
+void db9000_bpp_setup(struct db9000_device *db9000_dev, int bpp, int bus_width,
+		      bool pixelSelect)
+{
+	u32 format;
+	u32 reg_cr1 = reg_read(db9000_dev->regs, DB9000_CR1);
+
+	/* reset the BPP bits */
+	reg_cr1 &= ~DB9000_CR1_BPP(7);
+	reg_cr1 &= ~DB9000_CR1_OPS(5);
+	reg_cr1 &= ~DB9000_CR1_OPS(1);
+	db9000_dev->bpp = bpp;
+
+	switch (bpp) {
+	case 16:
+		if (pixelSelect) {
+			reg_cr1 |= DB9000_CR1_OPS(5);
+			reg_cr1 |= DB9000_CR1_OPS(1);
+		}
+
+		format = DB9000_CR1_BPP(DB9000_CR1_BPP_16bpp);
+		break;
+	case 24:
+	case 32:
+	default:
+		format = DB9000_CR1_BPP(DB9000_CR1_BPP_24bpp);
+	}
+
+	if (bpp <= 16 && bus_width == 24)
+		reg_cr1 |= DB9000_CR1_OPS(2);
+	else
+		reg_cr1 &= ~DB9000_CR1_OPS(2);
+
+	if (bpp == 24)
+		reg_cr1 |= DB9000_CR1_FBP;
+	else
+		reg_cr1 &= ~DB9000_CR1_FBP;
+
+	reg_cr1 |= format;
+	reg_write(db9000_dev->regs, DB9000_CR1, reg_cr1);
+}
+
+void db9000_toggle_controller(struct db9000_device *db9000_dev, bool on)
+{
+	u32 isr;
+	u32 reg_cr1 = reg_read(db9000_dev->regs, DB9000_CR1);
+	unsigned long flags;
+
+	if (on) {
+		/* Enable controller */
+		reg_cr1 |= DB9000_CR1_LCE;
+		reg_cr1 |= DB9000_CR1_LPE;
+
+		/* DMA Burst Size */
+		reg_cr1 |= DB9000_CR1_FDW(2);
+
+		/* Release pixel clock domain reset */
+		reg_write(db9000_dev->regs, DB9000_PCTR, DB9000_PCTR_PCR);
+
+		/* Enable BAU event for IRQ */
+		spin_lock_irqsave(&db9000_dev->lock, flags);
+		isr = reg_read(db9000_dev->regs, DB9000_ISR);
+		reg_write(db9000_dev->regs, DB9000_ISR, isr | DB9000_ISR_BAU);
+		reg_write(db9000_dev->regs, DB9000_IMR, DB9000_IMR_BAUM);
+		spin_unlock_irqrestore(&db9000_dev->lock, flags);
+
+	} else {
+		/* Disable controller */
+		reg_cr1 &= ~DB9000_CR1_LCE;
+		reg_cr1 &= ~DB9000_CR1_LPE;
+	}
+
+	reg_write(db9000_dev->regs, DB9000_CR1, reg_cr1);
+}
+
+/* CRTC Functions */
+static void db9000_crtc_atomic_enable(struct drm_crtc *crtc,
+				      struct drm_crtc_state *old_state)
+{
+	struct db9000_device *db9000_dev = crtc_to_db9000(crtc);
+	u32 imr;
+
+	/* Enable IRQ */
+	imr = reg_read(db9000_dev->regs, DB9000_IMR);
+	reg_write(db9000_dev->regs, DB9000_IMR, imr | DB9000_IMR_BAUM);
+}
+
+static void db9000_crtc_atomic_disable(struct drm_crtc *crtc,
+				       struct drm_crtc_state *old_state)
+{
+	struct db9000_device *db9000_dev = crtc_to_db9000(crtc);
+	u32 imr;
+
+	/* disable IRQ */
+	imr = reg_read(db9000_dev->regs, DB9000_IMR);
+	reg_write(db9000_dev->regs, DB9000_IMR, imr & ~DB9000_IMR_BAUM);
+}
+
+static void db9000_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+	struct db9000_device *db9000_dev = crtc_to_db9000(crtc);
+	struct videomode vm;
+	int bus_flags = db9000_dev->connector->display_info.bus_flags;
+	u32 vtr1, vtr2, hvter, htr, hpplor, dear_offset;
+	u32 reg_cr1 = reg_read(db9000_dev->regs, DB9000_CR1);
+
+	drm_display_mode_to_videomode(mode, &vm);
+
+	if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH)
+		reg_cr1 |= DB9000_CR1_HSP;
+	else
+		reg_cr1 &= ~DB9000_CR1_HSP;
+
+	if (mode->flags & DISPLAY_FLAGS_VSYNC_HIGH)
+		reg_cr1 |= DB9000_CR1_VSP;
+	else
+		reg_cr1 &= ~DB9000_CR1_VSP;
+
+	if (bus_flags & DRM_BUS_FLAG_DE_HIGH)
+		reg_cr1 |= DB9000_CR1_DEP;
+	else
+		reg_cr1 &= ~DB9000_CR1_DEP;
+
+	/* Horizontal Timing Register */
+	htr =	DB9000_HTR_HSW(vm.hsync_len) |
+		DB9000_HTR_HBP(vm.hback_porch) |
+		/* Pixels per line */
+		DB9000_HTR_HFP(vm.hfront_porch);
+
+	/* Horizontal Pixels-Per-Line Override */
+	hpplor = vm.hactive | DB9000_HPOE;
+
+	/* Vertical timing registers setup */
+	vtr1 =	DB9000_VTR1_VBP(vm.vback_porch) |
+		DB9000_VTR1_VFP(vm.vfront_porch) |
+		DB9000_VTR1_VSW(vm.vsync_len);
+
+	vtr2 = DB9000_VTR2_LPP(vm.vactive);
+
+	/* Vertical and Horizontal Timing Extension write */
+	hvter =	DB9000_HVTER_HFPE(vm.hfront_porch) |
+		DB9000_HVTER_HBPE(vm.hback_porch) |
+		DB9000_HVTER_VFPE(vm.vback_porch) |
+		DB9000_HVTER_VBPE(vm.vfront_porch);
+
+	db9000_dev->frame_size = vm.hactive * vm.vactive;
+
+	/* DEAR register must be configured to the block end + 8 */
+	dear_offset =
+		(db9000_dev->frame_size * db9000_dev->bpp) / 8 + 8;
+
+	reg_write(db9000_dev->regs, DB9000_CR1, reg_cr1);
+	reg_write(db9000_dev->regs, DB9000_HTR, htr);
+	reg_write(db9000_dev->regs, DB9000_VTR1, vtr1);
+	reg_write(db9000_dev->regs, DB9000_VTR2, vtr2);
+	reg_write(db9000_dev->regs, DB9000_HPPLOR, hpplor);
+	reg_write(db9000_dev->regs, DB9000_HVTER, hvter);
+
+	DRM_DEBUG_DRIVER("CRTC:%d mode:%s\n", crtc->base.id, mode->name);
+	DRM_DEBUG_DRIVER("Video mode: %dx%d", vm.hactive, vm.vactive);
+	DRM_DEBUG_DRIVER(" hfp %d hbp %d hsl %d vfp %d vbp %d vsl %d\n",
+			 vm.hfront_porch, vm.hback_porch, vm.hsync_len,
+			 vm.vfront_porch, vm.vback_porch, vm.vsync_len);
+}
+
+static void db9000_crtc_atomic_flush(struct drm_crtc *crtc,
+				     struct drm_crtc_state *old_crtc_state)
+{
+	struct drm_pending_vblank_event *event = crtc->state->event;
+
+	if (event) {
+		crtc->state->event = NULL;
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_send_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static const struct drm_crtc_helper_funcs db9000_crtc_helper_funcs = {
+	.mode_set_nofb = db9000_crtc_mode_set_nofb,
+	.atomic_flush = db9000_crtc_atomic_flush,
+	.atomic_enable = db9000_crtc_atomic_enable,
+	.atomic_disable = db9000_crtc_atomic_disable,
+};
+
+static const struct drm_crtc_funcs db9000_crtc_funcs = {
+	.destroy = drm_crtc_cleanup,
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.reset = drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+	.gamma_set = drm_atomic_helper_legacy_gamma_set,
+};
+
+/*
+ * DRM_PLANE
+ */
+static void db9000_plane_atomic_update(struct drm_plane *plane,
+				       struct drm_plane_state *oldstate)
+{
+	struct db9000_device *db9000_dev = plane_to_db9000(plane);
+	struct drm_plane_state *state = plane->state;
+	struct drm_framebuffer *fb = state->fb;
+	unsigned long flags;
+	u32 isr, paddr, dear_offset;
+	u32 format;
+
+	if (!state->crtc || !fb) {
+		DRM_DEBUG_DRIVER("fb or crtc NULL\n");
+		return;
+	}
+
+	format = fb->format->format;
+
+	/* The single plane is turning visible, so turn on the display */
+	if (!oldstate->visible && state->visible)
+		db9000_toggle_controller(db9000_dev, false);
+
+	/* The plane is no longer visible */
+	if (oldstate->visible && !state->visible)
+		db9000_toggle_controller(db9000_dev, true);
+
+	/* Check for format changes */
+	if (format == DRM_FORMAT_RGB565 || format == DRM_FORMAT_BGR565)
+		db9000_bpp_setup(db9000_dev, 16, db9000_dev->bus_width, false);
+	else if (format == DRM_FORMAT_XRGB1555 || format == DRM_FORMAT_XBGR1555)
+		db9000_bpp_setup(db9000_dev, 16, db9000_dev->bus_width, true);
+	else if (format == DRM_FORMAT_RGB888 || format == DRM_FORMAT_BGR888)
+		db9000_bpp_setup(db9000_dev, 24, db9000_dev->bus_width, false);
+	else if (format == DRM_FORMAT_XRGB8888 || format == DRM_FORMAT_XBGR8888)
+		db9000_bpp_setup(db9000_dev, 32, db9000_dev->bus_width, false);
+
+	dear_offset = (db9000_dev->frame_size * db9000_dev->bpp) / 8 + 8;
+
+	/* The frame buffer has changed, so get the new FB address */
+	if (oldstate->fb != state->fb || state->crtc->state->mode_changed) {
+		paddr = (u32)drm_fb_cma_get_gem_addr(fb, state, 0);
+
+		DRM_DEBUG_DRIVER("fb: phys 0x%08x\n", paddr);
+		reg_write(db9000_dev->regs, DB9000_DBAR, paddr);
+		reg_write(db9000_dev->regs, DB9000_DEAR,
+			  paddr + dear_offset);
+	}
+
+	/* Enable BAU event */
+	spin_lock_irqsave(&db9000_dev->lock, flags);
+	isr = reg_read(db9000_dev->regs, DB9000_ISR);
+	reg_write(db9000_dev->regs, DB9000_ISR, isr | DB9000_ISR_BAU);
+	reg_write(db9000_dev->regs, DB9000_IMR, DB9000_IMR_BAUM);
+	spin_unlock_irqrestore(&db9000_dev->lock, flags);
+
+	db9000_dev->plane_fpsi->counter++;
+
+	if (isr & DB9000_ISR_MBE) {
+		if (isr & DB9000_ISR_OFU)
+			DRM_ERROR("Output FIFO Underrun\n");
+
+		if (isr & DB9000_ISR_OFO)
+			DRM_ERROR("Output FIFO Overrun\n");
+
+		if (isr & DB9000_ISR_IFU)
+			DRM_ERROR("Input FIFO Underrun\n");
+
+		if (isr & DB9000_ISR_IFO)
+			DRM_ERROR("Input FIFO Overrun\n");
+	}
+}
+
+static void db9000_plane_atomic_print_state(struct drm_printer *p,
+					    const struct drm_plane_state *state)
+{
+	struct drm_plane *plane = state->plane;
+	struct db9000_device *db9000_dev = plane_to_db9000(plane);
+	struct fps_info *fpsi = db9000_dev->plane_fpsi;
+	int ms_since_last;
+	ktime_t now;
+
+	now = ktime_get();
+	ms_since_last = ktime_to_ms(ktime_sub(now, fpsi->last_timestamp));
+
+	drm_printf(p, "\tuser_updates=%dfps\n",
+		   DIV_ROUND_CLOSEST(fpsi->counter * 1000, ms_since_last));
+
+	fpsi->last_timestamp = now;
+	fpsi->counter = 0;
+}
+
+static const struct drm_plane_funcs db9000_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,
+	.atomic_print_state = db9000_plane_atomic_print_state,
+};
+
+static const struct drm_plane_helper_funcs db9000_plane_helper_funcs = {
+	.atomic_update = db9000_plane_atomic_update,
+};
+
+static struct drm_plane *db9000_plane_create(struct drm_device *ddev,
+					     enum drm_plane_type type)
+{
+	struct device *dev = ddev->dev;
+	struct drm_plane *plane;
+	int ret;
+
+	plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
+	if (!plane)
+		return NULL;
+
+	ret = drm_universal_plane_init(ddev, plane, NR_CRTC,
+				       &db9000_plane_funcs,
+				       db9000_fmts,
+				       ARRAY_SIZE(db9000_fmts), NULL,
+				       type, "rzn1_primary_rgb");
+	if (ret < 0)
+		return NULL;
+
+	drm_plane_helper_add(plane, &db9000_plane_helper_funcs);
+
+	DRM_DEBUG_DRIVER("plane:%d created\n", plane->base.id);
+
+	return plane;
+}
+
+static void db9000_plane_destroy_all(struct drm_device *ddev)
+{
+	struct drm_plane *plane, *plane_temp;
+
+	list_for_each_entry_safe(plane, plane_temp,
+				 &ddev->mode_config.plane_list, head)
+				 drm_plane_cleanup(plane);
+}
+
+static int db9000_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
+{
+	struct drm_plane *primary;
+	int ret;
+
+	primary = db9000_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
+	if (!primary) {
+		DRM_ERROR("Can not create primary plane\n");
+		return -EINVAL;
+	}
+
+	ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+					&db9000_crtc_funcs, NULL);
+	if (ret) {
+		DRM_ERROR("Can not initialize CRTC\n");
+		goto cleanup;
+	}
+
+	drm_crtc_helper_add(crtc, &db9000_crtc_helper_funcs);
+	DRM_DEBUG_DRIVER("CRTC:%d created\n", crtc->base.id);
+	return 0;
+
+cleanup:
+	db9000_plane_destroy_all(ddev);
+	return ret;
+}
+
+/*
+ * DRM_ENCODER
+ */
+static const struct drm_encoder_funcs db9000_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int db9000_encoder_init(struct drm_device *ddev,
+			       struct drm_bridge *bridge)
+{
+	struct drm_encoder *encoder;
+	int ret;
+
+	encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL);
+	if (!encoder)
+		return -ENOMEM;
+
+	encoder->possible_crtcs = NR_CRTC;
+	encoder->possible_clones = 0; /* No cloning support */
+
+	drm_encoder_init(ddev, encoder, &db9000_encoder_funcs,
+			 DRM_MODE_ENCODER_NONE, NULL);
+
+	ret = drm_bridge_attach(encoder, bridge, NULL);
+	if (ret) {
+		drm_encoder_cleanup(encoder);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id);
+
+	return 0;
+}
+
+void __maybe_unused db9000_suspend(struct drm_device *ddev)
+{
+	struct db9000_device *db9000_dev = container_of(ddev,
+						    struct db9000_device, drm);
+
+	clk_disable_unprepare(db9000_dev->lcd_eclk);
+}
+
+int __maybe_unused db9000_resume(struct drm_device *ddev)
+{
+	struct db9000_device *db9000_dev = container_of(ddev,
+						    struct db9000_device, drm);
+	int ret;
+
+	ret = clk_prepare_enable(db9000_dev->lcd_eclk);
+	if (ret) {
+		DRM_ERROR("failed to enable pixel clock (%d)\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int db9000_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	return pm_runtime_get_sync(chip->dev);
+}
+
+static void db9000_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+	pm_runtime_put(chip->dev);
+}
+
+static int db9000_pwm_enable(struct db9000_device *db9000_dev)
+{
+	u32 reg_pwmfr = reg_read(db9000_dev->regs, db9000_dev->addr_pwmfr);
+
+	reg_pwmfr |= DB9000_PWMFR_PWMFCE;
+	reg_write(db9000_dev->regs, db9000_dev->addr_pwmfr, reg_pwmfr);
+
+	return 0;
+}
+
+static void db9000_pwm_disable(struct db9000_device *db9000_dev)
+{
+	u32 reg_pwmfr = reg_read(db9000_dev->regs, db9000_dev->addr_pwmfr);
+
+	reg_pwmfr &= ~DB9000_PWMFR_PWMFCE;
+	reg_write(db9000_dev->regs, db9000_dev->addr_pwmfr, reg_pwmfr);
+}
+
+static int db9000_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+			    struct pwm_state *state)
+{
+	struct pwm_state cur_state;
+	struct db9000_device *db9000_dev = pwm_chip_to_db9000(chip);
+	int ret;
+
+	pwm_get_state(pwm, &cur_state);
+
+	if (state->enabled)
+		ret = db9000_pwm_enable(db9000_dev);
+	else
+		db9000_pwm_disable(db9000_dev);
+
+	if (state->period != cur_state.period) {
+		u32 pwmfcd;
+
+		pwmfcd = clk_get_rate(db9000_dev->lcd_eclk) / 256;
+		pwmfcd *= state->period;
+		pwmfcd = DB9000_PWMFR_PWMFCD(pwmfcd - 1);
+		reg_write(db9000_dev->regs, db9000_dev->addr_pwmfr, pwmfcd);
+	}
+
+	if (state->duty_cycle == 0) {
+		db9000_pwm_disable(db9000_dev);
+	} else if (state->period != cur_state.period ||
+	    state->duty_cycle != cur_state.duty_cycle) {
+		u32 dcr = div_u64((state->duty_cycle * ULL(256)),
+				   state->period) - 1;
+
+		reg_write(db9000_dev->regs, db9000_dev->addr_pwmdcr, dcr);
+	}
+
+	return ret;
+}
+
+static const struct pwm_ops db9000_pwm_ops = {
+	.request = db9000_pwm_request,
+	.free = db9000_pwm_free,
+	.apply = db9000_pwm_apply,
+	.owner = THIS_MODULE,
+};
+
+static int db9000_pwm_setup(struct device *dev,
+			    struct db9000_device *db9000_dev)
+{
+	struct pwm_chip *db9000_pwm;
+	int ret;
+
+	db9000_pwm = devm_kzalloc(dev, sizeof(*db9000_pwm), GFP_KERNEL);
+	if (db9000_pwm == NULL)
+		return -ENOMEM;
+
+	db9000_pwm = &db9000_dev->pwm;
+
+	db9000_pwm->dev = dev;
+	db9000_pwm->ops = &db9000_pwm_ops;
+	db9000_pwm->base = -1;
+	db9000_pwm->npwm = 1;
+
+	ret = pwmchip_add(db9000_pwm);
+	if (ret < 0) {
+		dev_err(dev, "failed to register PWM chip: %d\n", ret);
+		return ret;
+	}
+
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+
+int db9000_load(struct drm_device *ddev, int rzn1_pwm)
+{
+	struct platform_device *pdev = to_platform_device(ddev->dev);
+	struct device *dev = ddev->dev;
+	struct db9000_device *db9000_dev = container_of(ddev,
+						    struct db9000_device, drm);
+	struct device_node *np = dev->of_node;
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
+	struct drm_crtc *crtc;
+	struct reset_control *rstc;
+	struct resource *res;
+	int ret;
+
+	spin_lock_init(&db9000_dev->lock);
+
+	if (rzn1_pwm) {
+		db9000_dev->addr_pwmfr = DB9000_PWMFR_RZN1;
+		db9000_dev->addr_pwmdcr = DB9000_PWMDCR_RZN1;
+	} else {
+		db9000_dev->addr_pwmfr = DB9000_PWMFR_0;
+		db9000_dev->addr_pwmdcr = DB9000_PWMDCR_0;
+	}
+
+	/* Panel Setup */
+	ret = drm_of_find_panel_or_bridge(np, 0, 0, &panel, &bridge);
+	if (ret != 0) {
+		DRM_ERROR("Could not get Panel or bridge");
+		return ret;
+	}
+
+	rstc = devm_reset_control_get_exclusive(dev, NULL);
+
+	/* Clock setup */
+	db9000_dev->lcd_eclk = devm_clk_get(dev, "lcd_eclk");
+
+	if (IS_ERR(db9000_dev->lcd_eclk)) {
+		DRM_ERROR("Unable to get pixel clock\n");
+		return -ENODEV;
+	}
+
+	if (clk_prepare_enable(db9000_dev->lcd_eclk)) {
+		DRM_ERROR("Unable to prepare pixel clock\n");
+		return -ENODEV;
+	}
+
+	/* Memory setup */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		DRM_ERROR("Could not retrieve platform resources\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	db9000_dev->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(db9000_dev->regs)) {
+		DRM_ERROR("Unable to get memory resource\n");
+		ret = PTR_ERR(db9000_dev->regs);
+		goto err;
+	}
+
+	db9000_bpp_setup(db9000_dev, db9000_dev->bpp, db9000_dev->bus_width,
+			 false);
+
+	db9000_toggle_controller(db9000_dev, true);
+
+	/* Add endpoints panels or bridges if any */
+	if (panel) {
+		bridge = drm_panel_bridge_add(panel,
+					      DRM_MODE_CONNECTOR_Unknown);
+		if (IS_ERR(bridge)) {
+			DRM_ERROR("panel-bridge endpoint\n");
+			ret = PTR_ERR(bridge);
+			goto err;
+		}
+	}
+
+	if (bridge) {
+		ret = db9000_encoder_init(ddev, bridge);
+		if (ret) {
+			DRM_ERROR("init encoder endpoint\n");
+			goto err;
+		}
+	}
+
+	db9000_dev->connector = panel->connector;
+
+	/* CRTC setup  */
+	crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL);
+	if (!crtc) {
+		DRM_ERROR("Failed to allocate crtc\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = db9000_crtc_init(&db9000_dev->drm, crtc);
+	if (ret) {
+		DRM_ERROR("Failed to init crtc\n");
+		goto err;
+	}
+
+	if (!IS_ERR(rstc)) {
+		reset_control_assert(rstc);
+		usleep_range(10, 20);
+		reset_control_deassert(rstc);
+	}
+
+	return db9000_pwm_setup(dev, db9000_dev);
+
+err:
+	drm_panel_bridge_remove(bridge);
+
+	clk_disable_unprepare(db9000_dev->lcd_eclk);
+
+	return ret;
+}
+
+void db9000_unload(struct drm_device *ddev)
+{
+	struct db9000_device *db9000_dev;
+
+	db9000_dev = container_of(ddev, struct db9000_device, drm);
+
+	drm_of_panel_bridge_remove(ddev->dev->of_node, 0, 0);
+	clk_disable_unprepare(db9000_dev->lcd_eclk);
+}
+
+static const struct drm_mode_config_funcs drv_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static int db9000_gem_cma_dumb_create(struct drm_file *file,
+				      struct drm_device *dev,
+				      struct drm_mode_create_dumb *args)
+{
+	return drm_gem_cma_dumb_create_internal(file, dev, args);
+}
+
+DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops);
+
+static struct drm_driver drv_driver = {
+	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
+			   DRIVER_ATOMIC,
+	.name = "drm-db9000",
+	.desc = "Digital Blocks DB9000 DRM Driver",
+	.date = "20190825",
+	.major = 1,
+	.minor = 0,
+	.patchlevel = 0,
+	.fops = &drv_driver_fops,
+	.dumb_create = db9000_gem_cma_dumb_create,
+	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+	.gem_free_object_unlocked = drm_gem_cma_free_object,
+	.gem_vm_ops = &drm_gem_cma_vm_ops,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_import = drm_gem_prime_import,
+	.gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
+	.gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
+	.gem_prime_vmap = drm_gem_cma_prime_vmap,
+	.gem_prime_vunmap = drm_gem_cma_prime_vunmap,
+	.gem_prime_mmap = drm_gem_cma_prime_mmap,
+};
+
+static int drv_load(struct drm_device *ddev, u32 bpp,
+		    u32 bus_width, int rzn1_pwm)
+{
+	struct platform_device *pdev = to_platform_device(ddev->dev);
+	struct db9000_device *db9000_dev = container_of(ddev,
+						    struct db9000_device, drm);
+	int ret;
+
+	drm_mode_config_init(ddev);
+
+	/*
+	 * set max width and height as default value.
+	 * this value would be used to check framebuffer size limitation
+	 * at drm_mode_addfb().
+	 */
+	ddev->mode_config.min_width = 0;
+	ddev->mode_config.min_height = 0;
+	ddev->mode_config.max_width = DB9000_FB_MAX_WIDTH;
+	ddev->mode_config.max_height = DB9000_FB_MAX_WIDTH;
+	ddev->mode_config.funcs = &drv_mode_config_funcs;
+
+	db9000_dev->bus_width = bus_width;
+
+	ret = db9000_load(ddev, rzn1_pwm);
+
+	if (ret)
+		goto err;
+
+	drm_mode_config_reset(ddev);
+
+	drm_kms_helper_poll_init(ddev);
+
+	platform_set_drvdata(pdev, ddev);
+
+	return 0;
+
+err:
+	drm_mode_config_cleanup(ddev);
+
+	return ret;
+}
+
+static void drv_unload(struct drm_device *ddev)
+{
+	drm_kms_helper_poll_fini(ddev);
+	db9000_unload(ddev);
+	drm_mode_config_cleanup(ddev);
+}
+
+static __maybe_unused int db9000_drv_suspend(struct device *dev)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct db9000_device *db9000_dev = container_of(ddev,
+							struct db9000_device,
+							drm);
+	struct drm_atomic_state *state;
+
+	db9000_toggle_controller(db9000_dev, false);
+
+	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);
+	}
+	db9000_dev->suspend_state = state;
+	db9000_suspend(ddev);
+
+	return 0;
+}
+
+static __maybe_unused int db9000_drv_resume(struct device *dev)
+{
+	struct drm_device *ddev = dev_get_drvdata(dev);
+	struct db9000_device *db9000_dev = container_of(ddev,
+							struct db9000_device,
+							drm);
+
+	db9000_resume(ddev);
+	drm_atomic_helper_resume(ddev, db9000_dev->suspend_state);
+	drm_kms_helper_poll_enable(ddev);
+	db9000_toggle_controller(db9000_dev, true);
+
+	return 0;
+}
+
+static const struct dev_pm_ops drv_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(db9000_drv_suspend, db9000_drv_resume)
+};
+
+static int db9000_drm_platform_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct drm_device *ddev;
+	struct db9000_device *db9000_dev;
+	struct device_node *np = dev->of_node;
+	u32 bpp;
+	u32 bus_width;
+	int rzn1_pwm = 0;
+	int ret;
+
+	dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+
+	db9000_dev = kzalloc(sizeof(*db9000_dev), GFP_KERNEL);
+	if (!db9000_dev)
+		return -ENOMEM;
+
+	drm_dev_init(&db9000_dev->drm, &drv_driver, dev);
+	ddev = &db9000_dev->drm;
+
+	/* Parse the DTB */
+	ret = of_property_read_u32(np, "bits-per-pixel", &bpp);
+	if (ret)
+		bpp = 16;
+
+	if (bpp != 16 && bpp != 24 && bpp != 32) {
+		DRM_WARN("bits-per-pixel value invalid, default is 16 bpp");
+		bpp = 16;
+	}
+
+	ret = of_property_read_u32(np, "bus-width", &bus_width);
+	if (ret)
+		bus_width = 24;
+
+	rzn1_pwm = (int) of_device_get_match_data(dev);
+
+	ret = drv_load(ddev, bpp, bus_width, rzn1_pwm);
+	if (ret)
+		goto err_put;
+
+	ret = drm_dev_register(ddev, 0);
+	if (ret)
+		goto err_put;
+
+	ret = drm_fbdev_generic_setup(ddev, bpp);
+	if (ret)
+		goto err_put;
+
+	dev_info(dev, "DB9000 LCD Controller Ready");
+
+	return 0;
+
+err_put:
+	drm_dev_put(ddev);
+
+	return ret;
+}
+
+static int db9000_drm_platform_remove(struct platform_device *pdev)
+{
+	struct drm_device *ddev = platform_get_drvdata(pdev);
+
+	drv_unload(ddev);
+	drm_dev_put(ddev);
+
+	return 0;
+}
+
+static const struct of_device_id drv_dt_ids[] = {
+	{ .compatible = "digital-blocks,drm-db9000"},
+	{ .compatible = "digital-blocks,drm-rzn1", .data = RZN1_REGS },
+	{ /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, drv_dt_ids);
+
+static struct platform_driver db9000_drm_platform_driver = {
+	.probe = db9000_drm_platform_probe,
+	.remove = db9000_drm_platform_remove,
+	.driver = {
+		.name = "drm-db9000",
+		.of_match_table = drv_dt_ids,
+		.pm = &drv_pm_ops,
+	},
+};
+
+module_platform_driver(db9000_drm_platform_driver);
+
+MODULE_AUTHOR("Gareth Williams <gareth.williams.jx at renesas.com>");
+MODULE_DESCRIPTION("Digital Blocks DB9000 LCD Controller Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/digital-blocks/db9000-du.h b/drivers/gpu/drm/digital-blocks/db9000-du.h
new file mode 100644
index 0000000..325c9f0
--- /dev/null
+++ b/drivers/gpu/drm/digital-blocks/db9000-du.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019 Renesas Electronics Europe Ltd.
+ *
+ * Author: Gareth Williams <gareth.williams.jx at renesas.com>
+ *
+ * Based on ltdc.h
+ * Copyright (C) STMicroelectronics SA 2017
+ *
+ * Authors: Philippe Cornu <philippe.cornu at st.com>
+ *          Yannick Fertre <yannick.fertre at st.com>
+ *          Fabien Dessenne <fabien.dessenne at st.com>
+ *          Mickael Reulier <mickael.reulier at st.com>
+ */
+#ifndef __DB9000_DU_H__
+#define __DB9000_DU_H__
+
+#include <linux/backlight.h>
+#include <linux/interrupt.h>
+#include <linux/pwm.h>
+
+#define DB9000_MAX_LAYER		1
+
+/* LCD Controller Control Register 1 */
+#define DB9000_CR1			0x000
+/* Horizontal Timing Register */
+#define DB9000_HTR			0x008
+/* Vertical Timing Register 1 */
+#define DB9000_VTR1			0x00C
+/* Vertical Timing Register 2 */
+#define DB9000_VTR2			0x010
+/* Pixel Clock Timing Register */
+#define DB9000_PCTR			0x014
+/* Interrupt Status Register */
+#define DB9000_ISR			0x018
+/* Interrupt Mask Register */
+#define DB9000_IMR			0x01C
+/* Interrupt Vector Register */
+#define DB9000_IVR			0x020
+/* Interrupt Scan Compare Register */
+#define DB9000_ISCR			0x024
+/* DMA Base Address Register */
+#define DB9000_DBAR			0x028
+/* DMA Current Address Register */
+#define DB9000_DCAR			0x02C
+/* DMA End Address Register */
+#define DB9000_DEAR			0x030
+/* DMA Horizontal and Vertical Timing Extension Register */
+#define DB9000_HVTER			0x044
+/* Horizontal Pixels-Per-Line Override Control */
+#define DB9000_HPPLOR			0x048
+/* Horizontal Pixels-Per-Line Override Enable */
+#define DB9000_HPOE			BIT(31)
+/* GPIO Register */
+#define DB9000_GPIOR			0x1F8
+/* Core Identification Register */
+#define DB9000_CIR			0x1FC
+/* Palette Data Words */
+#define DB9000_PALT			0x200
+
+/* Control Register 1, Offset 0x000 */
+/* LCD Controller Enable */
+#define DB9000_CR1_LCE			BIT(0)
+/* LCD Power Enable */
+#define DB9000_CR1_LPE			BIT(1)
+/* LCD Bits per Pixel */
+#define DB9000_CR1_BPP(x)		(((x) & 0x7) << 2)
+/* RGB or BGR Format */
+#define DB9000_CR1_RGB			BIT(5)
+/* Data Enable Polarity */
+#define DB9000_CR1_DEP			BIT(8)
+/* Pixel Clock Polarity */
+#define DB9000_CR1_PCP			BIT(9)
+/* Horizontal Sync Polarity */
+#define DB9000_CR1_HSP			BIT(10)
+/* Vertical Sync Polarity */
+#define DB9000_CR1_VSP			BIT(11)
+/* Output Pixel Select */
+#define DB9000_CR1_OPS(x)		(((x) & 0x7) << 12)
+/* FIFO DMA Request Words */
+#define DB9000_CR1_FDW(x)		(((x) & 0x3) << 16)
+/* LCD 1 or Port Select */
+#define DB9000_CR1_LPS			BIT(18)
+/* Frame Buffer 24bpp Packed Word */
+#define DB9000_CR1_FBP			BIT(19)
+
+enum DB9000_CR1_BPP {
+	/* 1 bit per pixel */
+	DB9000_CR1_BPP_1bpp,
+	/* 2 bits per pixel */
+	DB9000_CR1_BPP_2bpp,
+	/* 4 bits per pixel */
+	DB9000_CR1_BPP_4bpp,
+	/* 8 bits per pixel */
+	DB9000_CR1_BPP_8bpp,
+	/* 16 bits per pixel */
+	DB9000_CR1_BPP_16bpp,
+	/* 18 bits per pixel */
+	DB9000_CR1_BPP_18bpp,
+	/* 24 bits per pixel */
+	DB9000_CR1_BPP_24bpp
+} DB9000_CR1_BPP_VAL;
+
+/* Horizontal Timing Register, Offset 0x008 */
+/* Horizontal Front Porch */
+#define DB9000_HTR_HFP(x)		(((x) & 0xff) << 0)
+/* Pixels per Line */
+#define DB9000_HTR_PPL(x)		(((x) & 0xff) << 8)
+/* Horizontal Back Porch */
+#define DB9000_HTR_HBP(x)		(((x) & 0xff) << 16)
+/* Horizontal Sync Width */
+#define DB9000_HTR_HSW(x)		(((x) & 0xff) << 24)
+
+/* Vertical Timing Register 1, Offset 0x00C */
+/* Vertical Sync Width */
+#define DB9000_VTR1_VSW(x)		(((x) & 0xff) << 0)
+/* Vertical Front Porch */
+#define DB9000_VTR1_VFP(x)		(((x) & 0xff) << 8)
+/* Vertical Back Porch */
+#define DB9000_VTR1_VBP(x)		(((x) & 0xff) << 16)
+
+/* Vertical Timing Register 2, Offset 0x010 */
+/* Lines Per Panel */
+#define DB9000_VTR2_LPP(x)		(((x) & 0xfff) << 0)
+
+/* Vertical and Horizontal Timing Extension Register, Offset 0x044 */
+/* Horizontal Front Porch Extension */
+#define DB9000_HVTER_HFPE(x)		((((x) >> 8) & 0x3) << 0)
+/* Horizontal Back Porch Extension */
+#define DB9000_HVTER_HBPE(x)		((((x) >> 8) & 0x3) << 4)
+/* Vertical Front Porch Extension */
+#define DB9000_HVTER_VFPE(x)		((((x) >> 8) & 0x3) << 8)
+/* Vertical Back Porch Extension */
+#define DB9000_HVTER_VBPE(x)		((((x) >> 8) & 0x3) << 12)
+
+/* clock reset select */
+#define DB9000_PCTR_PCR			BIT(10)
+
+/* Interrupt Status Register, Offset 0x018 */
+#define DB9000_ISR_OFU			BIT(0) /* Output FIFO Underrun */
+#define DB9000_ISR_OFO			BIT(1) /* Output FIFO Overrun */
+#define DB9000_ISR_IFU			BIT(2) /* Input FIFO Underrun */
+#define DB9000_ISR_IFO			BIT(3) /* Input FIFO Overrun */
+#define DB9000_ISR_FER			BIT(4) /* OR of OFU, OFO, IFU, IFO */
+#define DB9000_ISR_MBE			BIT(5) /* Master Bus Error */
+#define DB9000_ISR_VCT			BIT(6) /* Vertical Compare Triggered */
+#define DB9000_ISR_BAU			BIT(7) /* DMA Base Address Register Update to CAR */
+#define DB9000_ISR_LDD			BIT(8) /* LCD Controller Disable Done */
+
+/* Interrupt Mask Register, Offset 0x01C */
+#define DB9000_IMR_OFUM			BIT(0) /* Output FIFO Underrun - Mask */
+#define DB9000_IMR_OFOM			BIT(1) /* Output FIFO Overrun - Mask */
+#define DB9000_IMR_IFUM			BIT(2) /* Input FIFO Underrun - Mask */
+#define DB9000_IMR_IFOM			BIT(3) /* Input FIFO Overrun - Mask */
+#define DB9000_IMR_FERM			BIT(4) /* OR of OFU, OFO, IFU, IFO - Mask */
+#define DB9000_IMR_MBEM			BIT(5) /* Master Bus Error - Mask */
+#define DB9000_IMR_VCTM			BIT(6) /* Vertical Compare Triggered - Mask */
+/* DMA Base Address Register Update to CAR - Mask */
+#define DB9000_IMR_BAUM			BIT(7)
+#define DB9000_IMR_LDDM			BIT(8) /* LCD Controller Disable Done - Mask */
+
+/* PWM Frequency Register */
+#define DB9000_PWMFR_0			0x034
+#define DB9000_PWMFR_RZN1		0x04C
+/* PWM Duty Cycle Register */
+#define DB9000_PWMDCR_0			0x038
+#define DB9000_PWMDCR_RZN1		0x050
+/* PWM Frequency Registers, Offset 0x034 and 0x04c */
+#define DB9000_PWMFR_PWMFCD(x)		(((x) & 0x3fffff) << 0)
+#define DB9000_PWMFR_PWMFCE		BIT(22)
+
+struct fps_info {
+	unsigned int counter;
+	ktime_t last_timestamp;
+};
+
+struct db9000_device {
+	struct drm_device drm;
+	struct pwm_chip pwm;
+	struct drm_connector *connector;
+	void __iomem *regs;
+	spinlock_t lock;
+	struct clk *lcd_eclk;
+	struct fps_info plane_fpsi[DB9000_MAX_LAYER];
+	struct drm_atomic_state *suspend_state;
+	u8 bpp;
+	int bus_width;
+	size_t frame_size;
+	u32 addr_pwmfr;
+	u32 addr_pwmdcr;
+};
+
+#endif /* __DB9000_DU_H__ */
-- 
2.7.4



More information about the dri-devel mailing list