xref: /linux/drivers/gpu/drm/panel/panel-boe-th101mb31ig002-28a.c (revision ae22a94997b8a03dcb3c922857c203246711f9d4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2023 Alexander Warnecke <awarnecke002@hotmail.com>
4  * Copyright (c) 2023 Manuel Traut <manut@mecka.net>
5  * Copyright (c) 2023 Dang Huynh <danct12@riseup.net>
6  */
7 
8 #include <linux/delay.h>
9 #include <linux/gpio/consumer.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/of_device.h>
13 #include <linux/regulator/consumer.h>
14 
15 #include <drm/drm_connector.h>
16 #include <drm/drm_mipi_dsi.h>
17 #include <drm/drm_modes.h>
18 #include <drm/drm_panel.h>
19 
20 struct boe_th101mb31ig002 {
21 	struct drm_panel panel;
22 
23 	struct mipi_dsi_device *dsi;
24 
25 	struct regulator *power;
26 	struct gpio_desc *enable;
27 	struct gpio_desc *reset;
28 
29 	enum drm_panel_orientation orientation;
30 };
31 
32 static void boe_th101mb31ig002_reset(struct boe_th101mb31ig002 *ctx)
33 {
34 	gpiod_direction_output(ctx->reset, 0);
35 	usleep_range(10, 100);
36 	gpiod_direction_output(ctx->reset, 1);
37 	usleep_range(10, 100);
38 	gpiod_direction_output(ctx->reset, 0);
39 	usleep_range(5000, 6000);
40 }
41 
42 static int boe_th101mb31ig002_enable(struct drm_panel *panel)
43 {
44 	struct boe_th101mb31ig002 *ctx = container_of(panel,
45 						      struct boe_th101mb31ig002,
46 						      panel);
47 	struct mipi_dsi_device *dsi = ctx->dsi;
48 	struct device *dev = &dsi->dev;
49 	int ret;
50 
51 	mipi_dsi_dcs_write_seq(dsi, 0xE0, 0xAB, 0xBA);
52 	mipi_dsi_dcs_write_seq(dsi, 0xE1, 0xBA, 0xAB);
53 	mipi_dsi_dcs_write_seq(dsi, 0xB1, 0x10, 0x01, 0x47, 0xFF);
54 	mipi_dsi_dcs_write_seq(dsi, 0xB2, 0x0C, 0x14, 0x04, 0x50, 0x50, 0x14);
55 	mipi_dsi_dcs_write_seq(dsi, 0xB3, 0x56, 0x53, 0x00);
56 	mipi_dsi_dcs_write_seq(dsi, 0xB4, 0x33, 0x30, 0x04);
57 	mipi_dsi_dcs_write_seq(dsi, 0xB6, 0xB0, 0x00, 0x00, 0x10, 0x00, 0x10,
58 				    0x00);
59 	mipi_dsi_dcs_write_seq(dsi, 0xB8, 0x05, 0x12, 0x29, 0x49, 0x48, 0x00,
60 				    0x00);
61 	mipi_dsi_dcs_write_seq(dsi, 0xB9, 0x7C, 0x65, 0x55, 0x49, 0x46, 0x36,
62 				    0x3B, 0x24, 0x3D, 0x3C, 0x3D, 0x5C, 0x4C,
63 				    0x55, 0x47, 0x46, 0x39, 0x26, 0x06, 0x7C,
64 				    0x65, 0x55, 0x49, 0x46, 0x36, 0x3B, 0x24,
65 				    0x3D, 0x3C, 0x3D, 0x5C, 0x4C, 0x55, 0x47,
66 				    0x46, 0x39, 0x26, 0x06);
67 	mipi_dsi_dcs_write_seq(dsi, 0x00, 0xFF, 0x87, 0x12, 0x34, 0x44, 0x44,
68 				    0x44, 0x44, 0x98, 0x04, 0x98, 0x04, 0x0F,
69 				    0x00, 0x00, 0xC1);
70 	mipi_dsi_dcs_write_seq(dsi, 0xC1, 0x54, 0x94, 0x02, 0x85, 0x9F, 0x00,
71 				    0x7F, 0x00, 0x54, 0x00);
72 	mipi_dsi_dcs_write_seq(dsi, 0xC2, 0x17, 0x09, 0x08, 0x89, 0x08, 0x11,
73 				    0x22, 0x20, 0x44, 0xFF, 0x18, 0x00);
74 	mipi_dsi_dcs_write_seq(dsi, 0xC3, 0x86, 0x46, 0x05, 0x05, 0x1C, 0x1C,
75 				    0x1D, 0x1D, 0x02, 0x1F, 0x1F, 0x1E, 0x1E,
76 				    0x0F, 0x0F, 0x0D, 0x0D, 0x13, 0x13, 0x11,
77 				    0x11, 0x00);
78 	mipi_dsi_dcs_write_seq(dsi, 0xC4, 0x07, 0x07, 0x04, 0x04, 0x1C, 0x1C,
79 				    0x1D, 0x1D, 0x02, 0x1F, 0x1F, 0x1E, 0x1E,
80 				    0x0E, 0x0E, 0x0C, 0x0C, 0x12, 0x12, 0x10,
81 				    0x10, 0x00);
82 	mipi_dsi_dcs_write_seq(dsi, 0xC6, 0x2A, 0x2A);
83 	mipi_dsi_dcs_write_seq(dsi, 0xC8, 0x21, 0x00, 0x31, 0x42, 0x34, 0x16);
84 	mipi_dsi_dcs_write_seq(dsi, 0xCA, 0xCB, 0x43);
85 	mipi_dsi_dcs_write_seq(dsi, 0xCD, 0x0E, 0x4B, 0x4B, 0x20, 0x19, 0x6B,
86 				    0x06, 0xB3);
87 	mipi_dsi_dcs_write_seq(dsi, 0xD2, 0xE3, 0x2B, 0x38, 0x00);
88 	mipi_dsi_dcs_write_seq(dsi, 0xD4, 0x00, 0x01, 0x00, 0x0E, 0x04, 0x44,
89 				    0x08, 0x10, 0x00, 0x00, 0x00);
90 	mipi_dsi_dcs_write_seq(dsi, 0xE6, 0x80, 0x01, 0xFF, 0xFF, 0xFF, 0xFF,
91 				    0xFF, 0xFF);
92 	mipi_dsi_dcs_write_seq(dsi, 0xF0, 0x12, 0x03, 0x20, 0x00, 0xFF);
93 	mipi_dsi_dcs_write_seq(dsi, 0xF3, 0x00);
94 
95 	ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
96 	if (ret < 0) {
97 		dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
98 		return ret;
99 	}
100 
101 	msleep(120);
102 
103 	ret = mipi_dsi_dcs_set_display_on(dsi);
104 	if (ret < 0) {
105 		dev_err(dev, "Failed to set panel on: %d\n", ret);
106 		return ret;
107 	}
108 
109 	return 0;
110 }
111 
112 static int boe_th101mb31ig002_disable(struct drm_panel *panel)
113 {
114 	struct boe_th101mb31ig002 *ctx = container_of(panel,
115 						      struct boe_th101mb31ig002,
116 						      panel);
117 	struct mipi_dsi_device *dsi = ctx->dsi;
118 	struct device *dev = &dsi->dev;
119 	int ret;
120 
121 	ret = mipi_dsi_dcs_set_display_off(dsi);
122 	if (ret < 0)
123 		dev_err(dev, "Failed to set panel off: %d\n", ret);
124 
125 	msleep(120);
126 
127 	ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
128 	if (ret < 0)
129 		dev_err(dev, "Failed to enter sleep mode: %d\n", ret);
130 
131 	return 0;
132 }
133 
134 static int boe_th101mb31ig002_unprepare(struct drm_panel *panel)
135 {
136 	struct boe_th101mb31ig002 *ctx = container_of(panel,
137 						      struct boe_th101mb31ig002,
138 						      panel);
139 
140 	gpiod_set_value_cansleep(ctx->reset, 1);
141 	gpiod_set_value_cansleep(ctx->enable, 0);
142 	regulator_disable(ctx->power);
143 
144 	return 0;
145 }
146 
147 static int boe_th101mb31ig002_prepare(struct drm_panel *panel)
148 {
149 	struct boe_th101mb31ig002 *ctx = container_of(panel,
150 						      struct boe_th101mb31ig002,
151 						      panel);
152 	struct device *dev = &ctx->dsi->dev;
153 	int ret;
154 
155 	ret = regulator_enable(ctx->power);
156 	if (ret) {
157 		dev_err(dev, "Failed to enable power supply: %d\n", ret);
158 		return ret;
159 	}
160 
161 	gpiod_set_value_cansleep(ctx->enable, 1);
162 	msleep(50);
163 	boe_th101mb31ig002_reset(ctx);
164 	boe_th101mb31ig002_enable(panel);
165 
166 	return 0;
167 }
168 
169 static const struct drm_display_mode boe_th101mb31ig002_default_mode = {
170 	.clock		= 73500,
171 	.hdisplay	= 800,
172 	.hsync_start	= 800 + 64,
173 	.hsync_end	= 800 + 64 + 16,
174 	.htotal		= 800 + 64 + 16 + 64,
175 	.vdisplay	= 1280,
176 	.vsync_start	= 1280 + 2,
177 	.vsync_end	= 1280 + 2 + 4,
178 	.vtotal		= 1280 + 2 + 4 + 12,
179 	.width_mm	= 135,
180 	.height_mm	= 216,
181 	.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
182 };
183 
184 static int boe_th101mb31ig002_get_modes(struct drm_panel *panel,
185 					struct drm_connector *connector)
186 {
187 	struct boe_th101mb31ig002 *ctx = container_of(panel,
188 						      struct boe_th101mb31ig002,
189 						      panel);
190 	struct drm_display_mode *mode;
191 
192 	mode = drm_mode_duplicate(connector->dev,
193 				  &boe_th101mb31ig002_default_mode);
194 	if (!mode) {
195 		dev_err(panel->dev, "Failed to add mode %ux%u@%u\n",
196 			boe_th101mb31ig002_default_mode.hdisplay,
197 			boe_th101mb31ig002_default_mode.vdisplay,
198 			drm_mode_vrefresh(&boe_th101mb31ig002_default_mode));
199 		return -ENOMEM;
200 	}
201 
202 	drm_mode_set_name(mode);
203 
204 	connector->display_info.bpc = 8;
205 	connector->display_info.width_mm = mode->width_mm;
206 	connector->display_info.height_mm = mode->height_mm;
207 
208 	/*
209 	 * TODO: Remove once all drm drivers call
210 	 * drm_connector_set_orientation_from_panel()
211 	 */
212 	drm_connector_set_panel_orientation(connector, ctx->orientation);
213 
214 	drm_mode_probed_add(connector, mode);
215 
216 	return 1;
217 }
218 
219 static enum drm_panel_orientation
220 boe_th101mb31ig002_get_orientation(struct drm_panel *panel)
221 {
222 	struct boe_th101mb31ig002 *ctx = container_of(panel,
223 						      struct boe_th101mb31ig002,
224 						      panel);
225 
226 	return ctx->orientation;
227 }
228 
229 static const struct drm_panel_funcs boe_th101mb31ig002_funcs = {
230 	.prepare = boe_th101mb31ig002_prepare,
231 	.unprepare = boe_th101mb31ig002_unprepare,
232 	.disable = boe_th101mb31ig002_disable,
233 	.get_modes = boe_th101mb31ig002_get_modes,
234 	.get_orientation = boe_th101mb31ig002_get_orientation,
235 };
236 
237 static int boe_th101mb31ig002_dsi_probe(struct mipi_dsi_device *dsi)
238 {
239 	struct boe_th101mb31ig002 *ctx;
240 	int ret;
241 
242 	ctx = devm_kzalloc(&dsi->dev, sizeof(*ctx), GFP_KERNEL);
243 	if (!ctx)
244 		return -ENOMEM;
245 
246 	mipi_dsi_set_drvdata(dsi, ctx);
247 	ctx->dsi = dsi;
248 
249 	dsi->lanes = 4;
250 	dsi->format = MIPI_DSI_FMT_RGB888;
251 	dsi->mode_flags = MIPI_DSI_MODE_VIDEO_BURST |
252 			  MIPI_DSI_MODE_NO_EOT_PACKET |
253 			  MIPI_DSI_MODE_LPM;
254 
255 	ctx->power = devm_regulator_get(&dsi->dev, "power");
256 	if (IS_ERR(ctx->power))
257 		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->power),
258 				     "Failed to get power regulator\n");
259 
260 	ctx->enable = devm_gpiod_get(&dsi->dev, "enable", GPIOD_OUT_LOW);
261 	if (IS_ERR(ctx->enable))
262 		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->enable),
263 				     "Failed to get enable GPIO\n");
264 
265 	ctx->reset = devm_gpiod_get(&dsi->dev, "reset", GPIOD_OUT_HIGH);
266 	if (IS_ERR(ctx->reset))
267 		return dev_err_probe(&dsi->dev, PTR_ERR(ctx->reset),
268 				     "Failed to get reset GPIO\n");
269 
270 	ret = of_drm_get_panel_orientation(dsi->dev.of_node,
271 					   &ctx->orientation);
272 	if (ret)
273 		return dev_err_probe(&dsi->dev, ret,
274 				     "Failed to get orientation\n");
275 
276 	drm_panel_init(&ctx->panel, &dsi->dev, &boe_th101mb31ig002_funcs,
277 		       DRM_MODE_CONNECTOR_DSI);
278 
279 	ret = drm_panel_of_backlight(&ctx->panel);
280 	if (ret)
281 		return ret;
282 
283 	drm_panel_add(&ctx->panel);
284 
285 	ret = mipi_dsi_attach(dsi);
286 	if (ret < 0) {
287 		dev_err_probe(&dsi->dev, ret,
288 			      "Failed to attach panel to DSI host\n");
289 		drm_panel_remove(&ctx->panel);
290 		return ret;
291 	}
292 
293 	return 0;
294 }
295 
296 static void boe_th101mb31ig002_dsi_remove(struct mipi_dsi_device *dsi)
297 {
298 	struct boe_th101mb31ig002 *ctx = mipi_dsi_get_drvdata(dsi);
299 
300 	mipi_dsi_detach(dsi);
301 	drm_panel_remove(&ctx->panel);
302 }
303 
304 static const struct of_device_id boe_th101mb31ig002_of_match[] = {
305 	{ .compatible = "boe,th101mb31ig002-28a", },
306 	{ /* sentinel */ }
307 };
308 MODULE_DEVICE_TABLE(of, boe_th101mb31ig002_of_match);
309 
310 static struct mipi_dsi_driver boe_th101mb31ig002_driver = {
311 	.driver = {
312 		.name = "boe-th101mb31ig002-28a",
313 		.of_match_table = boe_th101mb31ig002_of_match,
314 	},
315 	.probe = boe_th101mb31ig002_dsi_probe,
316 	.remove = boe_th101mb31ig002_dsi_remove,
317 };
318 module_mipi_dsi_driver(boe_th101mb31ig002_driver);
319 
320 MODULE_AUTHOR("Alexander Warnecke <awarnecke002@hotmail.com>");
321 MODULE_DESCRIPTION("BOE TH101MB31IG002-28A MIPI-DSI LCD panel");
322 MODULE_LICENSE("GPL");
323