1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * DRM driver for Sitronix ST7586 panels |
4 | * |
5 | * Copyright 2017 David Lechner <david@lechnology.com> |
6 | */ |
7 | |
8 | #include <linux/delay.h> |
9 | #include <linux/gpio/consumer.h> |
10 | #include <linux/module.h> |
11 | #include <linux/property.h> |
12 | #include <linux/spi/spi.h> |
13 | #include <video/mipi_display.h> |
14 | |
15 | #include <drm/drm_atomic_helper.h> |
16 | #include <drm/drm_damage_helper.h> |
17 | #include <drm/drm_drv.h> |
18 | #include <drm/drm_fb_dma_helper.h> |
19 | #include <drm/drm_fbdev_generic.h> |
20 | #include <drm/drm_format_helper.h> |
21 | #include <drm/drm_framebuffer.h> |
22 | #include <drm/drm_gem_atomic_helper.h> |
23 | #include <drm/drm_gem_dma_helper.h> |
24 | #include <drm/drm_gem_framebuffer_helper.h> |
25 | #include <drm/drm_managed.h> |
26 | #include <drm/drm_mipi_dbi.h> |
27 | #include <drm/drm_rect.h> |
28 | |
29 | /* controller-specific commands */ |
30 | #define ST7586_DISP_MODE_GRAY 0x38 |
31 | #define ST7586_DISP_MODE_MONO 0x39 |
32 | #define ST7586_ENABLE_DDRAM 0x3a |
33 | #define ST7586_SET_DISP_DUTY 0xb0 |
34 | #define ST7586_SET_PART_DISP 0xb4 |
35 | #define ST7586_SET_NLINE_INV 0xb5 |
36 | #define ST7586_SET_VOP 0xc0 |
37 | #define ST7586_SET_BIAS_SYSTEM 0xc3 |
38 | #define ST7586_SET_BOOST_LEVEL 0xc4 |
39 | #define ST7586_SET_VOP_OFFSET 0xc7 |
40 | #define ST7586_ENABLE_ANALOG 0xd0 |
41 | #define ST7586_AUTO_READ_CTRL 0xd7 |
42 | #define ST7586_OTP_RW_CTRL 0xe0 |
43 | #define ST7586_OTP_CTRL_OUT 0xe1 |
44 | #define ST7586_OTP_READ 0xe3 |
45 | |
46 | #define ST7586_DISP_CTRL_MX BIT(6) |
47 | #define ST7586_DISP_CTRL_MY BIT(7) |
48 | |
49 | /* |
50 | * The ST7586 controller has an unusual pixel format where 2bpp grayscale is |
51 | * packed 3 pixels per byte with the first two pixels using 3 bits and the 3rd |
52 | * pixel using only 2 bits. |
53 | * |
54 | * | D7 | D6 | D5 || | || 2bpp | |
55 | * | (D4) | (D3) | (D2) || D1 | D0 || GRAY | |
56 | * +------+------+------++------+------++------+ |
57 | * | 1 | 1 | 1 || 1 | 1 || 0 0 | black |
58 | * | 1 | 0 | 0 || 1 | 0 || 0 1 | dark gray |
59 | * | 0 | 1 | 0 || 0 | 1 || 1 0 | light gray |
60 | * | 0 | 0 | 0 || 0 | 0 || 1 1 | white |
61 | */ |
62 | |
63 | static const u8 st7586_lookup[] = { 0x7, 0x4, 0x2, 0x0 }; |
64 | |
65 | static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, |
66 | struct drm_framebuffer *fb, |
67 | struct drm_rect *clip) |
68 | { |
69 | size_t len = (clip->x2 - clip->x1) * (clip->y2 - clip->y1); |
70 | unsigned int x, y; |
71 | u8 *src, *buf, val; |
72 | struct iosys_map dst_map, vmap; |
73 | |
74 | buf = kmalloc(size: len, GFP_KERNEL); |
75 | if (!buf) |
76 | return; |
77 | |
78 | iosys_map_set_vaddr(map: &dst_map, vaddr: buf); |
79 | iosys_map_set_vaddr(map: &vmap, vaddr); |
80 | drm_fb_xrgb8888_to_gray8(dst: &dst_map, NULL, src: &vmap, fb, clip); |
81 | src = buf; |
82 | |
83 | for (y = clip->y1; y < clip->y2; y++) { |
84 | for (x = clip->x1; x < clip->x2; x += 3) { |
85 | val = st7586_lookup[*src++ >> 6] << 5; |
86 | val |= st7586_lookup[*src++ >> 6] << 2; |
87 | val |= st7586_lookup[*src++ >> 6] >> 1; |
88 | *dst++ = val; |
89 | } |
90 | } |
91 | |
92 | kfree(objp: buf); |
93 | } |
94 | |
95 | static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, |
96 | struct drm_rect *clip) |
97 | { |
98 | int ret; |
99 | |
100 | ret = drm_gem_fb_begin_cpu_access(fb, dir: DMA_FROM_DEVICE); |
101 | if (ret) |
102 | return ret; |
103 | |
104 | st7586_xrgb8888_to_gray332(dst, vaddr: src->vaddr, fb, clip); |
105 | |
106 | drm_gem_fb_end_cpu_access(fb, dir: DMA_FROM_DEVICE); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, |
112 | struct drm_rect *rect) |
113 | { |
114 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(drm: fb->dev); |
115 | struct mipi_dbi *dbi = &dbidev->dbi; |
116 | int start, end, ret = 0; |
117 | |
118 | /* 3 pixels per byte, so grow clip to nearest multiple of 3 */ |
119 | rect->x1 = rounddown(rect->x1, 3); |
120 | rect->x2 = roundup(rect->x2, 3); |
121 | |
122 | DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n" , fb->base.id, DRM_RECT_ARG(rect)); |
123 | |
124 | ret = st7586_buf_copy(dst: dbidev->tx_buf, src, fb, clip: rect); |
125 | if (ret) |
126 | goto err_msg; |
127 | |
128 | /* Pixels are packed 3 per byte */ |
129 | start = rect->x1 / 3; |
130 | end = rect->x2 / 3; |
131 | |
132 | mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, |
133 | (start >> 8) & 0xFF, start & 0xFF, |
134 | (end >> 8) & 0xFF, (end - 1) & 0xFF); |
135 | mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, |
136 | (rect->y1 >> 8) & 0xFF, rect->y1 & 0xFF, |
137 | (rect->y2 >> 8) & 0xFF, (rect->y2 - 1) & 0xFF); |
138 | |
139 | ret = mipi_dbi_command_buf(dbi, cmd: MIPI_DCS_WRITE_MEMORY_START, |
140 | data: (u8 *)dbidev->tx_buf, |
141 | len: (end - start) * (rect->y2 - rect->y1)); |
142 | err_msg: |
143 | if (ret) |
144 | dev_err_once(fb->dev->dev, "Failed to update display %d\n" , ret); |
145 | } |
146 | |
147 | static void st7586_pipe_update(struct drm_simple_display_pipe *pipe, |
148 | struct drm_plane_state *old_state) |
149 | { |
150 | struct drm_plane_state *state = pipe->plane.state; |
151 | struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); |
152 | struct drm_framebuffer *fb = state->fb; |
153 | struct drm_rect rect; |
154 | int idx; |
155 | |
156 | if (!pipe->crtc.state->active) |
157 | return; |
158 | |
159 | if (!drm_dev_enter(dev: fb->dev, idx: &idx)) |
160 | return; |
161 | |
162 | if (drm_atomic_helper_damage_merged(old_state, state, rect: &rect)) |
163 | st7586_fb_dirty(src: &shadow_plane_state->data[0], fb, rect: &rect); |
164 | |
165 | drm_dev_exit(idx); |
166 | } |
167 | |
168 | static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, |
169 | struct drm_crtc_state *crtc_state, |
170 | struct drm_plane_state *plane_state) |
171 | { |
172 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(drm: pipe->crtc.dev); |
173 | struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state: plane_state); |
174 | struct drm_framebuffer *fb = plane_state->fb; |
175 | struct mipi_dbi *dbi = &dbidev->dbi; |
176 | struct drm_rect rect = { |
177 | .x1 = 0, |
178 | .x2 = fb->width, |
179 | .y1 = 0, |
180 | .y2 = fb->height, |
181 | }; |
182 | int idx, ret; |
183 | u8 addr_mode; |
184 | |
185 | if (!drm_dev_enter(dev: pipe->crtc.dev, idx: &idx)) |
186 | return; |
187 | |
188 | DRM_DEBUG_KMS("\n" ); |
189 | |
190 | ret = mipi_dbi_poweron_reset(dbidev); |
191 | if (ret) |
192 | goto out_exit; |
193 | |
194 | mipi_dbi_command(dbi, ST7586_AUTO_READ_CTRL, 0x9f); |
195 | mipi_dbi_command(dbi, ST7586_OTP_RW_CTRL, 0x00); |
196 | |
197 | msleep(msecs: 10); |
198 | |
199 | mipi_dbi_command(dbi, ST7586_OTP_READ); |
200 | |
201 | msleep(msecs: 20); |
202 | |
203 | mipi_dbi_command(dbi, ST7586_OTP_CTRL_OUT); |
204 | mipi_dbi_command(dbi, MIPI_DCS_EXIT_SLEEP_MODE); |
205 | mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_OFF); |
206 | |
207 | msleep(msecs: 50); |
208 | |
209 | mipi_dbi_command(dbi, ST7586_SET_VOP_OFFSET, 0x00); |
210 | mipi_dbi_command(dbi, ST7586_SET_VOP, 0xe3, 0x00); |
211 | mipi_dbi_command(dbi, ST7586_SET_BIAS_SYSTEM, 0x02); |
212 | mipi_dbi_command(dbi, ST7586_SET_BOOST_LEVEL, 0x04); |
213 | mipi_dbi_command(dbi, ST7586_ENABLE_ANALOG, 0x1d); |
214 | mipi_dbi_command(dbi, ST7586_SET_NLINE_INV, 0x00); |
215 | mipi_dbi_command(dbi, ST7586_DISP_MODE_GRAY); |
216 | mipi_dbi_command(dbi, ST7586_ENABLE_DDRAM, 0x02); |
217 | |
218 | switch (dbidev->rotation) { |
219 | default: |
220 | addr_mode = 0x00; |
221 | break; |
222 | case 90: |
223 | addr_mode = ST7586_DISP_CTRL_MY; |
224 | break; |
225 | case 180: |
226 | addr_mode = ST7586_DISP_CTRL_MX | ST7586_DISP_CTRL_MY; |
227 | break; |
228 | case 270: |
229 | addr_mode = ST7586_DISP_CTRL_MX; |
230 | break; |
231 | } |
232 | mipi_dbi_command(dbi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode); |
233 | |
234 | mipi_dbi_command(dbi, ST7586_SET_DISP_DUTY, 0x7f); |
235 | mipi_dbi_command(dbi, ST7586_SET_PART_DISP, 0xa0); |
236 | mipi_dbi_command(dbi, MIPI_DCS_SET_PARTIAL_ROWS, 0x00, 0x00, 0x00, 0x77); |
237 | mipi_dbi_command(dbi, MIPI_DCS_EXIT_INVERT_MODE); |
238 | |
239 | msleep(msecs: 100); |
240 | |
241 | st7586_fb_dirty(src: &shadow_plane_state->data[0], fb, rect: &rect); |
242 | |
243 | mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); |
244 | out_exit: |
245 | drm_dev_exit(idx); |
246 | } |
247 | |
248 | static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe) |
249 | { |
250 | struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(drm: pipe->crtc.dev); |
251 | |
252 | /* |
253 | * This callback is not protected by drm_dev_enter/exit since we want to |
254 | * turn off the display on regular driver unload. It's highly unlikely |
255 | * that the underlying SPI controller is gone should this be called after |
256 | * unplug. |
257 | */ |
258 | |
259 | DRM_DEBUG_KMS("\n" ); |
260 | |
261 | mipi_dbi_command(&dbidev->dbi, MIPI_DCS_SET_DISPLAY_OFF); |
262 | } |
263 | |
264 | static const u32 st7586_formats[] = { |
265 | DRM_FORMAT_XRGB8888, |
266 | }; |
267 | |
268 | static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = { |
269 | .mode_valid = mipi_dbi_pipe_mode_valid, |
270 | .enable = st7586_pipe_enable, |
271 | .disable = st7586_pipe_disable, |
272 | .update = st7586_pipe_update, |
273 | .begin_fb_access = mipi_dbi_pipe_begin_fb_access, |
274 | .end_fb_access = mipi_dbi_pipe_end_fb_access, |
275 | .reset_plane = mipi_dbi_pipe_reset_plane, |
276 | .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, |
277 | .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state, |
278 | }; |
279 | |
280 | static const struct drm_display_mode st7586_mode = { |
281 | DRM_SIMPLE_MODE(178, 128, 37, 27), |
282 | }; |
283 | |
284 | DEFINE_DRM_GEM_DMA_FOPS(st7586_fops); |
285 | |
286 | static const struct drm_driver st7586_driver = { |
287 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
288 | .fops = &st7586_fops, |
289 | DRM_GEM_DMA_DRIVER_OPS_VMAP, |
290 | .debugfs_init = mipi_dbi_debugfs_init, |
291 | .name = "st7586" , |
292 | .desc = "Sitronix ST7586" , |
293 | .date = "20170801" , |
294 | .major = 1, |
295 | .minor = 0, |
296 | }; |
297 | |
298 | static const struct of_device_id st7586_of_match[] = { |
299 | { .compatible = "lego,ev3-lcd" }, |
300 | {}, |
301 | }; |
302 | MODULE_DEVICE_TABLE(of, st7586_of_match); |
303 | |
304 | static const struct spi_device_id st7586_id[] = { |
305 | { "ev3-lcd" , 0 }, |
306 | { }, |
307 | }; |
308 | MODULE_DEVICE_TABLE(spi, st7586_id); |
309 | |
310 | static int st7586_probe(struct spi_device *spi) |
311 | { |
312 | struct device *dev = &spi->dev; |
313 | struct mipi_dbi_dev *dbidev; |
314 | struct drm_device *drm; |
315 | struct mipi_dbi *dbi; |
316 | struct gpio_desc *a0; |
317 | u32 rotation = 0; |
318 | size_t bufsize; |
319 | int ret; |
320 | |
321 | dbidev = devm_drm_dev_alloc(dev, &st7586_driver, |
322 | struct mipi_dbi_dev, drm); |
323 | if (IS_ERR(ptr: dbidev)) |
324 | return PTR_ERR(ptr: dbidev); |
325 | |
326 | dbi = &dbidev->dbi; |
327 | drm = &dbidev->drm; |
328 | |
329 | bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay; |
330 | |
331 | dbi->reset = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
332 | if (IS_ERR(ptr: dbi->reset)) |
333 | return dev_err_probe(dev, err: PTR_ERR(ptr: dbi->reset), fmt: "Failed to get GPIO 'reset'\n" ); |
334 | |
335 | a0 = devm_gpiod_get(dev, con_id: "a0" , flags: GPIOD_OUT_LOW); |
336 | if (IS_ERR(ptr: a0)) |
337 | return dev_err_probe(dev, err: PTR_ERR(ptr: a0), fmt: "Failed to get GPIO 'a0'\n" ); |
338 | |
339 | device_property_read_u32(dev, propname: "rotation" , val: &rotation); |
340 | |
341 | ret = mipi_dbi_spi_init(spi, dbi, dc: a0); |
342 | if (ret) |
343 | return ret; |
344 | |
345 | /* Cannot read from this controller via SPI */ |
346 | dbi->read_commands = NULL; |
347 | |
348 | ret = mipi_dbi_dev_init_with_formats(dbidev, funcs: &st7586_pipe_funcs, |
349 | formats: st7586_formats, ARRAY_SIZE(st7586_formats), |
350 | mode: &st7586_mode, rotation, tx_buf_size: bufsize); |
351 | if (ret) |
352 | return ret; |
353 | |
354 | /* |
355 | * we are using 8-bit data, so we are not actually swapping anything, |
356 | * but setting mipi->swap_bytes makes mipi_dbi_typec3_command() do the |
357 | * right thing and not use 16-bit transfers (which results in swapped |
358 | * bytes on little-endian systems and causes out of order data to be |
359 | * sent to the display). |
360 | */ |
361 | dbi->swap_bytes = true; |
362 | |
363 | drm_mode_config_reset(dev: drm); |
364 | |
365 | ret = drm_dev_register(dev: drm, flags: 0); |
366 | if (ret) |
367 | return ret; |
368 | |
369 | spi_set_drvdata(spi, data: drm); |
370 | |
371 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 0); |
372 | |
373 | return 0; |
374 | } |
375 | |
376 | static void st7586_remove(struct spi_device *spi) |
377 | { |
378 | struct drm_device *drm = spi_get_drvdata(spi); |
379 | |
380 | drm_dev_unplug(dev: drm); |
381 | drm_atomic_helper_shutdown(dev: drm); |
382 | } |
383 | |
384 | static void st7586_shutdown(struct spi_device *spi) |
385 | { |
386 | drm_atomic_helper_shutdown(dev: spi_get_drvdata(spi)); |
387 | } |
388 | |
389 | static struct spi_driver st7586_spi_driver = { |
390 | .driver = { |
391 | .name = "st7586" , |
392 | .owner = THIS_MODULE, |
393 | .of_match_table = st7586_of_match, |
394 | }, |
395 | .id_table = st7586_id, |
396 | .probe = st7586_probe, |
397 | .remove = st7586_remove, |
398 | .shutdown = st7586_shutdown, |
399 | }; |
400 | module_spi_driver(st7586_spi_driver); |
401 | |
402 | MODULE_DESCRIPTION("Sitronix ST7586 DRM driver" ); |
403 | MODULE_AUTHOR("David Lechner <david@lechnology.com>" ); |
404 | MODULE_LICENSE("GPL" ); |
405 | |