[PATCH 1/2] drm: Add drm driver for OpenCores VGA/LCD display controller

Andrea Merello andrea.merello at gmail.com
Thu Jun 9 13:32:55 UTC 2016


This driver supports the VGA/LCD core available from OpenCores:
http://opencores.org/project,vga_lcd

It's intended as a replacement for the "ocfb" framebuffer driver

Signed-off-by: Andrea Merello <andrea.merello at gmail.com>
Cc: Stefan Kristiansson <stefan.kristiansson at saunalahti.fi>
Cc: Tomi Valkeinen <tomi.valkeinen at ti.com>
Cc: Francesco Diotalevi <francesco.diotalevi at iit.it>
Cc: Claudio Lorini <claudio.lorini at iit.it>
---
 drivers/gpu/drm/Kconfig               |   2 +
 drivers/gpu/drm/Makefile              |   1 +
 drivers/gpu/drm/ocdrm/Kconfig         |   7 +
 drivers/gpu/drm/ocdrm/Makefile        |   7 +
 drivers/gpu/drm/ocdrm/ocdrm_crtc.c    | 336 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/ocdrm/ocdrm_crtc.h    |  48 +++++
 drivers/gpu/drm/ocdrm/ocdrm_drv.c     | 312 +++++++++++++++++++++++++++++++
 drivers/gpu/drm/ocdrm/ocdrm_drv.h     |  89 +++++++++
 drivers/gpu/drm/ocdrm/ocdrm_encoder.c |  95 ++++++++++
 drivers/gpu/drm/ocdrm/ocdrm_encoder.h |  48 +++++
 10 files changed, 945 insertions(+)
 create mode 100644 drivers/gpu/drm/ocdrm/Kconfig
 create mode 100644 drivers/gpu/drm/ocdrm/Makefile
 create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_crtc.c
 create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_crtc.h
 create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_drv.c
 create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_drv.h
 create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_encoder.c
 create mode 100644 drivers/gpu/drm/ocdrm/ocdrm_encoder.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index fc35731..48f56e4 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -290,3 +290,5 @@ source "drivers/gpu/drm/arc/Kconfig"
 source "drivers/gpu/drm/hisilicon/Kconfig"
 
 source "drivers/gpu/drm/mediatek/Kconfig"
+
+source "drivers/gpu/drm/ocdrm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index be43afb..871da6a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -82,3 +82,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/
 obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/
 obj-$(CONFIG_DRM_ARCPGU)+= arc/
 obj-y			+= hisilicon/
+obj-$(CONFIG_DRM_OCDRM) += ocdrm/
diff --git a/drivers/gpu/drm/ocdrm/Kconfig b/drivers/gpu/drm/ocdrm/Kconfig
new file mode 100644
index 0000000..a918503
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/Kconfig
@@ -0,0 +1,7 @@
+config DRM_OCDRM
+	tristate "DRM Support for opencores OCFB"
+	depends on DRM
+	default	n
+	select DRM_KMS_HELPER
+	select DRM_KMS_CMA_HELPER
+	select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
diff --git a/drivers/gpu/drm/ocdrm/Makefile b/drivers/gpu/drm/ocdrm/Makefile
new file mode 100644
index 0000000..4ea17d2
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ocdrm-y := ocdrm_crtc.o ocdrm_drv.o ocdrm_encoder.o
+
+obj-$(CONFIG_DRM_OCDRM) += ocdrm.o
diff --git a/drivers/gpu/drm/ocdrm/ocdrm_crtc.c b/drivers/gpu/drm/ocdrm/ocdrm_crtc.c
new file mode 100644
index 0000000..ebfe03e
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/ocdrm_crtc.c
@@ -0,0 +1,336 @@
+/*
+ * Open cores VGA/LCD 2.0 core DRM driver
+ * Copyright (c) 2016 Istituto Italiano di Tecnologia
+ * Electronic Design Lab.
+ *
+ * Author: Andrea Merello <andrea.merello at gmail.com>
+ *
+ * Based on the following drivers:
+ *   - Analog Devices AXI HDMI DRM driver, which is
+ *     Copyright 2012 Analog Devices Inc.
+ *
+ *   - ARC PGU DRM driver.
+ *     Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ *   - ARM HDLCD Driver
+ *     Copyright (C) 2013-2015 ARM Limited
+ *
+ *   - Atmel atmel-hlcdc driver, which is
+ *     Copyright (C) 2014 Traphandler
+ *     Copyright (C) 2014 Free Electrons
+ *
+ *   - OpenCores VGA/LCD 2.0 core frame buffer driver
+ *     Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson at saunalahti.fi
+ *
+ *   - R-Car Display Unit DRM driver
+ *     Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/slab.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic_helper.h>
+
+#include "ocdrm_crtc.h"
+
+
+static inline struct ocdrm_priv *crtc_to_ocdrm(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct ocdrm_priv, crtc);
+}
+
+static inline struct ocdrm_priv *plane_to_ocdrm(struct drm_plane *plane)
+{
+	return container_of(plane, struct ocdrm_priv, plane);
+}
+
+static void ocdrm_plane_atomic_update(struct drm_plane *plane,
+				struct drm_plane_state *old_state)
+{
+	struct drm_gem_cma_object *obj;
+	u32 val;
+	uint32_t pixel_format;
+	int hgate;
+	struct ocdrm_priv *priv = plane_to_ocdrm(plane);
+
+	if (!plane->state->crtc || !plane->state->fb)
+		return;
+
+	pixel_format = plane->state->fb->pixel_format;
+	hgate = plane->state->crtc->state->adjusted_mode.crtc_hdisplay;
+
+	val = ocdrm_readreg(priv, OCFB_CTRL);
+	ocdrm_writereg(priv, OCFB_CTRL, val & ~OCFB_CTRL_VEN);
+
+	if (!drm_atomic_plane_disabling(plane, plane->state)) {
+		obj = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
+		ocdrm_writereg(priv, OCFB_VBARA, obj->paddr);
+
+		val &= ~(OCFB_CTRL_CD8 | OCFB_CTRL_CD16 |
+			OCFB_CTRL_CD24 | OCFB_CTRL_CD32);
+		val &= ~(OCFB_CTRL_VBL8 | OCFB_CTRL_VBL4 |
+			OCFB_CTRL_VBL2 | OCFB_CTRL_VBL1);
+
+		switch (pixel_format) {
+		/* TODO
+		 *case DRM_FORMAT_RGB332:
+		 *	hgate /= 4;
+		 *	val |= OCFB_CTRL_CD8;
+		 *	val |= OCFB_CTRL_PC;
+		 *	break;
+		 */
+
+		case DRM_FORMAT_RGB565:
+			dev_dbg(priv->drm_dev->dev, "16 bpp\n");
+			hgate /= 2;
+			val |= OCFB_CTRL_CD16;
+			break;
+
+		case DRM_FORMAT_RGB888:
+			dev_dbg(priv->drm_dev->dev, "24 bpp\n");
+			hgate = hgate * 3 / 4;
+			val |= OCFB_CTRL_CD24;
+			break;
+
+		case DRM_FORMAT_XRGB8888:
+			dev_dbg(priv->drm_dev->dev, "32 bpp\n");
+			val |= OCFB_CTRL_CD32;
+			break;
+
+		default:
+			dev_err(priv->drm_dev->dev, "Invalid pixelformat specified\n");
+			return;
+		}
+
+		if ((0 == (obj->paddr & 0x1f)) && (0 == (hgate % 8))) {
+			dev_dbg(priv->drm_dev->dev, "dma burst 8 cycles\n");
+			val |= OCFB_CTRL_VBL8;
+		} else if ((0 == (obj->paddr & 0xf)) && (0 == (hgate % 4))) {
+			dev_dbg(priv->drm_dev->dev, "dma burst 4 cycles\n");
+			val |= OCFB_CTRL_VBL4;
+		} else if ((0 == (obj->paddr & 0x7)) && (0 == (hgate % 2))) {
+			dev_dbg(priv->drm_dev->dev, "dma burst 2 cycles\n");
+			val |= OCFB_CTRL_VBL2;
+		} else {
+			dev_dbg(priv->drm_dev->dev, "dma burst 1 cycle\n");
+			val |= OCFB_CTRL_VBL1;
+		}
+
+		ocdrm_writereg(priv, OCFB_CTRL, val | OCFB_CTRL_VEN);
+	}
+}
+
+static void ocdrm_crtc_enable(struct drm_crtc *crtc)
+{
+
+	struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
+
+	if (!priv->clk_enabled)
+		clk_prepare_enable(priv->pixel_clock);
+	priv->clk_enabled = true;
+}
+
+static void ocdrm_crtc_disable(struct drm_crtc *crtc)
+{
+	struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
+
+	/* why the plane has been not disabled ? .. we get here from destroy  */
+	ocdrm_writereg(priv, OCFB_CTRL, 0);
+
+	if (priv->clk_enabled)
+		clk_disable_unprepare(priv->pixel_clock);
+	priv->clk_enabled = false;
+}
+
+static void ocdrm_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+	u32 ctrl;
+	int ret;
+	struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
+	struct drm_display_mode *m = &crtc->state->adjusted_mode;
+	uint32_t hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
+	uint32_t vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
+	uint32_t vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
+	uint32_t hback_porch = m->crtc_htotal - m->crtc_hsync_end;
+
+	ctrl = ocdrm_readreg(priv, OCFB_CTRL);
+	ocdrm_writereg(priv, OCFB_CTRL, ctrl & ~OCFB_CTRL_VEN);
+
+	/* Horizontal timings */
+	ocdrm_writereg(priv, OCFB_HTIM, (hsync_len - 1) << 24 |
+		      (hback_porch - 1) << 16 | (m->crtc_hdisplay - 1));
+
+	/* Vertical timings */
+	ocdrm_writereg(priv, OCFB_VTIM, (vsync_len - 1) << 24 |
+		      (vback_porch - 1) << 16 | (m->crtc_vdisplay - 1));
+
+	ocdrm_writereg(priv, OCFB_HVLEN, ((uint32_t)m->crtc_htotal - 1) << 16 |
+		(m->crtc_vtotal - 1));
+
+	dev_dbg(priv->drm_dev->dev, "set mode H slen %u, bporch %u, tot %u\n",
+		hsync_len, hback_porch, m->crtc_htotal);
+	dev_dbg(priv->drm_dev->dev, "set mode V slen %u, bporch %u, tot %u\n",
+		vsync_len, vback_porch, m->crtc_vtotal);
+
+	if (m->flags & DRM_MODE_FLAG_NHSYNC)
+		ctrl |= OCFB_CTRL_HSL;
+	else
+		ctrl &= ~OCFB_CTRL_HSL;
+
+	if (m->flags & DRM_MODE_FLAG_NVSYNC)
+		ctrl |= OCFB_CTRL_VSL;
+	else
+		ctrl &= ~OCFB_CTRL_VSL;
+
+	dev_dbg(priv->drm_dev->dev, "VPOL %d, HPOL %d\n",
+		m->flags & DRM_MODE_FLAG_NVSYNC,
+		m->flags & DRM_MODE_FLAG_NHSYNC);
+
+
+	/* Set sync polarity. */
+	ocdrm_writereg(priv, OCFB_CTRL, ctrl);
+
+	if (priv->clk_enabled)
+		clk_disable_unprepare(priv->pixel_clock);
+
+	ret = clk_set_rate(priv->pixel_clock, m->crtc_clock * 1000);
+	if (ret) {
+		dev_err(priv->drm_dev->dev, "failed to set pixclk %d\n", ret);
+		return;
+	}
+
+	if (priv->clk_enabled)
+		clk_prepare_enable(priv->pixel_clock);
+
+	dev_dbg(priv->drm_dev->dev, "pixel clock: %d\n", m->crtc_clock);
+
+	/* if video was enabled, then enable it */
+	ocdrm_writereg(priv, OCFB_CTRL, ctrl);
+}
+
+static bool ocdrm_crtc_mode_fixup(struct drm_crtc *crtc,
+				const struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
+
+	if (mode->clock < 16000 || mode->clock > 165000)
+		return false;
+
+	adjusted_mode->clock = clk_round_rate(priv->pixel_clock,
+					mode->clock * 1000) / 1000;
+	return true;
+}
+
+static int ocdrm_crtc_atomic_check(struct drm_crtc *crtc,
+				struct drm_crtc_state *state)
+{
+	struct ocdrm_priv *priv = crtc_to_ocdrm(crtc);
+	struct drm_display_mode *m = &state->adjusted_mode;
+	uint32_t hsync_len = m->crtc_hsync_end - m->crtc_hsync_start;
+	uint32_t vsync_len = m->crtc_vsync_end - m->crtc_vsync_start;
+	uint32_t vback_porch = m->crtc_vtotal - m->crtc_vsync_end;
+	uint32_t hback_porch = m->crtc_htotal - m->crtc_hsync_end;
+	int rate;
+
+	if (m->clock < 16000 || m->clock > 165000)
+		return false;
+
+	rate = clk_round_rate(priv->pixel_clock, m->clock * 1000) / 1000;
+
+	if (m->clock != rate)
+		return -EINVAL;
+
+	if (hsync_len > 255 || vsync_len > 255 ||
+		vback_porch > 255 || hback_porch > 255)
+		return -EINVAL;
+
+	return 0;
+}
+
+static struct drm_crtc_helper_funcs ocdrm_crtc_helper_funcs = {
+	.mode_fixup	= ocdrm_crtc_mode_fixup,
+	.mode_set	= drm_helper_crtc_mode_set,
+	.mode_set_base	= drm_helper_crtc_mode_set_base,
+	.mode_set_nofb	= ocdrm_crtc_mode_set_nofb,
+	.disable	= ocdrm_crtc_disable,
+	.enable		= ocdrm_crtc_enable,
+	.atomic_check	= ocdrm_crtc_atomic_check,
+};
+
+static void ocdrm_crtc_destroy(struct drm_crtc *crtc)
+{
+	ocdrm_crtc_disable(crtc);
+	drm_crtc_cleanup(crtc);
+}
+
+static struct drm_crtc_funcs ocdrm_crtc_funcs = {
+	.page_flip		= drm_atomic_helper_page_flip,
+	.set_config		= drm_atomic_helper_set_config,
+	.destroy		= ocdrm_crtc_destroy,
+	.reset			= drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state
+};
+
+static const struct drm_plane_helper_funcs ocdrm_plane_helper_funcs = {
+	.atomic_update	= ocdrm_plane_atomic_update,
+	.atomic_disable	= NULL,
+	.prepare_fb	= NULL,
+	.cleanup_fb	= NULL
+};
+
+static const struct drm_plane_funcs ocdrm_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 ocdrm_crtc_create(struct ocdrm_priv *priv)
+{
+	int ret;
+	uint32_t format[] = { DRM_FORMAT_RGB565,
+			      DRM_FORMAT_RGB888,
+			      DRM_FORMAT_XRGB8888,
+	};
+
+	drm_plane_helper_add(&priv->plane, &ocdrm_plane_helper_funcs);
+
+	ret = drm_universal_plane_init(priv->drm_dev, &priv->plane, 0,
+				&ocdrm_plane_funcs,
+				format, ARRAY_SIZE(format),
+				DRM_PLANE_TYPE_PRIMARY, NULL);
+	if (ret) {
+		dev_err(priv->drm_dev->dev, "cannot initialize plane");
+		return ret;
+	}
+
+	drm_crtc_helper_add(&priv->crtc, &ocdrm_crtc_helper_funcs);
+	ret = drm_crtc_init_with_planes(priv->drm_dev, &priv->crtc,
+					&priv->plane, NULL,
+					&ocdrm_crtc_funcs, NULL);
+	if (ret)
+		dev_err(priv->drm_dev->dev, "cannot initialize crtc");
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/ocdrm/ocdrm_crtc.h b/drivers/gpu/drm/ocdrm/ocdrm_crtc.h
new file mode 100644
index 0000000..778327e
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/ocdrm_crtc.h
@@ -0,0 +1,48 @@
+/*
+ * Open cores VGA/LCD 2.0 core DRM driver
+ * Copyright (c) 2016 Istituto Italiano di Tecnologia
+ * Electronic Design Lab.
+ *
+ * Author: Andrea Merello <andrea.merello at gmail.com>
+ *
+ * Based on the following drivers:
+ *   - Analog Devices AXI HDMI DRM driver, which is
+ *     Copyright 2012 Analog Devices Inc.
+ *
+ *   - ARC PGU DRM driver.
+ *     Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ *   - ARM HDLCD Driver
+ *     Copyright (C) 2013-2015 ARM Limited
+ *
+ *   - Atmel atmel-hlcdc driver, which is
+ *     Copyright (C) 2014 Traphandler
+ *     Copyright (C) 2014 Free Electrons
+ *
+ *   - OpenCores VGA/LCD 2.0 core frame buffer driver
+ *     Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson at saunalahti.fi
+ *
+ *   - R-Car Display Unit DRM driver
+ *     Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _OCDRM_CRTC_H_
+#define _OCDRM_CRTC_H_
+
+#include "ocdrm_drv.h"
+
+int ocdrm_crtc_create(struct ocdrm_priv *priv);
+
+#endif
diff --git a/drivers/gpu/drm/ocdrm/ocdrm_drv.c b/drivers/gpu/drm/ocdrm/ocdrm_drv.c
new file mode 100644
index 0000000..4b335dc
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/ocdrm_drv.c
@@ -0,0 +1,312 @@
+/*
+ * Open cores VGA/LCD 2.0 core DRM driver
+ * Copyright (c) 2016 Istituto Italiano di Tecnologia
+ * Electronic Design Lab.
+ *
+ * Author: Andrea Merello <andrea.merello at gmail.com>
+ *
+ * Based on the following drivers:
+ *   - Analog Devices AXI HDMI DRM driver, which is
+ *     Copyright 2012 Analog Devices Inc.
+ *
+ *   - ARC PGU DRM driver.
+ *     Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ *   - ARM HDLCD Driver
+ *     Copyright (C) 2013-2015 ARM Limited
+ *
+ *   - Atmel atmel-hlcdc driver, which is
+ *     Copyright (C) 2014 Traphandler
+ *     Copyright (C) 2014 Free Electrons
+ *
+ *   - OpenCores VGA/LCD 2.0 core frame buffer driver
+ *     Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson at saunalahti.fi
+ *
+ *   - R-Car Display Unit DRM driver
+ *     Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/of_address.h>
+#include <linux/of_dma.h>
+#include <linux/clk.h>
+
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+
+#include "ocdrm_drv.h"
+#include "ocdrm_crtc.h"
+#include "ocdrm_encoder.h"
+
+#define DRIVER_NAME	"ocdrm"
+#define DRIVER_DESC	"OpenCores DRM"
+#define DRIVER_DATE	"20160527"
+#define DRIVER_MAJOR	1
+#define DRIVER_MINOR	0
+
+static void ocdrm_output_poll_changed(struct drm_device *drm)
+{
+	struct ocdrm_priv *priv = drm->dev_private;
+
+	drm_fbdev_cma_hotplug_event(priv->fbdev);
+}
+
+static int ocdrm_atomic_commit(struct drm_device *dev,
+				    struct drm_atomic_state *state, bool async)
+{
+	return drm_atomic_helper_commit(dev, state, false);
+}
+
+static struct drm_mode_config_funcs ocdrm_mode_config_funcs = {
+	.output_poll_changed = ocdrm_output_poll_changed,
+	.fb_create = drm_fb_cma_create,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = ocdrm_atomic_commit,
+};
+
+u32 ocdrm_readreg(struct ocdrm_priv *priv, loff_t offset)
+{
+	if (priv->little_endian)
+		return ioread32(priv->regs + offset);
+	else
+		return ioread32be(priv->regs + offset);
+}
+
+void ocdrm_writereg(struct ocdrm_priv *priv, loff_t offset, u32 data)
+{
+	if (priv->little_endian)
+		iowrite32(data, priv->regs + offset);
+	else
+		iowrite32be(data, priv->regs + offset);
+}
+
+static void ocdrm_mode_config_init(struct ocdrm_priv *priv)
+{
+	struct drm_device *dev = priv->drm_dev;
+
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	dev->mode_config.max_width = 1500;
+	dev->mode_config.max_height = 1500;
+
+	dev->mode_config.funcs = &ocdrm_mode_config_funcs;
+}
+
+static void ocdrm_detect_endian(struct ocdrm_priv *priv)
+{
+	priv->little_endian = false;
+	ocdrm_writereg(priv, OCFB_VBARA, 0xfffffff0);
+	if (ocdrm_readreg(priv, OCFB_VBARA) != 0xfffffff0)
+		priv->little_endian = true;
+
+	ocdrm_writereg(priv, OCFB_VBARA, 0x0);
+}
+
+static int ocdrm_load(struct drm_device *dev)
+{
+	struct ocdrm_priv *priv;
+	int ret;
+	struct resource *res;
+	struct platform_device *pdev = to_platform_device(dev->dev);
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	dev->dev_private = priv;
+	priv->drm_dev = dev;
+
+	priv->pixel_clock = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->pixel_clock))
+		return -EPROBE_DEFER;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->regs))
+		return PTR_ERR(priv->regs);
+
+	if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+		return -ENOMEM;
+
+	ocdrm_detect_endian(priv);
+
+	drm_mode_config_init(dev);
+	ocdrm_mode_config_init(priv);
+
+	ret = ocdrm_crtc_create(priv);
+	if (ret)
+		goto err_crtc;
+
+	ret = ocdrm_encoder_create(priv);
+	if (ret)
+		goto err_crtc;
+
+	drm_mode_config_reset(dev);
+	drm_kms_helper_poll_init(dev);
+
+	priv->fbdev = drm_fbdev_cma_init(dev, 16, dev->mode_config.num_crtc,
+					dev->mode_config.num_connector);
+
+	if (IS_ERR(priv->fbdev)) {
+		DRM_ERROR("failed to initialize drm fbdev\n");
+		ret = PTR_ERR(priv->fbdev);
+		goto err_crtc;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	return 0;
+
+err_crtc:
+	drm_mode_config_cleanup(dev);
+	return ret;
+}
+
+static int ocdrm_unload(struct drm_device *dev)
+{
+	struct ocdrm_priv *priv = dev->dev_private;
+
+	if (priv->fbdev) {
+		drm_fbdev_cma_fini(priv->fbdev);
+		priv->fbdev = NULL;
+	}
+	drm_kms_helper_poll_fini(dev);
+	drm_vblank_cleanup(dev);
+	drm_mode_config_cleanup(dev);
+
+	return 0;
+}
+
+static void ocdrm_lastclose(struct drm_device *dev)
+{
+	struct ocdrm_priv *priv = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(priv->fbdev);
+}
+
+static const struct file_operations ocdrm_driver_fops = {
+	.owner		= THIS_MODULE,
+	.open		= drm_open,
+	.mmap		= drm_gem_cma_mmap,
+	.poll		= drm_poll,
+	.read		= drm_read,
+	.unlocked_ioctl	= drm_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= drm_compat_ioctl,
+#endif
+	.llseek		= no_llseek,
+	.release	= drm_release,
+};
+
+static struct drm_driver ocdrm_drm_driver = {
+	.driver_features	= DRIVER_MODESET | DRIVER_GEM |
+		DRIVER_ATOMIC | DRIVER_PRIME,
+	.lastclose		= ocdrm_lastclose,
+	.fops			= &ocdrm_driver_fops,
+	.name			= DRIVER_NAME,
+	.desc			= DRIVER_DESC,
+	.date			= DRIVER_DATE,
+	.major			= DRIVER_MAJOR,
+	.minor			= DRIVER_MINOR,
+	.dumb_create		= drm_gem_cma_dumb_create,
+	.dumb_map_offset	= drm_gem_cma_dumb_map_offset,
+	.dumb_destroy		= drm_gem_dumb_destroy,
+	.get_vblank_counter	= drm_vblank_no_hw_counter,
+	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
+	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
+	.gem_free_object	= 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 const struct of_device_id ocdrm_of_match[] = {
+	{ .compatible = "opencores,ocfb-drm", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ocdrm_of_match);
+
+static int ocdrm_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct drm_device *drm;
+
+	drm = drm_dev_alloc(&ocdrm_drm_driver, &pdev->dev);
+	if (!drm)
+		return -ENOMEM;
+
+	ret = ocdrm_load(drm);
+	if (ret)
+		goto err_unref;
+
+	ret = drm_dev_register(drm, 0);
+	if (ret)
+		goto err_unload;
+
+	ret = drm_connector_register_all(drm);
+	if (ret)
+		goto err_unregister;
+	return 0;
+
+err_unregister:
+	drm_dev_unregister(drm);
+err_unload:
+	ocdrm_unload(drm);
+err_unref:
+	drm_dev_unref(drm);
+
+	return ret;
+}
+
+static int ocdrm_remove(struct platform_device *pdev)
+{
+	struct ocdrm_priv *priv = platform_get_drvdata(pdev);
+	struct drm_device *drm = priv->drm_dev;
+
+	drm_connector_unregister_all(priv->drm_dev);
+	drm_dev_unregister(drm);
+	ocdrm_unload(drm);
+	drm_dev_unref(drm);
+
+	return 0;
+}
+
+static struct platform_driver ocdrm_platform_driver = {
+	.driver = {
+		.name = "oc-drm",
+		.owner = THIS_MODULE,
+		.of_match_table = ocdrm_of_match,
+	},
+	.probe = ocdrm_probe,
+	.remove = ocdrm_remove,
+};
+module_platform_driver(ocdrm_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Andrea Merello <andrea.merello at gmail.com>");
+MODULE_DESCRIPTION("OpenCores DRM driver");
diff --git a/drivers/gpu/drm/ocdrm/ocdrm_drv.h b/drivers/gpu/drm/ocdrm/ocdrm_drv.h
new file mode 100644
index 0000000..14e5539
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/ocdrm_drv.h
@@ -0,0 +1,89 @@
+/*
+ * Open cores VGA/LCD 2.0 core DRM driver
+ * Copyright (c) 2016 Istituto Italiano di Tecnologia
+ * Electronic Design Lab.
+ *
+ * Author: Andrea Merello <andrea.merello at gmail.com>
+ *
+ * Based on the following drivers:
+ *   - Analog Devices AXI HDMI DRM driver, which is
+ *     Copyright 2012 Analog Devices Inc.
+ *
+ *   - ARC PGU DRM driver.
+ *     Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ *   - ARM HDLCD Driver
+ *     Copyright (C) 2013-2015 ARM Limited
+ *
+ *   - Atmel atmel-hlcdc driver, which is
+ *     Copyright (C) 2014 Traphandler
+ *     Copyright (C) 2014 Free Electrons
+ *
+ *   - OpenCores VGA/LCD 2.0 core frame buffer driver
+ *     Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson at saunalahti.fi
+ *
+ *   - R-Car Display Unit DRM driver
+ *     Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _OCDRM_DRV_H_
+#define _OCDRM_DRV_H_
+
+#include <drm/drm.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+
+/* OCFB register defines */
+#define OCFB_CTRL	0x000
+#define OCFB_STAT	0x004
+#define OCFB_HTIM	0x008
+#define OCFB_VTIM	0x00c
+#define OCFB_HVLEN	0x010
+#define OCFB_VBARA	0x014
+#define OCFB_PALETTE	0x800
+
+#define OCFB_CTRL_VEN	0x00000001 /* Video Enable */
+#define OCFB_CTRL_HIE	0x00000002 /* HSync Interrupt Enable */
+#define OCFB_CTRL_PC	0x00000800 /* 8-bit Pseudo Color Enable*/
+#define OCFB_CTRL_CD8	0x00000000 /* Color Depth 8 */
+#define OCFB_CTRL_CD16	0x00000200 /* Color Depth 16 */
+#define OCFB_CTRL_CD24	0x00000400 /* Color Depth 24 */
+#define OCFB_CTRL_CD32	0x00000600 /* Color Depth 32 */
+#define OCFB_CTRL_VBL1	0x00000000 /* Burst Length 1 */
+#define OCFB_CTRL_VBL2	0x00000080 /* Burst Length 2 */
+#define OCFB_CTRL_VBL4	0x00000100 /* Burst Length 4 */
+#define OCFB_CTRL_VBL8	0x00000180 /* Burst Length 8 */
+#define OCFB_CTRL_HSL	0x00001000 /* HSync active low */
+#define OCFB_CTRL_VSL	0x00002000 /* VSync active low */
+
+#define PALETTE_SIZE	256
+
+struct ocdrm_priv {
+	struct drm_device *drm_dev;
+	struct drm_fbdev_cma *fbdev;
+	struct drm_crtc crtc;
+	struct drm_plane plane;
+	struct drm_encoder encoder;
+	struct clk *pixel_clock;
+	bool clk_enabled;
+	void __iomem *regs;
+	bool little_endian;
+};
+
+extern void ocdrm_writereg(struct ocdrm_priv *priv, loff_t offset, u32 data);
+extern u32 ocdrm_readreg(struct ocdrm_priv *priv, loff_t offset);
+
+#endif
diff --git a/drivers/gpu/drm/ocdrm/ocdrm_encoder.c b/drivers/gpu/drm/ocdrm/ocdrm_encoder.c
new file mode 100644
index 0000000..f157bb5
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/ocdrm_encoder.c
@@ -0,0 +1,95 @@
+/*
+ * Open cores VGA/LCD 2.0 core DRM driver
+ * Copyright (c) 2016 Istituto Italiano di Tecnologia
+ * Electronic Design Lab.
+ *
+ * Author: Andrea Merello <andrea.merello at gmail.com>
+ *
+ * Based on the following drivers:
+ *   - Analog Devices AXI HDMI DRM driver, which is
+ *     Copyright 2012 Analog Devices Inc.
+ *
+ *   - ARC PGU DRM driver.
+ *     Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ *   - ARM HDLCD Driver
+ *     Copyright (C) 2013-2015 ARM Limited
+ *
+ *   - Atmel atmel-hlcdc driver, which is
+ *     Copyright (C) 2014 Traphandler
+ *     Copyright (C) 2014 Free Electrons
+ *
+ *   - OpenCores VGA/LCD 2.0 core frame buffer driver
+ *     Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson at saunalahti.fi
+ *
+ *   - R-Car Display Unit DRM driver
+ *     Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include "ocdrm_encoder.h"
+
+static void ocdrm_encoder_nop(struct drm_encoder *encoder)
+{
+}
+
+static const struct drm_encoder_funcs ocdrm_encoder_funcs = {
+	.destroy = drm_encoder_cleanup
+};
+
+static struct drm_encoder_helper_funcs ocdrm_encoder_helper_funcs = {
+	.commit = ocdrm_encoder_nop,
+	.enable  = ocdrm_encoder_nop,
+	.disable = ocdrm_encoder_nop
+};
+
+int ocdrm_encoder_create(struct ocdrm_priv *priv)
+{
+	struct drm_encoder *encoder;
+	struct drm_bridge *bridge;
+	struct device_node *ep, *bridge_node;
+	int ret;
+
+	encoder = &priv->encoder;
+	encoder->possible_crtcs = 1 << drm_crtc_index(&priv->crtc);
+
+	drm_encoder_helper_add(encoder, &ocdrm_encoder_helper_funcs);
+	ret = drm_encoder_init(priv->drm_dev, encoder, &ocdrm_encoder_funcs,
+			DRM_MODE_ENCODER_NONE, NULL);
+	if (ret)
+		return ret;
+	ep = of_graph_get_next_endpoint(priv->drm_dev->dev->of_node, NULL);
+	if (!ep)
+		return -ENODEV;
+
+	bridge_node = of_graph_get_remote_port_parent(ep);
+	if (!bridge_node)
+		return -ENODEV;
+
+	bridge = of_drm_find_bridge(bridge_node);
+	if (!bridge)
+		return -EPROBE_DEFER;
+
+	bridge->encoder = encoder;
+	encoder->bridge = bridge;
+	drm_bridge_attach(priv->drm_dev, bridge);
+	return 0;
+}
diff --git a/drivers/gpu/drm/ocdrm/ocdrm_encoder.h b/drivers/gpu/drm/ocdrm/ocdrm_encoder.h
new file mode 100644
index 0000000..a5ee5f6
--- /dev/null
+++ b/drivers/gpu/drm/ocdrm/ocdrm_encoder.h
@@ -0,0 +1,48 @@
+/*
+ * Open cores VGA/LCD 2.0 core DRM driver
+ * Copyright (c) 2016 Istituto Italiano di Tecnologia
+ * Electronic Design Lab.
+ *
+ * Author: Andrea Merello <andrea.merello at gmail.com>
+ *
+ * Based on the following drivers:
+ *   - Analog Devices AXI HDMI DRM driver, which is
+ *     Copyright 2012 Analog Devices Inc.
+ *
+ *   - ARC PGU DRM driver.
+ *     Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ *   - ARM HDLCD Driver
+ *     Copyright (C) 2013-2015 ARM Limited
+ *
+ *   - Atmel atmel-hlcdc driver, which is
+ *     Copyright (C) 2014 Traphandler
+ *     Copyright (C) 2014 Free Electrons
+ *
+ *   - OpenCores VGA/LCD 2.0 core frame buffer driver
+ *     Copyright (C) 2013 Stefan Kristiansson, stefan.kristiansson at saunalahti.fi
+ *
+ *   - R-Car Display Unit DRM driver
+ *     Copyright (C) 2013-2015 Renesas Electronics Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _OCDRM_ENCODER_H_
+#define _OCDRM_ENCODER_H_
+
+#include "ocdrm_drv.h"
+
+int ocdrm_encoder_create(struct ocdrm_priv *priv);
+
+#endif
-- 
1.9.1



More information about the dri-devel mailing list