* [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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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 = <dc_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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ messages in thread
* [PATCH 0/4] video: add STM32F429-DISCO video pipeline support @ 2022-01-31 8:02 Ahmad Fatoum 2022-01-31 8:03 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum 0 siblings, 1 reply; 12+ messages in thread From: Ahmad Fatoum @ 2022-01-31 8:02 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. 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 | 19 + commands/Makefile | 1 + commands/mipi_dbi.c | 104 +++++ 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 + include/video/mipi_dbi.h | 105 ++++++ include/video/mipi_display.h | 150 ++++++++ 13 files changed, 1905 insertions(+), 1 deletion(-) create mode 100644 commands/mipi_dbi.c 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 create mode 100644 include/video/mipi_dbi.h create mode 100644 include/video/mipi_display.h -- 2.30.2 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 3/4] video: add MIPI DBI Type C Option 3 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 2022-01-31 10:21 ` Sascha Hauer 0 siblings, 1 reply; 12+ messages in thread From: Ahmad Fatoum @ 2022-01-31 8:03 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 | 19 ++ commands/Makefile | 1 + commands/mipi_dbi.c | 104 ++++++++ drivers/video/Kconfig | 14 ++ drivers/video/Makefile | 1 + drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++ include/spi/spi.h | 20 ++ include/video/mipi_dbi.h | 105 ++++++++ include/video/mipi_display.h | 150 +++++++++++ 9 files changed, 881 insertions(+) create mode 100644 commands/mipi_dbi.c create mode 100644 drivers/video/mipi_dbi.c create mode 100644 include/video/mipi_dbi.h create mode 100644 include/video/mipi_display.h diff --git a/commands/Kconfig b/commands/Kconfig index ba8ca5cdebce..2e13a4ed90bd 100644 --- a/commands/Kconfig +++ b/commands/Kconfig @@ -1969,6 +1969,25 @@ 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 device on SPI bus + + Usage: mipi_dbi [-brcmfwv] DATA... + + Options: + -b BUS SPI bus number (default 0) + -r COUNT bytes to read + -c chip select (default 0) + -m MODE SPI mode (default 0) + -f HZ max speed frequency, in Hz (default 1 MHz) + -w BIT bits per word (default 8) + -v verbose + 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/commands/mipi_dbi.c b/commands/mipi_dbi.c new file mode 100644 index 000000000000..b9b665b72151 --- /dev/null +++ b/commands/mipi_dbi.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +// SPDX-FileCopyrightText: © 2022 Ahmad Fatoum + +#include <common.h> +#include <command.h> +#include <getopt.h> +#include <video/mipi_dbi.h> +#include <video/mipi_display.h> + +static int mipi_dbi_command_show(struct mipi_dbi *dbi, int cmd) +{ + u8 val[4]; + int ret; + size_t len; + + if (!mipi_dbi_command_is_read(dbi, cmd)) + return -EACCES; + + len = mipi_dbi_command_read_len(cmd); + + printf("%02x: ", cmd); + ret = mipi_dbi_command_buf(dbi, cmd, val, len); + if (ret) { + printf("XX\n"); + return ret; + } + printf("%*phN\n", (int)len, val); + + return 0; +} + +static int do_mipi_dbi(int argc, char *argv[]) +{ + struct mipi_dbi *dbi; + int opt, ret, i; + bool write = false; + u8 cmd, val[4]; + + dbi = list_first_entry_or_null(&mipi_dbi_list, struct mipi_dbi, list); + + while ((opt = getopt(argc, argv, "wld:")) > 0) { + struct mipi_dbi *tmp; + + switch (opt) { + case 'w': + write = true; + break; + case 'l': + list_for_each_entry(tmp, &mipi_dbi_list, list) + printf("%s\n", mipi_dbi_name(tmp)); + return 0; + case 'd': + dbi = NULL; + list_for_each_entry(tmp, &mipi_dbi_list, list) { + if (!strcmp(optarg, mipi_dbi_name(tmp))) { + dbi = tmp; + break; + } + } + break; + default: + return COMMAND_ERROR_USAGE; + } + } + + if (!dbi) + return -ENODEV; + + if (optind == argc) { + for (cmd = 0; cmd < 255; cmd++) + mipi_dbi_command_show(dbi, cmd); + return 0; + } + + ret = kstrtou8(argv[optind++], 16, &cmd); + if (ret < 0) + return ret; + + if (optind == argc && !write) + return mipi_dbi_command_show(dbi, cmd); + + for (i = optind; i < argc; i++) { + ret = kstrtou8(argv[optind + i], 16, &val[i]); + if (ret < 0) + return ret; + } + + return mipi_dbi_command_buf(dbi, cmd, val, argc - optind); +} + +BAREBOX_CMD_HELP_START(mipi_dbi) +BAREBOX_CMD_HELP_TEXT("Options:") +BAREBOX_CMD_HELP_OPT ("-l\t", "list all MIPI DBI devices") +BAREBOX_CMD_HELP_OPT ("-d DEVICE", "select specific device (default is first registered)") +BAREBOX_CMD_HELP_OPT ("-w", "issue write command") +BAREBOX_CMD_HELP_END + +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 diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1b8672fdea82..dcdc6c213591 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 @@ -178,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 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) { diff --git a/include/video/mipi_dbi.h b/include/video/mipi_dbi.h new file mode 100644 index 000000000000..92fdc500d1ba --- /dev/null +++ b/include/video/mipi_dbi.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * MIPI Display Bus Interface (DBI) LCD controller support + * + * Copyright 2016 Noralf Trønnes + */ + +#ifndef __LINUX_MIPI_DBI_H +#define __LINUX_MIPI_DBI_H + +#include <linux/types.h> +#include <spi/spi.h> +#include <driver.h> + +struct regulator; +struct fb_videomode; + +/** + * struct mipi_dbi - MIPI DBI interface + */ +struct mipi_dbi { + /** + * @command: Bus specific callback executing commands. + */ + int (*command)(struct mipi_dbi *dbi, u8 *cmd, u8 *param, size_t num); + + /** + * @read_commands: Array of read commands terminated by a zero entry. + * Reading is disabled if this is NULL. + */ + const u8 *read_commands; + + /** + * @swap_bytes: Swap bytes in buffer before transfer + */ + bool swap_bytes; + + /** + * @reset: Optional reset gpio + */ + int reset; + + /* Type C specific */ + + /** + * @spi: SPI device + */ + struct spi_device *spi; + + /** + * @dc: Optional D/C gpio. + */ + int dc; + + struct list_head list; +}; + +static inline const char *mipi_dbi_name(struct mipi_dbi *dbi) +{ + return dev_name(&dbi->spi->dev); +} + +int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi, + int dc); +void mipi_dbi_hw_reset(struct mipi_dbi *dbi); +bool mipi_dbi_display_is_on(struct mipi_dbi *dbi); + +u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len); +int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz, + u8 bpw, const void *buf, size_t len); + +int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val); +int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len); +int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, + size_t len); + +/** + * mipi_dbi_command - MIPI DCS command with optional parameter(s) + * @dbi: MIPI DBI structure + * @cmd: Command + * @seq: Optional parameter(s) + * + * Send MIPI DCS command to the controller. Use mipi_dbi_command_read() for + * get/read. + * + * Returns: + * Zero on success, negative error code on failure. + */ +#define mipi_dbi_command(dbi, cmd, seq...) \ +({ \ + const u8 d[] = { seq }; \ + struct device_d *dev = &(dbi)->spi->dev; \ + int ret; \ + ret = mipi_dbi_command_stackbuf(dbi, cmd, d, ARRAY_SIZE(d)); \ + if (ret) \ + dev_err(dev, "error %pe when sending command %#02x\n", ERR_PTR(ret), cmd); \ + ret; \ +}) + +bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd); +int mipi_dbi_command_read_len(int cmd); + +extern struct list_head mipi_dbi_list; + +#endif /* __LINUX_MIPI_DBI_H */ diff --git a/include/video/mipi_display.h b/include/video/mipi_display.h new file mode 100644 index 000000000000..b6d8b874233f --- /dev/null +++ b/include/video/mipi_display.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Defines for Mobile Industry Processor Interface (MIPI(R)) + * Display Working Group standards: DSI, DCS, DBI, DPI + * + * Copyright (C) 2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * Copyright (C) 2006 Nokia Corporation + * Author: Imre Deak <imre.deak@nokia.com> + */ +#ifndef MIPI_DISPLAY_H +#define MIPI_DISPLAY_H + +/* MIPI DSI Processor-to-Peripheral transaction types */ +enum { + MIPI_DSI_V_SYNC_START = 0x01, + MIPI_DSI_V_SYNC_END = 0x11, + MIPI_DSI_H_SYNC_START = 0x21, + MIPI_DSI_H_SYNC_END = 0x31, + + MIPI_DSI_COMPRESSION_MODE = 0x07, + MIPI_DSI_END_OF_TRANSMISSION = 0x08, + + MIPI_DSI_COLOR_MODE_OFF = 0x02, + MIPI_DSI_COLOR_MODE_ON = 0x12, + MIPI_DSI_SHUTDOWN_PERIPHERAL = 0x22, + MIPI_DSI_TURN_ON_PERIPHERAL = 0x32, + + MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM = 0x03, + MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM = 0x13, + MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM = 0x23, + + MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM = 0x04, + MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM = 0x14, + MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM = 0x24, + + MIPI_DSI_DCS_SHORT_WRITE = 0x05, + MIPI_DSI_DCS_SHORT_WRITE_PARAM = 0x15, + + MIPI_DSI_DCS_READ = 0x06, + MIPI_DSI_EXECUTE_QUEUE = 0x16, + + MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE = 0x37, + + MIPI_DSI_NULL_PACKET = 0x09, + MIPI_DSI_BLANKING_PACKET = 0x19, + MIPI_DSI_GENERIC_LONG_WRITE = 0x29, + MIPI_DSI_DCS_LONG_WRITE = 0x39, + + MIPI_DSI_PICTURE_PARAMETER_SET = 0x0a, + MIPI_DSI_COMPRESSED_PIXEL_STREAM = 0x0b, + + MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20 = 0x0c, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24 = 0x1c, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16 = 0x2c, + + MIPI_DSI_PACKED_PIXEL_STREAM_30 = 0x0d, + MIPI_DSI_PACKED_PIXEL_STREAM_36 = 0x1d, + MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12 = 0x3d, + + MIPI_DSI_PACKED_PIXEL_STREAM_16 = 0x0e, + MIPI_DSI_PACKED_PIXEL_STREAM_18 = 0x1e, + MIPI_DSI_PIXEL_STREAM_3BYTE_18 = 0x2e, + MIPI_DSI_PACKED_PIXEL_STREAM_24 = 0x3e, +}; + +/* MIPI DSI Peripheral-to-Processor transaction types */ +enum { + MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT = 0x02, + MIPI_DSI_RX_END_OF_TRANSMISSION = 0x08, + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE = 0x11, + MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE = 0x12, + MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE = 0x1a, + MIPI_DSI_RX_DCS_LONG_READ_RESPONSE = 0x1c, + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE = 0x21, + MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE = 0x22, +}; + +/* MIPI DCS commands */ +enum { + MIPI_DCS_NOP = 0x00, + MIPI_DCS_SOFT_RESET = 0x01, + MIPI_DCS_GET_COMPRESSION_MODE = 0x03, + MIPI_DCS_GET_DISPLAY_ID = 0x04, + MIPI_DCS_GET_ERROR_COUNT_ON_DSI = 0x05, + MIPI_DCS_GET_RED_CHANNEL = 0x06, + MIPI_DCS_GET_GREEN_CHANNEL = 0x07, + MIPI_DCS_GET_BLUE_CHANNEL = 0x08, + MIPI_DCS_GET_DISPLAY_STATUS = 0x09, + MIPI_DCS_GET_POWER_MODE = 0x0A, + MIPI_DCS_GET_ADDRESS_MODE = 0x0B, + MIPI_DCS_GET_PIXEL_FORMAT = 0x0C, + MIPI_DCS_GET_DISPLAY_MODE = 0x0D, + MIPI_DCS_GET_SIGNAL_MODE = 0x0E, + MIPI_DCS_GET_DIAGNOSTIC_RESULT = 0x0F, + MIPI_DCS_ENTER_SLEEP_MODE = 0x10, + MIPI_DCS_EXIT_SLEEP_MODE = 0x11, + MIPI_DCS_ENTER_PARTIAL_MODE = 0x12, + MIPI_DCS_ENTER_NORMAL_MODE = 0x13, + MIPI_DCS_GET_IMAGE_CHECKSUM_RGB = 0x14, + MIPI_DCS_GET_IMAGE_CHECKSUM_CT = 0x15, + MIPI_DCS_EXIT_INVERT_MODE = 0x20, + MIPI_DCS_ENTER_INVERT_MODE = 0x21, + MIPI_DCS_SET_GAMMA_CURVE = 0x26, + MIPI_DCS_SET_DISPLAY_OFF = 0x28, + MIPI_DCS_SET_DISPLAY_ON = 0x29, + MIPI_DCS_SET_COLUMN_ADDRESS = 0x2A, + MIPI_DCS_SET_PAGE_ADDRESS = 0x2B, + MIPI_DCS_WRITE_MEMORY_START = 0x2C, + MIPI_DCS_WRITE_LUT = 0x2D, + MIPI_DCS_READ_MEMORY_START = 0x2E, + MIPI_DCS_SET_PARTIAL_ROWS = 0x30, /* MIPI DCS 1.02 - MIPI_DCS_SET_PARTIAL_AREA before that */ + MIPI_DCS_SET_PARTIAL_COLUMNS = 0x31, + MIPI_DCS_SET_SCROLL_AREA = 0x33, + MIPI_DCS_SET_TEAR_OFF = 0x34, + MIPI_DCS_SET_TEAR_ON = 0x35, + MIPI_DCS_SET_ADDRESS_MODE = 0x36, + MIPI_DCS_SET_SCROLL_START = 0x37, + MIPI_DCS_EXIT_IDLE_MODE = 0x38, + MIPI_DCS_ENTER_IDLE_MODE = 0x39, + MIPI_DCS_SET_PIXEL_FORMAT = 0x3A, + MIPI_DCS_WRITE_MEMORY_CONTINUE = 0x3C, + MIPI_DCS_SET_3D_CONTROL = 0x3D, + MIPI_DCS_READ_MEMORY_CONTINUE = 0x3E, + MIPI_DCS_GET_3D_CONTROL = 0x3F, + MIPI_DCS_SET_VSYNC_TIMING = 0x40, + MIPI_DCS_SET_TEAR_SCANLINE = 0x44, + MIPI_DCS_GET_SCANLINE = 0x45, + MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 0x51, /* MIPI DCS 1.3 */ + MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 0x52, /* MIPI DCS 1.3 */ + MIPI_DCS_WRITE_CONTROL_DISPLAY = 0x53, /* MIPI DCS 1.3 */ + MIPI_DCS_GET_CONTROL_DISPLAY = 0x54, /* MIPI DCS 1.3 */ + MIPI_DCS_WRITE_POWER_SAVE = 0x55, /* MIPI DCS 1.3 */ + MIPI_DCS_GET_POWER_SAVE = 0x56, /* MIPI DCS 1.3 */ + MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 0x5E, /* MIPI DCS 1.3 */ + MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 0x5F, /* MIPI DCS 1.3 */ + MIPI_DCS_READ_DDB_START = 0xA1, + MIPI_DCS_READ_PPS_START = 0xA2, + MIPI_DCS_READ_DDB_CONTINUE = 0xA8, + MIPI_DCS_READ_PPS_CONTINUE = 0xA9, +}; + +/* MIPI DCS pixel formats */ +#define MIPI_DCS_PIXEL_FMT_24BIT 7 +#define MIPI_DCS_PIXEL_FMT_18BIT 6 +#define MIPI_DCS_PIXEL_FMT_16BIT 5 +#define MIPI_DCS_PIXEL_FMT_12BIT 3 +#define MIPI_DCS_PIXEL_FMT_8BIT 2 +#define MIPI_DCS_PIXEL_FMT_3BIT 1 + +#endif -- 2.30.2 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox ^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/4] video: add MIPI DBI Type C Option 3 support 2022-01-31 8:03 ` [PATCH 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum @ 2022-01-31 10:21 ` Sascha Hauer 2022-01-31 10:28 ` Ahmad Fatoum 0 siblings, 1 reply; 12+ messages in thread From: Sascha Hauer @ 2022-01-31 10:21 UTC (permalink / raw) To: Ahmad Fatoum; +Cc: barebox On Mon, Jan 31, 2022 at 09:03:01AM +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 | 19 ++ > commands/Makefile | 1 + > commands/mipi_dbi.c | 104 ++++++++ > drivers/video/Kconfig | 14 ++ > drivers/video/Makefile | 1 + > drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++ > include/spi/spi.h | 20 ++ > include/video/mipi_dbi.h | 105 ++++++++ > include/video/mipi_display.h | 150 +++++++++++ > 9 files changed, 881 insertions(+) > create mode 100644 commands/mipi_dbi.c > create mode 100644 drivers/video/mipi_dbi.c > create mode 100644 include/video/mipi_dbi.h > create mode 100644 include/video/mipi_display.h > > diff --git a/commands/Kconfig b/commands/Kconfig > index ba8ca5cdebce..2e13a4ed90bd 100644 > --- a/commands/Kconfig > +++ b/commands/Kconfig > @@ -1969,6 +1969,25 @@ 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 device on SPI bus > + > + Usage: mipi_dbi [-brcmfwv] DATA... > + > + Options: > + -b BUS SPI bus number (default 0) > + -r COUNT bytes to read > + -c chip select (default 0) > + -m MODE SPI mode (default 0) > + -f HZ max speed frequency, in Hz (default 1 MHz) > + -w BIT bits per word (default 8) > + -v verbose This doesn't match the actual command. Copy-paste from CMD_SPI above? > +BAREBOX_CMD_HELP_START(mipi_dbi) > +BAREBOX_CMD_HELP_TEXT("Options:") > +BAREBOX_CMD_HELP_OPT ("-l\t", "list all MIPI DBI devices") > +BAREBOX_CMD_HELP_OPT ("-d DEVICE", "select specific device (default is first registered)") > +BAREBOX_CMD_HELP_OPT ("-w", "issue write command") > +BAREBOX_CMD_HELP_END > + > + > config DRIVER_VIDEO_BACKLIGHT > bool "Add backlight support" > help > @@ -178,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. This hunk should be in 4/4. Sascha -- 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] 12+ messages in thread
* Re: [PATCH 3/4] video: add MIPI DBI Type C Option 3 support 2022-01-31 10:21 ` Sascha Hauer @ 2022-01-31 10:28 ` Ahmad Fatoum 0 siblings, 0 replies; 12+ messages in thread From: Ahmad Fatoum @ 2022-01-31 10:28 UTC (permalink / raw) To: Sascha Hauer; +Cc: barebox On 31.01.22 11:21, Sascha Hauer wrote: > On Mon, Jan 31, 2022 at 09:03:01AM +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 | 19 ++ >> commands/Makefile | 1 + >> commands/mipi_dbi.c | 104 ++++++++ >> drivers/video/Kconfig | 14 ++ >> drivers/video/Makefile | 1 + >> drivers/video/mipi_dbi.c | 467 +++++++++++++++++++++++++++++++++++ >> include/spi/spi.h | 20 ++ >> include/video/mipi_dbi.h | 105 ++++++++ >> include/video/mipi_display.h | 150 +++++++++++ >> 9 files changed, 881 insertions(+) >> create mode 100644 commands/mipi_dbi.c >> create mode 100644 drivers/video/mipi_dbi.c >> create mode 100644 include/video/mipi_dbi.h >> create mode 100644 include/video/mipi_display.h >> >> diff --git a/commands/Kconfig b/commands/Kconfig >> index ba8ca5cdebce..2e13a4ed90bd 100644 >> --- a/commands/Kconfig >> +++ b/commands/Kconfig >> @@ -1969,6 +1969,25 @@ 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 device on SPI bus >> + >> + Usage: mipi_dbi [-brcmfwv] DATA... >> + >> + Options: >> + -b BUS SPI bus number (default 0) >> + -r COUNT bytes to read >> + -c chip select (default 0) >> + -m MODE SPI mode (default 0) >> + -f HZ max speed frequency, in Hz (default 1 MHz) >> + -w BIT bits per word (default 8) >> + -v verbose > > This doesn't match the actual command. Copy-paste from CMD_SPI above? > >> +BAREBOX_CMD_HELP_START(mipi_dbi) >> +BAREBOX_CMD_HELP_TEXT("Options:") >> +BAREBOX_CMD_HELP_OPT ("-l\t", "list all MIPI DBI devices") >> +BAREBOX_CMD_HELP_OPT ("-d DEVICE", "select specific device (default is first registered)") >> +BAREBOX_CMD_HELP_OPT ("-w", "issue write command") >> +BAREBOX_CMD_HELP_END >> + >> + >> config DRIVER_VIDEO_BACKLIGHT >> bool "Add backlight support" >> help >> @@ -178,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. > > This hunk should be in 4/4. Ouch. Will fix both for v2. Thanks, Ahmad > > Sascha > -- 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] 12+ messages in thread
end of thread, other threads:[~2022-02-03 16:39 UTC | newest] Thread overview: 12+ 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 3/4] video: add MIPI DBI Type C Option 3 support Ahmad Fatoum 2022-01-31 10:21 ` Sascha Hauer 2022-01-31 10:28 ` Ahmad Fatoum
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox