Sending in a bunch of almost-finished (hence RFC) Sony panel drivers
upon drm/msm request, to further discussions around DSC panels and DSI
pclk calculations. This brings support for the following Sony Xperia
devices:
- Xperia XZ3 (DSC)
- Xperia 1 (DSC)
- Xperia 5
- Xperia 10 II (shared with Xperia 5)
- Xperia 5 II (DSC, 120Hz)
And, since the XZ3 already has all the DT in place to enable the panels
on its smaller XZ2(c) siblings, an extra patch is included to add the
new compatible string and properties to this device .dts. DTS for other
boards/platforms will come later, after cleaning up preliminary patches
(e.g. DPU catalog additions, SoC/board DTS for the MDSS subsystem, etc).
- File- and compatible names:
None of my downstream sources describe the product name of the panels
used here; in few cases the Display-IC is known but for the Xperia XZ3
Xperia 1 we have to make-do with a vendor name only.
Naming suggestions definitely welcome; i.e. I'm especially not fond of
sony-griffin-samsung.c :)
- Panels/drivers featuring multiple modes
As brought up earlier in #freedreno drm_panel drivers can provide
multiple modes, but the selected mode is never communicated back to
the panel. This either has to be added to the driver, or the drivers
in question have to be converted to drm_bridges (suggestion from
#freedreno). That should allow us to select a mode at runtime, and
downstream even defines "fast paths" to switch from one mode to the
next (e.g. when only adjusting the refresh rate) without powering the
panel off and on again, which we can hopefully support too.
For now the choice between either mode has been hardcoded behind a
static const bool.
- DSC
Not much to discuss here except that "it works" after piecing together
various series on the lists. No dependencies to make this series
apply/compile, though.
- pclk
The brunt of the discussion is around getting these command mode
panels functioning at their desired 60Hz or 120Hz refresh rate without
tearing/artifacts, and without hacks. Part of that discussion around
DSC-specific timing adjustments is happening in [1], but the sofef01
(non-DSC) Driver-IC is also struggling on the Xperia 5 specifically,
as outlined in that specific patch. That is currently "addressed"
with a "porch hack" but should probably have some sort of overhead /
transfer time taken into account in the MSM DSI driver.
Let me know what the best place is to collate all the relevant info
(links to downstream panel DTS, outcomes with different patches and
tweaks, etc). A new fd.o drm/msm issue?
[1]: https://gitlab.freedesktop.org/drm/msm/-/issues/24#note_1917707
Signed-off-by: Marijn Suijten <[email protected]>
---
Marijn Suijten (10):
drm/panel: Clean up SOFEF00 config dependencies
dt-bindings: display: panel: Describe Sony Xperia XZ3's LGD panel
drm/panel: Add LGD panel driver for Sony Xperia XZ3
arm64: dts: qcom: sdm845-akatsuki: Configure OLED panel
dt-bindings: display: panel: Describe Samsung SOFEF01-M Display-IC
drm/panel/samsung-sofef01: Add panel driver for Sony Xperia 5 / 10 II
dt-bindings: display: panel: Describe Samsung SOFEF03-M Display-IC
drm/panel/samsung-sofef03: Add panel driver for Sony Xperia 5 II
dt-bindings: display: panel: Describe Sony Xperia 1 display
drm/panel/sony-griffin-samsung: Add panel driver for Sony Xperia 1
.../bindings/display/panel/samsung,sofef01-m.yaml | 109 ++++++
.../bindings/display/panel/samsung,sofef03-m.yaml | 73 ++++
.../bindings/display/panel/sony,akatsuki-lgd.yaml | 71 ++++
.../display/panel/sony,griffin-samsung.yaml | 73 ++++
.../dts/qcom/sdm845-sony-xperia-tama-akatsuki.dts | 9 +
drivers/gpu/drm/panel/Kconfig | 52 ++-
drivers/gpu/drm/panel/Makefile | 4 +
drivers/gpu/drm/panel/panel-samsung-sofef01.c | 360 ++++++++++++++++++
drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 +++++++++++++++++++++
drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++
drivers/gpu/drm/panel/panel-sony-griffin-samsung.c | 410 ++++++++++++++++++++
11 files changed, 1945 insertions(+), 1 deletion(-)
---
base-commit: dbd91ef4e91c1ce3a24429f5fb3876b7a0306733
change-id: 20230521-drm-panels-sony-3c5ac3218427
Best regards,
--
Marijn Suijten <[email protected]>
As per the config name this Display IC features a DSI command-mode
interface (or the command to switch to video mode is not
known/documented) and does not use any of the video-mode helper
utilities, hence should not select VIDEOMODE_HELPERS. In addition it
uses devm_gpiod_get() and related functions from GPIOLIB.
Fixes: 5933baa36e26 ("drm/panel/samsung-sofef00: Add panel for OnePlus 6/T devices")
Signed-off-by: Marijn Suijten <[email protected]>
---
drivers/gpu/drm/panel/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 2b9d6db7860ba..67ef898d133f2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -608,10 +608,10 @@ config DRM_PANEL_SAMSUNG_S6E8AA0
config DRM_PANEL_SAMSUNG_SOFEF00
tristate "Samsung sofef00/s6e3fc2x01 OnePlus 6/6T DSI cmd mode panels"
+ depends on GPIOLIB
depends on OF
depends on DRM_MIPI_DSI
depends on BACKLIGHT_CLASS_DEVICE
- select VIDEOMODE_HELPERS
help
Say Y or M here if you want to enable support for the Samsung AMOLED
command mode panels found in the OnePlus 6/6T smartphones.
--
2.40.1
Sony provides an unlabeled LGD + Atmel maXTouch assembly in its Xperia
XZ3 (tama akatsuki) phone, with custom DCS commands to match.
This panel features Display Stream Compression 1.1.
Signed-off-by: Marijn Suijten <[email protected]>
---
drivers/gpu/drm/panel/Kconfig | 11 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++++++++
3 files changed, 374 insertions(+)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 67ef898d133f2..18bd116e78a71 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
Say Y here if you want to enable support for the Sony ACX565AKM
800x600 3.5" panel (found on the Nokia N900).
+config DRM_PANEL_SONY_AKATSUKI_LGD
+ tristate "Sony Xperia XZ3 LGD panel"
+ depends on GPIOLIB && OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for the Sony Xperia XZ3
+ 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG Display.
+
+ This panel uses Display Stream Compression 1.1.
+
config DRM_PANEL_SONY_TD4353_JDI
tristate "Sony TD4353 JDI panel"
depends on GPIOLIB && OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index ff169781e82d7..85133f73558f3 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
+obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
new file mode 100644
index 0000000000000..f55788f963dab
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Marijn Suijten <[email protected]>
+ *
+ * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
+
+struct sony_akatsuki_lgd {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator *vddio;
+ struct gpio_desc *reset_gpio;
+ bool prepared;
+};
+
+static inline struct sony_akatsuki_lgd *to_sony_akatsuki_lgd(struct drm_panel *panel)
+{
+ return container_of(panel, struct sony_akatsuki_lgd, panel);
+}
+
+static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
+ /* Enable backlight control */
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
+ mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
+
+ ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set column address: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set page address: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set tear on: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
+ mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
+ mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(120);
+
+ mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display on: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sony_akatsuki_lgd_off(struct sony_akatsuki_lgd *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display off: %d\n", ret);
+ return ret;
+ }
+ msleep(20);
+
+ ret = mipi_dsi_dcs_set_tear_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set tear off: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(100);
+
+ return 0;
+}
+
+static int sony_akatsuki_lgd_prepare(struct drm_panel *panel)
+{
+ struct sony_akatsuki_lgd *ctx = to_sony_akatsuki_lgd(panel);
+ struct drm_dsc_picture_parameter_set pps;
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_enable(ctx->vddio);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
+ return ret;
+ }
+
+ msleep(100);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(5000, 5100);
+
+ ret = sony_akatsuki_lgd_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to power on panel: %d\n", ret);
+ goto fail;
+ }
+
+ if (ctx->dsi->dsc) {
+ drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
+
+ ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
+ if (ret < 0) {
+ dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
+ goto fail;
+ }
+ ret = mipi_dsi_compression_mode(ctx->dsi, true);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable compression mode: %d\n", ret);
+ goto fail;
+ }
+
+ msleep(28);
+ }
+
+ ctx->prepared = true;
+ return 0;
+
+fail:
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ regulator_disable(ctx->vddio);
+ return ret;
+}
+
+static int sony_akatsuki_lgd_unprepare(struct drm_panel *panel)
+{
+ struct sony_akatsuki_lgd *ctx = to_sony_akatsuki_lgd(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = sony_akatsuki_lgd_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to power off panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ regulator_disable(ctx->vddio);
+
+ usleep_range(5000, 5100);
+
+ ctx->prepared = false;
+ return 0;
+}
+
+static const struct drm_display_mode sony_akatsuki_lgd_mode = {
+ .clock = (1440 + 312 + 8 + 8) * (2880 + 48 + 8 + 8) * 60 / 1000,
+ .hdisplay = 1440,
+ .hsync_start = 1440 + 312,
+ .hsync_end = 1440 + 312 + 8,
+ .htotal = 1440 + 312 + 8 + 8,
+ .vdisplay = 2880,
+ .vsync_start = 2880 + 48,
+ .vsync_end = 2880 + 48 + 8,
+ .vtotal = 2880 + 48 + 8 + 8,
+ .width_mm = 68,
+ .height_mm = 136,
+};
+
+static int sony_akatsuki_lgd_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ return drm_connector_helper_get_modes_fixed(connector, &sony_akatsuki_lgd_mode);
+}
+
+static const struct drm_panel_funcs sony_akatsuki_lgd_panel_funcs = {
+ .prepare = sony_akatsuki_lgd_prepare,
+ .unprepare = sony_akatsuki_lgd_unprepare,
+ .get_modes = sony_akatsuki_lgd_get_modes,
+};
+
+static int sony_akatsuki_lgd_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 sony_akatsuki_lgd_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 sony_akatsuki_lgd_bl_ops = {
+ .update_status = sony_akatsuki_lgd_bl_update_status,
+ .get_brightness = sony_akatsuki_lgd_bl_get_brightness,
+};
+
+static int sony_akatsuki_lgd_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 sony_akatsuki_lgd *ctx;
+ struct drm_dsc_config *dsc;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vddio = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(ctx->vddio))
+ return dev_err_probe(dev, PTR_ERR(ctx->vddio),
+ "Failed to get vddio\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+ 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;
+
+ drm_panel_init(&ctx->panel, dev, &sony_akatsuki_lgd_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ctx->panel.backlight = devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &sony_akatsuki_lgd_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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
+ if (!dsc)
+ return -ENOMEM;
+
+ dsc->dsc_version_major = 1;
+ dsc->dsc_version_minor = 1;
+
+ dsc->slice_height = 32;
+ dsc->slice_count = 2;
+ // TODO: Get hdisplay from the mode
+ WARN_ON(1440 % dsc->slice_count);
+ dsc->slice_width = 1440 / dsc->slice_count;
+ dsc->bits_per_component = 8;
+ dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
+ 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 sony_akatsuki_lgd_remove(struct mipi_dsi_device *dsi)
+{
+ struct sony_akatsuki_lgd *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 sony_akatsuki_lgd_of_match[] = {
+ { .compatible = "sony,akatsuki-lgd" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sony_akatsuki_lgd_of_match);
+
+static struct mipi_dsi_driver sony_akatsuki_lgd_driver = {
+ .probe = sony_akatsuki_lgd_probe,
+ .remove = sony_akatsuki_lgd_remove,
+ .driver = {
+ .name = "panel-sony-akatsuki-lgd",
+ .of_match_table = sony_akatsuki_lgd_of_match,
+ },
+};
+module_mipi_dsi_driver(sony_akatsuki_lgd_driver);
+
+MODULE_AUTHOR("Marijn Suijten <[email protected]>");
+MODULE_DESCRIPTION("DRM panel driver for an unnamed LGD OLED panel found in the Sony Xperia XZ3");
+MODULE_LICENSE("GPL");
--
2.40.1
Set the right compatible and reset-gpios for the LG Display 1440x2880
OLED panel found on the Xperia XZ3. All other properties (e.g. vddio
regulator and pinctrl) remain the valid from the base DTSI.
Signed-off-by: Marijn Suijten <[email protected]>
---
arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama-akatsuki.dts | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama-akatsuki.dts b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama-akatsuki.dts
index 5d2052a0ff69f..87095520d5842 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama-akatsuki.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama-akatsuki.dts
@@ -37,11 +37,20 @@ &lab {
};
&panel {
+ compatible = "sony,akatsuki-lgd";
+
/* Akatsuki uses an OLED panel. */
/delete-property/ backlight;
/delete-property/ vsp-supply;
/delete-property/ vsn-supply;
/delete-property/ touch-reset-gpios;
+
+ /*
+ * Correct misnamed property in board DTSI, which cannot be
+ * renamed for backwards compatibility with sony,td4353-jdi.
+ */
+ /delete-property/ panel-reset-gpios;
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
};
&pmi8998_wled {
--
2.40.1
The Sony Xperia 1 (codename kumano griffin) features an unnamed 4k OLED
DSI cmd mode panel produced by Samsung. It can be driven in a
1644x3840@60 or 1096x2560@60 mode, and always has Display Stream
Compression 1.1 enabled.
Signed-off-by: Marijn Suijten <[email protected]>
---
drivers/gpu/drm/panel/Kconfig | 13 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-sony-griffin-samsung.c | 410 +++++++++++++++++++++
3 files changed, 424 insertions(+)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 8e2668153bce2..888b5152ca55e 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -743,6 +743,19 @@ config DRM_PANEL_SONY_AKATSUKI_LGD
This panel uses Display Stream Compression 1.1.
+config DRM_PANEL_SONY_GRIFFIN_SAMSUNG
+ tristate "Sony Xperia 1 4k OLED panel"
+ depends on GPIOLIB && OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y here if you want to enable support for the Sony Xperia 1 4k
+ 6.5" OLED DSI cmd mode panel produced by Samsung.
+
+ This panel uses Display Stream Compression 1.1.
+
+ The panel features a 1644x3840@60 and 1096x2560@60 mode.
+
config DRM_PANEL_SONY_TD4353_JDI
tristate "Sony TD4353 JDI panel"
depends on GPIOLIB && OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 52dcd82e33120..734d32987330d 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
+obj-$(CONFIG_DRM_PANEL_SONY_GRIFFIN_SAMSUNG) += panel-sony-griffin-samsung.o
obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
diff --git a/drivers/gpu/drm/panel/panel-sony-griffin-samsung.c b/drivers/gpu/drm/panel/panel-sony-griffin-samsung.c
new file mode 100644
index 0000000000000..755cec7f9119b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sony-griffin-samsung.c
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023 Marijn Suijten <[email protected]>
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.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>
+
+static const bool enable_4k = true;
+
+struct sony_griffin_samsung {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct gpio_desc *reset_gpio;
+ struct regulator *vddio, *vci;
+ bool prepared;
+};
+
+static inline
+struct sony_griffin_samsung *to_sony_griffin_samsung(struct drm_panel *panel)
+{
+ return container_of(panel, struct sony_griffin_samsung, panel);
+}
+
+static void sony_griffin_samsung_reset(struct sony_griffin_samsung *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static int sony_griffin_samsung_on(struct sony_griffin_samsung *ctx)
+{
+ const u16 hdisplay = enable_4k ? 1644 : 1096;
+ const u16 vdisplay = enable_4k ? 3840 : 2560;
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ usleep_range(10000, 11000);
+
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set tear on: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
+ mipi_dsi_dcs_write_seq(dsi, 0xd7, 0x07);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ /* Enable backlight control */
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
+ msleep(110);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xe2, enable_4k ? 0 : 1);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+
+ ret = mipi_dsi_dcs_set_column_address(dsi, 0, hdisplay - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set column address: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_page_address(dsi, 0, vdisplay - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set page address: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x70);
+ mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0x60);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xc5, 0x2e, 0x21);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display on: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sony_griffin_samsung_off(struct sony_griffin_samsung *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display off: %d\n", ret);
+ return ret;
+ }
+ msleep(20);
+
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
+ usleep_range(17000, 18000);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(100);
+
+ return 0;
+}
+
+static int sony_griffin_samsung_prepare(struct drm_panel *panel)
+{
+ struct sony_griffin_samsung *ctx = to_sony_griffin_samsung(panel);
+ struct drm_dsc_picture_parameter_set pps;
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_enable(ctx->vddio);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(ctx->vci);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
+ regulator_disable(ctx->vddio);
+ return ret;
+ }
+
+ sony_griffin_samsung_reset(ctx);
+
+ ret = sony_griffin_samsung_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ goto fail;
+ }
+
+ if (ctx->dsi->dsc) {
+ drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
+
+ ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
+ if (ret < 0) {
+ dev_err(dev, "failed to transmit PPS: %d\n", ret);
+ goto fail;
+ }
+
+ ret = mipi_dsi_compression_mode(ctx->dsi, true);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable compression mode: %d\n", ret);
+ goto fail;
+ }
+
+ msleep(28);
+ }
+
+ ctx->prepared = true;
+ return 0;
+
+fail:
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_disable(ctx->vci);
+ regulator_disable(ctx->vddio);
+ return ret;
+}
+
+static int sony_griffin_samsung_unprepare(struct drm_panel *panel)
+{
+ struct sony_griffin_samsung *ctx = to_sony_griffin_samsung(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = sony_griffin_samsung_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_disable(ctx->vddio);
+ regulator_disable(ctx->vci);
+
+ ctx->prepared = false;
+ return 0;
+}
+
+static const struct drm_display_mode sony_griffin_samsung_2_5k_mode = {
+ .clock = (1096 + 56 + 8 + 8) * (2560 + 8 + 8 + 8) * 60 / 1000,
+ .hdisplay = 1096,
+ .hsync_start = 1096 + 56,
+ .hsync_end = 1096 + 56 + 8,
+ .htotal = 1096 + 56 + 8 + 8,
+ .vdisplay = 2560,
+ .vsync_start = 2560 + 8,
+ .vsync_end = 2560 + 8 + 8,
+ .vtotal = 2560 + 8 + 8 + 8,
+ .width_mm = 65,
+ .height_mm = 152,
+};
+
+static const struct drm_display_mode sony_griffin_samsung_4k_mode = {
+ .clock = (1644 + 60 + 8 + 8) * (3840 + 8 + 8 + 8) * 60 / 1000,
+ .hdisplay = 1644,
+ .hsync_start = 1644 + 60,
+ .hsync_end = 1644 + 60 + 8,
+ .htotal = 1644 + 60 + 8 + 8,
+ .vdisplay = 3840,
+ .vsync_start = 3840 + 8,
+ .vsync_end = 3840 + 8 + 8,
+ .vtotal = 3840 + 8 + 8 + 8,
+ .width_mm = 65,
+ .height_mm = 152,
+};
+
+static int sony_griffin_samsung_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ if (enable_4k)
+ return drm_connector_helper_get_modes_fixed(connector,
+ &sony_griffin_samsung_4k_mode);
+ else
+ return drm_connector_helper_get_modes_fixed(connector,
+ &sony_griffin_samsung_2_5k_mode);
+}
+
+static const struct drm_panel_funcs sony_griffin_samsung_panel_funcs = {
+ .prepare = sony_griffin_samsung_prepare,
+ .unprepare = sony_griffin_samsung_unprepare,
+ .get_modes = sony_griffin_samsung_get_modes,
+};
+
+static int sony_griffin_samsung_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return ret;
+}
+
+static int sony_griffin_samsung_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ if (ret < 0)
+ return ret;
+
+ return brightness;
+}
+
+static const struct backlight_ops sony_griffin_samsung_bl_ops = {
+ .update_status = sony_griffin_samsung_bl_update_status,
+ .get_brightness = sony_griffin_samsung_bl_get_brightness,
+};
+
+static struct backlight_device *
+sony_griffin_samsung_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 400,
+ .max_brightness = 4095,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &sony_griffin_samsung_bl_ops, &props);
+}
+
+static int sony_griffin_samsung_probe(struct mipi_dsi_device *dsi)
+{
+ const u16 hdisplay = enable_4k ? 1644 : 1096;
+ struct device *dev = &dsi->dev;
+ struct sony_griffin_samsung *ctx;
+ struct drm_dsc_config *dsc;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vddio = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(ctx->vddio))
+ return dev_err_probe(dev, PTR_ERR(ctx->vddio),
+ "Failed to get vddio regulator\n");
+
+ ctx->vci = devm_regulator_get(dev, "vci");
+ if (IS_ERR(ctx->vci))
+ return dev_err_probe(dev, PTR_ERR(ctx->vci),
+ "Failed to get vci regulator\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;
+
+ drm_panel_init(&ctx->panel, dev, &sony_griffin_samsung_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ctx->panel.backlight = sony_griffin_samsung_create_backlight(dsi);
+ 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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
+ if (!dsc)
+ return -ENOMEM;
+
+ dsc->dsc_version_major = 1;
+ dsc->dsc_version_minor = 1;
+
+ dsc->slice_height = 32;
+ dsc->slice_count = 2;
+ WARN_ON(hdisplay % dsc->slice_count);
+ dsc->slice_width = hdisplay / dsc->slice_count;
+ dsc->bits_per_component = 8;
+ dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
+ 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 sony_griffin_samsung_remove(struct mipi_dsi_device *dsi)
+{
+ struct sony_griffin_samsung *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 sony_griffin_samsung_of_match[] = {
+ { .compatible = "sony,griffin-samsung-4k-oled" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sony_griffin_samsung_of_match);
+
+static struct mipi_dsi_driver sony_griffin_samsung_driver = {
+ .probe = sony_griffin_samsung_probe,
+ .remove = sony_griffin_samsung_remove,
+ .driver = {
+ .name = "panel-sony-griffin-samsung-4k-oled",
+ .of_match_table = sony_griffin_samsung_of_match,
+ },
+};
+module_mipi_dsi_driver(sony_griffin_samsung_driver);
+
+MODULE_AUTHOR("Marijn Suijten <[email protected]>");
+MODULE_DESCRIPTION("DRM panel driver for an unnamed Samsung 4k OLED panel found in the Sony Xperia 1");
+MODULE_LICENSE("GPL");
--
2.40.1
Document an unnamed Samsung Display-IC and 1644x3840@60 6.5" panel found
in the Sony Xperia 1.
Signed-off-by: Marijn Suijten <[email protected]>
---
.../display/panel/sony,griffin-samsung.yaml | 73 ++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/sony,griffin-samsung.yaml b/Documentation/devicetree/bindings/display/panel/sony,griffin-samsung.yaml
new file mode 100644
index 0000000000000..24059d45331c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/sony,griffin-samsung.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/sony,griffin-samsung-4k-oled.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony Xperia 1 Samsung 1644x3840 6.5" OLED DSI panel
+
+maintainers:
+ - Marijn Suijten <[email protected]>
+
+description: |
+ Unnamed Samsung Display-IC and panel found in the Sony Xperia 1
+ (kumano griffin) smartphone.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - sony,griffin-samsung-4k-oled
+
+ port: true
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel
+
+ reset-gpios: true
+
+ vci-supply:
+ description: DriverIC Operation supply (3.0V)
+
+ vddio-supply:
+ description: I/O voltage supply (1.8V)
+
+required:
+ - compatible
+ - port
+ - reg
+ - reset-gpios
+ - vci-supply
+ - vddio-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "sony,griffin-samsung-4k-oled";
+ reg = <0>;
+
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
+
+ vci-supply = <&vreg_l17a_3p0>;
+ vddio-supply = <&vreg_l14a_1p8>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
+
--
2.40.1
This SOFEF01-M Display-IC driver supports two modes with different
compatibles to differentiate between slightly different physical sizes
(panels) found on the Xperia 5 (6.1") and 10 II (6.0").
It is currently also used to hardcode significantly higher fake porches
for the Xperia 5, which are unused in transfers due to this being a
command-mode panel but do have an effect on the clock rates set by
dsi_host.c. Without higher clock rates this panel fails to achieve
60fps and has significant tearing artifacts, while the same calculated
clock rate works perfectly fine on the Xperia 10 II.
Signed-off-by: Marijn Suijten <[email protected]>
---
drivers/gpu/drm/panel/Kconfig | 12 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-samsung-sofef01.c | 360 ++++++++++++++++++++++++++
3 files changed, 373 insertions(+)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 18bd116e78a71..3f11e9906f2cb 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -618,6 +618,18 @@ config DRM_PANEL_SAMSUNG_SOFEF00
The panels are 2280x1080@60Hz and 2340x1080@60Hz respectively
+config DRM_PANEL_SAMSUNG_SOFEF01
+ tristate "Samsung sofef01 Sony Xperia 5 / 10 II DSI cmd mode panels"
+ depends on GPIOLIB
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y or M here if you want to enable support for the Samsung AMOLED
+ command mode panels found in the Sony Xperia 5 / 10 II smartphones.
+
+ This panel features a fixed mode of 1080x2520@60.
+
config DRM_PANEL_SEIKO_43WVF1G
tristate "Seiko 43WVF1G panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index 85133f73558f3..a4039d0fc9cfb 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef01.c b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
new file mode 100644
index 0000000000000..18dc67a301a7b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
@@ -0,0 +1,360 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023 Marijn Suijten <[email protected]>
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.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>
+
+struct samsung_sofef01_m {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator *vddio, *vci;
+ struct gpio_desc *reset_gpio;
+ const struct drm_display_mode *mode;
+ bool prepared;
+};
+
+static inline struct samsung_sofef01_m *to_samsung_sofef01_m(struct drm_panel *panel)
+{
+ return container_of(panel, struct samsung_sofef01_m, panel);
+}
+
+static void samsung_sofef01_m_reset(struct samsung_sofef01_m *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static int samsung_sofef01_m_on(struct samsung_sofef01_m *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ usleep_range(10000, 11000);
+
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set tear on: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x03);
+ mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+
+ ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2520 - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set page address: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x92, 0x29);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x06);
+ mipi_dsi_dcs_write_seq(dsi, 0xb6, 0x90);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ msleep(110);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display on: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int samsung_sofef01_m_off(struct samsung_sofef01_m *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display off: %d\n", ret);
+ return ret;
+ }
+ msleep(20);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(120);
+
+ return 0;
+}
+
+static int samsung_sofef01_m_prepare(struct drm_panel *panel)
+{
+ struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_enable(ctx->vddio);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(ctx->vci);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
+ regulator_disable(ctx->vddio);
+ return ret;
+ }
+
+ samsung_sofef01_m_reset(ctx);
+
+ ret = samsung_sofef01_m_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_disable(ctx->vci);
+ regulator_disable(ctx->vddio);
+ return ret;
+ }
+
+ ctx->prepared = true;
+ return 0;
+}
+
+static int samsung_sofef01_m_unprepare(struct drm_panel *panel)
+{
+ struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = samsung_sofef01_m_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_disable(ctx->vci);
+ regulator_disable(ctx->vddio);
+
+ ctx->prepared = false;
+ return 0;
+}
+
+static int samsung_sofef01_m_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
+
+ return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
+}
+
+static const struct drm_panel_funcs samsung_sofef01_m_panel_funcs = {
+ .prepare = samsung_sofef01_m_prepare,
+ .unprepare = samsung_sofef01_m_unprepare,
+ .get_modes = samsung_sofef01_m_get_modes,
+};
+
+static int samsung_sofef01_m_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static int samsung_sofef01_m_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ if (ret < 0)
+ return ret;
+
+ return brightness;
+}
+
+static const struct backlight_ops samsung_sofef01_m_bl_ops = {
+ .update_status = samsung_sofef01_m_bl_update_status,
+ .get_brightness = samsung_sofef01_m_bl_get_brightness,
+};
+
+static struct backlight_device *
+samsung_sofef01_m_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 100,
+ .max_brightness = 1023,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &samsung_sofef01_m_bl_ops, &props);
+}
+
+static int samsung_sofef01_m_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct samsung_sofef01_m *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vddio = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(ctx->vddio))
+ return dev_err_probe(dev, PTR_ERR(ctx->vddio),
+ "Failed to get vddio regulator\n");
+
+ ctx->vci = devm_regulator_get(dev, "vci");
+ if (IS_ERR(ctx->vci))
+ return dev_err_probe(dev, PTR_ERR(ctx->vci),
+ "Failed to get vci regulator\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;
+ ctx->mode = of_device_get_match_data(dev);
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+ drm_panel_init(&ctx->panel, dev, &samsung_sofef01_m_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ctx->panel.backlight = samsung_sofef01_m_create_backlight(dsi);
+ 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);
+
+ 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 samsung_sofef01_m_remove(struct mipi_dsi_device *dsi)
+{
+ struct samsung_sofef01_m *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);
+}
+
+/* Sony Xperia 5 (kumano bahamut) */
+static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
+ /*
+ * WARNING: These massive porches are wrong/useless for CMDmode
+ * (and not defined in downstream DTS) but necessary to bump dsi
+ * clocks higher, so that we can achieve proper 60fps without tearing.
+ */
+ .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 156,
+ .hsync_end = 1080 + 156 + 8,
+ .htotal = 1080 + 156 + 8 + 8,
+ .vdisplay = 2520,
+ .vsync_start = 2520 + 2393,
+ .vsync_end = 2520 + 2393 + 8,
+ .vtotal = 2520 + 2393 + 8 + 8,
+ .width_mm = 61,
+ .height_mm = 142,
+};
+
+/* Sony Xperia 10 II (seine pdx201) */
+static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
+ .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 8,
+ .hsync_end = 1080 + 8 + 8,
+ .htotal = 1080 + 8 + 8 + 8,
+ .vdisplay = 2520,
+ .vsync_start = 2520 + 8,
+ .vsync_end = 2520 + 8 + 8,
+ .vtotal = 2520 + 8 + 8 + 8,
+ .width_mm = 60,
+ .height_mm = 139,
+};
+
+static const struct of_device_id samsung_sofef01_m_of_match[] = {
+ { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
+ { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, samsung_sofef01_m_of_match);
+
+static struct mipi_dsi_driver samsung_sofef01_m_driver = {
+ .probe = samsung_sofef01_m_probe,
+ .remove = samsung_sofef01_m_remove,
+ .driver = {
+ .name = "panel-samsung-sofef01-m",
+ .of_match_table = samsung_sofef01_m_of_match,
+ },
+};
+module_mipi_dsi_driver(samsung_sofef01_m_driver);
+
+MODULE_AUTHOR("Marijn Suijten <[email protected]>");
+MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF01-M Display-IC panels");
+MODULE_LICENSE("GPL");
--
2.40.1
Document the SOFEF01-M Display-IC and 1080x2520 panels found in the Sony
Xperia 5 (6.1") and Sony Xperia 10 II (6.0").
Signed-off-by: Marijn Suijten <[email protected]>
---
.../bindings/display/panel/samsung,sofef01-m.yaml | 109 +++++++++++++++++++++
1 file changed, 109 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,sofef01-m.yaml b/Documentation/devicetree/bindings/display/panel/samsung,sofef01-m.yaml
new file mode 100644
index 0000000000000..a91bc33568f43
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/samsung,sofef01-m.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/samsung,sofef01-m.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung SOFEF01-M 1080x2520 6.0"/6.1" OLED DSI panels
+
+maintainers:
+ - Marijn Suijten <[email protected]>
+
+description: |
+ Samsung SOFEF01-M Display-IC panels found in the Sony Xperia 5
+ (kumano bahamut) and Sony Xperia 10 II (seine pdx201) smartphones.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+ - if:
+ properties:
+ compatible:
+ const: samsung,sofef01-m-bahamut
+
+ then:
+ properties:
+ vci-supply:
+ description: DriverIC Operation supply (3.0V)
+
+ required:
+ - vci-supply
+
+ else:
+ properties:
+ vci-supply: false
+
+properties:
+ compatible:
+ enum:
+ - samsung,sofef01-m-bahamut # 6.1"
+ - samsung,sofef01-m-pdx201 # 6.0"
+
+ port: true
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel
+
+ reset-gpios: true
+
+ vddio-supply:
+ description: I/O voltage supply (1.8V)
+
+required:
+ - compatible
+ - port
+ - reg
+ - reset-gpios
+ - vddio-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "samsung,sofef01-m-bahamut";
+ reg = <0>;
+
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_LOW>;
+
+ vci-supply = <&vreg_l17a_3p0>;
+ vddio-supply = <&vreg_l14a_1p8>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "samsung,sofef01-m-pdx201";
+ reg = <0>;
+
+ reset-gpios = <&tlmm 90 GPIO_ACTIVE_LOW>;
+
+ vddio-supply = <&pm6125_l12>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
+
--
2.40.1
Document the SOFEF06-M Display-IC and 1080x2520 panel found in the Sony
Xperia 5 II (6.1").
Signed-off-by: Marijn Suijten <[email protected]>
---
.../bindings/display/panel/samsung,sofef03-m.yaml | 73 ++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/samsung,sofef03-m.yaml b/Documentation/devicetree/bindings/display/panel/samsung,sofef03-m.yaml
new file mode 100644
index 0000000000000..617218dfe8b3f
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/samsung,sofef03-m.yaml
@@ -0,0 +1,73 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/samsung,sofef03-m.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Samsung SOFEF03-M 1080x2520 6.1" OLED DSI panel
+
+maintainers:
+ - Marijn Suijten <[email protected]>
+
+description: |
+ Samsung SOFEF03-M Display-IC panel found in the Sony Xperia 5 II
+ (edo pdx206) smartphone.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - samsung,sofef03-m
+
+ port: true
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel
+
+ reset-gpios: true
+
+ vci-supply:
+ description: DriverIC Operation supply (3.0V)
+
+ vddio-supply:
+ description: I/O voltage supply (1.8V)
+
+required:
+ - compatible
+ - port
+ - reg
+ - reset-gpios
+ - vci-supply
+ - vddio-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "samsung,sofef03-m";
+ reg = <0>;
+
+ reset-gpios = <&tlmm 75 GPIO_ACTIVE_LOW>;
+
+ vci-supply = <&vreg_l11c_3p0>;
+ vddio-supply = <&vreg_l14a_1p8>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
+
--
2.40.1
Document the LG-Display OLED panel found in the Sony Xperia XZ3.
Signed-off-by: Marijn Suijten <[email protected]>
---
.../bindings/display/panel/sony,akatsuki-lgd.yaml | 71 ++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/panel/sony,akatsuki-lgd.yaml b/Documentation/devicetree/bindings/display/panel/sony,akatsuki-lgd.yaml
new file mode 100644
index 0000000000000..523e580b70c7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/panel/sony,akatsuki-lgd.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/sony,akatsuki-lgd.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony Xperia XZ3 LG Display 1440x2880 6.0" OLED DSI panel
+
+maintainers:
+ - Marijn Suijten <[email protected]>
+
+description: |
+ This is an unnamed 6.0" 1440x2880 (9:18 aspect ratio) 60 hz panel
+ produced by LG Display, found in the Sony Xperia XZ3 smartphone.
+ It is always programmed with DSI 1.1. enabled.
+
+ The assembly features an Atmel maXTouch digitizer, described separately
+ as atmel,maxtouch.
+
+allOf:
+ - $ref: panel-common.yaml#
+
+properties:
+ compatible:
+ const: sony,akatsuki-lgd
+
+ port: true
+
+ reg:
+ maxItems: 1
+ description: DSI virtual channel
+
+ reset-gpios: true
+
+ vddio-supply:
+ description: I/O voltage supply (1.8V)
+
+required:
+ - compatible
+ - port
+ - reg
+ - reset-gpios
+ - vddio-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ dsi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ panel@0 {
+ compatible = "sony,akatsuki-lgd";
+ reg = <0>;
+
+ vddio-supply = <&vreg_l14a_1p8>;
+
+ reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
+
+ port {
+ panel_in: endpoint {
+ remote-endpoint = <&dsi0_out>;
+ };
+ };
+ };
+ };
+
+...
+
--
2.40.1
The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
5 II always uses Display Stream Compression 1.1 and features a 60hz and
120hz refresh-rate mode.
Co-developed-by: Konrad Dybcio <[email protected]>
Signed-off-by: Marijn Suijten <[email protected]>
---
drivers/gpu/drm/panel/Kconfig | 14 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
3 files changed, 438 insertions(+)
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 3f11e9906f2cb..8e2668153bce2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
This panel features a fixed mode of 1080x2520@60.
+config DRM_PANEL_SAMSUNG_SOFEF03
+ tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
+ depends on GPIOLIB
+ depends on OF
+ depends on DRM_MIPI_DSI
+ depends on BACKLIGHT_CLASS_DEVICE
+ help
+ Say Y or M here if you want to enable support for the Samsung AMOLED
+ command mode panel found in the Sony Xperia 5 II smartphone.
+
+ This panel uses Display Stream Compression 1.1.
+
+ The panel features a 1080x2520@60 and 1080x2520@120 mode.
+
config DRM_PANEL_SEIKO_43WVF1G
tristate "Seiko 43WVF1G panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index a4039d0fc9cfb..52dcd82e33120 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
+obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
new file mode 100644
index 0000000000000..2763e1c56b37b
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022 Konrad Dybcio <[email protected]>
+ * Copyright (c) 2023 Marijn Suijten <[email protected]>
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.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>
+
+static const bool enable_120hz = true;
+
+struct samsung_sofef03_m {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator *vddio, *vci;
+ struct gpio_desc *reset_gpio;
+ bool prepared;
+};
+
+static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
+{
+ return container_of(panel, struct samsung_sofef03_m, panel);
+}
+
+static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
+{
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
+
+ ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+ return ret;
+ }
+ usleep_range(10000, 11000);
+
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
+ mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
+ mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+
+ ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set tear on: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set column address: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set page address: %d\n", ret);
+ return ret;
+ }
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
+ if (ret < 0) {
+ dev_err(dev, "Failed to set display brightness: %d\n", ret);
+ return ret;
+ }
+
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
+ mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
+ mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
+ mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
+ mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
+ mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
+ msleep(110);
+
+ ret = mipi_dsi_dcs_set_display_on(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display on: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
+{
+ struct mipi_dsi_device *dsi = ctx->dsi;
+ struct device *dev = &dsi->dev;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_set_display_off(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to turn display off: %d\n", ret);
+ return ret;
+ }
+ msleep(20);
+
+ ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
+ return ret;
+ }
+ msleep(100);
+
+ return 0;
+}
+
+static int samsung_sofef03_m_prepare(struct drm_panel *panel)
+{
+ struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
+ struct drm_dsc_picture_parameter_set pps;
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (ctx->prepared)
+ return 0;
+
+ ret = regulator_enable(ctx->vddio);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(ctx->vci);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
+ regulator_disable(ctx->vddio);
+ return ret;
+ }
+
+ samsung_sofef03_m_reset(ctx);
+
+ ret = samsung_sofef03_m_on(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize panel: %d\n", ret);
+ goto fail;
+ }
+
+ if (ctx->dsi->dsc) {
+ drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
+
+ ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
+ if (ret < 0) {
+ dev_err(dev, "failed to transmit PPS: %d\n", ret);
+ goto fail;
+ }
+
+ ret = mipi_dsi_compression_mode(ctx->dsi, true);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable compression mode: %d\n", ret);
+ goto fail;
+ }
+
+ msleep(28);
+ }
+
+ ctx->prepared = true;
+ return 0;
+
+fail:
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_disable(ctx->vci);
+ regulator_disable(ctx->vddio);
+ return ret;
+}
+
+static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
+{
+ struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
+ struct device *dev = &ctx->dsi->dev;
+ int ret;
+
+ if (!ctx->prepared)
+ return 0;
+
+ ret = samsung_sofef03_m_off(ctx);
+ if (ret < 0)
+ dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_disable(ctx->vci);
+ regulator_disable(ctx->vddio);
+
+ ctx->prepared = false;
+ return 0;
+}
+
+static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
+ .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 156,
+ .hsync_end = 1080 + 156 + 8,
+ .htotal = 1080 + 156 + 8 + 8,
+ .vdisplay = 2520,
+ .vsync_start = 2520 + 2393,
+ .vsync_end = 2520 + 2393 + 8,
+ .vtotal = 2520 + 2393 + 8 + 8,
+ .width_mm = 61,
+ .height_mm = 142,
+};
+
+static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
+ .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + 56,
+ .hsync_end = 1080 + 56 + 8,
+ .htotal = 1080 + 56 + 8 + 8,
+ .vdisplay = 2520,
+ .vsync_start = 2520 + 499,
+ .vsync_end = 2520 + 499 + 8,
+ .vtotal = 2520 + 499 + 8 + 8,
+ .width_mm = 61,
+ .height_mm = 142,
+};
+
+static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
+ struct drm_connector *connector)
+{
+ if (enable_120hz)
+ return drm_connector_helper_get_modes_fixed(connector,
+ &samsung_sofef03_m_120hz_mode);
+ else
+ return drm_connector_helper_get_modes_fixed(connector,
+ &samsung_sofef03_m_60hz_mode);
+}
+
+static const struct drm_panel_funcs samsung_sofef03_m_panel_funcs = {
+ .prepare = samsung_sofef03_m_prepare,
+ .unprepare = samsung_sofef03_m_unprepare,
+ .get_modes = samsung_sofef03_m_get_modes,
+};
+
+static int samsung_sofef03_m_bl_update_status(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness = backlight_get_brightness(bl);
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ pr_err("Writing %#x\n", brightness);
+
+ ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ return 0;
+}
+
+static int samsung_sofef03_m_bl_get_brightness(struct backlight_device *bl)
+{
+ struct mipi_dsi_device *dsi = bl_get_data(bl);
+ u16 brightness;
+ int ret;
+
+ dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+ if (ret < 0)
+ return ret;
+
+ dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ pr_err("Read display brightness %#x\n", brightness);
+
+ return brightness;
+}
+
+static const struct backlight_ops samsung_sofef03_m_bl_ops = {
+ .update_status = samsung_sofef03_m_bl_update_status,
+ .get_brightness = samsung_sofef03_m_bl_get_brightness,
+};
+
+static struct backlight_device *
+samsung_sofef03_m_create_backlight(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ const struct backlight_properties props = {
+ .type = BACKLIGHT_RAW,
+ .brightness = 100,
+ .max_brightness = 1023,
+ };
+
+ return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+ &samsung_sofef03_m_bl_ops, &props);
+}
+
+static int samsung_sofef03_m_probe(struct mipi_dsi_device *dsi)
+{
+ struct device *dev = &dsi->dev;
+ struct drm_dsc_config *dsc;
+ struct samsung_sofef03_m *ctx;
+ int ret;
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vddio = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(ctx->vddio))
+ return dev_err_probe(dev, PTR_ERR(ctx->vddio),
+ "Failed to get vddio regulator\n");
+
+ ctx->vci = devm_regulator_get(dev, "vci");
+ if (IS_ERR(ctx->vci))
+ return dev_err_probe(dev, PTR_ERR(ctx->vci),
+ "Failed to get vci regulator\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;
+
+ drm_panel_init(&ctx->panel, dev, &samsung_sofef03_m_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
+ ctx->panel.backlight = samsung_sofef03_m_create_backlight(dsi);
+ 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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
+ if (!dsc)
+ return -ENOMEM;
+
+ dsc->dsc_version_major = 1;
+ dsc->dsc_version_minor = 1;
+
+ dsc->slice_height = 30;
+ dsc->slice_width = 540;
+ dsc->slice_count = 2;
+ dsc->bits_per_component = 8;
+ dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
+ 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 samsung_sofef03_m_remove(struct mipi_dsi_device *dsi)
+{
+ struct samsung_sofef03_m *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 samsung_sofef03_m_of_match[] = {
+ { .compatible = "samsung,sofef03-m" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, samsung_sofef03_m_of_match);
+
+static struct mipi_dsi_driver samsung_sofef03_m_driver = {
+ .probe = samsung_sofef03_m_probe,
+ .remove = samsung_sofef03_m_remove,
+ .driver = {
+ .name = "panel-samsung-sofef03-m",
+ .of_match_table = samsung_sofef03_m_of_match,
+ },
+};
+module_mipi_dsi_driver(samsung_sofef03_m_driver);
+
+MODULE_AUTHOR("Konrad Dybcio <[email protected]>");
+MODULE_AUTHOR("Marijn Suijten <[email protected]>");
+MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF03-M Display-IC panels");
+MODULE_LICENSE("GPL");
--
2.40.1
On Sun, 21 May 2023 23:23:07 +0200, Marijn Suijten wrote:
> Document the SOFEF01-M Display-IC and 1080x2520 panels found in the Sony
> Xperia 5 (6.1") and Sony Xperia 10 II (6.0").
>
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> .../bindings/display/panel/samsung,sofef01-m.yaml | 109 +++++++++++++++++++++
> 1 file changed, 109 insertions(+)
>
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/panel/samsung,sofef01-m.example.dtb: panel@0: 'vci-supply' does not match any of the regexes: 'pinctrl-[0-9]+'
From schema: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/display/panel/samsung,sofef01-m.yaml
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
On Sun, 21 May 2023 23:23:11 +0200, Marijn Suijten wrote:
> Document an unnamed Samsung Display-IC and 1644x3840@60 6.5" panel found
> in the Sony Xperia 1.
>
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> .../display/panel/sony,griffin-samsung.yaml | 73 ++++++++++++++++++++++
> 1 file changed, 73 insertions(+)
>
My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check'
on your patch (DT_CHECKER_FLAGS is new in v5.13):
yamllint warnings/errors:
dtschema/dtc warnings/errors:
./Documentation/devicetree/bindings/display/panel/sony,griffin-samsung.yaml: $id: relative path/filename doesn't match actual path or filename
expected: http://devicetree.org/schemas/display/panel/sony,griffin-samsung.yaml#
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/[email protected]
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
On 22/05/2023 00:23, Marijn Suijten wrote:
> Document the LG-Display OLED panel found in the Sony Xperia XZ3.
According to find chineese market this is LG LH599QH3-EDB1
>
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> .../bindings/display/panel/sony,akatsuki-lgd.yaml | 71 ++++++++++++++++++++++
> 1 file changed, 71 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/display/panel/sony,akatsuki-lgd.yaml b/Documentation/devicetree/bindings/display/panel/sony,akatsuki-lgd.yaml
> new file mode 100644
> index 0000000000000..523e580b70c7d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/panel/sony,akatsuki-lgd.yaml
> @@ -0,0 +1,71 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/panel/sony,akatsuki-lgd.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Sony Xperia XZ3 LG Display 1440x2880 6.0" OLED DSI panel
> +
> +maintainers:
> + - Marijn Suijten <[email protected]>
> +
> +description: |
> + This is an unnamed 6.0" 1440x2880 (9:18 aspect ratio) 60 hz panel
> + produced by LG Display, found in the Sony Xperia XZ3 smartphone.
> + It is always programmed with DSI 1.1. enabled.
> +
> + The assembly features an Atmel maXTouch digitizer, described separately
> + as atmel,maxtouch.
> +
> +allOf:
> + - $ref: panel-common.yaml#
> +
> +properties:
> + compatible:
> + const: sony,akatsuki-lgd
> +
> + port: true
> +
> + reg:
> + maxItems: 1
> + description: DSI virtual channel
> +
> + reset-gpios: true
> +
> + vddio-supply:
> + description: I/O voltage supply (1.8V)
> +
> +required:
> + - compatible
> + - port
> + - reg
> + - reset-gpios
> + - vddio-supply
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + dsi {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + panel@0 {
> + compatible = "sony,akatsuki-lgd";
> + reg = <0>;
> +
> + vddio-supply = <&vreg_l14a_1p8>;
> +
> + reset-gpios = <&tlmm 6 GPIO_ACTIVE_HIGH>;
> +
> + port {
> + panel_in: endpoint {
> + remote-endpoint = <&dsi0_out>;
> + };
> + };
> + };
> + };
> +
> +...
> +
>
--
With best wishes
Dmitry
On 22/05/2023 00:23, Marijn Suijten wrote:
> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its Xperia
> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
>
> This panel features Display Stream Compression 1.1.
>
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> drivers/gpu/drm/panel/Kconfig | 11 +
> drivers/gpu/drm/panel/Makefile | 1 +
> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++++++++
> 3 files changed, 374 insertions(+)
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 67ef898d133f2..18bd116e78a71 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
> Say Y here if you want to enable support for the Sony ACX565AKM
> 800x600 3.5" panel (found on the Nokia N900).
>
> +config DRM_PANEL_SONY_AKATSUKI_LGD
> + tristate "Sony Xperia XZ3 LGD panel"
> + depends on GPIOLIB && OF
> + depends on DRM_MIPI_DSI
> + depends on BACKLIGHT_CLASS_DEVICE
> + help
> + Say Y here if you want to enable support for the Sony Xperia XZ3
> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG Display.
> +
> + This panel uses Display Stream Compression 1.1.
> +
> config DRM_PANEL_SONY_TD4353_JDI
> tristate "Sony TD4353 JDI panel"
> depends on GPIOLIB && OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index ff169781e82d7..85133f73558f3 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> new file mode 100644
> index 0000000000000..f55788f963dab
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> @@ -0,0 +1,362 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> + *
> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
> +
> +struct sony_akatsuki_lgd {
> + struct drm_panel panel;
> + struct mipi_dsi_device *dsi;
> + struct regulator *vddio;
> + struct gpio_desc *reset_gpio;
> + bool prepared;
> +};
> +
> +static inline struct sony_akatsuki_lgd *to_sony_akatsuki_lgd(struct drm_panel *panel)
> +{
> + return container_of(panel, struct sony_akatsuki_lgd, panel);
> +}
> +
> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
> + /* Enable backlight control */
> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
> +
> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set column address: %d\n", ret);
> + return ret;
> + }
> +
> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set page address: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
> +
> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
> +
> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> + return ret;
> + }
> + msleep(120);
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
> +
> + ret = mipi_dsi_dcs_set_display_on(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> + return ret;
> + }
My usual question: should the mipi_dsi_dcs_exit_sleep_mode() /
mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
> +
> + return 0;
> +}
> +
> +static int sony_akatsuki_lgd_off(struct sony_akatsuki_lgd *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_set_display_off(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display off: %d\n", ret);
> + return ret;
> + }
> + msleep(20);
> +
> + ret = mipi_dsi_dcs_set_tear_off(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set tear off: %d\n", ret);
> + return ret;
> + }
> +
> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> + return ret;
> + }
> + msleep(100);
> +
> + return 0;
> +}
> +
> +static int sony_akatsuki_lgd_prepare(struct drm_panel *panel)
> +{
> + struct sony_akatsuki_lgd *ctx = to_sony_akatsuki_lgd(panel);
> + struct drm_dsc_picture_parameter_set pps;
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (ctx->prepared)
> + return 0;
> +
> + ret = regulator_enable(ctx->vddio);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
> + return ret;
> + }
> +
> + msleep(100);
> +
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + usleep_range(5000, 5100);
> +
> + ret = sony_akatsuki_lgd_on(ctx);
> + if (ret < 0) {
> + dev_err(dev, "Failed to power on panel: %d\n", ret);
> + goto fail;
> + }
> +
> + if (ctx->dsi->dsc) {
dsi->dsc is always set, thus this condition can be dropped.
> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
> +
> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
> + if (ret < 0) {
> + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
> + goto fail;
> + }
> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
> + if (ret < 0) {
> + dev_err(dev, "failed to enable compression mode: %d\n", ret);
> + goto fail;
> + }
> +
> + msleep(28);
> + }
> +
> + ctx->prepared = true;
> + return 0;
> +
> +fail:
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + regulator_disable(ctx->vddio);
> + return ret;
> +}
> +
> +static int sony_akatsuki_lgd_unprepare(struct drm_panel *panel)
> +{
> + struct sony_akatsuki_lgd *ctx = to_sony_akatsuki_lgd(panel);
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (!ctx->prepared)
> + return 0;
> +
> + ret = sony_akatsuki_lgd_off(ctx);
> + if (ret < 0)
> + dev_err(dev, "Failed to power off panel: %d\n", ret);
> +
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + regulator_disable(ctx->vddio);
> +
> + usleep_range(5000, 5100);
> +
> + ctx->prepared = false;
> + return 0;
> +}
> +
> +static const struct drm_display_mode sony_akatsuki_lgd_mode = {
> + .clock = (1440 + 312 + 8 + 8) * (2880 + 48 + 8 + 8) * 60 / 1000,
> + .hdisplay = 1440,
> + .hsync_start = 1440 + 312,
> + .hsync_end = 1440 + 312 + 8,
> + .htotal = 1440 + 312 + 8 + 8,
> + .vdisplay = 2880,
> + .vsync_start = 2880 + 48,
> + .vsync_end = 2880 + 48 + 8,
> + .vtotal = 2880 + 48 + 8 + 8,
> + .width_mm = 68,
> + .height_mm = 136,
> +};
> +
> +static int sony_akatsuki_lgd_get_modes(struct drm_panel *panel,
> + struct drm_connector *connector)
> +{
> + return drm_connector_helper_get_modes_fixed(connector, &sony_akatsuki_lgd_mode);
> +}
> +
> +static const struct drm_panel_funcs sony_akatsuki_lgd_panel_funcs = {
> + .prepare = sony_akatsuki_lgd_prepare,
> + .unprepare = sony_akatsuki_lgd_unprepare,
> + .get_modes = sony_akatsuki_lgd_get_modes,
> +};
> +
> +static int sony_akatsuki_lgd_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 sony_akatsuki_lgd_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 sony_akatsuki_lgd_bl_ops = {
> + .update_status = sony_akatsuki_lgd_bl_update_status,
> + .get_brightness = sony_akatsuki_lgd_bl_get_brightness,
> +};
> +
> +static int sony_akatsuki_lgd_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 sony_akatsuki_lgd *ctx;
> + struct drm_dsc_config *dsc;
> + int ret;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->vddio = devm_regulator_get(dev, "vddio");
> + if (IS_ERR(ctx->vddio))
> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
> + "Failed to get vddio\n");
> +
> + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
> + 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;
> +
> + drm_panel_init(&ctx->panel, dev, &sony_akatsuki_lgd_panel_funcs,
> + DRM_MODE_CONNECTOR_DSI);
> +
> + ctx->panel.backlight = devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
> + &sony_akatsuki_lgd_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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
I think double assignments are frowned upon.
> + if (!dsc)
> + return -ENOMEM;
> +
> + dsc->dsc_version_major = 1;
> + dsc->dsc_version_minor = 1;
> +
> + dsc->slice_height = 32;
> + dsc->slice_count = 2;
> + // TODO: Get hdisplay from the mode
Would you like to fix the TODO?
> + WARN_ON(1440 % dsc->slice_count);
> + dsc->slice_width = 1440 / dsc->slice_count;
> + dsc->bits_per_component = 8;
> + dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
> + 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 sony_akatsuki_lgd_remove(struct mipi_dsi_device *dsi)
> +{
> + struct sony_akatsuki_lgd *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 sony_akatsuki_lgd_of_match[] = {
> + { .compatible = "sony,akatsuki-lgd" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sony_akatsuki_lgd_of_match);
> +
> +static struct mipi_dsi_driver sony_akatsuki_lgd_driver = {
> + .probe = sony_akatsuki_lgd_probe,
> + .remove = sony_akatsuki_lgd_remove,
> + .driver = {
> + .name = "panel-sony-akatsuki-lgd",
> + .of_match_table = sony_akatsuki_lgd_of_match,
> + },
> +};
> +module_mipi_dsi_driver(sony_akatsuki_lgd_driver);
> +
> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
> +MODULE_DESCRIPTION("DRM panel driver for an unnamed LGD OLED panel found in the Sony Xperia XZ3");
> +MODULE_LICENSE("GPL");
>
--
With best wishes
Dmitry
On 22/05/2023 00:23, Marijn Suijten wrote:
> This SOFEF01-M Display-IC driver supports two modes with different
> compatibles to differentiate between slightly different physical sizes
> (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
>
> It is currently also used to hardcode significantly higher fake porches
> for the Xperia 5, which are unused in transfers due to this being a
> command-mode panel but do have an effect on the clock rates set by
> dsi_host.c. Without higher clock rates this panel fails to achieve
> 60fps and has significant tearing artifacts, while the same calculated
> clock rate works perfectly fine on the Xperia 10 II.
>
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> drivers/gpu/drm/panel/Kconfig | 12 +
> drivers/gpu/drm/panel/Makefile | 1 +
> drivers/gpu/drm/panel/panel-samsung-sofef01.c | 360 ++++++++++++++++++++++++++
> 3 files changed, 373 insertions(+)
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 18bd116e78a71..3f11e9906f2cb 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -618,6 +618,18 @@ config DRM_PANEL_SAMSUNG_SOFEF00
>
> The panels are 2280x1080@60Hz and 2340x1080@60Hz respectively
>
> +config DRM_PANEL_SAMSUNG_SOFEF01
> + tristate "Samsung sofef01 Sony Xperia 5 / 10 II DSI cmd mode panels"
> + depends on GPIOLIB
> + depends on OF
> + depends on DRM_MIPI_DSI
> + depends on BACKLIGHT_CLASS_DEVICE
> + help
> + Say Y or M here if you want to enable support for the Samsung AMOLED
> + command mode panels found in the Sony Xperia 5 / 10 II smartphones.
> +
> + This panel features a fixed mode of 1080x2520@60.
> +
> config DRM_PANEL_SEIKO_43WVF1G
> tristate "Seiko 43WVF1G panel"
> depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index 85133f73558f3..a4039d0fc9cfb 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef01.c b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
> new file mode 100644
> index 0000000000000..18dc67a301a7b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
> @@ -0,0 +1,360 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// Copyright (c) 2023 Marijn Suijten <[email protected]>
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.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>
> +
> +struct samsung_sofef01_m {
> + struct drm_panel panel;
> + struct mipi_dsi_device *dsi;
> + struct regulator *vddio, *vci;
> + struct gpio_desc *reset_gpio;
> + const struct drm_display_mode *mode;
> + bool prepared;
> +};
> +
> +static inline struct samsung_sofef01_m *to_samsung_sofef01_m(struct drm_panel *panel)
> +{
> + return container_of(panel, struct samsung_sofef01_m, panel);
> +}
> +
> +static void samsung_sofef01_m_reset(struct samsung_sofef01_m *ctx)
> +{
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + usleep_range(10000, 11000);
> +}
> +
> +static int samsung_sofef01_m_on(struct samsung_sofef01_m *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> + return ret;
> + }
> + usleep_range(10000, 11000);
> +
> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x03);
> + mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> +
> + ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2520 - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set page address: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x92, 0x29);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x06);
> + mipi_dsi_dcs_write_seq(dsi, 0xb6, 0x90);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> + msleep(110);
> +
> + ret = mipi_dsi_dcs_set_display_on(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int samsung_sofef01_m_off(struct samsung_sofef01_m *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_set_display_off(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display off: %d\n", ret);
> + return ret;
> + }
> + msleep(20);
> +
> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> + return ret;
> + }
> + msleep(120);
> +
> + return 0;
> +}
> +
> +static int samsung_sofef01_m_prepare(struct drm_panel *panel)
> +{
> + struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (ctx->prepared)
> + return 0;
> +
> + ret = regulator_enable(ctx->vddio);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
> + return ret;
> + }
> +
> + ret = regulator_enable(ctx->vci);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
> + regulator_disable(ctx->vddio);
> + return ret;
> + }
> +
> + samsung_sofef01_m_reset(ctx);
> +
> + ret = samsung_sofef01_m_on(ctx);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + regulator_disable(ctx->vci);
> + regulator_disable(ctx->vddio);
> + return ret;
> + }
> +
> + ctx->prepared = true;
> + return 0;
> +}
> +
> +static int samsung_sofef01_m_unprepare(struct drm_panel *panel)
> +{
> + struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (!ctx->prepared)
> + return 0;
> +
> + ret = samsung_sofef01_m_off(ctx);
> + if (ret < 0)
> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
> +
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + regulator_disable(ctx->vci);
> + regulator_disable(ctx->vddio);
> +
> + ctx->prepared = false;
> + return 0;
> +}
> +
> +static int samsung_sofef01_m_get_modes(struct drm_panel *panel,
> + struct drm_connector *connector)
> +{
> + struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
> +
> + return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
> +}
> +
> +static const struct drm_panel_funcs samsung_sofef01_m_panel_funcs = {
> + .prepare = samsung_sofef01_m_prepare,
> + .unprepare = samsung_sofef01_m_unprepare,
> + .get_modes = samsung_sofef01_m_get_modes,
> +};
> +
> +static int samsung_sofef01_m_bl_update_status(struct backlight_device *bl)
> +{
> + struct mipi_dsi_device *dsi = bl_get_data(bl);
> + u16 brightness = backlight_get_brightness(bl);
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
> + if (ret < 0)
> + return ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + return 0;
> +}
> +
> +static int samsung_sofef01_m_bl_get_brightness(struct backlight_device *bl)
> +{
> + struct mipi_dsi_device *dsi = bl_get_data(bl);
> + u16 brightness;
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + if (ret < 0)
> + return ret;
> +
> + return brightness;
> +}
> +
> +static const struct backlight_ops samsung_sofef01_m_bl_ops = {
> + .update_status = samsung_sofef01_m_bl_update_status,
> + .get_brightness = samsung_sofef01_m_bl_get_brightness,
> +};
> +
> +static struct backlight_device *
> +samsung_sofef01_m_create_backlight(struct mipi_dsi_device *dsi)
> +{
> + struct device *dev = &dsi->dev;
> + const struct backlight_properties props = {
> + .type = BACKLIGHT_RAW,
> + .brightness = 100,
> + .max_brightness = 1023,
> + };
> +
> + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
> + &samsung_sofef01_m_bl_ops, &props);
> +}
> +
> +static int samsung_sofef01_m_probe(struct mipi_dsi_device *dsi)
> +{
> + struct device *dev = &dsi->dev;
> + struct samsung_sofef01_m *ctx;
> + int ret;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->vddio = devm_regulator_get(dev, "vddio");
> + if (IS_ERR(ctx->vddio))
> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
> + "Failed to get vddio regulator\n");
> +
> + ctx->vci = devm_regulator_get(dev, "vci");
> + if (IS_ERR(ctx->vci))
> + return dev_err_probe(dev, PTR_ERR(ctx->vci),
> + "Failed to get vci regulator\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;
> + ctx->mode = of_device_get_match_data(dev);
> + mipi_dsi_set_drvdata(dsi, ctx);
> +
> + dsi->lanes = 4;
> + dsi->format = MIPI_DSI_FMT_RGB888;
> + dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
> +
> + drm_panel_init(&ctx->panel, dev, &samsung_sofef01_m_panel_funcs,
> + DRM_MODE_CONNECTOR_DSI);
> +
> + ctx->panel.backlight = samsung_sofef01_m_create_backlight(dsi);
> + 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);
> +
> + 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 samsung_sofef01_m_remove(struct mipi_dsi_device *dsi)
> +{
> + struct samsung_sofef01_m *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);
> +}
> +
> +/* Sony Xperia 5 (kumano bahamut) */
> +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
> + /*
> + * WARNING: These massive porches are wrong/useless for CMDmode
> + * (and not defined in downstream DTS) but necessary to bump dsi
> + * clocks higher, so that we can achieve proper 60fps without tearing.
> + */
> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> + .hdisplay = 1080,
> + .hsync_start = 1080 + 156,
> + .hsync_end = 1080 + 156 + 8,
> + .htotal = 1080 + 156 + 8 + 8,
> + .vdisplay = 2520,
> + .vsync_start = 2520 + 2393,
> + .vsync_end = 2520 + 2393 + 8,
> + .vtotal = 2520 + 2393 + 8 + 8,
> + .width_mm = 61,
> + .height_mm = 142,
> +};
> +
> +/* Sony Xperia 10 II (seine pdx201) */
> +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
> + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
> + .hdisplay = 1080,
> + .hsync_start = 1080 + 8,
> + .hsync_end = 1080 + 8 + 8,
> + .htotal = 1080 + 8 + 8 + 8,
> + .vdisplay = 2520,
> + .vsync_start = 2520 + 8,
> + .vsync_end = 2520 + 8 + 8,
> + .vtotal = 2520 + 8 + 8 + 8,
> + .width_mm = 60,
> + .height_mm = 139,
> +};
> +
> +static const struct of_device_id samsung_sofef01_m_of_match[] = {
> + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
> + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
Are there really two panels? Can we use one mode for both usecases?
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, samsung_sofef01_m_of_match);
> +
> +static struct mipi_dsi_driver samsung_sofef01_m_driver = {
> + .probe = samsung_sofef01_m_probe,
> + .remove = samsung_sofef01_m_remove,
> + .driver = {
> + .name = "panel-samsung-sofef01-m",
> + .of_match_table = samsung_sofef01_m_of_match,
> + },
> +};
> +module_mipi_dsi_driver(samsung_sofef01_m_driver);
> +
> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
> +MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF01-M Display-IC panels");
> +MODULE_LICENSE("GPL");
>
--
With best wishes
Dmitry
On 22/05/2023 00:23, Marijn Suijten wrote:
> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
> 120hz refresh-rate mode.
>
> Co-developed-by: Konrad Dybcio <[email protected]>
Konrad's S-o-b is also required then
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> drivers/gpu/drm/panel/Kconfig | 14 +
> drivers/gpu/drm/panel/Makefile | 1 +
> drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
> 3 files changed, 438 insertions(+)
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 3f11e9906f2cb..8e2668153bce2 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
>
> This panel features a fixed mode of 1080x2520@60.
>
> +config DRM_PANEL_SAMSUNG_SOFEF03
> + tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
> + depends on GPIOLIB
> + depends on OF
> + depends on DRM_MIPI_DSI
> + depends on BACKLIGHT_CLASS_DEVICE
> + help
> + Say Y or M here if you want to enable support for the Samsung AMOLED
> + command mode panel found in the Sony Xperia 5 II smartphone.
> +
> + This panel uses Display Stream Compression 1.1.
> +
> + The panel features a 1080x2520@60 and 1080x2520@120 mode.
> +
> config DRM_PANEL_SEIKO_43WVF1G
> tristate "Seiko 43WVF1G panel"
> depends on OF
> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> index a4039d0fc9cfb..52dcd82e33120 100644
> --- a/drivers/gpu/drm/panel/Makefile
> +++ b/drivers/gpu/drm/panel/Makefile
> @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
> new file mode 100644
> index 0000000000000..2763e1c56b37b
> --- /dev/null
> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
> @@ -0,0 +1,423 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2022 Konrad Dybcio <[email protected]>
> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> + */
> +
> +#include <linux/backlight.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.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>
> +
> +static const bool enable_120hz = true;
> +
> +struct samsung_sofef03_m {
> + struct drm_panel panel;
> + struct mipi_dsi_device *dsi;
> + struct regulator *vddio, *vci;
> + struct gpio_desc *reset_gpio;
> + bool prepared;
> +};
> +
> +static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
> +{
> + return container_of(panel, struct samsung_sofef03_m, panel);
> +}
> +
> +static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
> +{
> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> + usleep_range(10000, 11000);
> +}
> +
> +static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
> +
> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> + return ret;
> + }
> + usleep_range(10000, 11000);
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
> + mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
> + mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> +
> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> + return ret;
> + }
> +
> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set column address: %d\n", ret);
> + return ret;
> + }
> +
> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set page address: %d\n", ret);
> + return ret;
> + }
> +
> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set display brightness: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
> + mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> + msleep(110);
> +
> + ret = mipi_dsi_dcs_set_display_on(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
> +{
> + struct mipi_dsi_device *dsi = ctx->dsi;
> + struct device *dev = &dsi->dev;
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_set_display_off(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display off: %d\n", ret);
> + return ret;
> + }
> + msleep(20);
> +
> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> + return ret;
> + }
> + msleep(100);
> +
> + return 0;
> +}
> +
> +static int samsung_sofef03_m_prepare(struct drm_panel *panel)
> +{
> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
> + struct drm_dsc_picture_parameter_set pps;
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (ctx->prepared)
> + return 0;
> +
> + ret = regulator_enable(ctx->vddio);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
> + return ret;
> + }
> +
> + ret = regulator_enable(ctx->vci);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
> + regulator_disable(ctx->vddio);
> + return ret;
> + }
> +
> + samsung_sofef03_m_reset(ctx);
> +
> + ret = samsung_sofef03_m_on(ctx);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
> + goto fail;
> + }
> +
> + if (ctx->dsi->dsc) {
Always true
> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
> +
> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
> + if (ret < 0) {
> + dev_err(dev, "failed to transmit PPS: %d\n", ret);
> + goto fail;
> + }
> +
> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
> + if (ret < 0) {
> + dev_err(dev, "Failed to enable compression mode: %d\n", ret);
> + goto fail;
> + }
> +
> + msleep(28);
> + }
> +
> + ctx->prepared = true;
> + return 0;
> +
> +fail:
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + regulator_disable(ctx->vci);
> + regulator_disable(ctx->vddio);
> + return ret;
> +}
> +
> +static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
> +{
> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
> + struct device *dev = &ctx->dsi->dev;
> + int ret;
> +
> + if (!ctx->prepared)
> + return 0;
> +
> + ret = samsung_sofef03_m_off(ctx);
> + if (ret < 0)
> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
> +
> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> + regulator_disable(ctx->vci);
> + regulator_disable(ctx->vddio);
> +
> + ctx->prepared = false;
> + return 0;
> +}
> +
> +static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> + .hdisplay = 1080,
> + .hsync_start = 1080 + 156,
> + .hsync_end = 1080 + 156 + 8,
> + .htotal = 1080 + 156 + 8 + 8,
> + .vdisplay = 2520,
> + .vsync_start = 2520 + 2393,
> + .vsync_end = 2520 + 2393 + 8,
> + .vtotal = 2520 + 2393 + 8 + 8,
> + .width_mm = 61,
> + .height_mm = 142,
> +};
> +
> +static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
> + .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
> + .hdisplay = 1080,
> + .hsync_start = 1080 + 56,
> + .hsync_end = 1080 + 56 + 8,
> + .htotal = 1080 + 56 + 8 + 8,
> + .vdisplay = 2520,
> + .vsync_start = 2520 + 499,
> + .vsync_end = 2520 + 499 + 8,
> + .vtotal = 2520 + 499 + 8 + 8,
> + .width_mm = 61,
> + .height_mm = 142,
> +};
> +
> +static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
> + struct drm_connector *connector)
> +{
> + if (enable_120hz)
Is it possible to switch between these modes at runtime? It might be
logical to define 60 Hz mode as preferred, while allowing users to
switch to 120 Hz when required for some reason.
> + return drm_connector_helper_get_modes_fixed(connector,
> + &samsung_sofef03_m_120hz_mode);
> + else
> + return drm_connector_helper_get_modes_fixed(connector,
> + &samsung_sofef03_m_60hz_mode);
> +}
> +
> +static const struct drm_panel_funcs samsung_sofef03_m_panel_funcs = {
> + .prepare = samsung_sofef03_m_prepare,
> + .unprepare = samsung_sofef03_m_unprepare,
> + .get_modes = samsung_sofef03_m_get_modes,
> +};
> +
> +static int samsung_sofef03_m_bl_update_status(struct backlight_device *bl)
> +{
> + struct mipi_dsi_device *dsi = bl_get_data(bl);
> + u16 brightness = backlight_get_brightness(bl);
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + pr_err("Writing %#x\n", brightness);
> +
> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
> + if (ret < 0)
> + return ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + return 0;
> +}
> +
> +static int samsung_sofef03_m_bl_get_brightness(struct backlight_device *bl)
> +{
> + struct mipi_dsi_device *dsi = bl_get_data(bl);
> + u16 brightness;
> + int ret;
> +
> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> +
> + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
> + if (ret < 0)
> + return ret;
> +
> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> +
> + pr_err("Read display brightness %#x\n", brightness);
> +
> + return brightness;
> +}
> +
> +static const struct backlight_ops samsung_sofef03_m_bl_ops = {
> + .update_status = samsung_sofef03_m_bl_update_status,
> + .get_brightness = samsung_sofef03_m_bl_get_brightness,
> +};
> +
> +static struct backlight_device *
> +samsung_sofef03_m_create_backlight(struct mipi_dsi_device *dsi)
> +{
> + struct device *dev = &dsi->dev;
> + const struct backlight_properties props = {
> + .type = BACKLIGHT_RAW,
> + .brightness = 100,
> + .max_brightness = 1023,
> + };
> +
> + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
> + &samsung_sofef03_m_bl_ops, &props);
> +}
> +
> +static int samsung_sofef03_m_probe(struct mipi_dsi_device *dsi)
> +{
> + struct device *dev = &dsi->dev;
> + struct drm_dsc_config *dsc;
> + struct samsung_sofef03_m *ctx;
> + int ret;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return -ENOMEM;
> +
> + ctx->vddio = devm_regulator_get(dev, "vddio");
> + if (IS_ERR(ctx->vddio))
> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
> + "Failed to get vddio regulator\n");
> +
> + ctx->vci = devm_regulator_get(dev, "vci");
> + if (IS_ERR(ctx->vci))
> + return dev_err_probe(dev, PTR_ERR(ctx->vci),
> + "Failed to get vci regulator\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;
> +
> + drm_panel_init(&ctx->panel, dev, &samsung_sofef03_m_panel_funcs,
> + DRM_MODE_CONNECTOR_DSI);
> +
> + ctx->panel.backlight = samsung_sofef03_m_create_backlight(dsi);
> + 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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
Double assignment
> + if (!dsc)
> + return -ENOMEM;
> +
> + dsc->dsc_version_major = 1;
> + dsc->dsc_version_minor = 1;
> +
> + dsc->slice_height = 30;
> + dsc->slice_width = 540;
> + dsc->slice_count = 2;
> + dsc->bits_per_component = 8;
> + dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
> + 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 samsung_sofef03_m_remove(struct mipi_dsi_device *dsi)
> +{
> + struct samsung_sofef03_m *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 samsung_sofef03_m_of_match[] = {
> + { .compatible = "samsung,sofef03-m" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, samsung_sofef03_m_of_match);
> +
> +static struct mipi_dsi_driver samsung_sofef03_m_driver = {
> + .probe = samsung_sofef03_m_probe,
> + .remove = samsung_sofef03_m_remove,
> + .driver = {
> + .name = "panel-samsung-sofef03-m",
> + .of_match_table = samsung_sofef03_m_of_match,
> + },
> +};
> +module_mipi_dsi_driver(samsung_sofef03_m_driver);
> +
> +MODULE_AUTHOR("Konrad Dybcio <[email protected]>");
> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
> +MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF03-M Display-IC panels");
> +MODULE_LICENSE("GPL");
>
--
With best wishes
Dmitry
On 21/05/2023 23:23, Marijn Suijten wrote:
> As per the config name this Display IC features a DSI command-mode
> interface (or the command to switch to video mode is not
> known/documented) and does not use any of the video-mode helper
> utilities, hence should not select VIDEOMODE_HELPERS. In addition it
> uses devm_gpiod_get() and related functions from GPIOLIB.
>
> Fixes: 5933baa36e26 ("drm/panel/samsung-sofef00: Add panel for OnePlus 6/T devices")
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> drivers/gpu/drm/panel/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 2b9d6db7860ba..67ef898d133f2 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -608,10 +608,10 @@ config DRM_PANEL_SAMSUNG_S6E8AA0
>
> config DRM_PANEL_SAMSUNG_SOFEF00
> tristate "Samsung sofef00/s6e3fc2x01 OnePlus 6/6T DSI cmd mode panels"
> + depends on GPIOLIB
> depends on OF
> depends on DRM_MIPI_DSI
> depends on BACKLIGHT_CLASS_DEVICE
> - select VIDEOMODE_HELPERS
> help
> Say Y or M here if you want to enable support for the Samsung AMOLED
> command mode panels found in the OnePlus 6/6T smartphones.
>
Reviewed-by: Neil Armstrong <[email protected]>
On 22/05/2023 03:16, Dmitry Baryshkov wrote:
> On 22/05/2023 00:23, Marijn Suijten wrote:
>> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its Xperia
>> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
>>
>> This panel features Display Stream Compression 1.1.
>>
>> Signed-off-by: Marijn Suijten <[email protected]>
>> ---
>> drivers/gpu/drm/panel/Kconfig | 11 +
>> drivers/gpu/drm/panel/Makefile | 1 +
>> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++++++++
>> 3 files changed, 374 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 67ef898d133f2..18bd116e78a71 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
>> Say Y here if you want to enable support for the Sony ACX565AKM
>> 800x600 3.5" panel (found on the Nokia N900).
>> +config DRM_PANEL_SONY_AKATSUKI_LGD
>> + tristate "Sony Xperia XZ3 LGD panel"
>> + depends on GPIOLIB && OF
>> + depends on DRM_MIPI_DSI
>> + depends on BACKLIGHT_CLASS_DEVICE
>> + help
>> + Say Y here if you want to enable support for the Sony Xperia XZ3
>> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG Display.
>> +
>> + This panel uses Display Stream Compression 1.1.
>> +
>> config DRM_PANEL_SONY_TD4353_JDI
>> tristate "Sony TD4353 JDI panel"
>> depends on GPIOLIB && OF
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index ff169781e82d7..85133f73558f3 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
>> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
>> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
>> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
>> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
>> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
>> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>> new file mode 100644
>> index 0000000000000..f55788f963dab
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>> @@ -0,0 +1,362 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>> + *
>> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
>> +
>> +struct sony_akatsuki_lgd {
>> + struct drm_panel panel;
>> + struct mipi_dsi_device *dsi;
>> + struct regulator *vddio;
>> + struct gpio_desc *reset_gpio;
>> + bool prepared;
>> +};
>> +
>> +static inline struct sony_akatsuki_lgd *to_sony_akatsuki_lgd(struct drm_panel *panel)
>> +{
>> + return container_of(panel, struct sony_akatsuki_lgd, panel);
>> +}
>> +
>> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
>> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
>> + /* Enable backlight control */
>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
>> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
>> +
>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
>> +
>> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
>> +
>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(120);
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
>> +
>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>> + return ret;
>> + }
>
> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
Neil
>
>> +
>> + return 0;
>> +}
>> +
>> +static int sony_akatsuki_lgd_off(struct sony_akatsuki_lgd *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_set_display_off(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display off: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(20);
>> +
>> + ret = mipi_dsi_dcs_set_tear_off(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set tear off: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(100);
>> +
>> + return 0;
>> +}
>> +
>> +static int sony_akatsuki_lgd_prepare(struct drm_panel *panel)
>> +{
>> + struct sony_akatsuki_lgd *ctx = to_sony_akatsuki_lgd(panel);
>> + struct drm_dsc_picture_parameter_set pps;
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (ctx->prepared)
>> + return 0;
>> +
>> + ret = regulator_enable(ctx->vddio);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + msleep(100);
>> +
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + usleep_range(5000, 5100);
>> +
>> + ret = sony_akatsuki_lgd_on(ctx);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to power on panel: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + if (ctx->dsi->dsc) {
>
> dsi->dsc is always set, thus this condition can be dropped.
>
>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>> +
>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>> + if (ret < 0) {
>> + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
>> + goto fail;
>> + }
>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>> + if (ret < 0) {
>> + dev_err(dev, "failed to enable compression mode: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + msleep(28);
>> + }
>> +
>> + ctx->prepared = true;
>> + return 0;
>> +
>> +fail:
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> +}
>> +
>> +static int sony_akatsuki_lgd_unprepare(struct drm_panel *panel)
>> +{
>> + struct sony_akatsuki_lgd *ctx = to_sony_akatsuki_lgd(panel);
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (!ctx->prepared)
>> + return 0;
>> +
>> + ret = sony_akatsuki_lgd_off(ctx);
>> + if (ret < 0)
>> + dev_err(dev, "Failed to power off panel: %d\n", ret);
>> +
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> + regulator_disable(ctx->vddio);
>> +
>> + usleep_range(5000, 5100);
>> +
>> + ctx->prepared = false;
>> + return 0;
>> +}
>> +
>> +static const struct drm_display_mode sony_akatsuki_lgd_mode = {
>> + .clock = (1440 + 312 + 8 + 8) * (2880 + 48 + 8 + 8) * 60 / 1000,
>> + .hdisplay = 1440,
>> + .hsync_start = 1440 + 312,
>> + .hsync_end = 1440 + 312 + 8,
>> + .htotal = 1440 + 312 + 8 + 8,
>> + .vdisplay = 2880,
>> + .vsync_start = 2880 + 48,
>> + .vsync_end = 2880 + 48 + 8,
>> + .vtotal = 2880 + 48 + 8 + 8,
>> + .width_mm = 68,
>> + .height_mm = 136,
>> +};
>> +
>> +static int sony_akatsuki_lgd_get_modes(struct drm_panel *panel,
>> + struct drm_connector *connector)
>> +{
>> + return drm_connector_helper_get_modes_fixed(connector, &sony_akatsuki_lgd_mode);
>> +}
>> +
>> +static const struct drm_panel_funcs sony_akatsuki_lgd_panel_funcs = {
>> + .prepare = sony_akatsuki_lgd_prepare,
>> + .unprepare = sony_akatsuki_lgd_unprepare,
>> + .get_modes = sony_akatsuki_lgd_get_modes,
>> +};
>> +
>> +static int sony_akatsuki_lgd_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 sony_akatsuki_lgd_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 sony_akatsuki_lgd_bl_ops = {
>> + .update_status = sony_akatsuki_lgd_bl_update_status,
>> + .get_brightness = sony_akatsuki_lgd_bl_get_brightness,
>> +};
>> +
>> +static int sony_akatsuki_lgd_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 sony_akatsuki_lgd *ctx;
>> + struct drm_dsc_config *dsc;
>> + int ret;
>> +
>> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> + if (!ctx)
>> + return -ENOMEM;
>> +
>> + ctx->vddio = devm_regulator_get(dev, "vddio");
>> + if (IS_ERR(ctx->vddio))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
>> + "Failed to get vddio\n");
>> +
>> + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
>> + 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;
>> +
>> + drm_panel_init(&ctx->panel, dev, &sony_akatsuki_lgd_panel_funcs,
>> + DRM_MODE_CONNECTOR_DSI);
>> +
>> + ctx->panel.backlight = devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
>> + &sony_akatsuki_lgd_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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>
> I think double assignments are frowned upon.
>
>> + if (!dsc)
>> + return -ENOMEM;
>> +
>> + dsc->dsc_version_major = 1;
>> + dsc->dsc_version_minor = 1;
>> +
>> + dsc->slice_height = 32;
>> + dsc->slice_count = 2;
>> + // TODO: Get hdisplay from the mode
>
> Would you like to fix the TODO?
>
>> + WARN_ON(1440 % dsc->slice_count);
>> + dsc->slice_width = 1440 / dsc->slice_count;
>> + dsc->bits_per_component = 8;
>> + dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
>> + 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 sony_akatsuki_lgd_remove(struct mipi_dsi_device *dsi)
>> +{
>> + struct sony_akatsuki_lgd *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 sony_akatsuki_lgd_of_match[] = {
>> + { .compatible = "sony,akatsuki-lgd" },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, sony_akatsuki_lgd_of_match);
>> +
>> +static struct mipi_dsi_driver sony_akatsuki_lgd_driver = {
>> + .probe = sony_akatsuki_lgd_probe,
>> + .remove = sony_akatsuki_lgd_remove,
>> + .driver = {
>> + .name = "panel-sony-akatsuki-lgd",
>> + .of_match_table = sony_akatsuki_lgd_of_match,
>> + },
>> +};
>> +module_mipi_dsi_driver(sony_akatsuki_lgd_driver);
>> +
>> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
>> +MODULE_DESCRIPTION("DRM panel driver for an unnamed LGD OLED panel found in the Sony Xperia XZ3");
>> +MODULE_LICENSE("GPL");
>>
>
On 22/05/2023 03:23, Dmitry Baryshkov wrote:
> On 22/05/2023 00:23, Marijn Suijten wrote:
>> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
>> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
>> 120hz refresh-rate mode.
>>
>> Co-developed-by: Konrad Dybcio <[email protected]>
>
> Konrad's S-o-b is also required then
>
>> Signed-off-by: Marijn Suijten <[email protected]>
>> ---
>> drivers/gpu/drm/panel/Kconfig | 14 +
>> drivers/gpu/drm/panel/Makefile | 1 +
>> drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
>> 3 files changed, 438 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 3f11e9906f2cb..8e2668153bce2 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
>> This panel features a fixed mode of 1080x2520@60.
>> +config DRM_PANEL_SAMSUNG_SOFEF03
>> + tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
>> + depends on GPIOLIB
>> + depends on OF
>> + depends on DRM_MIPI_DSI
>> + depends on BACKLIGHT_CLASS_DEVICE
>> + help
>> + Say Y or M here if you want to enable support for the Samsung AMOLED
>> + command mode panel found in the Sony Xperia 5 II smartphone.
>> +
>> + This panel uses Display Stream Compression 1.1.
>> +
>> + The panel features a 1080x2520@60 and 1080x2520@120 mode.
>> +
>> config DRM_PANEL_SEIKO_43WVF1G
>> tristate "Seiko 43WVF1G panel"
>> depends on OF
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index a4039d0fc9cfb..52dcd82e33120 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
>> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
>> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
>> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
>> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
>> new file mode 100644
>> index 0000000000000..2763e1c56b37b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
>> @@ -0,0 +1,423 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2022 Konrad Dybcio <[email protected]>
>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>> + */
>> +
>> +#include <linux/backlight.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/of.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>
>> +
>> +static const bool enable_120hz = true;
Maybe this can be a module parameter ? Can you explain why this can't be dynamically changed by a modeset ?
>> +
>> +struct samsung_sofef03_m {
>> + struct drm_panel panel;
>> + struct mipi_dsi_device *dsi;
>> + struct regulator *vddio, *vci;
>> + struct gpio_desc *reset_gpio;
>> + bool prepared;
>> +};
>> +
>> +static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
>> +{
>> + return container_of(panel, struct samsung_sofef03_m, panel);
>> +}
>> +
>> +static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
>> +{
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> + usleep_range(10000, 11000);
>> +}
>> +
>> +static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
>> +
>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + usleep_range(10000, 11000);
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
>> + mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
>> + mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> +
>> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set display brightness: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
>> + mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
>> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
>> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
>> + mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> + msleep(110);
>> +
>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_set_display_off(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display off: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(20);
>> +
>> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(100);
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef03_m_prepare(struct drm_panel *panel)
>> +{
>> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
>> + struct drm_dsc_picture_parameter_set pps;
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (ctx->prepared)
>> + return 0;
>> +
>> + ret = regulator_enable(ctx->vddio);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = regulator_enable(ctx->vci);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> + }
>> +
>> + samsung_sofef03_m_reset(ctx);
>> +
>> + ret = samsung_sofef03_m_on(ctx);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + if (ctx->dsi->dsc) {
>
> Always true
>
>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>> +
>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>> + if (ret < 0) {
>> + dev_err(dev, "failed to transmit PPS: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable compression mode: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + msleep(28);
>> + }
>> +
>> + ctx->prepared = true;
>> + return 0;
>> +
>> +fail:
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + regulator_disable(ctx->vci);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> +}
>> +
>> +static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
>> +{
>> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (!ctx->prepared)
>> + return 0;
>> +
>> + ret = samsung_sofef03_m_off(ctx);
>> + if (ret < 0)
>> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
>> +
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + regulator_disable(ctx->vci);
>> + regulator_disable(ctx->vddio);
>> +
>> + ctx->prepared = false;
>> + return 0;
>> +}
>> +
>> +static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
>> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
>> + .hdisplay = 1080,
>> + .hsync_start = 1080 + 156,
>> + .hsync_end = 1080 + 156 + 8,
>> + .htotal = 1080 + 156 + 8 + 8,
>> + .vdisplay = 2520,
>> + .vsync_start = 2520 + 2393,
>> + .vsync_end = 2520 + 2393 + 8,
>> + .vtotal = 2520 + 2393 + 8 + 8,
>> + .width_mm = 61,
>> + .height_mm = 142,
>> +};
>> +
>> +static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
>> + .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
>> + .hdisplay = 1080,
>> + .hsync_start = 1080 + 56,
>> + .hsync_end = 1080 + 56 + 8,
>> + .htotal = 1080 + 56 + 8 + 8,
>> + .vdisplay = 2520,
>> + .vsync_start = 2520 + 499,
>> + .vsync_end = 2520 + 499 + 8,
>> + .vtotal = 2520 + 499 + 8 + 8,
>> + .width_mm = 61,
>> + .height_mm = 142,
>> +};
>> +
>> +static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
>> + struct drm_connector *connector)
>> +{
>> + if (enable_120hz)
>
> Is it possible to switch between these modes at runtime? It might be logical to define 60 Hz mode as preferred, while allowing users to switch to 120 Hz when required for some reason.
>
>> + return drm_connector_helper_get_modes_fixed(connector,
>> + &samsung_sofef03_m_120hz_mode);
>> + else
>> + return drm_connector_helper_get_modes_fixed(connector,
>> + &samsung_sofef03_m_60hz_mode);
>> +}
>> +
>> +static const struct drm_panel_funcs samsung_sofef03_m_panel_funcs = {
>> + .prepare = samsung_sofef03_m_prepare,
>> + .unprepare = samsung_sofef03_m_unprepare,
>> + .get_modes = samsung_sofef03_m_get_modes,
>> +};
>> +
>> +static int samsung_sofef03_m_bl_update_status(struct backlight_device *bl)
>> +{
>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>> + u16 brightness = backlight_get_brightness(bl);
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + pr_err("Writing %#x\n", brightness);
You forgot to remove those desbug prints :-p
>> +
>> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef03_m_bl_get_brightness(struct backlight_device *bl)
>> +{
>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>> + u16 brightness;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + pr_err("Read display brightness %#x\n", brightness);
Ditto
>> +
>> + return brightness;
>> +}
>> +
>> +static const struct backlight_ops samsung_sofef03_m_bl_ops = {
>> + .update_status = samsung_sofef03_m_bl_update_status,
>> + .get_brightness = samsung_sofef03_m_bl_get_brightness,
>> +};
>> +
>> +static struct backlight_device *
>> +samsung_sofef03_m_create_backlight(struct mipi_dsi_device *dsi)
>> +{
>> + struct device *dev = &dsi->dev;
>> + const struct backlight_properties props = {
>> + .type = BACKLIGHT_RAW,
>> + .brightness = 100,
>> + .max_brightness = 1023,
>> + };
>> +
>> + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
>> + &samsung_sofef03_m_bl_ops, &props);
>> +}
>> +
>> +static int samsung_sofef03_m_probe(struct mipi_dsi_device *dsi)
>> +{
>> + struct device *dev = &dsi->dev;
>> + struct drm_dsc_config *dsc;
>> + struct samsung_sofef03_m *ctx;
>> + int ret;
>> +
>> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> + if (!ctx)
>> + return -ENOMEM;
>> +
>> + ctx->vddio = devm_regulator_get(dev, "vddio");
>> + if (IS_ERR(ctx->vddio))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
>> + "Failed to get vddio regulator\n");
>> +
>> + ctx->vci = devm_regulator_get(dev, "vci");
>> + if (IS_ERR(ctx->vci))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vci),
>> + "Failed to get vci regulator\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;
>> +
>> + drm_panel_init(&ctx->panel, dev, &samsung_sofef03_m_panel_funcs,
>> + DRM_MODE_CONNECTOR_DSI);
>> +
>> + ctx->panel.backlight = samsung_sofef03_m_create_backlight(dsi);
>> + 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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>
> Double assignment
>
>> + if (!dsc)
>> + return -ENOMEM;
>> +
>> + dsc->dsc_version_major = 1;
>> + dsc->dsc_version_minor = 1;
>> +
>> + dsc->slice_height = 30;
>> + dsc->slice_width = 540;
>> + dsc->slice_count = 2;
>> + dsc->bits_per_component = 8;
>> + dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
>> + 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 samsung_sofef03_m_remove(struct mipi_dsi_device *dsi)
>> +{
>> + struct samsung_sofef03_m *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 samsung_sofef03_m_of_match[] = {
>> + { .compatible = "samsung,sofef03-m" },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, samsung_sofef03_m_of_match);
>> +
>> +static struct mipi_dsi_driver samsung_sofef03_m_driver = {
>> + .probe = samsung_sofef03_m_probe,
>> + .remove = samsung_sofef03_m_remove,
>> + .driver = {
>> + .name = "panel-samsung-sofef03-m",
>> + .of_match_table = samsung_sofef03_m_of_match,
>> + },
>> +};
>> +module_mipi_dsi_driver(samsung_sofef03_m_driver);
>> +
>> +MODULE_AUTHOR("Konrad Dybcio <[email protected]>");
>> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
>> +MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF03-M Display-IC panels");
>> +MODULE_LICENSE("GPL");
>>
>
On Mon, 22 May 2023 at 12:04, Neil Armstrong <[email protected]> wrote:
>
> On 22/05/2023 03:16, Dmitry Baryshkov wrote:
> > On 22/05/2023 00:23, Marijn Suijten wrote:
> >> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its Xperia
> >> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
> >>
> >> This panel features Display Stream Compression 1.1.
> >>
> >> Signed-off-by: Marijn Suijten <[email protected]>
> >> ---
> >> drivers/gpu/drm/panel/Kconfig | 11 +
> >> drivers/gpu/drm/panel/Makefile | 1 +
> >> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++++++++
> >> 3 files changed, 374 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> >> index 67ef898d133f2..18bd116e78a71 100644
> >> --- a/drivers/gpu/drm/panel/Kconfig
> >> +++ b/drivers/gpu/drm/panel/Kconfig
> >> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
> >> Say Y here if you want to enable support for the Sony ACX565AKM
> >> 800x600 3.5" panel (found on the Nokia N900).
> >> +config DRM_PANEL_SONY_AKATSUKI_LGD
> >> + tristate "Sony Xperia XZ3 LGD panel"
> >> + depends on GPIOLIB && OF
> >> + depends on DRM_MIPI_DSI
> >> + depends on BACKLIGHT_CLASS_DEVICE
> >> + help
> >> + Say Y here if you want to enable support for the Sony Xperia XZ3
> >> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG Display.
> >> +
> >> + This panel uses Display Stream Compression 1.1.
> >> +
> >> config DRM_PANEL_SONY_TD4353_JDI
> >> tristate "Sony TD4353 JDI panel"
> >> depends on GPIOLIB && OF
> >> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> >> index ff169781e82d7..85133f73558f3 100644
> >> --- a/drivers/gpu/drm/panel/Makefile
> >> +++ b/drivers/gpu/drm/panel/Makefile
> >> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
> >> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
> >> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
> >> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
> >> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
> >> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
> >> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
> >> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
> >> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> >> new file mode 100644
> >> index 0000000000000..f55788f963dab
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> >> @@ -0,0 +1,362 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> >> + *
> >> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
> >> +
> >> +struct sony_akatsuki_lgd {
> >> + struct drm_panel panel;
> >> + struct mipi_dsi_device *dsi;
> >> + struct regulator *vddio;
> >> + struct gpio_desc *reset_gpio;
> >> + bool prepared;
> >> +};
> >> +
> >> +static inline struct sony_akatsuki_lgd *to_sony_akatsuki_lgd(struct drm_panel *panel)
> >> +{
> >> + return container_of(panel, struct sony_akatsuki_lgd, panel);
> >> +}
> >> +
> >> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
> >> +{
> >> + struct mipi_dsi_device *dsi = ctx->dsi;
> >> + struct device *dev = &dsi->dev;
> >> + int ret;
> >> +
> >> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
> >> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
> >> + /* Enable backlight control */
> >> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> >> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
> >> +
> >> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set column address: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set page address: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
> >> +
> >> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
> >> +
> >> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> >> + return ret;
> >> + }
> >> + msleep(120);
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
> >> +
> >> + ret = mipi_dsi_dcs_set_display_on(dsi);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> >> + return ret;
> >> + }
> >
> > My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
>
>
> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
>
Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
--
With best wishes
Dmitry
On Mon, 22 May 2023 at 12:08, Neil Armstrong <[email protected]> wrote:
>
> On 22/05/2023 03:23, Dmitry Baryshkov wrote:
> > On 22/05/2023 00:23, Marijn Suijten wrote:
> >> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
> >> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
> >> 120hz refresh-rate mode.
> >>
> >> Co-developed-by: Konrad Dybcio <[email protected]>
> >
> > Konrad's S-o-b is also required then
> >
> >> Signed-off-by: Marijn Suijten <[email protected]>
> >> ---
> >> drivers/gpu/drm/panel/Kconfig | 14 +
> >> drivers/gpu/drm/panel/Makefile | 1 +
> >> drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
> >> 3 files changed, 438 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> >> index 3f11e9906f2cb..8e2668153bce2 100644
> >> --- a/drivers/gpu/drm/panel/Kconfig
> >> +++ b/drivers/gpu/drm/panel/Kconfig
> >> @@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
> >> This panel features a fixed mode of 1080x2520@60.
> >> +config DRM_PANEL_SAMSUNG_SOFEF03
> >> + tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
> >> + depends on GPIOLIB
> >> + depends on OF
> >> + depends on DRM_MIPI_DSI
> >> + depends on BACKLIGHT_CLASS_DEVICE
> >> + help
> >> + Say Y or M here if you want to enable support for the Samsung AMOLED
> >> + command mode panel found in the Sony Xperia 5 II smartphone.
> >> +
> >> + This panel uses Display Stream Compression 1.1.
> >> +
> >> + The panel features a 1080x2520@60 and 1080x2520@120 mode.
> >> +
> >> config DRM_PANEL_SEIKO_43WVF1G
> >> tristate "Seiko 43WVF1G panel"
> >> depends on OF
> >> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> >> index a4039d0fc9cfb..52dcd82e33120 100644
> >> --- a/drivers/gpu/drm/panel/Makefile
> >> +++ b/drivers/gpu/drm/panel/Makefile
> >> @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
> >> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
> >> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
> >> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
> >> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
> >> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
> >> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> >> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
> >> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
> >> new file mode 100644
> >> index 0000000000000..2763e1c56b37b
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
> >> @@ -0,0 +1,423 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (c) 2022 Konrad Dybcio <[email protected]>
> >> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> >> + */
> >> +
> >> +#include <linux/backlight.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/gpio/consumer.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.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>
> >> +
> >> +static const bool enable_120hz = true;
>
> Maybe this can be a module parameter ? Can you explain why this can't be dynamically changed by a modeset ?
>
> >> +
> >> +struct samsung_sofef03_m {
> >> + struct drm_panel panel;
> >> + struct mipi_dsi_device *dsi;
> >> + struct regulator *vddio, *vci;
> >> + struct gpio_desc *reset_gpio;
> >> + bool prepared;
> >> +};
> >> +
> >> +static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
> >> +{
> >> + return container_of(panel, struct samsung_sofef03_m, panel);
> >> +}
> >> +
> >> +static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
> >> +{
> >> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> >> + usleep_range(10000, 11000);
> >> +}
> >> +
> >> +static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
> >> +{
> >> + struct mipi_dsi_device *dsi = ctx->dsi;
> >> + struct device *dev = &dsi->dev;
> >> + int ret;
> >> +
> >> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
> >> +
> >> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> >> + return ret;
> >> + }
> >> + usleep_range(10000, 11000);
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> >> +
> >> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set column address: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set page address: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to set display brightness: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> >> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
> >> + mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
> >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> >> + msleep(110);
> >> +
> >> + ret = mipi_dsi_dcs_set_display_on(dsi);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
> >> +{
> >> + struct mipi_dsi_device *dsi = ctx->dsi;
> >> + struct device *dev = &dsi->dev;
> >> + int ret;
> >> +
> >> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> >> +
> >> + ret = mipi_dsi_dcs_set_display_off(dsi);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to turn display off: %d\n", ret);
> >> + return ret;
> >> + }
> >> + msleep(20);
> >> +
> >> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> >> + return ret;
> >> + }
> >> + msleep(100);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int samsung_sofef03_m_prepare(struct drm_panel *panel)
> >> +{
> >> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
> >> + struct drm_dsc_picture_parameter_set pps;
> >> + struct device *dev = &ctx->dsi->dev;
> >> + int ret;
> >> +
> >> + if (ctx->prepared)
> >> + return 0;
> >> +
> >> + ret = regulator_enable(ctx->vddio);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> + ret = regulator_enable(ctx->vci);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
> >> + regulator_disable(ctx->vddio);
> >> + return ret;
> >> + }
> >> +
> >> + samsung_sofef03_m_reset(ctx);
> >> +
> >> + ret = samsung_sofef03_m_on(ctx);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
> >> + goto fail;
> >> + }
> >> +
> >> + if (ctx->dsi->dsc) {
> >
> > Always true
> >
> >> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
> >> +
> >> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
> >> + if (ret < 0) {
> >> + dev_err(dev, "failed to transmit PPS: %d\n", ret);
> >> + goto fail;
> >> + }
> >> +
> >> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to enable compression mode: %d\n", ret);
> >> + goto fail;
> >> + }
> >> +
> >> + msleep(28);
> >> + }
> >> +
> >> + ctx->prepared = true;
> >> + return 0;
> >> +
> >> +fail:
> >> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> >> + regulator_disable(ctx->vci);
> >> + regulator_disable(ctx->vddio);
> >> + return ret;
> >> +}
> >> +
> >> +static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
> >> +{
> >> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
> >> + struct device *dev = &ctx->dsi->dev;
> >> + int ret;
> >> +
> >> + if (!ctx->prepared)
> >> + return 0;
> >> +
> >> + ret = samsung_sofef03_m_off(ctx);
> >> + if (ret < 0)
> >> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
> >> +
> >> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> >> + regulator_disable(ctx->vci);
> >> + regulator_disable(ctx->vddio);
> >> +
> >> + ctx->prepared = false;
> >> + return 0;
> >> +}
> >> +
> >> +static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
> >> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> >> + .hdisplay = 1080,
> >> + .hsync_start = 1080 + 156,
> >> + .hsync_end = 1080 + 156 + 8,
> >> + .htotal = 1080 + 156 + 8 + 8,
> >> + .vdisplay = 2520,
> >> + .vsync_start = 2520 + 2393,
> >> + .vsync_end = 2520 + 2393 + 8,
> >> + .vtotal = 2520 + 2393 + 8 + 8,
> >> + .width_mm = 61,
> >> + .height_mm = 142,
> >> +};
> >> +
> >> +static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
> >> + .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
> >> + .hdisplay = 1080,
> >> + .hsync_start = 1080 + 56,
> >> + .hsync_end = 1080 + 56 + 8,
> >> + .htotal = 1080 + 56 + 8 + 8,
> >> + .vdisplay = 2520,
> >> + .vsync_start = 2520 + 499,
> >> + .vsync_end = 2520 + 499 + 8,
> >> + .vtotal = 2520 + 499 + 8 + 8,
> >> + .width_mm = 61,
> >> + .height_mm = 142,
> >> +};
> >> +
> >> +static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
> >> + struct drm_connector *connector)
> >> +{
> >> + if (enable_120hz)
> >
> > Is it possible to switch between these modes at runtime? It might be logical to define 60 Hz mode as preferred, while allowing users to switch to 120 Hz when required for some reason.
Current panel API does not have a way to handle modesetting. All
callbacks only get the panel as an input, state is left aside.
Probably it's time to add atomic_*() versions of these callbacks, so
that one can pass the state and maybe a connector?
Or maybe the panel should get lightweight state too, like bridges do.
> >
> >> + return drm_connector_helper_get_modes_fixed(connector,
> >> + &samsung_sofef03_m_120hz_mode);
> >> + else
> >> + return drm_connector_helper_get_modes_fixed(connector,
> >> + &samsung_sofef03_m_60hz_mode);
> >> +}
--
With best wishes
Dmitry
On 22.05.2023 03:23, Dmitry Baryshkov wrote:
> On 22/05/2023 00:23, Marijn Suijten wrote:
>> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
>> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
>> 120hz refresh-rate mode.
>>
>> Co-developed-by: Konrad Dybcio <[email protected]>
>
> Konrad's S-o-b is also required then
I guess I should use the matching email, so:
Signed-off-by: Konrad Dybcio <[email protected]>
Konrad
>
>> Signed-off-by: Marijn Suijten <[email protected]>
>> ---
>> drivers/gpu/drm/panel/Kconfig | 14 +
>> drivers/gpu/drm/panel/Makefile | 1 +
>> drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
>> 3 files changed, 438 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 3f11e9906f2cb..8e2668153bce2 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
>> This panel features a fixed mode of 1080x2520@60.
>> +config DRM_PANEL_SAMSUNG_SOFEF03
>> + tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
>> + depends on GPIOLIB
>> + depends on OF
>> + depends on DRM_MIPI_DSI
>> + depends on BACKLIGHT_CLASS_DEVICE
>> + help
>> + Say Y or M here if you want to enable support for the Samsung AMOLED
>> + command mode panel found in the Sony Xperia 5 II smartphone.
>> +
>> + This panel uses Display Stream Compression 1.1.
>> +
>> + The panel features a 1080x2520@60 and 1080x2520@120 mode.
>> +
>> config DRM_PANEL_SEIKO_43WVF1G
>> tristate "Seiko 43WVF1G panel"
>> depends on OF
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index a4039d0fc9cfb..52dcd82e33120 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
>> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
>> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
>> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
>> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
>> new file mode 100644
>> index 0000000000000..2763e1c56b37b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
>> @@ -0,0 +1,423 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2022 Konrad Dybcio <[email protected]>
>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>> + */
>> +
>> +#include <linux/backlight.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/of.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>
>> +
>> +static const bool enable_120hz = true;
>> +
>> +struct samsung_sofef03_m {
>> + struct drm_panel panel;
>> + struct mipi_dsi_device *dsi;
>> + struct regulator *vddio, *vci;
>> + struct gpio_desc *reset_gpio;
>> + bool prepared;
>> +};
>> +
>> +static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
>> +{
>> + return container_of(panel, struct samsung_sofef03_m, panel);
>> +}
>> +
>> +static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
>> +{
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> + usleep_range(10000, 11000);
>> +}
>> +
>> +static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
>> +
>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + usleep_range(10000, 11000);
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
>> + mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
>> + mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> +
>> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set display brightness: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
>> + mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
>> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
>> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
>> + mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> + msleep(110);
>> +
>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_set_display_off(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display off: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(20);
>> +
>> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(100);
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef03_m_prepare(struct drm_panel *panel)
>> +{
>> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
>> + struct drm_dsc_picture_parameter_set pps;
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (ctx->prepared)
>> + return 0;
>> +
>> + ret = regulator_enable(ctx->vddio);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = regulator_enable(ctx->vci);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> + }
>> +
>> + samsung_sofef03_m_reset(ctx);
>> +
>> + ret = samsung_sofef03_m_on(ctx);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + if (ctx->dsi->dsc) {
>
> Always true
>
>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>> +
>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>> + if (ret < 0) {
>> + dev_err(dev, "failed to transmit PPS: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable compression mode: %d\n", ret);
>> + goto fail;
>> + }
>> +
>> + msleep(28);
>> + }
>> +
>> + ctx->prepared = true;
>> + return 0;
>> +
>> +fail:
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + regulator_disable(ctx->vci);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> +}
>> +
>> +static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
>> +{
>> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (!ctx->prepared)
>> + return 0;
>> +
>> + ret = samsung_sofef03_m_off(ctx);
>> + if (ret < 0)
>> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
>> +
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + regulator_disable(ctx->vci);
>> + regulator_disable(ctx->vddio);
>> +
>> + ctx->prepared = false;
>> + return 0;
>> +}
>> +
>> +static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
>> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
>> + .hdisplay = 1080,
>> + .hsync_start = 1080 + 156,
>> + .hsync_end = 1080 + 156 + 8,
>> + .htotal = 1080 + 156 + 8 + 8,
>> + .vdisplay = 2520,
>> + .vsync_start = 2520 + 2393,
>> + .vsync_end = 2520 + 2393 + 8,
>> + .vtotal = 2520 + 2393 + 8 + 8,
>> + .width_mm = 61,
>> + .height_mm = 142,
>> +};
>> +
>> +static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
>> + .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
>> + .hdisplay = 1080,
>> + .hsync_start = 1080 + 56,
>> + .hsync_end = 1080 + 56 + 8,
>> + .htotal = 1080 + 56 + 8 + 8,
>> + .vdisplay = 2520,
>> + .vsync_start = 2520 + 499,
>> + .vsync_end = 2520 + 499 + 8,
>> + .vtotal = 2520 + 499 + 8 + 8,
>> + .width_mm = 61,
>> + .height_mm = 142,
>> +};
>> +
>> +static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
>> + struct drm_connector *connector)
>> +{
>> + if (enable_120hz)
>
> Is it possible to switch between these modes at runtime? It might be logical to define 60 Hz mode as preferred, while allowing users to switch to 120 Hz when required for some reason.
>
>> + return drm_connector_helper_get_modes_fixed(connector,
>> + &samsung_sofef03_m_120hz_mode);
>> + else
>> + return drm_connector_helper_get_modes_fixed(connector,
>> + &samsung_sofef03_m_60hz_mode);
>> +}
>> +
>> +static const struct drm_panel_funcs samsung_sofef03_m_panel_funcs = {
>> + .prepare = samsung_sofef03_m_prepare,
>> + .unprepare = samsung_sofef03_m_unprepare,
>> + .get_modes = samsung_sofef03_m_get_modes,
>> +};
>> +
>> +static int samsung_sofef03_m_bl_update_status(struct backlight_device *bl)
>> +{
>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>> + u16 brightness = backlight_get_brightness(bl);
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + pr_err("Writing %#x\n", brightness);
>> +
>> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef03_m_bl_get_brightness(struct backlight_device *bl)
>> +{
>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>> + u16 brightness;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + pr_err("Read display brightness %#x\n", brightness);
>> +
>> + return brightness;
>> +}
>> +
>> +static const struct backlight_ops samsung_sofef03_m_bl_ops = {
>> + .update_status = samsung_sofef03_m_bl_update_status,
>> + .get_brightness = samsung_sofef03_m_bl_get_brightness,
>> +};
>> +
>> +static struct backlight_device *
>> +samsung_sofef03_m_create_backlight(struct mipi_dsi_device *dsi)
>> +{
>> + struct device *dev = &dsi->dev;
>> + const struct backlight_properties props = {
>> + .type = BACKLIGHT_RAW,
>> + .brightness = 100,
>> + .max_brightness = 1023,
>> + };
>> +
>> + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
>> + &samsung_sofef03_m_bl_ops, &props);
>> +}
>> +
>> +static int samsung_sofef03_m_probe(struct mipi_dsi_device *dsi)
>> +{
>> + struct device *dev = &dsi->dev;
>> + struct drm_dsc_config *dsc;
>> + struct samsung_sofef03_m *ctx;
>> + int ret;
>> +
>> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> + if (!ctx)
>> + return -ENOMEM;
>> +
>> + ctx->vddio = devm_regulator_get(dev, "vddio");
>> + if (IS_ERR(ctx->vddio))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
>> + "Failed to get vddio regulator\n");
>> +
>> + ctx->vci = devm_regulator_get(dev, "vci");
>> + if (IS_ERR(ctx->vci))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vci),
>> + "Failed to get vci regulator\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;
>> +
>> + drm_panel_init(&ctx->panel, dev, &samsung_sofef03_m_panel_funcs,
>> + DRM_MODE_CONNECTOR_DSI);
>> +
>> + ctx->panel.backlight = samsung_sofef03_m_create_backlight(dsi);
>> + 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 = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>
> Double assignment
>
>> + if (!dsc)
>> + return -ENOMEM;
>> +
>> + dsc->dsc_version_major = 1;
>> + dsc->dsc_version_minor = 1;
>> +
>> + dsc->slice_height = 30;
>> + dsc->slice_width = 540;
>> + dsc->slice_count = 2;
>> + dsc->bits_per_component = 8;
>> + dsc->bits_per_pixel = 8 << 4; /* 4 fractional bits */
>> + 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 samsung_sofef03_m_remove(struct mipi_dsi_device *dsi)
>> +{
>> + struct samsung_sofef03_m *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 samsung_sofef03_m_of_match[] = {
>> + { .compatible = "samsung,sofef03-m" },
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, samsung_sofef03_m_of_match);
>> +
>> +static struct mipi_dsi_driver samsung_sofef03_m_driver = {
>> + .probe = samsung_sofef03_m_probe,
>> + .remove = samsung_sofef03_m_remove,
>> + .driver = {
>> + .name = "panel-samsung-sofef03-m",
>> + .of_match_table = samsung_sofef03_m_of_match,
>> + },
>> +};
>> +module_mipi_dsi_driver(samsung_sofef03_m_driver);
>> +
>> +MODULE_AUTHOR("Konrad Dybcio <[email protected]>");
>> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
>> +MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF03-M Display-IC panels");
>> +MODULE_LICENSE("GPL");
>>
>
On 22.05.2023 03:19, Dmitry Baryshkov wrote:
> On 22/05/2023 00:23, Marijn Suijten wrote:
>> This SOFEF01-M Display-IC driver supports two modes with different
>> compatibles to differentiate between slightly different physical sizes
>> (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
>>
>> It is currently also used to hardcode significantly higher fake porches
>> for the Xperia 5, which are unused in transfers due to this being a
>> command-mode panel but do have an effect on the clock rates set by
>> dsi_host.c. Without higher clock rates this panel fails to achieve
>> 60fps and has significant tearing artifacts, while the same calculated
>> clock rate works perfectly fine on the Xperia 10 II.
>>
>> Signed-off-by: Marijn Suijten <[email protected]>
>> ---
>> drivers/gpu/drm/panel/Kconfig | 12 +
>> drivers/gpu/drm/panel/Makefile | 1 +
>> drivers/gpu/drm/panel/panel-samsung-sofef01.c | 360 ++++++++++++++++++++++++++
>> 3 files changed, 373 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>> index 18bd116e78a71..3f11e9906f2cb 100644
>> --- a/drivers/gpu/drm/panel/Kconfig
>> +++ b/drivers/gpu/drm/panel/Kconfig
>> @@ -618,6 +618,18 @@ config DRM_PANEL_SAMSUNG_SOFEF00
>> The panels are 2280x1080@60Hz and 2340x1080@60Hz respectively
>> +config DRM_PANEL_SAMSUNG_SOFEF01
>> + tristate "Samsung sofef01 Sony Xperia 5 / 10 II DSI cmd mode panels"
>> + depends on GPIOLIB
>> + depends on OF
>> + depends on DRM_MIPI_DSI
>> + depends on BACKLIGHT_CLASS_DEVICE
>> + help
>> + Say Y or M here if you want to enable support for the Samsung AMOLED
>> + command mode panels found in the Sony Xperia 5 / 10 II smartphones.
>> +
>> + This panel features a fixed mode of 1080x2520@60.
>> +
>> config DRM_PANEL_SEIKO_43WVF1G
>> tristate "Seiko 43WVF1G panel"
>> depends on OF
>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>> index 85133f73558f3..a4039d0fc9cfb 100644
>> --- a/drivers/gpu/drm/panel/Makefile
>> +++ b/drivers/gpu/drm/panel/Makefile
>> @@ -62,6 +62,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E63M0_DSI) += panel-samsung-s6e63m0-dsi.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams452ef01.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
>> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
>> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
>> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
>> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef01.c b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
>> new file mode 100644
>> index 0000000000000..18dc67a301a7b
>> --- /dev/null
>> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
>> @@ -0,0 +1,360 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +// Copyright (c) 2023 Marijn Suijten <[email protected]>
>> +
>> +#include <linux/backlight.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/of.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>
>> +
>> +struct samsung_sofef01_m {
>> + struct drm_panel panel;
>> + struct mipi_dsi_device *dsi;
>> + struct regulator *vddio, *vci;
>> + struct gpio_desc *reset_gpio;
>> + const struct drm_display_mode *mode;
>> + bool prepared;
>> +};
>> +
>> +static inline struct samsung_sofef01_m *to_samsung_sofef01_m(struct drm_panel *panel)
>> +{
>> + return container_of(panel, struct samsung_sofef01_m, panel);
>> +}
>> +
>> +static void samsung_sofef01_m_reset(struct samsung_sofef01_m *ctx)
>> +{
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>> + usleep_range(10000, 11000);
>> +}
>> +
>> +static int samsung_sofef01_m_on(struct samsung_sofef01_m *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + usleep_range(10000, 11000);
>> +
>> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x03);
>> + mipi_dsi_dcs_write_seq(dsi, 0xe0, 0x01);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> +
>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0x0000, 2520 - 1);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xbe, 0x92, 0x29);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x06);
>> + mipi_dsi_dcs_write_seq(dsi, 0xb6, 0x90);
>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>> + msleep(110);
>> +
>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef01_m_off(struct samsung_sofef01_m *ctx)
>> +{
>> + struct mipi_dsi_device *dsi = ctx->dsi;
>> + struct device *dev = &dsi->dev;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_set_display_off(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to turn display off: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(20);
>> +
>> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
>> + return ret;
>> + }
>> + msleep(120);
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef01_m_prepare(struct drm_panel *panel)
>> +{
>> + struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (ctx->prepared)
>> + return 0;
>> +
>> + ret = regulator_enable(ctx->vddio);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = regulator_enable(ctx->vci);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> + }
>> +
>> + samsung_sofef01_m_reset(ctx);
>> +
>> + ret = samsung_sofef01_m_on(ctx);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + regulator_disable(ctx->vci);
>> + regulator_disable(ctx->vddio);
>> + return ret;
>> + }
>> +
>> + ctx->prepared = true;
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef01_m_unprepare(struct drm_panel *panel)
>> +{
>> + struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
>> + struct device *dev = &ctx->dsi->dev;
>> + int ret;
>> +
>> + if (!ctx->prepared)
>> + return 0;
>> +
>> + ret = samsung_sofef01_m_off(ctx);
>> + if (ret < 0)
>> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
>> +
>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>> + regulator_disable(ctx->vci);
>> + regulator_disable(ctx->vddio);
>> +
>> + ctx->prepared = false;
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef01_m_get_modes(struct drm_panel *panel,
>> + struct drm_connector *connector)
>> +{
>> + struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
>> +
>> + return drm_connector_helper_get_modes_fixed(connector, ctx->mode);
>> +}
>> +
>> +static const struct drm_panel_funcs samsung_sofef01_m_panel_funcs = {
>> + .prepare = samsung_sofef01_m_prepare,
>> + .unprepare = samsung_sofef01_m_unprepare,
>> + .get_modes = samsung_sofef01_m_get_modes,
>> +};
>> +
>> +static int samsung_sofef01_m_bl_update_status(struct backlight_device *bl)
>> +{
>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>> + u16 brightness = backlight_get_brightness(bl);
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
>> + if (ret < 0)
>> + return ret;
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + return 0;
>> +}
>> +
>> +static int samsung_sofef01_m_bl_get_brightness(struct backlight_device *bl)
>> +{
>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>> + u16 brightness;
>> + int ret;
>> +
>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>> +
>> + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
>> +
>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>> +
>> + if (ret < 0)
>> + return ret;
>> +
>> + return brightness;
>> +}
>> +
>> +static const struct backlight_ops samsung_sofef01_m_bl_ops = {
>> + .update_status = samsung_sofef01_m_bl_update_status,
>> + .get_brightness = samsung_sofef01_m_bl_get_brightness,
>> +};
>> +
>> +static struct backlight_device *
>> +samsung_sofef01_m_create_backlight(struct mipi_dsi_device *dsi)
>> +{
>> + struct device *dev = &dsi->dev;
>> + const struct backlight_properties props = {
>> + .type = BACKLIGHT_RAW,
>> + .brightness = 100,
>> + .max_brightness = 1023,
>> + };
>> +
>> + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
>> + &samsung_sofef01_m_bl_ops, &props);
>> +}
>> +
>> +static int samsung_sofef01_m_probe(struct mipi_dsi_device *dsi)
>> +{
>> + struct device *dev = &dsi->dev;
>> + struct samsung_sofef01_m *ctx;
>> + int ret;
>> +
>> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
>> + if (!ctx)
>> + return -ENOMEM;
>> +
>> + ctx->vddio = devm_regulator_get(dev, "vddio");
>> + if (IS_ERR(ctx->vddio))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vddio),
>> + "Failed to get vddio regulator\n");
>> +
>> + ctx->vci = devm_regulator_get(dev, "vci");
>> + if (IS_ERR(ctx->vci))
>> + return dev_err_probe(dev, PTR_ERR(ctx->vci),
>> + "Failed to get vci regulator\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;
>> + ctx->mode = of_device_get_match_data(dev);
>> + mipi_dsi_set_drvdata(dsi, ctx);
>> +
>> + dsi->lanes = 4;
>> + dsi->format = MIPI_DSI_FMT_RGB888;
>> + dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
>> +
>> + drm_panel_init(&ctx->panel, dev, &samsung_sofef01_m_panel_funcs,
>> + DRM_MODE_CONNECTOR_DSI);
>> +
>> + ctx->panel.backlight = samsung_sofef01_m_create_backlight(dsi);
>> + 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);
>> +
>> + 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 samsung_sofef01_m_remove(struct mipi_dsi_device *dsi)
>> +{
>> + struct samsung_sofef01_m *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);
>> +}
>> +
>> +/* Sony Xperia 5 (kumano bahamut) */
>> +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
>> + /*
>> + * WARNING: These massive porches are wrong/useless for CMDmode
>> + * (and not defined in downstream DTS) but necessary to bump dsi
>> + * clocks higher, so that we can achieve proper 60fps without tearing.
>> + */
>> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
>> + .hdisplay = 1080,
>> + .hsync_start = 1080 + 156,
>> + .hsync_end = 1080 + 156 + 8,
>> + .htotal = 1080 + 156 + 8 + 8,
>> + .vdisplay = 2520,
>> + .vsync_start = 2520 + 2393,
>> + .vsync_end = 2520 + 2393 + 8,
>> + .vtotal = 2520 + 2393 + 8 + 8,
>> + .width_mm = 61,
>> + .height_mm = 142,
>> +};
>> +
>> +/* Sony Xperia 10 II (seine pdx201) */
>> +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
>> + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
>> + .hdisplay = 1080,
>> + .hsync_start = 1080 + 8,
>> + .hsync_end = 1080 + 8 + 8,
>> + .htotal = 1080 + 8 + 8 + 8,
>> + .vdisplay = 2520,
>> + .vsync_start = 2520 + 8,
>> + .vsync_end = 2520 + 8 + 8,
>> + .vtotal = 2520 + 8 + 8 + 8,
>> + .width_mm = 60,
>> + .height_mm = 139,
>> +};
>> +
>> +static const struct of_device_id samsung_sofef01_m_of_match[] = {
>> + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
>> + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
>
> Are there really two panels? Can we use one mode for both usecases?
The porches differ by a significant margin but that may or may not
matter for cmd mode.. If we come to unify them, one can add width-mm
(or something like that) in the device tree if that turns out to be
the only difference.
Konrad
>
>> + { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, samsung_sofef01_m_of_match);
>> +
>> +static struct mipi_dsi_driver samsung_sofef01_m_driver = {
>> + .probe = samsung_sofef01_m_probe,
>> + .remove = samsung_sofef01_m_remove,
>> + .driver = {
>> + .name = "panel-samsung-sofef01-m",
>> + .of_match_table = samsung_sofef01_m_of_match,
>> + },
>> +};
>> +module_mipi_dsi_driver(samsung_sofef01_m_driver);
>> +
>> +MODULE_AUTHOR("Marijn Suijten <[email protected]>");
>> +MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF01-M Display-IC panels");
>> +MODULE_LICENSE("GPL");
>>
>
On 2023-05-22 04:19:45, Dmitry Baryshkov wrote:
> On 22/05/2023 00:23, Marijn Suijten wrote:
> > This SOFEF01-M Display-IC driver supports two modes with different
> > compatibles to differentiate between slightly different physical sizes
> > (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
> >
> > It is currently also used to hardcode significantly higher fake porches
> > for the Xperia 5, which are unused in transfers due to this being a
> > command-mode panel but do have an effect on the clock rates set by
> > dsi_host.c. Without higher clock rates this panel fails to achieve
> > 60fps and has significant tearing artifacts, while the same calculated
> > clock rate works perfectly fine on the Xperia 10 II.
<snip>
> > +/* Sony Xperia 5 (kumano bahamut) */
> > +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
> > + /*
> > + * WARNING: These massive porches are wrong/useless for CMDmode
> > + * (and not defined in downstream DTS) but necessary to bump dsi
> > + * clocks higher, so that we can achieve proper 60fps without tearing.
> > + */
> > + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> > + .hdisplay = 1080,
> > + .hsync_start = 1080 + 156,
> > + .hsync_end = 1080 + 156 + 8,
> > + .htotal = 1080 + 156 + 8 + 8,
> > + .vdisplay = 2520,
> > + .vsync_start = 2520 + 2393,
> > + .vsync_end = 2520 + 2393 + 8,
> > + .vtotal = 2520 + 2393 + 8 + 8,
> > + .width_mm = 61,
> > + .height_mm = 142,
> > +};
> > +
> > +/* Sony Xperia 10 II (seine pdx201) */
> > +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
> > + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
> > + .hdisplay = 1080,
> > + .hsync_start = 1080 + 8,
> > + .hsync_end = 1080 + 8 + 8,
> > + .htotal = 1080 + 8 + 8 + 8,
> > + .vdisplay = 2520,
> > + .vsync_start = 2520 + 8,
> > + .vsync_end = 2520 + 8 + 8,
> > + .vtotal = 2520 + 8 + 8 + 8,
> > + .width_mm = 60,
> > + .height_mm = 139,
> > +};
> > +
> > +static const struct of_device_id samsung_sofef01_m_of_match[] = {
> > + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
> > + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
>
> Are there really two panels? Can we use one mode for both usecases?
See the commit description where I explained exactly this: the panels
have different dimensions (6.1" vs 6.0", hence different DPI) and I also
abuse this to hack in higher clock rates via fake porches.
I just ended up on a scary website that supposedly contains the panel
names:
- Xperia 5 (bahamut, 6.1"): AMB609TC01
- Xperia 10 II (pdx201, 6.0"): AMS597UT01
- Marijn
On 2023-05-22 18:30:08, Konrad Dybcio wrote:
> On 22.05.2023 03:19, Dmitry Baryshkov wrote:
> > On 22/05/2023 00:23, Marijn Suijten wrote:
> >> This SOFEF01-M Display-IC driver supports two modes with different
> >> compatibles to differentiate between slightly different physical sizes
> >> (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
> >>
> >> It is currently also used to hardcode significantly higher fake porches
> >> for the Xperia 5, which are unused in transfers due to this being a
> >> command-mode panel but do have an effect on the clock rates set by
> >> dsi_host.c.? Without higher clock rates this panel fails to achieve
> >> 60fps and has significant tearing artifacts, while the same calculated
> >> clock rate works perfectly fine on the Xperia 10 II.
<snip>
> >> +/* Sony Xperia 5 (kumano bahamut) */
> >> +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
> >> +??? /*
> >> +???? * WARNING: These massive porches are wrong/useless for CMDmode
> >> +???? * (and not defined in downstream DTS) but necessary to bump dsi
> >> +???? * clocks higher, so that we can achieve proper 60fps without tearing.
> >> +???? */
> >> +??? .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> >> +??? .hdisplay = 1080,
> >> +??? .hsync_start = 1080 + 156,
> >> +??? .hsync_end = 1080 + 156 + 8,
> >> +??? .htotal = 1080 + 156 + 8 + 8,
> >> +??? .vdisplay = 2520,
> >> +??? .vsync_start = 2520 + 2393,
> >> +??? .vsync_end = 2520 + 2393 + 8,
> >> +??? .vtotal = 2520 + 2393 + 8 + 8,
> >> +??? .width_mm = 61,
> >> +??? .height_mm = 142,
> >> +};
> >> +
> >> +/* Sony Xperia 10 II (seine pdx201) */
> >> +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
> >> +??? .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
> >> +??? .hdisplay = 1080,
> >> +??? .hsync_start = 1080 + 8,
> >> +??? .hsync_end = 1080 + 8 + 8,
> >> +??? .htotal = 1080 + 8 + 8 + 8,
> >> +??? .vdisplay = 2520,
> >> +??? .vsync_start = 2520 + 8,
> >> +??? .vsync_end = 2520 + 8 + 8,
> >> +??? .vtotal = 2520 + 8 + 8 + 8,
> >> +??? .width_mm = 60,
> >> +??? .height_mm = 139,
> >> +};
> >> +
> >> +static const struct of_device_id samsung_sofef01_m_of_match[] = {
> >> +??? { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
> >> +??? { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
> >
> > Are there really two panels? Can we use one mode for both usecases?
> The porches differ by a significant margin but that may or may not
> matter for cmd mode.. If we come to unify them, one can add width-mm
> (or something like that) in the device tree if that turns out to be
> the only difference.
See patch description: they matter in that I can abuse them to force a
higher pclk on the DSI, otherwise the Xperia 5 refuses to vsync at 60fps
(and has artifacts) while the Xperia 10 II runs flawless (but I should
check the clock tree to confirm that the value is the same).
Downstream has:
qcom,mdss-dsi-panel-clockrate = <1132293600>;
But that is for the bitclk, which should theoretically be multiplied by
lanes=4 and divided by bpp=24 for the pclk, and divided by 8 for the
byte clock (without being multiplied by lanes...?).
I do think I have the panel names now, which we could use to
differentiate these actually-different panels on the same Display-IC
instead.
- Marijn
On Tue, 23 May 2023 at 01:32, Marijn Suijten
<[email protected]> wrote:
>
> On 2023-05-22 04:19:45, Dmitry Baryshkov wrote:
> > On 22/05/2023 00:23, Marijn Suijten wrote:
> > > This SOFEF01-M Display-IC driver supports two modes with different
> > > compatibles to differentiate between slightly different physical sizes
> > > (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
> > >
> > > It is currently also used to hardcode significantly higher fake porches
> > > for the Xperia 5, which are unused in transfers due to this being a
> > > command-mode panel but do have an effect on the clock rates set by
> > > dsi_host.c. Without higher clock rates this panel fails to achieve
> > > 60fps and has significant tearing artifacts, while the same calculated
> > > clock rate works perfectly fine on the Xperia 10 II.
>
> <snip>
>
> > > +/* Sony Xperia 5 (kumano bahamut) */
> > > +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
> > > + /*
> > > + * WARNING: These massive porches are wrong/useless for CMDmode
> > > + * (and not defined in downstream DTS) but necessary to bump dsi
> > > + * clocks higher, so that we can achieve proper 60fps without tearing.
> > > + */
> > > + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> > > + .hdisplay = 1080,
> > > + .hsync_start = 1080 + 156,
> > > + .hsync_end = 1080 + 156 + 8,
> > > + .htotal = 1080 + 156 + 8 + 8,
> > > + .vdisplay = 2520,
> > > + .vsync_start = 2520 + 2393,
> > > + .vsync_end = 2520 + 2393 + 8,
> > > + .vtotal = 2520 + 2393 + 8 + 8,
> > > + .width_mm = 61,
> > > + .height_mm = 142,
> > > +};
> > > +
> > > +/* Sony Xperia 10 II (seine pdx201) */
> > > +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
> > > + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
> > > + .hdisplay = 1080,
> > > + .hsync_start = 1080 + 8,
> > > + .hsync_end = 1080 + 8 + 8,
> > > + .htotal = 1080 + 8 + 8 + 8,
> > > + .vdisplay = 2520,
> > > + .vsync_start = 2520 + 8,
> > > + .vsync_end = 2520 + 8 + 8,
> > > + .vtotal = 2520 + 8 + 8 + 8,
> > > + .width_mm = 60,
> > > + .height_mm = 139,
> > > +};
> > > +
> > > +static const struct of_device_id samsung_sofef01_m_of_match[] = {
> > > + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
> > > + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
> >
> > Are there really two panels? Can we use one mode for both usecases?
>
> See the commit description where I explained exactly this: the panels
> have different dimensions (6.1" vs 6.0", hence different DPI) and I also
> abuse this to hack in higher clock rates via fake porches.
>
> I just ended up on a scary website that supposedly contains the panel
> names:
>
> - Xperia 5 (bahamut, 6.1"): AMB609TC01
> - Xperia 10 II (pdx201, 6.0"): AMS597UT01
Great! From the patch description it was not obvious if those are two
different panels or a single panel with slight difference in the glass
cover. With these names in place (well, with two distinct names in
place) it makes sense.
--
With best wishes
Dmitry
On 21/05/2023 22:23, Marijn Suijten wrote:
> As per the config name this Display IC features a DSI command-mode
> interface (or the command to switch to video mode is not
> known/documented) and does not use any of the video-mode helper
> utilities, hence should not select VIDEOMODE_HELPERS. In addition it
> uses devm_gpiod_get() and related functions from GPIOLIB.
>
> Fixes: 5933baa36e26 ("drm/panel/samsung-sofef00: Add panel for OnePlus 6/T devices")
> Signed-off-by: Marijn Suijten <[email protected]>
Reviewed-by: Caleb Connolly <[email protected]>
> ---
> drivers/gpu/drm/panel/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> index 2b9d6db7860ba..67ef898d133f2 100644
> --- a/drivers/gpu/drm/panel/Kconfig
> +++ b/drivers/gpu/drm/panel/Kconfig
> @@ -608,10 +608,10 @@ config DRM_PANEL_SAMSUNG_S6E8AA0
>
> config DRM_PANEL_SAMSUNG_SOFEF00
> tristate "Samsung sofef00/s6e3fc2x01 OnePlus 6/6T DSI cmd mode panels"
> + depends on GPIOLIB
> depends on OF
> depends on DRM_MIPI_DSI
> depends on BACKLIGHT_CLASS_DEVICE
> - select VIDEOMODE_HELPERS
> help
> Say Y or M here if you want to enable support for the Samsung AMOLED
> command mode panels found in the OnePlus 6/6T smartphones.
>
> --
> 2.40.1
>
--
Kind Regards,
Caleb
On 2023-05-23 01:56:46, Dmitry Baryshkov wrote:
> On Tue, 23 May 2023 at 01:32, Marijn Suijten
> <[email protected]> wrote:
> >
> > On 2023-05-22 04:19:45, Dmitry Baryshkov wrote:
> > > On 22/05/2023 00:23, Marijn Suijten wrote:
> > > > This SOFEF01-M Display-IC driver supports two modes with different
> > > > compatibles to differentiate between slightly different physical sizes
> > > > (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
> > > >
> > > > It is currently also used to hardcode significantly higher fake porches
> > > > for the Xperia 5, which are unused in transfers due to this being a
> > > > command-mode panel but do have an effect on the clock rates set by
> > > > dsi_host.c. Without higher clock rates this panel fails to achieve
> > > > 60fps and has significant tearing artifacts, while the same calculated
> > > > clock rate works perfectly fine on the Xperia 10 II.
> >
> > <snip>
> >
> > > > +/* Sony Xperia 5 (kumano bahamut) */
> > > > +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
> > > > + /*
> > > > + * WARNING: These massive porches are wrong/useless for CMDmode
> > > > + * (and not defined in downstream DTS) but necessary to bump dsi
> > > > + * clocks higher, so that we can achieve proper 60fps without tearing.
> > > > + */
> > > > + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> > > > + .hdisplay = 1080,
> > > > + .hsync_start = 1080 + 156,
> > > > + .hsync_end = 1080 + 156 + 8,
> > > > + .htotal = 1080 + 156 + 8 + 8,
> > > > + .vdisplay = 2520,
> > > > + .vsync_start = 2520 + 2393,
> > > > + .vsync_end = 2520 + 2393 + 8,
> > > > + .vtotal = 2520 + 2393 + 8 + 8,
> > > > + .width_mm = 61,
> > > > + .height_mm = 142,
> > > > +};
> > > > +
> > > > +/* Sony Xperia 10 II (seine pdx201) */
> > > > +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
> > > > + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
> > > > + .hdisplay = 1080,
> > > > + .hsync_start = 1080 + 8,
> > > > + .hsync_end = 1080 + 8 + 8,
> > > > + .htotal = 1080 + 8 + 8 + 8,
> > > > + .vdisplay = 2520,
> > > > + .vsync_start = 2520 + 8,
> > > > + .vsync_end = 2520 + 8 + 8,
> > > > + .vtotal = 2520 + 8 + 8 + 8,
> > > > + .width_mm = 60,
> > > > + .height_mm = 139,
> > > > +};
> > > > +
> > > > +static const struct of_device_id samsung_sofef01_m_of_match[] = {
> > > > + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
> > > > + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
> > >
> > > Are there really two panels? Can we use one mode for both usecases?
> >
> > See the commit description where I explained exactly this: the panels
> > have different dimensions (6.1" vs 6.0", hence different DPI) and I also
> > abuse this to hack in higher clock rates via fake porches.
> >
> > I just ended up on a scary website that supposedly contains the panel
> > names:
> >
> > - Xperia 5 (bahamut, 6.1"): AMB609TC01
> > - Xperia 10 II (pdx201, 6.0"): AMS597UT01
>
> Great! From the patch description it was not obvious if those are two
> different panels or a single panel with slight difference in the glass
> cover. With these names in place (well, with two distinct names in
> place) it makes sense.
For completeness: keep the current single file but embed these panel
names as suffix (eg. `samsung,sofef-01-m-am[bs]...`) to the compatible
(and document these more explicitly elsewhere)?
- Marijn
>
> --
> With best wishes
> Dmitry
On 2023-05-22 15:58:56, Dmitry Baryshkov wrote:
> On Mon, 22 May 2023 at 12:04, Neil Armstrong <[email protected]> wrote:
> >
> > On 22/05/2023 03:16, Dmitry Baryshkov wrote:
> > > On 22/05/2023 00:23, Marijn Suijten wrote:
> > >> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its Xperia
> > >> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
> > >>
> > >> This panel features Display Stream Compression 1.1.
> > >>
> > >> Signed-off-by: Marijn Suijten <[email protected]>
> > >> ---
> > >> drivers/gpu/drm/panel/Kconfig | 11 +
> > >> drivers/gpu/drm/panel/Makefile | 1 +
> > >> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++++++++
> > >> 3 files changed, 374 insertions(+)
> > >>
> > >> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > >> index 67ef898d133f2..18bd116e78a71 100644
> > >> --- a/drivers/gpu/drm/panel/Kconfig
> > >> +++ b/drivers/gpu/drm/panel/Kconfig
> > >> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
> > >> Say Y here if you want to enable support for the Sony ACX565AKM
> > >> 800x600 3.5" panel (found on the Nokia N900).
> > >> +config DRM_PANEL_SONY_AKATSUKI_LGD
> > >> + tristate "Sony Xperia XZ3 LGD panel"
> > >> + depends on GPIOLIB && OF
> > >> + depends on DRM_MIPI_DSI
> > >> + depends on BACKLIGHT_CLASS_DEVICE
> > >> + help
> > >> + Say Y here if you want to enable support for the Sony Xperia XZ3
> > >> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG Display.
> > >> +
> > >> + This panel uses Display Stream Compression 1.1.
> > >> +
> > >> config DRM_PANEL_SONY_TD4353_JDI
> > >> tristate "Sony TD4353 JDI panel"
> > >> depends on GPIOLIB && OF
> > >> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> > >> index ff169781e82d7..85133f73558f3 100644
> > >> --- a/drivers/gpu/drm/panel/Makefile
> > >> +++ b/drivers/gpu/drm/panel/Makefile
> > >> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
> > >> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
> > >> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
> > >> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
> > >> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
> > >> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
> > >> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
> > >> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
> > >> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> > >> new file mode 100644
> > >> index 0000000000000..f55788f963dab
> > >> --- /dev/null
> > >> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> > >> @@ -0,0 +1,362 @@
> > >> +// SPDX-License-Identifier: GPL-2.0-only
> > >> +/*
> > >> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> > >> + *
> > >> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
> > >> +
> > >> +struct sony_akatsuki_lgd {
> > >> + struct drm_panel panel;
> > >> + struct mipi_dsi_device *dsi;
> > >> + struct regulator *vddio;
> > >> + struct gpio_desc *reset_gpio;
> > >> + bool prepared;
> > >> +};
> > >> +
> > >> +static inline struct sony_akatsuki_lgd *to_sony_akatsuki_lgd(struct drm_panel *panel)
> > >> +{
> > >> + return container_of(panel, struct sony_akatsuki_lgd, panel);
> > >> +}
> > >> +
> > >> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
> > >> +{
> > >> + struct mipi_dsi_device *dsi = ctx->dsi;
> > >> + struct device *dev = &dsi->dev;
> > >> + int ret;
> > >> +
> > >> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > >> +
> > >> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
> > >> + /* Enable backlight control */
> > >> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> > >> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
> > >> +
> > >> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
> > >> + if (ret < 0) {
> > >> + dev_err(dev, "Failed to set column address: %d\n", ret);
> > >> + return ret;
> > >> + }
> > >> +
> > >> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
> > >> + if (ret < 0) {
> > >> + dev_err(dev, "Failed to set page address: %d\n", ret);
> > >> + return ret;
> > >> + }
> > >> +
> > >> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
> > >> +
> > >> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> > >> + if (ret < 0) {
> > >> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> > >> + return ret;
> > >> + }
> > >> +
> > >> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
> > >> +
> > >> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> > >> + if (ret < 0) {
> > >> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> > >> + return ret;
> > >> + }
> > >> + msleep(120);
> > >> +
> > >> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
> > >> +
> > >> + ret = mipi_dsi_dcs_set_display_on(dsi);
> > >> + if (ret < 0) {
> > >> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> > >> + return ret;
> > >> + }
> > >
> > > My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
> >
> >
> > No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
> >
>
> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
I have never investigated what it takes to split these functions, but
some of these panels do show some corruption at startup which may be
circumvented by powering the panel on after starting the video stream?
I'm just not sure where to make the split: downstream does describe a
qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
the latter only contains set_display_on() (not exit_sleep_mode()).
It is documented like:
same as "qcom,mdss-dsi-on-command" except commands are sent after
displaying an image."
So this seems like the right way to split them up, I'll test this out on
all submitted panel drivers.
- Marijn
>
> --
> With best wishes
> Dmitry
On 2023-05-22 11:08:12, Neil Armstrong wrote:
> On 22/05/2023 03:23, Dmitry Baryshkov wrote:
> > On 22/05/2023 00:23, Marijn Suijten wrote:
> >> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
> >> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
> >> 120hz refresh-rate mode.
> >>
> >> Co-developed-by: Konrad Dybcio <[email protected]>
> >
> > Konrad's S-o-b is also required then
I am unsure what to include here, since Konrad originally "authored" the
commit but I believe it was nothing more than a completely broken and
unusable driver spit out by "The mdss panel generator". This needed
enough rewriting that I don't feel like giving it much credit ;)
> >
> >> Signed-off-by: Marijn Suijten <[email protected]>
> >> ---
> >> ? drivers/gpu/drm/panel/Kconfig???????????????? |? 14 +
> >> ? drivers/gpu/drm/panel/Makefile??????????????? |?? 1 +
> >> ? drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
> >> ? 3 files changed, 438 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> >> index 3f11e9906f2cb..8e2668153bce2 100644
> >> --- a/drivers/gpu/drm/panel/Kconfig
> >> +++ b/drivers/gpu/drm/panel/Kconfig
> >> @@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
> >> ??????? This panel features a fixed mode of 1080x2520@60.
> >> +config DRM_PANEL_SAMSUNG_SOFEF03
> >> +??? tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
> >> +??? depends on GPIOLIB
> >> +??? depends on OF
> >> +??? depends on DRM_MIPI_DSI
> >> +??? depends on BACKLIGHT_CLASS_DEVICE
> >> +??? help
> >> +????? Say Y or M here if you want to enable support for the Samsung AMOLED
> >> +????? command mode panel found in the Sony Xperia 5 II smartphone.
> >> +
> >> +????? This panel uses Display Stream Compression 1.1.
> >> +
> >> +????? The panel features a 1080x2520@60 and 1080x2520@120 mode.
> >> +
> >> ? config DRM_PANEL_SEIKO_43WVF1G
> >> ????? tristate "Seiko 43WVF1G panel"
> >> ????? depends on OF
> >> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> >> index a4039d0fc9cfb..52dcd82e33120 100644
> >> --- a/drivers/gpu/drm/panel/Makefile
> >> +++ b/drivers/gpu/drm/panel/Makefile
> >> @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
> >> ? obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
> >> ? obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
> >> ? obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
> >> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
> >> ? obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
> >> ? obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
> >> ? obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
> >> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
> >> new file mode 100644
> >> index 0000000000000..2763e1c56b37b
> >> --- /dev/null
> >> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
> >> @@ -0,0 +1,423 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/*
> >> + * Copyright (c) 2022 Konrad Dybcio <[email protected]>
> >> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> >> + */
> >> +
> >> +#include <linux/backlight.h>
> >> +#include <linux/delay.h>
> >> +#include <linux/gpio/consumer.h>
> >> +#include <linux/module.h>
> >> +#include <linux/of.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>
> >> +
> >> +static const bool enable_120hz = true;
>
> Maybe this can be a module parameter ? Can you explain why this can't be dynamically changed by a modeset ?
>
> >> +
> >> +struct samsung_sofef03_m {
> >> +??? struct drm_panel panel;
> >> +??? struct mipi_dsi_device *dsi;
> >> +??? struct regulator *vddio, *vci;
> >> +??? struct gpio_desc *reset_gpio;
> >> +??? bool prepared;
> >> +};
> >> +
> >> +static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
> >> +{
> >> +??? return container_of(panel, struct samsung_sofef03_m, panel);
> >> +}
> >> +
> >> +static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
> >> +{
> >> +??? gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> >> +??? usleep_range(10000, 11000);
> >> +}
> >> +
> >> +static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
> >> +{
> >> +??? struct mipi_dsi_device *dsi = ctx->dsi;
> >> +??? struct device *dev = &dsi->dev;
> >> +??? int ret;
> >> +
> >> +??? dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> >> +
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
> >> +
> >> +??? ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +??? usleep_range(10000, 11000);
> >> +
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> >> +
> >> +??? ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to set tear on: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to set column address: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to set page address: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to set display brightness: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> >> +??? mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
> >> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> >> +??? msleep(110);
> >> +
> >> +??? ret = mipi_dsi_dcs_set_display_on(dsi);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to turn display on: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? return 0;
> >> +}
> >> +
> >> +static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
> >> +{
> >> +??? struct mipi_dsi_device *dsi = ctx->dsi;
> >> +??? struct device *dev = &dsi->dev;
> >> +??? int ret;
> >> +
> >> +??? dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> >> +
> >> +??? ret = mipi_dsi_dcs_set_display_off(dsi);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to turn display off: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +??? msleep(20);
> >> +
> >> +??? ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +??? msleep(100);
> >> +
> >> +??? return 0;
> >> +}
> >> +
> >> +static int samsung_sofef03_m_prepare(struct drm_panel *panel)
> >> +{
> >> +??? struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
> >> +??? struct drm_dsc_picture_parameter_set pps;
> >> +??? struct device *dev = &ctx->dsi->dev;
> >> +??? int ret;
> >> +
> >> +??? if (ctx->prepared)
> >> +??????? return 0;
> >> +
> >> +??? ret = regulator_enable(ctx->vddio);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? ret = regulator_enable(ctx->vci);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
> >> +??????? regulator_disable(ctx->vddio);
> >> +??????? return ret;
> >> +??? }
> >> +
> >> +??? samsung_sofef03_m_reset(ctx);
> >> +
> >> +??? ret = samsung_sofef03_m_on(ctx);
> >> +??? if (ret < 0) {
> >> +??????? dev_err(dev, "Failed to initialize panel: %d\n", ret);
> >> +??????? goto fail;
> >> +??? }
> >> +
> >> +??? if (ctx->dsi->dsc) {
> >
> > Always true
Would like to keep this as described in the Xperia XZ3 panel driver
review.
> >> +??????? drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
> >> +
> >> +??????? ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
> >> +??????? if (ret < 0) {
> >> +??????????? dev_err(dev, "failed to transmit PPS: %d\n", ret);
> >> +??????????? goto fail;
> >> +??????? }
> >> +
> >> +??????? ret = mipi_dsi_compression_mode(ctx->dsi, true);
> >> +??????? if (ret < 0) {
> >> +??????????? dev_err(dev, "Failed to enable compression mode: %d\n", ret);
> >> +??????????? goto fail;
> >> +??????? }
> >> +
> >> +??????? msleep(28);
> >> +??? }
> >> +
> >> +??? ctx->prepared = true;
> >> +??? return 0;
> >> +
> >> +fail:
> >> +??? gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> >> +??? regulator_disable(ctx->vci);
> >> +??? regulator_disable(ctx->vddio);
> >> +??? return ret;
> >> +}
> >> +
> >> +static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
> >> +{
> >> +??? struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
> >> +??? struct device *dev = &ctx->dsi->dev;
> >> +??? int ret;
> >> +
> >> +??? if (!ctx->prepared)
> >> +??????? return 0;
> >> +
> >> +??? ret = samsung_sofef03_m_off(ctx);
> >> +??? if (ret < 0)
> >> +??????? dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
> >> +
> >> +??? gpiod_set_value_cansleep(ctx->reset_gpio, 1);
> >> +??? regulator_disable(ctx->vci);
> >> +??? regulator_disable(ctx->vddio);
> >> +
> >> +??? ctx->prepared = false;
> >> +??? return 0;
> >> +}
> >> +
> >> +static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
> >> +??? .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> >> +??? .hdisplay = 1080,
> >> +??? .hsync_start = 1080 + 156,
> >> +??? .hsync_end = 1080 + 156 + 8,
> >> +??? .htotal = 1080 + 156 + 8 + 8,
> >> +??? .vdisplay = 2520,
> >> +??? .vsync_start = 2520 + 2393,
> >> +??? .vsync_end = 2520 + 2393 + 8,
> >> +??? .vtotal = 2520 + 2393 + 8 + 8,
> >> +??? .width_mm = 61,
> >> +??? .height_mm = 142,
> >> +};
> >> +
> >> +static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
> >> +??? .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
> >> +??? .hdisplay = 1080,
> >> +??? .hsync_start = 1080 + 56,
> >> +??? .hsync_end = 1080 + 56 + 8,
> >> +??? .htotal = 1080 + 56 + 8 + 8,
> >> +??? .vdisplay = 2520,
> >> +??? .vsync_start = 2520 + 499,
> >> +??? .vsync_end = 2520 + 499 + 8,
> >> +??? .vtotal = 2520 + 499 + 8 + 8,
> >> +??? .width_mm = 61,
> >> +??? .height_mm = 142,
> >> +};
> >> +
> >> +static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
> >> +?????????????????? struct drm_connector *connector)
> >> +{
> >> +??? if (enable_120hz)
> >
> > Is it possible to switch between these modes at runtime? It might be logical to define 60 Hz mode as preferred, while allowing users to switch to 120 Hz when required for some reason.
I'd love to define two modes but as described in the cover letter - and
the reason to send this as RFC - drm_panel currently does not expose the
mode back to the driver in e.g. _prepare, so I cannot update or resend
the necessary dcs commands to set **or change** the current mode
(downstream defines commands to change the rate without fully resetting
the panel).
I can either convert this to drm_bridge (is that easy / worth it?) or
hack it in with a global as done here so that we can tackle that
endeavour separately, while at least having the drivers upstream (or on
the lists) as requested by quic for DSC development.
> >> +??????? return drm_connector_helper_get_modes_fixed(connector,
> >> +??????????????????????????????? &samsung_sofef03_m_120hz_mode);
> >> +??? else
> >> +??????? return drm_connector_helper_get_modes_fixed(connector,
> >> +??????????????????????????????? &samsung_sofef03_m_60hz_mode);
> >> +}
> >> +
> >> +static const struct drm_panel_funcs samsung_sofef03_m_panel_funcs = {
> >> +??? .prepare = samsung_sofef03_m_prepare,
> >> +??? .unprepare = samsung_sofef03_m_unprepare,
> >> +??? .get_modes = samsung_sofef03_m_get_modes,
> >> +};
> >> +
> >> +static int samsung_sofef03_m_bl_update_status(struct backlight_device *bl)
> >> +{
> >> +??? struct mipi_dsi_device *dsi = bl_get_data(bl);
> >> +??? u16 brightness = backlight_get_brightness(bl);
> >> +??? int ret;
> >> +
> >> +??? dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> >> +
> >> +??? pr_err("Writing %#x\n", brightness);
>
> You forgot to remove those desbug prints :-p
RFC: I indeed forgot to mention this in the cover letter but this driver
is still mostly broken beyond displaying an image. Setting brightness
here does nothing (except after sending the command many times in quick
succession by e.g. a brightness slider in UI, the panel gets stuck on a
broken image) and the panel remained black if it wasn't for the random
set_brightness_large(100) in _prepare.
I will remove these in a followup once figuring out how to make and keep
it working.
- Marijn
On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
<snip>
> > + if (ctx->dsi->dsc) {
>
> dsi->dsc is always set, thus this condition can be dropped.
I want to leave room for possibly running the panel without DSC (at a
lower resolution/refresh rate, or at higher power consumption if there
is enough BW) by not assigning the pointer, if we get access to panel
documentation: probably one of the magic commands sent in this driver
controls it but we don't know which.
> > + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
> > +
> > + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
> > + if (ret < 0) {
> > + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
> > + goto fail;
> > + }
> > + ret = mipi_dsi_compression_mode(ctx->dsi, true);
> > + if (ret < 0) {
> > + dev_err(dev, "failed to enable compression mode: %d\n", ret);
> > + goto fail;
> > + }
> > +
> > + msleep(28);
> > + }
> > +
> > + ctx->prepared = true;
> > + return 0;
> > +
> > +fail:
> > + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
> > + regulator_disable(ctx->vddio);
> > + return ret;
> > +}
<snip>
> > + /* This panel only supports DSC; unconditionally enable it */
On that note I should perhaps reword this.
> > + dsi->dsc = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>
> I think double assignments are frowned upon.
Ack.
>
> > + if (!dsc)
> > + return -ENOMEM;
> > +
> > + dsc->dsc_version_major = 1;
> > + dsc->dsc_version_minor = 1;
> > +
> > + dsc->slice_height = 32;
> > + dsc->slice_count = 2;
> > + // TODO: Get hdisplay from the mode
>
> Would you like to fix the TODO?
I can't unless either migrating to drm_bridge (is that doable?) or
expand drm_panel. That's a larger task, but I don't think this driver
is the right place to track that desire. Should I drop the comment
entirely or reword it?
> > + WARN_ON(1440 % dsc->slice_count);
> > + dsc->slice_width = 1440 / dsc->slice_count;
<snip>
- Marijn
On 29.05.2023 23:21, Marijn Suijten wrote:
> On 2023-05-22 11:08:12, Neil Armstrong wrote:
>> On 22/05/2023 03:23, Dmitry Baryshkov wrote:
>>> On 22/05/2023 00:23, Marijn Suijten wrote:
>>>> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
>>>> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
>>>> 120hz refresh-rate mode.
>>>>
>>>> Co-developed-by: Konrad Dybcio <[email protected]>
>>>
>>> Konrad's S-o-b is also required then
>
> I am unsure what to include here, since Konrad originally "authored" the
> commit but I believe it was nothing more than a completely broken and
> unusable driver spit out by "The mdss panel generator". This needed
> enough rewriting that I don't feel like giving it much credit ;)
Might have been. I won't be mad if you drop this!
Konrad
>
>>>
>>>> Signed-off-by: Marijn Suijten <[email protected]>
>>>> ---
>>>> drivers/gpu/drm/panel/Kconfig | 14 +
>>>> drivers/gpu/drm/panel/Makefile | 1 +
>>>> drivers/gpu/drm/panel/panel-samsung-sofef03.c | 423 ++++++++++++++++++++++++++
>>>> 3 files changed, 438 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>>>> index 3f11e9906f2cb..8e2668153bce2 100644
>>>> --- a/drivers/gpu/drm/panel/Kconfig
>>>> +++ b/drivers/gpu/drm/panel/Kconfig
>>>> @@ -630,6 +630,20 @@ config DRM_PANEL_SAMSUNG_SOFEF01
>>>> This panel features a fixed mode of 1080x2520@60.
>>>> +config DRM_PANEL_SAMSUNG_SOFEF03
>>>> + tristate "Samsung sofef03 Sony Xperia 5 II DSI cmd mode panel"
>>>> + depends on GPIOLIB
>>>> + depends on OF
>>>> + depends on DRM_MIPI_DSI
>>>> + depends on BACKLIGHT_CLASS_DEVICE
>>>> + help
>>>> + Say Y or M here if you want to enable support for the Samsung AMOLED
>>>> + command mode panel found in the Sony Xperia 5 II smartphone.
>>>> +
>>>> + This panel uses Display Stream Compression 1.1.
>>>> +
>>>> + The panel features a 1080x2520@60 and 1080x2520@120 mode.
>>>> +
>>>> config DRM_PANEL_SEIKO_43WVF1G
>>>> tristate "Seiko 43WVF1G panel"
>>>> depends on OF
>>>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>>>> index a4039d0fc9cfb..52dcd82e33120 100644
>>>> --- a/drivers/gpu/drm/panel/Makefile
>>>> +++ b/drivers/gpu/drm/panel/Makefile
>>>> @@ -63,6 +63,7 @@ obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01) += panel-samsung-s6e88a0-ams4
>>>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0) += panel-samsung-s6e8aa0.o
>>>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF00) += panel-samsung-sofef00.o
>>>> obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF01) += panel-samsung-sofef01.o
>>>> +obj-$(CONFIG_DRM_PANEL_SAMSUNG_SOFEF03) += panel-samsung-sofef03.o
>>>> obj-$(CONFIG_DRM_PANEL_SEIKO_43WVF1G) += panel-seiko-43wvf1g.o
>>>> obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.o
>>>> obj-$(CONFIG_DRM_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
>>>> diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef03.c b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
>>>> new file mode 100644
>>>> index 0000000000000..2763e1c56b37b
>>>> --- /dev/null
>>>> +++ b/drivers/gpu/drm/panel/panel-samsung-sofef03.c
>>>> @@ -0,0 +1,423 @@
>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>> +/*
>>>> + * Copyright (c) 2022 Konrad Dybcio <[email protected]>
>>>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>>>> + */
>>>> +
>>>> +#include <linux/backlight.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/gpio/consumer.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.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>
>>>> +
>>>> +static const bool enable_120hz = true;
>>
>> Maybe this can be a module parameter ? Can you explain why this can't be dynamically changed by a modeset ?
>>
>>>> +
>>>> +struct samsung_sofef03_m {
>>>> + struct drm_panel panel;
>>>> + struct mipi_dsi_device *dsi;
>>>> + struct regulator *vddio, *vci;
>>>> + struct gpio_desc *reset_gpio;
>>>> + bool prepared;
>>>> +};
>>>> +
>>>> +static inline struct samsung_sofef03_m *to_samsung_sofef03_m(struct drm_panel *panel)
>>>> +{
>>>> + return container_of(panel, struct samsung_sofef03_m, panel);
>>>> +}
>>>> +
>>>> +static void samsung_sofef03_m_reset(struct samsung_sofef03_m *ctx)
>>>> +{
>>>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>>>> + usleep_range(10000, 11000);
>>>> +}
>>>> +
>>>> +static int samsung_sofef03_m_on(struct samsung_sofef03_m *ctx)
>>>> +{
>>>> + struct mipi_dsi_device *dsi = ctx->dsi;
>>>> + struct device *dev = &dsi->dev;
>>>> + int ret;
>>>> +
>>>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>>>> +
>>>> + mipi_dsi_dcs_write_seq(dsi, 0x9d, 0x01);
>>>> +
>>>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> + usleep_range(10000, 11000);
>>>> +
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x09);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xd5, 0x00, 0x00, 0x00);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x08);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xee, 0x00, 0x00);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>>>> +
>>>> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1080 - 1);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2520 - 1);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, 100);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to set display brightness: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xdf, 0x83);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x01);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xe6, 0x01);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x02);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x02, 0x00, 0x1c, 0x1c);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x0c);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xec, 0x01, 0x19);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xc2, 0x2d, 0x27);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0x60, enable_120hz ? 0x10 : 0x00);
>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
>>>> + msleep(110);
>>>> +
>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int samsung_sofef03_m_off(struct samsung_sofef03_m *ctx)
>>>> +{
>>>> + struct mipi_dsi_device *dsi = ctx->dsi;
>>>> + struct device *dev = &dsi->dev;
>>>> + int ret;
>>>> +
>>>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>>>> +
>>>> + ret = mipi_dsi_dcs_set_display_off(dsi);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to turn display off: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> + msleep(20);
>>>> +
>>>> + ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> + msleep(100);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static int samsung_sofef03_m_prepare(struct drm_panel *panel)
>>>> +{
>>>> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
>>>> + struct drm_dsc_picture_parameter_set pps;
>>>> + struct device *dev = &ctx->dsi->dev;
>>>> + int ret;
>>>> +
>>>> + if (ctx->prepared)
>>>> + return 0;
>>>> +
>>>> + ret = regulator_enable(ctx->vddio);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to enable vddio regulator: %d\n", ret);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = regulator_enable(ctx->vci);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to enable vci regulator: %d\n", ret);
>>>> + regulator_disable(ctx->vddio);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + samsung_sofef03_m_reset(ctx);
>>>> +
>>>> + ret = samsung_sofef03_m_on(ctx);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to initialize panel: %d\n", ret);
>>>> + goto fail;
>>>> + }
>>>> +
>>>> + if (ctx->dsi->dsc) {
>>>
>>> Always true
>
> Would like to keep this as described in the Xperia XZ3 panel driver
> review.
>
>>>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>>>> +
>>>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "failed to transmit PPS: %d\n", ret);
>>>> + goto fail;
>>>> + }
>>>> +
>>>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>>>> + if (ret < 0) {
>>>> + dev_err(dev, "Failed to enable compression mode: %d\n", ret);
>>>> + goto fail;
>>>> + }
>>>> +
>>>> + msleep(28);
>>>> + }
>>>> +
>>>> + ctx->prepared = true;
>>>> + return 0;
>>>> +
>>>> +fail:
>>>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>>>> + regulator_disable(ctx->vci);
>>>> + regulator_disable(ctx->vddio);
>>>> + return ret;
>>>> +}
>>>> +
>>>> +static int samsung_sofef03_m_unprepare(struct drm_panel *panel)
>>>> +{
>>>> + struct samsung_sofef03_m *ctx = to_samsung_sofef03_m(panel);
>>>> + struct device *dev = &ctx->dsi->dev;
>>>> + int ret;
>>>> +
>>>> + if (!ctx->prepared)
>>>> + return 0;
>>>> +
>>>> + ret = samsung_sofef03_m_off(ctx);
>>>> + if (ret < 0)
>>>> + dev_err(dev, "Failed to un-initialize panel: %d\n", ret);
>>>> +
>>>> + gpiod_set_value_cansleep(ctx->reset_gpio, 1);
>>>> + regulator_disable(ctx->vci);
>>>> + regulator_disable(ctx->vddio);
>>>> +
>>>> + ctx->prepared = false;
>>>> + return 0;
>>>> +}
>>>> +
>>>> +static const struct drm_display_mode samsung_sofef03_m_60hz_mode = {
>>>> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
>>>> + .hdisplay = 1080,
>>>> + .hsync_start = 1080 + 156,
>>>> + .hsync_end = 1080 + 156 + 8,
>>>> + .htotal = 1080 + 156 + 8 + 8,
>>>> + .vdisplay = 2520,
>>>> + .vsync_start = 2520 + 2393,
>>>> + .vsync_end = 2520 + 2393 + 8,
>>>> + .vtotal = 2520 + 2393 + 8 + 8,
>>>> + .width_mm = 61,
>>>> + .height_mm = 142,
>>>> +};
>>>> +
>>>> +static const struct drm_display_mode samsung_sofef03_m_120hz_mode = {
>>>> + .clock = (1080 + 56 + 8 + 8) * (2520 + 499 + 8 + 8) * 120 / 1000,
>>>> + .hdisplay = 1080,
>>>> + .hsync_start = 1080 + 56,
>>>> + .hsync_end = 1080 + 56 + 8,
>>>> + .htotal = 1080 + 56 + 8 + 8,
>>>> + .vdisplay = 2520,
>>>> + .vsync_start = 2520 + 499,
>>>> + .vsync_end = 2520 + 499 + 8,
>>>> + .vtotal = 2520 + 499 + 8 + 8,
>>>> + .width_mm = 61,
>>>> + .height_mm = 142,
>>>> +};
>>>> +
>>>> +static int samsung_sofef03_m_get_modes(struct drm_panel *panel,
>>>> + struct drm_connector *connector)
>>>> +{
>>>> + if (enable_120hz)
>>>
>>> Is it possible to switch between these modes at runtime? It might be logical to define 60 Hz mode as preferred, while allowing users to switch to 120 Hz when required for some reason.
>
> I'd love to define two modes but as described in the cover letter - and
> the reason to send this as RFC - drm_panel currently does not expose the
> mode back to the driver in e.g. _prepare, so I cannot update or resend
> the necessary dcs commands to set **or change** the current mode
> (downstream defines commands to change the rate without fully resetting
> the panel).
>
> I can either convert this to drm_bridge (is that easy / worth it?) or
> hack it in with a global as done here so that we can tackle that
> endeavour separately, while at least having the drivers upstream (or on
> the lists) as requested by quic for DSC development.
>
>>>> + return drm_connector_helper_get_modes_fixed(connector,
>>>> + &samsung_sofef03_m_120hz_mode);
>>>> + else
>>>> + return drm_connector_helper_get_modes_fixed(connector,
>>>> + &samsung_sofef03_m_60hz_mode);
>>>> +}
>>>> +
>>>> +static const struct drm_panel_funcs samsung_sofef03_m_panel_funcs = {
>>>> + .prepare = samsung_sofef03_m_prepare,
>>>> + .unprepare = samsung_sofef03_m_unprepare,
>>>> + .get_modes = samsung_sofef03_m_get_modes,
>>>> +};
>>>> +
>>>> +static int samsung_sofef03_m_bl_update_status(struct backlight_device *bl)
>>>> +{
>>>> + struct mipi_dsi_device *dsi = bl_get_data(bl);
>>>> + u16 brightness = backlight_get_brightness(bl);
>>>> + int ret;
>>>> +
>>>> + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
>>>> +
>>>> + pr_err("Writing %#x\n", brightness);
>>
>> You forgot to remove those desbug prints :-p
>
> RFC: I indeed forgot to mention this in the cover letter but this driver
> is still mostly broken beyond displaying an image. Setting brightness
> here does nothing (except after sending the command many times in quick
> succession by e.g. a brightness slider in UI, the panel gets stuck on a
> broken image) and the panel remained black if it wasn't for the random
> set_brightness_large(100) in _prepare.
>
> I will remove these in a followup once figuring out how to make and keep
> it working.
>
> - Marijn
On 29/05/2023 23:58, Marijn Suijten wrote:
> On 2023-05-23 01:56:46, Dmitry Baryshkov wrote:
>> On Tue, 23 May 2023 at 01:32, Marijn Suijten
>> <[email protected]> wrote:
>>>
>>> On 2023-05-22 04:19:45, Dmitry Baryshkov wrote:
>>>> On 22/05/2023 00:23, Marijn Suijten wrote:
>>>>> This SOFEF01-M Display-IC driver supports two modes with different
>>>>> compatibles to differentiate between slightly different physical sizes
>>>>> (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
>>>>>
>>>>> It is currently also used to hardcode significantly higher fake porches
>>>>> for the Xperia 5, which are unused in transfers due to this being a
>>>>> command-mode panel but do have an effect on the clock rates set by
>>>>> dsi_host.c. Without higher clock rates this panel fails to achieve
>>>>> 60fps and has significant tearing artifacts, while the same calculated
>>>>> clock rate works perfectly fine on the Xperia 10 II.
>>>
>>> <snip>
>>>
>>>>> +/* Sony Xperia 5 (kumano bahamut) */
>>>>> +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
>>>>> + /*
>>>>> + * WARNING: These massive porches are wrong/useless for CMDmode
>>>>> + * (and not defined in downstream DTS) but necessary to bump dsi
>>>>> + * clocks higher, so that we can achieve proper 60fps without tearing.
>>>>> + */
>>>>> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
>>>>> + .hdisplay = 1080,
>>>>> + .hsync_start = 1080 + 156,
>>>>> + .hsync_end = 1080 + 156 + 8,
>>>>> + .htotal = 1080 + 156 + 8 + 8,
>>>>> + .vdisplay = 2520,
>>>>> + .vsync_start = 2520 + 2393,
>>>>> + .vsync_end = 2520 + 2393 + 8,
>>>>> + .vtotal = 2520 + 2393 + 8 + 8,
>>>>> + .width_mm = 61,
>>>>> + .height_mm = 142,
>>>>> +};
>>>>> +
>>>>> +/* Sony Xperia 10 II (seine pdx201) */
>>>>> +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
>>>>> + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
>>>>> + .hdisplay = 1080,
>>>>> + .hsync_start = 1080 + 8,
>>>>> + .hsync_end = 1080 + 8 + 8,
>>>>> + .htotal = 1080 + 8 + 8 + 8,
>>>>> + .vdisplay = 2520,
>>>>> + .vsync_start = 2520 + 8,
>>>>> + .vsync_end = 2520 + 8 + 8,
>>>>> + .vtotal = 2520 + 8 + 8 + 8,
>>>>> + .width_mm = 60,
>>>>> + .height_mm = 139,
>>>>> +};
>>>>> +
>>>>> +static const struct of_device_id samsung_sofef01_m_of_match[] = {
>>>>> + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
>>>>> + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
>>>>
>>>> Are there really two panels? Can we use one mode for both usecases?
>>>
>>> See the commit description where I explained exactly this: the panels
>>> have different dimensions (6.1" vs 6.0", hence different DPI) and I also
>>> abuse this to hack in higher clock rates via fake porches.
>>>
>>> I just ended up on a scary website that supposedly contains the panel
>>> names:
>>>
>>> - Xperia 5 (bahamut, 6.1"): AMB609TC01
>>> - Xperia 10 II (pdx201, 6.0"): AMS597UT01
>>
>> Great! From the patch description it was not obvious if those are two
>> different panels or a single panel with slight difference in the glass
>> cover. With these names in place (well, with two distinct names in
>> place) it makes sense.
>
> For completeness: keep the current single file but embed these panel
> names as suffix (eg. `samsung,sofef-01-m-am[bs]...`) to the compatible
> (and document these more explicitly elsewhere)?
Where do the sofef parts of the name come from? Glancing at other
panels, I'd expect something simpler. Maybe:
samsung,sofef01m-amb60...
>
> - Marijn
>
>>
>> --
>> With best wishes
>> Dmitry
--
With best wishes
Dmitry
On 30/05/2023 00:11, Marijn Suijten wrote:
> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> <snip>
>>> + if (ctx->dsi->dsc) {
>>
>> dsi->dsc is always set, thus this condition can be dropped.
>
> I want to leave room for possibly running the panel without DSC (at a
> lower resolution/refresh rate, or at higher power consumption if there
> is enough BW) by not assigning the pointer, if we get access to panel
> documentation: probably one of the magic commands sent in this driver
> controls it but we don't know which.
>
>>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>>> +
>>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>>> + if (ret < 0) {
>>> + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
>>> + goto fail;
>>> + }
>>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>>> + if (ret < 0) {
>>> + dev_err(dev, "failed to enable compression mode: %d\n", ret);
>>> + goto fail;
>>> + }
>>> +
>>> + msleep(28);
>>> + }
>>> +
>>> + ctx->prepared = true;
>>> + return 0;
>>> +
>>> +fail:
>>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>>> + regulator_disable(ctx->vddio);
>>> + return ret;
>>> +}
> <snip>
>>> + /* This panel only supports DSC; unconditionally enable it */
>
> On that note I should perhaps reword this.
>
>>> + dsi->dsc = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>>
>> I think double assignments are frowned upon.
>
> Ack.
>
>>
>>> + if (!dsc)
>>> + return -ENOMEM;
>>> +
>>> + dsc->dsc_version_major = 1;
>>> + dsc->dsc_version_minor = 1;
>>> +
>>> + dsc->slice_height = 32;
>>> + dsc->slice_count = 2;
>>> + // TODO: Get hdisplay from the mode
>>
>> Would you like to fix the TODO?
>
> I can't unless either migrating to drm_bridge (is that doable?) or
> expand drm_panel. That's a larger task, but I don't think this driver
> is the right place to track that desire. Should I drop the comment
> entirely or reword it?
I'd say, reword it, so that it becomes more obvious why this TODO can
not be fixed at this moment.
>
>>> + WARN_ON(1440 % dsc->slice_count);
>>> + dsc->slice_width = 1440 / dsc->slice_count;
>
> <snip>
>
> - Marijn
--
With best wishes
Dmitry
On 30/05/2023 00:07, Marijn Suijten wrote:
> On 2023-05-22 15:58:56, Dmitry Baryshkov wrote:
>> On Mon, 22 May 2023 at 12:04, Neil Armstrong <[email protected]> wrote:
>>>
>>> On 22/05/2023 03:16, Dmitry Baryshkov wrote:
>>>> On 22/05/2023 00:23, Marijn Suijten wrote:
>>>>> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its Xperia
>>>>> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
>>>>>
>>>>> This panel features Display Stream Compression 1.1.
>>>>>
>>>>> Signed-off-by: Marijn Suijten <[email protected]>
>>>>> ---
>>>>> drivers/gpu/drm/panel/Kconfig | 11 +
>>>>> drivers/gpu/drm/panel/Makefile | 1 +
>>>>> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362 ++++++++++++++++++++++++
>>>>> 3 files changed, 374 insertions(+)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
>>>>> index 67ef898d133f2..18bd116e78a71 100644
>>>>> --- a/drivers/gpu/drm/panel/Kconfig
>>>>> +++ b/drivers/gpu/drm/panel/Kconfig
>>>>> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
>>>>> Say Y here if you want to enable support for the Sony ACX565AKM
>>>>> 800x600 3.5" panel (found on the Nokia N900).
>>>>> +config DRM_PANEL_SONY_AKATSUKI_LGD
>>>>> + tristate "Sony Xperia XZ3 LGD panel"
>>>>> + depends on GPIOLIB && OF
>>>>> + depends on DRM_MIPI_DSI
>>>>> + depends on BACKLIGHT_CLASS_DEVICE
>>>>> + help
>>>>> + Say Y here if you want to enable support for the Sony Xperia XZ3
>>>>> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG Display.
>>>>> +
>>>>> + This panel uses Display Stream Compression 1.1.
>>>>> +
>>>>> config DRM_PANEL_SONY_TD4353_JDI
>>>>> tristate "Sony TD4353 JDI panel"
>>>>> depends on GPIOLIB && OF
>>>>> diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
>>>>> index ff169781e82d7..85133f73558f3 100644
>>>>> --- a/drivers/gpu/drm/panel/Makefile
>>>>> +++ b/drivers/gpu/drm/panel/Makefile
>>>>> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) += panel-sitronix-st7701.o
>>>>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
>>>>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
>>>>> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
>>>>> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) += panel-sony-akatsuki-lgd.o
>>>>> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
>>>>> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o
>>>>> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
>>>>> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>> new file mode 100644
>>>>> index 0000000000000..f55788f963dab
>>>>> --- /dev/null
>>>>> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>> @@ -0,0 +1,362 @@
>>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>>> +/*
>>>>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>>>>> + *
>>>>> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
>>>>> +
>>>>> +struct sony_akatsuki_lgd {
>>>>> + struct drm_panel panel;
>>>>> + struct mipi_dsi_device *dsi;
>>>>> + struct regulator *vddio;
>>>>> + struct gpio_desc *reset_gpio;
>>>>> + bool prepared;
>>>>> +};
>>>>> +
>>>>> +static inline struct sony_akatsuki_lgd *to_sony_akatsuki_lgd(struct drm_panel *panel)
>>>>> +{
>>>>> + return container_of(panel, struct sony_akatsuki_lgd, panel);
>>>>> +}
>>>>> +
>>>>> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
>>>>> +{
>>>>> + struct mipi_dsi_device *dsi = ctx->dsi;
>>>>> + struct device *dev = &dsi->dev;
>>>>> + int ret;
>>>>> +
>>>>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>>>>> +
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
>>>>> + /* Enable backlight control */
>>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
>>>>> +
>>>>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
>>>>> + if (ret < 0) {
>>>>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
>>>>> + if (ret < 0) {
>>>>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
>>>>> +
>>>>> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>>>>> + if (ret < 0) {
>>>>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> +
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
>>>>> +
>>>>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>>>>> + if (ret < 0) {
>>>>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>> + msleep(120);
>>>>> +
>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
>>>>> +
>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>>>>> + if (ret < 0) {
>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>>>>> + return ret;
>>>>> + }
>>>>
>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
>>>
>>>
>>> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
>>>
>>
>> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
>
> I have never investigated what it takes to split these functions, but
> some of these panels do show some corruption at startup which may be
> circumvented by powering the panel on after starting the video stream?
>
> I'm just not sure where to make the split: downstream does describe a
> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
> the latter only contains set_display_on() (not exit_sleep_mode()).
> It is documented like:
>
> same as "qcom,mdss-dsi-on-command" except commands are sent after
> displaying an image."
>
> So this seems like the right way to split them up, I'll test this out on
> all submitted panel drivers.
Interesting enough, Neil suggested that sending all the commands during
pre_enable() is the correct sequence (especially for VIDEO mode panels),
since not all DSI hosts can send commands after switching to the VIDEO mode.
--
With best wishes
Dmitry
On 30/05/2023 00:29, Konrad Dybcio wrote:
>
>
> On 29.05.2023 23:21, Marijn Suijten wrote:
>> On 2023-05-22 11:08:12, Neil Armstrong wrote:
>>> On 22/05/2023 03:23, Dmitry Baryshkov wrote:
>>>> On 22/05/2023 00:23, Marijn Suijten wrote:
>>>>> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
>>>>> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
>>>>> 120hz refresh-rate mode.
>>>>>
>>>>> Co-developed-by: Konrad Dybcio <[email protected]>
>>>>
>>>> Konrad's S-o-b is also required then
>>
>> I am unsure what to include here, since Konrad originally "authored" the
>> commit but I believe it was nothing more than a completely broken and
>> unusable driver spit out by "The mdss panel generator". This needed
>> enough rewriting that I don't feel like giving it much credit ;)
> Might have been. I won't be mad if you drop this!
I'd say, either add S-o-B, or drop C-D-B. The Co-developed-by should
always come with the Signed-of-by, otherwise one can not be sure that
the co-developer didn't copy-paste some super-proprietary stolen code.
>
> Konrad
>>
>>>>
>>>>> Signed-off-by: Marijn Suijten <[email protected]>
--
With best wishes
Dmitry
On 30/05/2023 00:11, Marijn Suijten wrote:
> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> <snip>
>>> + if (ctx->dsi->dsc) {
>>
>> dsi->dsc is always set, thus this condition can be dropped.
>
> I want to leave room for possibly running the panel without DSC (at a
> lower resolution/refresh rate, or at higher power consumption if there
> is enough BW) by not assigning the pointer, if we get access to panel
> documentation: probably one of the magic commands sent in this driver
> controls it but we don't know which.
This sounds like 'a possible room for expansion' which might never be
actually used. I think, if we ever get such information or when the
panel's DSC config gets variadic following the mode, we can reintroduce
this check.
>
>>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>>> +
>>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>>> + if (ret < 0) {
>>> + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
>>> + goto fail;
>>> + }
>>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>>> + if (ret < 0) {
>>> + dev_err(dev, "failed to enable compression mode: %d\n", ret);
>>> + goto fail;
>>> + }
>>> +
>>> + msleep(28);
>>> + }
>>> +
>>> + ctx->prepared = true;
>>> + return 0;
>>> +
>>> +fail:
>>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>>> + regulator_disable(ctx->vddio);
>>> + return ret;
>>> +}
> <snip>
>>> + /* This panel only supports DSC; unconditionally enable it */
--
With best wishes
Dmitry
On 2023-05-30 01:20:17, Dmitry Baryshkov wrote:
> On 29/05/2023 23:58, Marijn Suijten wrote:
> > On 2023-05-23 01:56:46, Dmitry Baryshkov wrote:
> >> On Tue, 23 May 2023 at 01:32, Marijn Suijten
> >> <[email protected]> wrote:
> >>>
> >>> On 2023-05-22 04:19:45, Dmitry Baryshkov wrote:
> >>>> On 22/05/2023 00:23, Marijn Suijten wrote:
> >>>>> This SOFEF01-M Display-IC driver supports two modes with different
> >>>>> compatibles to differentiate between slightly different physical sizes
> >>>>> (panels) found on the Xperia 5 (6.1") and 10 II (6.0").
> >>>>>
> >>>>> It is currently also used to hardcode significantly higher fake porches
> >>>>> for the Xperia 5, which are unused in transfers due to this being a
> >>>>> command-mode panel but do have an effect on the clock rates set by
> >>>>> dsi_host.c. Without higher clock rates this panel fails to achieve
> >>>>> 60fps and has significant tearing artifacts, while the same calculated
> >>>>> clock rate works perfectly fine on the Xperia 10 II.
> >>>
> >>> <snip>
> >>>
> >>>>> +/* Sony Xperia 5 (kumano bahamut) */
> >>>>> +static const struct drm_display_mode samsung_sofef01_m_bahamut_mode = {
> >>>>> + /*
> >>>>> + * WARNING: These massive porches are wrong/useless for CMDmode
> >>>>> + * (and not defined in downstream DTS) but necessary to bump dsi
> >>>>> + * clocks higher, so that we can achieve proper 60fps without tearing.
> >>>>> + */
> >>>>> + .clock = (1080 + 156 + 8 + 8) * (2520 + 2393 + 8 + 8) * 60 / 1000,
> >>>>> + .hdisplay = 1080,
> >>>>> + .hsync_start = 1080 + 156,
> >>>>> + .hsync_end = 1080 + 156 + 8,
> >>>>> + .htotal = 1080 + 156 + 8 + 8,
> >>>>> + .vdisplay = 2520,
> >>>>> + .vsync_start = 2520 + 2393,
> >>>>> + .vsync_end = 2520 + 2393 + 8,
> >>>>> + .vtotal = 2520 + 2393 + 8 + 8,
> >>>>> + .width_mm = 61,
> >>>>> + .height_mm = 142,
> >>>>> +};
> >>>>> +
> >>>>> +/* Sony Xperia 10 II (seine pdx201) */
> >>>>> +static const struct drm_display_mode samsung_sofef01_m_pdx201_mode = {
> >>>>> + .clock = (1080 + 8 + 8 + 8) * (2520 + 8 + 8 + 8) * 60 / 1000,
> >>>>> + .hdisplay = 1080,
> >>>>> + .hsync_start = 1080 + 8,
> >>>>> + .hsync_end = 1080 + 8 + 8,
> >>>>> + .htotal = 1080 + 8 + 8 + 8,
> >>>>> + .vdisplay = 2520,
> >>>>> + .vsync_start = 2520 + 8,
> >>>>> + .vsync_end = 2520 + 8 + 8,
> >>>>> + .vtotal = 2520 + 8 + 8 + 8,
> >>>>> + .width_mm = 60,
> >>>>> + .height_mm = 139,
> >>>>> +};
> >>>>> +
> >>>>> +static const struct of_device_id samsung_sofef01_m_of_match[] = {
> >>>>> + { .compatible = "samsung,sofef01-m-bahamut", .data = &samsung_sofef01_m_bahamut_mode },
> >>>>> + { .compatible = "samsung,sofef01-m-pdx201", .data = &samsung_sofef01_m_pdx201_mode },
> >>>>
> >>>> Are there really two panels? Can we use one mode for both usecases?
> >>>
> >>> See the commit description where I explained exactly this: the panels
> >>> have different dimensions (6.1" vs 6.0", hence different DPI) and I also
> >>> abuse this to hack in higher clock rates via fake porches.
> >>>
> >>> I just ended up on a scary website that supposedly contains the panel
> >>> names:
> >>>
> >>> - Xperia 5 (bahamut, 6.1"): AMB609TC01
> >>> - Xperia 10 II (pdx201, 6.0"): AMS597UT01
> >>
> >> Great! From the patch description it was not obvious if those are two
> >> different panels or a single panel with slight difference in the glass
> >> cover. With these names in place (well, with two distinct names in
> >> place) it makes sense.
> >
> > For completeness: keep the current single file but embed these panel
> > names as suffix (eg. `samsung,sofef-01-m-am[bs]...`) to the compatible
> > (and document these more explicitly elsewhere)?
>
> Where do the sofef parts of the name come from? Glancing at other
> panels, I'd expect something simpler. Maybe:
That is the Driver-IC. Sorry, I meant sofef01-m without the first dash,
matching the original compatibles. But can also drop the dash in 01-m
if desired.
> samsung,sofef01m-amb60...
- Marijn
On 2023-05-30 01:22:54, Dmitry Baryshkov wrote:
> On 30/05/2023 00:29, Konrad Dybcio wrote:
> >
> >
> > On 29.05.2023 23:21, Marijn Suijten wrote:
> >> On 2023-05-22 11:08:12, Neil Armstrong wrote:
> >>> On 22/05/2023 03:23, Dmitry Baryshkov wrote:
> >>>> On 22/05/2023 00:23, Marijn Suijten wrote:
> >>>>> The SOFEF03-M Display-IC paired with an unknown panel in the Sony Xperia
> >>>>> 5 II always uses Display Stream Compression 1.1 and features a 60hz and
> >>>>> 120hz refresh-rate mode.
> >>>>>
> >>>>> Co-developed-by: Konrad Dybcio <[email protected]>
> >>>>
> >>>> Konrad's S-o-b is also required then
> >>
> >> I am unsure what to include here, since Konrad originally "authored" the
> >> commit but I believe it was nothing more than a completely broken and
> >> unusable driver spit out by "The mdss panel generator". This needed
> >> enough rewriting that I don't feel like giving it much credit ;)
> > Might have been. I won't be mad if you drop this!
>
> I'd say, either add S-o-B, or drop C-D-B. The Co-developed-by should
> always come with the Signed-of-by, otherwise one can not be sure that
> the co-developer didn't copy-paste some super-proprietary stolen code.
That is effectively what the downstream command sequences are, with
their meaning removed :P
I'll drop it then, that makes most sense I think.
- Marijn
On 2023-05-30 01:18:40, Dmitry Baryshkov wrote:
<snip>
> >>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
> >>>>> + if (ret < 0) {
> >>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> >>>>> + return ret;
> >>>>> + }
> >>>>
> >>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
> >>>
> >>>
> >>> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
> >>>
> >>
> >> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
> >
> > I have never investigated what it takes to split these functions, but
> > some of these panels do show some corruption at startup which may be
> > circumvented by powering the panel on after starting the video stream?
> >
> > I'm just not sure where to make the split: downstream does describe a
> > qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
> > the latter only contains set_display_on() (not exit_sleep_mode()).
> > It is documented like:
> >
> > same as "qcom,mdss-dsi-on-command" except commands are sent after
> > displaying an image."
> >
> > So this seems like the right way to split them up, I'll test this out on
> > all submitted panel drivers.
>
> Interesting enough, Neil suggested that sending all the commands during
> pre_enable() is the correct sequence (especially for VIDEO mode panels),
> since not all DSI hosts can send commands after switching to the VIDEO mode.
Note that all these panels and Driver-ICs are command-mode, and/or
programmed to run in command-mode, so there shouldn't be any notion of a
VIDEO stream (any command-mode frame is just an "arbitrary command" as
far as I understood).
- Marijn
On 30/05/2023 01:37, Marijn Suijten wrote:
> On 2023-05-30 01:18:40, Dmitry Baryshkov wrote:
> <snip>
>>>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>>>>>>> + if (ret < 0) {
>>>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>>>>>>> + return ret;
>>>>>>> + }
>>>>>>
>>>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
>>>>>
>>>>>
>>>>> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
>>>>>
>>>>
>>>> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
>>>
>>> I have never investigated what it takes to split these functions, but
>>> some of these panels do show some corruption at startup which may be
>>> circumvented by powering the panel on after starting the video stream?
>>>
>>> I'm just not sure where to make the split: downstream does describe a
>>> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
>>> the latter only contains set_display_on() (not exit_sleep_mode()).
>>> It is documented like:
>>>
>>> same as "qcom,mdss-dsi-on-command" except commands are sent after
>>> displaying an image."
>>>
>>> So this seems like the right way to split them up, I'll test this out on
>>> all submitted panel drivers.
>>
>> Interesting enough, Neil suggested that sending all the commands during
>> pre_enable() is the correct sequence (especially for VIDEO mode panels),
>> since not all DSI hosts can send commands after switching to the VIDEO mode.
>
> Note that all these panels and Driver-ICs are command-mode, and/or
> programmed to run in command-mode, so there shouldn't be any notion of a
> VIDEO stream (any command-mode frame is just an "arbitrary command" as
> far as I understood).
Yes, from the data stream point of view. I was talking about the DSI
host being able to send arbitrary commands or not after enabling the
video/cmd stream.
>
> - Marijn
--
With best wishes
Dmitry
Hi Marijn, Dmitry, Caleb, Jessica,
On 29/05/2023 23:11, Marijn Suijten wrote:
> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> <snip>
>>> + if (ctx->dsi->dsc) {
>>
>> dsi->dsc is always set, thus this condition can be dropped.
>
> I want to leave room for possibly running the panel without DSC (at a
> lower resolution/refresh rate, or at higher power consumption if there
> is enough BW) by not assigning the pointer, if we get access to panel
> documentation: probably one of the magic commands sent in this driver
> controls it but we don't know which.
I'd like to investigate if DSC should perhaps only be enabled if we
run non certain platforms/socs ?
I mean, we don't know if the controller supports DSC and those particular
DSC parameters so we should probably start adding something like :
static drm_dsc_config dsc_params_qcom = {}
static const struct of_device_id panel_of_dsc_params[] = {
{ .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
{ .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
{ .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
{ .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
};
...
static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
...
const struct of_device_id *match;
...
match = of_match_node(panel_of_dsc_params, of_root);
if (match && match->data) {
dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
memcpy(dsi->dsc, match->data, sizeof(*dsc));
} else {
dev_warn(&dsi->dev, "DSI controller is not marked as supporting DSC\n");
}
...
}
and probably bail out if it's a DSC only panel.
We could alternatively match on the DSI controller's dsi->host->dev instead of the SoC root compatible.
Neil
>
>>> + drm_dsc_pps_payload_pack(&pps, ctx->dsi->dsc);
>>> +
>>> + ret = mipi_dsi_picture_parameter_set(ctx->dsi, &pps);
>>> + if (ret < 0) {
>>> + dev_err(panel->dev, "failed to transmit PPS: %d\n", ret);
>>> + goto fail;
>>> + }
>>> + ret = mipi_dsi_compression_mode(ctx->dsi, true);
>>> + if (ret < 0) {
>>> + dev_err(dev, "failed to enable compression mode: %d\n", ret);
>>> + goto fail;
>>> + }
>>> +
>>> + msleep(28);
>>> + }
>>> +
>>> + ctx->prepared = true;
>>> + return 0;
>>> +
>>> +fail:
>>> + gpiod_set_value_cansleep(ctx->reset_gpio, 0);
>>> + regulator_disable(ctx->vddio);
>>> + return ret;
>>> +}
> <snip>
>>> + /* This panel only supports DSC; unconditionally enable it */
>
> On that note I should perhaps reword this.
>
>>> + dsi->dsc = dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>>
>> I think double assignments are frowned upon.
>
> Ack.
>
>>
>>> + if (!dsc)
>>> + return -ENOMEM;
>>> +
>>> + dsc->dsc_version_major = 1;
>>> + dsc->dsc_version_minor = 1;
>>> +
>>> + dsc->slice_height = 32;
>>> + dsc->slice_count = 2;
>>> + // TODO: Get hdisplay from the mode
>>
>> Would you like to fix the TODO?
>
> I can't unless either migrating to drm_bridge (is that doable?) or
> expand drm_panel. That's a larger task, but I don't think this driver
> is the right place to track that desire. Should I drop the comment
> entirely or reword it?
>
>>> + WARN_ON(1440 % dsc->slice_count);
>>> + dsc->slice_width = 1440 / dsc->slice_count;
>
> <snip>
>
> - Marijn
On 2023-05-30 01:39:10, Dmitry Baryshkov wrote:
> On 30/05/2023 01:37, Marijn Suijten wrote:
> > On 2023-05-30 01:18:40, Dmitry Baryshkov wrote:
> > <snip>
> >>>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
> >>>>>>> + if (ret < 0) {
> >>>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> >>>>>>> + return ret;
> >>>>>>> + }
> >>>>>>
> >>>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
> >>>>>
> >>>>>
> >>>>> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
> >>>>>
> >>>>
> >>>> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
> >>>
> >>> I have never investigated what it takes to split these functions, but
> >>> some of these panels do show some corruption at startup which may be
> >>> circumvented by powering the panel on after starting the video stream?
> >>>
> >>> I'm just not sure where to make the split: downstream does describe a
> >>> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
> >>> the latter only contains set_display_on() (not exit_sleep_mode()).
> >>> It is documented like:
> >>>
> >>> same as "qcom,mdss-dsi-on-command" except commands are sent after
> >>> displaying an image."
> >>>
> >>> So this seems like the right way to split them up, I'll test this out on
> >>> all submitted panel drivers.
> >>
> >> Interesting enough, Neil suggested that sending all the commands during
> >> pre_enable() is the correct sequence (especially for VIDEO mode panels),
> >> since not all DSI hosts can send commands after switching to the VIDEO mode.
> >
> > Note that all these panels and Driver-ICs are command-mode, and/or
> > programmed to run in command-mode, so there shouldn't be any notion of a
> > VIDEO stream (any command-mode frame is just an "arbitrary command" as
> > far as I understood).
>
> Yes, from the data stream point of view. I was talking about the DSI
> host being able to send arbitrary commands or not after enabling the
> video/cmd stream.
Is this a known limitation of SM8250 then? Or is the brightness_set
issue more likely a "problem" with the panel that the downstream kernel
is "somehow" working around or aware of, and I just haven't found
how/where it deals with that?
(Alternatively we could be "doing it wrong" for other panels but it
turns out to be working anyway)
- Marijn
On 2023-05-30 09:24:24, Neil Armstrong wrote:
> Hi Marijn, Dmitry, Caleb, Jessica,
>
> On 29/05/2023 23:11, Marijn Suijten wrote:
> > On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> > <snip>
> >>> + if (ctx->dsi->dsc) {
> >>
> >> dsi->dsc is always set, thus this condition can be dropped.
> >
> > I want to leave room for possibly running the panel without DSC (at a
> > lower resolution/refresh rate, or at higher power consumption if there
> > is enough BW) by not assigning the pointer, if we get access to panel
> > documentation: probably one of the magic commands sent in this driver
> > controls it but we don't know which.
>
> I'd like to investigate if DSC should perhaps only be enabled if we
> run non certain platforms/socs ?
>
> I mean, we don't know if the controller supports DSC and those particular
> DSC parameters so we should probably start adding something like :
>
> static drm_dsc_config dsc_params_qcom = {}
>
> static const struct of_device_id panel_of_dsc_params[] = {
> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
> };
I'd absolutely hate hardcoding a list of compatible SoC names in a panel
driver. For one these lists will fall out of date really soon even if
we store this list in a generic place: even the current DPU catalog and
new entries floating on the lists weren't faithfully representing DSC
capabilities (but that's all being / been fixed now).
What's more, most of these panel drivers are "hardcoded" for a specific
(smartphone) device (and SoC...) since we don't (usually) have the
DrIC/panel name nor documentation to make the commands generic enough.
I don't think we should be specific on that end, while being generic on
the DSC side.
That does mean I'll remove the if (dsc) here, as Dmitry noted most of
this driver expects/requires it is enabled.
> ...
> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
> ...
> const struct of_device_id *match;
>
> ...
> match = of_match_node(panel_of_dsc_params, of_root);
> if (match && match->data) {
> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
> memcpy(dsi->dsc, match->data, sizeof(*dsc));
> } else {
> dev_warn(&dsi->dev, "DSI controller is not marked as supporting DSC\n");
> }
> ...
> }
>
> and probably bail out if it's a DSC only panel.
>
> We could alternatively match on the DSI controller's dsi->host->dev instead of the SoC root compatible.
I'd much rather have the DSI host/controller state whether it is capable
of DSC (likely allowing us to expose different modes for panels that
support toggling DSC), but for starters also validate (in DPU?) that the
pointer is NULL when the hardware does not support it (but maybe that
already happens implicitly somewhere in e.g.
dpu_encoder_virt_atomic_mode_set when finding the DSC blocks).
- Marijn
On 30.05.2023 10:41, Marijn Suijten wrote:
> On 2023-05-30 09:24:24, Neil Armstrong wrote:
>> Hi Marijn, Dmitry, Caleb, Jessica,
>>
>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>> <snip>
>>>>> + if (ctx->dsi->dsc) {
>>>>
>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>
>>> I want to leave room for possibly running the panel without DSC (at a
>>> lower resolution/refresh rate, or at higher power consumption if there
>>> is enough BW) by not assigning the pointer, if we get access to panel
>>> documentation: probably one of the magic commands sent in this driver
>>> controls it but we don't know which.
>>
>> I'd like to investigate if DSC should perhaps only be enabled if we
>> run non certain platforms/socs ?
>>
>> I mean, we don't know if the controller supports DSC and those particular
>> DSC parameters so we should probably start adding something like :
>>
>> static drm_dsc_config dsc_params_qcom = {}
>>
>> static const struct of_device_id panel_of_dsc_params[] = {
>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>> };
>
> I'd absolutely hate hardcoding a list of compatible SoC names in a panel
> driver. For one these lists will fall out of date really soon even if
> we store this list in a generic place: even the current DPU catalog and
> new entries floating on the lists weren't faithfully representing DSC
> capabilities (but that's all being / been fixed now).
Yes, a driver should behave predictably, regardless of the platform.
>
> What's more, most of these panel drivers are "hardcoded" for a specific
> (smartphone) device (and SoC...) since we don't (usually) have the
> DrIC/panel name nor documentation to make the commands generic enough.
> I don't think we should be specific on that end, while being generic on
> the DSC side.
>
> That does mean I'll remove the if (dsc) here, as Dmitry noted most of
> this driver expects/requires it is enabled.
I'd say we could assume it's mandatory as of today.
Konrad
>
>> ...
>> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
>> ...
>> const struct of_device_id *match;
>>
>> ...
>> match = of_match_node(panel_of_dsc_params, of_root);
>> if (match && match->data) {
>> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>> memcpy(dsi->dsc, match->data, sizeof(*dsc));
>> } else {
>> dev_warn(&dsi->dev, "DSI controller is not marked as supporting DSC\n");
>> }
>> ...
>> }
>>
>> and probably bail out if it's a DSC only panel.
>>
>> We could alternatively match on the DSI controller's dsi->host->dev instead of the SoC root compatible.
>
> I'd much rather have the DSI host/controller state whether it is capable
> of DSC (likely allowing us to expose different modes for panels that
> support toggling DSC), but for starters also validate (in DPU?) that the
> pointer is NULL when the hardware does not support it (but maybe that
> already happens implicitly somewhere in e.g.
> dpu_encoder_virt_atomic_mode_set when finding the DSC blocks).
>
> - Marijn
On Tue, 30 May 2023 at 11:27, Marijn Suijten
<[email protected]> wrote:
>
> On 2023-05-30 01:39:10, Dmitry Baryshkov wrote:
> > On 30/05/2023 01:37, Marijn Suijten wrote:
> > > On 2023-05-30 01:18:40, Dmitry Baryshkov wrote:
> > > <snip>
> > >>>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
> > >>>>>>> + if (ret < 0) {
> > >>>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> > >>>>>>> + return ret;
> > >>>>>>> + }
> > >>>>>>
> > >>>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
> > >>>>>
> > >>>>>
> > >>>>> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
> > >>>>>
> > >>>>
> > >>>> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
> > >>>
> > >>> I have never investigated what it takes to split these functions, but
> > >>> some of these panels do show some corruption at startup which may be
> > >>> circumvented by powering the panel on after starting the video stream?
> > >>>
> > >>> I'm just not sure where to make the split: downstream does describe a
> > >>> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
> > >>> the latter only contains set_display_on() (not exit_sleep_mode()).
> > >>> It is documented like:
> > >>>
> > >>> same as "qcom,mdss-dsi-on-command" except commands are sent after
> > >>> displaying an image."
> > >>>
> > >>> So this seems like the right way to split them up, I'll test this out on
> > >>> all submitted panel drivers.
> > >>
> > >> Interesting enough, Neil suggested that sending all the commands during
> > >> pre_enable() is the correct sequence (especially for VIDEO mode panels),
> > >> since not all DSI hosts can send commands after switching to the VIDEO mode.
> > >
> > > Note that all these panels and Driver-ICs are command-mode, and/or
> > > programmed to run in command-mode, so there shouldn't be any notion of a
> > > VIDEO stream (any command-mode frame is just an "arbitrary command" as
> > > far as I understood).
> >
> > Yes, from the data stream point of view. I was talking about the DSI
> > host being able to send arbitrary commands or not after enabling the
> > video/cmd stream.
>
> Is this a known limitation of SM8250 then? Or is the brightness_set
> issue more likely a "problem" with the panel that the downstream kernel
> is "somehow" working around or aware of, and I just haven't found
> how/where it deals with that?
> (Alternatively we could be "doing it wrong" for other panels but it
> turns out to be working anyway)
Please excuse me for not being explicit enough. Qualcomm hardware
doesn't have this problem. Thus I was completely unaware of it before
talking to Neil.
So, our hardware works in most of the cases.
--
With best wishes
Dmitry
On Tue, 30 May 2023 at 10:24, Neil Armstrong <[email protected]> wrote:
>
> Hi Marijn, Dmitry, Caleb, Jessica,
>
> On 29/05/2023 23:11, Marijn Suijten wrote:
> > On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> > <snip>
> >>> + if (ctx->dsi->dsc) {
> >>
> >> dsi->dsc is always set, thus this condition can be dropped.
> >
> > I want to leave room for possibly running the panel without DSC (at a
> > lower resolution/refresh rate, or at higher power consumption if there
> > is enough BW) by not assigning the pointer, if we get access to panel
> > documentation: probably one of the magic commands sent in this driver
> > controls it but we don't know which.
>
> I'd like to investigate if DSC should perhaps only be enabled if we
> run non certain platforms/socs ?
>
> I mean, we don't know if the controller supports DSC and those particular
> DSC parameters so we should probably start adding something like :
>
> static drm_dsc_config dsc_params_qcom = {}
>
> static const struct of_device_id panel_of_dsc_params[] = {
> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
> };
I think this would damage the reusability of the drivers. The panel
driver does not actually care if the SoC is SM8350, sunxi-something or
RCar.
Instead it cares about host capabilities.
I think instead we should extend mipi_dsi_host:
#define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
#define MIPI_DSI_HOST_MODE_CMD BIT(1)
#define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
// FIXME: do we need to provide additional caps here ?
#define MIPI_DSI_DSC_1_1 BIT(0)
#define MIPI_DSI_DSC_1_2 BIT(1)
#define MIPI_DSI_DSC_NATIVE_422 BIT(2)
#define MIPI_DSI_DSC_NATIVE_420 BIT(3)
#define MIPI_DSI_DSC_FRAC_BPP BIT(4)
// etc.
struct mipi_dsi_host {
// new fields only
unsigned long mode_flags;
unsigned long dsc_flags;
};
Then the panel driver can adapt itself to the host capabilities and
(possibly) select one of the internally supported DSC profiles.
>
> ...
> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
> ...
> const struct of_device_id *match;
>
> ...
> match = of_match_node(panel_of_dsc_params, of_root);
> if (match && match->data) {
> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
> memcpy(dsi->dsc, match->data, sizeof(*dsc));
> } else {
> dev_warn(&dsi->dev, "DSI controller is not marked as supporting DSC\n");
> }
> ...
> }
>
> and probably bail out if it's a DSC only panel.
>
> We could alternatively match on the DSI controller's dsi->host->dev instead of the SoC root compatible.
>
> Neil
--
With best wishes
Dmitry
Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
> On Tue, 30 May 2023 at 10:24, Neil Armstrong <[email protected]> wrote:
>>
>> Hi Marijn, Dmitry, Caleb, Jessica,
>>
>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>> <snip>
>>>>> + if (ctx->dsi->dsc) {
>>>>
>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>
>>> I want to leave room for possibly running the panel without DSC (at a
>>> lower resolution/refresh rate, or at higher power consumption if there
>>> is enough BW) by not assigning the pointer, if we get access to panel
>>> documentation: probably one of the magic commands sent in this driver
>>> controls it but we don't know which.
>>
>> I'd like to investigate if DSC should perhaps only be enabled if we
>> run non certain platforms/socs ?
>>
>> I mean, we don't know if the controller supports DSC and those particular
>> DSC parameters so we should probably start adding something like :
>>
>> static drm_dsc_config dsc_params_qcom = {}
>>
>> static const struct of_device_id panel_of_dsc_params[] = {
>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>> };
>
> I think this would damage the reusability of the drivers. The panel
> driver does not actually care if the SoC is SM8350, sunxi-something or
> RCar.
> Instead it cares about host capabilities.
>
> I think instead we should extend mipi_dsi_host:
>
> #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
> #define MIPI_DSI_HOST_MODE_CMD BIT(1)
> #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
> // FIXME: do we need to provide additional caps here ?
>
> #define MIPI_DSI_DSC_1_1 BIT(0)
> #define MIPI_DSI_DSC_1_2 BIT(1)
> #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
> #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
> #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
> // etc.
>
> struct mipi_dsi_host {
> // new fields only
> unsigned long mode_flags;
> unsigned long dsc_flags;
> };
>
> Then the panel driver can adapt itself to the host capabilities and
> (possibly) select one of the internally supported DSC profiles.
>
I completely agree about extending mipi_dsi_host, other SoCs could reuse that and
support for DSC panels would become a lot cleaner.
For example, on MediaTek DRM there's some support for DSC, more or less the same
for SPRD DRM and some DSI bridge drivers... having a clean infrastructure would
definitely help.
I'm sad I cannot offer testing in that case because despite being sure that there
are MTK smartphones around with DSI panels using DSC, I have none... and all of the
Chromebooks are not using DSC anyway (but using DisplayPort compression, which is
obviously an entirely different beast).
>>
>> ...
>> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
>> ...
>> const struct of_device_id *match;
>>
>> ...
>> match = of_match_node(panel_of_dsc_params, of_root);
>> if (match && match->data) {
>> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>> memcpy(dsi->dsc, match->data, sizeof(*dsc));
>> } else {
>> dev_warn(&dsi->dev, "DSI controller is not marked as supporting DSC\n");
>> }
>> ...
>> }
>>
>> and probably bail out if it's a DSC only panel.
>>
Usually DDICs support both DSC and non-DSC modes, depending on the initial
programming (read: init commands)... but the usual issue is that many DDICs
are not publicly documented for reasons, so yes, bailing out if DSC is not
supported would be the only option, and would be fine at this point.
Cheers,
Angelo
>> We could alternatively match on the DSI controller's dsi->host->dev instead of the SoC root compatible.
>>
>> Neil
>
On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
> Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
>> On Tue, 30 May 2023 at 10:24, Neil Armstrong
>> <[email protected]> wrote:
>>>
>>> Hi Marijn, Dmitry, Caleb, Jessica,
>>>
>>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>>> <snip>
>>>>>> + if (ctx->dsi->dsc) {
>>>>>
>>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>>
>>>> I want to leave room for possibly running the panel without DSC (at a
>>>> lower resolution/refresh rate, or at higher power consumption if there
>>>> is enough BW) by not assigning the pointer, if we get access to panel
>>>> documentation: probably one of the magic commands sent in this driver
>>>> controls it but we don't know which.
>>>
>>> I'd like to investigate if DSC should perhaps only be enabled if we
>>> run non certain platforms/socs ?
>>>
>>> I mean, we don't know if the controller supports DSC and those
>>> particular
>>> DSC parameters so we should probably start adding something like :
>>>
>>> static drm_dsc_config dsc_params_qcom = {}
>>>
>>> static const struct of_device_id panel_of_dsc_params[] = {
>>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>>> };
>>
>> I think this would damage the reusability of the drivers. The panel
>> driver does not actually care if the SoC is SM8350, sunxi-something or
>> RCar.
>> Instead it cares about host capabilities.
>>
>> I think instead we should extend mipi_dsi_host:
>>
>> #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
>> #define MIPI_DSI_HOST_MODE_CMD BIT(1)
>> #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
>> // FIXME: do we need to provide additional caps here ?
>>
>> #define MIPI_DSI_DSC_1_1 BIT(0)
>> #define MIPI_DSI_DSC_1_2 BIT(1)
>> #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
>> #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
>> #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
>> // etc.
>>
>> struct mipi_dsi_host {
>> // new fields only
>> unsigned long mode_flags;
>> unsigned long dsc_flags;
>> };
>>
>> Then the panel driver can adapt itself to the host capabilities and
>> (possibly) select one of the internally supported DSC profiles.
>>
>
> I completely agree about extending mipi_dsi_host, other SoCs could reuse
> that and
> support for DSC panels would become a lot cleaner.
Sounds good. I will wait for one or two more days (to get the possible
feedback on fields/flags/etc) and post an RFC patch to dri-devel.
>
> For example, on MediaTek DRM there's some support for DSC, more or less
> the same
> for SPRD DRM and some DSI bridge drivers... having a clean
> infrastructure would
> definitely help.
>
> I'm sad I cannot offer testing in that case because despite being sure
> that there
> are MTK smartphones around with DSI panels using DSC, I have none... and
> all of the
> Chromebooks are not using DSC anyway (but using DisplayPort compression,
> which is
> obviously an entirely different beast).
>
>>>
>>> ...
>>> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
>>> ...
>>> const struct of_device_id *match;
>>>
>>> ...
>>> match = of_match_node(panel_of_dsc_params, of_root);
>>> if (match && match->data) {
>>> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc),
>>> GFP_KERNEL);
>>> memcpy(dsi->dsc, match->data, sizeof(*dsc));
>>> } else {
>>> dev_warn(&dsi->dev, "DSI controller is not marked as
>>> supporting DSC\n");
>>> }
>>> ...
>>> }
>>>
>>> and probably bail out if it's a DSC only panel.
>>>
>
> Usually DDICs support both DSC and non-DSC modes, depending on the initial
> programming (read: init commands)... but the usual issue is that many DDICs
> are not publicly documented for reasons, so yes, bailing out if DSC is not
> supported would be the only option, and would be fine at this point.
>
> Cheers,
> Angelo
>
>>> We could alternatively match on the DSI controller's dsi->host->dev
>>> instead of the SoC root compatible.
>>>
>>> Neil
>>
>
--
With best wishes
Dmitry
On 30/05/2023 14:36, Dmitry Baryshkov wrote:
> On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
>> Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
>>> On Tue, 30 May 2023 at 10:24, Neil Armstrong <[email protected]> wrote:
>>>>
>>>> Hi Marijn, Dmitry, Caleb, Jessica,
>>>>
>>>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>>>> <snip>
>>>>>>> + if (ctx->dsi->dsc) {
>>>>>>
>>>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>>>
>>>>> I want to leave room for possibly running the panel without DSC (at a
>>>>> lower resolution/refresh rate, or at higher power consumption if there
>>>>> is enough BW) by not assigning the pointer, if we get access to panel
>>>>> documentation: probably one of the magic commands sent in this driver
>>>>> controls it but we don't know which.
>>>>
>>>> I'd like to investigate if DSC should perhaps only be enabled if we
>>>> run non certain platforms/socs ?
>>>>
>>>> I mean, we don't know if the controller supports DSC and those particular
>>>> DSC parameters so we should probably start adding something like :
>>>>
>>>> static drm_dsc_config dsc_params_qcom = {}
>>>>
>>>> static const struct of_device_id panel_of_dsc_params[] = {
>>>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>>>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>>>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>>>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>>>> };
>>>
>>> I think this would damage the reusability of the drivers. The panel
>>> driver does not actually care if the SoC is SM8350, sunxi-something or
>>> RCar.
>>> Instead it cares about host capabilities.
>>>
>>> I think instead we should extend mipi_dsi_host:
>>>
>>> #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
I assume all DSI controller supports Video mode, so it should be a negative here
if for a reason it's not the case.
There should also be a flag to tell if sending LP commands sending while
in HS Video mode is supported.
>>> #define MIPI_DSI_HOST_MODE_CMD BIT(1)
>>> #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
>>> // FIXME: do we need to provide additional caps here ?
>>>
>>> #define MIPI_DSI_DSC_1_1 BIT(0)
>>> #define MIPI_DSI_DSC_1_2 BIT(1)
>>> #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
>>> #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
>>> #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
>>> // etc.
>>>
>>> struct mipi_dsi_host {
>>> // new fields only
>>> unsigned long mode_flags;
>>> unsigned long dsc_flags;
>>> };
>>>
>>> Then the panel driver can adapt itself to the host capabilities and
>>> (possibly) select one of the internally supported DSC profiles.
>>>
>>
>> I completely agree about extending mipi_dsi_host, other SoCs could reuse that and
>> support for DSC panels would become a lot cleaner.
>
> Sounds good. I will wait for one or two more days (to get the possible feedback on fields/flags/etc) and post an RFC patch to dri-devel.
Good, I was waiting until a DSC panel appears on the list (and I failed to be the first), it's now the case.
For VTRD6130, the panel is capable of the 4 modes:
- video mode
- command mode
- video mode & DSC
- command mode & DSC
So it would need such info to enable one of the mode in some order to determine.
Thanks,
Neil
>
>>
>> For example, on MediaTek DRM there's some support for DSC, more or less the same
>> for SPRD DRM and some DSI bridge drivers... having a clean infrastructure would
>> definitely help.
>>
>> I'm sad I cannot offer testing in that case because despite being sure that there
>> are MTK smartphones around with DSI panels using DSC, I have none... and all of the
>> Chromebooks are not using DSC anyway (but using DisplayPort compression, which is
>> obviously an entirely different beast).
>>
>>>>
>>>> ...
>>>> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
>>>> ...
>>>> const struct of_device_id *match;
>>>>
>>>> ...
>>>> match = of_match_node(panel_of_dsc_params, of_root);
>>>> if (match && match->data) {
>>>> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>>>> memcpy(dsi->dsc, match->data, sizeof(*dsc));
>>>> } else {
>>>> dev_warn(&dsi->dev, "DSI controller is not marked as supporting DSC\n");
>>>> }
>>>> ...
>>>> }
>>>>
>>>> and probably bail out if it's a DSC only panel.
>>>>
>>
>> Usually DDICs support both DSC and non-DSC modes, depending on the initial
>> programming (read: init commands)... but the usual issue is that many DDICs
>> are not publicly documented for reasons, so yes, bailing out if DSC is not
>> supported would be the only option, and would be fine at this point.
>>
>> Cheers,
>> Angelo
>>
>>>> We could alternatively match on the DSI controller's dsi->host->dev instead of the SoC root compatible.
>>>>
>>>> Neil
>>>
>>
>
On 5/29/2023 3:18 PM, Dmitry Baryshkov wrote:
> On 30/05/2023 00:07, Marijn Suijten wrote:
>> On 2023-05-22 15:58:56, Dmitry Baryshkov wrote:
>>> On Mon, 22 May 2023 at 12:04, Neil Armstrong
>>> <[email protected]> wrote:
>>>>
>>>> On 22/05/2023 03:16, Dmitry Baryshkov wrote:
>>>>> On 22/05/2023 00:23, Marijn Suijten wrote:
>>>>>> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its
>>>>>> Xperia
>>>>>> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
>>>>>>
>>>>>> This panel features Display Stream Compression 1.1.
>>>>>>
>>>>>> Signed-off-by: Marijn Suijten <[email protected]>
>>>>>> ---
>>>>>> drivers/gpu/drm/panel/Kconfig | 11 +
>>>>>> drivers/gpu/drm/panel/Makefile | 1 +
>>>>>> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362
>>>>>> ++++++++++++++++++++++++
>>>>>> 3 files changed, 374 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/panel/Kconfig
>>>>>> b/drivers/gpu/drm/panel/Kconfig
>>>>>> index 67ef898d133f2..18bd116e78a71 100644
>>>>>> --- a/drivers/gpu/drm/panel/Kconfig
>>>>>> +++ b/drivers/gpu/drm/panel/Kconfig
>>>>>> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
>>>>>> Say Y here if you want to enable support for the Sony
>>>>>> ACX565AKM
>>>>>> 800x600 3.5" panel (found on the Nokia N900).
>>>>>> +config DRM_PANEL_SONY_AKATSUKI_LGD
>>>>>> + tristate "Sony Xperia XZ3 LGD panel"
>>>>>> + depends on GPIOLIB && OF
>>>>>> + depends on DRM_MIPI_DSI
>>>>>> + depends on BACKLIGHT_CLASS_DEVICE
>>>>>> + help
>>>>>> + Say Y here if you want to enable support for the Sony
>>>>>> Xperia XZ3
>>>>>> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG
>>>>>> Display.
>>>>>> +
>>>>>> + This panel uses Display Stream Compression 1.1.
>>>>>> +
>>>>>> config DRM_PANEL_SONY_TD4353_JDI
>>>>>> tristate "Sony TD4353 JDI panel"
>>>>>> depends on GPIOLIB && OF
>>>>>> diff --git a/drivers/gpu/drm/panel/Makefile
>>>>>> b/drivers/gpu/drm/panel/Makefile
>>>>>> index ff169781e82d7..85133f73558f3 100644
>>>>>> --- a/drivers/gpu/drm/panel/Makefile
>>>>>> +++ b/drivers/gpu/drm/panel/Makefile
>>>>>> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) +=
>>>>>> panel-sitronix-st7701.o
>>>>>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
>>>>>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) +=
>>>>>> panel-sitronix-st7789v.o
>>>>>> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
>>>>>> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) +=
>>>>>> panel-sony-akatsuki-lgd.o
>>>>>> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
>>>>>> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) +=
>>>>>> panel-sony-tulip-truly-nt35521.o
>>>>>> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
>>>>>> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>>> b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>>> new file mode 100644
>>>>>> index 0000000000000..f55788f963dab
>>>>>> --- /dev/null
>>>>>> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>>> @@ -0,0 +1,362 @@
>>>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>>>> +/*
>>>>>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>>>>>> + *
>>>>>> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
>>>>>> +
>>>>>> +struct sony_akatsuki_lgd {
>>>>>> + struct drm_panel panel;
>>>>>> + struct mipi_dsi_device *dsi;
>>>>>> + struct regulator *vddio;
>>>>>> + struct gpio_desc *reset_gpio;
>>>>>> + bool prepared;
>>>>>> +};
>>>>>> +
>>>>>> +static inline struct sony_akatsuki_lgd
>>>>>> *to_sony_akatsuki_lgd(struct drm_panel *panel)
>>>>>> +{
>>>>>> + return container_of(panel, struct sony_akatsuki_lgd, panel);
>>>>>> +}
>>>>>> +
>>>>>> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
>>>>>> +{
>>>>>> + struct mipi_dsi_device *dsi = ctx->dsi;
>>>>>> + struct device *dev = &dsi->dev;
>>>>>> + int ret;
>>>>>> +
>>>>>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>>>>>> +
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
>>>>>> + /* Enable backlight control */
>>>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
>>>>>> BIT(5));
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
>>>>>> +
>>>>>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
>>>>>> + if (ret < 0) {
>>>>>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
>>>>>> + if (ret < 0) {
>>>>>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
>>>>>> +
>>>>>> + ret = mipi_dsi_dcs_set_tear_on(dsi,
>>>>>> MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>>>>>> + if (ret < 0) {
>>>>>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> +
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
>>>>>> +
>>>>>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>>>>>> + if (ret < 0) {
>>>>>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>> + msleep(120);
>>>>>> +
>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
>>>>>> +
>>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>>>>>> + if (ret < 0) {
>>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>>>>>> + return ret;
>>>>>> + }
>>>>>
>>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() /
>>>>> mipi_dsi_dcs_set_display_on() be moved from prepare() to enable()
>>>>> part?
>>>>
>>>>
>>>> No, prepare is called before the video stream is started and when
>>>> display is still in LPM mode and the mode hasn't been set.
>>>>
>>>
>>> Yes, that's my point. Shouldn't we enable the panel _after_ starting
>>> the stream?
>>
>> I have never investigated what it takes to split these functions, but
>> some of these panels do show some corruption at startup which may be
>> circumvented by powering the panel on after starting the video stream?
>>
>> I'm just not sure where to make the split: downstream does describe a
>> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
>> the latter only contains set_display_on() (not exit_sleep_mode()).
>> It is documented like:
>>
>> same as "qcom,mdss-dsi-on-command" except commands are sent after
>> displaying an image."
>>
>> So this seems like the right way to split them up, I'll test this out on
>> all submitted panel drivers.
>
> Interesting enough, Neil suggested that sending all the commands during
> pre_enable() is the correct sequence (especially for VIDEO mode panels),
> since not all DSI hosts can send commands after switching to the VIDEO
> mode.
>
I agree with Neil here.
Yes, it does seem natural to think that sending the video stream before
sending the on commands would avoid any potential corruption / garbage
screen issues.
But even from panel side should allow that. I have seen panel ON
sequences where some explicitly ask for ON commands before the video stream.
So, we cannot really generalize it and needs to be treated on a
host-to-host and panel-to-panel basis.
On 2023-05-30 14:11:06, Dmitry Baryshkov wrote:
> On Tue, 30 May 2023 at 11:27, Marijn Suijten
> <[email protected]> wrote:
> >
> > On 2023-05-30 01:39:10, Dmitry Baryshkov wrote:
> > > On 30/05/2023 01:37, Marijn Suijten wrote:
> > > > On 2023-05-30 01:18:40, Dmitry Baryshkov wrote:
> > > > <snip>
> > > >>>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
> > > >>>>>>> + if (ret < 0) {
> > > >>>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> > > >>>>>>> + return ret;
> > > >>>>>>> + }
> > > >>>>>>
> > > >>>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() / mipi_dsi_dcs_set_display_on() be moved from prepare() to enable() part?
> > > >>>>>
> > > >>>>>
> > > >>>>> No, prepare is called before the video stream is started and when display is still in LPM mode and the mode hasn't been set.
> > > >>>>>
> > > >>>>
> > > >>>> Yes, that's my point. Shouldn't we enable the panel _after_ starting the stream?
> > > >>>
> > > >>> I have never investigated what it takes to split these functions, but
> > > >>> some of these panels do show some corruption at startup which may be
> > > >>> circumvented by powering the panel on after starting the video stream?
> > > >>>
> > > >>> I'm just not sure where to make the split: downstream does describe a
> > > >>> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
> > > >>> the latter only contains set_display_on() (not exit_sleep_mode()).
> > > >>> It is documented like:
> > > >>>
> > > >>> same as "qcom,mdss-dsi-on-command" except commands are sent after
> > > >>> displaying an image."
> > > >>>
> > > >>> So this seems like the right way to split them up, I'll test this out on
> > > >>> all submitted panel drivers.
> > > >>
> > > >> Interesting enough, Neil suggested that sending all the commands during
> > > >> pre_enable() is the correct sequence (especially for VIDEO mode panels),
> > > >> since not all DSI hosts can send commands after switching to the VIDEO mode.
> > > >
> > > > Note that all these panels and Driver-ICs are command-mode, and/or
> > > > programmed to run in command-mode, so there shouldn't be any notion of a
> > > > VIDEO stream (any command-mode frame is just an "arbitrary command" as
> > > > far as I understood).
> > >
> > > Yes, from the data stream point of view. I was talking about the DSI
> > > host being able to send arbitrary commands or not after enabling the
> > > video/cmd stream.
> >
> > Is this a known limitation of SM8250 then? Or is the brightness_set
> > issue more likely a "problem" with the panel that the downstream kernel
> > is "somehow" working around or aware of, and I just haven't found
> > how/where it deals with that?
> > (Alternatively we could be "doing it wrong" for other panels but it
> > turns out to be working anyway)
>
> Please excuse me for not being explicit enough. Qualcomm hardware
> doesn't have this problem. Thus I was completely unaware of it before
> talking to Neil.
> So, our hardware works in most of the cases.
Also excuse me for mocking the hardware here; it seems quite illogical
for it to not work on this specific device which is more likely a
failure in porting the panel DT to the driver than related to this
specific SoC. There's probably one of the hundred-or-so DT params
responsible for triggering a setting, delay, or other magic sequence
that gets the brightness toggle working.
- Marijn
On 2023-05-30 10:54:17, Abhinav Kumar wrote:
> > On 30/05/2023 00:07, Marijn Suijten wrote:
> >> On 2023-05-22 15:58:56, Dmitry Baryshkov wrote:
> >>> On Mon, 22 May 2023 at 12:04, Neil Armstrong
> >>> <[email protected]> wrote:
> >>>>
> >>>> On 22/05/2023 03:16, Dmitry Baryshkov wrote:
> >>>>> On 22/05/2023 00:23, Marijn Suijten wrote:
> >>>>>> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its
> >>>>>> Xperia
> >>>>>> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
> >>>>>>
> >>>>>> This panel features Display Stream Compression 1.1.
> >>>>>>
> >>>>>> Signed-off-by: Marijn Suijten <[email protected]>
> >>>>>> ---
> >>>>>> ?? drivers/gpu/drm/panel/Kconfig?????????????????? |? 11 +
> >>>>>> ?? drivers/gpu/drm/panel/Makefile????????????????? |?? 1 +
> >>>>>> ?? drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362
> >>>>>> ++++++++++++++++++++++++
> >>>>>> ?? 3 files changed, 374 insertions(+)
> >>>>>>
> >>>>>> diff --git a/drivers/gpu/drm/panel/Kconfig
> >>>>>> b/drivers/gpu/drm/panel/Kconfig
> >>>>>> index 67ef898d133f2..18bd116e78a71 100644
> >>>>>> --- a/drivers/gpu/drm/panel/Kconfig
> >>>>>> +++ b/drivers/gpu/drm/panel/Kconfig
> >>>>>> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
> >>>>>> ???????? Say Y here if you want to enable support for the Sony
> >>>>>> ACX565AKM
> >>>>>> ???????? 800x600 3.5" panel (found on the Nokia N900).
> >>>>>> +config DRM_PANEL_SONY_AKATSUKI_LGD
> >>>>>> +??? tristate "Sony Xperia XZ3 LGD panel"
> >>>>>> +??? depends on GPIOLIB && OF
> >>>>>> +??? depends on DRM_MIPI_DSI
> >>>>>> +??? depends on BACKLIGHT_CLASS_DEVICE
> >>>>>> +??? help
> >>>>>> +????? Say Y here if you want to enable support for the Sony
> >>>>>> Xperia XZ3
> >>>>>> +????? 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG
> >>>>>> Display.
> >>>>>> +
> >>>>>> +????? This panel uses Display Stream Compression 1.1.
> >>>>>> +
> >>>>>> ?? config DRM_PANEL_SONY_TD4353_JDI
> >>>>>> ?????? tristate "Sony TD4353 JDI panel"
> >>>>>> ?????? depends on GPIOLIB && OF
> >>>>>> diff --git a/drivers/gpu/drm/panel/Makefile
> >>>>>> b/drivers/gpu/drm/panel/Makefile
> >>>>>> index ff169781e82d7..85133f73558f3 100644
> >>>>>> --- a/drivers/gpu/drm/panel/Makefile
> >>>>>> +++ b/drivers/gpu/drm/panel/Makefile
> >>>>>> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) +=
> >>>>>> panel-sitronix-st7701.o
> >>>>>> ?? obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
> >>>>>> ?? obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) +=
> >>>>>> panel-sitronix-st7789v.o
> >>>>>> ?? obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
> >>>>>> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) +=
> >>>>>> panel-sony-akatsuki-lgd.o
> >>>>>> ?? obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
> >>>>>> ?? obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) +=
> >>>>>> panel-sony-tulip-truly-nt35521.o
> >>>>>> ?? obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
> >>>>>> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> >>>>>> b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> >>>>>> new file mode 100644
> >>>>>> index 0000000000000..f55788f963dab
> >>>>>> --- /dev/null
> >>>>>> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
> >>>>>> @@ -0,0 +1,362 @@
> >>>>>> +// SPDX-License-Identifier: GPL-2.0-only
> >>>>>> +/*
> >>>>>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
> >>>>>> + *
> >>>>>> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
> >>>>>> +
> >>>>>> +struct sony_akatsuki_lgd {
> >>>>>> +??? struct drm_panel panel;
> >>>>>> +??? struct mipi_dsi_device *dsi;
> >>>>>> +??? struct regulator *vddio;
> >>>>>> +??? struct gpio_desc *reset_gpio;
> >>>>>> +??? bool prepared;
> >>>>>> +};
> >>>>>> +
> >>>>>> +static inline struct sony_akatsuki_lgd
> >>>>>> *to_sony_akatsuki_lgd(struct drm_panel *panel)
> >>>>>> +{
> >>>>>> +??? return container_of(panel, struct sony_akatsuki_lgd, panel);
> >>>>>> +}
> >>>>>> +
> >>>>>> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
> >>>>>> +{
> >>>>>> +??? struct mipi_dsi_device *dsi = ctx->dsi;
> >>>>>> +??? struct device *dev = &dsi->dev;
> >>>>>> +??? int ret;
> >>>>>> +
> >>>>>> +??? dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> >>>>>> +
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
> >>>>>> +??? /* Enable backlight control */
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
> >>>>>> BIT(5));
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
> >>>>>> +
> >>>>>> +??? ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
> >>>>>> +??? if (ret < 0) {
> >>>>>> +??????? dev_err(dev, "Failed to set column address: %d\n", ret);
> >>>>>> +??????? return ret;
> >>>>>> +??? }
> >>>>>> +
> >>>>>> +??? ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
> >>>>>> +??? if (ret < 0) {
> >>>>>> +??????? dev_err(dev, "Failed to set page address: %d\n", ret);
> >>>>>> +??????? return ret;
> >>>>>> +??? }
> >>>>>> +
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
> >>>>>> +
> >>>>>> +??? ret = mipi_dsi_dcs_set_tear_on(dsi,
> >>>>>> MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> >>>>>> +??? if (ret < 0) {
> >>>>>> +??????? dev_err(dev, "Failed to set tear on: %d\n", ret);
> >>>>>> +??????? return ret;
> >>>>>> +??? }
> >>>>>> +
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
> >>>>>> +
> >>>>>> +??? ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> >>>>>> +??? if (ret < 0) {
> >>>>>> +??????? dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> >>>>>> +??????? return ret;
> >>>>>> +??? }
> >>>>>> +??? msleep(120);
> >>>>>> +
> >>>>>> +??? mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
> >>>>>> +
> >>>>>> +??? ret = mipi_dsi_dcs_set_display_on(dsi);
> >>>>>> +??? if (ret < 0) {
> >>>>>> +??????? dev_err(dev, "Failed to turn display on: %d\n", ret);
> >>>>>> +??????? return ret;
> >>>>>> +??? }
> >>>>>
> >>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() /
> >>>>> mipi_dsi_dcs_set_display_on() be moved from prepare() to enable()
> >>>>> part?
> >>>>
> >>>>
> >>>> No, prepare is called before the video stream is started and when
> >>>> display is still in LPM mode and the mode hasn't been set.
> >>>>
> >>>
> >>> Yes, that's my point. Shouldn't we enable the panel _after_ starting
> >>> the stream?
> >>
> >> I have never investigated what it takes to split these functions, but
> >> some of these panels do show some corruption at startup which may be
> >> circumvented by powering the panel on after starting the video stream?
> >>
> >> I'm just not sure where to make the split: downstream does describe a
> >> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
> >> the latter only contains set_display_on() (not exit_sleep_mode()).
> >> It is documented like:
> >>
> >> ???? same as "qcom,mdss-dsi-on-command" except commands are sent after
> >> ???? displaying an image."
> >>
> >> So this seems like the right way to split them up, I'll test this out on
> >> all submitted panel drivers.
> >
> > Interesting enough, Neil suggested that sending all the commands during
> > pre_enable() is the correct sequence (especially for VIDEO mode panels),
> > since not all DSI hosts can send commands after switching to the VIDEO
> > mode.
> >
>
> I agree with Neil here.
>
> Yes, it does seem natural to think that sending the video stream before
> sending the on commands would avoid any potential corruption / garbage
> screen issues.
>
> But even from panel side should allow that. I have seen panel ON
> sequences where some explicitly ask for ON commands before the video stream.
>
> So, we cannot really generalize it and needs to be treated on a
> host-to-host and panel-to-panel basis.
All four panel drivers in this series have a `post-panel-on` separation
(containing _just_ the panel_on DCS) and should be implemented with a
separate .enable callback.
More importantly: is the API to enable/disable, and separately
prepare/unprepare the panel exposed to userspace? And is there an app
(maybe as part of mesa/drm where modetest lives) that is able to trigger
these calls to test adequate behaviour? In the past I've seen panels
working, until my userspace suspends and turn the panel off, where it
never comes back up properly after.
- Marijn
On 30/05/2023 21:13, Marijn Suijten wrote:
> On 2023-05-30 10:54:17, Abhinav Kumar wrote:
>>> On 30/05/2023 00:07, Marijn Suijten wrote:
>>>> On 2023-05-22 15:58:56, Dmitry Baryshkov wrote:
>>>>> On Mon, 22 May 2023 at 12:04, Neil Armstrong
>>>>> <[email protected]> wrote:
>>>>>>
>>>>>> On 22/05/2023 03:16, Dmitry Baryshkov wrote:
>>>>>>> On 22/05/2023 00:23, Marijn Suijten wrote:
>>>>>>>> Sony provides an unlabeled LGD + Atmel maXTouch assembly in its
>>>>>>>> Xperia
>>>>>>>> XZ3 (tama akatsuki) phone, with custom DCS commands to match.
>>>>>>>>
>>>>>>>> This panel features Display Stream Compression 1.1.
>>>>>>>>
>>>>>>>> Signed-off-by: Marijn Suijten <[email protected]>
>>>>>>>> ---
>>>>>>>> drivers/gpu/drm/panel/Kconfig | 11 +
>>>>>>>> drivers/gpu/drm/panel/Makefile | 1 +
>>>>>>>> drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c | 362
>>>>>>>> ++++++++++++++++++++++++
>>>>>>>> 3 files changed, 374 insertions(+)
>>>>>>>>
>>>>>>>> diff --git a/drivers/gpu/drm/panel/Kconfig
>>>>>>>> b/drivers/gpu/drm/panel/Kconfig
>>>>>>>> index 67ef898d133f2..18bd116e78a71 100644
>>>>>>>> --- a/drivers/gpu/drm/panel/Kconfig
>>>>>>>> +++ b/drivers/gpu/drm/panel/Kconfig
>>>>>>>> @@ -706,6 +706,17 @@ config DRM_PANEL_SONY_ACX565AKM
>>>>>>>> Say Y here if you want to enable support for the Sony
>>>>>>>> ACX565AKM
>>>>>>>> 800x600 3.5" panel (found on the Nokia N900).
>>>>>>>> +config DRM_PANEL_SONY_AKATSUKI_LGD
>>>>>>>> + tristate "Sony Xperia XZ3 LGD panel"
>>>>>>>> + depends on GPIOLIB && OF
>>>>>>>> + depends on DRM_MIPI_DSI
>>>>>>>> + depends on BACKLIGHT_CLASS_DEVICE
>>>>>>>> + help
>>>>>>>> + Say Y here if you want to enable support for the Sony
>>>>>>>> Xperia XZ3
>>>>>>>> + 1440x2880@60 6.0" OLED DSI cmd mode panel produced by LG
>>>>>>>> Display.
>>>>>>>> +
>>>>>>>> + This panel uses Display Stream Compression 1.1.
>>>>>>>> +
>>>>>>>> config DRM_PANEL_SONY_TD4353_JDI
>>>>>>>> tristate "Sony TD4353 JDI panel"
>>>>>>>> depends on GPIOLIB && OF
>>>>>>>> diff --git a/drivers/gpu/drm/panel/Makefile
>>>>>>>> b/drivers/gpu/drm/panel/Makefile
>>>>>>>> index ff169781e82d7..85133f73558f3 100644
>>>>>>>> --- a/drivers/gpu/drm/panel/Makefile
>>>>>>>> +++ b/drivers/gpu/drm/panel/Makefile
>>>>>>>> @@ -71,6 +71,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7701) +=
>>>>>>>> panel-sitronix-st7701.o
>>>>>>>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += panel-sitronix-st7703.o
>>>>>>>> obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) +=
>>>>>>>> panel-sitronix-st7789v.o
>>>>>>>> obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
>>>>>>>> +obj-$(CONFIG_DRM_PANEL_SONY_AKATSUKI_LGD) +=
>>>>>>>> panel-sony-akatsuki-lgd.o
>>>>>>>> obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o
>>>>>>>> obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) +=
>>>>>>>> panel-sony-tulip-truly-nt35521.o
>>>>>>>> obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
>>>>>>>> diff --git a/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>>>>> b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000000000..f55788f963dab
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/drivers/gpu/drm/panel/panel-sony-akatsuki-lgd.c
>>>>>>>> @@ -0,0 +1,362 @@
>>>>>>>> +// SPDX-License-Identifier: GPL-2.0-only
>>>>>>>> +/*
>>>>>>>> + * Copyright (c) 2023 Marijn Suijten <[email protected]>
>>>>>>>> + *
>>>>>>>> + * Based on Sony Downstream's "Atmel LGD ID5" Akatsuki panel 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>
>>>>>>>> +
>>>>>>>> +struct sony_akatsuki_lgd {
>>>>>>>> + struct drm_panel panel;
>>>>>>>> + struct mipi_dsi_device *dsi;
>>>>>>>> + struct regulator *vddio;
>>>>>>>> + struct gpio_desc *reset_gpio;
>>>>>>>> + bool prepared;
>>>>>>>> +};
>>>>>>>> +
>>>>>>>> +static inline struct sony_akatsuki_lgd
>>>>>>>> *to_sony_akatsuki_lgd(struct drm_panel *panel)
>>>>>>>> +{
>>>>>>>> + return container_of(panel, struct sony_akatsuki_lgd, panel);
>>>>>>>> +}
>>>>>>>> +
>>>>>>>> +static int sony_akatsuki_lgd_on(struct sony_akatsuki_lgd *ctx)
>>>>>>>> +{
>>>>>>>> + struct mipi_dsi_device *dsi = ctx->dsi;
>>>>>>>> + struct device *dev = &dsi->dev;
>>>>>>>> + int ret;
>>>>>>>> +
>>>>>>>> + dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>>>>>>>> +
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x02, 0x01);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x59, 0x01);
>>>>>>>> + /* Enable backlight control */
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
>>>>>>>> BIT(5));
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x57, 0x20, 0x80, 0xde, 0x60, 0x00);
>>>>>>>> +
>>>>>>>> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, 1440 - 1);
>>>>>>>> + if (ret < 0) {
>>>>>>>> + dev_err(dev, "Failed to set column address: %d\n", ret);
>>>>>>>> + return ret;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, 2880 - 1);
>>>>>>>> + if (ret < 0) {
>>>>>>>> + dev_err(dev, "Failed to set page address: %d\n", ret);
>>>>>>>> + return ret;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
>>>>>>>> +
>>>>>>>> + ret = mipi_dsi_dcs_set_tear_on(dsi,
>>>>>>>> MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>>>>>>>> + if (ret < 0) {
>>>>>>>> + dev_err(dev, "Failed to set tear on: %d\n", ret);
>>>>>>>> + return ret;
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0x7f, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf1, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf2, 0x5a, 0x5a);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x03);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x04);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xf6, 0x01, 0x7f, 0x00);
>>>>>>>> +
>>>>>>>> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
>>>>>>>> + if (ret < 0) {
>>>>>>>> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
>>>>>>>> + return ret;
>>>>>>>> + }
>>>>>>>> + msleep(120);
>>>>>>>> +
>>>>>>>> + mipi_dsi_dcs_write_seq(dsi, 0xe3, 0xac, 0x19, 0x34, 0x14, 0x7d);
>>>>>>>> +
>>>>>>>> + ret = mipi_dsi_dcs_set_display_on(dsi);
>>>>>>>> + if (ret < 0) {
>>>>>>>> + dev_err(dev, "Failed to turn display on: %d\n", ret);
>>>>>>>> + return ret;
>>>>>>>> + }
>>>>>>>
>>>>>>> My usual question: should the mipi_dsi_dcs_exit_sleep_mode() /
>>>>>>> mipi_dsi_dcs_set_display_on() be moved from prepare() to enable()
>>>>>>> part?
>>>>>>
>>>>>>
>>>>>> No, prepare is called before the video stream is started and when
>>>>>> display is still in LPM mode and the mode hasn't been set.
>>>>>>
>>>>>
>>>>> Yes, that's my point. Shouldn't we enable the panel _after_ starting
>>>>> the stream?
>>>>
>>>> I have never investigated what it takes to split these functions, but
>>>> some of these panels do show some corruption at startup which may be
>>>> circumvented by powering the panel on after starting the video stream?
>>>>
>>>> I'm just not sure where to make the split: downstream does describe a
>>>> qcom,mdss-dsi-on-command and qcom,mdss-dsi-post-panel-on-command, where
>>>> the latter only contains set_display_on() (not exit_sleep_mode()).
>>>> It is documented like:
>>>>
>>>> same as "qcom,mdss-dsi-on-command" except commands are sent after
>>>> displaying an image."
>>>>
>>>> So this seems like the right way to split them up, I'll test this out on
>>>> all submitted panel drivers.
>>>
>>> Interesting enough, Neil suggested that sending all the commands during
>>> pre_enable() is the correct sequence (especially for VIDEO mode panels),
>>> since not all DSI hosts can send commands after switching to the VIDEO
>>> mode.
>>>
>>
>> I agree with Neil here.
>>
>> Yes, it does seem natural to think that sending the video stream before
>> sending the on commands would avoid any potential corruption / garbage
>> screen issues.
>>
>> But even from panel side should allow that. I have seen panel ON
>> sequences where some explicitly ask for ON commands before the video stream.
>>
>> So, we cannot really generalize it and needs to be treated on a
>> host-to-host and panel-to-panel basis.
>
> All four panel drivers in this series have a `post-panel-on` separation
> (containing _just_ the panel_on DCS) and should be implemented with a
> separate .enable callback.
>
> More importantly: is the API to enable/disable, and separately
> prepare/unprepare the panel exposed to userspace? And is there an app
> (maybe as part of mesa/drm where modetest lives) that is able to trigger
> these calls to test adequate behaviour? In the past I've seen panels
> working, until my userspace suspends and turn the panel off, where it
> never comes back up properly after.
No. The prepare/enable (and disable/unrepare) translate to the bridge's
pre_enable/enable and disable/post_disable callbacks, which are a part
of modesetting. One can not call them separately.
--
With best wishes
Dmitry
Il 30/05/23 17:44, Neil Armstrong ha scritto:
> On 30/05/2023 14:36, Dmitry Baryshkov wrote:
>> On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
>>> Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
>>>> On Tue, 30 May 2023 at 10:24, Neil Armstrong <[email protected]> wrote:
>>>>>
>>>>> Hi Marijn, Dmitry, Caleb, Jessica,
>>>>>
>>>>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>>>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>>>>> <snip>
>>>>>>>> + if (ctx->dsi->dsc) {
>>>>>>>
>>>>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>>>>
>>>>>> I want to leave room for possibly running the panel without DSC (at a
>>>>>> lower resolution/refresh rate, or at higher power consumption if there
>>>>>> is enough BW) by not assigning the pointer, if we get access to panel
>>>>>> documentation: probably one of the magic commands sent in this driver
>>>>>> controls it but we don't know which.
>>>>>
>>>>> I'd like to investigate if DSC should perhaps only be enabled if we
>>>>> run non certain platforms/socs ?
>>>>>
>>>>> I mean, we don't know if the controller supports DSC and those particular
>>>>> DSC parameters so we should probably start adding something like :
>>>>>
>>>>> static drm_dsc_config dsc_params_qcom = {}
>>>>>
>>>>> static const struct of_device_id panel_of_dsc_params[] = {
>>>>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>>>>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>>>>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>>>>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>>>>> };
>>>>
>>>> I think this would damage the reusability of the drivers. The panel
>>>> driver does not actually care if the SoC is SM8350, sunxi-something or
>>>> RCar.
>>>> Instead it cares about host capabilities.
>>>>
>>>> I think instead we should extend mipi_dsi_host:
>>>>
>>>> #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
>
> I assume all DSI controller supports Video mode, so it should be a negative here
> if for a reason it's not the case.
Either all positive or all negative... and yes I agree that all DSI controllers
support video mode nowadays, but:
- Will that be true for future controllers? (likely yes, but you never know)
- Is there any controller driver not implementing video mode?
- Will there be one in the future?
>
> There should also be a flag to tell if sending LP commands sending while
> in HS Video mode is supported.
>
+1. This is the case for both qcom and mtk.
>>>> #define MIPI_DSI_HOST_MODE_CMD BIT(1)
>>>> #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
>>>> // FIXME: do we need to provide additional caps here ?
>>>>
>>>> #define MIPI_DSI_DSC_1_1 BIT(0)
>>>> #define MIPI_DSI_DSC_1_2 BIT(1)
>>>> #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
>>>> #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
>>>> #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
>>>> // etc.
>>>>
>>>> struct mipi_dsi_host {
>>>> // new fields only
>>>> unsigned long mode_flags;
>>>> unsigned long dsc_flags;
>>>> };
>>>>
>>>> Then the panel driver can adapt itself to the host capabilities and
>>>> (possibly) select one of the internally supported DSC profiles.
>>>>
>>>
>>> I completely agree about extending mipi_dsi_host, other SoCs could reuse that and
>>> support for DSC panels would become a lot cleaner.
>>
>> Sounds good. I will wait for one or two more days (to get the possible feedback
>> on fields/flags/etc) and post an RFC patch to dri-devel.
>
> Good, I was waiting until a DSC panel appears on the list (and I failed to be the
> first), it's now the case.
>
> For VTRD6130, the panel is capable of the 4 modes:
> - video mode
> - command mode
> - video mode & DSC
> - command mode & DSC
>
> So it would need such info to enable one of the mode in some order to determine.
>
Dynamically determining is not trivial, as that depends on multiple variables:
- Availability of the modes (obviously)
- Available lanes
- Available bandwidth per lane
- Available total bandwidth
- Power consumption considerations (DSC IP may be using more or less power
depending on the actual SoC//controller)
- Thermal management: DSC may make no thermal sense as in, more heat output
vs thermal envelope (laptop vs embedded vs handset)
- Others
Hence, the implementation should also provide a way of choosing a preferred mode
on a per-controller basis (DSC or no compression).
Just a few considerations that came to mind with a good sleep.
Cheers!
> Thanks,
> Neil
>>
>>>
>>> For example, on MediaTek DRM there's some support for DSC, more or less the same
>>> for SPRD DRM and some DSI bridge drivers... having a clean infrastructure would
>>> definitely help.
>>>
>>> I'm sad I cannot offer testing in that case because despite being sure that there
>>> are MTK smartphones around with DSI panels using DSC, I have none... and all of the
>>> Chromebooks are not using DSC anyway (but using DisplayPort compression, which is
>>> obviously an entirely different beast).
>>>
>>>>>
>>>>> ...
>>>>> static int sony_akatsuki_lgd_probe(struct mipi_dsi_device *dsi)
>>>>> ...
>>>>> const struct of_device_id *match;
>>>>>
>>>>> ...
>>>>> match = of_match_node(panel_of_dsc_params, of_root);
>>>>> if (match && match->data) {
>>>>> dsi->dsc = devm_kzalloc(&dsi->dev, sizeof(*dsc), GFP_KERNEL);
>>>>> memcpy(dsi->dsc, match->data, sizeof(*dsc));
>>>>> } else {
>>>>> dev_warn(&dsi->dev, "DSI controller is not marked as
>>>>> supporting DSC\n");
>>>>> }
>>>>> ...
>>>>> }
>>>>>
>>>>> and probably bail out if it's a DSC only panel.
>>>>>
>>>
>>> Usually DDICs support both DSC and non-DSC modes, depending on the initial
>>> programming (read: init commands)... but the usual issue is that many DDICs
>>> are not publicly documented for reasons, so yes, bailing out if DSC is not
>>> supported would be the only option, and would be fine at this point.
>>>
>>> Cheers,
>>> Angelo
>>>
>>>>> We could alternatively match on the DSI controller's dsi->host->dev instead of
>>>>> the SoC root compatible.
>>>>>
>>>>> Neil
>>>>
>>>
>>
>
On Sun, 21 May 2023 23:23:09 +0200, Marijn Suijten wrote:
> Document the SOFEF06-M Display-IC and 1080x2520 panel found in the Sony
> Xperia 5 II (6.1").
>
> Signed-off-by: Marijn Suijten <[email protected]>
> ---
> .../bindings/display/panel/samsung,sofef03-m.yaml | 73 ++++++++++++++++++++++
> 1 file changed, 73 insertions(+)
>
Reviewed-by: Rob Herring <[email protected]>
On Sun, May 21, 2023 at 11:23 PM Marijn Suijten
<[email protected]> wrote:
> The Sony Xperia 1 (codename kumano griffin) features an unnamed 4k OLED
> DSI cmd mode panel produced by Samsung. It can be driven in a
> 1644x3840@60 or 1096x2560@60 mode, and always has Display Stream
> Compression 1.1 enabled.
>
> Signed-off-by: Marijn Suijten <[email protected]>
(...)
> +static int sony_griffin_samsung_on(struct sony_griffin_samsung *ctx)
> +{
> + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> + return ret;
> + }
> + usleep_range(10000, 11000);
> +
> + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set tear on: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
> + mipi_dsi_dcs_write_seq(dsi, 0xd7, 0x07);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> + /* Enable backlight control */
> + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> + msleep(110);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xe2, enable_4k ? 0 : 1);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> +
> + ret = mipi_dsi_dcs_set_column_address(dsi, 0, hdisplay - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set column address: %d\n", ret);
> + return ret;
> + }
> +
> + ret = mipi_dsi_dcs_set_page_address(dsi, 0, vdisplay - 1);
> + if (ret < 0) {
> + dev_err(dev, "Failed to set page address: %d\n", ret);
> + return ret;
> + }
> +
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x70);
> + mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0x60);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq(dsi, 0xc5, 0x2e, 0x21);
> + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> +
> + ret = mipi_dsi_dcs_set_display_on(dsi);
> + if (ret < 0) {
> + dev_err(dev, "Failed to turn display on: %d\n", ret);
> + return ret;
> + }
> +
> + return 0;
> +}
This is eerily similar to the sequence in panel-samsung-sofef00.c:
static int sofef00_panel_on(struct sofef00_panel *ctx)
{
struct mipi_dsi_device *dsi = ctx->dsi;
struct device *dev = &dsi->dev;
int ret;
dsi->mode_flags |= MIPI_DSI_MODE_LPM;
ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
if (ret < 0) {
dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
return ret;
}
usleep_range(10000, 11000);
mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
if (ret < 0) {
dev_err(dev, "Failed to set tear on: %d\n", ret);
return ret;
}
mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x07);
mipi_dsi_dcs_write_seq(dsi, 0xb6, 0x12);
mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
ret = mipi_dsi_dcs_set_display_on(dsi);
if (ret < 0) {
dev_err(dev, "Failed to set display on: %d\n", ret);
return ret;
}
return 0;
}
Isn't this just the same display controller with a different configuration?
Especially the sleep ranges are even the same.
I almost feel like buying these phones just to pry them apart and put
under a microscope to figure out what these displays actually contain.
Yours,
Linus Walleij
On 2023-06-28 11:22:37, Linus Walleij wrote:
> On Sun, May 21, 2023 at 11:23 PM Marijn Suijten
> <[email protected]> wrote:
>
> > The Sony Xperia 1 (codename kumano griffin) features an unnamed 4k OLED
> > DSI cmd mode panel produced by Samsung. It can be driven in a
> > 1644x3840@60 or 1096x2560@60 mode, and always has Display Stream
> > Compression 1.1 enabled.
> >
> > Signed-off-by: Marijn Suijten <[email protected]>
> (...)
>
> > +static int sony_griffin_samsung_on(struct sony_griffin_samsung *ctx)
> > +{
>
> > + ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> > + return ret;
> > + }
> > + usleep_range(10000, 11000);
> > +
> > + ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to set tear on: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> > + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x05);
> > + mipi_dsi_dcs_write_seq(dsi, 0xd7, 0x07);
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> > + /* Enable backlight control */
> > + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, BIT(5));
> > + msleep(110);
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> > + mipi_dsi_dcs_write_seq(dsi, 0xe2, enable_4k ? 0 : 1);
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> > +
> > + ret = mipi_dsi_dcs_set_column_address(dsi, 0, hdisplay - 1);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to set column address: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = mipi_dsi_dcs_set_page_address(dsi, 0, vdisplay - 1);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to set page address: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> > + mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x70);
> > + mipi_dsi_dcs_write_seq(dsi, 0xb9, 0x00, 0x60);
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> > + mipi_dsi_dcs_write_seq(dsi, 0xc5, 0x2e, 0x21);
> > + mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> > +
> > + ret = mipi_dsi_dcs_set_display_on(dsi);
> > + if (ret < 0) {
> > + dev_err(dev, "Failed to turn display on: %d\n", ret);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
>
> This is eerily similar to the sequence in panel-samsung-sofef00.c:
>
> static int sofef00_panel_on(struct sofef00_panel *ctx)
> {
> struct mipi_dsi_device *dsi = ctx->dsi;
> struct device *dev = &dsi->dev;
> int ret;
>
> dsi->mode_flags |= MIPI_DSI_MODE_LPM;
>
> ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> if (ret < 0) {
> dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> return ret;
> }
> usleep_range(10000, 11000);
>
> mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
>
> ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
> if (ret < 0) {
> dev_err(dev, "Failed to set tear on: %d\n", ret);
> return ret;
> }
>
> mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> mipi_dsi_dcs_write_seq(dsi, 0xf0, 0x5a, 0x5a);
> mipi_dsi_dcs_write_seq(dsi, 0xb0, 0x07);
> mipi_dsi_dcs_write_seq(dsi, 0xb6, 0x12);
> mipi_dsi_dcs_write_seq(dsi, 0xf0, 0xa5, 0xa5);
> mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x20);
> mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
>
> ret = mipi_dsi_dcs_set_display_on(dsi);
> if (ret < 0) {
> dev_err(dev, "Failed to set display on: %d\n", ret);
> return ret;
> }
>
> return 0;
> }
>
> Isn't this just the same display controller with a different configuration?
> Especially the sleep ranges are even the same.
>
> I almost feel like buying these phones just to pry them apart and put
> under a microscope to figure out what these displays actually contain.
In the second iteration this is going to be the souxp00 controller,
powering specifically the amb650wh01 panel on the Xperia 1 (griffin) and
amb650wh07 on the Xperia 1 II.
To answer your question more generically, open a few more of the Samsung
panels already in the tree and within this series. All their commands
look awkwardly similar. Some call the 0xf0, 0x5a, 0x5a sequence an MCS
password, others call it an MCS_LEVEL_2_KEY, and for our panels where we
have zero documentation and only a list of commands downstream we leave
them undocumented.
In an ideal world we have documentation and can accurately determine
what the commands mean (and if they're similar between DrIC revisions),
and instead describe the right parameters per-panel which are
generically converted to commands, allowing us to implement all these
lookalikes in a single driver.
But for now we might already create a step-up version of that by having
a "Samsung panel driver library" to deduplicate generic commands, which
drivers can freely call into? Or do you envision anything else here
considering that there is no spec to build on top of that guarantees our
observations?
On the other hand of the spectrum we currently have 4 downstream panels
for Sony devices that all declare to be using the sofef01 controller,
but with vastly different command sets. And even if we "accidentally"
send the wrong set for the wrong device, the panel works anyway with no
noticeable color shifts or otherwise...
- Marijn
On Wed, Jun 28, 2023 at 4:20 PM Marijn Suijten
<[email protected]> wrote:
> But for now we might already create a step-up version of that by having
> a "Samsung panel driver library" to deduplicate generic commands, which
> drivers can freely call into?
Yeah something like that is likely what we want.
> On the other hand of the spectrum we currently have 4 downstream panels
> for Sony devices that all declare to be using the sofef01 controller,
> but with vastly different command sets. And even if we "accidentally"
> send the wrong set for the wrong device, the panel works anyway with no
> noticeable color shifts or otherwise...
Yeah that is typically the case :/
We should try to group the similar code together and expect that sooner
or later we will figure out what display controller(s) it is and name it after
that rather than after the panel (which I define as the combination of
a display controller and an actual panel).
Yours,
Linus Walleij
Hi,
On Tue, May 30, 2023 at 03:36:04PM +0300, Dmitry Baryshkov wrote:
> On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
> > Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
> > > On Tue, 30 May 2023 at 10:24, Neil Armstrong
> > > <[email protected]> wrote:
> > > >
> > > > Hi Marijn, Dmitry, Caleb, Jessica,
> > > >
> > > > On 29/05/2023 23:11, Marijn Suijten wrote:
> > > > > On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> > > > > <snip>
> > > > > > > +?? if (ctx->dsi->dsc) {
> > > > > >
> > > > > > dsi->dsc is always set, thus this condition can be dropped.
> > > > >
> > > > > I want to leave room for possibly running the panel without DSC (at a
> > > > > lower resolution/refresh rate, or at higher power consumption if there
> > > > > is enough BW) by not assigning the pointer, if we get access to panel
> > > > > documentation: probably one of the magic commands sent in this driver
> > > > > controls it but we don't know which.
> > > >
> > > > I'd like to investigate if DSC should perhaps only be enabled if we
> > > > run non certain platforms/socs ?
> > > >
> > > > I mean, we don't know if the controller supports DSC and those
> > > > particular
> > > > DSC parameters so we should probably start adding something like :
> > > >
> > > > static drm_dsc_config dsc_params_qcom = {}
> > > >
> > > > static const struct of_device_id panel_of_dsc_params[] = {
> > > > ???????? { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
> > > > ???????? { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
> > > > ???????? { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
> > > > ???????? { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
> > > > };
> > >
> > > I think this would damage the reusability of the drivers. The panel
> > > driver does not actually care if the SoC is SM8350, sunxi-something or
> > > RCar.
> > > Instead it cares about host capabilities.
> > >
> > > I think instead we should extend mipi_dsi_host:
> > >
> > > #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
> > > #define MIPI_DSI_HOST_MODE_CMD? BIT(1)
> > > #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
> > > // FIXME: do we need to provide additional caps here ?
> > >
> > > #define MIPI_DSI_DSC_1_1 BIT(0)
> > > #define MIPI_DSI_DSC_1_2 BIT(1)
> > > #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
> > > #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
> > > #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
> > > // etc.
> > >
> > > struct mipi_dsi_host {
> > > ? // new fields only
> > > ?? unsigned long mode_flags;
> > > ?? unsigned long dsc_flags;
> > > };
> > >
> > > Then the panel driver can adapt itself to the host capabilities and
> > > (possibly) select one of the internally supported DSC profiles.
> > >
> >
> > I completely agree about extending mipi_dsi_host, other SoCs could reuse
> > that and
> > support for DSC panels would become a lot cleaner.
>
> Sounds good. I will wait for one or two more days (to get the possible
> feedback on fields/flags/etc) and post an RFC patch to dri-devel.
I just came across that discussion, and couldn't find those patches, did
you ever send them?
Either way, I'm not really sure it's a good idea to multiply the
capabilities flags of the DSI host, and we should just stick to the
spec. If the spec says that we have to support DSC while video is
output, then that's what the panels should expect.
If a host isn't able to provide that, it's a bug and we should fix the
controller driver instead of creating a workaround in the core for
broken drivers.
Another concern I have is that, those broken drivers are usually the
undocumented ones that already have trouble supporting the most trivial
setup. Creating more combinations both at the controller and panel level
will just make it harder for those drivers.
Maxime
On 05/07/2023 14:04, Maxime Ripard wrote:
> Hi,
>
> On Tue, May 30, 2023 at 03:36:04PM +0300, Dmitry Baryshkov wrote:
>> On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
>>> Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
>>>> On Tue, 30 May 2023 at 10:24, Neil Armstrong
>>>> <[email protected]> wrote:
>>>>>
>>>>> Hi Marijn, Dmitry, Caleb, Jessica,
>>>>>
>>>>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>>>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>>>>> <snip>
>>>>>>>> + if (ctx->dsi->dsc) {
>>>>>>>
>>>>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>>>>
>>>>>> I want to leave room for possibly running the panel without DSC (at a
>>>>>> lower resolution/refresh rate, or at higher power consumption if there
>>>>>> is enough BW) by not assigning the pointer, if we get access to panel
>>>>>> documentation: probably one of the magic commands sent in this driver
>>>>>> controls it but we don't know which.
>>>>>
>>>>> I'd like to investigate if DSC should perhaps only be enabled if we
>>>>> run non certain platforms/socs ?
>>>>>
>>>>> I mean, we don't know if the controller supports DSC and those
>>>>> particular
>>>>> DSC parameters so we should probably start adding something like :
>>>>>
>>>>> static drm_dsc_config dsc_params_qcom = {}
>>>>>
>>>>> static const struct of_device_id panel_of_dsc_params[] = {
>>>>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>>>>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>>>>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>>>>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>>>>> };
>>>>
>>>> I think this would damage the reusability of the drivers. The panel
>>>> driver does not actually care if the SoC is SM8350, sunxi-something or
>>>> RCar.
>>>> Instead it cares about host capabilities.
>>>>
>>>> I think instead we should extend mipi_dsi_host:
>>>>
>>>> #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
>>>> #define MIPI_DSI_HOST_MODE_CMD BIT(1)
>>>> #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
>>>> // FIXME: do we need to provide additional caps here ?
>>>>
>>>> #define MIPI_DSI_DSC_1_1 BIT(0)
>>>> #define MIPI_DSI_DSC_1_2 BIT(1)
>>>> #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
>>>> #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
>>>> #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
>>>> // etc.
>>>>
>>>> struct mipi_dsi_host {
>>>> // new fields only
>>>> unsigned long mode_flags;
>>>> unsigned long dsc_flags;
>>>> };
>>>>
>>>> Then the panel driver can adapt itself to the host capabilities and
>>>> (possibly) select one of the internally supported DSC profiles.
>>>>
>>>
>>> I completely agree about extending mipi_dsi_host, other SoCs could reuse
>>> that and
>>> support for DSC panels would become a lot cleaner.
>>
>> Sounds good. I will wait for one or two more days (to get the possible
>> feedback on fields/flags/etc) and post an RFC patch to dri-devel.
>
> I just came across that discussion, and couldn't find those patches, did
> you ever send them?
>
> Either way, I'm not really sure it's a good idea to multiply the
> capabilities flags of the DSI host, and we should just stick to the
> spec. If the spec says that we have to support DSC while video is
> output, then that's what the panels should expect.
Except some panels supports DSC & non-DSC, Video and Command mode, and
all that is runtime configurable. How do you handle that ?
>
> If a host isn't able to provide that, it's a bug and we should fix the
> controller driver instead of creating a workaround in the core for
> broken drivers.
>
> Another concern I have is that, those broken drivers are usually the
> undocumented ones that already have trouble supporting the most trivial
> setup. Creating more combinations both at the controller and panel level
> will just make it harder for those drivers.
>
> Maxime
On Wed, Jul 05, 2023 at 03:05:33PM +0200, Neil Armstrong wrote:
> On 05/07/2023 14:04, Maxime Ripard wrote:
> > Hi,
> >
> > On Tue, May 30, 2023 at 03:36:04PM +0300, Dmitry Baryshkov wrote:
> > > On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
> > > > Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
> > > > > On Tue, 30 May 2023 at 10:24, Neil Armstrong
> > > > > <[email protected]> wrote:
> > > > > >
> > > > > > Hi Marijn, Dmitry, Caleb, Jessica,
> > > > > >
> > > > > > On 29/05/2023 23:11, Marijn Suijten wrote:
> > > > > > > On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
> > > > > > > <snip>
> > > > > > > > > +?? if (ctx->dsi->dsc) {
> > > > > > > >
> > > > > > > > dsi->dsc is always set, thus this condition can be dropped.
> > > > > > >
> > > > > > > I want to leave room for possibly running the panel without DSC (at a
> > > > > > > lower resolution/refresh rate, or at higher power consumption if there
> > > > > > > is enough BW) by not assigning the pointer, if we get access to panel
> > > > > > > documentation: probably one of the magic commands sent in this driver
> > > > > > > controls it but we don't know which.
> > > > > >
> > > > > > I'd like to investigate if DSC should perhaps only be enabled if we
> > > > > > run non certain platforms/socs ?
> > > > > >
> > > > > > I mean, we don't know if the controller supports DSC and those
> > > > > > particular
> > > > > > DSC parameters so we should probably start adding something like :
> > > > > >
> > > > > > static drm_dsc_config dsc_params_qcom = {}
> > > > > >
> > > > > > static const struct of_device_id panel_of_dsc_params[] = {
> > > > > > ???????? { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
> > > > > > ???????? { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
> > > > > > ???????? { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
> > > > > > ???????? { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
> > > > > > };
> > > > >
> > > > > I think this would damage the reusability of the drivers. The panel
> > > > > driver does not actually care if the SoC is SM8350, sunxi-something or
> > > > > RCar.
> > > > > Instead it cares about host capabilities.
> > > > >
> > > > > I think instead we should extend mipi_dsi_host:
> > > > >
> > > > > #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
> > > > > #define MIPI_DSI_HOST_MODE_CMD? BIT(1)
> > > > > #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
> > > > > // FIXME: do we need to provide additional caps here ?
> > > > >
> > > > > #define MIPI_DSI_DSC_1_1 BIT(0)
> > > > > #define MIPI_DSI_DSC_1_2 BIT(1)
> > > > > #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
> > > > > #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
> > > > > #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
> > > > > // etc.
> > > > >
> > > > > struct mipi_dsi_host {
> > > > > ? // new fields only
> > > > > ?? unsigned long mode_flags;
> > > > > ?? unsigned long dsc_flags;
> > > > > };
> > > > >
> > > > > Then the panel driver can adapt itself to the host capabilities and
> > > > > (possibly) select one of the internally supported DSC profiles.
> > > > >
> > > >
> > > > I completely agree about extending mipi_dsi_host, other SoCs could reuse
> > > > that and
> > > > support for DSC panels would become a lot cleaner.
> > >
> > > Sounds good. I will wait for one or two more days (to get the possible
> > > feedback on fields/flags/etc) and post an RFC patch to dri-devel.
> >
> > I just came across that discussion, and couldn't find those patches, did
> > you ever send them?
> >
> > Either way, I'm not really sure it's a good idea to multiply the
> > capabilities flags of the DSI host, and we should just stick to the
> > spec. If the spec says that we have to support DSC while video is
> > output, then that's what the panels should expect.
>
> Except some panels supports DSC & non-DSC, Video and Command mode, and
> all that is runtime configurable. How do you handle that ?
In this case, most of the constraints are going to be on the encoder
still so it should be the one driving it. The panel will only care about
which mode has been selected, but it shouldn't be the one driving it,
and thus we still don't really need to expose the host capabilities.
This is very much like HDMI: the encoder knows what the monitor is
capable of, will take a decision based on its capabilities and the
monitor's and will then let the monitor know. But the monitor never
knows what the encoder is truly capable of, nor will it enforce
something.
Maxime
On 05/07/2023 16:29, Maxime Ripard wrote:
> On Wed, Jul 05, 2023 at 03:05:33PM +0200, Neil Armstrong wrote:
>> On 05/07/2023 14:04, Maxime Ripard wrote:
>>> Hi,
>>>
>>> On Tue, May 30, 2023 at 03:36:04PM +0300, Dmitry Baryshkov wrote:
>>>> On 30/05/2023 15:15, AngeloGioacchino Del Regno wrote:
>>>>> Il 30/05/23 13:44, Dmitry Baryshkov ha scritto:
>>>>>> On Tue, 30 May 2023 at 10:24, Neil Armstrong
>>>>>> <[email protected]> wrote:
>>>>>>>
>>>>>>> Hi Marijn, Dmitry, Caleb, Jessica,
>>>>>>>
>>>>>>> On 29/05/2023 23:11, Marijn Suijten wrote:
>>>>>>>> On 2023-05-22 04:16:20, Dmitry Baryshkov wrote:
>>>>>>>> <snip>
>>>>>>>>>> + if (ctx->dsi->dsc) {
>>>>>>>>>
>>>>>>>>> dsi->dsc is always set, thus this condition can be dropped.
>>>>>>>>
>>>>>>>> I want to leave room for possibly running the panel without DSC (at a
>>>>>>>> lower resolution/refresh rate, or at higher power consumption if there
>>>>>>>> is enough BW) by not assigning the pointer, if we get access to panel
>>>>>>>> documentation: probably one of the magic commands sent in this driver
>>>>>>>> controls it but we don't know which.
>>>>>>>
>>>>>>> I'd like to investigate if DSC should perhaps only be enabled if we
>>>>>>> run non certain platforms/socs ?
>>>>>>>
>>>>>>> I mean, we don't know if the controller supports DSC and those
>>>>>>> particular
>>>>>>> DSC parameters so we should probably start adding something like :
>>>>>>>
>>>>>>> static drm_dsc_config dsc_params_qcom = {}
>>>>>>>
>>>>>>> static const struct of_device_id panel_of_dsc_params[] = {
>>>>>>> { .compatible = "qcom,sm8150", , .data = &dsc_params_qcom },
>>>>>>> { .compatible = "qcom,sm8250", , .data = &dsc_params_qcom },
>>>>>>> { .compatible = "qcom,sm8350", , .data = &dsc_params_qcom },
>>>>>>> { .compatible = "qcom,sm8450", , .data = &dsc_params_qcom },
>>>>>>> };
>>>>>>
>>>>>> I think this would damage the reusability of the drivers. The panel
>>>>>> driver does not actually care if the SoC is SM8350, sunxi-something or
>>>>>> RCar.
>>>>>> Instead it cares about host capabilities.
>>>>>>
>>>>>> I think instead we should extend mipi_dsi_host:
>>>>>>
>>>>>> #define MIPI_DSI_HOST_MODE_VIDEO BIT(0)
>>>>>> #define MIPI_DSI_HOST_MODE_CMD BIT(1)
>>>>>> #define MIPI_DSI_HOST_VIDEO_SUPPORTS_COMMANDS BIT(2)
>>>>>> // FIXME: do we need to provide additional caps here ?
>>>>>>
>>>>>> #define MIPI_DSI_DSC_1_1 BIT(0)
>>>>>> #define MIPI_DSI_DSC_1_2 BIT(1)
>>>>>> #define MIPI_DSI_DSC_NATIVE_422 BIT(2)
>>>>>> #define MIPI_DSI_DSC_NATIVE_420 BIT(3)
>>>>>> #define MIPI_DSI_DSC_FRAC_BPP BIT(4)
>>>>>> // etc.
>>>>>>
>>>>>> struct mipi_dsi_host {
>>>>>> // new fields only
>>>>>> unsigned long mode_flags;
>>>>>> unsigned long dsc_flags;
>>>>>> };
>>>>>>
>>>>>> Then the panel driver can adapt itself to the host capabilities and
>>>>>> (possibly) select one of the internally supported DSC profiles.
>>>>>>
>>>>>
>>>>> I completely agree about extending mipi_dsi_host, other SoCs could reuse
>>>>> that and
>>>>> support for DSC panels would become a lot cleaner.
>>>>
>>>> Sounds good. I will wait for one or two more days (to get the possible
>>>> feedback on fields/flags/etc) and post an RFC patch to dri-devel.
>>>
>>> I just came across that discussion, and couldn't find those patches, did
>>> you ever send them?
No, I got sidetracked by other issues.
>>>
>>> Either way, I'm not really sure it's a good idea to multiply the
>>> capabilities flags of the DSI host, and we should just stick to the
>>> spec. If the spec says that we have to support DSC while video is
>>> output, then that's what the panels should expect.
>>
>> Except some panels supports DSC & non-DSC, Video and Command mode, and
>> all that is runtime configurable. How do you handle that ?
>
> In this case, most of the constraints are going to be on the encoder
> still so it should be the one driving it. The panel will only care about
> which mode has been selected, but it shouldn't be the one driving it,
> and thus we still don't really need to expose the host capabilities.
This is an interesting perspective. This means that we can and actually
have to extend the drm_display_mode with the DSI data and compression
information.
For example, the panel that supports all four types for the 1080p should
export several modes:
1920x1080-command
1920x1080-command-DSC
1920x1080-video
1920x1080-video-DSC
where video/command and DSC are some kinds of flags and/or information
in the drm_display_mode? Ideally DSC also has several sub-flags, which
denote what kind of configuration is supported by the DSC sink (e.g.
bpp, yuv, etc).
Another option would be to get this handled via the bus format
negotiation, but that sounds like worse idea to me.
> This is very much like HDMI: the encoder knows what the monitor is
> capable of, will take a decision based on its capabilities and the
> monitor's and will then let the monitor know. But the monitor never
> knows what the encoder is truly capable of, nor will it enforce
> something.
>
> Maxime
--
With best wishes
Dmitry
On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
> > > >
> > > > Either way, I'm not really sure it's a good idea to multiply the
> > > > capabilities flags of the DSI host, and we should just stick to the
> > > > spec. If the spec says that we have to support DSC while video is
> > > > output, then that's what the panels should expect.
> > >
> > > Except some panels supports DSC & non-DSC, Video and Command mode, and
> > > all that is runtime configurable. How do you handle that ?
> >
> > In this case, most of the constraints are going to be on the encoder
> > still so it should be the one driving it. The panel will only care about
> > which mode has been selected, but it shouldn't be the one driving it,
> > and thus we still don't really need to expose the host capabilities.
>
> This is an interesting perspective. This means that we can and actually have
> to extend the drm_display_mode with the DSI data and compression
> information.
I wouldn't extend drm_display_mode, but extending one of the state
structures definitely.
We already have some extra variables in drm_connector_state for HDMI,
I don't think it would be a big deal to add a few for MIPI-DSI.
We also floated the idea for a while to create bus-specific states, with
helpers to match. Maybe it would be a good occasion to start doing it?
> For example, the panel that supports all four types for the 1080p should
> export several modes:
>
> 1920x1080-command
> 1920x1080-command-DSC
> 1920x1080-video
> 1920x1080-video-DSC
>
> where video/command and DSC are some kinds of flags and/or information in
> the drm_display_mode? Ideally DSC also has several sub-flags, which denote
> what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
> etc).
So we have two things to do, right? We need to expose what the panel can
take (ie, EDID for HDMI), and then we need to tell it what we picked
(infoframes).
We already express the former in mipi_dsi_device, so we could extend the
flags stored there.
And then, we need to tie what the DSI host chose to a given atomic state
so the panel knows what was picked and how it should set everything up.
> Another option would be to get this handled via the bus format negotiation,
> but that sounds like worse idea to me.
Yeah, I'm not really fond of the format negociation stuff either.
Maxime
On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
>
> On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
> > > > >
> > > > > Either way, I'm not really sure it's a good idea to multiply the
> > > > > capabilities flags of the DSI host, and we should just stick to the
> > > > > spec. If the spec says that we have to support DSC while video is
> > > > > output, then that's what the panels should expect.
> > > >
> > > > Except some panels supports DSC & non-DSC, Video and Command mode, and
> > > > all that is runtime configurable. How do you handle that ?
> > >
> > > In this case, most of the constraints are going to be on the encoder
> > > still so it should be the one driving it. The panel will only care about
> > > which mode has been selected, but it shouldn't be the one driving it,
> > > and thus we still don't really need to expose the host capabilities.
> >
> > This is an interesting perspective. This means that we can and actually have
> > to extend the drm_display_mode with the DSI data and compression
> > information.
>
> I wouldn't extend drm_display_mode, but extending one of the state
> structures definitely.
>
> We already have some extra variables in drm_connector_state for HDMI,
> I don't think it would be a big deal to add a few for MIPI-DSI.
>
> We also floated the idea for a while to create bus-specific states, with
> helpers to match. Maybe it would be a good occasion to start doing it?
>
> > For example, the panel that supports all four types for the 1080p should
> > export several modes:
> >
> > 1920x1080-command
> > 1920x1080-command-DSC
> > 1920x1080-video
> > 1920x1080-video-DSC
> >
> > where video/command and DSC are some kinds of flags and/or information in
> > the drm_display_mode? Ideally DSC also has several sub-flags, which denote
> > what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
> > etc).
>
> So we have two things to do, right? We need to expose what the panel can
> take (ie, EDID for HDMI), and then we need to tell it what we picked
> (infoframes).
>
> We already express the former in mipi_dsi_device, so we could extend the
> flags stored there.
>
> And then, we need to tie what the DSI host chose to a given atomic state
> so the panel knows what was picked and how it should set everything up.
This is definitely something we need. Marijn has been stuck with the
panels that support different models ([1]).
Would you prefer a separate API for this kind of information or
abusing atomic_enable() is fine from your point of view?
My vote would be for going with existing operations, with the slight
fear of ending up with another DSI-specific hack (like
pre_enable_prev_first).
>
> > Another option would be to get this handled via the bus format negotiation,
> > but that sounds like worse idea to me.
>
> Yeah, I'm not really fond of the format negociation stuff either.
[1] https://lore.kernel.org/linux-arm-msm/[email protected]/
--
With best wishes
Dmitry
On 05/07/2023 16:24, Maxime Ripard wrote:
> On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
>>>>>
>>>>> Either way, I'm not really sure it's a good idea to multiply the
>>>>> capabilities flags of the DSI host, and we should just stick to the
>>>>> spec. If the spec says that we have to support DSC while video is
>>>>> output, then that's what the panels should expect.
>>>>
>>>> Except some panels supports DSC & non-DSC, Video and Command mode, and
>>>> all that is runtime configurable. How do you handle that ?
>>>
>>> In this case, most of the constraints are going to be on the encoder
>>> still so it should be the one driving it. The panel will only care about
>>> which mode has been selected, but it shouldn't be the one driving it,
>>> and thus we still don't really need to expose the host capabilities.
>>
>> This is an interesting perspective. This means that we can and actually have
>> to extend the drm_display_mode with the DSI data and compression
>> information.
>
> I wouldn't extend drm_display_mode, but extending one of the state
> structures definitely.
>
> We already have some extra variables in drm_connector_state for HDMI,
> I don't think it would be a big deal to add a few for MIPI-DSI.
>
> We also floated the idea for a while to create bus-specific states, with
> helpers to match. Maybe it would be a good occasion to start doing it?
>
>> For example, the panel that supports all four types for the 1080p should
>> export several modes:
>>
>> 1920x1080-command
>> 1920x1080-command-DSC
>> 1920x1080-video
>> 1920x1080-video-DSC
>>
>> where video/command and DSC are some kinds of flags and/or information in
>> the drm_display_mode? Ideally DSC also has several sub-flags, which denote
>> what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
>> etc).
>
> So we have two things to do, right? We need to expose what the panel can
> take (ie, EDID for HDMI), and then we need to tell it what we picked
> (infoframes).
>
> We already express the former in mipi_dsi_device, so we could extend the
> flags stored there.
>
> And then, we need to tie what the DSI host chose to a given atomic state
> so the panel knows what was picked and how it should set everything up.
Yep this looks like a good plan
Neil
>
>> Another option would be to get this handled via the bus format negotiation,
>> but that sounds like worse idea to me.
>
> Yeah, I'm not really fond of the format negociation stuff either.
>
> Maxime
On Wed, Jul 05, 2023 at 06:20:13PM +0300, Dmitry Baryshkov wrote:
> On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
> >
> > On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
> > > > > >
> > > > > > Either way, I'm not really sure it's a good idea to multiply the
> > > > > > capabilities flags of the DSI host, and we should just stick to the
> > > > > > spec. If the spec says that we have to support DSC while video is
> > > > > > output, then that's what the panels should expect.
> > > > >
> > > > > Except some panels supports DSC & non-DSC, Video and Command mode, and
> > > > > all that is runtime configurable. How do you handle that ?
> > > >
> > > > In this case, most of the constraints are going to be on the encoder
> > > > still so it should be the one driving it. The panel will only care about
> > > > which mode has been selected, but it shouldn't be the one driving it,
> > > > and thus we still don't really need to expose the host capabilities.
> > >
> > > This is an interesting perspective. This means that we can and actually have
> > > to extend the drm_display_mode with the DSI data and compression
> > > information.
> >
> > I wouldn't extend drm_display_mode, but extending one of the state
> > structures definitely.
> >
> > We already have some extra variables in drm_connector_state for HDMI,
> > I don't think it would be a big deal to add a few for MIPI-DSI.
> >
> > We also floated the idea for a while to create bus-specific states, with
> > helpers to match. Maybe it would be a good occasion to start doing it?
> >
> > > For example, the panel that supports all four types for the 1080p should
> > > export several modes:
> > >
> > > 1920x1080-command
> > > 1920x1080-command-DSC
> > > 1920x1080-video
> > > 1920x1080-video-DSC
> > >
> > > where video/command and DSC are some kinds of flags and/or information in
> > > the drm_display_mode? Ideally DSC also has several sub-flags, which denote
> > > what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
> > > etc).
> >
> > So we have two things to do, right? We need to expose what the panel can
> > take (ie, EDID for HDMI), and then we need to tell it what we picked
> > (infoframes).
> >
> > We already express the former in mipi_dsi_device, so we could extend the
> > flags stored there.
> >
> > And then, we need to tie what the DSI host chose to a given atomic state
> > so the panel knows what was picked and how it should set everything up.
>
> This is definitely something we need. Marijn has been stuck with the
> panels that support different models ([1]).
>
> Would you prefer a separate API for this kind of information or
> abusing atomic_enable() is fine from your point of view?
>
> My vote would be for going with existing operations, with the slight
> fear of ending up with another DSI-specific hack (like
> pre_enable_prev_first).
I don't think we can get away without getting access to the atomic_state
from the panel at least.
Choosing one setup over another is likely going to depend on the mode,
and that's only available in the state.
We don't have to go the whole way though and create the sub-classes of
drm_connector_state, but I think we should at least provide it to the
panel.
What do you think of creating a new set of atomic_* callbacks for
panels, call that new set of functions from msm and start from there?
Maxime
On 05/07/2023 19:53, Maxime Ripard wrote:
> On Wed, Jul 05, 2023 at 06:20:13PM +0300, Dmitry Baryshkov wrote:
>> On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
>>>
>>> On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
>>>>>>>
>>>>>>> Either way, I'm not really sure it's a good idea to multiply the
>>>>>>> capabilities flags of the DSI host, and we should just stick to the
>>>>>>> spec. If the spec says that we have to support DSC while video is
>>>>>>> output, then that's what the panels should expect.
>>>>>>
>>>>>> Except some panels supports DSC & non-DSC, Video and Command mode, and
>>>>>> all that is runtime configurable. How do you handle that ?
>>>>>
>>>>> In this case, most of the constraints are going to be on the encoder
>>>>> still so it should be the one driving it. The panel will only care about
>>>>> which mode has been selected, but it shouldn't be the one driving it,
>>>>> and thus we still don't really need to expose the host capabilities.
>>>>
>>>> This is an interesting perspective. This means that we can and actually have
>>>> to extend the drm_display_mode with the DSI data and compression
>>>> information.
>>>
>>> I wouldn't extend drm_display_mode, but extending one of the state
>>> structures definitely.
>>>
>>> We already have some extra variables in drm_connector_state for HDMI,
>>> I don't think it would be a big deal to add a few for MIPI-DSI.
>>>
>>> We also floated the idea for a while to create bus-specific states, with
>>> helpers to match. Maybe it would be a good occasion to start doing it?
>>>
>>>> For example, the panel that supports all four types for the 1080p should
>>>> export several modes:
>>>>
>>>> 1920x1080-command
>>>> 1920x1080-command-DSC
>>>> 1920x1080-video
>>>> 1920x1080-video-DSC
>>>>
>>>> where video/command and DSC are some kinds of flags and/or information in
>>>> the drm_display_mode? Ideally DSC also has several sub-flags, which denote
>>>> what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
>>>> etc).
>>>
>>> So we have two things to do, right? We need to expose what the panel can
>>> take (ie, EDID for HDMI), and then we need to tell it what we picked
>>> (infoframes).
>>>
>>> We already express the former in mipi_dsi_device, so we could extend the
>>> flags stored there.
>>>
>>> And then, we need to tie what the DSI host chose to a given atomic state
>>> so the panel knows what was picked and how it should set everything up.
>>
>> This is definitely something we need. Marijn has been stuck with the
>> panels that support different models ([1]).
>>
>> Would you prefer a separate API for this kind of information or
>> abusing atomic_enable() is fine from your point of view?
>>
>> My vote would be for going with existing operations, with the slight
>> fear of ending up with another DSI-specific hack (like
>> pre_enable_prev_first).
>
> I don't think we can get away without getting access to the atomic_state
> from the panel at least.
>
> Choosing one setup over another is likely going to depend on the mode,
> and that's only available in the state.
>
> We don't have to go the whole way though and create the sub-classes of
> drm_connector_state, but I think we should at least provide it to the
> panel.
>
> What do you think of creating a new set of atomic_* callbacks for
> panels, call that new set of functions from msm and start from there?
We are (somewhat) bound by the panel_bridge, but yeah, it seems possible.
--
With best wishes
Dmitry
On Wed, Jul 05, 2023 at 11:09:40PM +0300, Dmitry Baryshkov wrote:
> On 05/07/2023 19:53, Maxime Ripard wrote:
> > On Wed, Jul 05, 2023 at 06:20:13PM +0300, Dmitry Baryshkov wrote:
> > > On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
> > > >
> > > > On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
> > > > > > > >
> > > > > > > > Either way, I'm not really sure it's a good idea to multiply the
> > > > > > > > capabilities flags of the DSI host, and we should just stick to the
> > > > > > > > spec. If the spec says that we have to support DSC while video is
> > > > > > > > output, then that's what the panels should expect.
> > > > > > >
> > > > > > > Except some panels supports DSC & non-DSC, Video and Command mode, and
> > > > > > > all that is runtime configurable. How do you handle that ?
> > > > > >
> > > > > > In this case, most of the constraints are going to be on the encoder
> > > > > > still so it should be the one driving it. The panel will only care about
> > > > > > which mode has been selected, but it shouldn't be the one driving it,
> > > > > > and thus we still don't really need to expose the host capabilities.
> > > > >
> > > > > This is an interesting perspective. This means that we can and actually have
> > > > > to extend the drm_display_mode with the DSI data and compression
> > > > > information.
> > > >
> > > > I wouldn't extend drm_display_mode, but extending one of the state
> > > > structures definitely.
> > > >
> > > > We already have some extra variables in drm_connector_state for HDMI,
> > > > I don't think it would be a big deal to add a few for MIPI-DSI.
> > > >
> > > > We also floated the idea for a while to create bus-specific states, with
> > > > helpers to match. Maybe it would be a good occasion to start doing it?
> > > >
> > > > > For example, the panel that supports all four types for the 1080p should
> > > > > export several modes:
> > > > >
> > > > > 1920x1080-command
> > > > > 1920x1080-command-DSC
> > > > > 1920x1080-video
> > > > > 1920x1080-video-DSC
> > > > >
> > > > > where video/command and DSC are some kinds of flags and/or information in
> > > > > the drm_display_mode? Ideally DSC also has several sub-flags, which denote
> > > > > what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
> > > > > etc).
> > > >
> > > > So we have two things to do, right? We need to expose what the panel can
> > > > take (ie, EDID for HDMI), and then we need to tell it what we picked
> > > > (infoframes).
> > > >
> > > > We already express the former in mipi_dsi_device, so we could extend the
> > > > flags stored there.
> > > >
> > > > And then, we need to tie what the DSI host chose to a given atomic state
> > > > so the panel knows what was picked and how it should set everything up.
> > >
> > > This is definitely something we need. Marijn has been stuck with the
> > > panels that support different models ([1]).
> > >
> > > Would you prefer a separate API for this kind of information or
> > > abusing atomic_enable() is fine from your point of view?
> > >
> > > My vote would be for going with existing operations, with the slight
> > > fear of ending up with another DSI-specific hack (like
> > > pre_enable_prev_first).
> >
> > I don't think we can get away without getting access to the atomic_state
> > from the panel at least.
> >
> > Choosing one setup over another is likely going to depend on the mode,
> > and that's only available in the state.
> >
> > We don't have to go the whole way though and create the sub-classes of
> > drm_connector_state, but I think we should at least provide it to the
> > panel.
> >
> > What do you think of creating a new set of atomic_* callbacks for
> > panels, call that new set of functions from msm and start from there?
>
> We are (somewhat) bound by the panel_bridge, but yeah, it seems possible.
Bridges have access to the atomic state already, so it's another place
to plumb this through but I guess it would still be doable?
Maxime
On 06/07/2023 09:24, Maxime Ripard wrote:
> On Wed, Jul 05, 2023 at 11:09:40PM +0300, Dmitry Baryshkov wrote:
>> On 05/07/2023 19:53, Maxime Ripard wrote:
>>> On Wed, Jul 05, 2023 at 06:20:13PM +0300, Dmitry Baryshkov wrote:
>>>> On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
>>>>>
>>>>> On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
>>>>>>>>>
>>>>>>>>> Either way, I'm not really sure it's a good idea to multiply the
>>>>>>>>> capabilities flags of the DSI host, and we should just stick to the
>>>>>>>>> spec. If the spec says that we have to support DSC while video is
>>>>>>>>> output, then that's what the panels should expect.
>>>>>>>>
>>>>>>>> Except some panels supports DSC & non-DSC, Video and Command mode, and
>>>>>>>> all that is runtime configurable. How do you handle that ?
>>>>>>>
>>>>>>> In this case, most of the constraints are going to be on the encoder
>>>>>>> still so it should be the one driving it. The panel will only care about
>>>>>>> which mode has been selected, but it shouldn't be the one driving it,
>>>>>>> and thus we still don't really need to expose the host capabilities.
>>>>>>
>>>>>> This is an interesting perspective. This means that we can and actually have
>>>>>> to extend the drm_display_mode with the DSI data and compression
>>>>>> information.
>>>>>
>>>>> I wouldn't extend drm_display_mode, but extending one of the state
>>>>> structures definitely.
>>>>>
>>>>> We already have some extra variables in drm_connector_state for HDMI,
>>>>> I don't think it would be a big deal to add a few for MIPI-DSI.
>>>>>
>>>>> We also floated the idea for a while to create bus-specific states, with
>>>>> helpers to match. Maybe it would be a good occasion to start doing it?
>>>>>
>>>>>> For example, the panel that supports all four types for the 1080p should
>>>>>> export several modes:
>>>>>>
>>>>>> 1920x1080-command
>>>>>> 1920x1080-command-DSC
>>>>>> 1920x1080-video
>>>>>> 1920x1080-video-DSC
>>>>>>
>>>>>> where video/command and DSC are some kinds of flags and/or information in
>>>>>> the drm_display_mode? Ideally DSC also has several sub-flags, which denote
>>>>>> what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
>>>>>> etc).
>>>>>
>>>>> So we have two things to do, right? We need to expose what the panel can
>>>>> take (ie, EDID for HDMI), and then we need to tell it what we picked
>>>>> (infoframes).
>>>>>
>>>>> We already express the former in mipi_dsi_device, so we could extend the
>>>>> flags stored there.
>>>>>
>>>>> And then, we need to tie what the DSI host chose to a given atomic state
>>>>> so the panel knows what was picked and how it should set everything up.
>>>>
>>>> This is definitely something we need. Marijn has been stuck with the
>>>> panels that support different models ([1]).
>>>>
>>>> Would you prefer a separate API for this kind of information or
>>>> abusing atomic_enable() is fine from your point of view?
>>>>
>>>> My vote would be for going with existing operations, with the slight
>>>> fear of ending up with another DSI-specific hack (like
>>>> pre_enable_prev_first).
>>>
>>> I don't think we can get away without getting access to the atomic_state
>>> from the panel at least.
>>>
>>> Choosing one setup over another is likely going to depend on the mode,
>>> and that's only available in the state.
>>>
>>> We don't have to go the whole way though and create the sub-classes of
>>> drm_connector_state, but I think we should at least provide it to the
>>> panel.
>>>
>>> What do you think of creating a new set of atomic_* callbacks for
>>> panels, call that new set of functions from msm and start from there?
>>
>> We are (somewhat) bound by the panel_bridge, but yeah, it seems possible.
>
> Bridges have access to the atomic state already, so it's another place
> to plumb this through but I guess it would still be doable?
It's definitely doable, but I fear we won't be able to test most of the
panel drivers, should we introduce a new atomic set of panel callbacks ?
Or shall be simply move the "new" panel driver supporting atomic to bridge
and only use panel_bridge for basic panels ?
Neil
>
> Maxime
On Thu, Jul 06, 2023 at 09:33:15AM +0200, Neil Armstrong wrote:
> On 06/07/2023 09:24, Maxime Ripard wrote:
> > On Wed, Jul 05, 2023 at 11:09:40PM +0300, Dmitry Baryshkov wrote:
> > > On 05/07/2023 19:53, Maxime Ripard wrote:
> > > > On Wed, Jul 05, 2023 at 06:20:13PM +0300, Dmitry Baryshkov wrote:
> > > > > On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
> > > > > >
> > > > > > On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
> > > > > > > > > >
> > > > > > > > > > Either way, I'm not really sure it's a good idea to multiply the
> > > > > > > > > > capabilities flags of the DSI host, and we should just stick to the
> > > > > > > > > > spec. If the spec says that we have to support DSC while video is
> > > > > > > > > > output, then that's what the panels should expect.
> > > > > > > > >
> > > > > > > > > Except some panels supports DSC & non-DSC, Video and Command mode, and
> > > > > > > > > all that is runtime configurable. How do you handle that ?
> > > > > > > >
> > > > > > > > In this case, most of the constraints are going to be on the encoder
> > > > > > > > still so it should be the one driving it. The panel will only care about
> > > > > > > > which mode has been selected, but it shouldn't be the one driving it,
> > > > > > > > and thus we still don't really need to expose the host capabilities.
> > > > > > >
> > > > > > > This is an interesting perspective. This means that we can and actually have
> > > > > > > to extend the drm_display_mode with the DSI data and compression
> > > > > > > information.
> > > > > >
> > > > > > I wouldn't extend drm_display_mode, but extending one of the state
> > > > > > structures definitely.
> > > > > >
> > > > > > We already have some extra variables in drm_connector_state for HDMI,
> > > > > > I don't think it would be a big deal to add a few for MIPI-DSI.
> > > > > >
> > > > > > We also floated the idea for a while to create bus-specific states, with
> > > > > > helpers to match. Maybe it would be a good occasion to start doing it?
> > > > > >
> > > > > > > For example, the panel that supports all four types for the 1080p should
> > > > > > > export several modes:
> > > > > > >
> > > > > > > 1920x1080-command
> > > > > > > 1920x1080-command-DSC
> > > > > > > 1920x1080-video
> > > > > > > 1920x1080-video-DSC
> > > > > > >
> > > > > > > where video/command and DSC are some kinds of flags and/or information in
> > > > > > > the drm_display_mode? Ideally DSC also has several sub-flags, which denote
> > > > > > > what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
> > > > > > > etc).
> > > > > >
> > > > > > So we have two things to do, right? We need to expose what the panel can
> > > > > > take (ie, EDID for HDMI), and then we need to tell it what we picked
> > > > > > (infoframes).
> > > > > >
> > > > > > We already express the former in mipi_dsi_device, so we could extend the
> > > > > > flags stored there.
> > > > > >
> > > > > > And then, we need to tie what the DSI host chose to a given atomic state
> > > > > > so the panel knows what was picked and how it should set everything up.
> > > > >
> > > > > This is definitely something we need. Marijn has been stuck with the
> > > > > panels that support different models ([1]).
> > > > >
> > > > > Would you prefer a separate API for this kind of information or
> > > > > abusing atomic_enable() is fine from your point of view?
> > > > >
> > > > > My vote would be for going with existing operations, with the slight
> > > > > fear of ending up with another DSI-specific hack (like
> > > > > pre_enable_prev_first).
> > > >
> > > > I don't think we can get away without getting access to the atomic_state
> > > > from the panel at least.
> > > >
> > > > Choosing one setup over another is likely going to depend on the mode,
> > > > and that's only available in the state.
> > > >
> > > > We don't have to go the whole way though and create the sub-classes of
> > > > drm_connector_state, but I think we should at least provide it to the
> > > > panel.
> > > >
> > > > What do you think of creating a new set of atomic_* callbacks for
> > > > panels, call that new set of functions from msm and start from there?
> > >
> > > We are (somewhat) bound by the panel_bridge, but yeah, it seems possible.
> >
> > Bridges have access to the atomic state already, so it's another place
> > to plumb this through but I guess it would still be doable?
>
> It's definitely doable, but I fear we won't be able to test most of the
> panel drivers, should we introduce a new atomic set of panel callbacks ?
That was my original intent yeah :)
Creating an atomic_enable/disable/ etc. and then switch
drm_panel_enable() to take the state (and fixing up all the callers), or
create a drm_panel_enable_atomic() function.
The latter is probably simpler, something like:
int drm_panel_enable_atomic(struct drm_panel *panel,
struct drm_atomic_state *state)
{
struct drm_panel_funcs *funcs = panel->funcs;
if (funcs->atomic_enable)
return funcs->atomic_enable(panel, state);
return funcs->enable(panel);
}
And we should probably mention that it supersedes/deprecates
drm_panel_enable.
We've switched most of the other atomic hooks to take the full
drm_atomic_state so I'd prefer to use it. However, for it to be somewhat
useful we'd need to have access to the connector assigned to that panel.
drm_panel doesn't store the drm_connector it's connected to at all, and
of_drm_find_panel() doesn't take it as an argument so we can't fill it
when we retrieve it either.
So I guess we can go for:
- Create a new set of atomic hooks
- Create a new set of functions to call those hooks, that we would
document as deprecating the former functions. Those functions would
take a pointer to the drm_connector_state of the drm_connector it's
connected to.
- We add a TODO item to add a pointer to the connector in drm_panel
- We add a TODO item that depend on the first one to switch the new
functions and hooks to drm_atomic_state
- We add a TODO item to convert callers of drm_panel_enable et al. to
our new functions.
It should work in all setups, paves a nice way forward and documents the
trade-offs we had to take and eventually address. And without creating a
dependency on 30+ patches series.
Does it sound like a plan?
> Or shall be simply move the "new" panel driver supporting atomic to bridge
> and only use panel_bridge for basic panels ?
I don't think we can expect panel_bridge to be used all the time any
time soon, so I'd rather avoid to rely on it.
Maxime
On 06/07/2023 09:59, Maxime Ripard wrote:
> On Thu, Jul 06, 2023 at 09:33:15AM +0200, Neil Armstrong wrote:
>> On 06/07/2023 09:24, Maxime Ripard wrote:
>>> On Wed, Jul 05, 2023 at 11:09:40PM +0300, Dmitry Baryshkov wrote:
>>>> On 05/07/2023 19:53, Maxime Ripard wrote:
>>>>> On Wed, Jul 05, 2023 at 06:20:13PM +0300, Dmitry Baryshkov wrote:
>>>>>> On Wed, 5 Jul 2023 at 17:24, Maxime Ripard <[email protected]> wrote:
>>>>>>>
>>>>>>> On Wed, Jul 05, 2023 at 04:37:57PM +0300, Dmitry Baryshkov wrote:
>>>>>>>>>>>
>>>>>>>>>>> Either way, I'm not really sure it's a good idea to multiply the
>>>>>>>>>>> capabilities flags of the DSI host, and we should just stick to the
>>>>>>>>>>> spec. If the spec says that we have to support DSC while video is
>>>>>>>>>>> output, then that's what the panels should expect.
>>>>>>>>>>
>>>>>>>>>> Except some panels supports DSC & non-DSC, Video and Command mode, and
>>>>>>>>>> all that is runtime configurable. How do you handle that ?
>>>>>>>>>
>>>>>>>>> In this case, most of the constraints are going to be on the encoder
>>>>>>>>> still so it should be the one driving it. The panel will only care about
>>>>>>>>> which mode has been selected, but it shouldn't be the one driving it,
>>>>>>>>> and thus we still don't really need to expose the host capabilities.
>>>>>>>>
>>>>>>>> This is an interesting perspective. This means that we can and actually have
>>>>>>>> to extend the drm_display_mode with the DSI data and compression
>>>>>>>> information.
>>>>>>>
>>>>>>> I wouldn't extend drm_display_mode, but extending one of the state
>>>>>>> structures definitely.
>>>>>>>
>>>>>>> We already have some extra variables in drm_connector_state for HDMI,
>>>>>>> I don't think it would be a big deal to add a few for MIPI-DSI.
>>>>>>>
>>>>>>> We also floated the idea for a while to create bus-specific states, with
>>>>>>> helpers to match. Maybe it would be a good occasion to start doing it?
>>>>>>>
>>>>>>>> For example, the panel that supports all four types for the 1080p should
>>>>>>>> export several modes:
>>>>>>>>
>>>>>>>> 1920x1080-command
>>>>>>>> 1920x1080-command-DSC
>>>>>>>> 1920x1080-video
>>>>>>>> 1920x1080-video-DSC
>>>>>>>>
>>>>>>>> where video/command and DSC are some kinds of flags and/or information in
>>>>>>>> the drm_display_mode? Ideally DSC also has several sub-flags, which denote
>>>>>>>> what kind of configuration is supported by the DSC sink (e.g. bpp, yuv,
>>>>>>>> etc).
>>>>>>>
>>>>>>> So we have two things to do, right? We need to expose what the panel can
>>>>>>> take (ie, EDID for HDMI), and then we need to tell it what we picked
>>>>>>> (infoframes).
>>>>>>>
>>>>>>> We already express the former in mipi_dsi_device, so we could extend the
>>>>>>> flags stored there.
>>>>>>>
>>>>>>> And then, we need to tie what the DSI host chose to a given atomic state
>>>>>>> so the panel knows what was picked and how it should set everything up.
>>>>>>
>>>>>> This is definitely something we need. Marijn has been stuck with the
>>>>>> panels that support different models ([1]).
>>>>>>
>>>>>> Would you prefer a separate API for this kind of information or
>>>>>> abusing atomic_enable() is fine from your point of view?
>>>>>>
>>>>>> My vote would be for going with existing operations, with the slight
>>>>>> fear of ending up with another DSI-specific hack (like
>>>>>> pre_enable_prev_first).
>>>>>
>>>>> I don't think we can get away without getting access to the atomic_state
>>>>> from the panel at least.
>>>>>
>>>>> Choosing one setup over another is likely going to depend on the mode,
>>>>> and that's only available in the state.
>>>>>
>>>>> We don't have to go the whole way though and create the sub-classes of
>>>>> drm_connector_state, but I think we should at least provide it to the
>>>>> panel.
>>>>>
>>>>> What do you think of creating a new set of atomic_* callbacks for
>>>>> panels, call that new set of functions from msm and start from there?
>>>>
>>>> We are (somewhat) bound by the panel_bridge, but yeah, it seems possible.
>>>
>>> Bridges have access to the atomic state already, so it's another place
>>> to plumb this through but I guess it would still be doable?
>>
>> It's definitely doable, but I fear we won't be able to test most of the
>> panel drivers, should we introduce a new atomic set of panel callbacks ?
>
> That was my original intent yeah :)
>
> Creating an atomic_enable/disable/ etc. and then switch
> drm_panel_enable() to take the state (and fixing up all the callers), or
> create a drm_panel_enable_atomic() function.
>
> The latter is probably simpler, something like:
>
> int drm_panel_enable_atomic(struct drm_panel *panel,
> struct drm_atomic_state *state)
> {
> struct drm_panel_funcs *funcs = panel->funcs;
>
> if (funcs->atomic_enable)
> return funcs->atomic_enable(panel, state);
>
> return funcs->enable(panel);
> }
>
> And we should probably mention that it supersedes/deprecates
> drm_panel_enable.
>
> We've switched most of the other atomic hooks to take the full
> drm_atomic_state so I'd prefer to use it. However, for it to be somewhat
> useful we'd need to have access to the connector assigned to that panel.
>
> drm_panel doesn't store the drm_connector it's connected to at all, and
> of_drm_find_panel() doesn't take it as an argument so we can't fill it
> when we retrieve it either.
>
> So I guess we can go for:
>
> - Create a new set of atomic hooks
>
> - Create a new set of functions to call those hooks, that we would
> document as deprecating the former functions. Those functions would
> take a pointer to the drm_connector_state of the drm_connector it's
> connected to.
>
> - We add a TODO item to add a pointer to the connector in drm_panel
>
> - We add a TODO item that depend on the first one to switch the new
> functions and hooks to drm_atomic_state
>
> - We add a TODO item to convert callers of drm_panel_enable et al. to
> our new functions.
>
> It should work in all setups, paves a nice way forward and documents the
> trade-offs we had to take and eventually address. And without creating a
> dependency on 30+ patches series.
>
> Does it sound like a plan?
Yep that looks a fine plan to start of
>
>> Or shall be simply move the "new" panel driver supporting atomic to bridge
>> and only use panel_bridge for basic panels ?
>
> I don't think we can expect panel_bridge to be used all the time any
> time soon, so I'd rather avoid to rely on it.
Ack
>
> Maxime