xref: /linux/drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c (revision c17ee635fd3a482b2ad2bf5e269755c2eae5f25e)
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");
180