From 649d9c38a8d786b348484c6036848442f6cd15c2 Mon Sep 17 00:00:00 2001 From: Ion Agorria Date: Sat, 19 Apr 2025 01:48:59 +0200 Subject: [PATCH] video: panel: add Sony L4F00430T01 panel driver Sony L4F00430T01 LCD module is found in Samsung Galaxy R. The panel has a WVGA resolution (480x800) and is setup over SPI, video data comes from RGB. Signed-off-by: Ion Agorria Reviewed-by: Svyatoslav Ryhel Signed-off-by: Svyatoslav Ryhel --- drivers/video/Kconfig | 9 ++ drivers/video/Makefile | 1 + drivers/video/sony-l4f00430t01.c | 210 +++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 drivers/video/sony-l4f00430t01.c diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index dfe4b3b8a02..1e405e743ae 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -691,6 +691,15 @@ config VIDEO_LCD_HITACHI_TX18D42VM lcd controller which needs to be initialized over SPI, once that is done they work like a regular LVDS panel. +config VIDEO_LCD_SONY_L4F00430T01 + tristate "Sony L4F00430T01 480x800 LCD panel support" + depends on PANEL + help + Say Y here if you want to enable support for Sony L4F00430T01 + LCD module found in Samsung Galaxy R. The panel has a + WVGA resolution (480x800) and is setup over SPI, video + data comes from RGB. + config VIDEO_LCD_SPI_CS string "SPI CS pin for LCD related config job" depends on VIDEO_LCD_SSD2828 || VIDEO_LCD_HITACHI_TX18D42VM diff --git a/drivers/video/Makefile b/drivers/video/Makefile index ebe4a3961fc..2151353551d 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_VIDEO_LCD_SHARP_LQ079L1SX01) += sharp-lq079l1sx01.o obj-$(CONFIG_VIDEO_LCD_SHARP_LQ101R1SX01) += sharp-lq101r1sx01.o 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_MCDE_SIMPLE) += mcde_simple.o obj-${CONFIG_VIDEO_MESON} += meson/ obj-${CONFIG_VIDEO_MIPI_DSI} += mipi_dsi.o diff --git a/drivers/video/sony-l4f00430t01.c b/drivers/video/sony-l4f00430t01.c new file mode 100644 index 00000000000..2303eb86143 --- /dev/null +++ b/drivers/video/sony-l4f00430t01.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2025 Ion Agorria + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SONY_L4F00430T01_DCS_CMD 0 +#define SONY_L4F00430T01_DCS_DATA 1 + +struct sony_l4f00430t01_priv { + struct udevice *vdd1v8; + struct udevice *vdd3v0; + + struct udevice *backlight; + + struct gpio_desc reset_gpio; +}; + +static struct display_timing default_timing = { + .pixelclock.typ = 24000000, + .hactive.typ = 480, + .hfront_porch.typ = 10, + .hback_porch.typ = 20, + .hsync_len.typ = 10, + .vactive.typ = 800, + .vfront_porch.typ = 3, + .vback_porch.typ = 4, + .vsync_len.typ = 2, + .flags = DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW | + DISPLAY_FLAGS_DE_HIGH | DISPLAY_FLAGS_PIXDATA_NEGEDGE, +}; + +static int sony_l4f00430t01_write(struct udevice *dev, u8 cmd, const u8 *seq, int len) +{ + u8 data[2]; + int i, ret; + + data[0] = SONY_L4F00430T01_DCS_CMD; + data[1] = cmd; + + ret = dm_spi_xfer(dev, 9, &data, NULL, SPI_XFER_ONCE); + if (ret) + return ret; + + for (i = 0; i < len; i++) { + data[0] = SONY_L4F00430T01_DCS_DATA; + data[1] = seq[i]; + + ret = dm_spi_xfer(dev, 9, &data, NULL, SPI_XFER_ONCE); + if (ret) + return ret; + } + + return 0; +} + +#define sony_l4f00430t01_write_seq(dev, cmd, seq...) do { \ + static const u8 b[] = { seq }; \ + sony_l4f00430t01_write(dev, cmd, b, ARRAY_SIZE(b)); \ + } while (0) + +static int sony_l4f00430t01_enable_backlight(struct udevice *dev) +{ + sony_l4f00430t01_write_seq(dev, MIPI_DCS_SET_ADDRESS_MODE, 0xd4); + mdelay(25); + + sony_l4f00430t01_write_seq(dev, MIPI_DCS_EXIT_SLEEP_MODE); + mdelay(150); + + sony_l4f00430t01_write_seq(dev, MIPI_DCS_SET_DISPLAY_ON); + + return 0; +} + +static int sony_l4f00430t01_set_backlight(struct udevice *dev, int percent) +{ + struct sony_l4f00430t01_priv *priv = dev_get_priv(dev); + int ret; + + ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev, + "backlight", &priv->backlight); + if (ret) { + log_debug("%s: cannot get backlight: ret = %d\n", + __func__, ret); + return ret; + } + + ret = backlight_enable(priv->backlight); + if (ret) + return ret; + + return backlight_set_brightness(priv->backlight, percent); +} + +static int sony_l4f00430t01_get_display_timing(struct udevice *dev, + struct display_timing *timing) +{ + memcpy(timing, &default_timing, sizeof(*timing)); + return 0; +} + +static int sony_l4f00430t01_of_to_plat(struct udevice *dev) +{ + struct sony_l4f00430t01_priv *priv = dev_get_priv(dev); + int ret; + + ret = device_get_supply_regulator(dev, "vdd1v8-supply", &priv->vdd1v8); + if (ret) { + log_debug("%s: cannot get vdd1v8-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = device_get_supply_regulator(dev, "vdd3v0-supply", &priv->vdd3v0); + if (ret) { + log_debug("%s: cannot get vdd3v0-supply: ret = %d\n", + __func__, ret); + return ret; + } + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->reset_gpio, GPIOD_IS_OUT); + if (ret) { + log_debug("%s: cannot decode reset-gpios (%d)\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int sony_l4f00430t01_hw_init(struct udevice *dev) +{ + struct sony_l4f00430t01_priv *priv = dev_get_priv(dev); + int ret; + + ret = dm_gpio_set_value(&priv->reset_gpio, 1); + if (ret) { + log_debug("%s: error entering reset (%d)\n", __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->vdd1v8, 1); + if (ret) { + log_debug("%s: enabling vdd1v8-supply failed (%d)\n", + __func__, ret); + return ret; + } + + ret = regulator_set_enable_if_allowed(priv->vdd3v0, 1); + if (ret) { + log_debug("%s: enabling vdd3v0-supply failed (%d)\n", + __func__, ret); + return ret; + } + mdelay(15); + + ret = dm_gpio_set_value(&priv->reset_gpio, 0); + if (ret) { + log_debug("%s: error exiting reset (%d)\n", __func__, ret); + return ret; + } + mdelay(100); + + return 0; +} + +static int sony_l4f00430t01_probe(struct udevice *dev) +{ + struct spi_slave *slave = dev_get_parent_priv(dev); + int ret; + + ret = spi_claim_bus(slave); + if (ret) { + log_err("SPI bus allocation failed (%d)\n", ret); + return ret; + } + + return sony_l4f00430t01_hw_init(dev); +} + +static const struct panel_ops sony_l4f00430t01_ops = { + .enable_backlight = sony_l4f00430t01_enable_backlight, + .set_backlight = sony_l4f00430t01_set_backlight, + .get_display_timing = sony_l4f00430t01_get_display_timing, +}; + +static const struct udevice_id sony_l4f00430t01_ids[] = { + { .compatible = "sony,l4f00430t01" }, + { } +}; + +U_BOOT_DRIVER(sony_l4f00430t01) = { + .name = "sony_l4f00430t01", + .id = UCLASS_PANEL, + .of_match = sony_l4f00430t01_ids, + .ops = &sony_l4f00430t01_ops, + .of_to_plat = sony_l4f00430t01_of_to_plat, + .probe = sony_l4f00430t01_probe, + .priv_auto = sizeof(struct sony_l4f00430t01_priv), +}; -- 2.47.2