lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251222-drm-panels-sony-v2-3-82a87465d163@somainline.org>
Date: Mon, 22 Dec 2025 00:32:09 +0100
From: Marijn Suijten <marijn.suijten@...ainline.org>
To: Neil Armstrong <neil.armstrong@...aro.org>, 
 Sam Ravnborg <sam@...nborg.org>, David Airlie <airlied@...il.com>, 
 Rob Herring <robh+dt@...nel.org>, 
 Krzysztof Kozlowski <krzysztof.kozlowski+dt@...aro.org>, 
 Conor Dooley <conor+dt@...nel.org>, Andy Gross <agross@...nel.org>, 
 Bjorn Andersson <andersson@...nel.org>, 
 Jessica Zhang <jesszhan0024@...il.com>, 
 Maarten Lankhorst <maarten.lankhorst@...ux.intel.com>, 
 Maxime Ripard <mripard@...nel.org>, Thomas Zimmermann <tzimmermann@...e.de>, 
 Simona Vetter <simona@...ll.ch>, Casey Connolly <casey.connolly@...aro.org>, 
 Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>, 
 Simona Vetter <simona.vetter@...ll.ch>
Cc: ~postmarketos/upstreaming@...ts.sr.ht, 
 AngeloGioacchino Del Regno <angelogioacchino.delregno@...ainline.org>, 
 Martin Botka <martin.botka@...ainline.org>, 
 Jami Kettunen <jami.kettunen@...ainline.org>, 
 dri-devel@...ts.freedesktop.org, linux-kernel@...r.kernel.org, 
 devicetree@...r.kernel.org, linux-arm-msm@...r.kernel.org, 
 Abhinav Kumar <quic_abhinavk@...cinc.com>, 
 Kuogee Hsieh <quic_khsieh@...cinc.com>, 
 Jessica Zhang <quic_jesszhan@...cinc.com>, 
 AngeloGioacchino Del Regno <angelogioacchino.delregno@...labora.com>, 
 Konrad Dybcio <konrad.dybcio@....qualcomm.com>, 
 Marijn Suijten <marijn.suijten@...ainline.org>, 
 Konrad Dybcio <konradybcio@...nel.org>, Dmitry Baryshkov <lumag@...nel.org>
Subject: [PATCH v2 03/11] drm/panel: Add LGD LH599QH3-EDB1 panel driver for
 Sony Xperia XZ3

Sony provides an LGD LH599QH3-EDB1 panel + Atmel maXTouch assembly in
its Xperia XZ3 (tama akatsuki) phone, with custom DCS commands to match.

The panel is 1440x2880 pixels and runs at 60Hz.  It requires Display
Stream Compression 1.1 to be driven at that mode.

Signed-off-by: Marijn Suijten <marijn.suijten@...ainline.org>
---
 MAINTAINERS                                     |   1 +
 drivers/gpu/drm/panel/Kconfig                   |  16 ++
 drivers/gpu/drm/panel/Makefile                  |   1 +
 drivers/gpu/drm/panel/panel-lgd-lh599qh3-edb1.c | 340 ++++++++++++++++++++++++
 4 files changed, 358 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 6a358fee4cae..fcd99a8f9c71 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7935,6 +7935,7 @@ DRM DRIVER FOR LGD LH599QH3-EDB1 PANELS
 M:	Marijn Suijten <marijn.suijten@...ainline.org>
 S:	Maintained
 F:	Documentation/devicetree/bindings/display/panel/lgd,lh599qh3-edb1.yaml
+F:	drivers/gpu/drm/panel/panel-lgd-lh599qh3-edb1.c
 
 DRM DRIVER FOR LOGICVC DISPLAY CONTROLLER
 M:	Paul Kocialkowski <paulk@...-base.io>
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 9242fb894511..10381291707e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -442,6 +442,22 @@ config DRM_PANEL_LG_SW43408
 	  pixel. It provides a MIPI DSI interface to the host and has a
 	  built-in LED backlight.
 
+config DRM_PANEL_LGD_LH599QH3_EDB1
+	tristate "LGD LH599QH3-EDB1 DSI cmd mode panel"
+	depends on GPIOLIB
+	depends on OF
+	depends on DRM_MIPI_DSI
+	depends on BACKLIGHT_CLASS_DEVICE
+	select DRM_DISPLAY_DSC_HELPER
+	select DRM_DISPLAY_HELPER
+	help
+	  Say Y or M here if you want to enable support for the LGD LH599QH3-EDB1
+	  6.0" OLED DSI command-mode panel found in the Sony
+	  Xperia XZ3.
+
+	  This Display-IC uses Display Stream Compression 1.1 and features a
+	  fixed 1440x2880@60 mode.
+
 config DRM_PANEL_MAGNACHIP_D53E6EA8966
 	tristate "Magnachip D53E6EA8966 DSI panel"
 	depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index aeffaa95666d..186eb895af21 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_DRM_PANEL_LG_LB035Q02) += panel-lg-lb035q02.o
 obj-$(CONFIG_DRM_PANEL_LG_LD070WX3) += panel-lg-ld070wx3.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_LG_SW43408) += panel-lg-sw43408.o
+obj-$(CONFIG_DRM_PANEL_LGD_LH599QH3_EDB1) += panel-lgd-lh599qh3-edb1.o
 obj-$(CONFIG_DRM_PANEL_MAGNACHIP_D53E6EA8966) += panel-magnachip-d53e6ea8966.o
 obj-$(CONFIG_DRM_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o
 obj-$(CONFIG_DRM_PANEL_NEWVISION_NV3051D) += panel-newvision-nv3051d.o
diff --git a/drivers/gpu/drm/panel/panel-lgd-lh599qh3-edb1.c b/drivers/gpu/drm/panel/panel-lgd-lh599qh3-edb1.c
new file mode 100644
index 000000000000..02049b37729e
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-lgd-lh599qh3-edb1.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Marijn Suijten <marijn.suijten@...ainline.org>
+ *
+ * Based on the following Sony downstream DTS command sequence:
+ * https://github.com/sonyxperiadev/kernel-copyleft/blob/52.0.A.3.xxx/arch/arm64/boot/dts/somc/dsi-panel-akatsuki_vendor.dtsi
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dsc_helper.h>
+
+#define WRITE_CONTROL_DISPLAY_BACKLIGHT BIT(5)
+
+const struct regulator_bulk_data lgd_lh599qh3_edb1_supplies[] = {
+	{ .supply = "vddio", /* 1.8 V */ },
+	{ .supply = "avdd", /* 3.0 V */ },
+};
+
+struct lgd_lh599qh3_edb1 {
+	struct drm_panel panel;
+	struct mipi_dsi_device *dsi;
+	struct drm_dsc_config dsc;
+	struct regulator_bulk_data *supplies;
+	struct gpio_desc *reset_gpio;
+};
+
+static inline struct lgd_lh599qh3_edb1 *
+to_lgd_lh599qh3_edb1(struct drm_panel *panel)
+{
+	return container_of(panel, struct lgd_lh599qh3_edb1, panel);
+}
+
+static int lgd_lh599qh3_edb1_program(struct lgd_lh599qh3_edb1 *ctx)
+{
+	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+	dsi_ctx.dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf1, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x02, 0x01);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x59, 0x01);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+				     WRITE_CONTROL_DISPLAY_BACKLIGHT);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
+
+	mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, 1440 - 1);
+	mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, 2880 - 1);
+
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+	mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x7f, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf1, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x5a, 0x5a);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x03);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf6, 0x04);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x05);
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf6, 0x01, 0x7f, 0x00);
+
+	mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+	mipi_dsi_msleep(&dsi_ctx, 120);
+
+	mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
+
+	return 0;
+}
+
+static int lgd_lh599qh3_edb1_prepare(struct drm_panel *panel)
+{
+	struct lgd_lh599qh3_edb1 *ctx = to_lgd_lh599qh3_edb1(panel);
+	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+	struct drm_dsc_picture_parameter_set pps;
+	struct device *dev = &ctx->dsi->dev;
+	int ret;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(lgd_lh599qh3_edb1_supplies), ctx->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators: %d\n", ret);
+		return ret;
+	}
+
+	msleep(100);
+
+	gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+	usleep_range(5000, 5100);
+
+	ret = lgd_lh599qh3_edb1_program(ctx);
+	if (ret < 0) {
+		dev_err(dev, "Failed to program panel: %d\n", ret);
+		goto fail;
+	}
+
+	drm_dsc_pps_payload_pack(&pps, &ctx->dsc);
+
+	mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps);
+	mipi_dsi_compression_mode_multi(&dsi_ctx, true);
+	mipi_dsi_msleep(&dsi_ctx, 28);
+
+	ret = dsi_ctx.accum_err;
+
+	if (ret < 0)
+		goto fail;
+
+	return 0;
+
+fail:
+	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+	regulator_bulk_disable(ARRAY_SIZE(lgd_lh599qh3_edb1_supplies), ctx->supplies);
+	return ret;
+}
+
+static int lgd_lh599qh3_edb1_enable(struct drm_panel *panel)
+{
+	struct lgd_lh599qh3_edb1 *ctx = to_lgd_lh599qh3_edb1(panel);
+	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+	mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+
+	return dsi_ctx.accum_err;
+}
+
+static int lgd_lh599qh3_edb1_disable(struct drm_panel *panel)
+{
+	struct lgd_lh599qh3_edb1 *ctx = to_lgd_lh599qh3_edb1(panel);
+	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+	mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+	mipi_dsi_msleep(&dsi_ctx, 20);
+
+	return dsi_ctx.accum_err;
+}
+
+static int lgd_lh599qh3_edb1_unprepare(struct drm_panel *panel)
+{
+	struct lgd_lh599qh3_edb1 *ctx = to_lgd_lh599qh3_edb1(panel);
+	struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+	mipi_dsi_dcs_set_tear_off_multi(&dsi_ctx);
+	mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+	mipi_dsi_msleep(&dsi_ctx, 100);
+
+	dsi_ctx.dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+	gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+	regulator_bulk_disable(ARRAY_SIZE(lgd_lh599qh3_edb1_supplies), ctx->supplies);
+
+	usleep_range(5000, 5100);
+
+	return dsi_ctx.accum_err;
+}
+
+/*
+ * Small fake porch to force the DSI pclk/byteclk
+ * high enough to have a smooth panel at 60Hz.
+ */
+static const int fake_porch = 60;
+
+static const struct drm_display_mode lgd_lh599qh3_edb1_mode = {
+	.clock = (1440 + fake_porch) * 2880 * 60 / 1000,
+	.hdisplay = 1440,
+	.hsync_start = 1440 + fake_porch,
+	.hsync_end = 1440 + fake_porch,
+	.htotal = 1440 + fake_porch,
+	.vdisplay = 2880,
+	.vsync_start = 2880,
+	.vsync_end = 2880,
+	.vtotal = 2880,
+	.width_mm = 68,
+	.height_mm = 136,
+	.type = DRM_MODE_TYPE_DRIVER,
+};
+
+static int lgd_lh599qh3_edb1_get_modes(struct drm_panel *panel,
+				       struct drm_connector *connector)
+{
+	return drm_connector_helper_get_modes_fixed(connector,
+						    &lgd_lh599qh3_edb1_mode);
+}
+
+static const struct drm_panel_funcs lgd_lh599qh3_edb1_panel_funcs = {
+	.prepare = lgd_lh599qh3_edb1_prepare,
+	.enable = lgd_lh599qh3_edb1_enable,
+	.disable = lgd_lh599qh3_edb1_disable,
+	.unprepare = lgd_lh599qh3_edb1_unprepare,
+	.get_modes = lgd_lh599qh3_edb1_get_modes,
+};
+
+static int lgd_lh599qh3_edb1_bl_update_status(struct backlight_device *bl)
+{
+	struct mipi_dsi_device *dsi = bl_get_data(bl);
+	u16 brightness = backlight_get_brightness(bl);
+
+	return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+}
+
+static int lgd_lh599qh3_edb1_bl_get_brightness(struct backlight_device *bl)
+{
+	struct mipi_dsi_device *dsi = bl_get_data(bl);
+	u16 brightness;
+	int ret;
+
+	ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+	if (ret < 0)
+		return ret;
+
+	return brightness & 0x3ff;
+}
+
+static const struct backlight_ops lgd_lh599qh3_edb1_bl_ops = {
+	.update_status = lgd_lh599qh3_edb1_bl_update_status,
+	.get_brightness = lgd_lh599qh3_edb1_bl_get_brightness,
+};
+
+static int lgd_lh599qh3_edb1_probe(struct mipi_dsi_device *dsi)
+{
+	const struct backlight_properties props = {
+		.type = BACKLIGHT_RAW,
+		.brightness = 100,
+		.max_brightness = 1023,
+	};
+	struct device *dev = &dsi->dev;
+	struct lgd_lh599qh3_edb1 *ctx;
+	int ret;
+
+	ctx = devm_drm_panel_alloc(dev, struct lgd_lh599qh3_edb1, panel,
+				   &lgd_lh599qh3_edb1_panel_funcs,
+				   DRM_MODE_CONNECTOR_DSI);
+	if (IS_ERR(ctx))
+		return PTR_ERR(ctx);
+
+	ret = devm_regulator_bulk_get_const(
+		dev,
+		ARRAY_SIZE(lgd_lh599qh3_edb1_supplies),
+		lgd_lh599qh3_edb1_supplies,
+		&ctx->supplies);
+	if (ret < 0)
+		return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+	ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(ctx->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+				     "Failed to get reset-gpios\n");
+
+	ctx->dsi = dsi;
+	mipi_dsi_set_drvdata(dsi, ctx);
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ctx->panel.prepare_prev_first = true;
+
+	ctx->panel.backlight = devm_backlight_device_register(
+		dev, dev_name(dev), dev, dsi,
+		&lgd_lh599qh3_edb1_bl_ops,
+		&props);
+	if (IS_ERR(ctx->panel.backlight))
+		return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
+				     "Failed to create backlight\n");
+
+	drm_panel_add(&ctx->panel);
+
+	/* This panel only supports DSC; unconditionally enable it */
+	dsi->dsc = &ctx->dsc;
+
+	ctx->dsc.dsc_version_major = 1;
+	ctx->dsc.dsc_version_minor = 1;
+
+	ctx->dsc.slice_height = 32;
+	ctx->dsc.slice_count = 2;
+	/*
+	 * hdisplay should be read from the selected mode once
+	 * it is passed back to drm_panel (in prepare?)
+	 */
+	WARN_ON(1440 % ctx->dsc.slice_count);
+	ctx->dsc.slice_width = 1440 / ctx->dsc.slice_count;
+	ctx->dsc.bits_per_component = 8;
+	ctx->dsc.bits_per_pixel = 8 << 4; /* 4 fractional bits */
+	ctx->dsc.block_pred_enable = true;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
+		drm_panel_remove(&ctx->panel);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void lgd_lh599qh3_edb1_remove(struct mipi_dsi_device *dsi)
+{
+	struct lgd_lh599qh3_edb1 *ctx = mipi_dsi_get_drvdata(dsi);
+	int ret;
+
+	ret = mipi_dsi_detach(dsi);
+	if (ret < 0)
+		dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret);
+
+	drm_panel_remove(&ctx->panel);
+}
+
+static const struct of_device_id lgd_lh599qh3_edb1_of_match[] = {
+	{ .compatible = "lgd,lh599qh3-edb1-um1" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, lgd_lh599qh3_edb1_of_match);
+
+static struct mipi_dsi_driver lgd_lh599qh3_edb1_driver = {
+	.probe = lgd_lh599qh3_edb1_probe,
+	.remove = lgd_lh599qh3_edb1_remove,
+	.driver = {
+		.name = "panel-lgd-lh599qh3-edb1",
+		.of_match_table = lgd_lh599qh3_edb1_of_match,
+	},
+};
+module_mipi_dsi_driver(lgd_lh599qh3_edb1_driver);
+
+MODULE_AUTHOR("Marijn Suijten <marijn.suijten@...ainline.org>");
+MODULE_DESCRIPTION("DRM panel driver for an LGD LH599QH3-EDB1 OLED assembly found in the Sony Xperia XZ3");
+MODULE_LICENSE("GPL");

-- 
2.52.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ