чт, 16 лип. 2026 р. о 14:03 Advait Dhamorikar <[email protected]> пише:
>
> 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 <[email protected]>
> ---
>
> v1->v2
> - Expand CC list based on get_maintainer.pl output.
>
> v2 -> v3
> Link to v2: 
> https://lore.kernel.org/all/[email protected]/
> - Add regulator support using the U-Boot regulator framework.
> - Minor code cleanup.
>
>  drivers/video/Kconfig               |   8 +
>  drivers/video/Makefile              |   1 +
>  drivers/video/chipwealth-ch13726a.c | 221 ++++++++++++++++++++++++++++
>  3 files changed, 230 insertions(+)
>  create mode 100644 drivers/video/chipwealth-ch13726a.c
>
> diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
> index c2acc13139c..a214626f376 100644
> --- a/drivers/video/Kconfig
> +++ b/drivers/video/Kconfig
> @@ -762,6 +762,14 @@ config VIDEO_LCD_SPI_MISO
>         option takes a string in the format understood by 'sunxi_name_to_gpio'
>         function, e.g. PH1 for pin 1 of port H.
>
> +config VIDEO_PANEL_CHIPWEALTH_CH13726A
> +       bool "ChipWealth CH13726A AMOLED panel support"
> +       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.
> +
>  source "drivers/video/meson/Kconfig"
>
>  config VIDEO_MVEBU
> diff --git a/drivers/video/Makefile b/drivers/video/Makefile
> index 082b8967982..efa7e288691 100644
> --- a/drivers/video/Makefile
> +++ b/drivers/video/Makefile
> @@ -77,6 +77,7 @@ obj-$(CONFIG_VIDEO_LCD_SSD2828) += ssd2828.o
>  obj-$(CONFIG_VIDEO_LCD_TDO_TL070WSH30) += tdo-tl070wsh30.o
>  obj-$(CONFIG_VIDEO_LCD_SONY_L4F00430T01) += sony-l4f00430t01.o
>  obj-$(CONFIG_VIDEO_LCD_SAMSUNG_S6E63M0) += samsung-s6e63m0.o
> +obj-$(CONFIG_VIDEO_PANEL_CHIPWEALTH_CH13726A) += chipwealth-ch13726a.o
>  obj-$(CONFIG_VIDEO_MCDE_SIMPLE) += mcde_simple.o
>  obj-${CONFIG_VIDEO_MESON} += meson/
>  obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o
> diff --git a/drivers/video/chipwealth-ch13726a.c 
> b/drivers/video/chipwealth-ch13726a.c
> new file mode 100644
> index 00000000000..18f1dbc2e78
> --- /dev/null
> +++ b/drivers/video/chipwealth-ch13726a.c
> @@ -0,0 +1,221 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * ChipWealth CH13726A DSI panel driver
> + *
> + * Copyright (C) 2026 Mecha Systems
> + * Author(s):
> + *   Advait Dhamorikar <[email protected]>
> + *   Pratham Malaviya  <[email protected]>
> + */
> +
> +#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 *vdd;
> +       struct udevice *vddio;
> +       struct udevice *vdd1v2;
> +       struct udevice *avdd;
> +       struct udevice *reg;

I must have missed this previously, sorry. The reg udevice pointer
seems not to be used anywhere.
Else seems fine, with this fixed.

Reviewed-by: Svyatoslav Ryhel <[email protected]>

> +       struct udevice *backlight;
> +       struct gpio_desc reset;
> +};
> +
> +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,
> +};
> +
> +#define dcs_write_seq(dev, cmd, seq...)                                \
> +({                                                             \
> +       struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);   \
> +       const u8 d[] = { cmd, seq };                            \
> +       int ret;                                                \
> +                                                               \
> +       ret = mipi_dsi_dcs_write_buffer(plat->device,           \
> +                                       d, ARRAY_SIZE(d));      \
> +       if (ret < 0)                                            \
> +               log_err("failed to write dcs sequence: %d\n", ret);\
> +       ret;                                                    \
> +})
> +
> +static int ch13726a_hw_init(struct udevice *dev)
> +{
> +       int ret;
> +
> +       ret = dcs_write_seq(dev, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x00);
> +       if (ret < 0) {
> +               log_debug("%s: Init command failed (%d)\n",
> +                         __func__, ret);
> +               return ret;
> +       }
> +
> +       ret = dcs_write_seq(dev, MIPI_DSI_V_SYNC_END, 0x00);
> +       if (ret < 0) {
> +               log_debug("%s: Init command failed (%d)\n",
> +                         __func__, ret);
> +               return ret;
> +       }
> +
> +       mdelay(120);
> +
> +       ret = dcs_write_seq(dev, MIPI_DSI_GENERIC_LONG_WRITE, 0x00);
> +       if (ret < 0) {
> +               log_debug("%s: Init command failed (%d)\n",
> +                         __func__, ret);
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +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;
> +
> +       ret = mipi_dsi_attach(device);
> +       if (ret < 0)
> +               return ret;
> +
> +       ch13726a_reset(dev);
> +
> +       ret = ch13726a_hw_init(dev);
> +       if (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;
> +
> +       if (CONFIG_IS_ENABLED(DM_REGULATOR)) {
> +               ret = device_get_supply_regulator(dev, "vdd-supply",
> +                                                 &priv->vdd);
> +               if (ret && ret != -ENOENT)
> +                       return ret;
> +
> +               ret = device_get_supply_regulator(dev, "vddio-supply",
> +                                                 &priv->vddio);
> +               if (ret && ret != -ENOENT)
> +                       return ret;
> +
> +               ret = device_get_supply_regulator(dev, "vdd1v2-supply",
> +                                                 &priv->vdd1v2);
> +               if (ret && ret != -ENOENT)
> +                       return ret;
> +
> +               ret = device_get_supply_regulator(dev, "avdd-supply",
> +                                                 &priv->avdd);
> +               if (ret && ret != -ENOENT)
> +                       return 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);
> +               if (ret != -ENOENT)
> +                       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 |
> +                       MIPI_DSI_MODE_LPM;
> +
> +       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
>

Reply via email to