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