Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu/drm: panel: add Samsung LTL106HL02 MIPI DSI panel driver

LTL106HL02 is a color active matrix TFT (Thin Film Transistor) liquid
crystal display (LCD) that uses amorphous silicon TFT as switching
devices. This model is composed of a TFT LCD panel, a driver circuit and a
backlight unit. The resolution of a 10.6" contains 1920 x 1080 pixels and
can display up to 16,8M color with wide viewing angle.

Signed-off-by: Jonas Schwöbel <jonasschwoebel@yahoo.de>
Signed-off-by: Anton Bambura <jenneron@protonmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
Link: https://patch.msgid.link/20251110091440.5251-8-clamor95@gmail.com

authored by

Anton Bambura and committed by
Neil Armstrong
ac488854 06fb75e2

+193
+13
drivers/gpu/drm/panel/Kconfig
··· 781 781 depends on BACKLIGHT_CLASS_DEVICE 782 782 select VIDEOMODE_HELPERS 783 783 784 + config DRM_PANEL_SAMSUNG_LTL106HL02 785 + tristate "Samsung LTL106HL02 panel" 786 + depends on OF 787 + depends on DRM_MIPI_DSI 788 + depends on BACKLIGHT_CLASS_DEVICE 789 + select VIDEOMODE_HELPERS 790 + help 791 + Say Y here if you want to enable support for the Samsung LTL106HL02 792 + panel driver which is used in Microsoft Surface 2. 793 + 794 + To compile this driver as a module, choose M here: the module 795 + will be called panel-samsung-ltl106hl02. 796 + 784 797 config DRM_PANEL_SAMSUNG_S6E3FA7 785 798 tristate "Samsung S6E3FA7 panel driver" 786 799 depends on OF
+1
drivers/gpu/drm/panel/Makefile
··· 76 76 obj-$(CONFIG_DRM_PANEL_SAMSUNG_ATNA33XC20) += panel-samsung-atna33xc20.o 77 77 obj-$(CONFIG_DRM_PANEL_SAMSUNG_DB7430) += panel-samsung-db7430.o 78 78 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o 79 + obj-$(CONFIG_DRM_PANEL_SAMSUNG_LTL106HL02) += panel-samsung-ltl106hl02.o 79 80 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D16D0) += panel-samsung-s6d16d0.o 80 81 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D27A1) += panel-samsung-s6d27a1.o 81 82 obj-$(CONFIG_DRM_PANEL_SAMSUNG_S6D7AA0) += panel-samsung-s6d7aa0.o
+179
drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + #include <linux/array_size.h> 4 + #include <linux/delay.h> 5 + #include <linux/err.h> 6 + #include <linux/gpio/consumer.h> 7 + #include <linux/mod_devicetable.h> 8 + #include <linux/module.h> 9 + #include <linux/property.h> 10 + #include <linux/regulator/consumer.h> 11 + 12 + #include <video/mipi_display.h> 13 + 14 + #include <drm/drm_mipi_dsi.h> 15 + #include <drm/drm_modes.h> 16 + #include <drm/drm_panel.h> 17 + #include <drm/drm_probe_helper.h> 18 + 19 + struct samsung_ltl106hl02 { 20 + struct drm_panel panel; 21 + struct mipi_dsi_device *dsi; 22 + 23 + struct regulator *supply; 24 + struct gpio_desc *reset_gpio; 25 + }; 26 + 27 + static inline struct samsung_ltl106hl02 *to_samsung_ltl106hl02(struct drm_panel *panel) 28 + { 29 + return container_of(panel, struct samsung_ltl106hl02, panel); 30 + } 31 + 32 + static void samsung_ltl106hl02_reset(struct samsung_ltl106hl02 *ctx) 33 + { 34 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 35 + usleep_range(10000, 11000); 36 + gpiod_set_value_cansleep(ctx->reset_gpio, 0); 37 + usleep_range(2000, 3000); 38 + } 39 + 40 + static int samsung_ltl106hl02_prepare(struct drm_panel *panel) 41 + { 42 + struct samsung_ltl106hl02 *ctx = to_samsung_ltl106hl02(panel); 43 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 44 + struct device *dev = &ctx->dsi->dev; 45 + int ret; 46 + 47 + ret = regulator_enable(ctx->supply); 48 + if (ret < 0) { 49 + dev_err(dev, "failed to enable power supply %d\n", ret); 50 + return ret; 51 + } 52 + 53 + if (ctx->reset_gpio) 54 + samsung_ltl106hl02_reset(ctx); 55 + 56 + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); 57 + mipi_dsi_msleep(&dsi_ctx, 70); 58 + 59 + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); 60 + mipi_dsi_msleep(&dsi_ctx, 5); 61 + 62 + return dsi_ctx.accum_err; 63 + } 64 + 65 + static int samsung_ltl106hl02_unprepare(struct drm_panel *panel) 66 + { 67 + struct samsung_ltl106hl02 *ctx = to_samsung_ltl106hl02(panel); 68 + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; 69 + 70 + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); 71 + mipi_dsi_msleep(&dsi_ctx, 50); 72 + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); 73 + mipi_dsi_msleep(&dsi_ctx, 150); 74 + 75 + if (ctx->reset_gpio) 76 + gpiod_set_value_cansleep(ctx->reset_gpio, 1); 77 + 78 + regulator_disable(ctx->supply); 79 + 80 + return 0; 81 + } 82 + 83 + static const struct drm_display_mode samsung_ltl106hl02_mode = { 84 + .clock = (1920 + 32 + 32 + 64) * (1080 + 6 + 3 + 22) * 60 / 1000, 85 + .hdisplay = 1920, 86 + .hsync_start = 1920 + 32, 87 + .hsync_end = 1920 + 32 + 32, 88 + .htotal = 1920 + 32 + 32 + 64, 89 + .vdisplay = 1080, 90 + .vsync_start = 1080 + 6, 91 + .vsync_end = 1080 + 6 + 3, 92 + .vtotal = 1080 + 6 + 3 + 22, 93 + .width_mm = 235, 94 + .height_mm = 132, 95 + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 96 + }; 97 + 98 + static int samsung_ltl106hl02_get_modes(struct drm_panel *panel, 99 + struct drm_connector *connector) 100 + { 101 + return drm_connector_helper_get_modes_fixed(connector, &samsung_ltl106hl02_mode); 102 + } 103 + 104 + static const struct drm_panel_funcs samsung_ltl106hl02_panel_funcs = { 105 + .prepare = samsung_ltl106hl02_prepare, 106 + .unprepare = samsung_ltl106hl02_unprepare, 107 + .get_modes = samsung_ltl106hl02_get_modes, 108 + }; 109 + 110 + static int samsung_ltl106hl02_probe(struct mipi_dsi_device *dsi) 111 + { 112 + struct device *dev = &dsi->dev; 113 + struct samsung_ltl106hl02 *ctx; 114 + int ret; 115 + 116 + ctx = devm_drm_panel_alloc(dev, struct samsung_ltl106hl02, panel, 117 + &samsung_ltl106hl02_panel_funcs, 118 + DRM_MODE_CONNECTOR_DSI); 119 + if (IS_ERR(ctx)) 120 + return PTR_ERR(ctx); 121 + 122 + ctx->supply = devm_regulator_get(dev, "power"); 123 + if (IS_ERR(ctx->supply)) 124 + return dev_err_probe(dev, PTR_ERR(ctx->supply), 125 + "Failed to get power regulator\n"); 126 + 127 + ctx->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 128 + if (IS_ERR(ctx->reset_gpio)) 129 + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), 130 + "Failed to get reset-gpios\n"); 131 + 132 + ctx->dsi = dsi; 133 + mipi_dsi_set_drvdata(dsi, ctx); 134 + 135 + dsi->lanes = 4; 136 + dsi->format = MIPI_DSI_FMT_RGB888; 137 + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM; 138 + 139 + ret = drm_panel_of_backlight(&ctx->panel); 140 + if (ret) 141 + return dev_err_probe(dev, ret, "Failed to get backlight\n"); 142 + 143 + drm_panel_add(&ctx->panel); 144 + 145 + ret = devm_mipi_dsi_attach(dev, dsi); 146 + if (ret < 0) { 147 + drm_panel_remove(&ctx->panel); 148 + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); 149 + } 150 + 151 + return 0; 152 + } 153 + 154 + static void samsung_ltl106hl02_remove(struct mipi_dsi_device *dsi) 155 + { 156 + struct samsung_ltl106hl02 *ctx = mipi_dsi_get_drvdata(dsi); 157 + 158 + drm_panel_remove(&ctx->panel); 159 + } 160 + 161 + static const struct of_device_id samsung_ltl106hl02_of_match[] = { 162 + { .compatible = "samsung,ltl106hl02-001" }, 163 + { /* sentinel */ } 164 + }; 165 + MODULE_DEVICE_TABLE(of, samsung_ltl106hl02_of_match); 166 + 167 + static struct mipi_dsi_driver samsung_ltl106hl02_driver = { 168 + .driver = { 169 + .name = "panel-samsung-ltl106hl02", 170 + .of_match_table = samsung_ltl106hl02_of_match, 171 + }, 172 + .probe = samsung_ltl106hl02_probe, 173 + .remove = samsung_ltl106hl02_remove, 174 + }; 175 + module_mipi_dsi_driver(samsung_ltl106hl02_driver); 176 + 177 + MODULE_AUTHOR("Anton Bambura <jenneron@protonmail.com>"); 178 + MODULE_DESCRIPTION("DRM driver for Samsung LTL106HL02 video mode DSI panel"); 179 + MODULE_LICENSE("GPL");