From fe0165c07de6a5c31d9c2be88ec6642d8ac98872 Mon Sep 17 00:00:00 2001
From: Timon Skerutsch <kernel@diodes-delight.com>
Date: Mon, 13 Nov 2023 22:53:12 +0100
Subject: [PATCH 0757/1085] drm/panel: add panel-dsi

Equivalent to panel-dpi for configuring a simple DSI panel with
device tree side timings and bus settings.
Motiviation is the same as for panel-dpi of wanting to support
new simple panels without needing to patch the kernel.

Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
---
 drivers/gpu/drm/panel/panel-simple.c | 121 +++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)

--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -40,6 +40,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
+#include <drm/drm_of.h>
 
 /**
  * struct panel_desc - Describes a simple panel.
@@ -4903,6 +4904,9 @@ static const struct panel_desc_dsi osd10
 	.lanes = 4,
 };
 
+// for panels using generic panel-dsi binding
+static struct panel_desc_dsi panel_dsi;
+
 static const struct of_device_id dsi_of_match[] = {
 	{
 		.compatible = "auo,b080uan01",
@@ -4926,20 +4930,137 @@ static const struct of_device_id dsi_of_
 		.compatible = "osddisplays,osd101t2045-53ts",
 		.data = &osd101t2045_53ts
 	}, {
+		/* Must be the last entry */
+		.compatible = "panel-dsi",
+		.data = &panel_dsi,
+	}, {
 		/* sentinel */
 	}
 };
 MODULE_DEVICE_TABLE(of, dsi_of_match);
 
+
+/* Checks for DSI panel definition in device-tree, analog to panel_dpi */
+static int panel_dsi_dt_probe(struct device *dev,
+			  struct panel_desc_dsi *desc_dsi)
+{
+	struct panel_desc *desc;
+	struct display_timing *timing;
+	const struct device_node *np;
+	const char *dsi_color_format;
+	const char *dsi_mode_flags;
+	struct property *prop;
+	int dsi_lanes, ret;
+
+	np = dev->of_node;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
+	if (!timing)
+		return -ENOMEM;
+
+	ret = of_get_display_timing(np, "panel-timing", timing);
+	if (ret < 0) {
+		dev_err(dev, "%pOF: no panel-timing node found for \"panel-dsi\" binding\n",
+			np);
+		return ret;
+	}
+
+	desc->timings = timing;
+	desc->num_timings = 1;
+
+	of_property_read_u32(np, "width-mm", &desc->size.width);
+	of_property_read_u32(np, "height-mm", &desc->size.height);
+
+	dsi_lanes = drm_of_get_data_lanes_count_ep(np, 0, 0, 1, 4);
+
+	if (dsi_lanes < 0) {
+		dev_err(dev, "%pOF: no or too many data-lanes defined", np);
+		return dsi_lanes;
+	}
+
+	desc_dsi->lanes = dsi_lanes;
+
+	of_property_read_string(np, "dsi-color-format", &dsi_color_format);
+	if (!strcmp(dsi_color_format, "RGB888")) {
+		desc_dsi->format = MIPI_DSI_FMT_RGB888;
+		desc->bpc = 8;
+	} else if (!strcmp(dsi_color_format, "RGB565")) {
+		desc_dsi->format = MIPI_DSI_FMT_RGB565;
+		desc->bpc = 6;
+	} else if (!strcmp(dsi_color_format, "RGB666")) {
+		desc_dsi->format = MIPI_DSI_FMT_RGB666;
+		desc->bpc = 6;
+	} else if (!strcmp(dsi_color_format, "RGB666_PACKED")) {
+		desc_dsi->format = MIPI_DSI_FMT_RGB666_PACKED;
+		desc->bpc = 6;
+	} else {
+		dev_err(dev, "%pOF: no valid dsi-color-format defined", np);
+		return -EINVAL;
+	}
+
+
+	of_property_for_each_string(np, "mode", prop, dsi_mode_flags) {
+		if (!strcmp(dsi_mode_flags, "MODE_VIDEO"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_BURST"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_BURST;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_SYNC_PULSE"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_AUTO_VERT"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_AUTO_VERT;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_HSE"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_HSE;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HFP"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HFP;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HBP"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HBP;
+		else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HSA"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HSA;
+		else if (!strcmp(dsi_mode_flags, "MODE_VSYNC_FLUSH"))
+			desc_dsi->flags |= MIPI_DSI_MODE_VSYNC_FLUSH;
+		else if (!strcmp(dsi_mode_flags, "MODE_NO_EOT_PACKET"))
+			desc_dsi->flags |= MIPI_DSI_MODE_NO_EOT_PACKET;
+		else if (!strcmp(dsi_mode_flags, "CLOCK_NON_CONTINUOUS"))
+			desc_dsi->flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS;
+		else if (!strcmp(dsi_mode_flags, "MODE_LPM"))
+			desc_dsi->flags |= MIPI_DSI_MODE_LPM;
+		else if (!strcmp(dsi_mode_flags, "HS_PKT_END_ALIGNED"))
+			desc_dsi->flags |= MIPI_DSI_HS_PKT_END_ALIGNED;
+	}
+
+	desc->connector_type = DRM_MODE_CONNECTOR_DSI;
+	desc_dsi->desc = *desc;
+
+	return 0;
+}
+
 static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 {
 	const struct panel_desc_dsi *desc;
+	struct panel_desc_dsi *dt_desc;
 	int err;
 
 	desc = of_device_get_match_data(&dsi->dev);
 	if (!desc)
 		return -ENODEV;
 
+	if (desc == &panel_dsi) {
+		/* Handle the generic panel-dsi binding */
+		dt_desc = devm_kzalloc(&dsi->dev, sizeof(*dt_desc), GFP_KERNEL);
+		if (!dt_desc)
+			return -ENOMEM;
+
+		err = panel_dsi_dt_probe(&dsi->dev, dt_desc);
+		if (err < 0)
+			return err;
+
+		desc = dt_desc;
+	}
+
 	err = panel_simple_probe(&dsi->dev, &desc->desc);
 	if (err < 0)
 		return err;
