This Samsung SOFEF01-M Display-Driver-IC is used to drive 1080x2520@60Hz
command-mode DSI panels found in many Sony phones:
- Sony Xperia 5 (kumano bahamut): amb609tc01
- Sony Xperia 10 II (seine pdx201): ams597ut01
- Sony Xperia 10 III (lena pdx213): ams597ut04
- Sony Xperia 10 IV (murray pdx225): ams597ut05
- Sony Xperia 10 V (zambezi pdx235): ams605dk01
- Sony Xperia 10 VI (columbia pdx246): ams605dk01
The amb609tc01 and ams605dk01 come in slightly larger at 6.1" while the
others are 6.0".
A "fake" porch calculation is included to artificially bump the clock
rate necessary to account for "transfer overhead" (DSI packet headers)
since this is missing from the MSM DSI host driver; porches aren't
otherwise used on command-mode panels.
Signed-off-by: Marijn Suijten <marijn.suijten@somainline.org>
---
MAINTAINERS | 1 +
drivers/gpu/drm/panel/Kconfig | 18 +
drivers/gpu/drm/panel/Makefile | 1 +
drivers/gpu/drm/panel/panel-samsung-sofef01.c | 463 ++++++++++++++++++++++++++
4 files changed, 483 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 93468dde9df2..cd2c924749d3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8198,6 +8198,7 @@ DRM DRIVER FOR SAMSUNG SOFEF01 DDIC
M: Marijn Suijten <marijn.suijten@somainline.org>
S: Maintained
F: Documentation/devicetree/bindings/display/panel/samsung,sofef01-m.yaml
+F: drivers/gpu/drm/panel/panel-samsung-sofef01.c
DRM DRIVER FOR SHARP MEMORY LCD
M: Alex Lanzano <lanzano.alex@gmail.com>
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 10381291707e..2f06b48dfb89 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -935,6 +935,24 @@ config DRM_PANEL_SAMSUNG_SOFEF00
Samsung AMS628NW01 (found in OnePlus 6, 1080x2280@60Hz)
+config DRM_PANEL_SAMSUNG_SOFEF01
+ tristate "Samsung SOFEF01-M 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 6.0"/6.1"
+ AMOLED DSI command mode panels found in multiple Sony smartphones:
+ - Sony Xperia 5 (kumano bahamut): amb609tc01
+ - Sony Xperia 10 II (seine pdx201): ams597ut01
+ - Sony Xperia 10 III (lena pdx213): ams597ut04
+ - Sony Xperia 10 IV (murray pdx225): ams597ut05
+ - Sony Xperia 10 V (zambezi pdx235): ams605dk01
+ - Sony Xperia 10 VI (columbia pdx246): ams605dk01
+
+ This Display-IC features a fixed 1080x2520@60 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 186eb895af21..cbd9ef23b8d8 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -94,6 +94,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_S6E8AA5X01_AMS561RA01) += panel-samsung-s6e8aa5x01-ams561ra01.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_LQ079L1SX01) += panel-sharp-lq079l1sx01.o
obj-$(CONFIG_DRM_PANEL_SHARP_LQ101R1SX01) += panel-sharp-lq101r1sx01.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 000000000000..bb1cefd16fe9
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-samsung-sofef01.c
@@ -0,0 +1,463 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2023 Marijn Suijten <marijn.suijten@somainline.org>
+ *
+ * Based on the following Sony downstream DTS command sequences:
+ * - Xperia 5 (kumano bahamut): https://github.com/sonyxperiadev/kernel-copyleft/blob/55.2.A.4.xxx/arch/arm64/boot/dts/somc/dsi-panel-sofef01_m-fhd_plus.dtsi
+ * - Xperia 10 II (seine pdx201): https://github.com/sonyxperiadev/kernel-copyleft/blob/59.1.A.2.xxx/arch/arm64/boot/dts/somc/dsi-panel-sofef01_m-fhd_plus.dtsi
+ * - Xperia 10 III (lena pdx213): https://github.com/sonyxperiadev/kernel-copyleft-dts/blob/62.1.A.0.xxx/qcom/dsi-panel-pdx213-amoled-fhd-cmd.dtsi
+ * - Xperia 10 IV (murray pdx225): https://github.com/sonyxperiadev/kernel-copyleft-dts/blob/65.1.A.4.xxx/qcom/dsi-panel-samsung-amoled-fhd-cmd.dtsi
+ * - Xperia 10 V (zambezi pdx235): https://github.com/sonyxperiadev/kernel-copyleft-dts/blob/68.2.A.2.xxx/qcom/dsi-panel-samsung-amoled-fhd-cmd.dtsi
+ * - Xperia 10 VI (columbia pdx246): https://github.com/sonyxperiadev/kernel-copyleft-dts/blob/70.0.A.2.xxx/qcom/dsi-panel-samsung-amoled-fhd-cmd.dtsi
+ */
+
+#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>
+
+#define WRITE_CONTROL_DISPLAY_AOD_LOW BIT(0)
+#define WRITE_CONTROL_DISPLAY_AOD_ON BIT(1)
+#define WRITE_CONTROL_DISPLAY_DIMMING BIT(3)
+#define WRITE_CONTROL_DISPLAY_LOCAL_HBM BIT(4)
+#define WRITE_CONTROL_DISPLAY_BACKLIGHT BIT(5)
+#define WRITE_CONTROL_DISPLAY_HBM GENMASK(6, 7)
+
+/* Only used to send a few differentiating DCS between the two panel variants,
+ * without exactly knowing what they mean. These do not seem to be related to
+ * panel functionality nor have any visual impact, but they are sent anyway just
+ * in case.
+ */
+enum panel_type {
+ /* Sony Xperia 5 */
+ PANEL_TYPE_TC01,
+ /* Sony Xperia 10 II */
+ PANEL_TYPE_UT01,
+ /* Sony Xperia 10 III */
+ PANEL_TYPE_UT04,
+ /* Sony Xperia 10 IV */
+ PANEL_TYPE_UT05,
+ /* Sony Xperia 10 V and 10 VI */
+ PANEL_TYPE_DK01,
+};
+
+const struct regulator_bulk_data samsung_sofef01_m_supplies[] = {
+ { .supply = "vddio", /* 1.8 V */ },
+ { .supply = "vci", /* 3.0 V */ },
+};
+
+struct samsung_sofef01_m {
+ struct drm_panel panel;
+ struct mipi_dsi_device *dsi;
+ struct regulator_bulk_data *supplies;
+ struct gpio_desc *reset_gpio;
+ const struct drm_display_mode *mode;
+ enum panel_type panel_type;
+};
+
+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);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ usleep_range(10000, 11000);
+ gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+ usleep_range(10000, 11000);
+}
+
+static int samsung_sofef01_m_program(struct samsung_sofef01_m *ctx)
+{
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ dsi_ctx.dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+ mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx);
+ /* TC01 & UT01 require 10ms, UT04 11ms, and the rest 120ms */
+ mipi_dsi_msleep(&dsi_ctx, 120);
+
+ mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
+
+ if (ctx->panel_type == PANEL_TYPE_TC01 ||
+ ctx->panel_type == PANEL_TYPE_UT01 ||
+ ctx->panel_type == PANEL_TYPE_UT04) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xdf, 0x03);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe0, 0x01);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+ }
+
+ if (ctx->panel_type == PANEL_TYPE_UT04) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x00, 0x00, 0x02, 0x00, 0x1C, 0x1C,
+ 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x19);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0xa5, 0xa5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+ }
+
+ mipi_dsi_dcs_set_column_address_multi(&dsi_ctx, 0, 1080 - 1);
+ mipi_dsi_dcs_set_page_address_multi(&dsi_ctx, 0, 2520 - 1);
+
+ if (ctx->panel_type == PANEL_TYPE_UT05 || ctx->panel_type == PANEL_TYPE_DK01) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x27, 0xf2);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x80);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf7, 0x07);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ /* Downstream: ERR_FG Enable */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe5, 0x15);
+ if (ctx->panel_type == PANEL_TYPE_DK01)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x0f, 0x4c, 0x20);
+ else
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x04, 0x4c, 0x20);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x02, 0x8f);
+
+ if (ctx->panel_type == PANEL_TYPE_DK01)
+ /* Downstream Xperia 10 V: FLM1,FLM2 On */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x27, 0x25);
+ else if (0) /* TODO: Both use the DK01 panel */
+ /* Downstream Xperia 10 VI: FLM1 On, FLM2 On */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x27, 0x27);
+ else
+ /* Downsteam: FLM1 on, FLM2 off */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x27, 0x05);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x92, 0x63);
+ /* Downstream: dimming speed setting */
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x63, 0x05);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+ }
+
+ if (ctx->panel_type == PANEL_TYPE_UT05 || ctx->panel_type == PANEL_TYPE_DK01) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ WRITE_CONTROL_DISPLAY_BACKLIGHT
+ | WRITE_CONTROL_DISPLAY_DIMMING);
+ } else {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+ WRITE_CONTROL_DISPLAY_BACKLIGHT);
+ }
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x00);
+
+ if (ctx->panel_type != PANEL_TYPE_UT05 && ctx->panel_type != PANEL_TYPE_DK01) {
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+
+ if (ctx->panel_type == PANEL_TYPE_TC01 || ctx->panel_type == PANEL_TYPE_UT01)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x92, 0x29);
+ else if (ctx->panel_type == PANEL_TYPE_UT04)
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xbe, 0x92, 0x09);
+
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x06);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb6, 0x90);
+ mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
+ }
+
+ mipi_dsi_msleep(&dsi_ctx, 110);
+
+ return dsi_ctx.accum_err;
+}
+
+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;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(samsung_sofef01_m_supplies), ctx->supplies);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ samsung_sofef01_m_reset(ctx);
+
+ ret = samsung_sofef01_m_program(ctx);
+ if (ret < 0) {
+ dev_err(dev, "Failed to program panel: %d\n", ret);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(samsung_sofef01_m_supplies), ctx->supplies);
+ return ret;
+}
+
+static int samsung_sofef01_m_enable(struct drm_panel *panel)
+{
+ struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_set_display_on_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 16000, 17000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int samsung_sofef01_m_disable(struct drm_panel *panel)
+{
+ struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_set_display_off_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 120000, 121000);
+
+ return dsi_ctx.accum_err;
+}
+
+static int samsung_sofef01_m_unprepare(struct drm_panel *panel)
+{
+ struct samsung_sofef01_m *ctx = to_samsung_sofef01_m(panel);
+ struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi };
+
+ mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx);
+ mipi_dsi_usleep_range(&dsi_ctx, 100000, 101000);
+
+ ctx->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+ gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(samsung_sofef01_m_supplies), ctx->supplies);
+
+ return dsi_ctx.accum_err;
+}
+
+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,
+ .enable = samsung_sofef01_m_enable,
+ .disable = samsung_sofef01_m_disable,
+ .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);
+
+ return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness);
+}
+
+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;
+
+ ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness);
+
+ 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,
+};
+
+/*
+ * drm/msm's DSI code does not calculate transfer time but instead relies on
+ * fake porch values (which are not a thing in CMD mode) to represent the
+ * transfer time.
+ *
+ * Use the following expressions based on qcom,mdss-dsi-panel-clockrate from
+ * downstream DT to artificially bump the mode's clock that reflects the
+ * necessary transfer time / overhead.
+ */
+static const unsigned int dsi_lanes = 4;
+static const unsigned int bpp = 24;
+/* qcom,mdss-dsi-panel-clockrate from downstream DT */
+static const unsigned long bitclk_hz = 1132293600;
+static const unsigned long stable_clockrate = bitclk_hz * dsi_lanes / bpp;
+static const unsigned long fake_porch = stable_clockrate / (2520 * 60) - 1080;
+
+/* 61x142mm variant, Sony Xperia 5 */
+static const struct drm_display_mode samsung_sofef01_m_61_142_mode = {
+ .clock = (1080 + fake_porch) * 2520 * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + fake_porch,
+ .hsync_end = 1080 + fake_porch,
+ .htotal = 1080 + fake_porch,
+ .vdisplay = 2520,
+ .vsync_start = 2520,
+ .vsync_end = 2520,
+ .vtotal = 2520,
+ .width_mm = 61,
+ .height_mm = 142,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+/* 61x141mm variant, Sony Xperia 10 V and 10 VI */
+static const struct drm_display_mode samsung_sofef01_m_61_141_mode = {
+ .clock = (1080 + fake_porch) * 2520 * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + fake_porch,
+ .hsync_end = 1080 + fake_porch,
+ .htotal = 1080 + fake_porch,
+ .vdisplay = 2520,
+ .vsync_start = 2520,
+ .vsync_end = 2520,
+ .vtotal = 2520,
+ .width_mm = 61,
+ .height_mm = 141,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+/* 60x139mm variant, Sony Xperia 10 II, 10 III and 10 IV */
+static const struct drm_display_mode samsung_sofef01_m_60_139_mode = {
+ .clock = (1080 + fake_porch) * 2520 * 60 / 1000,
+ .hdisplay = 1080,
+ .hsync_start = 1080 + fake_porch,
+ .hsync_end = 1080 + fake_porch,
+ .htotal = 1080 + fake_porch,
+ .vdisplay = 2520,
+ .vsync_start = 2520,
+ .vsync_end = 2520,
+ .vtotal = 2520,
+ .width_mm = 60,
+ .height_mm = 139,
+ .type = DRM_MODE_TYPE_DRIVER,
+};
+
+static int samsung_sofef01_m_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 samsung_sofef01_m *ctx;
+ int ret;
+
+ ctx = devm_drm_panel_alloc(dev, struct samsung_sofef01_m, panel,
+ &samsung_sofef01_m_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+ if (IS_ERR(ctx))
+ return PTR_ERR(ctx);
+
+ ret = devm_regulator_bulk_get_const(
+ dev,
+ ARRAY_SIZE(samsung_sofef01_m_supplies),
+ samsung_sofef01_m_supplies,
+ &ctx->supplies);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
+ ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
+ "Failed to get reset-gpios\n");
+
+ ctx->dsi = dsi;
+ ctx->panel_type = (enum panel_type)of_device_get_match_data(dev);
+ if (ctx->panel_type == PANEL_TYPE_TC01)
+ ctx->mode = &samsung_sofef01_m_61_142_mode;
+ else if (ctx->panel_type == PANEL_TYPE_DK01)
+ ctx->mode = &samsung_sofef01_m_61_141_mode;
+ else
+ ctx->mode = &samsung_sofef01_m_60_139_mode;
+ mipi_dsi_set_drvdata(dsi, ctx);
+
+ dsi->lanes = 4;
+ dsi->format = MIPI_DSI_FMT_RGB888;
+ dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+ ctx->panel.prepare_prev_first = true;
+
+ ctx->panel.backlight = devm_backlight_device_register(
+ dev, dev_name(dev), dev, dsi,
+ &samsung_sofef01_m_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);
+
+ 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);
+}
+
+static const struct of_device_id samsung_sofef01_m_of_match[] = {
+ { .compatible = "samsung,sofef01-m-amb609tc01",
+ .data = (void *)PANEL_TYPE_TC01 },
+ { .compatible = "samsung,sofef01-m-ams597ut01",
+ .data = (void *)PANEL_TYPE_UT01 },
+ { .compatible = "samsung,sofef01-m-ams597ut04",
+ .data = (void *)PANEL_TYPE_UT04 },
+ { .compatible = "samsung,sofef01-m-ams597ut05",
+ .data = (void *)PANEL_TYPE_UT05 },
+ { .compatible = "samsung,sofef01-m-ams605dk01",
+ .data = (void *)PANEL_TYPE_DK01 },
+ { /* 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 <marijn.suijten@somainline.org>");
+MODULE_DESCRIPTION("DRM panel driver for Samsung SOFEF01-M Display-Driver-IC panels");
+MODULE_LICENSE("GPL");
--
2.52.0
Hi Marijn,
thanks for your patch!
On Mon, Dec 22, 2025 at 12:32 AM Marijn Suijten
<marijn.suijten@somainline.org> wrote:
> This Samsung SOFEF01-M Display-Driver-IC is used to drive 1080x2520@60Hz
> command-mode DSI panels found in many Sony phones:
> - Sony Xperia 5 (kumano bahamut): amb609tc01
> - Sony Xperia 10 II (seine pdx201): ams597ut01
> - Sony Xperia 10 III (lena pdx213): ams597ut04
> - Sony Xperia 10 IV (murray pdx225): ams597ut05
> - Sony Xperia 10 V (zambezi pdx235): ams605dk01
> - Sony Xperia 10 VI (columbia pdx246): ams605dk01
>
> The amb609tc01 and ams605dk01 come in slightly larger at 6.1" while the
> others are 6.0".
>
> A "fake" porch calculation is included to artificially bump the clock
> rate necessary to account for "transfer overhead" (DSI packet headers)
> since this is missing from the MSM DSI host driver; porches aren't
> otherwise used on command-mode panels.
>
> Signed-off-by: Marijn Suijten <marijn.suijten@somainline.org>
Excellent work with abstracting all the sofef01-m panels!
Only nitpicks follow:
> + if (ctx->panel_type == PANEL_TYPE_TC01 ||
> + ctx->panel_type == PANEL_TYPE_UT01 ||
> + ctx->panel_type == PANEL_TYPE_UT04) {
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
The sofef00 driver tell us exactly what this sequence is:
#define sofef00_test_key_on_lvl2(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
#define sofef00_test_key_off_lvl2(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
I would just rename these two to sofef01_test_key_on/off_lvl2()
and use the same helpers in this driver to follow the sofef00 pattern.
> + if (ctx->panel_type == PANEL_TYPE_UT04) {
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
Like here
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0x5a, 0x5a);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xE1, 0x00, 0x00, 0x02, 0x00, 0x1C, 0x1C,
> + 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x19);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xfc, 0xa5, 0xa5);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
And here.
> + if (ctx->panel_type == PANEL_TYPE_UT05 || ctx->panel_type == PANEL_TYPE_DK01) {
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
And here.
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x27, 0xf2);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf2, 0x80);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf7, 0x07);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
And here.
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
And here.
> + /* Downstream: ERR_FG Enable */
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xe5, 0x15);
> + if (ctx->panel_type == PANEL_TYPE_DK01)
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x0f, 0x4c, 0x20);
> + else
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xed, 0x04, 0x4c, 0x20);
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
And here.
> +
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
And here.
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xb0, 0x02, 0x8f);
> +
> + if (ctx->panel_type == PANEL_TYPE_DK01)
> + /* Downstream Xperia 10 V: FLM1,FLM2 On */
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x27, 0x25);
> + else if (0) /* TODO: Both use the DK01 panel */
> + /* Downstream Xperia 10 VI: FLM1 On, FLM2 On */
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x27, 0x27);
> + else
> + /* Downsteam: FLM1 on, FLM2 off */
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x8f, 0x27, 0x05);
> +
> + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0xa5, 0xa5);
And here.
(etc).
+/- these changes:
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Yours,
Linus Walleij
On 2025-12-26 14:21:37, Linus Walleij wrote:
...
> Only nitpicks follow:
>
> > + if (ctx->panel_type == PANEL_TYPE_TC01 ||
> > + ctx->panel_type == PANEL_TYPE_UT01 ||
> > + ctx->panel_type == PANEL_TYPE_UT04) {
> > + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
>
> The sofef00 driver tell us exactly what this sequence is:
>
> #define sofef00_test_key_on_lvl2(ctx) \
> mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
> #define sofef00_test_key_off_lvl2(ctx) \
> mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
>
> I would just rename these two to sofef01_test_key_on/off_lvl2()
> and use the same helpers in this driver to follow the sofef00 pattern.
Right, yes. I think I already brought this up in V1, that some existing
Samsung drivers call this (the 0xf0 part) the MCS PASSWORD, others call it the
LEVEL_2_KEY or USER_KEY or ACCESSPROT.
I'm curious (though should probably look up the patch history) where these names
come from, which of these names match the sofef01/03/souxp00 DDICs provided
here, and what they're supposed to mean (MCS: Magic Command Sequence?).
Then there are possibly a lot more constants we can glean from other drivers,
though again without confirmation that it's identical on this DDIC.
- Marijn
On Fri, Dec 26, 2025 at 3:16 PM Marijn Suijten
<marijn.suijten@somainline.org> wrote:
> On 2025-12-26 14:21:37, Linus Walleij wrote:
> > The sofef00 driver tell us exactly what this sequence is:
> >
> > #define sofef00_test_key_on_lvl2(ctx) \
> > mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
> > #define sofef00_test_key_off_lvl2(ctx) \
> > mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
> >
> > I would just rename these two to sofef01_test_key_on/off_lvl2()
> > and use the same helpers in this driver to follow the sofef00 pattern.
>
> Right, yes. I think I already brought this up in V1, that some existing
> Samsung drivers call this (the 0xf0 part) the MCS PASSWORD, others call it the
> LEVEL_2_KEY or USER_KEY or ACCESSPROT.
Actually all of the samsung s6e panels are suspected to be s0fef0/1
display controller variants, which you see if you look inside
panel-samsung-s6e*, for example panel-samsung-s6e3fc2x01.c has this:
#define s6e3fc2x01_test_key_on_lvl1(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0x9f, 0xa5, 0xa5)
#define s6e3fc2x01_test_key_off_lvl1(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0x9f, 0x5a, 0x5a)
#define s6e3fc2x01_test_key_on_lvl2(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
#define s6e3fc2x01_test_key_off_lvl2(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
#define s6e3fc2x01_test_key_on_lvl3(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0x5a, 0x5a)
#define s6e3fc2x01_test_key_off_lvl3(ctx) \
mipi_dsi_dcs_write_seq_multi(ctx, 0xfc, 0xa5, 0xa5)
So there is also the explanation of the 0xfc command.
> Then there are possibly a lot more constants we can glean from other drivers,
> though again without confirmation that it's identical on this DDIC.
I think the s0fef0/1 drivers and all the panel-samsung-s6e* drivers
should probably be unified a bit maybe we can just create a
local s0fef.h file with the above for all these drivers to use?
(If someone has actual datasheets for s0fef0/1 that would be great.)
Yours,
Linus Walleij
On 2025-12-26 18:43:03, Linus Walleij wrote: > On Fri, Dec 26, 2025 at 3:16 PM Marijn Suijten > <marijn.suijten@somainline.org> wrote: > > On 2025-12-26 14:21:37, Linus Walleij wrote: > > > > The sofef00 driver tell us exactly what this sequence is: > > > > > > #define sofef00_test_key_on_lvl2(ctx) \ > > > mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a) > > > #define sofef00_test_key_off_lvl2(ctx) \ > > > mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5) > > > > > > I would just rename these two to sofef01_test_key_on/off_lvl2() > > > and use the same helpers in this driver to follow the sofef00 pattern. > > > > Right, yes. I think I already brought this up in V1, that some existing > > Samsung drivers call this (the 0xf0 part) the MCS PASSWORD, others call it the > > LEVEL_2_KEY or USER_KEY or ACCESSPROT. > > Actually all of the samsung s6e panels are suspected to be s0fef0/1 Just noting the first 0 is actually the letter o. > display controller variants, which you see if you look inside > panel-samsung-s6e*, for example panel-samsung-s6e3fc2x01.c has this: ... > I think the s0fef0/1 drivers and all the panel-samsung-s6e* drivers > should probably be unified a bit maybe we can just create a > local s0fef.h file with the above for all these drivers to use? > > (If someone has actual datasheets for s0fef0/1 that would be great.) If we know exactly what the commands are, and have their arguments documented, it'd be nice to have these as constants and functions in a global reusable header, but it does require being absolutely certain on their meaning and equivalence. - Marijn
On Mon, Dec 29, 2025 at 12:43 PM Marijn Suijten <marijn.suijten@somainline.org> wrote: > > Actually all of the samsung s6e panels are suspected to be s0fef0/1 > > Just noting the first 0 is actually the letter o. Yeah I'm sloppy :D sorry. > > display controller variants, which you see if you look inside > > panel-samsung-s6e*, for example panel-samsung-s6e3fc2x01.c has this: > ... > > > I think the s0fef0/1 drivers and all the panel-samsung-s6e* drivers > > should probably be unified a bit maybe we can just create a > > local s0fef.h file with the above for all these drivers to use? > > > > (If someone has actual datasheets for s0fef0/1 that would be great.) > > If we know exactly what the commands are, and have their arguments documented, > it'd be nice to have these as constants and functions in a global reusable > header, but it does require being absolutely certain on their meaning and > equivalence. Yeah well. When all we have is code dumps all we can do is try to split out the stuff we know for sure is shared. Like the different unlock commands... Yours, Linus Walleij
On 2025-12-26 15:16:30, Marijn Suijten wrote:
> On 2025-12-26 14:21:37, Linus Walleij wrote:
> ...
> > Only nitpicks follow:
> >
> > > + if (ctx->panel_type == PANEL_TYPE_TC01 ||
> > > + ctx->panel_type == PANEL_TYPE_UT01 ||
> > > + ctx->panel_type == PANEL_TYPE_UT04) {
> > > + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0xf0, 0x5a, 0x5a);
> >
> > The sofef00 driver tell us exactly what this sequence is:
> >
> > #define sofef00_test_key_on_lvl2(ctx) \
> > mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0x5a, 0x5a)
> > #define sofef00_test_key_off_lvl2(ctx) \
> > mipi_dsi_dcs_write_seq_multi(ctx, 0xf0, 0xa5, 0xa5)
> >
> > I would just rename these two to sofef01_test_key_on/off_lvl2()
> > and use the same helpers in this driver to follow the sofef00 pattern.
>
> Right, yes. I think I already brought this up in V1, that some existing
> Samsung drivers call this (the 0xf0 part) the MCS PASSWORD, others call it the
> LEVEL_2_KEY or USER_KEY or ACCESSPROT.
>
> I'm curious (though should probably look up the patch history) where these names
> come from, which of these names match the sofef01/03/souxp00 DDICs provided
> here, and what they're supposed to mean (MCS: Magic Command Sequence?).
I should have probably looked this up before asking; Manufacturer Command
Sequence.
Likely a generic prefix someone came up with to label commands that are not
part of DCS.
> Then there are possibly a lot more constants we can glean from other drivers,
> though again without confirmation that it's identical on this DDIC.
>
> - Marijn
Hi Marijn,
kernel test robot noticed the following build warnings:
[auto build test WARNING on cc3aa43b44bdb43dfbac0fcb51c56594a11338a8]
url: https://github.com/intel-lab-lkp/linux/commits/Marijn-Suijten/drm-panel-Clean-up-SOFEF00-config-dependencies/20251222-073548
base: cc3aa43b44bdb43dfbac0fcb51c56594a11338a8
patch link: https://lore.kernel.org/r/20251222-drm-panels-sony-v2-5-82a87465d163%40somainline.org
patch subject: [PATCH v2 05/11] drm/panel: Add panel driver for Samsung SOFEF01 DDIC
config: riscv-allyesconfig (https://download.01.org/0day-ci/archive/20251224/202512242356.EwESE8Qv-lkp@intel.com/config)
compiler: clang version 16.0.6 (https://github.com/llvm/llvm-project 7cbf1a2591520c2491aa35339f227775f4d3adf6)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251224/202512242356.EwESE8Qv-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512242356.EwESE8Qv-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/gpu/drm/panel/panel-samsung-sofef01.c:389:20: warning: cast to smaller integer type 'enum panel_type' from 'const void *' [-Wvoid-pointer-to-enum-cast]
ctx->panel_type = (enum panel_type)of_device_get_match_data(dev);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
vim +389 drivers/gpu/drm/panel/panel-samsung-sofef01.c
357
358 static int samsung_sofef01_m_probe(struct mipi_dsi_device *dsi)
359 {
360 const struct backlight_properties props = {
361 .type = BACKLIGHT_RAW,
362 .brightness = 100,
363 .max_brightness = 1023,
364 };
365 struct device *dev = &dsi->dev;
366 struct samsung_sofef01_m *ctx;
367 int ret;
368
369 ctx = devm_drm_panel_alloc(dev, struct samsung_sofef01_m, panel,
370 &samsung_sofef01_m_panel_funcs,
371 DRM_MODE_CONNECTOR_DSI);
372 if (IS_ERR(ctx))
373 return PTR_ERR(ctx);
374
375 ret = devm_regulator_bulk_get_const(
376 dev,
377 ARRAY_SIZE(samsung_sofef01_m_supplies),
378 samsung_sofef01_m_supplies,
379 &ctx->supplies);
380 if (ret < 0)
381 return dev_err_probe(dev, ret, "Failed to get regulators\n");
382
383 ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
384 if (IS_ERR(ctx->reset_gpio))
385 return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio),
386 "Failed to get reset-gpios\n");
387
388 ctx->dsi = dsi;
> 389 ctx->panel_type = (enum panel_type)of_device_get_match_data(dev);
390 if (ctx->panel_type == PANEL_TYPE_TC01)
391 ctx->mode = &samsung_sofef01_m_61_142_mode;
392 else if (ctx->panel_type == PANEL_TYPE_DK01)
393 ctx->mode = &samsung_sofef01_m_61_141_mode;
394 else
395 ctx->mode = &samsung_sofef01_m_60_139_mode;
396 mipi_dsi_set_drvdata(dsi, ctx);
397
398 dsi->lanes = 4;
399 dsi->format = MIPI_DSI_FMT_RGB888;
400 dsi->mode_flags = MIPI_DSI_CLOCK_NON_CONTINUOUS;
401
402 ctx->panel.prepare_prev_first = true;
403
404 ctx->panel.backlight = devm_backlight_device_register(
405 dev, dev_name(dev), dev, dsi,
406 &samsung_sofef01_m_bl_ops,
407 &props);
408 if (IS_ERR(ctx->panel.backlight))
409 return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight),
410 "Failed to create backlight\n");
411
412 drm_panel_add(&ctx->panel);
413
414 ret = mipi_dsi_attach(dsi);
415 if (ret < 0) {
416 dev_err(dev, "Failed to attach to DSI host: %d\n", ret);
417 drm_panel_remove(&ctx->panel);
418 return ret;
419 }
420
421 return 0;
422 }
423
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Mon, Dec 22, 2025 at 12:32:11AM +0100, Marijn Suijten wrote: > This Samsung SOFEF01-M Display-Driver-IC is used to drive 1080x2520@60Hz > command-mode DSI panels found in many Sony phones: > - Sony Xperia 5 (kumano bahamut): amb609tc01 > - Sony Xperia 10 II (seine pdx201): ams597ut01 > - Sony Xperia 10 III (lena pdx213): ams597ut04 > - Sony Xperia 10 IV (murray pdx225): ams597ut05 > - Sony Xperia 10 V (zambezi pdx235): ams605dk01 > - Sony Xperia 10 VI (columbia pdx246): ams605dk01 > > The amb609tc01 and ams605dk01 come in slightly larger at 6.1" while the > others are 6.0". > > A "fake" porch calculation is included to artificially bump the clock > rate necessary to account for "transfer overhead" (DSI packet headers) > since this is missing from the MSM DSI host driver; porches aren't > otherwise used on command-mode panels. > > Signed-off-by: Marijn Suijten <marijn.suijten@somainline.org> > --- > MAINTAINERS | 1 + > drivers/gpu/drm/panel/Kconfig | 18 + > drivers/gpu/drm/panel/Makefile | 1 + > drivers/gpu/drm/panel/panel-samsung-sofef01.c | 463 ++++++++++++++++++++++++++ > 4 files changed, 483 insertions(+) > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com> -- With best wishes Dmitry
© 2016 - 2026 Red Hat, Inc.