All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Noralf Trønnes" <noralf@tronnes.org>
To: dri-devel@lists.freedesktop.org
Cc: thomas.petazzoni@free-electrons.com
Subject: [RFC 4/5] drm/tinydrm: Add mipi-dbi support
Date: Wed, 16 Mar 2016 14:34:18 +0100	[thread overview]
Message-ID: <1458135259-31050-5-git-send-email-noralf@tronnes.org> (raw)
In-Reply-To: <1458135259-31050-1-git-send-email-noralf@tronnes.org>

Add support for MIPI DBI interfaced controllers.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/Kconfig    |   3 +
 drivers/gpu/drm/tinydrm/Makefile   |   3 +
 drivers/gpu/drm/tinydrm/mipi-dbi.c | 231 +++++++++++++++++++++++++++++++++++++
 include/drm/tinydrm/mipi-dbi.h     |  24 ++++
 4 files changed, 261 insertions(+)
 create mode 100644 drivers/gpu/drm/tinydrm/mipi-dbi.c
 create mode 100644 include/drm/tinydrm/mipi-dbi.h

diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
index 671239f..a7929e0 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -10,4 +10,7 @@ menuconfig DRM_TINYDRM
 	  Choose this option if you have a tinydrm supported display.
 	  If M is selected the module will be called tinydrm.
 
+config TINYDRM_MIPI_DBI
+	tristate
+
 source "drivers/gpu/drm/tinydrm/lcdreg/Kconfig"
diff --git a/drivers/gpu/drm/tinydrm/Makefile b/drivers/gpu/drm/tinydrm/Makefile
index f4a92d9..35ba822 100644
--- a/drivers/gpu/drm/tinydrm/Makefile
+++ b/drivers/gpu/drm/tinydrm/Makefile
@@ -1,2 +1,5 @@
 obj-$(CONFIG_DRM_TINYDRM)		+= core/
 obj-$(CONFIG_LCDREG)			+= lcdreg/
+
+# Controllers
+obj-$(CONFIG_TINYDRM_MIPI_DBI)		+= mipi-dbi.o
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
new file mode 100644
index 0000000..3021349
--- /dev/null
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
@@ -0,0 +1,231 @@
+//#define DEBUG
+/*
+ * MIPI Display Bus Interface (DBI) LCD controller support
+ *
+ * Copyright 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/tinydrm/lcdreg.h>
+#include <drm/tinydrm/tinydrm.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/swab.h>
+#include <video/mipi_display.h>
+
+#define DCS_POWER_MODE_DISPLAY			BIT(2)
+#define DCS_POWER_MODE_DISPLAY_NORMAL_MODE	BIT(3)
+#define DCS_POWER_MODE_SLEEP_MODE		BIT(4)
+#define DCS_POWER_MODE_PARTIAL_MODE		BIT(5)
+#define DCS_POWER_MODE_IDLE_MODE		BIT(6)
+#define DCS_POWER_MODE_RESERVED_MASK		(BIT(0) | BIT(1) | BIT(7))
+
+/* TODO: Move common functions to a separate module */
+void tinydrm_xrgb8888_to_rgb565(u32 *src, u16 *dst, unsigned num_pixels, bool swap_bytes)
+{
+	int i;
+
+	for (i = 0; i < num_pixels; i++) {
+		*dst = ((*src & 0x00F80000) >> 8) |
+		       ((*src & 0x0000FC00) >> 5) |
+		       ((*src & 0x000000F8) >> 3);
+		if (swap_bytes)
+			*dst = swab16(*dst);
+		src++;
+		dst++;
+	}
+}
+
+// TODO: Pass in regnr
+int tinydrm_update_rgb565_lcdreg(struct tinydrm_device *tdev, struct drm_framebuffer *fb, void *vmem, struct drm_clip_rect *clip)
+{
+	unsigned num_pixels = (clip->x2 - clip->x1 + 1) *
+			      (clip->y2 - clip->y1 + 1);
+	struct lcdreg_transfer tr = {
+		.index = 1,
+		.width = 16,
+		.count = num_pixels
+	};
+	bool byte_swap = false;
+	u16 *buf = NULL;
+	int ret;
+
+	dev_err_once(tdev->base->dev, "pixel_format = %s, bpw = 0x%08x\n", drm_get_format_name(fb->pixel_format), tdev->lcdreg->bits_per_word_mask);
+
+	switch (fb->pixel_format) {
+	case DRM_FORMAT_RGB565:
+		tr.buf = vmem;
+		break;
+	case DRM_FORMAT_XRGB8888:
+		buf = kmalloc(num_pixels * sizeof(u16), GFP_KERNEL);
+		if (!buf)
+			return -ENOMEM;
+
+#if defined(__LITTLE_ENDIAN)
+		byte_swap = !lcdreg_bpw_supported(tdev->lcdreg, 16);
+#endif
+		tinydrm_xrgb8888_to_rgb565(vmem, buf, num_pixels, byte_swap);
+		tr.buf = buf;
+		if (byte_swap) {
+			tr.width = 8;
+			tr.count *= 2;
+		}
+		break;
+	default:
+		dev_err_once(tdev->base->dev,
+			     "pixel_format '%s' is not supported\n",
+			     drm_get_format_name(fb->pixel_format));
+		return -EINVAL;
+	}
+
+	ret = lcdreg_write(tdev->lcdreg, MIPI_DCS_WRITE_MEMORY_START, &tr);
+	kfree(buf);
+
+	return ret;
+}
+
+static void mipi_dbi_deferred_update(struct work_struct *work)
+{
+	struct tinydrm_device *tdev = work_to_tinydrm(work);
+	struct lcdreg *reg = tdev->lcdreg;
+	struct tinydrm_fb_clip fb_clip;
+	struct drm_clip_rect *clip = &fb_clip.clip;
+	int ret;
+
+	dev_dbg(tdev->base->dev, "%s\n", __func__);
+
+	if (!tinydrm_deferred_begin(tdev, &fb_clip))
+		return;
+
+	dev_dbg(tdev->base->dev, "%s: vmem=%p, x1=%u, x2=%u, y1=%u, y2=%u\n", __func__, fb_clip.vmem, clip->x1, clip->x2, clip->y1, clip->y2);
+
+	lcdreg_writereg(reg, MIPI_DCS_SET_COLUMN_ADDRESS,
+			(clip->x1 >> 8) & 0xFF, clip->x1 & 0xFF,
+			(clip->x2 >> 8) & 0xFF, clip->x2 & 0xFF);
+	lcdreg_writereg(reg, MIPI_DCS_SET_PAGE_ADDRESS,
+			(clip->y1 >> 8) & 0xFF, clip->y1 & 0xFF,
+			(clip->y2 >> 8) & 0xFF, clip->y2 & 0xFF);
+
+	ret = tinydrm_update_rgb565_lcdreg(tdev, fb_clip.fb, fb_clip.vmem, clip);
+	if (ret)
+		dev_err_once(tdev->base->dev, "Failed to update display %d\n", ret);
+
+	tinydrm_deferred_end(tdev);
+}
+
+int mipi_dbi_init(struct device *dev, struct tinydrm_device *tdev)
+{
+	tdev->deferred = devm_kzalloc(dev, sizeof(*tdev->deferred),
+				      GFP_KERNEL);
+	if (!tdev->deferred)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&tdev->deferred->dwork, mipi_dbi_deferred_update);
+	tinydrm_reset_clip(&tdev->deferred->fb_clip.clip);
+	tdev->lcdreg->def_width = 8;
+	tdev->dirtyfb = tinydrm_dirtyfb;
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dbi_init);
+
+/* Returns true if the display can be verified to be on */
+bool mipi_dbi_display_is_on(struct lcdreg *reg)
+{
+	u32 val;
+
+	if (!lcdreg_is_readable(reg))
+		return false;
+
+	if (lcdreg_readreg_buf32(reg, MIPI_DCS_GET_POWER_MODE, &val, 1))
+		return false;
+
+	val &= ~DCS_POWER_MODE_RESERVED_MASK;
+
+	if (val != (DCS_POWER_MODE_DISPLAY |
+	    DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
+		return false;
+
+	DRM_DEBUG_DRIVER("Display is ON\n");
+
+	return true;
+}
+EXPORT_SYMBOL(mipi_dbi_display_is_on);
+
+void mipi_dbi_debug_dump_regs(struct lcdreg *reg)
+{
+	u32 val[4];
+	int ret;
+
+	if (!(lcdreg_is_readable(reg) && (drm_debug & DRM_UT_DRIVER)))
+		return;
+
+	ret = lcdreg_readreg_buf32(reg, MIPI_DCS_GET_DISPLAY_ID, val, 3);
+	if (ret) {
+		dev_warn(reg->dev,
+			 "failed to read from controller: %d", ret);
+		return;
+	}
+
+	/* RDDID is not part of the MIPI standard, but seems to be common */
+	DRM_DEBUG_DRIVER("Display ID (%02x): %02x %02x %02x\n",
+			 MIPI_DCS_GET_DISPLAY_ID, val[0], val[1], val[2]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_DISPLAY_STATUS, val, 4);
+	DRM_DEBUG_DRIVER("Display status (%02x): %02x %02x %02x %02x\n",
+			 MIPI_DCS_GET_DISPLAY_STATUS, val[0], val[1], val[2], val[3]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_POWER_MODE, val, 1);
+	DRM_DEBUG_DRIVER("Power mode (%02x): %02x\n",
+			 MIPI_DCS_GET_POWER_MODE, val[0]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_ADDRESS_MODE, val, 1);
+	DRM_DEBUG_DRIVER("Address mode (%02x): %02x\n",
+			 MIPI_DCS_GET_ADDRESS_MODE, val[0]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_PIXEL_FORMAT, val, 1);
+	DRM_DEBUG_DRIVER("Pixel format (%02x): %02x\n",
+			 MIPI_DCS_GET_PIXEL_FORMAT, val[0]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_DISPLAY_MODE, val, 1);
+	DRM_DEBUG_DRIVER("Display mode (%02x): %02x\n",
+			 MIPI_DCS_GET_DISPLAY_MODE, val[0]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_SIGNAL_MODE, val, 1);
+	DRM_DEBUG_DRIVER("Display signal mode (%02x): %02x\n",
+			 MIPI_DCS_GET_SIGNAL_MODE, val[0]);
+
+	lcdreg_readreg_buf32(reg, MIPI_DCS_GET_DIAGNOSTIC_RESULT, val, 1);
+	DRM_DEBUG_DRIVER("Diagnostic result (%02x): %02x\n",
+			 MIPI_DCS_GET_DIAGNOSTIC_RESULT, val[0]);
+}
+EXPORT_SYMBOL(mipi_dbi_debug_dump_regs);
+
+int mipi_dbi_panel_unprepare(struct drm_panel *panel)
+{
+	struct tinydrm_device *tdev = tinydrm_from_panel(panel);
+	struct lcdreg *reg = tdev->lcdreg;
+
+	/*
+	 * Only do this if we have turned off backlight because if it's on the
+	 * display will in most cases turn all white when the pixels are
+	 * turned off.
+	 */
+	if (tdev->backlight) {
+		lcdreg_writereg(reg, MIPI_DCS_SET_DISPLAY_OFF);
+		lcdreg_writereg(reg, MIPI_DCS_ENTER_SLEEP_MODE);
+	}
+
+	if (tdev->regulator)
+		regulator_disable(tdev->regulator);
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dbi_panel_unprepare);
+
+MODULE_LICENSE("GPL");
diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h
new file mode 100644
index 0000000..108a73b
--- /dev/null
+++ b/include/drm/tinydrm/mipi-dbi.h
@@ -0,0 +1,24 @@
+/*
+ * MIPI Display Bus Interface (DBI) LCD controller support
+ *
+ * Copyright 2016 Noralf Trønnes
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __LINUX_MIPI_DBI_H
+#define __LINUX_MIPI_DBI_H
+
+struct tinydrm_device;
+struct drm_panel;
+struct lcdreg;
+
+int mipi_dbi_init(struct device *dev, struct tinydrm_device *tdev);
+bool mipi_dbi_display_is_on(struct lcdreg *reg);
+void mipi_dbi_debug_dump_regs(struct lcdreg *reg);
+int mipi_dbi_panel_unprepare(struct drm_panel *panel);
+
+#endif /* __LINUX_MIPI_DBI_H */
-- 
2.2.2

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

  parent reply	other threads:[~2016-03-16 13:40 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-16 13:34 [RFC 0/5] drm: Add support for tiny LCD displays Noralf Trønnes
2016-03-16 13:34 ` [RFC 1/5] drm: Add DRM " Noralf Trønnes
2016-03-16 15:11   ` Daniel Vetter
2016-03-17 21:51     ` Noralf Trønnes
2016-03-18 17:47       ` Daniel Vetter
2016-03-23 17:07         ` Noralf Trønnes
2016-03-23 17:28           ` Daniel Vetter
2016-03-25 10:41             ` Noralf Trønnes
2016-03-29  7:27               ` Daniel Vetter
2016-04-01 19:15                 ` Noralf Trønnes
2016-04-12 10:40                   ` Daniel Vetter
2016-03-23 17:37   ` David Herrmann
2016-03-25 19:39     ` Noralf Trønnes
2016-03-16 13:34 ` [RFC 2/5] drm/tinydrm: Add lcd register abstraction Noralf Trønnes
2016-03-16 13:34 ` [RFC 3/5] drm/tinydrm/lcdreg: Add SPI support Noralf Trønnes
2016-03-16 13:34 ` Noralf Trønnes [this message]
2016-03-16 13:34 ` [RFC 5/5] drm/tinydrm: Add support for several Adafruit TFT displays Noralf Trønnes
2016-03-16 14:50 ` [RFC 0/5] drm: Add support for tiny LCD displays Daniel Vetter
2016-03-16 18:26 ` Eric Anholt
2016-03-17 22:00   ` Noralf Trønnes
2016-03-18 17:48     ` Daniel Vetter
2016-03-26 19:11       ` Noralf Trønnes
2016-03-29  7:29         ` Daniel Vetter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1458135259-31050-5-git-send-email-noralf@tronnes.org \
    --to=noralf@tronnes.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=thomas.petazzoni@free-electrons.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.