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.
---
--
+++ 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.
+ *
--
+				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 */
--
+	.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);
--
+
+	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);
--
+	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));
--
+
+	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;
+	}
--
+					 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;
--
+	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;
--
+
+	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 <ruimail24@gmail.com>");
+MODULE_AUTHOR("Oleg Belousov <www.strijar.ru>");
+MODULE_DESCRIPTION("Driver for the Jinglitai JLT4013A LCD Panel");
+MODULE_LICENSE("GPL v2");
