[PATCH v2] video: add ChipWealth CH13726A AMOLED panel driver

Svyatoslav Ryhel clamor95 at gmail.com
Tue Jul 7 10:14:03 CEST 2026


чт, 2 лип. 2026 р. о 18:14 Advait Dhamorikar <advaitd at mechasystems.com> пише:
>
> Add support for AMOLED panels based on the ChipWealth CH13726A
> MIPI DSI display driver IC.
>
> The driver configures the MIPI DSI interface for 4 data lanes in RGB888
> video mode, performs the panel reset sequence, sends the vendor-specific
> initialization commands required by the controller, and enables the
> panel using the standard MIPI DCS Sleep Out and Display On commands.
>
> This driver is based on an earlier implementation by Pratham Malaviya.
>
> Signed-off-by: Advait Dhamorikar <advaitd at mechasystems.com>
> ---
>
> v1->v2
> - Expand CC list based on get_maintainer.pl output.
>
>  drivers/video/Kconfig               |   8 ++
>  drivers/video/Makefile              |   1 +
>  drivers/video/chipwealth-ch13726a.c | 193 ++++++++++++++++++++++++++++
>  3 files changed, 202 insertions(+)
>  create mode 100644 drivers/video/chipwealth-ch13726a.c
>

Hey there! Thanks for your contribution, it is pretty solid but there
are a few places that can be improved.

> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index c2acc13139c..7e82778e294 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -533,6 +533,14 @@ config VIDEO_BCM2835
>           that same resolution (or as near as possible) and 32bpp depth, so
>           that U-Boot can access it with full colour depth.
>
> +config VIDEO_AMOLED_CHIPWEALTH_CH13726A

May you please rename AMOLED > PANEL

> +       bool "ChipWealth CH13726A AMOLED panel support"
> +       depends on VIDEO

"depends on PANEL && BACKLIGHT"

> +       select VIDEO_MIPI_DSI
> +       help
> +         Say Y here if you want to enable support for AMOLED panels
> +         based on the ChipWealth CH13726A MIPI DSI display driver IC.
> +
>  config VIDEO_LCD_ENDEAVORU
>         tristate "Endeavoru 720x1280 DSI video mode panel"
>         depends on PANEL && BACKLIGHT
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 082b8967982..56df5e1860d 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -55,6 +55,7 @@ obj-$(CONFIG_VIDEO_DW_HDMI) += dw_hdmi.o
>  obj-$(CONFIG_VIDEO_DW_MIPI_DSI) += dw_mipi_dsi.o
>  obj-$(CONFIG_VIDEO_EFI) += efi.o
>  obj-y += imx/
> +obj-$(CONFIG_VIDEO_AMOLED_CHIPWEALTH_CH13726A) += chipwealth-ch13726a.o
>  obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
>  obj-$(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804) += anx9804.o
>  obj-$(CONFIG_VIDEO_LCD_ENDEAVORU) += endeavoru-panel.o
> diff --git a/drivers/video/chipwealth-ch13726a.c b/drivers/video/chipwealth-ch13726a.c
> new file mode 100644
> index 00000000000..e6fef6c8e7f
> --- /dev/null
> +++ b/drivers/video/chipwealth-ch13726a.c
> @@ -0,0 +1,193 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * ChipWealth CH13726A DSI panel driver
> + *
> + * Copyright (C) 2026 Mecha Systems
> + * Author(s):
> + *   Advait Dhamorikar <advaitd at mechasystems.com>
> + *   Pratham Malaviya  <prathamm at mechasystems.com>
> + */
> +
> +#include <backlight.h>
> +#include <dm.h>
> +#include <mipi_dsi.h>
> +#include <panel.h>
> +#include <asm/gpio.h>
> +#include <linux/delay.h>
> +#include <log.h>
> +
> +struct ch13726a_panel_priv {
> +       struct udevice *reg;
> +       struct udevice *backlight;
> +       struct gpio_desc reset;
> +};

You are lacking supplies here: vdd1v2, vddio, vdd and avdd

U-Boot can handle them so you should include control over them.

> +
> +static const struct display_timing default_timing = {
> +       .pixelclock.typ                 =       59832000,
> +       .hactive.typ                    =       1080,
> +       .hfront_porch.typ               =       12,
> +       .hback_porch.typ                =       12,
> +       .hsync_len.typ                  =       28,
> +       .vactive.typ                    =       1240,
> +       .vfront_porch.typ               =       2,
> +       .vback_porch.typ                =       2,
> +       .vsync_len.typ                  =       2,
> +};
> +
> +static int ch13726a_dcs_write_buf(struct udevice *dev, const void *data,
> +                                 size_t len)
> +{
> +       struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
> +       int ret;
> +
> +       ret = mipi_dsi_dcs_write_buffer(plat->device, data, len);
> +       if (ret < 0)
> +               log_err("failed to write dcs sequence: %d\n", ret);
> +
> +       return ret;
> +}
> +
> +#define dcs_write_seq(dev, cmd, seq...)                                \
> +({                                                             \
> +       const u8 d[] = { cmd, seq };                    \
> +                                                               \
> +       ch13726a_dcs_write_buf(dev, d, ARRAY_SIZE(d));          \
> +})
> +

You can fold this.

> +static void ch13726a_reset(struct udevice *dev)
> +{
> +       struct ch13726a_panel_priv *priv = dev_get_priv(dev);
> +
> +       dm_gpio_set_value(&priv->reset, true);
> +       mdelay(10);
> +       dm_gpio_set_value(&priv->reset, false);
> +       mdelay(10);
> +       dm_gpio_set_value(&priv->reset, true);
> +       mdelay(5);
> +}
> +
> +static int ch13726a_set_backlight(struct udevice *dev, int percent)
> +{
> +       struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
> +       struct mipi_dsi_device *device = plat->device;
> +       u8 brightness;
> +
> +       if (percent < 0)
> +               percent = 0;
> +       else if (percent > 100)
> +               percent = 100;
> +
> +       brightness = (percent * 255) / 100;
> +
> +       return mipi_dsi_dcs_set_display_brightness(device, brightness);
> +}
> +
> +static int ch13726a_panel_enable_backlight(struct udevice *dev)
> +{
> +       struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
> +       struct mipi_dsi_device *device = plat->device;
> +       int ret;
> +
> +       device->lanes = plat->lanes;
> +       device->format = plat->format;
> +       device->mode_flags = plat->mode_flags;
> +

You don't need this assignment.

> +       ret = mipi_dsi_attach(device);
> +       if (ret < 0)
> +               return ret;
> +
> +       ch13726a_reset(dev);
> +
> +       device->mode_flags |= MIPI_DSI_MODE_LPM;
> +

LPM flag is removed by mipi_dsi_device_transfer so you don't need it here.

> +       ret = dcs_write_seq(dev, 0x53, 0x00);
> +       if (ret < 0) {
> +               log_debug("%s: Init command failed(%d)\n",
> +                         __func__, ret);
> +               return ret;
> +       }
> +
> +       ret = dcs_write_seq(dev, 0x17, 0x00);
> +       if (ret < 0) {
> +               log_debug("%s: Init command failed (%d)\n",
> +                         __func__, ret);
> +               return ret;
> +       }
> +
> +       ret = dcs_write_seq(dev, 0x41, 0x00);
> +       if (ret < 0) {
> +               log_debug("%s: Init command failed (%d)\n",
> +                         __func__, ret);
> +               return ret;
> +       }
> +
> +       ret = mipi_dsi_dcs_exit_sleep_mode(device);
> +       if (ret)
> +               return ret;
> +
> +       mdelay(120);
> +
> +       ret = mipi_dsi_dcs_set_display_on(device);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static int ch13726a_panel_get_display_timing(struct udevice *dev,
> +                                            struct display_timing *timings)
> +{
> +       memcpy(timings, &default_timing, sizeof(*timings));
> +       return 0;
> +}
> +
> +static int ch13726a_panel_of_to_plat(struct udevice *dev)
> +{
> +       struct ch13726a_panel_priv *priv = dev_get_priv(dev);
> +       int ret;
> +
> +       ret = gpio_request_by_name(dev, "reset-gpios", 0,
> +                                  &priv->reset,
> +                                  GPIOD_IS_OUT);
> +       if (ret) {
> +               log_err("%s: cannot get reset GPIO (%d)\n",
> +                       __func__, ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int ch13726a_panel_probe(struct udevice *dev)
> +{
> +       struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
> +
> +       plat->lanes = 4;
> +       plat->format = MIPI_DSI_FMT_RGB888;
> +       plat->mode_flags = MIPI_DSI_MODE_VIDEO |
> +                          MIPI_DSI_MODE_VIDEO_BURST;
> +
> +       return 0;
> +}
> +
> +static const struct panel_ops ch13726a_panel_ops = {
> +       .enable_backlight       =       ch13726a_panel_enable_backlight,
> +       .set_backlight          =       ch13726a_set_backlight,
> +       .get_display_timing     =       ch13726a_panel_get_display_timing,
> +};
> +
> +static const struct udevice_id ch13726a_panel_ids[] = {
> +       { .compatible   =       "chipwealth,ch13726a" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(ch13726a_panel) = {
> +       .name           =       "ch13726a_panel",
> +       .id                     =       UCLASS_PANEL,
> +       .of_match       =       ch13726a_panel_ids,
> +       .ops            =       &ch13726a_panel_ops,
> +       .of_to_plat     =       ch13726a_panel_of_to_plat,
> +       .probe          =       ch13726a_panel_probe,
> +       .plat_auto      =       sizeof(struct mipi_dsi_panel_plat),
> +       .priv_auto      =       sizeof(struct ch13726a_panel_priv),
> +};
> --
> 2.43.0
>


More information about the U-Boot mailing list