mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/4] video: add STM32F429-DISCO video pipeline support
@ 2022-02-02  9:55 Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 1/4] gpiolib: implement gpiod_set_value Ahmad Fatoum
                   ` (3 more replies)
  0 siblings, 4 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-02  9:55 UTC (permalink / raw)
  To: barebox

The STM32F429-DISCO has the LCD-TFT display controller connected to a
parallel panel with MIPI-DBI over SPI for panel control.

The LTDC driver was originally written for the STM32MP1, but had some
issues last time I tested it a few years ago. I debugged it into
submission on the STM32F429 now though, so it might just work on the MP1
as well.

v1 -> v2:
  - removed stray Kconfig hunk for panel support to appropriate patch
  (Sascha)
  - fixed Kconfig help text for mipi_dbi helper

Ahmad Fatoum (4):
  gpiolib: implement gpiod_set_value
  video: add driver for STM32 LCD-TFT Display Controller
  video: add MIPI DBI Type C Option 3 support
  video: add Ilitek ILI9341 panel support

 commands/Kconfig                     |  23 ++
 commands/Makefile                    |   1 +
 drivers/video/Kconfig                |  22 ++
 drivers/video/Makefile               |   3 +
 drivers/video/mipi_dbi.c             | 467 +++++++++++++++++++++++
 drivers/video/panel-ilitek-ili9341.c | 541 +++++++++++++++++++++++++++
 drivers/video/stm32_ltdc.c           | 336 +++++++++++++++++
 drivers/video/stm32_ltdc.h           | 130 +++++++
 include/gpiod.h                      |   8 +-
 include/spi/spi.h                    |  20 +
 10 files changed, 1550 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/mipi_dbi.c
 create mode 100644 drivers/video/panel-ilitek-ili9341.c
 create mode 100644 drivers/video/stm32_ltdc.c
 create mode 100644 drivers/video/stm32_ltdc.h

-- 
2.30.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/4] gpiolib: implement gpiod_set_value
  2022-02-02  9:55 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
@ 2022-02-02  9:55 ` Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 2/4] video: add driver for STM32 LCD-TFT Display Controller Ahmad Fatoum
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-02  9:55 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Linux gpiod_set_value is silent if the GPIO descriptor is NULL, but
barebox warns about it. Also having gpio_set_value not respect active
low/high, while Linux gpiod_set_value respects it is error-prone.

Add a new gpiod_set_value function that covers this. gpio == -ENOENT is
taken as the dummy value for optional GPIOs.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 include/gpiod.h | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/include/gpiod.h b/include/gpiod.h
index c8b2cd47a3cb..adac50b4c36d 100644
--- a/include/gpiod.h
+++ b/include/gpiod.h
@@ -14,7 +14,7 @@ enum gpiod_flags {
 	GPIOD_IN	= GPIOF_IN,
 	/*
 	 * To change this later to a different logic level (i.e. taking
-	 * active low into account), use gpio_direction_active()
+	 * active low into account), use gpiod_set_value()
 	 */
 	GPIOD_OUT_LOW	= GPIOF_OUT_INIT_INACTIVE,
 	GPIOD_OUT_HIGH	= GPIOF_OUT_INIT_ACTIVE,
@@ -23,4 +23,10 @@ enum gpiod_flags {
 /* returned gpio descriptor can be passed to any normal gpio_* function */
 int gpiod_get(struct device_d *dev, const char *_con_id, enum gpiod_flags flags);
 
+static inline void gpiod_set_value(unsigned gpio, bool value)
+{
+	if (gpio != -ENOENT)
+		gpio_direction_active(gpio, value);
+}
+
 #endif
-- 
2.30.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/4] video: add driver for STM32 LCD-TFT Display Controller
  2022-02-02  9:55 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 1/4] gpiolib: implement gpiod_set_value Ahmad Fatoum
@ 2022-02-02  9:55 ` Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 4/4] video: add Ilitek ILI9341 panel support Ahmad Fatoum
  3 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-02  9:55 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

This driver has been tested on a STM32F429 connected to an Ilitek 9341
Display for which support is added in a follow-up commit. The same
driver can be used (but wasn't tested) for the STM32MP1 as well. The
official ST evaluation kits all use MIPI-DSI for which we still lack
support. The LXA MC-1 has a parallel display connected to the LTDC, but
I didn't have one readily available to test.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/video/Kconfig      |   8 +
 drivers/video/Makefile     |   1 +
 drivers/video/stm32_ltdc.c | 336 +++++++++++++++++++++++++++++++++++++
 drivers/video/stm32_ltdc.h | 130 ++++++++++++++
 4 files changed, 475 insertions(+)
 create mode 100644 drivers/video/stm32_ltdc.c
 create mode 100644 drivers/video/stm32_ltdc.h

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index cfbd541a956e..1b8672fdea82 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -59,6 +59,14 @@ config DRIVER_VIDEO_STM
 	  Say 'Y' here to enable framebuffer and splash screen support for
 	  i.MX23 and i.MX28 based systems.
 
+config DRIVER_VIDEO_STM32_LTDC
+	bool "STM32 LTDC framebuffer driver"
+	select VIDEO_VPL
+	depends on ARCH_STM32 || COMPILE_TEST
+	help
+	  Say 'Y' here to enable framebuffer and splash screen support for
+	  STM32 and STM32MP1.
+
 config DRIVER_VIDEO_S3C24XX
 	bool "S3C244x framebuffer driver"
 	depends on ARCH_S3C24xx
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 8344bdd2af2a..7f4429278987 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
 obj-$(CONFIG_DRIVER_VIDEO_STM) += stm.o
+obj-$(CONFIG_DRIVER_VIDEO_STM32_LTDC) += stm32_ltdc.o
 obj-$(CONFIG_DRIVER_VIDEO_IMX) += imx.o
 obj-$(CONFIG_DRIVER_VIDEO_IMX_IPU) += imx-ipu-fb.o
 obj-$(CONFIG_DRIVER_VIDEO_S3C24XX) += s3c24xx.o
diff --git a/drivers/video/stm32_ltdc.c b/drivers/video/stm32_ltdc.c
new file mode 100644
index 000000000000..645c20b5545f
--- /dev/null
+++ b/drivers/video/stm32_ltdc.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2018 STMicroelectronics - All Rights Reserved
+ * Author(s): Philippe Cornu <philippe.cornu@st.com> for STMicroelectronics.
+ *	      Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
+ *	      Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <io.h>
+#include <fb.h>
+#include <dma.h>
+#include <video/media-bus-format.h>
+#include <video/vpl.h>
+#include <of_graph.h>
+
+#include "stm32_ltdc.h"
+
+struct ltdc_hw {
+	void __iomem *regs;
+	struct device_d *dev;
+	struct clk *pclk;
+	bool claimed;
+};
+
+struct ltdc_fb {
+	int id;
+	struct fb_info info;
+	u32 bg_col_argb;
+	u32 alpha;
+	u32 bus_format;
+	enum stm32_ltdc_pixfmt pixfmt;
+	struct vpl vpl;
+	struct ltdc_hw *hw;
+};
+
+static bool has_alpha(enum stm32_ltdc_pixfmt pixfmt)
+{
+	switch (pixfmt) {
+	case PF_ARGB8888:
+	case PF_ARGB1555:
+	case PF_ARGB4444:
+	case PF_AL44:
+	case PF_AL88:
+		return true;
+	case PF_RGB888:
+	case PF_RGB565:
+	case PF_L8:
+	default:
+		return false;
+	}
+}
+
+static void ltdc_set_mode(struct ltdc_fb *priv,
+			  struct fb_videomode *mode)
+{
+	void __iomem *regs = priv->hw->regs;
+	u32 hsync, vsync, acc_hbp, acc_vbp, acc_act_w, acc_act_h;
+	u32 total_w, total_h;
+	u32 val;
+
+	/* Convert video timings to ltdc timings */
+	hsync = mode->hsync_len - 1;
+	vsync = mode->vsync_len - 1;
+	acc_hbp = hsync + mode->left_margin;
+	acc_vbp = vsync + mode->upper_margin;
+	acc_act_w = acc_hbp + mode->xres;
+	acc_act_h = acc_vbp + mode->yres;
+	total_w = acc_act_w + mode->right_margin;
+	total_h = acc_act_h + mode->lower_margin;
+
+	/* Synchronization sizes */
+	val = (hsync << 16) | vsync;
+	clrsetbits_le32(regs + LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+
+	/* Accumulated back porch */
+	val = (acc_hbp << 16) | acc_vbp;
+	clrsetbits_le32(regs + LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+
+	/* Accumulated active width */
+	val = (acc_act_w << 16) | acc_act_h;
+	clrsetbits_le32(regs + LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+
+	/* Total width & height */
+	val = (total_w << 16) | total_h;
+	clrsetbits_le32(regs + LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+
+	setbits_le32(regs + LTDC_LIPCR, acc_act_h + 1);
+
+	/* Signal polarities */
+	val = 0;
+	dev_dbg(priv->hw->dev, "mode->display_flags 0x%x mode->sync 0x%x\n",
+		mode->display_flags, mode->sync);
+	if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
+		val |= GCR_HSPOL;
+	if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
+		val |= GCR_VSPOL;
+	if (mode->display_flags & DISPLAY_FLAGS_DE_HIGH)
+		val |= GCR_DEPOL;
+	if (mode->display_flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+		val |= GCR_PCPOL;
+
+	clrsetbits_le32(regs + LTDC_GCR,
+			GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+
+	/* Overall background color */
+	writel(priv->bg_col_argb, regs + LTDC_BCCR);
+}
+
+static void ltdc_set_layer1(struct ltdc_fb *priv)
+{
+	void __iomem *regs = priv->hw->regs;
+	u32 x0, x1, y0, y1;
+	u32 pitch_in_bytes;
+	u32 line_length;
+	u32 bus_width;
+	u32 val, tmp, bpp;
+	struct fb_videomode *mode = priv->info.mode;
+
+	x0 = y0 = 0;
+	x1 = mode->xres - 1;
+	y1 = mode->yres - 1;
+
+	/* Horizontal start and stop position */
+	tmp = (readl(regs + LTDC_BPCR) & BPCR_AHBP) >> 16;
+	val = ((x1 + 1 + tmp) << 16) + (x0 + 1 + tmp);
+	clrsetbits_le32(regs + LTDC_L1WHPCR, LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS,
+			val);
+
+	/* Vertical start & stop position */
+	tmp = readl(regs + LTDC_BPCR) & BPCR_AVBP;
+	val = ((y1 + 1 + tmp) << 16) + (y0 + 1 + tmp);
+	clrsetbits_le32(regs + LTDC_L1WVPCR, LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS,
+			val);
+
+	/* Layer background color */
+	writel(priv->bg_col_argb, regs + LTDC_L1DCCR);
+
+	/* Color frame buffer pitch in bytes & line length */
+	bpp = priv->info.bits_per_pixel;
+	pitch_in_bytes = mode->xres * (bpp >> 3);
+	bus_width = 8 << ((readl(regs + LTDC_GC2R) & GC2R_BW) >> 4);
+	line_length = ((bpp >> 3) * mode->xres) + (bus_width >> 3) - 1;
+	val = (pitch_in_bytes << 16) | line_length;
+	clrsetbits_le32(regs + LTDC_L1CFBLR, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+
+	/* Pixel format */
+	clrsetbits_le32(regs + LTDC_L1PFCR, LXPFCR_PF, priv->pixfmt);
+
+	/* Constant alpha value */
+	clrsetbits_le32(regs + LTDC_L1CACR, LXCACR_CONSTA, priv->alpha);
+
+	/* Specifies the blending factors : with or without pixel alpha */
+	/* Manage hw-specific capabilities */
+	val = has_alpha(priv->pixfmt) ? BF1_PAXCA | BF2_1PAXCA : BF1_CA | BF2_1CA;
+
+	/* Blending factors */
+	clrsetbits_le32(regs + LTDC_L1BFCR, LXBFCR_BF2 | LXBFCR_BF1, val);
+
+	/* Frame buffer line number */
+	clrsetbits_le32(regs + LTDC_L1CFBLNR, LXCFBLNR_CFBLN, mode->yres);
+
+	/* Frame buffer address */
+	writel((unsigned long)priv->info.screen_base, regs + LTDC_L1CFBAR);
+
+	/* Enable layer 1 */
+	setbits_le32(regs + LTDC_L1CR, LXCR_LEN);
+}
+
+static int ltdc_activate_var(struct fb_info *info)
+{
+	info->line_length = info->xres * (info->bits_per_pixel >> 3);
+
+	info->screen_base = dma_alloc_writecombine(info->line_length * info->yres,
+						   DMA_ADDRESS_BROKEN);
+	if (!info->screen_base)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void ltdc_enable(struct fb_info *info)
+{
+	struct fb_videomode *mode = info->mode;
+	struct ltdc_fb *priv = info->priv;
+	struct ltdc_hw *hw = priv->hw;
+	u32 pixclock;
+	int ret;
+
+	if (hw->claimed) {
+		dev_warn(hw->dev, "CRTC currently claimed by other frame buffer!\n");
+		return;
+	}
+
+	vpl_ioctl_prepare(&priv->vpl, priv->id, mode);
+
+	pixclock = PICOS2KHZ(mode->pixclock) * 1000;
+
+	ret = clk_enable(hw->pclk);
+	if (ret) {
+		dev_err(hw->dev, "peripheral clock enable error %d\n", ret);
+		return;
+	}
+
+	clk_set_rate(clk_get_parent(hw->pclk), pixclock);
+	if (!ret)
+		ret = clk_set_rate(hw->pclk, pixclock);
+	if (ret < 0) {
+		dev_err(hw->dev, "fail to set pixel clock %d hz: %d\n",
+			pixclock, ret);
+		return;
+	}
+
+	ret = device_reset_us(hw->dev, 100000);
+	if (ret) {
+		dev_err(hw->dev, "error resetting controller %d\n", ret);
+		return;
+	}
+
+	/* Configure & start LTDC */
+	ltdc_set_mode(priv, mode);
+	ltdc_set_layer1(priv);
+
+	/* Reload configuration immediately & enable LTDC */
+	setbits_le32(hw->regs + LTDC_SRCR, SRCR_IMR);
+	setbits_le32(hw->regs + LTDC_GCR, GCR_LTDCEN);
+
+	vpl_ioctl_enable(&priv->vpl, priv->id);
+
+	hw->claimed = true;
+}
+
+static void ltdc_disable(struct fb_info *info)
+{
+	struct ltdc_fb *priv = info->priv;
+
+	vpl_ioctl_disable(&priv->vpl, priv->id);
+
+	clrbits_le32(priv->hw->regs + LTDC_GCR, GCR_LTDCEN);
+	clk_disable(priv->hw->pclk);
+	priv->hw->claimed = false;
+
+	vpl_ioctl_unprepare(&priv->vpl, priv->id);
+}
+
+static struct fb_ops ltdc_ops = {
+	.fb_activate_var	= ltdc_activate_var,
+	.fb_enable		= ltdc_enable,
+	.fb_disable		= ltdc_disable,
+};
+
+static int ltdc_probe(struct device_d *dev)
+{
+	struct device_node *np;
+	struct resource *iores;
+	struct ltdc_hw *hw;
+	int ret;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+
+	hw = xzalloc(sizeof *hw);
+	hw->dev = dev;
+	hw->regs = IOMEM(iores->start);
+
+	hw->pclk = clk_get(dev, NULL);
+	if (IS_ERR(hw->pclk)) {
+		dev_err(dev, "peripheral clock get error %d\n", ret);
+		return PTR_ERR(hw->pclk);
+	}
+
+	for_each_available_child_of_node(dev->device_node, np) {
+		struct ltdc_fb *priv;
+		struct of_endpoint ep;
+		struct fb_info *info;
+
+		if (!of_graph_port_is_available(np))
+			continue;
+
+		ret = of_graph_parse_endpoint(np, &ep);
+		if (ret)
+			return ret;
+
+		dev_dbg(hw->dev, "register vpl for %s\n", np->full_name);
+
+		priv = xzalloc(sizeof(*priv));
+		priv->hw = hw;
+		priv->id = ep.id;
+		priv->vpl.node = dev->device_node;
+
+		ret = vpl_register(&priv->vpl);
+		if (ret)
+			return ret;
+
+		info = &priv->info;
+		info->priv = priv;
+		info->fbops = &ltdc_ops;
+
+		info->red	= (struct fb_bitfield){ .offset = 11, .length = 5, };
+		info->green	= (struct fb_bitfield){ .offset =  5, .length = 6, };
+		info->blue	= (struct fb_bitfield){ .offset =  0, .length = 5, };
+		info->bits_per_pixel = 16,
+		priv->pixfmt = PF_RGB565;
+		/* TODO Below parameters are hard-coded for the moment... */
+		priv->bg_col_argb = 0xFFFFFFFF; /* white no transparency */
+		priv->alpha = 0xFF;
+
+		ret = vpl_ioctl(&priv->vpl, priv->id, VPL_GET_VIDEOMODES, &info->modes);
+		if (ret)
+			dev_dbg(dev, "failed to get modes: %s\n", strerror(-ret));
+
+		ret = register_framebuffer(info);
+		if (ret < 0) {
+			dev_err(dev, "failed to register framebuffer\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static __maybe_unused struct of_device_id ltdc_ids[] = {
+	{ .compatible = "st,stm32-ltdc" },
+	{ /* sentinel */ }
+};
+
+static struct driver_d ltdc_driver = {
+	.name = "stm32-ltdc",
+	.probe = ltdc_probe,
+	.of_compatible = DRV_OF_COMPAT(ltdc_ids),
+};
+device_platform_driver(ltdc_driver);
diff --git a/drivers/video/stm32_ltdc.h b/drivers/video/stm32_ltdc.h
new file mode 100644
index 000000000000..6481f2613b95
--- /dev/null
+++ b/drivers/video/stm32_ltdc.h
@@ -0,0 +1,130 @@
+#ifndef STM32_LTDC_H__
+#define STM32_LTDC_H__
+
+/* LTDC main registers */
+#define LTDC_IDR	0x00	/* IDentification */
+#define LTDC_LCR	0x04	/* Layer Count */
+#define LTDC_SSCR	0x08	/* Synchronization Size Configuration */
+#define LTDC_BPCR	0x0C	/* Back Porch Configuration */
+#define LTDC_AWCR	0x10	/* Active Width Configuration */
+#define LTDC_TWCR	0x14	/* Total Width Configuration */
+#define LTDC_GCR	0x18	/* Global Control */
+#define LTDC_GC1R	0x1C	/* Global Configuration 1 */
+#define LTDC_GC2R	0x20	/* Global Configuration 2 */
+#define LTDC_SRCR	0x24	/* Shadow Reload Configuration */
+#define LTDC_GACR	0x28	/* GAmma Correction */
+#define LTDC_BCCR	0x2C	/* Background Color Configuration */
+#define LTDC_IER	0x34	/* Interrupt Enable */
+#define LTDC_ISR	0x38	/* Interrupt Status */
+#define LTDC_ICR	0x3C	/* Interrupt Clear */
+#define LTDC_LIPCR	0x40	/* Line Interrupt Position Conf. */
+#define LTDC_CPSR	0x44	/* Current Position Status */
+#define LTDC_CDSR	0x48	/* Current Display Status */
+
+/* LTDC layer 1 registers */
+#define LTDC_L1LC1R	0x80	/* L1 Layer Configuration 1 */
+#define LTDC_L1LC2R	0x84	/* L1 Layer Configuration 2 */
+#define LTDC_L1CR	0x84	/* L1 Control */
+#define LTDC_L1WHPCR	0x88	/* L1 Window Hor Position Config */
+#define LTDC_L1WVPCR	0x8C	/* L1 Window Vert Position Config */
+#define LTDC_L1CKCR	0x90	/* L1 Color Keying Configuration */
+#define LTDC_L1PFCR	0x94	/* L1 Pixel Format Configuration */
+#define LTDC_L1CACR	0x98	/* L1 Constant Alpha Config */
+#define LTDC_L1DCCR	0x9C	/* L1 Default Color Configuration */
+#define LTDC_L1BFCR	0xA0	/* L1 Blend Factors Configuration */
+#define LTDC_L1FBBCR	0xA4	/* L1 FrameBuffer Bus Control */
+#define LTDC_L1AFBCR	0xA8	/* L1 AuxFB Control */
+#define LTDC_L1CFBAR	0xAC	/* L1 Color FrameBuffer Address */
+#define LTDC_L1CFBLR	0xB0	/* L1 Color FrameBuffer Length */
+#define LTDC_L1CFBLNR	0xB4	/* L1 Color FrameBuffer Line Nb */
+#define LTDC_L1AFBAR	0xB8	/* L1 AuxFB Address */
+#define LTDC_L1AFBLR	0xBC	/* L1 AuxFB Length */
+#define LTDC_L1AFBLNR	0xC0	/* L1 AuxFB Line Number */
+#define LTDC_L1CLUTWR	0xC4	/* L1 CLUT Write */
+
+/* Bit definitions */
+#define SSCR_VSH	GENMASK(10, 0)	/* Vertical Synchronization Height */
+#define SSCR_HSW	GENMASK(27, 16)	/* Horizontal Synchronization Width */
+
+#define BPCR_AVBP	GENMASK(10, 0)	/* Accumulated Vertical Back Porch */
+#define BPCR_AHBP	GENMASK(27, 16)	/* Accumulated Horizontal Back Porch */
+
+#define AWCR_AAH	GENMASK(10, 0)	/* Accumulated Active Height */
+#define AWCR_AAW	GENMASK(27, 16)	/* Accumulated Active Width */
+
+#define TWCR_TOTALH	GENMASK(10, 0)	/* TOTAL Height */
+#define TWCR_TOTALW	GENMASK(27, 16)	/* TOTAL Width */
+
+#define GCR_LTDCEN	BIT(0)		/* LTDC ENable */
+#define GCR_DEN		BIT(16)		/* Dither ENable */
+#define GCR_PCPOL	BIT(28)		/* Pixel Clock POLarity-Inverted */
+#define GCR_DEPOL	BIT(29)		/* Data Enable POLarity-High */
+#define GCR_VSPOL	BIT(30)		/* Vertical Synchro POLarity-High */
+#define GCR_HSPOL	BIT(31)		/* Horizontal Synchro POLarity-High */
+
+#define GC1R_WBCH	GENMASK(3, 0)	/* Width of Blue CHannel output */
+#define GC1R_WGCH	GENMASK(7, 4)	/* Width of Green Channel output */
+#define GC1R_WRCH	GENMASK(11, 8)	/* Width of Red Channel output */
+#define GC1R_PBEN	BIT(12)		/* Precise Blending ENable */
+#define GC1R_DT		GENMASK(15, 14)	/* Dithering Technique */
+#define GC1R_GCT	GENMASK(19, 17)	/* Gamma Correction Technique */
+#define GC1R_SHREN	BIT(21)		/* SHadow Registers ENabled */
+#define GC1R_BCP	BIT(22)		/* Background Colour Programmable */
+#define GC1R_BBEN	BIT(23)		/* Background Blending ENabled */
+#define GC1R_LNIP	BIT(24)		/* Line Number IRQ Position */
+#define GC1R_TP		BIT(25)		/* Timing Programmable */
+#define GC1R_IPP	BIT(26)		/* IRQ Polarity Programmable */
+#define GC1R_SPP	BIT(27)		/* Sync Polarity Programmable */
+#define GC1R_DWP	BIT(28)		/* Dither Width Programmable */
+#define GC1R_STREN	BIT(29)		/* STatus Registers ENabled */
+#define GC1R_BMEN	BIT(31)		/* Blind Mode ENabled */
+
+#define GC2R_EDCA	BIT(0)		/* External Display Control Ability  */
+#define GC2R_STSAEN	BIT(1)		/* Slave Timing Sync Ability ENabled */
+#define GC2R_DVAEN	BIT(2)		/* Dual-View Ability ENabled */
+#define GC2R_DPAEN	BIT(3)		/* Dual-Port Ability ENabled */
+#define GC2R_BW		GENMASK(6, 4)	/* Bus Width (log2 of nb of bytes) */
+#define GC2R_EDCEN	BIT(7)		/* External Display Control ENabled */
+
+#define SRCR_IMR	BIT(0)		/* IMmediate Reload */
+#define SRCR_VBR	BIT(1)		/* Vertical Blanking Reload */
+
+#define LXCR_LEN	BIT(0)		/* Layer ENable */
+#define LXCR_COLKEN	BIT(1)		/* Color Keying Enable */
+#define LXCR_CLUTEN	BIT(4)		/* Color Look-Up Table ENable */
+
+#define LXWHPCR_WHSTPOS	GENMASK(11, 0)	/* Window Horizontal StarT POSition */
+#define LXWHPCR_WHSPPOS	GENMASK(27, 16)	/* Window Horizontal StoP POSition */
+
+#define LXWVPCR_WVSTPOS	GENMASK(10, 0)	/* Window Vertical StarT POSition */
+#define LXWVPCR_WVSPPOS	GENMASK(26, 16)	/* Window Vertical StoP POSition */
+
+#define LXPFCR_PF	GENMASK(2, 0)	/* Pixel Format */
+
+#define LXCACR_CONSTA	GENMASK(7, 0)	/* CONSTant Alpha */
+
+#define LXBFCR_BF2	GENMASK(2, 0)	/* Blending Factor 2 */
+#define LXBFCR_BF1	GENMASK(10, 8)	/* Blending Factor 1 */
+
+#define LXCFBLR_CFBLL	GENMASK(12, 0)	/* Color Frame Buffer Line Length */
+#define LXCFBLR_CFBP	GENMASK(28, 16)	/* Color Frame Buffer Pitch in bytes */
+
+#define LXCFBLNR_CFBLN	GENMASK(10, 0)	/* Color Frame Buffer Line Number */
+
+#define BF1_PAXCA	0x600		/* Pixel Alpha x Constant Alpha */
+#define BF1_CA		0x400		/* Constant Alpha */
+#define BF2_1PAXCA	0x007		/* 1 - (Pixel Alpha x Constant Alpha) */
+#define BF2_1CA		0x005		/* 1 - Constant Alpha */
+
+enum stm32_ltdc_pixfmt {
+	PF_ARGB8888 = 0,
+	PF_RGB888,
+	PF_RGB565,
+	PF_ARGB1555,
+	PF_ARGB4444,
+	PF_L8,
+	PF_AL44,
+	PF_AL88
+};
+
+#endif
-- 
2.30.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/4] video: add MIPI DBI Type C Option 3 support
  2022-02-02  9:55 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 1/4] gpiolib: implement gpiod_set_value Ahmad Fatoum
  2022-02-02  9:55 ` [PATCH 2/4] video: add driver for STM32 LCD-TFT Display Controller Ahmad Fatoum
@ 2022-02-02  9:55 ` Ahmad Fatoum
  2022-02-03 14:30   ` Thorsten Scherer
  2022-02-03 14:34   ` Thorsten Scherer
  2022-02-02  9:55 ` [PATCH 4/4] video: add Ilitek ILI9341 panel support Ahmad Fatoum
  3 siblings, 2 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-02  9:55 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Import the Linux v5.15 state of the driver to allow easy porting of
MIPI-DBI displays like the Ilitek 9431 added in a follow-up commit.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 commands/Kconfig         |  23 ++
 commands/Makefile        |   1 +
 drivers/video/Kconfig    |   3 +
 drivers/video/Makefile   |   1 +
 drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++++++
 include/spi/spi.h        |  20 ++
 6 files changed, 515 insertions(+)
 create mode 100644 drivers/video/mipi_dbi.c

diff --git a/commands/Kconfig b/commands/Kconfig
index ba8ca5cdebce..af60f7be1587 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -1969,6 +1969,29 @@ config CMD_SPI
 		  -w BIT	bits per word (default 8)
 		  -v		verbose
 
+config CMD_MIPI_DBI
+	bool
+	depends on DRIVER_VIDEO_MIPI_DBI && SPI
+	select PRINTF_HEXSTR
+	prompt "mipi_dbi command"
+	help
+	  write/read from MIPI DBI SPI device
+
+	  Usage: mipi_dbi [-wld] [REG] [DATA...]
+
+	  Options:
+		  -l		list all MIPI DBI devices
+		  -d DEVICE	select specific device (default is first registered)
+		  -w		issue write command
+
+BAREBOX_CMD_START(mipi_dbi)
+	.cmd		= do_mipi_dbi,
+	BAREBOX_CMD_DESC("write/read from MIPI DBI SPI device")
+	BAREBOX_CMD_OPTS("[-wld] [REG] [DATA...]")
+	BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
+	BAREBOX_CMD_HELP(cmd_mipi_dbi_help)
+BAREBOX_CMD_END
+
 config CMD_LED_TRIGGER
 	bool
 	depends on LED_TRIGGERS
diff --git a/commands/Makefile b/commands/Makefile
index db78d0b877f6..fffb6d979e82 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_GPIO)		+= gpio.o
 obj-$(CONFIG_CMD_UNCOMPRESS)	+= uncompress.o
 obj-$(CONFIG_CMD_I2C)		+= i2c.o
 obj-$(CONFIG_CMD_SPI)		+= spi.o
+obj-$(CONFIG_CMD_MIPI_DBI)	+= mipi_dbi.o
 obj-$(CONFIG_CMD_UBI)		+= ubi.o
 obj-$(CONFIG_CMD_UBIFORMAT)	+= ubiformat.o
 obj-$(CONFIG_CMD_MENU)		+= menu.o
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1b8672fdea82..70d1d809536b 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -129,6 +129,9 @@ config DRIVER_VIDEO_EDID
 	  This enabled support for reading and parsing EDID data from an attached
 	  monitor.
 
+config DRIVER_VIDEO_MIPI_DBI
+	bool
+
 config DRIVER_VIDEO_BACKLIGHT
 	bool "Add backlight support"
 	help
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 7f4429278987..a7b70d82072a 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_VPL) += vpl.o
 obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
 obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
 obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
+obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
 
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
new file mode 100644
index 000000000000..48b1110f72ab
--- /dev/null
+++ b/drivers/video/mipi_dbi.c
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MIPI Display Bus Interface (DBI) LCD controller support
+ *
+ * Copyright 2016 Noralf Trønnes
+ */
+
+#define pr_fmt(fmt) "mipi-dbi: " fmt
+
+#include <common.h>
+#include <linux/kernel.h>
+#include <linux/sizes.h>
+#include <gpiod.h>
+#include <regulator.h>
+#include <spi/spi.h>
+#include <video/mipi_dbi.h>
+
+#include <video/vpl.h>
+#include <video/mipi_display.h>
+#include <video/fourcc.h>
+
+#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
+
+#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))
+
+LIST_HEAD(mipi_dbi_list);
+EXPORT_SYMBOL(mipi_dbi_list);
+
+/**
+ * DOC: overview
+ *
+ * This library provides helpers for MIPI Display Bus Interface (DBI)
+ * compatible display controllers.
+ *
+ * Many controllers for tiny lcd displays are MIPI compliant and can use this
+ * library. If a controller uses registers 0x2A and 0x2B to set the area to
+ * update and uses register 0x2C to write to frame memory, it is most likely
+ * MIPI compliant.
+ *
+ * Only MIPI Type 1 displays are supported since a full frame memory is needed.
+ *
+ * There are 3 MIPI DBI implementation types:
+ *
+ * A. Motorola 6800 type parallel bus
+ *
+ * B. Intel 8080 type parallel bus
+ *
+ * C. SPI type with 3 options:
+ *
+ *    1. 9-bit with the Data/Command signal as the ninth bit
+ *    2. Same as above except it's sent as 16 bits
+ *    3. 8-bit with the Data/Command signal as a separate D/CX pin
+ *
+ * Currently barebox mipi_dbi only supports Type C option 3 with
+ * mipi_dbi_spi_init().
+ */
+
+#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
+({ \
+	if (!len) \
+		pr_debug("cmd=%02x\n", cmd); \
+	else if (len <= 32) \
+		pr_debug("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\
+	else \
+		pr_debug("cmd=%02x, len=%zu\n", cmd, len); \
+})
+
+static const u8 mipi_dbi_dcs_read_commands[] = {
+	MIPI_DCS_GET_DISPLAY_ID,
+	MIPI_DCS_GET_RED_CHANNEL,
+	MIPI_DCS_GET_GREEN_CHANNEL,
+	MIPI_DCS_GET_BLUE_CHANNEL,
+	MIPI_DCS_GET_DISPLAY_STATUS,
+	MIPI_DCS_GET_POWER_MODE,
+	MIPI_DCS_GET_ADDRESS_MODE,
+	MIPI_DCS_GET_PIXEL_FORMAT,
+	MIPI_DCS_GET_DISPLAY_MODE,
+	MIPI_DCS_GET_SIGNAL_MODE,
+	MIPI_DCS_GET_DIAGNOSTIC_RESULT,
+	MIPI_DCS_READ_MEMORY_START,
+	MIPI_DCS_READ_MEMORY_CONTINUE,
+	MIPI_DCS_GET_SCANLINE,
+	MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
+	MIPI_DCS_GET_CONTROL_DISPLAY,
+	MIPI_DCS_GET_POWER_SAVE,
+	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS,
+	MIPI_DCS_READ_DDB_START,
+	MIPI_DCS_READ_DDB_CONTINUE,
+	0, /* sentinel */
+};
+
+bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd)
+{
+	unsigned int i;
+
+	if (!dbi->read_commands)
+		return false;
+
+	for (i = 0; i < 0xff; i++) {
+		if (!dbi->read_commands[i])
+			return false;
+		if (cmd == dbi->read_commands[i])
+			return true;
+	}
+
+	return false;
+}
+
+int mipi_dbi_command_read_len(int cmd)
+{
+	switch (cmd) {
+	case MIPI_DCS_READ_MEMORY_START:
+	case MIPI_DCS_READ_MEMORY_CONTINUE:
+		return 2;
+	case MIPI_DCS_GET_DISPLAY_ID:
+		return 3;
+	case MIPI_DCS_GET_DISPLAY_STATUS:
+		return 4;
+	default:
+		return 1;
+	}
+}
+
+/**
+ * mipi_dbi_command_read - MIPI DCS read command
+ * @dbi: MIPI DBI structure
+ * @cmd: Command
+ * @val: Value read
+ *
+ * Send MIPI DCS read command to the controller.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val)
+{
+	if (!dbi->read_commands)
+		return -EACCES;
+
+	if (!mipi_dbi_command_is_read(dbi, cmd))
+		return -EINVAL;
+
+	return mipi_dbi_command_buf(dbi, cmd, val, 1);
+}
+EXPORT_SYMBOL(mipi_dbi_command_read);
+
+/**
+ * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array
+ * @dbi: MIPI DBI structure
+ * @cmd: Command
+ * @data: Parameter buffer
+ * @len: Buffer length
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len)
+{
+	u8 *cmdbuf;
+	int ret;
+
+	/* SPI requires dma-safe buffers */
+	cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
+	if (!cmdbuf)
+		return -ENOMEM;
+
+	ret = dbi->command(dbi, cmdbuf, data, len);
+
+	kfree(cmdbuf);
+
+	return ret;
+}
+EXPORT_SYMBOL(mipi_dbi_command_buf);
+
+/* This should only be used by mipi_dbi_command() */
+int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
+			      size_t len)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmemdup(data, len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = mipi_dbi_command_buf(dbi, cmd, buf, len);
+
+	kfree(buf);
+
+	return ret;
+}
+EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
+
+/**
+ * mipi_dbi_hw_reset - Hardware reset of controller
+ * @dbi: MIPI DBI structure
+ *
+ * Reset controller if the &mipi_dbi->reset gpio is set.
+ */
+void mipi_dbi_hw_reset(struct mipi_dbi *dbi)
+{
+	if (!gpio_is_valid(dbi->reset))
+		return;
+
+	gpiod_set_value(dbi->reset, 0);
+	udelay(20);
+	gpiod_set_value(dbi->reset, 1);
+	mdelay(120);
+}
+EXPORT_SYMBOL(mipi_dbi_hw_reset);
+
+/**
+ * mipi_dbi_display_is_on - Check if display is on
+ * @dbi: MIPI DBI structure
+ *
+ * This function checks the Power Mode register (if readable) to see if
+ * display output is turned on. This can be used to see if the bootloader
+ * has already turned on the display avoiding flicker when the pipeline is
+ * enabled.
+ *
+ * Returns:
+ * true if the display can be verified to be on, false otherwise.
+ */
+bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
+{
+	u8 val;
+
+	if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val))
+		return false;
+
+	val &= ~DCS_POWER_MODE_RESERVED_MASK;
+
+	/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
+	if (val != (DCS_POWER_MODE_DISPLAY |
+	    DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
+		return false;
+
+	pr_debug("Display is ON\n");
+
+	return true;
+}
+EXPORT_SYMBOL(mipi_dbi_display_is_on);
+
+#if IS_ENABLED(CONFIG_SPI)
+
+/**
+ * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
+ * @spi: SPI device
+ * @len: The transfer buffer length.
+ *
+ * Many controllers have a max speed of 10MHz, but can be pushed way beyond
+ * that. Increase reliability by running pixel data at max speed and the rest
+ * at 10MHz, preventing transfer glitches from messing up the init settings.
+ */
+u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
+{
+	if (len > 64)
+		return 0; /* use default */
+
+	return min_t(u32, 10000000, spi->max_speed_hz);
+}
+EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
+
+static bool mipi_dbi_machine_little_endian(void)
+{
+#if defined(__LITTLE_ENDIAN)
+	return true;
+#else
+	return false;
+#endif
+}
+
+/* MIPI DBI Type C Option 3 */
+
+static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
+					u8 *data, size_t len)
+{
+	struct spi_device *spi = dbi->spi;
+	u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED,
+			     spi->max_speed_hz / 2);
+	struct spi_transfer tr[2] = {
+		{
+			.speed_hz = speed_hz,
+			.tx_buf = cmd,
+			.len = 1,
+		}, {
+			.speed_hz = speed_hz,
+			.len = len,
+		},
+	};
+	struct spi_message m;
+	u8 *buf;
+	int ret;
+
+	if (!len)
+		return -EINVAL;
+
+	/*
+	 * Support non-standard 24-bit and 32-bit Nokia read commands which
+	 * start with a dummy clock, so we need to read an extra byte.
+	 */
+	if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
+	    *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
+		if (!(len == 3 || len == 4))
+			return -EINVAL;
+
+		tr[1].len = len + 1;
+	}
+
+	buf = kmalloc(tr[1].len, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	tr[1].rx_buf = buf;
+	gpiod_set_value(dbi->dc, 0);
+
+	spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
+	ret = spi_sync(spi, &m);
+	if (ret)
+		goto err_free;
+
+	if (tr[1].len == len) {
+		memcpy(data, buf, len);
+	} else {
+		unsigned int i;
+
+		for (i = 0; i < len; i++)
+			data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
+	}
+
+	MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
+
+err_free:
+	kfree(buf);
+
+	return ret;
+}
+
+static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
+				   u8 *par, size_t num)
+{
+	struct spi_device *spi = dbi->spi;
+	unsigned int bpw = 8;
+	u32 speed_hz;
+	int ret;
+
+	if (mipi_dbi_command_is_read(dbi, *cmd))
+		return mipi_dbi_typec3_command_read(dbi, cmd, par, num);
+
+	MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
+
+	gpiod_set_value(dbi->dc, 0);
+	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
+	ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
+	if (ret || !num)
+		return ret;
+
+	if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
+		bpw = 16;
+
+	gpiod_set_value(dbi->dc, 1);
+	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
+
+	return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
+}
+
+/**
+ * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
+ * @spi: SPI device
+ * @dbi: MIPI DBI structure to initialize
+ * @dc: D/C gpio
+ *
+ * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the
+ * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or
+ * a driver-specific init.
+ *
+ * Type C Option 3 interface is assumed, Type C Option 1 is not yet supported,
+ * because barebox has no generic way yet to require a 9-bit SPI transfer
+ *
+ * If the SPI master driver doesn't support the necessary bits per word,
+ * the following transformation is used:
+ *
+ * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command.
+ * - 16-bit: if big endian send as 8-bit, if little endian swap bytes
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
+		      int dc)
+{
+	struct device_d *dev = &spi->dev;
+
+	dbi->spi = spi;
+	dbi->read_commands = mipi_dbi_dcs_read_commands;
+
+	if (!gpio_is_valid(dc)) {
+		dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n");
+		return -ENOSYS;
+	}
+
+	dbi->command = mipi_dbi_typec3_command;
+	dbi->dc = dc;
+	// TODO: can we just force 16 bit?
+	if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16)
+		dbi->swap_bytes = true;
+
+	dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
+
+	list_add(&dbi->list, &mipi_dbi_list);
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dbi_spi_init);
+
+/**
+ * mipi_dbi_spi_transfer - SPI transfer helper
+ * @spi: SPI device
+ * @speed_hz: Override speed (optional)
+ * @bpw: Bits per word
+ * @buf: Buffer to transfer
+ * @len: Buffer length
+ *
+ * This SPI transfer helper breaks up the transfer of @buf into chunks which
+ * the SPI controller driver can handle.
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
+			  u8 bpw, const void *buf, size_t len)
+{
+	size_t max_chunk = spi_max_transfer_size(spi);
+	struct spi_transfer tr = {
+		.bits_per_word = bpw,
+		.speed_hz = speed_hz,
+	};
+	struct spi_message m;
+	size_t chunk;
+	int ret;
+
+	spi_message_init_with_transfers(&m, &tr, 1);
+
+	while (len) {
+		chunk = min(len, max_chunk);
+
+		tr.tx_buf = buf;
+		tr.len = chunk;
+		buf += chunk;
+		len -= chunk;
+
+		ret = spi_sync(spi, &m);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mipi_dbi_spi_transfer);
+
+#endif /* CONFIG_SPI */
+
+MODULE_LICENSE("GPL");
diff --git a/include/spi/spi.h b/include/spi/spi.h
index c5ad6bd39ff9..d133e0e21265 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -409,6 +409,26 @@ spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
 	list_add_tail(&t->transfer_list, &m->transfers);
 }
 
+/**
+ * spi_message_init_with_transfers - Initialize spi_message and append transfers
+ * @m: spi_message to be initialized
+ * @xfers: An array of spi transfers
+ * @num_xfers: Number of items in the xfer array
+ *
+ * This function initializes the given spi_message and adds each spi_transfer in
+ * the given array to the message.
+ */
+static inline void
+spi_message_init_with_transfers(struct spi_message *m,
+struct spi_transfer *xfers, unsigned int num_xfers)
+{
+	unsigned int i;
+
+	spi_message_init(m);
+	for (i = 0; i < num_xfers; ++i)
+		spi_message_add_tail(&xfers[i], m);
+}
+
 static inline void
 spi_transfer_del(struct spi_transfer *t)
 {
-- 
2.30.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/4] video: add Ilitek ILI9341 panel support
  2022-02-02  9:55 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2022-02-02  9:55 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum
@ 2022-02-02  9:55 ` Ahmad Fatoum
  3 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-02  9:55 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Port the Linux v5.15 DRM panel driver to barebox. This has been tested
against a STM32F4 LTDC.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/video/Kconfig                |  11 +
 drivers/video/Makefile               |   1 +
 drivers/video/panel-ilitek-ili9341.c | 541 +++++++++++++++++++++++++++
 3 files changed, 553 insertions(+)
 create mode 100644 drivers/video/panel-ilitek-ili9341.c

diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 70d1d809536b..dcdc6c213591 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -181,4 +181,15 @@ config DRIVER_VIDEO_SIMPLE_PANEL
 	  Linux Kernel implementation this one is able to understand display-timings
 	  nodes so that it's not necessary to keep a list of all known displays
 	  with their corresponding timings in barebox.
+
+config DRIVER_VIDEO_PANEL_ILITEK_ILI9341
+	tristate "Ilitek ILI9341 240x320 QVGA panels"
+	depends on OFTREE && SPI
+	select DRIVER_VIDEO_MIPI_DBI
+	select VIDEO_VPL
+	help
+	  Say Y here if you want to enable support for Ilitek IL9341
+	  QVGA (240x320) RGB panels. support serial & parallel rgb
+	  interface.
+
 endif
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index a7b70d82072a..9ec0420ccad1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
 obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
 obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
 obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
+obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
 
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
diff --git a/drivers/video/panel-ilitek-ili9341.c b/drivers/video/panel-ilitek-ili9341.c
new file mode 100644
index 000000000000..d8774420226d
--- /dev/null
+++ b/drivers/video/panel-ilitek-ili9341.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ilitek ILI9341 TFT LCD driver.
+ *
+ * This panel can be configured to support:
+ * - 16-bit parallel RGB interface
+ * - 18-bit parallel RGB interface
+ * - 4-line serial spi interface
+ *
+ * Copyright 2018 David Lechner <david@lechnology.com>
+ * Copyright (C) 2021 Dillon Min <dillon.minfei@gmail.com>
+ *
+ * Derived from Linux drivers/drm/gpu/panel/panel-ilitek-ili9341.c
+ */
+#define DEBUG 1
+#include <common.h>
+#include <linux/bitops.h>
+#include <gpiod.h>
+#include <of.h>
+#include <regulator.h>
+#include <spi/spi.h>
+#include <video/vpl.h>
+#include <video/mipi_dbi.h>
+
+#include <video/mipi_display.h>
+
+#define ILI9341_RGB_INTERFACE  0xb0   /* RGB Interface Signal Control */
+#define ILI9341_FRC            0xb1   /* Frame Rate Control register */
+#define ILI9341_DFC            0xb6   /* Display Function Control register */
+#define ILI9341_POWER1         0xc0   /* Power Control 1 register */
+#define ILI9341_POWER2         0xc1   /* Power Control 2 register */
+#define ILI9341_VCOM1          0xc5   /* VCOM Control 1 register */
+#define ILI9341_VCOM2          0xc7   /* VCOM Control 2 register */
+#define ILI9341_POWERA         0xcb   /* Power control A register */
+#define ILI9341_POWERB         0xcf   /* Power control B register */
+#define ILI9341_PGAMMA         0xe0   /* Positive Gamma Correction register */
+#define ILI9341_NGAMMA         0xe1   /* Negative Gamma Correction register */
+#define ILI9341_DTCA           0xe8   /* Driver timing control A */
+#define ILI9341_DTCB           0xea   /* Driver timing control B */
+#define ILI9341_POWER_SEQ      0xed   /* Power on sequence register */
+#define ILI9341_3GAMMA_EN      0xf2   /* 3 Gamma enable register */
+#define ILI9341_INTERFACE      0xf6   /* Interface control register */
+#define ILI9341_PRC            0xf7   /* Pump ratio control register */
+#define ILI9341_ETMOD	       0xb7   /* Entry mode set */
+
+#define ILI9341_MADCTL_BGR	BIT(3)
+#define ILI9341_MADCTL_MV	BIT(5)
+#define ILI9341_MADCTL_MX	BIT(6)
+#define ILI9341_MADCTL_MY	BIT(7)
+
+#define ILI9341_POWER_B_LEN	3
+#define ILI9341_POWER_SEQ_LEN	4
+#define ILI9341_DTCA_LEN	3
+#define ILI9341_DTCB_LEN	2
+#define ILI9341_POWER_A_LEN	5
+#define ILI9341_DFC_1_LEN	2
+#define ILI9341_FRC_LEN		2
+#define ILI9341_VCOM_1_LEN	2
+#define ILI9341_DFC_2_LEN	4
+#define ILI9341_COLUMN_ADDR_LEN	4
+#define ILI9341_PAGE_ADDR_LEN	4
+#define ILI9341_INTERFACE_LEN	3
+#define ILI9341_PGAMMA_LEN	15
+#define ILI9341_NGAMMA_LEN	15
+#define ILI9341_CA_LEN		3
+
+#define ILI9341_PIXEL_DPI_16_BITS	(BIT(6) | BIT(4))
+#define ILI9341_PIXEL_DPI_18_BITS	(BIT(6) | BIT(5))
+#define ILI9341_GAMMA_CURVE_1		BIT(0)
+#define ILI9341_IF_WE_MODE		BIT(0)
+#define ILI9341_IF_BIG_ENDIAN		0x00
+#define ILI9341_IF_DM_RGB		BIT(2)
+#define ILI9341_IF_DM_INTERNAL		0x00
+#define ILI9341_IF_DM_VSYNC		BIT(3)
+#define ILI9341_IF_RM_RGB		BIT(1)
+#define ILI9341_IF_RIM_RGB		0x00
+
+#define ILI9341_COLUMN_ADDR		0x00ef
+#define ILI9341_PAGE_ADDR		0x013f
+
+#define ILI9341_RGB_EPL			BIT(0)
+#define ILI9341_RGB_DPL			BIT(1)
+#define ILI9341_RGB_HSPL		BIT(2)
+#define ILI9341_RGB_VSPL		BIT(3)
+#define ILI9341_RGB_DE_MODE		BIT(6)
+#define ILI9341_RGB_DISP_PATH_MEM	BIT(7)
+
+#define ILI9341_DBI_VCOMH_4P6V		0x23
+#define ILI9341_DBI_PWR_2_DEFAULT	0x10
+#define ILI9341_DBI_PRC_NORMAL		0x20
+#define ILI9341_DBI_VCOM_1_VMH_4P25V	0x3e
+#define ILI9341_DBI_VCOM_1_VML_1P5V	0x28
+#define ILI9341_DBI_VCOM_2_DEC_58	0x86
+#define ILI9341_DBI_FRC_DIVA		0x00
+#define ILI9341_DBI_FRC_RTNA		0x1b
+#define ILI9341_DBI_EMS_GAS		BIT(0)
+#define ILI9341_DBI_EMS_DTS		BIT(1)
+#define ILI9341_DBI_EMS_GON		BIT(2)
+
+/* struct ili9341_config - the system specific ILI9341 configuration */
+struct ili9341_config {
+	u32 max_spi_speed;
+	/* mode: the display mode */
+	const struct fb_videomode mode;
+	/* ca: TODO: need comments for this register */
+	u8 ca[ILI9341_CA_LEN];
+	/* power_b: TODO: need comments for this register */
+	u8 power_b[ILI9341_POWER_B_LEN];
+	/* power_seq: TODO: need comments for this register */
+	u8 power_seq[ILI9341_POWER_SEQ_LEN];
+	/* dtca: TODO: need comments for this register */
+	u8 dtca[ILI9341_DTCA_LEN];
+	/* dtcb: TODO: need comments for this register */
+	u8 dtcb[ILI9341_DTCB_LEN];
+	/* power_a: TODO: need comments for this register */
+	u8 power_a[ILI9341_POWER_A_LEN];
+	/* frc: Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
+	u8 frc[ILI9341_FRC_LEN];
+	/* prc: TODO: need comments for this register */
+	u8 prc;
+	/* dfc_1: B6h DISCTRL (Display Function Control) */
+	u8 dfc_1[ILI9341_DFC_1_LEN];
+	/* power_1: Power Control 1 (C0h) */
+	u8 power_1;
+	/* power_2: Power Control 2 (C1h) */
+	u8 power_2;
+	/* vcom_1: VCOM Control 1(C5h) */
+	u8 vcom_1[ILI9341_VCOM_1_LEN];
+	/* vcom_2: VCOM Control 2(C7h) */
+	u8 vcom_2;
+	/* address_mode: Memory Access Control (36h) */
+	u8 address_mode;
+	/* g3amma_en: TODO: need comments for this register */
+	u8 g3amma_en;
+	/* rgb_interface: RGB Interface Signal Control (B0h) */
+	u8 rgb_interface;
+	/* dfc_2: refer to dfc_1 */
+	u8 dfc_2[ILI9341_DFC_2_LEN];
+	/* column_addr: Column Address Set (2Ah) */
+	u8 column_addr[ILI9341_COLUMN_ADDR_LEN];
+	/* page_addr: Page Address Set (2Bh) */
+	u8 page_addr[ILI9341_PAGE_ADDR_LEN];
+	/* interface: Interface Control (F6h) */
+	u8 interface[ILI9341_INTERFACE_LEN];
+	/*
+	 * pixel_format: This command sets the pixel format for the RGB
+	 * image data used by
+	 */
+	u8 pixel_format;
+	/*
+	 * gamma_curve: This command is used to select the desired Gamma
+	 * curve for the
+	 */
+	u8 gamma_curve;
+	/* pgamma: Positive Gamma Correction (E0h) */
+	u8 pgamma[ILI9341_PGAMMA_LEN];
+	/* ngamma: Negative Gamma Correction (E1h) */
+	u8 ngamma[ILI9341_NGAMMA_LEN];
+};
+
+struct ili9341 {
+	struct device_d *dev;
+	struct vpl vpl;
+	const struct ili9341_config *conf;
+	int reset_gpio;
+	int dc_gpio;
+	struct mipi_dbi *dbi;
+	u32 max_spi_speed;
+	struct regulator_bulk_data supplies[3];
+};
+
+/*
+ * The Stm32f429-disco board has a panel ili9341 connected to ltdc controller
+ */
+static const struct ili9341_config ili9341_stm32f429_disco_data = {
+	.max_spi_speed = 10000000,
+	.mode = {
+		.name = "240x320",
+		.xres = 240,
+		.yres = 320,
+		.pixclock = KHZ2PICOS(6100),
+		.left_margin = 10,
+		.hsync_len = 10,
+		.right_margin = 20,
+		.upper_margin = 4,
+		.lower_margin = 2,
+		.vsync_len = 2,
+	},
+	.ca = {0xc3, 0x08, 0x50},
+	.power_b = {0x00, 0xc1, 0x30},
+	.power_seq = {0x64, 0x03, 0x12, 0x81},
+	.dtca = {0x85, 0x00, 0x78},
+	.power_a = {0x39, 0x2c, 0x00, 0x34, 0x02},
+	.prc = 0x20,
+	.dtcb = {0x00, 0x00},
+	/* 0x00 fosc, 0x1b 70hz */
+	.frc = {0x00, 0x1b},
+	/*
+	 * 0x0a Interval scan, AGND AGND AGND AGND
+	 * 0xa2 Normally white, G1 -> G320, S720 -> S1,
+	 *	Scan Cycle 5 frames,85ms
+	 */
+	.dfc_1 = {0x0a, 0xa2},
+	/* 0x10 3.65v */
+	.power_1 = 0x10,
+	/* 0x10 AVDD=vci*2, VGH=vci*7, VGL=-vci*4 */
+	.power_2 = 0x10,
+	/* 0x45 VCOMH 4.425v, 0x15 VCOML -1.975*/
+	.vcom_1 = {0x45, 0x15},
+	/* 0x90 offset voltage, VMH-48, VML-48 */
+	.vcom_2 = 0x90,
+	/*
+	 * 0xc8 Row Address Order, Column Address Order
+	 * BGR 1
+	 */
+	.address_mode = 0xc8,
+	.g3amma_en = 0x00,
+	/*
+	 * 0xc2
+	 * Display Data Path: Memory
+	 * RGB: DE mode
+	 * DOTCLK polarity set (data fetched at the falling time)
+	 */
+	.rgb_interface = ILI9341_RGB_DISP_PATH_MEM |
+			ILI9341_RGB_DE_MODE |
+			ILI9341_RGB_DPL,
+	/*
+	 * 0x0a
+	 * Gate outputs in non-display area: Interval scan
+	 * Determine source/VCOM output in a non-display area in the partial
+	 * display mode: AGND AGND AGND AGND
+	 *
+	 * 0xa7
+	 * Scan Cycle: 15 frames
+	 * fFLM = 60Hz: 255ms
+	 * Liquid crystal type: Normally white
+	 * Gate Output Scan Direction: G1 -> G320
+	 * Source Output Scan Direction: S720 -> S1
+	 *
+	 * 0x27
+	 * LCD Driver Line: 320 lines
+	 *
+	 * 0x04
+	 * PCDIV: 4
+	 */
+	.dfc_2 = {0x0a, 0xa7, 0x27, 0x04},
+	/* column address: 240 */
+	.column_addr = {0x00, 0x00, (ILI9341_COLUMN_ADDR >> 4) & 0xff,
+				ILI9341_COLUMN_ADDR & 0xff},
+	/* page address: 320 */
+	.page_addr = {0x00, 0x00, (ILI9341_PAGE_ADDR >> 4) & 0xff,
+				ILI9341_PAGE_ADDR & 0xff},
+	/*
+	 * Memory write control: When the transfer number of data exceeds
+	 * (EC-SC+1)*(EP-SP+1), the column and page number will be
+	 * reset, and the exceeding data will be written into the following
+	 * column and page.
+	 * Display Operation Mode: RGB Interface Mode
+	 * Interface for RAM Access: RGB interface
+	 * 16- bit RGB interface (1 transfer/pixel)
+	 */
+	.interface = {ILI9341_IF_WE_MODE, 0x00,
+			ILI9341_IF_DM_RGB | ILI9341_IF_RM_RGB},
+	/* DPI: 16 bits / pixel */
+	.pixel_format = ILI9341_PIXEL_DPI_16_BITS,
+	/* Curve Selected: Gamma curve 1 (G2.2) */
+	.gamma_curve = ILI9341_GAMMA_CURVE_1,
+	.pgamma = {0x0f, 0x29, 0x24, 0x0c, 0x0e,
+			0x09, 0x4e, 0x78, 0x3c, 0x09,
+			0x13, 0x05, 0x17, 0x11, 0x00},
+	.ngamma = {0x00, 0x16, 0x1b, 0x04, 0x11,
+			0x07, 0x31, 0x33, 0x42, 0x05,
+			0x0c, 0x0a, 0x28, 0x2f, 0x0f},
+};
+
+static inline struct ili9341 *vpl_to_ili9341(struct vpl *vpl)
+{
+	return container_of(vpl, struct ili9341, vpl);
+}
+
+static void ili9341_dpi_init(struct ili9341 *ili)
+{
+	struct device_d *dev = ili->dev;
+	struct mipi_dbi *dbi = ili->dbi;
+	struct ili9341_config *cfg = (struct ili9341_config *)ili->conf;
+
+	/* Power Control */
+	mipi_dbi_command_stackbuf(dbi, 0xca, cfg->ca, ILI9341_CA_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_POWERB, cfg->power_b,
+				  ILI9341_POWER_B_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_POWER_SEQ, cfg->power_seq,
+				  ILI9341_POWER_SEQ_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DTCA, cfg->dtca,
+				  ILI9341_DTCA_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_POWERA, cfg->power_a,
+				  ILI9341_POWER_A_LEN);
+	mipi_dbi_command(ili->dbi, ILI9341_PRC, cfg->prc);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DTCB, cfg->dtcb,
+				  ILI9341_DTCB_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_FRC, cfg->frc, ILI9341_FRC_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_1,
+				  ILI9341_DFC_1_LEN);
+	mipi_dbi_command(dbi, ILI9341_POWER1, cfg->power_1);
+	mipi_dbi_command(dbi, ILI9341_POWER2, cfg->power_2);
+
+	/* VCOM */
+	mipi_dbi_command_stackbuf(dbi, ILI9341_VCOM1, cfg->vcom_1,
+				  ILI9341_VCOM_1_LEN);
+	mipi_dbi_command(dbi, ILI9341_VCOM2, cfg->vcom_2);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, cfg->address_mode);
+
+	/* Gamma */
+	mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, cfg->g3amma_en);
+	mipi_dbi_command(dbi, ILI9341_RGB_INTERFACE, cfg->rgb_interface);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_2,
+				  ILI9341_DFC_2_LEN);
+
+	/* Colomn address set */
+	mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_COLUMN_ADDRESS,
+				  cfg->column_addr, ILI9341_COLUMN_ADDR_LEN);
+
+	/* Page address set */
+	mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
+				  cfg->page_addr, ILI9341_PAGE_ADDR_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_INTERFACE, cfg->interface,
+				  ILI9341_INTERFACE_LEN);
+
+	/* Format */
+	mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, cfg->pixel_format);
+	mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START);
+	mdelay(200);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, cfg->gamma_curve);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_PGAMMA, cfg->pgamma,
+				  ILI9341_PGAMMA_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_NGAMMA, cfg->ngamma,
+				  ILI9341_NGAMMA_LEN);
+	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+	mdelay(200);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+	mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START);
+
+	dev_info(dev, "Initialized display rgb interface\n");
+}
+
+static int ili9341_dpi_power_on(struct ili9341 *ili)
+{
+	struct device_d *dev = ili->dev;
+	int ret = 0;
+
+	/* Assert RESET */
+	gpiod_set_value(ili->reset_gpio, 1);
+
+	/* Enable power */
+	ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies),
+				    ili->supplies);
+	if (ret < 0) {
+		dev_err(dev, "unable to enable vcc\n");
+		return ret;
+	}
+	mdelay(20);
+
+	/* De-assert RESET */
+	gpiod_set_value(ili->reset_gpio, 0);
+	mdelay(20);
+
+	return 0;
+}
+
+static int ili9341_dpi_power_off(struct ili9341 *ili)
+{
+	/* Assert RESET */
+	gpiod_set_value(ili->reset_gpio, 1);
+
+	/* Disable power */
+	return regulator_bulk_disable(ARRAY_SIZE(ili->supplies),
+				      ili->supplies);
+}
+
+static void ili9341_dpi_disable(struct ili9341 *ili)
+{
+	mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_OFF);
+}
+
+static int ili9341_dpi_prepare(struct ili9341 *ili)
+{
+	int ret;
+
+	ret = ili9341_dpi_power_on(ili);
+	if (ret < 0)
+		return ret;
+
+	ili9341_dpi_init(ili);
+
+	return ret;
+}
+
+static void ili9341_dpi_enable(struct ili9341 *ili)
+{
+	mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_ON);
+}
+
+static int ili9341_dpi_get_modes(struct ili9341 *ili,
+				 struct display_timings *timings)
+{
+	struct fb_videomode *mode;
+
+	mode = memdup(&ili->conf->mode, sizeof(*mode));
+	if (!mode)
+		return -ENOMEM;
+
+	/*
+	 * These are from the PoV of the display controller, so
+	 * DPL=1 => display samples at positive edge
+	 *       => controller drives at negative edge
+	 */
+	if (ili->conf->rgb_interface & ILI9341_RGB_DPL)
+		mode->display_flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+	else
+		mode->display_flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+
+	if (ili->conf->rgb_interface & ILI9341_RGB_EPL)
+		mode->display_flags |= DISPLAY_FLAGS_DE_HIGH;
+	else
+		mode->display_flags |= DISPLAY_FLAGS_DE_LOW;
+
+	/* Set up the polarity */
+	if (ili->conf->rgb_interface & ILI9341_RGB_HSPL)
+		mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+
+	if (ili->conf->rgb_interface & ILI9341_RGB_VSPL)
+		mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+
+	timings->modes = mode;
+	timings->num_modes = 1;
+	return 0;
+}
+
+static int ili9341_ioctl(struct vpl *vpl, unsigned int port,
+			unsigned int cmd, void *ptr)
+{
+	struct ili9341 *ili = vpl_to_ili9341(vpl);
+
+	switch (cmd) {
+	case VPL_PREPARE:
+		return ili9341_dpi_prepare(ili);
+	case VPL_ENABLE:
+		ili9341_dpi_enable(ili);
+		return 0;
+	case VPL_DISABLE:
+		ili9341_dpi_disable(ili);
+		return 0;
+	case VPL_UNPREPARE:
+		return ili9341_dpi_power_off(ili);
+	case VPL_GET_VIDEOMODES:
+		return ili9341_dpi_get_modes(ili, ptr);
+	default:
+		return 0;
+	}
+}
+
+static int ili9341_dpi_probe(struct spi_device *spi, int dc, int reset)
+{
+	struct device_d *dev = &spi->dev;
+	struct ili9341 *ili;
+	int ret;
+
+	ili = kzalloc(sizeof(struct ili9341), GFP_KERNEL);
+	if (!ili)
+		return -ENOMEM;
+
+	ili->dbi = kzalloc(sizeof(struct mipi_dbi), GFP_KERNEL);
+	if (!ili->dbi)
+		return -ENOMEM;
+
+	ili->supplies[0].supply = "vci";
+	ili->supplies[1].supply = "vddi";
+	ili->supplies[2].supply = "vddi-led";
+	ret = regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
+				 ili->supplies);
+	if (ret < 0) {
+		dev_err(dev, "failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	ret = mipi_dbi_spi_init(spi, ili->dbi, dc);
+	if (ret)
+		return ret;
+
+	ili->reset_gpio = reset;
+	/*
+	 * Every new incarnation of this display must have a unique
+	 * data entry for the system in this driver.
+	 */
+	ili->conf = device_get_match_data(dev);
+	if (!ili->conf) {
+		dev_err(dev, "missing device configuration\n");
+		return -ENODEV;
+	}
+
+	ili->dev = dev;
+	ili->max_spi_speed = ili->conf->max_spi_speed;
+	ili->vpl.node = dev->device_node;
+	ili->vpl.ioctl = ili9341_ioctl;
+
+	return vpl_register(&ili->vpl);
+}
+
+static int ili9341_probe(struct device_d *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int dc, reset;
+
+	reset = gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (!gpio_is_valid(reset) && reset != -ENOENT)
+		dev_err(dev, "Failed to get gpio 'reset'\n");
+
+	dc = gpiod_get(dev, "dc", GPIOD_OUT_LOW);
+	if (!gpio_is_valid(dc) && dc != -ENOENT)
+		dev_err(dev, "Failed to get gpio 'dc'\n");
+
+	return ili9341_dpi_probe(spi, dc, reset);
+}
+
+static const struct of_device_id ili9341_of_match[] = {
+	{
+		.compatible = "st,sf-tc240t-9370-t",
+		.data = &ili9341_stm32f429_disco_data,
+	},
+	{ }
+};
+
+static struct driver_d ili9341_driver = {
+	.name = "panel-ilitek-ili9341",
+	.of_compatible = ili9341_of_match,
+	.probe = ili9341_probe,
+};
+device_spi_driver(ili9341_driver);
+
+MODULE_AUTHOR("Dillon Min <dillon.minfei@gmail.com>");
+MODULE_DESCRIPTION("ILI9341 LCD panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.30.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/4] video: add MIPI DBI Type C Option 3 support
  2022-02-02  9:55 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum
@ 2022-02-03 14:30   ` Thorsten Scherer
  2022-02-03 14:34     ` Ahmad Fatoum
  2022-02-03 14:34   ` Thorsten Scherer
  1 sibling, 1 reply; 10+ messages in thread
From: Thorsten Scherer @ 2022-02-03 14:30 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

Hi Ahmad,

applying this patch makes menuconfig fail.

On Wed, Feb 02, 2022 at 10:55:53AM +0100, Ahmad Fatoum wrote:
> Import the Linux v5.15 state of the driver to allow easy porting of
> MIPI-DBI displays like the Ilitek 9431 added in a follow-up commit.
> 
> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
>  commands/Kconfig         |  23 ++
>  commands/Makefile        |   1 +
>  drivers/video/Kconfig    |   3 +
>  drivers/video/Makefile   |   1 +
>  drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++++++
>  include/spi/spi.h        |  20 ++
>  6 files changed, 515 insertions(+)
>  create mode 100644 drivers/video/mipi_dbi.c
> 
> diff --git a/commands/Kconfig b/commands/Kconfig
> index ba8ca5cdebce..af60f7be1587 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -1969,6 +1969,29 @@ config CMD_SPI
>  		  -w BIT	bits per word (default 8)
>  		  -v		verbose
>  
> +config CMD_MIPI_DBI
> +	bool
> +	depends on DRIVER_VIDEO_MIPI_DBI && SPI
> +	select PRINTF_HEXSTR
> +	prompt "mipi_dbi command"
> +	help
> +	  write/read from MIPI DBI SPI device
> +
> +	  Usage: mipi_dbi [-wld] [REG] [DATA...]
> +
> +	  Options:
> +		  -l		list all MIPI DBI devices
> +		  -d DEVICE	select specific device (default is first registered)
> +		  -w		issue write command
> +
> +BAREBOX_CMD_START(mipi_dbi)
> +	.cmd		= do_mipi_dbi,
> +	BAREBOX_CMD_DESC("write/read from MIPI DBI SPI device")
> +	BAREBOX_CMD_OPTS("[-wld] [REG] [DATA...]")
> +	BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
> +	BAREBOX_CMD_HELP(cmd_mipi_dbi_help)
> +BAREBOX_CMD_END

Copy and paste error?

> +
>  config CMD_LED_TRIGGER
>  	bool
>  	depends on LED_TRIGGERS
> diff --git a/commands/Makefile b/commands/Makefile
> index db78d0b877f6..fffb6d979e82 100644
> --- a/commands/Makefile
> +++ b/commands/Makefile
> @@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_GPIO)		+= gpio.o
>  obj-$(CONFIG_CMD_UNCOMPRESS)	+= uncompress.o
>  obj-$(CONFIG_CMD_I2C)		+= i2c.o
>  obj-$(CONFIG_CMD_SPI)		+= spi.o
> +obj-$(CONFIG_CMD_MIPI_DBI)	+= mipi_dbi.o
>  obj-$(CONFIG_CMD_UBI)		+= ubi.o
>  obj-$(CONFIG_CMD_UBIFORMAT)	+= ubiformat.o
>  obj-$(CONFIG_CMD_MENU)		+= menu.o
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 1b8672fdea82..70d1d809536b 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -129,6 +129,9 @@ config DRIVER_VIDEO_EDID
>  	  This enabled support for reading and parsing EDID data from an attached
>  	  monitor.
>  
> +config DRIVER_VIDEO_MIPI_DBI
> +	bool
> +
>  config DRIVER_VIDEO_BACKLIGHT
>  	bool "Add backlight support"
>  	help
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 7f4429278987..a7b70d82072a 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_VPL) += vpl.o
>  obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
>  obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
>  obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
> +obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
>  
>  obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
>  obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
> diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
> new file mode 100644
> index 000000000000..48b1110f72ab
> --- /dev/null
> +++ b/drivers/video/mipi_dbi.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * MIPI Display Bus Interface (DBI) LCD controller support
> + *
> + * Copyright 2016 Noralf Trønnes
> + */
> +
> +#define pr_fmt(fmt) "mipi-dbi: " fmt
> +
> +#include <common.h>
> +#include <linux/kernel.h>
> +#include <linux/sizes.h>
> +#include <gpiod.h>
> +#include <regulator.h>
> +#include <spi/spi.h>
> +#include <video/mipi_dbi.h>
> +
> +#include <video/vpl.h>
> +#include <video/mipi_display.h>
> +#include <video/fourcc.h>
> +
> +#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
> +
> +#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))
> +
> +LIST_HEAD(mipi_dbi_list);
> +EXPORT_SYMBOL(mipi_dbi_list);
> +
> +/**
> + * DOC: overview
> + *
> + * This library provides helpers for MIPI Display Bus Interface (DBI)
> + * compatible display controllers.
> + *
> + * Many controllers for tiny lcd displays are MIPI compliant and can use this
> + * library. If a controller uses registers 0x2A and 0x2B to set the area to
> + * update and uses register 0x2C to write to frame memory, it is most likely
> + * MIPI compliant.
> + *
> + * Only MIPI Type 1 displays are supported since a full frame memory is needed.
> + *
> + * There are 3 MIPI DBI implementation types:
> + *
> + * A. Motorola 6800 type parallel bus
> + *
> + * B. Intel 8080 type parallel bus
> + *
> + * C. SPI type with 3 options:
> + *
> + *    1. 9-bit with the Data/Command signal as the ninth bit
> + *    2. Same as above except it's sent as 16 bits
> + *    3. 8-bit with the Data/Command signal as a separate D/CX pin
> + *
> + * Currently barebox mipi_dbi only supports Type C option 3 with
> + * mipi_dbi_spi_init().
> + */
> +
> +#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
> +({ \
> +	if (!len) \
> +		pr_debug("cmd=%02x\n", cmd); \
> +	else if (len <= 32) \
> +		pr_debug("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\
> +	else \
> +		pr_debug("cmd=%02x, len=%zu\n", cmd, len); \
> +})
> +
> +static const u8 mipi_dbi_dcs_read_commands[] = {
> +	MIPI_DCS_GET_DISPLAY_ID,
> +	MIPI_DCS_GET_RED_CHANNEL,
> +	MIPI_DCS_GET_GREEN_CHANNEL,
> +	MIPI_DCS_GET_BLUE_CHANNEL,
> +	MIPI_DCS_GET_DISPLAY_STATUS,
> +	MIPI_DCS_GET_POWER_MODE,
> +	MIPI_DCS_GET_ADDRESS_MODE,
> +	MIPI_DCS_GET_PIXEL_FORMAT,
> +	MIPI_DCS_GET_DISPLAY_MODE,
> +	MIPI_DCS_GET_SIGNAL_MODE,
> +	MIPI_DCS_GET_DIAGNOSTIC_RESULT,
> +	MIPI_DCS_READ_MEMORY_START,
> +	MIPI_DCS_READ_MEMORY_CONTINUE,
> +	MIPI_DCS_GET_SCANLINE,
> +	MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
> +	MIPI_DCS_GET_CONTROL_DISPLAY,
> +	MIPI_DCS_GET_POWER_SAVE,
> +	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS,
> +	MIPI_DCS_READ_DDB_START,
> +	MIPI_DCS_READ_DDB_CONTINUE,
> +	0, /* sentinel */
> +};
> +
> +bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd)
> +{
> +	unsigned int i;
> +
> +	if (!dbi->read_commands)
> +		return false;
> +
> +	for (i = 0; i < 0xff; i++) {
> +		if (!dbi->read_commands[i])
> +			return false;
> +		if (cmd == dbi->read_commands[i])
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int mipi_dbi_command_read_len(int cmd)
> +{
> +	switch (cmd) {
> +	case MIPI_DCS_READ_MEMORY_START:
> +	case MIPI_DCS_READ_MEMORY_CONTINUE:
> +		return 2;
> +	case MIPI_DCS_GET_DISPLAY_ID:
> +		return 3;
> +	case MIPI_DCS_GET_DISPLAY_STATUS:
> +		return 4;
> +	default:
> +		return 1;
> +	}
> +}
> +
> +/**
> + * mipi_dbi_command_read - MIPI DCS read command
> + * @dbi: MIPI DBI structure
> + * @cmd: Command
> + * @val: Value read
> + *
> + * Send MIPI DCS read command to the controller.
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val)
> +{
> +	if (!dbi->read_commands)
> +		return -EACCES;
> +
> +	if (!mipi_dbi_command_is_read(dbi, cmd))
> +		return -EINVAL;
> +
> +	return mipi_dbi_command_buf(dbi, cmd, val, 1);
> +}
> +EXPORT_SYMBOL(mipi_dbi_command_read);
> +
> +/**
> + * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array
> + * @dbi: MIPI DBI structure
> + * @cmd: Command
> + * @data: Parameter buffer
> + * @len: Buffer length
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len)
> +{
> +	u8 *cmdbuf;
> +	int ret;
> +
> +	/* SPI requires dma-safe buffers */
> +	cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
> +	if (!cmdbuf)
> +		return -ENOMEM;
> +
> +	ret = dbi->command(dbi, cmdbuf, data, len);
> +
> +	kfree(cmdbuf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mipi_dbi_command_buf);
> +
> +/* This should only be used by mipi_dbi_command() */
> +int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
> +			      size_t len)
> +{
> +	u8 *buf;
> +	int ret;
> +
> +	buf = kmemdup(data, len, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ret = mipi_dbi_command_buf(dbi, cmd, buf, len);
> +
> +	kfree(buf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
> +
> +/**
> + * mipi_dbi_hw_reset - Hardware reset of controller
> + * @dbi: MIPI DBI structure
> + *
> + * Reset controller if the &mipi_dbi->reset gpio is set.
> + */
> +void mipi_dbi_hw_reset(struct mipi_dbi *dbi)
> +{
> +	if (!gpio_is_valid(dbi->reset))
> +		return;
> +
> +	gpiod_set_value(dbi->reset, 0);
> +	udelay(20);
> +	gpiod_set_value(dbi->reset, 1);
> +	mdelay(120);
> +}
> +EXPORT_SYMBOL(mipi_dbi_hw_reset);
> +
> +/**
> + * mipi_dbi_display_is_on - Check if display is on
> + * @dbi: MIPI DBI structure
> + *
> + * This function checks the Power Mode register (if readable) to see if
> + * display output is turned on. This can be used to see if the bootloader
> + * has already turned on the display avoiding flicker when the pipeline is
> + * enabled.
> + *
> + * Returns:
> + * true if the display can be verified to be on, false otherwise.
> + */
> +bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
> +{
> +	u8 val;
> +
> +	if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val))
> +		return false;
> +
> +	val &= ~DCS_POWER_MODE_RESERVED_MASK;
> +
> +	/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
> +	if (val != (DCS_POWER_MODE_DISPLAY |
> +	    DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
> +		return false;
> +
> +	pr_debug("Display is ON\n");
> +
> +	return true;
> +}
> +EXPORT_SYMBOL(mipi_dbi_display_is_on);
> +
> +#if IS_ENABLED(CONFIG_SPI)
> +
> +/**
> + * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
> + * @spi: SPI device
> + * @len: The transfer buffer length.
> + *
> + * Many controllers have a max speed of 10MHz, but can be pushed way beyond
> + * that. Increase reliability by running pixel data at max speed and the rest
> + * at 10MHz, preventing transfer glitches from messing up the init settings.
> + */
> +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
> +{
> +	if (len > 64)
> +		return 0; /* use default */
> +
> +	return min_t(u32, 10000000, spi->max_speed_hz);
> +}
> +EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
> +
> +static bool mipi_dbi_machine_little_endian(void)
> +{
> +#if defined(__LITTLE_ENDIAN)
> +	return true;
> +#else
> +	return false;
> +#endif
> +}
> +
> +/* MIPI DBI Type C Option 3 */
> +
> +static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
> +					u8 *data, size_t len)
> +{
> +	struct spi_device *spi = dbi->spi;
> +	u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED,
> +			     spi->max_speed_hz / 2);
> +	struct spi_transfer tr[2] = {
> +		{
> +			.speed_hz = speed_hz,
> +			.tx_buf = cmd,
> +			.len = 1,
> +		}, {
> +			.speed_hz = speed_hz,
> +			.len = len,
> +		},
> +	};
> +	struct spi_message m;
> +	u8 *buf;
> +	int ret;
> +
> +	if (!len)
> +		return -EINVAL;
> +
> +	/*
> +	 * Support non-standard 24-bit and 32-bit Nokia read commands which
> +	 * start with a dummy clock, so we need to read an extra byte.
> +	 */
> +	if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
> +	    *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
> +		if (!(len == 3 || len == 4))
> +			return -EINVAL;
> +
> +		tr[1].len = len + 1;
> +	}
> +
> +	buf = kmalloc(tr[1].len, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	tr[1].rx_buf = buf;
> +	gpiod_set_value(dbi->dc, 0);
> +
> +	spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
> +	ret = spi_sync(spi, &m);
> +	if (ret)
> +		goto err_free;
> +
> +	if (tr[1].len == len) {
> +		memcpy(data, buf, len);
> +	} else {
> +		unsigned int i;
> +
> +		for (i = 0; i < len; i++)
> +			data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
> +	}
> +
> +	MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
> +
> +err_free:
> +	kfree(buf);
> +
> +	return ret;
> +}
> +
> +static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
> +				   u8 *par, size_t num)
> +{
> +	struct spi_device *spi = dbi->spi;
> +	unsigned int bpw = 8;
> +	u32 speed_hz;
> +	int ret;
> +
> +	if (mipi_dbi_command_is_read(dbi, *cmd))
> +		return mipi_dbi_typec3_command_read(dbi, cmd, par, num);
> +
> +	MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
> +
> +	gpiod_set_value(dbi->dc, 0);
> +	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
> +	ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
> +	if (ret || !num)
> +		return ret;
> +
> +	if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
> +		bpw = 16;
> +
> +	gpiod_set_value(dbi->dc, 1);
> +	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
> +
> +	return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
> +}
> +
> +/**
> + * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
> + * @spi: SPI device
> + * @dbi: MIPI DBI structure to initialize
> + * @dc: D/C gpio
> + *
> + * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the
> + * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or
> + * a driver-specific init.
> + *
> + * Type C Option 3 interface is assumed, Type C Option 1 is not yet supported,
> + * because barebox has no generic way yet to require a 9-bit SPI transfer
> + *
> + * If the SPI master driver doesn't support the necessary bits per word,
> + * the following transformation is used:
> + *
> + * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command.
> + * - 16-bit: if big endian send as 8-bit, if little endian swap bytes
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
> +		      int dc)
> +{
> +	struct device_d *dev = &spi->dev;
> +
> +	dbi->spi = spi;
> +	dbi->read_commands = mipi_dbi_dcs_read_commands;
> +
> +	if (!gpio_is_valid(dc)) {
> +		dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n");
> +		return -ENOSYS;
> +	}
> +
> +	dbi->command = mipi_dbi_typec3_command;
> +	dbi->dc = dc;
> +	// TODO: can we just force 16 bit?
> +	if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16)
> +		dbi->swap_bytes = true;
> +
> +	dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
> +
> +	list_add(&dbi->list, &mipi_dbi_list);
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dbi_spi_init);
> +
> +/**
> + * mipi_dbi_spi_transfer - SPI transfer helper
> + * @spi: SPI device
> + * @speed_hz: Override speed (optional)
> + * @bpw: Bits per word
> + * @buf: Buffer to transfer
> + * @len: Buffer length
> + *
> + * This SPI transfer helper breaks up the transfer of @buf into chunks which
> + * the SPI controller driver can handle.
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
> +			  u8 bpw, const void *buf, size_t len)
> +{
> +	size_t max_chunk = spi_max_transfer_size(spi);
> +	struct spi_transfer tr = {
> +		.bits_per_word = bpw,
> +		.speed_hz = speed_hz,
> +	};
> +	struct spi_message m;
> +	size_t chunk;
> +	int ret;
> +
> +	spi_message_init_with_transfers(&m, &tr, 1);
> +
> +	while (len) {
> +		chunk = min(len, max_chunk);
> +
> +		tr.tx_buf = buf;
> +		tr.len = chunk;
> +		buf += chunk;
> +		len -= chunk;
> +
> +		ret = spi_sync(spi, &m);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dbi_spi_transfer);
> +
> +#endif /* CONFIG_SPI */
> +
> +MODULE_LICENSE("GPL");
> diff --git a/include/spi/spi.h b/include/spi/spi.h
> index c5ad6bd39ff9..d133e0e21265 100644
> --- a/include/spi/spi.h
> +++ b/include/spi/spi.h
> @@ -409,6 +409,26 @@ spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
>  	list_add_tail(&t->transfer_list, &m->transfers);
>  }
>  
> +/**
> + * spi_message_init_with_transfers - Initialize spi_message and append transfers
> + * @m: spi_message to be initialized
> + * @xfers: An array of spi transfers
> + * @num_xfers: Number of items in the xfer array
> + *
> + * This function initializes the given spi_message and adds each spi_transfer in
> + * the given array to the message.
> + */
> +static inline void
> +spi_message_init_with_transfers(struct spi_message *m,
> +struct spi_transfer *xfers, unsigned int num_xfers)
> +{
> +	unsigned int i;
> +
> +	spi_message_init(m);
> +	for (i = 0; i < num_xfers; ++i)
> +		spi_message_add_tail(&xfers[i], m);
> +}
> +
>  static inline void
>  spi_transfer_del(struct spi_transfer *t)
>  {
> -- 
> 2.30.2
> 
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox

Best regards
Thorsten

--
Thorsten Scherer | Eckelmann AG | www.eckelmann.de |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/4] video: add MIPI DBI Type C Option 3 support
  2022-02-03 14:30   ` Thorsten Scherer
@ 2022-02-03 14:34     ` Ahmad Fatoum
  0 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-03 14:34 UTC (permalink / raw)
  To: Thorsten Scherer; +Cc: barebox

Hello Thorsten,

On 03.02.22 15:30, Thorsten Scherer wrote:
> Hi Ahmad,
> 
> applying this patch makes menuconfig fail.

Yes.

>> +config CMD_MIPI_DBI
>> +	bool
>> +	depends on DRIVER_VIDEO_MIPI_DBI && SPI
>> +	select PRINTF_HEXSTR
>> +	prompt "mipi_dbi command"
>> +	help
>> +	  write/read from MIPI DBI SPI device
>> +
>> +	  Usage: mipi_dbi [-wld] [REG] [DATA...]
>> +
>> +	  Options:
>> +		  -l		list all MIPI DBI devices
>> +		  -d DEVICE	select specific device (default is first registered)
>> +		  -w		issue write command
>> +
>> +BAREBOX_CMD_START(mipi_dbi)
>> +	.cmd		= do_mipi_dbi,
>> +	BAREBOX_CMD_DESC("write/read from MIPI DBI SPI device")
>> +	BAREBOX_CMD_OPTS("[-wld] [REG] [DATA...]")
>> +	BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
>> +	BAREBOX_CMD_HELP(cmd_mipi_dbi_help)
>> +BAREBOX_CMD_END
> 
> Copy and paste error?

Ack. I thought the change is so trivial, I don't need to build test..

I sent out a v3 yesteday with this revised, see
https://lore.barebox.org/barebox/20220202223023.341817-1-a.fatoum@pengutronix.de/

Thanks,
Ahmad


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/4] video: add MIPI DBI Type C Option 3 support
  2022-02-02  9:55 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum
  2022-02-03 14:30   ` Thorsten Scherer
@ 2022-02-03 14:34   ` Thorsten Scherer
  2022-02-03 16:37     ` Ahmad Fatoum
  1 sibling, 1 reply; 10+ messages in thread
From: Thorsten Scherer @ 2022-02-03 14:34 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

Hi Ahmad,

uups.  I did not see v3 of this patch. Sorry for the noise.

On Wed, Feb 02, 2022 at 10:55:53AM +0100, Ahmad Fatoum wrote:
> Import the Linux v5.15 state of the driver to allow easy porting of
> MIPI-DBI displays like the Ilitek 9431 added in a follow-up commit.
> 
> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
>  commands/Kconfig         |  23 ++
>  commands/Makefile        |   1 +
>  drivers/video/Kconfig    |   3 +
>  drivers/video/Makefile   |   1 +
>  drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++++++
>  include/spi/spi.h        |  20 ++
>  6 files changed, 515 insertions(+)
>  create mode 100644 drivers/video/mipi_dbi.c
> 
> diff --git a/commands/Kconfig b/commands/Kconfig
> index ba8ca5cdebce..af60f7be1587 100644
> --- a/commands/Kconfig
> +++ b/commands/Kconfig
> @@ -1969,6 +1969,29 @@ config CMD_SPI
>  		  -w BIT	bits per word (default 8)
>  		  -v		verbose
>  
> +config CMD_MIPI_DBI
> +	bool
> +	depends on DRIVER_VIDEO_MIPI_DBI && SPI
> +	select PRINTF_HEXSTR
> +	prompt "mipi_dbi command"
> +	help
> +	  write/read from MIPI DBI SPI device
> +
> +	  Usage: mipi_dbi [-wld] [REG] [DATA...]
> +
> +	  Options:
> +		  -l		list all MIPI DBI devices
> +		  -d DEVICE	select specific device (default is first registered)
> +		  -w		issue write command
> +
> +BAREBOX_CMD_START(mipi_dbi)
> +	.cmd		= do_mipi_dbi,
> +	BAREBOX_CMD_DESC("write/read from MIPI DBI SPI device")
> +	BAREBOX_CMD_OPTS("[-wld] [REG] [DATA...]")
> +	BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
> +	BAREBOX_CMD_HELP(cmd_mipi_dbi_help)
> +BAREBOX_CMD_END
> +
>  config CMD_LED_TRIGGER
>  	bool
>  	depends on LED_TRIGGERS
> diff --git a/commands/Makefile b/commands/Makefile
> index db78d0b877f6..fffb6d979e82 100644
> --- a/commands/Makefile
> +++ b/commands/Makefile
> @@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_GPIO)		+= gpio.o
>  obj-$(CONFIG_CMD_UNCOMPRESS)	+= uncompress.o
>  obj-$(CONFIG_CMD_I2C)		+= i2c.o
>  obj-$(CONFIG_CMD_SPI)		+= spi.o
> +obj-$(CONFIG_CMD_MIPI_DBI)	+= mipi_dbi.o
>  obj-$(CONFIG_CMD_UBI)		+= ubi.o
>  obj-$(CONFIG_CMD_UBIFORMAT)	+= ubiformat.o
>  obj-$(CONFIG_CMD_MENU)		+= menu.o
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index 1b8672fdea82..70d1d809536b 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -129,6 +129,9 @@ config DRIVER_VIDEO_EDID
>  	  This enabled support for reading and parsing EDID data from an attached
>  	  monitor.
>  
> +config DRIVER_VIDEO_MIPI_DBI
> +	bool
> +
>  config DRIVER_VIDEO_BACKLIGHT
>  	bool "Add backlight support"
>  	help
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 7f4429278987..a7b70d82072a 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_VPL) += vpl.o
>  obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
>  obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
>  obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
> +obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
>  
>  obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
>  obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
> diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
> new file mode 100644
> index 000000000000..48b1110f72ab
> --- /dev/null
> +++ b/drivers/video/mipi_dbi.c
> @@ -0,0 +1,467 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * MIPI Display Bus Interface (DBI) LCD controller support
> + *
> + * Copyright 2016 Noralf Trønnes
> + */
> +
> +#define pr_fmt(fmt) "mipi-dbi: " fmt
> +
> +#include <common.h>
> +#include <linux/kernel.h>
> +#include <linux/sizes.h>
> +#include <gpiod.h>
> +#include <regulator.h>
> +#include <spi/spi.h>
> +#include <video/mipi_dbi.h>
> +
> +#include <video/vpl.h>
> +#include <video/mipi_display.h>
> +#include <video/fourcc.h>
> +
> +#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
> +
> +#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))
> +
> +LIST_HEAD(mipi_dbi_list);
> +EXPORT_SYMBOL(mipi_dbi_list);
> +
> +/**
> + * DOC: overview
> + *
> + * This library provides helpers for MIPI Display Bus Interface (DBI)
> + * compatible display controllers.
> + *
> + * Many controllers for tiny lcd displays are MIPI compliant and can use this
> + * library. If a controller uses registers 0x2A and 0x2B to set the area to
> + * update and uses register 0x2C to write to frame memory, it is most likely
> + * MIPI compliant.
> + *
> + * Only MIPI Type 1 displays are supported since a full frame memory is needed.
> + *
> + * There are 3 MIPI DBI implementation types:
> + *
> + * A. Motorola 6800 type parallel bus
> + *
> + * B. Intel 8080 type parallel bus
> + *
> + * C. SPI type with 3 options:
> + *
> + *    1. 9-bit with the Data/Command signal as the ninth bit
> + *    2. Same as above except it's sent as 16 bits
> + *    3. 8-bit with the Data/Command signal as a separate D/CX pin
> + *
> + * Currently barebox mipi_dbi only supports Type C option 3 with
> + * mipi_dbi_spi_init().
> + */
> +
> +#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
> +({ \
> +	if (!len) \
> +		pr_debug("cmd=%02x\n", cmd); \
> +	else if (len <= 32) \
> +		pr_debug("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\
> +	else \
> +		pr_debug("cmd=%02x, len=%zu\n", cmd, len); \
> +})
> +
> +static const u8 mipi_dbi_dcs_read_commands[] = {
> +	MIPI_DCS_GET_DISPLAY_ID,
> +	MIPI_DCS_GET_RED_CHANNEL,
> +	MIPI_DCS_GET_GREEN_CHANNEL,
> +	MIPI_DCS_GET_BLUE_CHANNEL,
> +	MIPI_DCS_GET_DISPLAY_STATUS,
> +	MIPI_DCS_GET_POWER_MODE,
> +	MIPI_DCS_GET_ADDRESS_MODE,
> +	MIPI_DCS_GET_PIXEL_FORMAT,
> +	MIPI_DCS_GET_DISPLAY_MODE,
> +	MIPI_DCS_GET_SIGNAL_MODE,
> +	MIPI_DCS_GET_DIAGNOSTIC_RESULT,
> +	MIPI_DCS_READ_MEMORY_START,
> +	MIPI_DCS_READ_MEMORY_CONTINUE,
> +	MIPI_DCS_GET_SCANLINE,
> +	MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
> +	MIPI_DCS_GET_CONTROL_DISPLAY,
> +	MIPI_DCS_GET_POWER_SAVE,
> +	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS,
> +	MIPI_DCS_READ_DDB_START,
> +	MIPI_DCS_READ_DDB_CONTINUE,
> +	0, /* sentinel */
> +};
> +
> +bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd)
> +{
> +	unsigned int i;
> +
> +	if (!dbi->read_commands)
> +		return false;
> +
> +	for (i = 0; i < 0xff; i++) {
> +		if (!dbi->read_commands[i])
> +			return false;
> +		if (cmd == dbi->read_commands[i])
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +int mipi_dbi_command_read_len(int cmd)
> +{
> +	switch (cmd) {
> +	case MIPI_DCS_READ_MEMORY_START:
> +	case MIPI_DCS_READ_MEMORY_CONTINUE:
> +		return 2;
> +	case MIPI_DCS_GET_DISPLAY_ID:
> +		return 3;
> +	case MIPI_DCS_GET_DISPLAY_STATUS:
> +		return 4;
> +	default:
> +		return 1;
> +	}
> +}
> +
> +/**
> + * mipi_dbi_command_read - MIPI DCS read command
> + * @dbi: MIPI DBI structure
> + * @cmd: Command
> + * @val: Value read
> + *
> + * Send MIPI DCS read command to the controller.
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val)
> +{
> +	if (!dbi->read_commands)
> +		return -EACCES;
> +
> +	if (!mipi_dbi_command_is_read(dbi, cmd))
> +		return -EINVAL;
> +
> +	return mipi_dbi_command_buf(dbi, cmd, val, 1);
> +}
> +EXPORT_SYMBOL(mipi_dbi_command_read);
> +
> +/**
> + * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array
> + * @dbi: MIPI DBI structure
> + * @cmd: Command
> + * @data: Parameter buffer
> + * @len: Buffer length
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len)
> +{
> +	u8 *cmdbuf;
> +	int ret;
> +
> +	/* SPI requires dma-safe buffers */
> +	cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
> +	if (!cmdbuf)
> +		return -ENOMEM;
> +
> +	ret = dbi->command(dbi, cmdbuf, data, len);
> +
> +	kfree(cmdbuf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mipi_dbi_command_buf);
> +
> +/* This should only be used by mipi_dbi_command() */
> +int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
> +			      size_t len)
> +{
> +	u8 *buf;
> +	int ret;
> +
> +	buf = kmemdup(data, len, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	ret = mipi_dbi_command_buf(dbi, cmd, buf, len);
> +
> +	kfree(buf);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
> +
> +/**
> + * mipi_dbi_hw_reset - Hardware reset of controller
> + * @dbi: MIPI DBI structure
> + *
> + * Reset controller if the &mipi_dbi->reset gpio is set.
> + */
> +void mipi_dbi_hw_reset(struct mipi_dbi *dbi)
> +{
> +	if (!gpio_is_valid(dbi->reset))
> +		return;
> +
> +	gpiod_set_value(dbi->reset, 0);
> +	udelay(20);
> +	gpiod_set_value(dbi->reset, 1);
> +	mdelay(120);
> +}
> +EXPORT_SYMBOL(mipi_dbi_hw_reset);
> +
> +/**
> + * mipi_dbi_display_is_on - Check if display is on
> + * @dbi: MIPI DBI structure
> + *
> + * This function checks the Power Mode register (if readable) to see if
> + * display output is turned on. This can be used to see if the bootloader
> + * has already turned on the display avoiding flicker when the pipeline is
> + * enabled.
> + *
> + * Returns:
> + * true if the display can be verified to be on, false otherwise.
> + */
> +bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
> +{
> +	u8 val;
> +
> +	if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val))
> +		return false;
> +
> +	val &= ~DCS_POWER_MODE_RESERVED_MASK;
> +
> +	/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
> +	if (val != (DCS_POWER_MODE_DISPLAY |
> +	    DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
> +		return false;
> +
> +	pr_debug("Display is ON\n");
> +
> +	return true;
> +}
> +EXPORT_SYMBOL(mipi_dbi_display_is_on);
> +
> +#if IS_ENABLED(CONFIG_SPI)
> +
> +/**
> + * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
> + * @spi: SPI device
> + * @len: The transfer buffer length.
> + *
> + * Many controllers have a max speed of 10MHz, but can be pushed way beyond
> + * that. Increase reliability by running pixel data at max speed and the rest
> + * at 10MHz, preventing transfer glitches from messing up the init settings.
> + */
> +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
> +{
> +	if (len > 64)
> +		return 0; /* use default */
> +
> +	return min_t(u32, 10000000, spi->max_speed_hz);
> +}
> +EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
> +
> +static bool mipi_dbi_machine_little_endian(void)
> +{
> +#if defined(__LITTLE_ENDIAN)
> +	return true;
> +#else
> +	return false;
> +#endif
> +}
> +
> +/* MIPI DBI Type C Option 3 */
> +
> +static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
> +					u8 *data, size_t len)
> +{
> +	struct spi_device *spi = dbi->spi;
> +	u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED,
> +			     spi->max_speed_hz / 2);
> +	struct spi_transfer tr[2] = {
> +		{
> +			.speed_hz = speed_hz,
> +			.tx_buf = cmd,
> +			.len = 1,
> +		}, {
> +			.speed_hz = speed_hz,
> +			.len = len,
> +		},
> +	};
> +	struct spi_message m;
> +	u8 *buf;
> +	int ret;
> +
> +	if (!len)
> +		return -EINVAL;
> +
> +	/*
> +	 * Support non-standard 24-bit and 32-bit Nokia read commands which
> +	 * start with a dummy clock, so we need to read an extra byte.
> +	 */
> +	if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
> +	    *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
> +		if (!(len == 3 || len == 4))
> +			return -EINVAL;
> +
> +		tr[1].len = len + 1;
> +	}
> +
> +	buf = kmalloc(tr[1].len, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	tr[1].rx_buf = buf;
> +	gpiod_set_value(dbi->dc, 0);
> +
> +	spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
> +	ret = spi_sync(spi, &m);
> +	if (ret)
> +		goto err_free;
> +
> +	if (tr[1].len == len) {
> +		memcpy(data, buf, len);
> +	} else {
> +		unsigned int i;
> +
> +		for (i = 0; i < len; i++)
> +			data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
> +	}
> +
> +	MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
> +
> +err_free:
> +	kfree(buf);
> +
> +	return ret;
> +}
> +
> +static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
> +				   u8 *par, size_t num)
> +{
> +	struct spi_device *spi = dbi->spi;
> +	unsigned int bpw = 8;
> +	u32 speed_hz;
> +	int ret;
> +
> +	if (mipi_dbi_command_is_read(dbi, *cmd))
> +		return mipi_dbi_typec3_command_read(dbi, cmd, par, num);
> +
> +	MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
> +
> +	gpiod_set_value(dbi->dc, 0);
> +	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
> +	ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
> +	if (ret || !num)
> +		return ret;
> +
> +	if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
> +		bpw = 16;
> +
> +	gpiod_set_value(dbi->dc, 1);
> +	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
> +
> +	return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
> +}
> +
> +/**
> + * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
> + * @spi: SPI device
> + * @dbi: MIPI DBI structure to initialize
> + * @dc: D/C gpio
> + *
> + * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the
> + * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or
> + * a driver-specific init.
> + *
> + * Type C Option 3 interface is assumed, Type C Option 1 is not yet supported,
> + * because barebox has no generic way yet to require a 9-bit SPI transfer
> + *
> + * If the SPI master driver doesn't support the necessary bits per word,
> + * the following transformation is used:
> + *
> + * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command.
> + * - 16-bit: if big endian send as 8-bit, if little endian swap bytes
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
> +		      int dc)
> +{
> +	struct device_d *dev = &spi->dev;
> +
> +	dbi->spi = spi;
> +	dbi->read_commands = mipi_dbi_dcs_read_commands;
> +
> +	if (!gpio_is_valid(dc)) {
> +		dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n");
> +		return -ENOSYS;
> +	}
> +
> +	dbi->command = mipi_dbi_typec3_command;
> +	dbi->dc = dc;
> +	// TODO: can we just force 16 bit?
> +	if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16)
> +		dbi->swap_bytes = true;
> +
> +	dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
> +
> +	list_add(&dbi->list, &mipi_dbi_list);
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dbi_spi_init);
> +
> +/**
> + * mipi_dbi_spi_transfer - SPI transfer helper
> + * @spi: SPI device
> + * @speed_hz: Override speed (optional)
> + * @bpw: Bits per word
> + * @buf: Buffer to transfer
> + * @len: Buffer length
> + *
> + * This SPI transfer helper breaks up the transfer of @buf into chunks which
> + * the SPI controller driver can handle.
> + *
> + * Returns:
> + * Zero on success, negative error code on failure.
> + */
> +int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
> +			  u8 bpw, const void *buf, size_t len)
> +{
> +	size_t max_chunk = spi_max_transfer_size(spi);
> +	struct spi_transfer tr = {
> +		.bits_per_word = bpw,
> +		.speed_hz = speed_hz,
> +	};
> +	struct spi_message m;
> +	size_t chunk;
> +	int ret;
> +
> +	spi_message_init_with_transfers(&m, &tr, 1);
> +
> +	while (len) {
> +		chunk = min(len, max_chunk);
> +
> +		tr.tx_buf = buf;
> +		tr.len = chunk;
> +		buf += chunk;
> +		len -= chunk;
> +
> +		ret = spi_sync(spi, &m);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mipi_dbi_spi_transfer);
> +
> +#endif /* CONFIG_SPI */
> +
> +MODULE_LICENSE("GPL");
> diff --git a/include/spi/spi.h b/include/spi/spi.h
> index c5ad6bd39ff9..d133e0e21265 100644
> --- a/include/spi/spi.h
> +++ b/include/spi/spi.h
> @@ -409,6 +409,26 @@ spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
>  	list_add_tail(&t->transfer_list, &m->transfers);
>  }
>  
> +/**
> + * spi_message_init_with_transfers - Initialize spi_message and append transfers
> + * @m: spi_message to be initialized
> + * @xfers: An array of spi transfers
> + * @num_xfers: Number of items in the xfer array
> + *
> + * This function initializes the given spi_message and adds each spi_transfer in
> + * the given array to the message.
> + */
> +static inline void
> +spi_message_init_with_transfers(struct spi_message *m,
> +struct spi_transfer *xfers, unsigned int num_xfers)
> +{
> +	unsigned int i;
> +
> +	spi_message_init(m);
> +	for (i = 0; i < num_xfers; ++i)
> +		spi_message_add_tail(&xfers[i], m);
> +}
> +
>  static inline void
>  spi_transfer_del(struct spi_transfer *t)
>  {
> -- 
> 2.30.2
> 
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox

Best regards
Thorsten

--
Thorsten Scherer | Eckelmann AG | www.eckelmann.de |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/4] video: add MIPI DBI Type C Option 3 support
  2022-02-03 14:34   ` Thorsten Scherer
@ 2022-02-03 16:37     ` Ahmad Fatoum
  0 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-02-03 16:37 UTC (permalink / raw)
  To: Thorsten Scherer; +Cc: barebox

On 03.02.22 15:34, Thorsten Scherer wrote:
> Hi Ahmad,
> 
> uups.  I did not see v3 of this patch. Sorry for the noise.

All good. Thanks for looking out :)

> 
> On Wed, Feb 02, 2022 at 10:55:53AM +0100, Ahmad Fatoum wrote:
>> Import the Linux v5.15 state of the driver to allow easy porting of
>> MIPI-DBI displays like the Ilitek 9431 added in a follow-up commit.
>>
>> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
>> ---
>>  commands/Kconfig         |  23 ++
>>  commands/Makefile        |   1 +
>>  drivers/video/Kconfig    |   3 +
>>  drivers/video/Makefile   |   1 +
>>  drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++++++
>>  include/spi/spi.h        |  20 ++
>>  6 files changed, 515 insertions(+)
>>  create mode 100644 drivers/video/mipi_dbi.c
>>
>> diff --git a/commands/Kconfig b/commands/Kconfig
>> index ba8ca5cdebce..af60f7be1587 100644
>> --- a/commands/Kconfig
>> +++ b/commands/Kconfig
>> @@ -1969,6 +1969,29 @@ config CMD_SPI
>>  		  -w BIT	bits per word (default 8)
>>  		  -v		verbose
>>  
>> +config CMD_MIPI_DBI
>> +	bool
>> +	depends on DRIVER_VIDEO_MIPI_DBI && SPI
>> +	select PRINTF_HEXSTR
>> +	prompt "mipi_dbi command"
>> +	help
>> +	  write/read from MIPI DBI SPI device
>> +
>> +	  Usage: mipi_dbi [-wld] [REG] [DATA...]
>> +
>> +	  Options:
>> +		  -l		list all MIPI DBI devices
>> +		  -d DEVICE	select specific device (default is first registered)
>> +		  -w		issue write command
>> +
>> +BAREBOX_CMD_START(mipi_dbi)
>> +	.cmd		= do_mipi_dbi,
>> +	BAREBOX_CMD_DESC("write/read from MIPI DBI SPI device")
>> +	BAREBOX_CMD_OPTS("[-wld] [REG] [DATA...]")
>> +	BAREBOX_CMD_GROUP(CMD_GRP_HWMANIP)
>> +	BAREBOX_CMD_HELP(cmd_mipi_dbi_help)
>> +BAREBOX_CMD_END
>> +
>>  config CMD_LED_TRIGGER
>>  	bool
>>  	depends on LED_TRIGGERS
>> diff --git a/commands/Makefile b/commands/Makefile
>> index db78d0b877f6..fffb6d979e82 100644
>> --- a/commands/Makefile
>> +++ b/commands/Makefile
>> @@ -67,6 +67,7 @@ obj-$(CONFIG_CMD_GPIO)		+= gpio.o
>>  obj-$(CONFIG_CMD_UNCOMPRESS)	+= uncompress.o
>>  obj-$(CONFIG_CMD_I2C)		+= i2c.o
>>  obj-$(CONFIG_CMD_SPI)		+= spi.o
>> +obj-$(CONFIG_CMD_MIPI_DBI)	+= mipi_dbi.o
>>  obj-$(CONFIG_CMD_UBI)		+= ubi.o
>>  obj-$(CONFIG_CMD_UBIFORMAT)	+= ubiformat.o
>>  obj-$(CONFIG_CMD_MENU)		+= menu.o
>> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
>> index 1b8672fdea82..70d1d809536b 100644
>> --- a/drivers/video/Kconfig
>> +++ b/drivers/video/Kconfig
>> @@ -129,6 +129,9 @@ config DRIVER_VIDEO_EDID
>>  	  This enabled support for reading and parsing EDID data from an attached
>>  	  monitor.
>>  
>> +config DRIVER_VIDEO_MIPI_DBI
>> +	bool
>> +
>>  config DRIVER_VIDEO_BACKLIGHT
>>  	bool "Add backlight support"
>>  	help
>> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
>> index 7f4429278987..a7b70d82072a 100644
>> --- a/drivers/video/Makefile
>> +++ b/drivers/video/Makefile
>> @@ -9,6 +9,7 @@ obj-$(CONFIG_VIDEO_VPL) += vpl.o
>>  obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
>>  obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
>>  obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
>> +obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
>>  
>>  obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
>>  obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
>> diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c
>> new file mode 100644
>> index 000000000000..48b1110f72ab
>> --- /dev/null
>> +++ b/drivers/video/mipi_dbi.c
>> @@ -0,0 +1,467 @@
>> +// SPDX-License-Identifier: GPL-2.0-or-later
>> +/*
>> + * MIPI Display Bus Interface (DBI) LCD controller support
>> + *
>> + * Copyright 2016 Noralf Trønnes
>> + */
>> +
>> +#define pr_fmt(fmt) "mipi-dbi: " fmt
>> +
>> +#include <common.h>
>> +#include <linux/kernel.h>
>> +#include <linux/sizes.h>
>> +#include <gpiod.h>
>> +#include <regulator.h>
>> +#include <spi/spi.h>
>> +#include <video/mipi_dbi.h>
>> +
>> +#include <video/vpl.h>
>> +#include <video/mipi_display.h>
>> +#include <video/fourcc.h>
>> +
>> +#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
>> +
>> +#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))
>> +
>> +LIST_HEAD(mipi_dbi_list);
>> +EXPORT_SYMBOL(mipi_dbi_list);
>> +
>> +/**
>> + * DOC: overview
>> + *
>> + * This library provides helpers for MIPI Display Bus Interface (DBI)
>> + * compatible display controllers.
>> + *
>> + * Many controllers for tiny lcd displays are MIPI compliant and can use this
>> + * library. If a controller uses registers 0x2A and 0x2B to set the area to
>> + * update and uses register 0x2C to write to frame memory, it is most likely
>> + * MIPI compliant.
>> + *
>> + * Only MIPI Type 1 displays are supported since a full frame memory is needed.
>> + *
>> + * There are 3 MIPI DBI implementation types:
>> + *
>> + * A. Motorola 6800 type parallel bus
>> + *
>> + * B. Intel 8080 type parallel bus
>> + *
>> + * C. SPI type with 3 options:
>> + *
>> + *    1. 9-bit with the Data/Command signal as the ninth bit
>> + *    2. Same as above except it's sent as 16 bits
>> + *    3. 8-bit with the Data/Command signal as a separate D/CX pin
>> + *
>> + * Currently barebox mipi_dbi only supports Type C option 3 with
>> + * mipi_dbi_spi_init().
>> + */
>> +
>> +#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
>> +({ \
>> +	if (!len) \
>> +		pr_debug("cmd=%02x\n", cmd); \
>> +	else if (len <= 32) \
>> +		pr_debug("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\
>> +	else \
>> +		pr_debug("cmd=%02x, len=%zu\n", cmd, len); \
>> +})
>> +
>> +static const u8 mipi_dbi_dcs_read_commands[] = {
>> +	MIPI_DCS_GET_DISPLAY_ID,
>> +	MIPI_DCS_GET_RED_CHANNEL,
>> +	MIPI_DCS_GET_GREEN_CHANNEL,
>> +	MIPI_DCS_GET_BLUE_CHANNEL,
>> +	MIPI_DCS_GET_DISPLAY_STATUS,
>> +	MIPI_DCS_GET_POWER_MODE,
>> +	MIPI_DCS_GET_ADDRESS_MODE,
>> +	MIPI_DCS_GET_PIXEL_FORMAT,
>> +	MIPI_DCS_GET_DISPLAY_MODE,
>> +	MIPI_DCS_GET_SIGNAL_MODE,
>> +	MIPI_DCS_GET_DIAGNOSTIC_RESULT,
>> +	MIPI_DCS_READ_MEMORY_START,
>> +	MIPI_DCS_READ_MEMORY_CONTINUE,
>> +	MIPI_DCS_GET_SCANLINE,
>> +	MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
>> +	MIPI_DCS_GET_CONTROL_DISPLAY,
>> +	MIPI_DCS_GET_POWER_SAVE,
>> +	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS,
>> +	MIPI_DCS_READ_DDB_START,
>> +	MIPI_DCS_READ_DDB_CONTINUE,
>> +	0, /* sentinel */
>> +};
>> +
>> +bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd)
>> +{
>> +	unsigned int i;
>> +
>> +	if (!dbi->read_commands)
>> +		return false;
>> +
>> +	for (i = 0; i < 0xff; i++) {
>> +		if (!dbi->read_commands[i])
>> +			return false;
>> +		if (cmd == dbi->read_commands[i])
>> +			return true;
>> +	}
>> +
>> +	return false;
>> +}
>> +
>> +int mipi_dbi_command_read_len(int cmd)
>> +{
>> +	switch (cmd) {
>> +	case MIPI_DCS_READ_MEMORY_START:
>> +	case MIPI_DCS_READ_MEMORY_CONTINUE:
>> +		return 2;
>> +	case MIPI_DCS_GET_DISPLAY_ID:
>> +		return 3;
>> +	case MIPI_DCS_GET_DISPLAY_STATUS:
>> +		return 4;
>> +	default:
>> +		return 1;
>> +	}
>> +}
>> +
>> +/**
>> + * mipi_dbi_command_read - MIPI DCS read command
>> + * @dbi: MIPI DBI structure
>> + * @cmd: Command
>> + * @val: Value read
>> + *
>> + * Send MIPI DCS read command to the controller.
>> + *
>> + * Returns:
>> + * Zero on success, negative error code on failure.
>> + */
>> +int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val)
>> +{
>> +	if (!dbi->read_commands)
>> +		return -EACCES;
>> +
>> +	if (!mipi_dbi_command_is_read(dbi, cmd))
>> +		return -EINVAL;
>> +
>> +	return mipi_dbi_command_buf(dbi, cmd, val, 1);
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_command_read);
>> +
>> +/**
>> + * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array
>> + * @dbi: MIPI DBI structure
>> + * @cmd: Command
>> + * @data: Parameter buffer
>> + * @len: Buffer length
>> + *
>> + * Returns:
>> + * Zero on success, negative error code on failure.
>> + */
>> +int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len)
>> +{
>> +	u8 *cmdbuf;
>> +	int ret;
>> +
>> +	/* SPI requires dma-safe buffers */
>> +	cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
>> +	if (!cmdbuf)
>> +		return -ENOMEM;
>> +
>> +	ret = dbi->command(dbi, cmdbuf, data, len);
>> +
>> +	kfree(cmdbuf);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_command_buf);
>> +
>> +/* This should only be used by mipi_dbi_command() */
>> +int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
>> +			      size_t len)
>> +{
>> +	u8 *buf;
>> +	int ret;
>> +
>> +	buf = kmemdup(data, len, GFP_KERNEL);
>> +	if (!buf)
>> +		return -ENOMEM;
>> +
>> +	ret = mipi_dbi_command_buf(dbi, cmd, buf, len);
>> +
>> +	kfree(buf);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_command_stackbuf);
>> +
>> +/**
>> + * mipi_dbi_hw_reset - Hardware reset of controller
>> + * @dbi: MIPI DBI structure
>> + *
>> + * Reset controller if the &mipi_dbi->reset gpio is set.
>> + */
>> +void mipi_dbi_hw_reset(struct mipi_dbi *dbi)
>> +{
>> +	if (!gpio_is_valid(dbi->reset))
>> +		return;
>> +
>> +	gpiod_set_value(dbi->reset, 0);
>> +	udelay(20);
>> +	gpiod_set_value(dbi->reset, 1);
>> +	mdelay(120);
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_hw_reset);
>> +
>> +/**
>> + * mipi_dbi_display_is_on - Check if display is on
>> + * @dbi: MIPI DBI structure
>> + *
>> + * This function checks the Power Mode register (if readable) to see if
>> + * display output is turned on. This can be used to see if the bootloader
>> + * has already turned on the display avoiding flicker when the pipeline is
>> + * enabled.
>> + *
>> + * Returns:
>> + * true if the display can be verified to be on, false otherwise.
>> + */
>> +bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
>> +{
>> +	u8 val;
>> +
>> +	if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val))
>> +		return false;
>> +
>> +	val &= ~DCS_POWER_MODE_RESERVED_MASK;
>> +
>> +	/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
>> +	if (val != (DCS_POWER_MODE_DISPLAY |
>> +	    DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
>> +		return false;
>> +
>> +	pr_debug("Display is ON\n");
>> +
>> +	return true;
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_display_is_on);
>> +
>> +#if IS_ENABLED(CONFIG_SPI)
>> +
>> +/**
>> + * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
>> + * @spi: SPI device
>> + * @len: The transfer buffer length.
>> + *
>> + * Many controllers have a max speed of 10MHz, but can be pushed way beyond
>> + * that. Increase reliability by running pixel data at max speed and the rest
>> + * at 10MHz, preventing transfer glitches from messing up the init settings.
>> + */
>> +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
>> +{
>> +	if (len > 64)
>> +		return 0; /* use default */
>> +
>> +	return min_t(u32, 10000000, spi->max_speed_hz);
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
>> +
>> +static bool mipi_dbi_machine_little_endian(void)
>> +{
>> +#if defined(__LITTLE_ENDIAN)
>> +	return true;
>> +#else
>> +	return false;
>> +#endif
>> +}
>> +
>> +/* MIPI DBI Type C Option 3 */
>> +
>> +static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
>> +					u8 *data, size_t len)
>> +{
>> +	struct spi_device *spi = dbi->spi;
>> +	u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED,
>> +			     spi->max_speed_hz / 2);
>> +	struct spi_transfer tr[2] = {
>> +		{
>> +			.speed_hz = speed_hz,
>> +			.tx_buf = cmd,
>> +			.len = 1,
>> +		}, {
>> +			.speed_hz = speed_hz,
>> +			.len = len,
>> +		},
>> +	};
>> +	struct spi_message m;
>> +	u8 *buf;
>> +	int ret;
>> +
>> +	if (!len)
>> +		return -EINVAL;
>> +
>> +	/*
>> +	 * Support non-standard 24-bit and 32-bit Nokia read commands which
>> +	 * start with a dummy clock, so we need to read an extra byte.
>> +	 */
>> +	if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
>> +	    *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
>> +		if (!(len == 3 || len == 4))
>> +			return -EINVAL;
>> +
>> +		tr[1].len = len + 1;
>> +	}
>> +
>> +	buf = kmalloc(tr[1].len, GFP_KERNEL);
>> +	if (!buf)
>> +		return -ENOMEM;
>> +
>> +	tr[1].rx_buf = buf;
>> +	gpiod_set_value(dbi->dc, 0);
>> +
>> +	spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
>> +	ret = spi_sync(spi, &m);
>> +	if (ret)
>> +		goto err_free;
>> +
>> +	if (tr[1].len == len) {
>> +		memcpy(data, buf, len);
>> +	} else {
>> +		unsigned int i;
>> +
>> +		for (i = 0; i < len; i++)
>> +			data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
>> +	}
>> +
>> +	MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
>> +
>> +err_free:
>> +	kfree(buf);
>> +
>> +	return ret;
>> +}
>> +
>> +static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
>> +				   u8 *par, size_t num)
>> +{
>> +	struct spi_device *spi = dbi->spi;
>> +	unsigned int bpw = 8;
>> +	u32 speed_hz;
>> +	int ret;
>> +
>> +	if (mipi_dbi_command_is_read(dbi, *cmd))
>> +		return mipi_dbi_typec3_command_read(dbi, cmd, par, num);
>> +
>> +	MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
>> +
>> +	gpiod_set_value(dbi->dc, 0);
>> +	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
>> +	ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
>> +	if (ret || !num)
>> +		return ret;
>> +
>> +	if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
>> +		bpw = 16;
>> +
>> +	gpiod_set_value(dbi->dc, 1);
>> +	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
>> +
>> +	return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
>> +}
>> +
>> +/**
>> + * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
>> + * @spi: SPI device
>> + * @dbi: MIPI DBI structure to initialize
>> + * @dc: D/C gpio
>> + *
>> + * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the
>> + * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or
>> + * a driver-specific init.
>> + *
>> + * Type C Option 3 interface is assumed, Type C Option 1 is not yet supported,
>> + * because barebox has no generic way yet to require a 9-bit SPI transfer
>> + *
>> + * If the SPI master driver doesn't support the necessary bits per word,
>> + * the following transformation is used:
>> + *
>> + * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command.
>> + * - 16-bit: if big endian send as 8-bit, if little endian swap bytes
>> + *
>> + * Returns:
>> + * Zero on success, negative error code on failure.
>> + */
>> +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
>> +		      int dc)
>> +{
>> +	struct device_d *dev = &spi->dev;
>> +
>> +	dbi->spi = spi;
>> +	dbi->read_commands = mipi_dbi_dcs_read_commands;
>> +
>> +	if (!gpio_is_valid(dc)) {
>> +		dev_dbg(dev, "MIPI DBI Type-C 1 unsupported\n");
>> +		return -ENOSYS;
>> +	}
>> +
>> +	dbi->command = mipi_dbi_typec3_command;
>> +	dbi->dc = dc;
>> +	// TODO: can we just force 16 bit?
>> +	if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16)
>> +		dbi->swap_bytes = true;
>> +
>> +	dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
>> +
>> +	list_add(&dbi->list, &mipi_dbi_list);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_spi_init);
>> +
>> +/**
>> + * mipi_dbi_spi_transfer - SPI transfer helper
>> + * @spi: SPI device
>> + * @speed_hz: Override speed (optional)
>> + * @bpw: Bits per word
>> + * @buf: Buffer to transfer
>> + * @len: Buffer length
>> + *
>> + * This SPI transfer helper breaks up the transfer of @buf into chunks which
>> + * the SPI controller driver can handle.
>> + *
>> + * Returns:
>> + * Zero on success, negative error code on failure.
>> + */
>> +int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
>> +			  u8 bpw, const void *buf, size_t len)
>> +{
>> +	size_t max_chunk = spi_max_transfer_size(spi);
>> +	struct spi_transfer tr = {
>> +		.bits_per_word = bpw,
>> +		.speed_hz = speed_hz,
>> +	};
>> +	struct spi_message m;
>> +	size_t chunk;
>> +	int ret;
>> +
>> +	spi_message_init_with_transfers(&m, &tr, 1);
>> +
>> +	while (len) {
>> +		chunk = min(len, max_chunk);
>> +
>> +		tr.tx_buf = buf;
>> +		tr.len = chunk;
>> +		buf += chunk;
>> +		len -= chunk;
>> +
>> +		ret = spi_sync(spi, &m);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(mipi_dbi_spi_transfer);
>> +
>> +#endif /* CONFIG_SPI */
>> +
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/spi/spi.h b/include/spi/spi.h
>> index c5ad6bd39ff9..d133e0e21265 100644
>> --- a/include/spi/spi.h
>> +++ b/include/spi/spi.h
>> @@ -409,6 +409,26 @@ spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
>>  	list_add_tail(&t->transfer_list, &m->transfers);
>>  }
>>  
>> +/**
>> + * spi_message_init_with_transfers - Initialize spi_message and append transfers
>> + * @m: spi_message to be initialized
>> + * @xfers: An array of spi transfers
>> + * @num_xfers: Number of items in the xfer array
>> + *
>> + * This function initializes the given spi_message and adds each spi_transfer in
>> + * the given array to the message.
>> + */
>> +static inline void
>> +spi_message_init_with_transfers(struct spi_message *m,
>> +struct spi_transfer *xfers, unsigned int num_xfers)
>> +{
>> +	unsigned int i;
>> +
>> +	spi_message_init(m);
>> +	for (i = 0; i < num_xfers; ++i)
>> +		spi_message_add_tail(&xfers[i], m);
>> +}
>> +
>>  static inline void
>>  spi_transfer_del(struct spi_transfer *t)
>>  {
>> -- 
>> 2.30.2
>>
>>
>> _______________________________________________
>> barebox mailing list
>> barebox@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/barebox
> 
> Best regards
> Thorsten
> 
> --
> Thorsten Scherer | Eckelmann AG | www.eckelmann.de |
> 


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/4] video: add Ilitek ILI9341 panel support
  2022-01-31  8:02 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
@ 2022-01-31  8:03 ` Ahmad Fatoum
  0 siblings, 0 replies; 10+ messages in thread
From: Ahmad Fatoum @ 2022-01-31  8:03 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Port the Linux v5.15 DRM panel driver to barebox. This has been tested
against a STM32F4 LTDC.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/video/Makefile               |   1 +
 drivers/video/panel-ilitek-ili9341.c | 541 +++++++++++++++++++++++++++
 2 files changed, 542 insertions(+)
 create mode 100644 drivers/video/panel-ilitek-ili9341.c

diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index a7b70d82072a..9ec0420ccad1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_DRIVER_VIDEO_MTL017) += mtl017.o
 obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o
 obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o
 obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o
+obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o
 
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o
 obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o
diff --git a/drivers/video/panel-ilitek-ili9341.c b/drivers/video/panel-ilitek-ili9341.c
new file mode 100644
index 000000000000..d8774420226d
--- /dev/null
+++ b/drivers/video/panel-ilitek-ili9341.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ilitek ILI9341 TFT LCD driver.
+ *
+ * This panel can be configured to support:
+ * - 16-bit parallel RGB interface
+ * - 18-bit parallel RGB interface
+ * - 4-line serial spi interface
+ *
+ * Copyright 2018 David Lechner <david@lechnology.com>
+ * Copyright (C) 2021 Dillon Min <dillon.minfei@gmail.com>
+ *
+ * Derived from Linux drivers/drm/gpu/panel/panel-ilitek-ili9341.c
+ */
+#define DEBUG 1
+#include <common.h>
+#include <linux/bitops.h>
+#include <gpiod.h>
+#include <of.h>
+#include <regulator.h>
+#include <spi/spi.h>
+#include <video/vpl.h>
+#include <video/mipi_dbi.h>
+
+#include <video/mipi_display.h>
+
+#define ILI9341_RGB_INTERFACE  0xb0   /* RGB Interface Signal Control */
+#define ILI9341_FRC            0xb1   /* Frame Rate Control register */
+#define ILI9341_DFC            0xb6   /* Display Function Control register */
+#define ILI9341_POWER1         0xc0   /* Power Control 1 register */
+#define ILI9341_POWER2         0xc1   /* Power Control 2 register */
+#define ILI9341_VCOM1          0xc5   /* VCOM Control 1 register */
+#define ILI9341_VCOM2          0xc7   /* VCOM Control 2 register */
+#define ILI9341_POWERA         0xcb   /* Power control A register */
+#define ILI9341_POWERB         0xcf   /* Power control B register */
+#define ILI9341_PGAMMA         0xe0   /* Positive Gamma Correction register */
+#define ILI9341_NGAMMA         0xe1   /* Negative Gamma Correction register */
+#define ILI9341_DTCA           0xe8   /* Driver timing control A */
+#define ILI9341_DTCB           0xea   /* Driver timing control B */
+#define ILI9341_POWER_SEQ      0xed   /* Power on sequence register */
+#define ILI9341_3GAMMA_EN      0xf2   /* 3 Gamma enable register */
+#define ILI9341_INTERFACE      0xf6   /* Interface control register */
+#define ILI9341_PRC            0xf7   /* Pump ratio control register */
+#define ILI9341_ETMOD	       0xb7   /* Entry mode set */
+
+#define ILI9341_MADCTL_BGR	BIT(3)
+#define ILI9341_MADCTL_MV	BIT(5)
+#define ILI9341_MADCTL_MX	BIT(6)
+#define ILI9341_MADCTL_MY	BIT(7)
+
+#define ILI9341_POWER_B_LEN	3
+#define ILI9341_POWER_SEQ_LEN	4
+#define ILI9341_DTCA_LEN	3
+#define ILI9341_DTCB_LEN	2
+#define ILI9341_POWER_A_LEN	5
+#define ILI9341_DFC_1_LEN	2
+#define ILI9341_FRC_LEN		2
+#define ILI9341_VCOM_1_LEN	2
+#define ILI9341_DFC_2_LEN	4
+#define ILI9341_COLUMN_ADDR_LEN	4
+#define ILI9341_PAGE_ADDR_LEN	4
+#define ILI9341_INTERFACE_LEN	3
+#define ILI9341_PGAMMA_LEN	15
+#define ILI9341_NGAMMA_LEN	15
+#define ILI9341_CA_LEN		3
+
+#define ILI9341_PIXEL_DPI_16_BITS	(BIT(6) | BIT(4))
+#define ILI9341_PIXEL_DPI_18_BITS	(BIT(6) | BIT(5))
+#define ILI9341_GAMMA_CURVE_1		BIT(0)
+#define ILI9341_IF_WE_MODE		BIT(0)
+#define ILI9341_IF_BIG_ENDIAN		0x00
+#define ILI9341_IF_DM_RGB		BIT(2)
+#define ILI9341_IF_DM_INTERNAL		0x00
+#define ILI9341_IF_DM_VSYNC		BIT(3)
+#define ILI9341_IF_RM_RGB		BIT(1)
+#define ILI9341_IF_RIM_RGB		0x00
+
+#define ILI9341_COLUMN_ADDR		0x00ef
+#define ILI9341_PAGE_ADDR		0x013f
+
+#define ILI9341_RGB_EPL			BIT(0)
+#define ILI9341_RGB_DPL			BIT(1)
+#define ILI9341_RGB_HSPL		BIT(2)
+#define ILI9341_RGB_VSPL		BIT(3)
+#define ILI9341_RGB_DE_MODE		BIT(6)
+#define ILI9341_RGB_DISP_PATH_MEM	BIT(7)
+
+#define ILI9341_DBI_VCOMH_4P6V		0x23
+#define ILI9341_DBI_PWR_2_DEFAULT	0x10
+#define ILI9341_DBI_PRC_NORMAL		0x20
+#define ILI9341_DBI_VCOM_1_VMH_4P25V	0x3e
+#define ILI9341_DBI_VCOM_1_VML_1P5V	0x28
+#define ILI9341_DBI_VCOM_2_DEC_58	0x86
+#define ILI9341_DBI_FRC_DIVA		0x00
+#define ILI9341_DBI_FRC_RTNA		0x1b
+#define ILI9341_DBI_EMS_GAS		BIT(0)
+#define ILI9341_DBI_EMS_DTS		BIT(1)
+#define ILI9341_DBI_EMS_GON		BIT(2)
+
+/* struct ili9341_config - the system specific ILI9341 configuration */
+struct ili9341_config {
+	u32 max_spi_speed;
+	/* mode: the display mode */
+	const struct fb_videomode mode;
+	/* ca: TODO: need comments for this register */
+	u8 ca[ILI9341_CA_LEN];
+	/* power_b: TODO: need comments for this register */
+	u8 power_b[ILI9341_POWER_B_LEN];
+	/* power_seq: TODO: need comments for this register */
+	u8 power_seq[ILI9341_POWER_SEQ_LEN];
+	/* dtca: TODO: need comments for this register */
+	u8 dtca[ILI9341_DTCA_LEN];
+	/* dtcb: TODO: need comments for this register */
+	u8 dtcb[ILI9341_DTCB_LEN];
+	/* power_a: TODO: need comments for this register */
+	u8 power_a[ILI9341_POWER_A_LEN];
+	/* frc: Frame Rate Control (In Normal Mode/Full Colors) (B1h) */
+	u8 frc[ILI9341_FRC_LEN];
+	/* prc: TODO: need comments for this register */
+	u8 prc;
+	/* dfc_1: B6h DISCTRL (Display Function Control) */
+	u8 dfc_1[ILI9341_DFC_1_LEN];
+	/* power_1: Power Control 1 (C0h) */
+	u8 power_1;
+	/* power_2: Power Control 2 (C1h) */
+	u8 power_2;
+	/* vcom_1: VCOM Control 1(C5h) */
+	u8 vcom_1[ILI9341_VCOM_1_LEN];
+	/* vcom_2: VCOM Control 2(C7h) */
+	u8 vcom_2;
+	/* address_mode: Memory Access Control (36h) */
+	u8 address_mode;
+	/* g3amma_en: TODO: need comments for this register */
+	u8 g3amma_en;
+	/* rgb_interface: RGB Interface Signal Control (B0h) */
+	u8 rgb_interface;
+	/* dfc_2: refer to dfc_1 */
+	u8 dfc_2[ILI9341_DFC_2_LEN];
+	/* column_addr: Column Address Set (2Ah) */
+	u8 column_addr[ILI9341_COLUMN_ADDR_LEN];
+	/* page_addr: Page Address Set (2Bh) */
+	u8 page_addr[ILI9341_PAGE_ADDR_LEN];
+	/* interface: Interface Control (F6h) */
+	u8 interface[ILI9341_INTERFACE_LEN];
+	/*
+	 * pixel_format: This command sets the pixel format for the RGB
+	 * image data used by
+	 */
+	u8 pixel_format;
+	/*
+	 * gamma_curve: This command is used to select the desired Gamma
+	 * curve for the
+	 */
+	u8 gamma_curve;
+	/* pgamma: Positive Gamma Correction (E0h) */
+	u8 pgamma[ILI9341_PGAMMA_LEN];
+	/* ngamma: Negative Gamma Correction (E1h) */
+	u8 ngamma[ILI9341_NGAMMA_LEN];
+};
+
+struct ili9341 {
+	struct device_d *dev;
+	struct vpl vpl;
+	const struct ili9341_config *conf;
+	int reset_gpio;
+	int dc_gpio;
+	struct mipi_dbi *dbi;
+	u32 max_spi_speed;
+	struct regulator_bulk_data supplies[3];
+};
+
+/*
+ * The Stm32f429-disco board has a panel ili9341 connected to ltdc controller
+ */
+static const struct ili9341_config ili9341_stm32f429_disco_data = {
+	.max_spi_speed = 10000000,
+	.mode = {
+		.name = "240x320",
+		.xres = 240,
+		.yres = 320,
+		.pixclock = KHZ2PICOS(6100),
+		.left_margin = 10,
+		.hsync_len = 10,
+		.right_margin = 20,
+		.upper_margin = 4,
+		.lower_margin = 2,
+		.vsync_len = 2,
+	},
+	.ca = {0xc3, 0x08, 0x50},
+	.power_b = {0x00, 0xc1, 0x30},
+	.power_seq = {0x64, 0x03, 0x12, 0x81},
+	.dtca = {0x85, 0x00, 0x78},
+	.power_a = {0x39, 0x2c, 0x00, 0x34, 0x02},
+	.prc = 0x20,
+	.dtcb = {0x00, 0x00},
+	/* 0x00 fosc, 0x1b 70hz */
+	.frc = {0x00, 0x1b},
+	/*
+	 * 0x0a Interval scan, AGND AGND AGND AGND
+	 * 0xa2 Normally white, G1 -> G320, S720 -> S1,
+	 *	Scan Cycle 5 frames,85ms
+	 */
+	.dfc_1 = {0x0a, 0xa2},
+	/* 0x10 3.65v */
+	.power_1 = 0x10,
+	/* 0x10 AVDD=vci*2, VGH=vci*7, VGL=-vci*4 */
+	.power_2 = 0x10,
+	/* 0x45 VCOMH 4.425v, 0x15 VCOML -1.975*/
+	.vcom_1 = {0x45, 0x15},
+	/* 0x90 offset voltage, VMH-48, VML-48 */
+	.vcom_2 = 0x90,
+	/*
+	 * 0xc8 Row Address Order, Column Address Order
+	 * BGR 1
+	 */
+	.address_mode = 0xc8,
+	.g3amma_en = 0x00,
+	/*
+	 * 0xc2
+	 * Display Data Path: Memory
+	 * RGB: DE mode
+	 * DOTCLK polarity set (data fetched at the falling time)
+	 */
+	.rgb_interface = ILI9341_RGB_DISP_PATH_MEM |
+			ILI9341_RGB_DE_MODE |
+			ILI9341_RGB_DPL,
+	/*
+	 * 0x0a
+	 * Gate outputs in non-display area: Interval scan
+	 * Determine source/VCOM output in a non-display area in the partial
+	 * display mode: AGND AGND AGND AGND
+	 *
+	 * 0xa7
+	 * Scan Cycle: 15 frames
+	 * fFLM = 60Hz: 255ms
+	 * Liquid crystal type: Normally white
+	 * Gate Output Scan Direction: G1 -> G320
+	 * Source Output Scan Direction: S720 -> S1
+	 *
+	 * 0x27
+	 * LCD Driver Line: 320 lines
+	 *
+	 * 0x04
+	 * PCDIV: 4
+	 */
+	.dfc_2 = {0x0a, 0xa7, 0x27, 0x04},
+	/* column address: 240 */
+	.column_addr = {0x00, 0x00, (ILI9341_COLUMN_ADDR >> 4) & 0xff,
+				ILI9341_COLUMN_ADDR & 0xff},
+	/* page address: 320 */
+	.page_addr = {0x00, 0x00, (ILI9341_PAGE_ADDR >> 4) & 0xff,
+				ILI9341_PAGE_ADDR & 0xff},
+	/*
+	 * Memory write control: When the transfer number of data exceeds
+	 * (EC-SC+1)*(EP-SP+1), the column and page number will be
+	 * reset, and the exceeding data will be written into the following
+	 * column and page.
+	 * Display Operation Mode: RGB Interface Mode
+	 * Interface for RAM Access: RGB interface
+	 * 16- bit RGB interface (1 transfer/pixel)
+	 */
+	.interface = {ILI9341_IF_WE_MODE, 0x00,
+			ILI9341_IF_DM_RGB | ILI9341_IF_RM_RGB},
+	/* DPI: 16 bits / pixel */
+	.pixel_format = ILI9341_PIXEL_DPI_16_BITS,
+	/* Curve Selected: Gamma curve 1 (G2.2) */
+	.gamma_curve = ILI9341_GAMMA_CURVE_1,
+	.pgamma = {0x0f, 0x29, 0x24, 0x0c, 0x0e,
+			0x09, 0x4e, 0x78, 0x3c, 0x09,
+			0x13, 0x05, 0x17, 0x11, 0x00},
+	.ngamma = {0x00, 0x16, 0x1b, 0x04, 0x11,
+			0x07, 0x31, 0x33, 0x42, 0x05,
+			0x0c, 0x0a, 0x28, 0x2f, 0x0f},
+};
+
+static inline struct ili9341 *vpl_to_ili9341(struct vpl *vpl)
+{
+	return container_of(vpl, struct ili9341, vpl);
+}
+
+static void ili9341_dpi_init(struct ili9341 *ili)
+{
+	struct device_d *dev = ili->dev;
+	struct mipi_dbi *dbi = ili->dbi;
+	struct ili9341_config *cfg = (struct ili9341_config *)ili->conf;
+
+	/* Power Control */
+	mipi_dbi_command_stackbuf(dbi, 0xca, cfg->ca, ILI9341_CA_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_POWERB, cfg->power_b,
+				  ILI9341_POWER_B_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_POWER_SEQ, cfg->power_seq,
+				  ILI9341_POWER_SEQ_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DTCA, cfg->dtca,
+				  ILI9341_DTCA_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_POWERA, cfg->power_a,
+				  ILI9341_POWER_A_LEN);
+	mipi_dbi_command(ili->dbi, ILI9341_PRC, cfg->prc);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DTCB, cfg->dtcb,
+				  ILI9341_DTCB_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_FRC, cfg->frc, ILI9341_FRC_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_1,
+				  ILI9341_DFC_1_LEN);
+	mipi_dbi_command(dbi, ILI9341_POWER1, cfg->power_1);
+	mipi_dbi_command(dbi, ILI9341_POWER2, cfg->power_2);
+
+	/* VCOM */
+	mipi_dbi_command_stackbuf(dbi, ILI9341_VCOM1, cfg->vcom_1,
+				  ILI9341_VCOM_1_LEN);
+	mipi_dbi_command(dbi, ILI9341_VCOM2, cfg->vcom_2);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, cfg->address_mode);
+
+	/* Gamma */
+	mipi_dbi_command(dbi, ILI9341_3GAMMA_EN, cfg->g3amma_en);
+	mipi_dbi_command(dbi, ILI9341_RGB_INTERFACE, cfg->rgb_interface);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_DFC, cfg->dfc_2,
+				  ILI9341_DFC_2_LEN);
+
+	/* Colomn address set */
+	mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_COLUMN_ADDRESS,
+				  cfg->column_addr, ILI9341_COLUMN_ADDR_LEN);
+
+	/* Page address set */
+	mipi_dbi_command_stackbuf(dbi, MIPI_DCS_SET_PAGE_ADDRESS,
+				  cfg->page_addr, ILI9341_PAGE_ADDR_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_INTERFACE, cfg->interface,
+				  ILI9341_INTERFACE_LEN);
+
+	/* Format */
+	mipi_dbi_command(dbi, MIPI_DCS_SET_PIXEL_FORMAT, cfg->pixel_format);
+	mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START);
+	mdelay(200);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_GAMMA_CURVE, cfg->gamma_curve);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_PGAMMA, cfg->pgamma,
+				  ILI9341_PGAMMA_LEN);
+	mipi_dbi_command_stackbuf(dbi, ILI9341_NGAMMA, cfg->ngamma,
+				  ILI9341_NGAMMA_LEN);
+	mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE);
+	mdelay(200);
+	mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON);
+	mipi_dbi_command(dbi, MIPI_DCS_WRITE_MEMORY_START);
+
+	dev_info(dev, "Initialized display rgb interface\n");
+}
+
+static int ili9341_dpi_power_on(struct ili9341 *ili)
+{
+	struct device_d *dev = ili->dev;
+	int ret = 0;
+
+	/* Assert RESET */
+	gpiod_set_value(ili->reset_gpio, 1);
+
+	/* Enable power */
+	ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies),
+				    ili->supplies);
+	if (ret < 0) {
+		dev_err(dev, "unable to enable vcc\n");
+		return ret;
+	}
+	mdelay(20);
+
+	/* De-assert RESET */
+	gpiod_set_value(ili->reset_gpio, 0);
+	mdelay(20);
+
+	return 0;
+}
+
+static int ili9341_dpi_power_off(struct ili9341 *ili)
+{
+	/* Assert RESET */
+	gpiod_set_value(ili->reset_gpio, 1);
+
+	/* Disable power */
+	return regulator_bulk_disable(ARRAY_SIZE(ili->supplies),
+				      ili->supplies);
+}
+
+static void ili9341_dpi_disable(struct ili9341 *ili)
+{
+	mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_OFF);
+}
+
+static int ili9341_dpi_prepare(struct ili9341 *ili)
+{
+	int ret;
+
+	ret = ili9341_dpi_power_on(ili);
+	if (ret < 0)
+		return ret;
+
+	ili9341_dpi_init(ili);
+
+	return ret;
+}
+
+static void ili9341_dpi_enable(struct ili9341 *ili)
+{
+	mipi_dbi_command(ili->dbi, MIPI_DCS_SET_DISPLAY_ON);
+}
+
+static int ili9341_dpi_get_modes(struct ili9341 *ili,
+				 struct display_timings *timings)
+{
+	struct fb_videomode *mode;
+
+	mode = memdup(&ili->conf->mode, sizeof(*mode));
+	if (!mode)
+		return -ENOMEM;
+
+	/*
+	 * These are from the PoV of the display controller, so
+	 * DPL=1 => display samples at positive edge
+	 *       => controller drives at negative edge
+	 */
+	if (ili->conf->rgb_interface & ILI9341_RGB_DPL)
+		mode->display_flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+	else
+		mode->display_flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE;
+
+	if (ili->conf->rgb_interface & ILI9341_RGB_EPL)
+		mode->display_flags |= DISPLAY_FLAGS_DE_HIGH;
+	else
+		mode->display_flags |= DISPLAY_FLAGS_DE_LOW;
+
+	/* Set up the polarity */
+	if (ili->conf->rgb_interface & ILI9341_RGB_HSPL)
+		mode->sync |= FB_SYNC_HOR_HIGH_ACT;
+
+	if (ili->conf->rgb_interface & ILI9341_RGB_VSPL)
+		mode->sync |= FB_SYNC_VERT_HIGH_ACT;
+
+	timings->modes = mode;
+	timings->num_modes = 1;
+	return 0;
+}
+
+static int ili9341_ioctl(struct vpl *vpl, unsigned int port,
+			unsigned int cmd, void *ptr)
+{
+	struct ili9341 *ili = vpl_to_ili9341(vpl);
+
+	switch (cmd) {
+	case VPL_PREPARE:
+		return ili9341_dpi_prepare(ili);
+	case VPL_ENABLE:
+		ili9341_dpi_enable(ili);
+		return 0;
+	case VPL_DISABLE:
+		ili9341_dpi_disable(ili);
+		return 0;
+	case VPL_UNPREPARE:
+		return ili9341_dpi_power_off(ili);
+	case VPL_GET_VIDEOMODES:
+		return ili9341_dpi_get_modes(ili, ptr);
+	default:
+		return 0;
+	}
+}
+
+static int ili9341_dpi_probe(struct spi_device *spi, int dc, int reset)
+{
+	struct device_d *dev = &spi->dev;
+	struct ili9341 *ili;
+	int ret;
+
+	ili = kzalloc(sizeof(struct ili9341), GFP_KERNEL);
+	if (!ili)
+		return -ENOMEM;
+
+	ili->dbi = kzalloc(sizeof(struct mipi_dbi), GFP_KERNEL);
+	if (!ili->dbi)
+		return -ENOMEM;
+
+	ili->supplies[0].supply = "vci";
+	ili->supplies[1].supply = "vddi";
+	ili->supplies[2].supply = "vddi-led";
+	ret = regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies),
+				 ili->supplies);
+	if (ret < 0) {
+		dev_err(dev, "failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	ret = mipi_dbi_spi_init(spi, ili->dbi, dc);
+	if (ret)
+		return ret;
+
+	ili->reset_gpio = reset;
+	/*
+	 * Every new incarnation of this display must have a unique
+	 * data entry for the system in this driver.
+	 */
+	ili->conf = device_get_match_data(dev);
+	if (!ili->conf) {
+		dev_err(dev, "missing device configuration\n");
+		return -ENODEV;
+	}
+
+	ili->dev = dev;
+	ili->max_spi_speed = ili->conf->max_spi_speed;
+	ili->vpl.node = dev->device_node;
+	ili->vpl.ioctl = ili9341_ioctl;
+
+	return vpl_register(&ili->vpl);
+}
+
+static int ili9341_probe(struct device_d *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int dc, reset;
+
+	reset = gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (!gpio_is_valid(reset) && reset != -ENOENT)
+		dev_err(dev, "Failed to get gpio 'reset'\n");
+
+	dc = gpiod_get(dev, "dc", GPIOD_OUT_LOW);
+	if (!gpio_is_valid(dc) && dc != -ENOENT)
+		dev_err(dev, "Failed to get gpio 'dc'\n");
+
+	return ili9341_dpi_probe(spi, dc, reset);
+}
+
+static const struct of_device_id ili9341_of_match[] = {
+	{
+		.compatible = "st,sf-tc240t-9370-t",
+		.data = &ili9341_stm32f429_disco_data,
+	},
+	{ }
+};
+
+static struct driver_d ili9341_driver = {
+	.name = "panel-ilitek-ili9341",
+	.of_compatible = ili9341_of_match,
+	.probe = ili9341_probe,
+};
+device_spi_driver(ili9341_driver);
+
+MODULE_AUTHOR("Dillon Min <dillon.minfei@gmail.com>");
+MODULE_DESCRIPTION("ILI9341 LCD panel driver");
+MODULE_LICENSE("GPL v2");
-- 
2.30.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2022-02-03 16:39 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-02  9:55 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
2022-02-02  9:55 ` [PATCH 1/4] gpiolib: implement gpiod_set_value Ahmad Fatoum
2022-02-02  9:55 ` [PATCH 2/4] video: add driver for STM32 LCD-TFT Display Controller Ahmad Fatoum
2022-02-02  9:55 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum
2022-02-03 14:30   ` Thorsten Scherer
2022-02-03 14:34     ` Ahmad Fatoum
2022-02-03 14:34   ` Thorsten Scherer
2022-02-03 16:37     ` Ahmad Fatoum
2022-02-02  9:55 ` [PATCH 4/4] video: add Ilitek ILI9341 panel support Ahmad Fatoum
  -- strict thread matches above, loose matches on Subject: below --
2022-01-31  8:02 [PATCH 0/4] video: add STM32F429-DISCO video pipeline support Ahmad Fatoum
2022-01-31  8:03 ` [PATCH 4/4] video: add Ilitek ILI9341 panel support Ahmad Fatoum

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox