1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Mantix MLAF057WE51 5.7" MIPI-DSI panel driver |
4 | * |
5 | * Copyright (C) Purism SPC 2020 |
6 | */ |
7 | |
8 | #include <linux/backlight.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/gpio/consumer.h> |
11 | #include <linux/media-bus-format.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/regulator/consumer.h> |
15 | |
16 | #include <video/mipi_display.h> |
17 | |
18 | #include <drm/drm_mipi_dsi.h> |
19 | #include <drm/drm_modes.h> |
20 | #include <drm/drm_panel.h> |
21 | |
22 | #define DRV_NAME "panel-mantix-mlaf057we51" |
23 | |
24 | /* Manufacturer specific Commands send via DSI */ |
25 | #define MANTIX_CMD_OTP_STOP_RELOAD_MIPI 0x41 |
26 | #define MANTIX_CMD_INT_CANCEL 0x4C |
27 | #define MANTIX_CMD_SPI_FINISH 0x90 |
28 | |
29 | struct mantix { |
30 | struct device *dev; |
31 | struct drm_panel panel; |
32 | |
33 | struct gpio_desc *reset_gpio; |
34 | struct gpio_desc *tp_rstn_gpio; |
35 | |
36 | struct regulator *avdd; |
37 | struct regulator *avee; |
38 | struct regulator *vddi; |
39 | |
40 | const struct drm_display_mode *default_mode; |
41 | }; |
42 | |
43 | static inline struct mantix *panel_to_mantix(struct drm_panel *panel) |
44 | { |
45 | return container_of(panel, struct mantix, panel); |
46 | } |
47 | |
48 | static int mantix_init_sequence(struct mantix *ctx) |
49 | { |
50 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); |
51 | struct device *dev = ctx->dev; |
52 | |
53 | /* |
54 | * Init sequence was supplied by the panel vendor. |
55 | */ |
56 | mipi_dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x5A); |
57 | |
58 | mipi_dsi_generic_write_seq(dsi, MANTIX_CMD_INT_CANCEL, 0x03); |
59 | mipi_dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x5A, 0x03); |
60 | mipi_dsi_generic_write_seq(dsi, 0x80, 0xA9, 0x00); |
61 | |
62 | mipi_dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x5A, 0x09); |
63 | mipi_dsi_generic_write_seq(dsi, 0x80, 0x64, 0x00, 0x64, 0x00, 0x00); |
64 | msleep(msecs: 20); |
65 | |
66 | mipi_dsi_generic_write_seq(dsi, MANTIX_CMD_SPI_FINISH, 0xA5); |
67 | mipi_dsi_generic_write_seq(dsi, MANTIX_CMD_OTP_STOP_RELOAD_MIPI, 0x00, 0x2F); |
68 | msleep(msecs: 20); |
69 | |
70 | dev_dbg(dev, "Panel init sequence done\n" ); |
71 | return 0; |
72 | } |
73 | |
74 | static int mantix_enable(struct drm_panel *panel) |
75 | { |
76 | struct mantix *ctx = panel_to_mantix(panel); |
77 | struct device *dev = ctx->dev; |
78 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev); |
79 | int ret; |
80 | |
81 | ret = mantix_init_sequence(ctx); |
82 | if (ret < 0) { |
83 | dev_err(ctx->dev, "Panel init sequence failed: %d\n" , ret); |
84 | return ret; |
85 | } |
86 | |
87 | ret = mipi_dsi_dcs_exit_sleep_mode(dsi); |
88 | if (ret < 0) { |
89 | dev_err(dev, "Failed to exit sleep mode\n" ); |
90 | return ret; |
91 | } |
92 | msleep(msecs: 20); |
93 | |
94 | ret = mipi_dsi_dcs_set_display_on(dsi); |
95 | if (ret) |
96 | return ret; |
97 | usleep_range(min: 10000, max: 12000); |
98 | |
99 | ret = mipi_dsi_turn_on_peripheral(dsi); |
100 | if (ret < 0) { |
101 | dev_err(dev, "Failed to turn on peripheral\n" ); |
102 | return ret; |
103 | } |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static int mantix_disable(struct drm_panel *panel) |
109 | { |
110 | struct mantix *ctx = panel_to_mantix(panel); |
111 | struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); |
112 | int ret; |
113 | |
114 | ret = mipi_dsi_dcs_set_display_off(dsi); |
115 | if (ret < 0) |
116 | dev_err(ctx->dev, "Failed to turn off the display: %d\n" , ret); |
117 | |
118 | ret = mipi_dsi_dcs_enter_sleep_mode(dsi); |
119 | if (ret < 0) |
120 | dev_err(ctx->dev, "Failed to enter sleep mode: %d\n" , ret); |
121 | |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static int mantix_unprepare(struct drm_panel *panel) |
127 | { |
128 | struct mantix *ctx = panel_to_mantix(panel); |
129 | |
130 | gpiod_set_value_cansleep(desc: ctx->tp_rstn_gpio, value: 1); |
131 | usleep_range(min: 5000, max: 6000); |
132 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 1); |
133 | |
134 | regulator_disable(regulator: ctx->avee); |
135 | regulator_disable(regulator: ctx->avdd); |
136 | /* T11 */ |
137 | usleep_range(min: 5000, max: 6000); |
138 | regulator_disable(regulator: ctx->vddi); |
139 | /* T14 */ |
140 | msleep(msecs: 50); |
141 | |
142 | return 0; |
143 | } |
144 | |
145 | static int mantix_prepare(struct drm_panel *panel) |
146 | { |
147 | struct mantix *ctx = panel_to_mantix(panel); |
148 | int ret; |
149 | |
150 | /* Focaltech FT8006P, section 7.3.1 and 7.3.4 */ |
151 | dev_dbg(ctx->dev, "Resetting the panel\n" ); |
152 | ret = regulator_enable(regulator: ctx->vddi); |
153 | if (ret < 0) { |
154 | dev_err(ctx->dev, "Failed to enable vddi supply: %d\n" , ret); |
155 | return ret; |
156 | } |
157 | |
158 | /* T1 + T2 */ |
159 | usleep_range(min: 8000, max: 10000); |
160 | |
161 | ret = regulator_enable(regulator: ctx->avdd); |
162 | if (ret < 0) { |
163 | dev_err(ctx->dev, "Failed to enable avdd supply: %d\n" , ret); |
164 | return ret; |
165 | } |
166 | |
167 | /* T2d */ |
168 | usleep_range(min: 3500, max: 4000); |
169 | ret = regulator_enable(regulator: ctx->avee); |
170 | if (ret < 0) { |
171 | dev_err(ctx->dev, "Failed to enable avee supply: %d\n" , ret); |
172 | return ret; |
173 | } |
174 | |
175 | /* T3 + T4 + time for voltage to become stable: */ |
176 | usleep_range(min: 6000, max: 7000); |
177 | gpiod_set_value_cansleep(desc: ctx->reset_gpio, value: 0); |
178 | gpiod_set_value_cansleep(desc: ctx->tp_rstn_gpio, value: 0); |
179 | |
180 | /* T6 */ |
181 | msleep(msecs: 50); |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | static const struct drm_display_mode default_mode_mantix = { |
187 | .hdisplay = 720, |
188 | .hsync_start = 720 + 45, |
189 | .hsync_end = 720 + 45 + 14, |
190 | .htotal = 720 + 45 + 14 + 25, |
191 | .vdisplay = 1440, |
192 | .vsync_start = 1440 + 130, |
193 | .vsync_end = 1440 + 130 + 8, |
194 | .vtotal = 1440 + 130 + 8 + 106, |
195 | .clock = 85298, |
196 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
197 | .width_mm = 65, |
198 | .height_mm = 130, |
199 | }; |
200 | |
201 | static const struct drm_display_mode default_mode_ys = { |
202 | .hdisplay = 720, |
203 | .hsync_start = 720 + 45, |
204 | .hsync_end = 720 + 45 + 14, |
205 | .htotal = 720 + 45 + 14 + 25, |
206 | .vdisplay = 1440, |
207 | .vsync_start = 1440 + 175, |
208 | .vsync_end = 1440 + 175 + 8, |
209 | .vtotal = 1440 + 175 + 8 + 50, |
210 | .clock = 85298, |
211 | .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, |
212 | .width_mm = 65, |
213 | .height_mm = 130, |
214 | }; |
215 | |
216 | static const u32 mantix_bus_formats[] = { |
217 | MEDIA_BUS_FMT_RGB888_1X24, |
218 | }; |
219 | |
220 | static int mantix_get_modes(struct drm_panel *panel, |
221 | struct drm_connector *connector) |
222 | { |
223 | struct mantix *ctx = panel_to_mantix(panel); |
224 | struct drm_display_mode *mode; |
225 | |
226 | mode = drm_mode_duplicate(dev: connector->dev, mode: ctx->default_mode); |
227 | if (!mode) { |
228 | dev_err(ctx->dev, "Failed to add mode %ux%u@%u\n" , |
229 | ctx->default_mode->hdisplay, ctx->default_mode->vdisplay, |
230 | drm_mode_vrefresh(ctx->default_mode)); |
231 | return -ENOMEM; |
232 | } |
233 | |
234 | drm_mode_set_name(mode); |
235 | |
236 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; |
237 | connector->display_info.width_mm = mode->width_mm; |
238 | connector->display_info.height_mm = mode->height_mm; |
239 | drm_mode_probed_add(connector, mode); |
240 | |
241 | drm_display_info_set_bus_formats(info: &connector->display_info, |
242 | formats: mantix_bus_formats, |
243 | ARRAY_SIZE(mantix_bus_formats)); |
244 | |
245 | return 1; |
246 | } |
247 | |
248 | static const struct drm_panel_funcs mantix_drm_funcs = { |
249 | .disable = mantix_disable, |
250 | .unprepare = mantix_unprepare, |
251 | .prepare = mantix_prepare, |
252 | .enable = mantix_enable, |
253 | .get_modes = mantix_get_modes, |
254 | }; |
255 | |
256 | static int mantix_probe(struct mipi_dsi_device *dsi) |
257 | { |
258 | struct device *dev = &dsi->dev; |
259 | struct mantix *ctx; |
260 | int ret; |
261 | |
262 | ctx = devm_kzalloc(dev, size: sizeof(*ctx), GFP_KERNEL); |
263 | if (!ctx) |
264 | return -ENOMEM; |
265 | ctx->default_mode = of_device_get_match_data(dev); |
266 | |
267 | ctx->reset_gpio = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
268 | if (IS_ERR(ptr: ctx->reset_gpio)) { |
269 | dev_err(dev, "cannot get reset gpio\n" ); |
270 | return PTR_ERR(ptr: ctx->reset_gpio); |
271 | } |
272 | |
273 | ctx->tp_rstn_gpio = devm_gpiod_get(dev, con_id: "mantix,tp-rstn" , flags: GPIOD_OUT_HIGH); |
274 | if (IS_ERR(ptr: ctx->tp_rstn_gpio)) { |
275 | dev_err(dev, "cannot get tp-rstn gpio\n" ); |
276 | return PTR_ERR(ptr: ctx->tp_rstn_gpio); |
277 | } |
278 | |
279 | mipi_dsi_set_drvdata(dsi, data: ctx); |
280 | ctx->dev = dev; |
281 | |
282 | dsi->lanes = 4; |
283 | dsi->format = MIPI_DSI_FMT_RGB888; |
284 | dsi->mode_flags = MIPI_DSI_MODE_VIDEO | |
285 | MIPI_DSI_MODE_VIDEO_BURST | MIPI_DSI_MODE_VIDEO_SYNC_PULSE; |
286 | |
287 | ctx->avdd = devm_regulator_get(dev, id: "avdd" ); |
288 | if (IS_ERR(ptr: ctx->avdd)) |
289 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->avdd), fmt: "Failed to request avdd regulator\n" ); |
290 | |
291 | ctx->avee = devm_regulator_get(dev, id: "avee" ); |
292 | if (IS_ERR(ptr: ctx->avee)) |
293 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->avee), fmt: "Failed to request avee regulator\n" ); |
294 | |
295 | ctx->vddi = devm_regulator_get(dev, id: "vddi" ); |
296 | if (IS_ERR(ptr: ctx->vddi)) |
297 | return dev_err_probe(dev, err: PTR_ERR(ptr: ctx->vddi), fmt: "Failed to request vddi regulator\n" ); |
298 | |
299 | drm_panel_init(panel: &ctx->panel, dev, funcs: &mantix_drm_funcs, |
300 | DRM_MODE_CONNECTOR_DSI); |
301 | |
302 | ret = drm_panel_of_backlight(panel: &ctx->panel); |
303 | if (ret) |
304 | return ret; |
305 | |
306 | drm_panel_add(panel: &ctx->panel); |
307 | |
308 | ret = mipi_dsi_attach(dsi); |
309 | if (ret < 0) { |
310 | dev_err(dev, "mipi_dsi_attach failed (%d). Is host ready?\n" , ret); |
311 | drm_panel_remove(panel: &ctx->panel); |
312 | return ret; |
313 | } |
314 | |
315 | dev_info(dev, "%ux%u@%u %ubpp dsi %udl - ready\n" , |
316 | ctx->default_mode->hdisplay, ctx->default_mode->vdisplay, |
317 | drm_mode_vrefresh(ctx->default_mode), |
318 | mipi_dsi_pixel_format_to_bpp(dsi->format), dsi->lanes); |
319 | |
320 | return 0; |
321 | } |
322 | |
323 | static void mantix_shutdown(struct mipi_dsi_device *dsi) |
324 | { |
325 | struct mantix *ctx = mipi_dsi_get_drvdata(dsi); |
326 | |
327 | drm_panel_unprepare(panel: &ctx->panel); |
328 | drm_panel_disable(panel: &ctx->panel); |
329 | } |
330 | |
331 | static void mantix_remove(struct mipi_dsi_device *dsi) |
332 | { |
333 | struct mantix *ctx = mipi_dsi_get_drvdata(dsi); |
334 | |
335 | mantix_shutdown(dsi); |
336 | |
337 | mipi_dsi_detach(dsi); |
338 | drm_panel_remove(panel: &ctx->panel); |
339 | } |
340 | |
341 | static const struct of_device_id mantix_of_match[] = { |
342 | { .compatible = "mantix,mlaf057we51-x" , .data = &default_mode_mantix }, |
343 | { .compatible = "ys,ys57pss36bh5gq" , .data = &default_mode_ys }, |
344 | { /* sentinel */ } |
345 | }; |
346 | MODULE_DEVICE_TABLE(of, mantix_of_match); |
347 | |
348 | static struct mipi_dsi_driver mantix_driver = { |
349 | .probe = mantix_probe, |
350 | .remove = mantix_remove, |
351 | .shutdown = mantix_shutdown, |
352 | .driver = { |
353 | .name = DRV_NAME, |
354 | .of_match_table = mantix_of_match, |
355 | }, |
356 | }; |
357 | module_mipi_dsi_driver(mantix_driver); |
358 | |
359 | MODULE_AUTHOR("Guido Günther <agx@sigxcpu.org>" ); |
360 | MODULE_DESCRIPTION("DRM driver for Mantix MLAF057WE51-X MIPI DSI panel" ); |
361 | MODULE_LICENSE("GPL v2" ); |
362 | |