路径:/u-boot/drivers/video/drm/panel-lh24030c50.c
// SPDX-License-Identifier: GPL-2.0+
/*
* LH24030C50 RGB LCD panel driver based on the ST7789V controller.
*
* GPIO bitbang SPI for panel initialization, RGB interface for display data.
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <backlight.h>
#include <asm/gpio.h>
#include <power/regulator.h>
#include <linux/media-bus-format.h>
#include <drm_modes.h>
#include "rockchip_display.h"
#include "rockchip_panel.h"
struct lh24030c50_priv {
struct gpio_desc reset;
struct gpio_desc sclk;
struct gpio_desc mosi;
struct gpio_desc cs;
struct udevice *power_supply;
struct udevice *backlight;
bool prepared;
bool enabled;
};
/*
* 9-bit SPI bitbang: DC bit + 8 data bits, MSB first.
* CS active low, SCLK idles low, data clocked on rising edge.
*/
static void lh24030c50_spi_xfer(struct lh24030c50_priv *priv, int dc, u8 data)
{
int i;
dm_gpio_set_value(&priv->cs, 0);
udelay(2);
/* DC bit */
dm_gpio_set_value(&priv->sclk, 0);
dm_gpio_set_value(&priv->mosi, dc ? 1 : 0);
udelay(2);
dm_gpio_set_value(&priv->sclk, 1);
udelay(2);
/* 8 data bits, MSB first */
for (i = 0; i < 8; i++) {
dm_gpio_set_value(&priv->sclk, 0);
dm_gpio_set_value(&priv->mosi, (data >> (7 - i)) & 1);
udelay(2);
dm_gpio_set_value(&priv->sclk, 1);
udelay(2);
}
dm_gpio_set_value(&priv->sclk, 0);
udelay(1);
dm_gpio_set_value(&priv->cs, 1);
udelay(1);
}
static void lh24030c50_write_cmd(struct lh24030c50_priv *priv, u8 cmd)
{
lh24030c50_spi_xfer(priv, 0, cmd);
}
static void lh24030c50_write_data(struct lh24030c50_priv *priv, u8 data)
{
lh24030c50_spi_xfer(priv, 1, data);
}
static void lh24030c50_spi_idle(struct lh24030c50_priv *priv)
{
dm_gpio_set_value(&priv->cs, 1);
dm_gpio_set_value(&priv->sclk, 0);
dm_gpio_set_value(&priv->mosi, 0);
}
/*
* ST7789V LCD initialization sequence.
* This matches the kernel panel-lh24030c50.c LCD_Init().
*/
static void lh24030c50_lcd_init(struct lh24030c50_priv *priv)
{
lh24030c50_spi_idle(priv);
/* Software Reset */
lh24030c50_write_cmd(priv, 0x01);
mdelay(150);
/* Sleep Out */
lh24030c50_write_cmd(priv, 0x11);
mdelay(120);
/* MADCTL: RGB mode */
lh24030c50_write_cmd(priv, 0x36); //控制 RGB/BGR、屏幕镜像、扫描方向
lh24030c50_write_data(priv, 0x00);
/* COLMOD: 18-bit/pixel (RGB666) */
lh24030c50_write_cmd(priv, 0x3a);
lh24030c50_write_data(priv, 0x06);
/* RAMCTRL */
lh24030c50_write_cmd(priv, 0xB0);
lh24030c50_write_data(priv, 0x12); /* RM=1, DM=01(RGB) */
lh24030c50_write_data(priv, 0xC0); /* EPF1 EPF0=11 */
/* Porch setting */
lh24030c50_write_cmd(priv, 0xB1);
lh24030c50_write_data(priv, 0x40);
lh24030c50_write_data(priv, 0x04);
lh24030c50_write_data(priv, 0x0a);
/* Frame Rate Control */
lh24030c50_write_cmd(priv, 0xB2);
lh24030c50_write_data(priv, 0x0C);
lh24030c50_write_data(priv, 0x0C);
lh24030c50_write_data(priv, 0x00);
lh24030c50_write_data(priv, 0x33);
lh24030c50_write_data(priv, 0x33);
/* Gate Control */
lh24030c50_write_cmd(priv, 0xB7);
lh24030c50_write_data(priv, 0x35);
/* VCOM Setting */
lh24030c50_write_cmd(priv, 0xBB);
lh24030c50_write_data(priv, 0x2B);
/* Power Control 1 */
lh24030c50_write_cmd(priv, 0xC0);
lh24030c50_write_data(priv, 0x2C);
/* Power Control 2 */
lh24030c50_write_cmd(priv, 0xC2);
lh24030c50_write_data(priv, 0x01);
/* Power Control 3 */
lh24030c50_write_cmd(priv, 0xC3);
lh24030c50_write_data(priv, 0x11);
/* Power Control 4 */
lh24030c50_write_cmd(priv, 0xC4);
lh24030c50_write_data(priv, 0x20);
/* VCOM Control 1 */
lh24030c50_write_cmd(priv, 0xC6);
lh24030c50_write_data(priv, 0x0F);
/* Power Control A */
lh24030c50_write_cmd(priv, 0xD0);
lh24030c50_write_data(priv, 0xA4);
lh24030c50_write_data(priv, 0xA1);
/* Positive Gamma Correction */
lh24030c50_write_cmd(priv, 0xE0);
lh24030c50_write_data(priv, 0xD0);
lh24030c50_write_data(priv, 0x00);
lh24030c50_write_data(priv, 0x06);
lh24030c50_write_data(priv, 0x09);
lh24030c50_write_data(priv, 0x0B);
lh24030c50_write_data(priv, 0x2A);
lh24030c50_write_data(priv, 0x3C);
lh24030c50_write_data(priv, 0x55);
lh24030c50_write_data(priv, 0x4B);
lh24030c50_write_data(priv, 0x08);
lh24030c50_write_data(priv, 0x16);
lh24030c50_write_data(priv, 0x14);
lh24030c50_write_data(priv, 0x19);
lh24030c50_write_data(priv, 0x20);
/* Negative Gamma Correction */
lh24030c50_write_cmd(priv, 0xE1);
lh24030c50_write_data(priv, 0xD0);
lh24030c50_write_data(priv, 0x00);
lh24030c50_write_data(priv, 0x06);
lh24030c50_write_data(priv, 0x09);
lh24030c50_write_data(priv, 0x0B);
lh24030c50_write_data(priv, 0x29);
lh24030c50_write_data(priv, 0x36);
lh24030c50_write_data(priv, 0x54);
lh24030c50_write_data(priv, 0x4B);
lh24030c50_write_data(priv, 0x0D);
lh24030c50_write_data(priv, 0x16);
lh24030c50_write_data(priv, 0x14);
lh24030c50_write_data(priv, 0x21);
lh24030c50_write_data(priv, 0x20);
lh24030c50_write_cmd(priv, 0x21); // 开启像素反转,解决黑白颠倒
}
static void lh24030c50_prepare(struct rockchip_panel *panel)
{
struct lh24030c50_priv *priv = dev_get_priv(panel->dev);
if (priv->prepared)
return;
/* Enable power supply */
if (priv->power_supply)
regulator_set_enable(priv->power_supply, true);
/* Reset sequence: assert for 50ms, then deassert */
dm_gpio_set_value(&priv->reset, 1);
mdelay(50);
dm_gpio_set_value(&priv->reset, 0);
mdelay(50);
/* SPI idle then run LCD init */
lh24030c50_spi_idle(priv);
mdelay(150);
lh24030c50_lcd_init(priv);
priv->prepared = true;
}
static void lh24030c50_unprepare(struct rockchip_panel *panel)
{
struct lh24030c50_priv *priv = dev_get_priv(panel->dev);
if (!priv->prepared)
return;
/* Sleep In */
lh24030c50_write_cmd(priv, 0x10);
mdelay(120);
dm_gpio_set_value(&priv->reset, 1);
lh24030c50_spi_idle(priv);
if (priv->power_supply)
regulator_set_enable(priv->power_supply, false);
priv->prepared = false;
}
static void lh24030c50_enable(struct rockchip_panel *panel)
{
struct lh24030c50_priv *priv = dev_get_priv(panel->dev);
if (priv->enabled)
return;
/* Wait for first frame to settle, then turn display on */
mdelay(20);
lh24030c50_write_cmd(priv, 0x29);
mdelay(50);
/* Enable backlight */
if (priv->backlight)
backlight_enable(priv->backlight);
priv->enabled = true;
}
static void lh24030c50_disable(struct rockchip_panel *panel)
{
struct lh24030c50_priv *priv = dev_get_priv(panel->dev);
if (!priv->enabled)
return;
/* Disable backlight */
if (priv->backlight)
backlight_disable(priv->backlight);
/* Display Off */
lh24030c50_write_cmd(priv, 0x28);
priv->enabled = false;
}
/* Panel display timing: 240x320 @ 7MHz RGB */
static int lh24030c50_get_mode(struct rockchip_panel *panel,
struct drm_display_mode *mode)
{
printf("lh24030c50: get_mode called\n");
mode->clock = 7000;
mode->hdisplay = 240;
mode->hsync_start = 240 + 38;
mode->hsync_end = 240 + 38 + 10;
mode->htotal = 240 + 38 + 10 + 10;
mode->vdisplay = 320;
mode->vsync_start = 320 + 8;
mode->vsync_end = 320 + 8 + 4;
mode->vtotal = 320 + 8 + 4 + 4;
mode->flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC;
mode->vrefresh = 70;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
return 0;
}
static const struct rockchip_panel_funcs lh24030c50_funcs = {
.prepare = lh24030c50_prepare,
.unprepare = lh24030c50_unprepare,
.enable = lh24030c50_enable,
.disable = lh24030c50_disable,
.get_mode = lh24030c50_get_mode,
};
static int lh24030c50_probe(struct udevice *dev)
{
struct lh24030c50_priv *priv = dev_get_priv(dev);
struct rockchip_panel *panel;
int ret;
/* Reset GPIO (active low) */
ret = gpio_request_by_name(dev, "reset-gpios", 0, &priv->reset,
GPIOD_IS_OUT);
if (ret) {
printf("%s: Cannot get reset GPIO: %d\n", __func__, ret);
return ret;
}
/* SPI bitbang GPIOs */
ret = gpio_request_by_name(dev, "spi-scl-gpios", 0, &priv->sclk,
GPIOD_IS_OUT);
if (ret) {
printf("%s: Cannot get SPI SCLK GPIO: %d\n", __func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "spi-sdi-gpios", 0, &priv->mosi,
GPIOD_IS_OUT);
if (ret) {
printf("%s: Cannot get SPI SDI GPIO: %d\n", __func__, ret);
return ret;
}
ret = gpio_request_by_name(dev, "spi-cs-gpios", 0, &priv->cs,
GPIOD_IS_OUT);
if (ret) {
printf("%s: Cannot get SPI CS GPIO: %d\n", __func__, ret);
return ret;
}
/* Power supply (optional) */
ret = uclass_get_device_by_phandle(UCLASS_REGULATOR, dev,
"power-supply", &priv->power_supply);
if (ret && ret != -ENOENT && ret != -ENODEV) {
printf("%s: Cannot get power supply: %d\n", __func__, ret);
return ret;
}
/* Backlight (optional) */
ret = uclass_get_device_by_phandle(UCLASS_PANEL_BACKLIGHT, dev,
"backlight", &priv->backlight);
if (ret && ret != -ENOENT && ret != -ENODEV) {
printf("%s: Cannot get backlight: %d\n", __func__, ret);
return ret;
}
/* Set initial idle state */
dm_gpio_set_value(&priv->cs, 1);
dm_gpio_set_value(&priv->sclk, 0);
dm_gpio_set_value(&priv->mosi, 0);
dm_gpio_set_value(&priv->reset, 0);
/* Allocate and register rockchip_panel */
panel = calloc(1, sizeof(*panel));
if (!panel)
return -ENOMEM;
dev->driver_data = (ulong)panel;
panel->dev = dev;
panel->bpc = 6; /* RGB666: 6 bits per component */
panel->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
panel->funcs = &lh24030c50_funcs;
return 0;
}
static const struct udevice_id lh24030c50_ids[] = {
{ .compatible = "lh,lh24030c50" },
{ }
};
U_BOOT_DRIVER(lh24030c50) = {
.name = "lh24030c50",
.id = UCLASS_PANEL,
.of_match = lh24030c50_ids,
.probe = lh24030c50_probe,
.priv_auto_alloc_size = sizeof(struct lh24030c50_priv),
};