From 51fdd3f58bffdab48078bf1659efabbd977c7f16 Mon Sep 17 00:00:00 2001
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Date: Fri, 26 Mar 2021 17:06:36 +0000
Subject: [PATCH] drm/panel-simple: Add a timing for the Raspberry Pi 7" panel

The Raspberry Pi 7" 800x480 panel uses a Toshiba TC358762 DSI
to DPI bridge chip, so there is a requirement for the timings
to be specified for the end panel. Add such a definition.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

drm/panel-simple: Populate bpc when using panel-dpi

panel-dpi doesn't know the bit depth, so in the same way that
DPI is guessed for the connector type, guess that it'll be 8bpc.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

drm/panel-simple: Allow the bus format to be read from DT for panel-dpi

The "panel-dpi" compatible string configures panel from device tree,
but it doesn't provide any way of configuring the bus format (colour
representation), nor does it populate it.

Add a DT parameter "bus-format" that allows the MEDIA_BUS_FMT_xxx value
to be specified from device tree.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

drm/panel: simple: add Geekworm MZP280 Panel

Add support for the Geekworm MZP280 Panel

Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Acked-by: Maxime Ripard <maxime@cerno.tech>

drm/panel: simple: Add Innolux AT056tN53V1 5.6" VGA

Add support for the Innolux AT056tN53V1 5.6" VGA (640x480) TFT LCD
panel.

Signed-off-by: Joerg Quinten <aBUGSworstnightmare@gmail.com>
Signed-off-by: Phil Elwell <phil@raspberrypi.com>

drm/panel: simple: Alter the timing for the Pi 7" DSI display

vc4 has always fixed up the timing, so the values defined have
never actually appeared on the wire.
The display appears to want a slightly longer HFP, so extend
the timings and recompute the clock to give the same frame rate.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

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>

drm/panel-simple: Remove custom handling of orientation

The framework now handles reading orientation from DT, therefore
remove the custom get_orientation hook from panel-simple.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

drm/panel-simple: Fix 7inch panel mode for misalignment

The 7inch panel is one line off the screen both horizontally
and vertically.

Alter the panel mode to correct this.

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

drm/panel-simple: Increase pixel clock on Pi 7inch panel

The Toshiba bridge is very fussy and doesn't like the CM3
output when being told to produce a 27.777MHz pixel clock, which
is an almost perfect match to the DSI link integer divider.

Increasing to 30MHz will switch the DSI link from 333MHz to 400MHz
and makes the bridge happy with the same video timing as works
on Pi4.
(Pi4 will be using a link frequency of 375MHz due to a 3GHz
parent PLL).

Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
 .../bindings/display/panel/panel-simple.yaml  |   2 +
 .../media/v4l/subdev-formats.rst              | 111 ++++++++
 drivers/gpu/drm/panel/panel-simple.c          | 237 ++++++++++++++++--
 3 files changed, 328 insertions(+), 22 deletions(-)

--- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
@@ -154,6 +154,8 @@ properties:
       - hit,tx23d38vm0caa
         # Innolux AT043TN24 4.3" WQVGA TFT LCD panel
       - innolux,at043tn24
+        # Innolux AT056tN53V1 5.6" VGA (640x480) TFT LCD panel
+      - innolux,at056tn53v1
         # Innolux AT070TN92 7.0" WQVGA TFT LCD panel
       - innolux,at070tn92
         # Innolux G070ACE-L01 7" WVGA (800x480) TFT LCD panel
--- a/Documentation/userspace-api/media/v4l/subdev-formats.rst
+++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst
@@ -625,6 +625,43 @@ The following tables list existing packe
       - b\ :sub:`2`
       - b\ :sub:`1`
       - b\ :sub:`0`
+    * .. _MEDIA_BUS_FMT_RGB565_1X24_CPADHI:
+
+      - MEDIA_BUS_FMT_RGB565_1X24_CPADHI
+      - 0x1022
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      - 0
+      - 0
+      - 0
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
+      - 0
+      - 0
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
     * .. _MEDIA-BUS-FMT-BGR565-2X8-BE:
 
       - MEDIA_BUS_FMT_BGR565_2X8_BE
@@ -913,6 +950,43 @@ The following tables list existing packe
       - g\ :sub:`5`
       - g\ :sub:`4`
       - g\ :sub:`3`
+    * .. _MEDIA-BUS-FMT-BGR666-1X18:
+
+      - MEDIA_BUS_FMT-BGR666_1X18
+      - 0x1023
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
     * .. _MEDIA-BUS-FMT-RGB666-1X18:
 
       - MEDIA_BUS_FMT_RGB666_1X18
@@ -1096,6 +1170,43 @@ The following tables list existing packe
       - g\ :sub:`2`
       - g\ :sub:`1`
       - g\ :sub:`0`
+    * .. _MEDIA-BUS-FMT-BGR666-1X24_CPADHI:
+
+      - MEDIA_BUS_FMT_BGR666_1X24_CPADHI
+      - 0x1024
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      -
+      - 0
+      - 0
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - 0
+      - 0
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
     * .. _MEDIA-BUS-FMT-RGB666-1X24_CPADHI:
 
       - MEDIA_BUS_FMT_RGB666_1X24_CPADHI
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -151,8 +151,6 @@ struct panel_simple {
 	const struct drm_edid *drm_edid;
 
 	struct drm_display_mode override_mode;
-
-	enum drm_panel_orientation orientation;
 };
 
 static inline struct panel_simple *to_panel_simple(struct drm_panel *panel)
@@ -387,12 +385,6 @@ static int panel_simple_get_modes(struct
 	/* add hard-coded panel modes */
 	num += panel_simple_get_non_edid_modes(p, connector);
 
-	/*
-	 * TODO: Remove once all drm drivers call
-	 * drm_connector_set_orientation_from_panel()
-	 */
-	drm_connector_set_panel_orientation(connector, p->orientation);
-
 	return num;
 }
 
@@ -413,20 +405,12 @@ static int panel_simple_get_timings(stru
 	return p->desc->num_timings;
 }
 
-static enum drm_panel_orientation panel_simple_get_orientation(struct drm_panel *panel)
-{
-	struct panel_simple *p = to_panel_simple(panel);
-
-	return p->orientation;
-}
-
 static const struct drm_panel_funcs panel_simple_funcs = {
 	.disable = panel_simple_disable,
 	.unprepare = panel_simple_unprepare,
 	.prepare = panel_simple_prepare,
 	.enable = panel_simple_enable,
 	.get_modes = panel_simple_get_modes,
-	.get_orientation = panel_simple_get_orientation,
 	.get_timings = panel_simple_get_timings,
 };
 
@@ -463,6 +447,7 @@ static int panel_dpi_probe(struct device
 
 	of_property_read_u32(np, "width-mm", &desc->size.width);
 	of_property_read_u32(np, "height-mm", &desc->size.height);
+	of_property_read_u32(np, "bus-format", &desc->bus_format);
 
 	/* Extract bus_flags from display_timing */
 	bus_flags = 0;
@@ -472,6 +457,8 @@ static int panel_dpi_probe(struct device
 
 	/* We do not know the connector for the DT node, so guess it */
 	desc->connector_type = DRM_MODE_CONNECTOR_DPI;
+	/* Likewise for the bit depth. */
+	desc->bpc = 8;
 
 	panel->desc = desc;
 
@@ -595,12 +582,6 @@ static int panel_simple_probe(struct dev
 		return dev_err_probe(dev, PTR_ERR(panel->enable_gpio),
 				     "failed to request GPIO\n");
 
-	err = of_drm_get_panel_orientation(dev->of_node, &panel->orientation);
-	if (err) {
-		dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, err);
-		return err;
-	}
-
 	ddc = of_parse_phandle(dev->of_node, "ddc-i2c-bus", 0);
 	if (ddc) {
 		panel->ddc = of_find_i2c_adapter_by_node(ddc);
@@ -2264,6 +2245,32 @@ static const struct panel_desc friendlya
 	},
 };
 
+static const struct drm_display_mode geekworm_mzp280_mode = {
+	.clock = 32000,
+	.hdisplay = 480,
+	.hsync_start = 480 + 41,
+	.hsync_end = 480 + 41 + 20,
+	.htotal = 480 + 41 + 20 + 60,
+	.vdisplay = 640,
+	.vsync_start = 640 + 5,
+	.vsync_end = 640 + 5 + 10,
+	.vtotal = 640 + 5 + 10 + 10,
+	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc geekworm_mzp280 = {
+	.modes = &geekworm_mzp280_mode,
+	.num_modes = 1,
+	.bpc = 6,
+	.size = {
+		.width = 47,
+		.height = 61,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB565_1X24_CPADHI,
+	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
+	.connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
 static const struct drm_display_mode giantplus_gpg482739qs5_mode = {
 	.clock = 9000,
 	.hdisplay = 480,
@@ -2444,6 +2451,38 @@ static const struct panel_desc innolux_a
 	.bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
 };
 
+static const struct display_timing innolux_at056tn53v1_timing = {
+	.pixelclock = { 39700000, 39700000, 39700000},
+	.hactive = { 640, 640, 640 },
+	.hfront_porch = { 16, 16, 16 },
+	.hback_porch = { 134, 134, 134 },
+	.hsync_len = { 10, 10, 10},
+	.vactive = { 480, 480, 480 },
+	.vfront_porch = { 32, 32, 32},
+	.vback_porch = { 11, 11, 11 },
+	.vsync_len = { 2, 2, 2 },
+	.flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc innolux_at056tn53v1 = {
+	.timings = &innolux_at056tn53v1_timing,
+	.num_timings = 1,
+	.bpc = 6,
+	.size = {
+		.width = 112,
+		.height = 84,
+	},
+	.delay = {
+		.prepare = 50,
+		.enable = 200,
+		.disable = 110,
+		.unprepare = 200,
+	},
+	.bus_format = MEDIA_BUS_FMT_BGR666_1X24_CPADHI,
+	.bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE,
+	.connector_type = DRM_MODE_CONNECTOR_DPI,
+};
+
 static const struct drm_display_mode innolux_at070tn92_mode = {
 	.clock = 33333,
 	.hdisplay = 800,
@@ -3857,6 +3896,31 @@ static const struct panel_desc rocktech_
 	.connector_type = DRM_MODE_CONNECTOR_DPI,
 };
 
+static const struct drm_display_mode raspberrypi_7inch_mode = {
+	.clock = 30000,
+	.hdisplay = 800,
+	.hsync_start = 800 + 131,
+	.hsync_end = 800 + 131 + 2,
+	.htotal = 800 + 131 + 2 + 45,
+	.vdisplay = 480,
+	.vsync_start = 480 + 7,
+	.vsync_end = 480 + 7 + 2,
+	.vtotal = 480 + 7 + 2 + 22,
+	.flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC,
+};
+
+static const struct panel_desc raspberrypi_7inch = {
+	.modes = &raspberrypi_7inch_mode,
+	.num_modes = 1,
+	.bpc = 8,
+	.size = {
+		.width = 154,
+		.height = 86,
+	},
+	.bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+	.connector_type = DRM_MODE_CONNECTOR_DSI,
+};
+
 static const struct display_timing rocktech_rk070er9427_timing = {
 	.pixelclock = { 26400000, 33300000, 46800000 },
 	.hactive = { 800, 800, 800 },
@@ -4801,6 +4865,9 @@ static const struct of_device_id platfor
 		.compatible = "friendlyarm,hd702e",
 		.data = &friendlyarm_hd702e,
 	}, {
+		.compatible = "geekworm,mzp280",
+		.data = &geekworm_mzp280,
+	}, {
 		.compatible = "giantplus,gpg482739qs5",
 		.data = &giantplus_gpg482739qs5
 	}, {
@@ -4822,6 +4889,9 @@ static const struct of_device_id platfor
 		.compatible = "innolux,at043tn24",
 		.data = &innolux_at043tn24,
 	}, {
+		.compatible = "innolux,at056tn53v1",
+		.data = &innolux_at056tn53v1,
+	}, {
 		.compatible = "innolux,at070tn92",
 		.data = &innolux_at070tn92,
 	}, {
@@ -4981,6 +5051,9 @@ static const struct of_device_id platfor
 		.compatible = "rocktech,rk043fn48h",
 		.data = &rocktech_rk043fn48h,
 	}, {
+		.compatible = "raspberrypi,7inch-dsi",
+		.data = &raspberrypi_7inch,
+	}, {
 		.compatible = "rocktech,rk070er9427",
 		.data = &rocktech_rk070er9427,
 	}, {
@@ -5337,6 +5410,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",
@@ -5360,20 +5436,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;
