1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * DRM driver for MIPI DBI compatible display panels |
4 | * |
5 | * Copyright 2022 Noralf Trønnes |
6 | */ |
7 | |
8 | #include <linux/backlight.h> |
9 | #include <linux/delay.h> |
10 | #include <linux/firmware.h> |
11 | #include <linux/gpio/consumer.h> |
12 | #include <linux/module.h> |
13 | #include <linux/property.h> |
14 | #include <linux/regulator/consumer.h> |
15 | #include <linux/spi/spi.h> |
16 | |
17 | #include <drm/drm_atomic_helper.h> |
18 | #include <drm/drm_drv.h> |
19 | #include <drm/drm_fbdev_generic.h> |
20 | #include <drm/drm_gem_atomic_helper.h> |
21 | #include <drm/drm_gem_dma_helper.h> |
22 | #include <drm/drm_managed.h> |
23 | #include <drm/drm_mipi_dbi.h> |
24 | #include <drm/drm_modes.h> |
25 | #include <drm/drm_modeset_helper.h> |
26 | |
27 | #include <video/mipi_display.h> |
28 | |
29 | static const u8 panel_mipi_dbi_magic[15] = { 'M', 'I', 'P', 'I', ' ', 'D', 'B', 'I', |
30 | 0, 0, 0, 0, 0, 0, 0 }; |
31 | |
32 | /* |
33 | * The display controller configuration is stored in a firmware file. |
34 | * The Device Tree 'compatible' property value with a '.bin' suffix is passed |
35 | * to request_firmware() to fetch this file. |
36 | */ |
37 | struct panel_mipi_dbi_config { |
38 | /* Magic string: panel_mipi_dbi_magic */ |
39 | u8 magic[15]; |
40 | |
41 | /* Config file format version */ |
42 | u8 file_format_version; |
43 | |
44 | /* |
45 | * MIPI commands to execute when the display pipeline is enabled. |
46 | * This is used to configure the display controller. |
47 | * |
48 | * The commands are stored in a byte array with the format: |
49 | * command, num_parameters, [ parameter, ...], command, ... |
50 | * |
51 | * Some commands require a pause before the next command can be received. |
52 | * Inserting a delay in the command sequence is done by using the NOP command with one |
53 | * parameter: delay in miliseconds (the No Operation command is part of the MIPI Display |
54 | * Command Set where it has no parameters). |
55 | * |
56 | * Example: |
57 | * command 0x11 |
58 | * sleep 120ms |
59 | * command 0xb1 parameters 0x01, 0x2c, 0x2d |
60 | * command 0x29 |
61 | * |
62 | * Byte sequence: |
63 | * 0x11 0x00 |
64 | * 0x00 0x01 0x78 |
65 | * 0xb1 0x03 0x01 0x2c 0x2d |
66 | * 0x29 0x00 |
67 | */ |
68 | u8 commands[]; |
69 | }; |
70 | |
71 | struct panel_mipi_dbi_commands { |
72 | const u8 *buf; |
73 | size_t len; |
74 | }; |
75 | |
76 | static struct panel_mipi_dbi_commands * |
77 | panel_mipi_dbi_check_commands(struct device *dev, const struct firmware *fw) |
78 | { |
79 | const struct panel_mipi_dbi_config *config = (struct panel_mipi_dbi_config *)fw->data; |
80 | struct panel_mipi_dbi_commands *commands; |
81 | size_t size = fw->size, commands_len; |
82 | unsigned int i = 0; |
83 | |
84 | if (size < sizeof(*config) + 2) { /* At least 1 command */ |
85 | dev_err(dev, "config: file size=%zu is too small\n" , size); |
86 | return ERR_PTR(error: -EINVAL); |
87 | } |
88 | |
89 | if (memcmp(p: config->magic, q: panel_mipi_dbi_magic, size: sizeof(config->magic))) { |
90 | dev_err(dev, "config: Bad magic: %15ph\n" , config->magic); |
91 | return ERR_PTR(error: -EINVAL); |
92 | } |
93 | |
94 | if (config->file_format_version != 1) { |
95 | dev_err(dev, "config: version=%u is not supported\n" , config->file_format_version); |
96 | return ERR_PTR(error: -EINVAL); |
97 | } |
98 | |
99 | drm_dev_dbg(dev, DRM_UT_DRIVER, "size=%zu version=%u\n" , size, config->file_format_version); |
100 | |
101 | commands_len = size - sizeof(*config); |
102 | |
103 | while ((i + 1) < commands_len) { |
104 | u8 command = config->commands[i++]; |
105 | u8 num_parameters = config->commands[i++]; |
106 | const u8 *parameters = &config->commands[i]; |
107 | |
108 | i += num_parameters; |
109 | if (i > commands_len) { |
110 | dev_err(dev, "config: command=0x%02x num_parameters=%u overflows\n" , |
111 | command, num_parameters); |
112 | return ERR_PTR(error: -EINVAL); |
113 | } |
114 | |
115 | if (command == 0x00 && num_parameters == 1) |
116 | drm_dev_dbg(dev, DRM_UT_DRIVER, "sleep %ums\n" , parameters[0]); |
117 | else |
118 | drm_dev_dbg(dev, DRM_UT_DRIVER, "command %02x %*ph\n" , |
119 | command, num_parameters, parameters); |
120 | } |
121 | |
122 | if (i != commands_len) { |
123 | dev_err(dev, "config: malformed command array\n" ); |
124 | return ERR_PTR(error: -EINVAL); |
125 | } |
126 | |
127 | commands = devm_kzalloc(dev, size: sizeof(*commands), GFP_KERNEL); |
128 | if (!commands) |
129 | return ERR_PTR(error: -ENOMEM); |
130 | |
131 | commands->len = commands_len; |
132 | commands->buf = devm_kmemdup(dev, src: config->commands, len: commands->len, GFP_KERNEL); |
133 | if (!commands->buf) |
134 | return ERR_PTR(error: -ENOMEM); |
135 | |
136 | return commands; |
137 | } |
138 | |
139 | static struct panel_mipi_dbi_commands *panel_mipi_dbi_commands_from_fw(struct device *dev) |
140 | { |
141 | struct panel_mipi_dbi_commands *commands; |
142 | const struct firmware *fw; |
143 | const char *compatible; |
144 | char fw_name[40]; |
145 | int ret; |
146 | |
147 | ret = of_property_read_string_index(np: dev->of_node, propname: "compatible" , index: 0, output: &compatible); |
148 | if (ret) |
149 | return ERR_PTR(error: ret); |
150 | |
151 | snprintf(buf: fw_name, size: sizeof(fw_name), fmt: "%s.bin" , compatible); |
152 | ret = request_firmware(fw: &fw, name: fw_name, device: dev); |
153 | if (ret) { |
154 | dev_err(dev, "No config file found for compatible '%s' (error=%d)\n" , |
155 | compatible, ret); |
156 | |
157 | return ERR_PTR(error: ret); |
158 | } |
159 | |
160 | commands = panel_mipi_dbi_check_commands(dev, fw); |
161 | release_firmware(fw); |
162 | |
163 | return commands; |
164 | } |
165 | |
166 | static void panel_mipi_dbi_commands_execute(struct mipi_dbi *dbi, |
167 | struct panel_mipi_dbi_commands *commands) |
168 | { |
169 | unsigned int i = 0; |
170 | |
171 | if (!commands) |
172 | return; |
173 | |
174 | while (i < commands->len) { |
175 | u8 command = commands->buf[i++]; |
176 | u8 num_parameters = commands->buf[i++]; |
177 | const u8 *parameters = &commands->buf[i]; |
178 | |
179 | if (command == 0x00 && num_parameters == 1) |
180 | msleep(msecs: parameters[0]); |
181 | else if (num_parameters) |
182 | mipi_dbi_command_stackbuf(dbi, cmd: command, data: parameters, len: num_parameters); |
183 | else |
184 | mipi_dbi_command(dbi, command); |
185 | |
186 | i += num_parameters; |
187 | } |
188 | } |
189 | |
190 | static void panel_mipi_dbi_enable(struct drm_simple_display_pipe *pipe, |
191 | struct drm_crtc_state *crtc_state, |
192 | struct drm_plane_state *plane_state) |
193 | { |
194 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(drm: pipe->crtc.dev); |
195 | struct mipi_dbi *dbi = &dbidev->dbi; |
196 | int ret, idx; |
197 | |
198 | if (!drm_dev_enter(dev: pipe->crtc.dev, idx: &idx)) |
199 | return; |
200 | |
201 | drm_dbg(pipe->crtc.dev, "\n" ); |
202 | |
203 | ret = mipi_dbi_poweron_conditional_reset(dbidev); |
204 | if (ret < 0) |
205 | goto out_exit; |
206 | if (!ret) |
207 | panel_mipi_dbi_commands_execute(dbi, commands: dbidev->driver_private); |
208 | |
209 | mipi_dbi_enable_flush(dbidev, crtc_state, plan_state: plane_state); |
210 | out_exit: |
211 | drm_dev_exit(idx); |
212 | } |
213 | |
214 | static const struct drm_simple_display_pipe_funcs panel_mipi_dbi_pipe_funcs = { |
215 | DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(panel_mipi_dbi_enable), |
216 | }; |
217 | |
218 | DEFINE_DRM_GEM_DMA_FOPS(panel_mipi_dbi_fops); |
219 | |
220 | static const struct drm_driver panel_mipi_dbi_driver = { |
221 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
222 | .fops = &panel_mipi_dbi_fops, |
223 | DRM_GEM_DMA_DRIVER_OPS_VMAP, |
224 | .debugfs_init = mipi_dbi_debugfs_init, |
225 | .name = "panel-mipi-dbi" , |
226 | .desc = "MIPI DBI compatible display panel" , |
227 | .date = "20220103" , |
228 | .major = 1, |
229 | .minor = 0, |
230 | }; |
231 | |
232 | static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev, struct drm_display_mode *mode) |
233 | { |
234 | struct device *dev = dbidev->drm.dev; |
235 | u16 hback_porch, vback_porch; |
236 | int ret; |
237 | |
238 | ret = of_get_drm_panel_display_mode(np: dev->of_node, dmode: mode, NULL); |
239 | if (ret) { |
240 | dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n" , dev->of_node, ret); |
241 | return ret; |
242 | } |
243 | |
244 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; |
245 | |
246 | hback_porch = mode->htotal - mode->hsync_end; |
247 | vback_porch = mode->vtotal - mode->vsync_end; |
248 | |
249 | /* |
250 | * Make sure width and height are set and that only back porch and |
251 | * pixelclock are set in the other timing values. Also check that |
252 | * width and height don't exceed the 16-bit value specified by MIPI DCS. |
253 | */ |
254 | if (!mode->hdisplay || !mode->vdisplay || mode->flags || |
255 | mode->hsync_end > mode->hdisplay || (hback_porch + mode->hdisplay) > 0xffff || |
256 | mode->vsync_end > mode->vdisplay || (vback_porch + mode->vdisplay) > 0xffff) { |
257 | dev_err(dev, "%pOF: panel-timing out of bounds\n" , dev->of_node); |
258 | return -EINVAL; |
259 | } |
260 | |
261 | /* The driver doesn't use the pixel clock but it is mandatory so fake one if not set */ |
262 | if (!mode->clock) |
263 | mode->clock = mode->htotal * mode->vtotal * 60 / 1000; |
264 | |
265 | dbidev->top_offset = vback_porch; |
266 | dbidev->left_offset = hback_porch; |
267 | |
268 | return 0; |
269 | } |
270 | |
271 | static int panel_mipi_dbi_spi_probe(struct spi_device *spi) |
272 | { |
273 | struct device *dev = &spi->dev; |
274 | struct drm_display_mode mode; |
275 | struct mipi_dbi_dev *dbidev; |
276 | struct drm_device *drm; |
277 | struct mipi_dbi *dbi; |
278 | struct gpio_desc *dc; |
279 | int ret; |
280 | |
281 | dbidev = devm_drm_dev_alloc(dev, &panel_mipi_dbi_driver, struct mipi_dbi_dev, drm); |
282 | if (IS_ERR(ptr: dbidev)) |
283 | return PTR_ERR(ptr: dbidev); |
284 | |
285 | dbi = &dbidev->dbi; |
286 | drm = &dbidev->drm; |
287 | |
288 | ret = panel_mipi_dbi_get_mode(dbidev, mode: &mode); |
289 | if (ret) |
290 | return ret; |
291 | |
292 | dbidev->regulator = devm_regulator_get(dev, id: "power" ); |
293 | if (IS_ERR(ptr: dbidev->regulator)) |
294 | return dev_err_probe(dev, err: PTR_ERR(ptr: dbidev->regulator), |
295 | fmt: "Failed to get regulator 'power'\n" ); |
296 | |
297 | dbidev->io_regulator = devm_regulator_get(dev, id: "io" ); |
298 | if (IS_ERR(ptr: dbidev->io_regulator)) |
299 | return dev_err_probe(dev, err: PTR_ERR(ptr: dbidev->io_regulator), |
300 | fmt: "Failed to get regulator 'io'\n" ); |
301 | |
302 | dbidev->backlight = devm_of_find_backlight(dev); |
303 | if (IS_ERR(ptr: dbidev->backlight)) |
304 | return dev_err_probe(dev, err: PTR_ERR(ptr: dbidev->backlight), fmt: "Failed to get backlight\n" ); |
305 | |
306 | dbi->reset = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
307 | if (IS_ERR(ptr: dbi->reset)) |
308 | return dev_err_probe(dev, err: PTR_ERR(ptr: dbi->reset), fmt: "Failed to get GPIO 'reset'\n" ); |
309 | |
310 | /* Multiple panels can share the "dc" GPIO, but only if they are on the same SPI bus! */ |
311 | dc = devm_gpiod_get_optional(dev, con_id: "dc" , flags: GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); |
312 | if (IS_ERR(ptr: dc)) |
313 | return dev_err_probe(dev, err: PTR_ERR(ptr: dc), fmt: "Failed to get GPIO 'dc'\n" ); |
314 | |
315 | ret = mipi_dbi_spi_init(spi, dbi, dc); |
316 | if (ret) |
317 | return ret; |
318 | |
319 | if (device_property_present(dev, propname: "write-only" )) |
320 | dbi->read_commands = NULL; |
321 | |
322 | dbidev->driver_private = panel_mipi_dbi_commands_from_fw(dev); |
323 | if (IS_ERR(ptr: dbidev->driver_private)) |
324 | return PTR_ERR(ptr: dbidev->driver_private); |
325 | |
326 | ret = mipi_dbi_dev_init(dbidev, funcs: &panel_mipi_dbi_pipe_funcs, mode: &mode, rotation: 0); |
327 | if (ret) |
328 | return ret; |
329 | |
330 | drm_mode_config_reset(dev: drm); |
331 | |
332 | ret = drm_dev_register(dev: drm, flags: 0); |
333 | if (ret) |
334 | return ret; |
335 | |
336 | spi_set_drvdata(spi, data: drm); |
337 | |
338 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 0); |
339 | |
340 | return 0; |
341 | } |
342 | |
343 | static void panel_mipi_dbi_spi_remove(struct spi_device *spi) |
344 | { |
345 | struct drm_device *drm = spi_get_drvdata(spi); |
346 | |
347 | drm_dev_unplug(dev: drm); |
348 | drm_atomic_helper_shutdown(dev: drm); |
349 | } |
350 | |
351 | static void panel_mipi_dbi_spi_shutdown(struct spi_device *spi) |
352 | { |
353 | drm_atomic_helper_shutdown(dev: spi_get_drvdata(spi)); |
354 | } |
355 | |
356 | static int __maybe_unused panel_mipi_dbi_pm_suspend(struct device *dev) |
357 | { |
358 | return drm_mode_config_helper_suspend(dev: dev_get_drvdata(dev)); |
359 | } |
360 | |
361 | static int __maybe_unused panel_mipi_dbi_pm_resume(struct device *dev) |
362 | { |
363 | drm_mode_config_helper_resume(dev: dev_get_drvdata(dev)); |
364 | |
365 | return 0; |
366 | } |
367 | |
368 | static const struct dev_pm_ops panel_mipi_dbi_pm_ops = { |
369 | SET_SYSTEM_SLEEP_PM_OPS(panel_mipi_dbi_pm_suspend, panel_mipi_dbi_pm_resume) |
370 | }; |
371 | |
372 | static const struct of_device_id panel_mipi_dbi_spi_of_match[] = { |
373 | { .compatible = "panel-mipi-dbi-spi" }, |
374 | {}, |
375 | }; |
376 | MODULE_DEVICE_TABLE(of, panel_mipi_dbi_spi_of_match); |
377 | |
378 | static const struct spi_device_id panel_mipi_dbi_spi_id[] = { |
379 | { "panel-mipi-dbi-spi" , 0 }, |
380 | { }, |
381 | }; |
382 | MODULE_DEVICE_TABLE(spi, panel_mipi_dbi_spi_id); |
383 | |
384 | static struct spi_driver panel_mipi_dbi_spi_driver = { |
385 | .driver = { |
386 | .name = "panel-mipi-dbi-spi" , |
387 | .owner = THIS_MODULE, |
388 | .of_match_table = panel_mipi_dbi_spi_of_match, |
389 | .pm = &panel_mipi_dbi_pm_ops, |
390 | }, |
391 | .id_table = panel_mipi_dbi_spi_id, |
392 | .probe = panel_mipi_dbi_spi_probe, |
393 | .remove = panel_mipi_dbi_spi_remove, |
394 | .shutdown = panel_mipi_dbi_spi_shutdown, |
395 | }; |
396 | module_spi_driver(panel_mipi_dbi_spi_driver); |
397 | |
398 | MODULE_DESCRIPTION("MIPI DBI compatible display panel driver" ); |
399 | MODULE_AUTHOR("Noralf Trønnes" ); |
400 | MODULE_LICENSE("GPL" ); |
401 | |