чт, 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 >
