From: Vendored from gdyuldin/AetherX6200Buildroot (GPL-2.0) Source: https://github.com/gdyuldin/AetherX6200Buildroot Original authors: Rui Oliveira (2022), Oleg Belousov (2022) Adds driver for the Jinglitai JLT4013A LCD panel used in the Xiegu X6200. This patch was written against a ~6.1-era kernel. It may require minor forward-porting for 6.6 (panel/Kconfig line offsets may have shifted as new panel drivers were added upstream). The core driver file (panel-jinglitai-jlt4013a.c) should apply cleanly. NOTE: Milestone 1 (serial console boot) does not require this patch. If the patch fails to apply, comment out BR2_LINUX_KERNEL_PATCH in the defconfig and address the LCD driver in Milestone 2. --- diff -Naur orig/drivers/gpu/drm/panel/Kconfig my/drivers/gpu/drm/panel/Kconfig --- orig/drivers/gpu/drm/panel/Kconfig 2023-01-12 14:00:49.000000000 +0300 +++ my/drivers/gpu/drm/panel/Kconfig 2023-01-30 03:24:48.108365590 +0300 @@ -717,4 +717,8 @@ Say Y here if you want to enable support for the Xinpeng XPP055C272 controller for 720x1280 LCD panels with MIPI/RGB/SPI system interfaces. + +config DRM_PANEL_JINGLITAI_JLT4013A + tristate "Jinglitai JLT4013A panel driver" + endmenu diff -Naur orig/drivers/gpu/drm/panel/Makefile my/drivers/gpu/drm/panel/Makefile --- orig/drivers/gpu/drm/panel/Makefile 2023-01-12 14:00:49.000000000 +0300 +++ my/drivers/gpu/drm/panel/Makefile 2023-01-30 03:23:34.824674091 +0300 @@ -73,3 +73,4 @@ obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o obj-$(CONFIG_DRM_PANEL_WIDECHIPS_WS2401) += panel-widechips-ws2401.o obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o +obj-$(CONFIG_DRM_PANEL_JINGLITAI_JLT4013A) += panel-jinglitai-jlt4013a.o diff -Naur orig/drivers/gpu/drm/panel/panel-jinglitai-jlt4013a.c my/drivers/gpu/drm/panel/panel-jinglitai-jlt4013a.c --- orig/drivers/gpu/drm/panel/panel-jinglitai-jlt4013a.c 1970-01-01 03:00:00.000000000 +0300 +++ my/drivers/gpu/drm/panel/panel-jinglitai-jlt4013a.c 2023-01-30 03:22:15.521007934 +0300 @@ -0,0 +1,549 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Jinglitai JLT4013A LCD Panel. + * + * Copyright (C) Rui Oliveira 2022 + * Copyright (C) Oleg Belousov 2022 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ST7701S_SWRESET 0x01 +#define ST7701S_SLPOUT 0x11 +#define ST7701S_DISPOFF 0x28 +#define ST7701S_DISPON 0x29 +#define ST7701S_COLMOD 0x3A + +#define ST7701S_CN2BKxSEL 0xFF + +/* BK0 */ + +#define ST7701S_LNESET 0xC0 +#define ST7701S_PORCTRL 0xC1 +#define ST7701S_INVSET 0xC2 +#define ST7701S_PVGAMCTRL 0xB0 +#define ST7701S_NVGAMCTRL 0xB1 + +/* BK1 */ + +#define ST7701S_VRHS 0xB0 +#define ST7701S_VCOM 0xB1 +#define ST7701S_VGHSS 0xB2 +#define ST7701S_TESTCMD 0xB3 +#define ST7701S_VGLS 0xB5 +#define ST7701S_PWCTRL1 0xB7 +#define ST7701S_PWCTRL2 0xB8 +#define ST7701S_PWCTRL3 0xB9 +#define ST7701S_SPD1 0xC1 +#define ST7701S_SPD2 0xC2 +#define ST7701S_MIPISET1 0xD0 + +#define ST7701S_TRY(val, func) \ + do { \ + if ((val = (func))) { \ + pr_warn("Jinglitai JLT4013A: SPI write failed with error %d\n", \ + val); \ + return val; \ + } \ + } while (0) + +static const struct of_device_id jlt4013a_of_match[] = { + { .compatible = "sitronix,st7701s" }, + { .compatible = "jinglitai,jlt4013a" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jlt4013a_of_match); + +static const struct drm_display_mode jlt4013a_default_display_mode = { + .clock = 27500, + .hdisplay = 480, /* x */ + .hsync_start = 480 + 38, /* x + ri */ + .hsync_end = 480 + 38 + 12, /* x + ri + hs */ + .htotal = 480 + 38 + 12 + 12, /* x + ri + hs + le */ + .vdisplay = 800, /* y */ + .vsync_start = 800 + 18, /* y + lo */ + .vsync_end = 800 + 18 + 8, /* y + lo + vs */ + .vtotal = 800 + 18 + 8 + 17, /* y + lo + vs + up */ + .width_mm = 52, + .height_mm = 86, +}; + + +struct jlt4013a { + struct drm_panel panel; + struct spi_device *spi; + struct gpio_desc *reset; + struct gpio_desc *dcx; + struct regulator *supply; +}; + +static int st7701s_spi_write(struct jlt4013a *ctx, u8 data) +{ + struct spi_transfer xfer = {}; + struct spi_message msg; + + spi_message_init(&msg); + + xfer.tx_buf = &data; + xfer.bits_per_word = 8; + xfer.len = sizeof(data); + + spi_message_add_tail(&xfer, &msg); + return spi_sync(ctx->spi, &msg); +} + +static int st7701s_write_command(struct jlt4013a *ctx, u8 cmd) +{ + // pr_info("Jinglitai JLT4013A: Writing SPI command with value %x\n", cmd); + + gpiod_set_value(ctx->dcx, 0); + + return st7701s_spi_write(ctx, cmd); +} + +static int st7701s_write_data(struct jlt4013a *ctx, u8 cmd) +{ + // pr_info("Jinglitai JLT4013A: Writing SPI data with value %x\n", cmd); + + gpiod_set_value(ctx->dcx, 1); + + return st7701s_spi_write(ctx, cmd); +} + +static inline struct jlt4013a *panel_to_jlt4013a(struct drm_panel *panel) +{ + return container_of(panel, struct jlt4013a, panel); +} + +static int jlt4013a_prepare(struct drm_panel *panel) +{ + int ret; + struct jlt4013a *ctx = panel_to_jlt4013a(panel); + + /* Enable power supply */ + + pr_info("Jinglitai JLT4013A: Trying to enable power supply\n"); + ret = regulator_enable(ctx->supply); + if (ret) { + pr_err("Jinglitai JLT4013A: Failed to enable power supply\n"); + return ret; + } + msleep(120); + pr_info("Jinglitai JLT4013A: Enabled power supply\n"); + + /* Reset routine */ + pr_info("Jinglitai JLT4013A: Doing the reset routine\n"); + gpiod_set_value(ctx->reset, 1); + msleep(120); + gpiod_set_value(ctx->reset, 0); + msleep(120); // Sleep mandated by the datasheet + pr_info("Jinglitai JLT4013A: Panel is reset\n"); + + /* Initialization routine */ + pr_info("Jinglitai JLT4013A: Doing the initialization routine\n"); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_SLPOUT)); + msleep(120); + + /* BK0 */ + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_CN2BKxSEL)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x77)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x01)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x10)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_PORCTRL)); + ST7701S_TRY(ret, st7701s_write_data(ctx, + jlt4013a_default_display_mode.vtotal + - jlt4013a_default_display_mode.vsync_end)); // mode.vtotal - mode.vsync_end + ST7701S_TRY(ret, st7701s_write_data(ctx, + jlt4013a_default_display_mode.vsync_start + - jlt4013a_default_display_mode.vdisplay)); // mode.vsync_start - mode.vdisplay + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_INVSET)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x31)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x03)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xC3)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x02)); // DataPolarity: negative (The data is input on the negative edge of DOTCLK) + ST7701S_TRY(ret, st7701s_write_data(ctx, + jlt4013a_default_display_mode.htotal + - jlt4013a_default_display_mode.hsync_end)); // HBP = mode.htotal - mode.hsync_end + ST7701S_TRY(ret, st7701s_write_data(ctx, + jlt4013a_default_display_mode.vsync_start + - jlt4013a_default_display_mode.vdisplay)); // VBP = mode.vsync_start - mode.vdisplay + + /* Something strange */ + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xCC)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x10)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_PVGAMCTRL)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x40)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xC9)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x90)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0F)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x04)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x07)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x07)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x1C)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x04)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x52)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0F)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xDF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x26)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xCF)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_NVGAMCTRL)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x40)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xC9)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xCF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0C)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x90)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x04)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x07)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x08)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x1B)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x06)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x55)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x13)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x62)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xE7)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xCF)); + + /* BK1 */ + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_CN2BKxSEL)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x77)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x01)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x11)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_VRHS)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x5B)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_VCOM)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x68)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_VGHSS)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x07)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_TESTCMD)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x80)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_VGLS)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x47)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_PWCTRL1)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x85)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_PWCTRL2)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x21)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_PWCTRL3)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x10)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_SPD1)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x21)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x36)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_SPD2)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x78)); + + msleep(120); + + /* Something strange */ + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x02)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE1)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x08)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0A)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x07)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x09)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x33)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x33)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE2)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE3)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x33)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x33)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE4)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x44)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x44)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE5)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0E)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x10)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0A)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0C)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE6)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x33)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x33)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE7)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x44)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x44)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xE8)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0F)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x09)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x0B)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x2D)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xA0)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xEB)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x02)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x01)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xE4)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xE4)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x44)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x40)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xEC)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x02)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x01)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, 0xED)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xAB)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x89)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x76)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x54)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x01)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xFF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xFF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xFF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xFF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xFF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xFF)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x10)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x45)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x67)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x98)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0xBA)); + + /* BK disable */ + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_CN2BKxSEL)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x77)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x01)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x00)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_COLMOD)); + ST7701S_TRY(ret, st7701s_write_data(ctx, 0x77)); + + ST7701S_TRY(ret, st7701s_write_command(ctx, ST7701S_DISPON)); + + msleep(120); + + pr_info("Jinglitai JLT4013A: Panel is initialized\n"); + return ret; +} + +static int jlt4013a_unprepare(struct drm_panel *panel) +{ + struct jlt4013a *ctx = panel_to_jlt4013a(panel); + + int ret = regulator_disable(ctx->supply); + return ret; +} + + +static int jlt4013a_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + static const u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; + static const u32 bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE; + + + struct drm_display_mode *mode = drm_mode_duplicate( + connector->dev, &jlt4013a_default_display_mode); + if (mode == NULL) { + dev_err(panel->dev, + "Jinglitai JLT4013A: Failed to add default mode\n"); + return -EAGAIN; + } + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + + connector->display_info.width_mm = mode->width_mm; + connector->display_info.height_mm = mode->height_mm; + connector->display_info.bpc = 8; + connector->display_info.bus_flags = bus_flags; + + drm_mode_probed_add(connector, mode); + + drm_display_info_set_bus_formats(&connector->display_info, &bus_format, + 1); + + return 1; +} + +static int jlt4013a_enable(struct drm_panel *panel) +{ + return 0; +} + +static int jlt4013a_disable(struct drm_panel *panel) +{ + return 0; +} + +static const struct drm_panel_funcs jlt4013afuncs = { + .prepare = jlt4013a_prepare, + .unprepare = jlt4013a_unprepare, + .get_modes = jlt4013a_get_modes, + .enable = jlt4013a_enable, + .disable = jlt4013a_disable, +}; + +static int jlt4013a_probe(struct spi_device *spi) +{ + int err; + + struct device *dev = &spi->dev; + + struct jlt4013a *ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (ctx == NULL) { + return -EAGAIN; + } + + ctx->spi = spi; + spi_set_drvdata(spi, ctx); + + ctx->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(ctx->supply)) { + dev_err(dev, + "Jinglitai JLT4013A: Failed to get power supply\n"); + return PTR_ERR(ctx->supply); + } + + ctx->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset)) { + dev_err(dev, "Jinglitai JLT4013A: Failed to get reset GPIO\n"); + return PTR_ERR(ctx->reset); + } + + ctx->dcx = devm_gpiod_get(dev, "dcx", GPIOD_OUT_LOW); + if (IS_ERR(ctx->dcx)) { + dev_err(dev, "Jinglitai JLT4013A: Failed to get dcx GPIO\n"); + return PTR_ERR(ctx->dcx); + } + + drm_panel_init(&ctx->panel, dev, &jlt4013afuncs, + DRM_MODE_CONNECTOR_DPI); + + err = drm_panel_of_backlight(&ctx->panel); + if (err) + return err; + + drm_panel_add(&ctx->panel); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,0,0) +static void jlt4013a_remove(struct spi_device *spi) +{ + struct jlt4013a *ctx = spi_get_drvdata(spi); + + drm_panel_remove(&(ctx->panel)); +} +#else +static int jlt4013a_remove(struct spi_device *spi) +{ + struct jlt4013a *ctx = spi_get_drvdata(spi); + + drm_panel_remove(&(ctx->panel)); + return 0; +} +#endif + +static struct spi_driver jlt4013a_driver = { + .probe = jlt4013a_probe, + .remove = jlt4013a_remove, + .driver = { + .name = "jlt4013a", + .of_match_table = jlt4013a_of_match, + }, +}; +module_spi_driver(jlt4013a_driver); + +MODULE_AUTHOR("Rui Oliveira "); +MODULE_AUTHOR("Oleg Belousov "); +MODULE_DESCRIPTION("Driver for the Jinglitai JLT4013A LCD Panel"); +MODULE_LICENSE("GPL v2");