1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * DRM driver for Pervasive Displays RePaper branded e-ink panels |
4 | * |
5 | * Copyright 2013-2017 Pervasive Displays, Inc. |
6 | * Copyright 2017 Noralf Trønnes |
7 | * |
8 | * The driver supports: |
9 | * Material Film: Aurora Mb (V231) |
10 | * Driver IC: G2 (eTC) |
11 | * |
12 | * The controller code was taken from the userspace driver: |
13 | * https://github.com/repaper/gratis |
14 | */ |
15 | |
16 | #include <linux/delay.h> |
17 | #include <linux/gpio/consumer.h> |
18 | #include <linux/module.h> |
19 | #include <linux/property.h> |
20 | #include <linux/sched/clock.h> |
21 | #include <linux/spi/spi.h> |
22 | #include <linux/thermal.h> |
23 | |
24 | #include <drm/drm_atomic_helper.h> |
25 | #include <drm/drm_connector.h> |
26 | #include <drm/drm_damage_helper.h> |
27 | #include <drm/drm_drv.h> |
28 | #include <drm/drm_fb_dma_helper.h> |
29 | #include <drm/drm_fbdev_generic.h> |
30 | #include <drm/drm_format_helper.h> |
31 | #include <drm/drm_framebuffer.h> |
32 | #include <drm/drm_gem_atomic_helper.h> |
33 | #include <drm/drm_gem_dma_helper.h> |
34 | #include <drm/drm_gem_framebuffer_helper.h> |
35 | #include <drm/drm_managed.h> |
36 | #include <drm/drm_modes.h> |
37 | #include <drm/drm_rect.h> |
38 | #include <drm/drm_probe_helper.h> |
39 | #include <drm/drm_simple_kms_helper.h> |
40 | |
41 | #define REPAPER_RID_G2_COG_ID 0x12 |
42 | |
43 | enum repaper_model { |
44 | /* 0 is reserved to avoid clashing with NULL */ |
45 | E1144CS021 = 1, |
46 | E1190CS021, |
47 | E2200CS021, |
48 | E2271CS021, |
49 | }; |
50 | |
51 | enum repaper_stage { /* Image pixel -> Display pixel */ |
52 | REPAPER_COMPENSATE, /* B -> W, W -> B (Current Image) */ |
53 | REPAPER_WHITE, /* B -> N, W -> W (Current Image) */ |
54 | REPAPER_INVERSE, /* B -> N, W -> B (New Image) */ |
55 | REPAPER_NORMAL /* B -> B, W -> W (New Image) */ |
56 | }; |
57 | |
58 | enum repaper_epd_border_byte { |
59 | REPAPER_BORDER_BYTE_NONE, |
60 | REPAPER_BORDER_BYTE_ZERO, |
61 | REPAPER_BORDER_BYTE_SET, |
62 | }; |
63 | |
64 | struct repaper_epd { |
65 | struct drm_device drm; |
66 | struct drm_simple_display_pipe pipe; |
67 | const struct drm_display_mode *mode; |
68 | struct drm_connector connector; |
69 | struct spi_device *spi; |
70 | |
71 | struct gpio_desc *panel_on; |
72 | struct gpio_desc *border; |
73 | struct gpio_desc *discharge; |
74 | struct gpio_desc *reset; |
75 | struct gpio_desc *busy; |
76 | |
77 | struct thermal_zone_device *thermal; |
78 | |
79 | unsigned int height; |
80 | unsigned int width; |
81 | unsigned int bytes_per_scan; |
82 | const u8 *channel_select; |
83 | unsigned int stage_time; |
84 | unsigned int factored_stage_time; |
85 | bool middle_scan; |
86 | bool pre_border_byte; |
87 | enum repaper_epd_border_byte border_byte; |
88 | |
89 | u8 *line_buffer; |
90 | void *current_frame; |
91 | |
92 | bool cleared; |
93 | bool partial; |
94 | }; |
95 | |
96 | static inline struct repaper_epd *drm_to_epd(struct drm_device *drm) |
97 | { |
98 | return container_of(drm, struct repaper_epd, drm); |
99 | } |
100 | |
101 | static int repaper_spi_transfer(struct spi_device *spi, u8 , |
102 | const void *tx, void *rx, size_t len) |
103 | { |
104 | void *txbuf = NULL, *rxbuf = NULL; |
105 | struct spi_transfer tr[2] = {}; |
106 | u8 *; |
107 | int ret; |
108 | |
109 | headerbuf = kmalloc(size: 1, GFP_KERNEL); |
110 | if (!headerbuf) |
111 | return -ENOMEM; |
112 | |
113 | headerbuf[0] = header; |
114 | tr[0].tx_buf = headerbuf; |
115 | tr[0].len = 1; |
116 | |
117 | /* Stack allocated tx? */ |
118 | if (tx && len <= 32) { |
119 | txbuf = kmemdup(p: tx, size: len, GFP_KERNEL); |
120 | if (!txbuf) { |
121 | ret = -ENOMEM; |
122 | goto out_free; |
123 | } |
124 | } |
125 | |
126 | if (rx) { |
127 | rxbuf = kmalloc(size: len, GFP_KERNEL); |
128 | if (!rxbuf) { |
129 | ret = -ENOMEM; |
130 | goto out_free; |
131 | } |
132 | } |
133 | |
134 | tr[1].tx_buf = txbuf ? txbuf : tx; |
135 | tr[1].rx_buf = rxbuf; |
136 | tr[1].len = len; |
137 | |
138 | ndelay(80); |
139 | ret = spi_sync_transfer(spi, xfers: tr, num_xfers: 2); |
140 | if (rx && !ret) |
141 | memcpy(rx, rxbuf, len); |
142 | |
143 | out_free: |
144 | kfree(objp: headerbuf); |
145 | kfree(objp: txbuf); |
146 | kfree(objp: rxbuf); |
147 | |
148 | return ret; |
149 | } |
150 | |
151 | static int repaper_write_buf(struct spi_device *spi, u8 reg, |
152 | const u8 *buf, size_t len) |
153 | { |
154 | int ret; |
155 | |
156 | ret = repaper_spi_transfer(spi, header: 0x70, tx: ®, NULL, len: 1); |
157 | if (ret) |
158 | return ret; |
159 | |
160 | return repaper_spi_transfer(spi, header: 0x72, tx: buf, NULL, len); |
161 | } |
162 | |
163 | static int repaper_write_val(struct spi_device *spi, u8 reg, u8 val) |
164 | { |
165 | return repaper_write_buf(spi, reg, buf: &val, len: 1); |
166 | } |
167 | |
168 | static int repaper_read_val(struct spi_device *spi, u8 reg) |
169 | { |
170 | int ret; |
171 | u8 val; |
172 | |
173 | ret = repaper_spi_transfer(spi, header: 0x70, tx: ®, NULL, len: 1); |
174 | if (ret) |
175 | return ret; |
176 | |
177 | ret = repaper_spi_transfer(spi, header: 0x73, NULL, rx: &val, len: 1); |
178 | |
179 | return ret ? ret : val; |
180 | } |
181 | |
182 | static int repaper_read_id(struct spi_device *spi) |
183 | { |
184 | int ret; |
185 | u8 id; |
186 | |
187 | ret = repaper_spi_transfer(spi, header: 0x71, NULL, rx: &id, len: 1); |
188 | |
189 | return ret ? ret : id; |
190 | } |
191 | |
192 | static void repaper_spi_mosi_low(struct spi_device *spi) |
193 | { |
194 | const u8 buf[1] = { 0 }; |
195 | |
196 | spi_write(spi, buf, len: 1); |
197 | } |
198 | |
199 | /* pixels on display are numbered from 1 so even is actually bits 1,3,5,... */ |
200 | static void repaper_even_pixels(struct repaper_epd *epd, u8 **pp, |
201 | const u8 *data, u8 fixed_value, const u8 *mask, |
202 | enum repaper_stage stage) |
203 | { |
204 | unsigned int b; |
205 | |
206 | for (b = 0; b < (epd->width / 8); b++) { |
207 | if (data) { |
208 | u8 pixels = data[b] & 0xaa; |
209 | u8 pixel_mask = 0xff; |
210 | u8 p1, p2, p3, p4; |
211 | |
212 | if (mask) { |
213 | pixel_mask = (mask[b] ^ pixels) & 0xaa; |
214 | pixel_mask |= pixel_mask >> 1; |
215 | } |
216 | |
217 | switch (stage) { |
218 | case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */ |
219 | pixels = 0xaa | ((pixels ^ 0xaa) >> 1); |
220 | break; |
221 | case REPAPER_WHITE: /* B -> N, W -> W (Current) */ |
222 | pixels = 0x55 + ((pixels ^ 0xaa) >> 1); |
223 | break; |
224 | case REPAPER_INVERSE: /* B -> N, W -> B (New) */ |
225 | pixels = 0x55 | (pixels ^ 0xaa); |
226 | break; |
227 | case REPAPER_NORMAL: /* B -> B, W -> W (New) */ |
228 | pixels = 0xaa | (pixels >> 1); |
229 | break; |
230 | } |
231 | |
232 | pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55); |
233 | p1 = (pixels >> 6) & 0x03; |
234 | p2 = (pixels >> 4) & 0x03; |
235 | p3 = (pixels >> 2) & 0x03; |
236 | p4 = (pixels >> 0) & 0x03; |
237 | pixels = (p1 << 0) | (p2 << 2) | (p3 << 4) | (p4 << 6); |
238 | *(*pp)++ = pixels; |
239 | } else { |
240 | *(*pp)++ = fixed_value; |
241 | } |
242 | } |
243 | } |
244 | |
245 | /* pixels on display are numbered from 1 so odd is actually bits 0,2,4,... */ |
246 | static void repaper_odd_pixels(struct repaper_epd *epd, u8 **pp, |
247 | const u8 *data, u8 fixed_value, const u8 *mask, |
248 | enum repaper_stage stage) |
249 | { |
250 | unsigned int b; |
251 | |
252 | for (b = epd->width / 8; b > 0; b--) { |
253 | if (data) { |
254 | u8 pixels = data[b - 1] & 0x55; |
255 | u8 pixel_mask = 0xff; |
256 | |
257 | if (mask) { |
258 | pixel_mask = (mask[b - 1] ^ pixels) & 0x55; |
259 | pixel_mask |= pixel_mask << 1; |
260 | } |
261 | |
262 | switch (stage) { |
263 | case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */ |
264 | pixels = 0xaa | (pixels ^ 0x55); |
265 | break; |
266 | case REPAPER_WHITE: /* B -> N, W -> W (Current) */ |
267 | pixels = 0x55 + (pixels ^ 0x55); |
268 | break; |
269 | case REPAPER_INVERSE: /* B -> N, W -> B (New) */ |
270 | pixels = 0x55 | ((pixels ^ 0x55) << 1); |
271 | break; |
272 | case REPAPER_NORMAL: /* B -> B, W -> W (New) */ |
273 | pixels = 0xaa | pixels; |
274 | break; |
275 | } |
276 | |
277 | pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55); |
278 | *(*pp)++ = pixels; |
279 | } else { |
280 | *(*pp)++ = fixed_value; |
281 | } |
282 | } |
283 | } |
284 | |
285 | /* interleave bits: (byte)76543210 -> (16 bit).7.6.5.4.3.2.1 */ |
286 | static inline u16 repaper_interleave_bits(u16 value) |
287 | { |
288 | value = (value | (value << 4)) & 0x0f0f; |
289 | value = (value | (value << 2)) & 0x3333; |
290 | value = (value | (value << 1)) & 0x5555; |
291 | |
292 | return value; |
293 | } |
294 | |
295 | /* pixels on display are numbered from 1 */ |
296 | static void repaper_all_pixels(struct repaper_epd *epd, u8 **pp, |
297 | const u8 *data, u8 fixed_value, const u8 *mask, |
298 | enum repaper_stage stage) |
299 | { |
300 | unsigned int b; |
301 | |
302 | for (b = epd->width / 8; b > 0; b--) { |
303 | if (data) { |
304 | u16 pixels = repaper_interleave_bits(value: data[b - 1]); |
305 | u16 pixel_mask = 0xffff; |
306 | |
307 | if (mask) { |
308 | pixel_mask = repaper_interleave_bits(value: mask[b - 1]); |
309 | |
310 | pixel_mask = (pixel_mask ^ pixels) & 0x5555; |
311 | pixel_mask |= pixel_mask << 1; |
312 | } |
313 | |
314 | switch (stage) { |
315 | case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */ |
316 | pixels = 0xaaaa | (pixels ^ 0x5555); |
317 | break; |
318 | case REPAPER_WHITE: /* B -> N, W -> W (Current) */ |
319 | pixels = 0x5555 + (pixels ^ 0x5555); |
320 | break; |
321 | case REPAPER_INVERSE: /* B -> N, W -> B (New) */ |
322 | pixels = 0x5555 | ((pixels ^ 0x5555) << 1); |
323 | break; |
324 | case REPAPER_NORMAL: /* B -> B, W -> W (New) */ |
325 | pixels = 0xaaaa | pixels; |
326 | break; |
327 | } |
328 | |
329 | pixels = (pixels & pixel_mask) | (~pixel_mask & 0x5555); |
330 | *(*pp)++ = pixels >> 8; |
331 | *(*pp)++ = pixels; |
332 | } else { |
333 | *(*pp)++ = fixed_value; |
334 | *(*pp)++ = fixed_value; |
335 | } |
336 | } |
337 | } |
338 | |
339 | /* output one line of scan and data bytes to the display */ |
340 | static void repaper_one_line(struct repaper_epd *epd, unsigned int line, |
341 | const u8 *data, u8 fixed_value, const u8 *mask, |
342 | enum repaper_stage stage) |
343 | { |
344 | u8 *p = epd->line_buffer; |
345 | unsigned int b; |
346 | |
347 | repaper_spi_mosi_low(spi: epd->spi); |
348 | |
349 | if (epd->pre_border_byte) |
350 | *p++ = 0x00; |
351 | |
352 | if (epd->middle_scan) { |
353 | /* data bytes */ |
354 | repaper_odd_pixels(epd, pp: &p, data, fixed_value, mask, stage); |
355 | |
356 | /* scan line */ |
357 | for (b = epd->bytes_per_scan; b > 0; b--) { |
358 | if (line / 4 == b - 1) |
359 | *p++ = 0x03 << (2 * (line & 0x03)); |
360 | else |
361 | *p++ = 0x00; |
362 | } |
363 | |
364 | /* data bytes */ |
365 | repaper_even_pixels(epd, pp: &p, data, fixed_value, mask, stage); |
366 | } else { |
367 | /* |
368 | * even scan line, but as lines on display are numbered from 1, |
369 | * line: 1,3,5,... |
370 | */ |
371 | for (b = 0; b < epd->bytes_per_scan; b++) { |
372 | if (0 != (line & 0x01) && line / 8 == b) |
373 | *p++ = 0xc0 >> (line & 0x06); |
374 | else |
375 | *p++ = 0x00; |
376 | } |
377 | |
378 | /* data bytes */ |
379 | repaper_all_pixels(epd, pp: &p, data, fixed_value, mask, stage); |
380 | |
381 | /* |
382 | * odd scan line, but as lines on display are numbered from 1, |
383 | * line: 0,2,4,6,... |
384 | */ |
385 | for (b = epd->bytes_per_scan; b > 0; b--) { |
386 | if (0 == (line & 0x01) && line / 8 == b - 1) |
387 | *p++ = 0x03 << (line & 0x06); |
388 | else |
389 | *p++ = 0x00; |
390 | } |
391 | } |
392 | |
393 | switch (epd->border_byte) { |
394 | case REPAPER_BORDER_BYTE_NONE: |
395 | break; |
396 | |
397 | case REPAPER_BORDER_BYTE_ZERO: |
398 | *p++ = 0x00; |
399 | break; |
400 | |
401 | case REPAPER_BORDER_BYTE_SET: |
402 | switch (stage) { |
403 | case REPAPER_COMPENSATE: |
404 | case REPAPER_WHITE: |
405 | case REPAPER_INVERSE: |
406 | *p++ = 0x00; |
407 | break; |
408 | case REPAPER_NORMAL: |
409 | *p++ = 0xaa; |
410 | break; |
411 | } |
412 | break; |
413 | } |
414 | |
415 | repaper_write_buf(spi: epd->spi, reg: 0x0a, buf: epd->line_buffer, |
416 | len: p - epd->line_buffer); |
417 | |
418 | /* Output data to panel */ |
419 | repaper_write_val(spi: epd->spi, reg: 0x02, val: 0x07); |
420 | |
421 | repaper_spi_mosi_low(spi: epd->spi); |
422 | } |
423 | |
424 | static void repaper_frame_fixed(struct repaper_epd *epd, u8 fixed_value, |
425 | enum repaper_stage stage) |
426 | { |
427 | unsigned int line; |
428 | |
429 | for (line = 0; line < epd->height; line++) |
430 | repaper_one_line(epd, line, NULL, fixed_value, NULL, stage); |
431 | } |
432 | |
433 | static void repaper_frame_data(struct repaper_epd *epd, const u8 *image, |
434 | const u8 *mask, enum repaper_stage stage) |
435 | { |
436 | unsigned int line; |
437 | |
438 | if (!mask) { |
439 | for (line = 0; line < epd->height; line++) { |
440 | repaper_one_line(epd, line, |
441 | data: &image[line * (epd->width / 8)], |
442 | fixed_value: 0, NULL, stage); |
443 | } |
444 | } else { |
445 | for (line = 0; line < epd->height; line++) { |
446 | size_t n = line * epd->width / 8; |
447 | |
448 | repaper_one_line(epd, line, data: &image[n], fixed_value: 0, mask: &mask[n], |
449 | stage); |
450 | } |
451 | } |
452 | } |
453 | |
454 | static void repaper_frame_fixed_repeat(struct repaper_epd *epd, u8 fixed_value, |
455 | enum repaper_stage stage) |
456 | { |
457 | u64 start = local_clock(); |
458 | u64 end = start + (epd->factored_stage_time * 1000 * 1000); |
459 | |
460 | do { |
461 | repaper_frame_fixed(epd, fixed_value, stage); |
462 | } while (local_clock() < end); |
463 | } |
464 | |
465 | static void repaper_frame_data_repeat(struct repaper_epd *epd, const u8 *image, |
466 | const u8 *mask, enum repaper_stage stage) |
467 | { |
468 | u64 start = local_clock(); |
469 | u64 end = start + (epd->factored_stage_time * 1000 * 1000); |
470 | |
471 | do { |
472 | repaper_frame_data(epd, image, mask, stage); |
473 | } while (local_clock() < end); |
474 | } |
475 | |
476 | static void repaper_get_temperature(struct repaper_epd *epd) |
477 | { |
478 | int ret, temperature = 0; |
479 | unsigned int factor10x; |
480 | |
481 | if (!epd->thermal) |
482 | return; |
483 | |
484 | ret = thermal_zone_get_temp(tz: epd->thermal, temp: &temperature); |
485 | if (ret) { |
486 | DRM_DEV_ERROR(&epd->spi->dev, "Failed to get temperature (%d)\n" , ret); |
487 | return; |
488 | } |
489 | |
490 | temperature /= 1000; |
491 | |
492 | if (temperature <= -10) |
493 | factor10x = 170; |
494 | else if (temperature <= -5) |
495 | factor10x = 120; |
496 | else if (temperature <= 5) |
497 | factor10x = 80; |
498 | else if (temperature <= 10) |
499 | factor10x = 40; |
500 | else if (temperature <= 15) |
501 | factor10x = 30; |
502 | else if (temperature <= 20) |
503 | factor10x = 20; |
504 | else if (temperature <= 40) |
505 | factor10x = 10; |
506 | else |
507 | factor10x = 7; |
508 | |
509 | epd->factored_stage_time = epd->stage_time * factor10x / 10; |
510 | } |
511 | |
512 | static int repaper_fb_dirty(struct drm_framebuffer *fb, |
513 | struct drm_format_conv_state *fmtcnv_state) |
514 | { |
515 | struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, plane: 0); |
516 | struct repaper_epd *epd = drm_to_epd(drm: fb->dev); |
517 | unsigned int dst_pitch = 0; |
518 | struct iosys_map dst, vmap; |
519 | struct drm_rect clip; |
520 | int idx, ret = 0; |
521 | u8 *buf = NULL; |
522 | |
523 | if (!drm_dev_enter(dev: fb->dev, idx: &idx)) |
524 | return -ENODEV; |
525 | |
526 | /* repaper can't do partial updates */ |
527 | clip.x1 = 0; |
528 | clip.x2 = fb->width; |
529 | clip.y1 = 0; |
530 | clip.y2 = fb->height; |
531 | |
532 | repaper_get_temperature(epd); |
533 | |
534 | DRM_DEBUG("Flushing [FB:%d] st=%ums\n" , fb->base.id, |
535 | epd->factored_stage_time); |
536 | |
537 | buf = kmalloc(size: fb->width * fb->height / 8, GFP_KERNEL); |
538 | if (!buf) { |
539 | ret = -ENOMEM; |
540 | goto out_exit; |
541 | } |
542 | |
543 | ret = drm_gem_fb_begin_cpu_access(fb, dir: DMA_FROM_DEVICE); |
544 | if (ret) |
545 | goto out_free; |
546 | |
547 | iosys_map_set_vaddr(map: &dst, vaddr: buf); |
548 | iosys_map_set_vaddr(map: &vmap, vaddr: dma_obj->vaddr); |
549 | drm_fb_xrgb8888_to_mono(dst: &dst, dst_pitch: &dst_pitch, src: &vmap, fb, clip: &clip, state: fmtcnv_state); |
550 | |
551 | drm_gem_fb_end_cpu_access(fb, dir: DMA_FROM_DEVICE); |
552 | |
553 | if (epd->partial) { |
554 | repaper_frame_data_repeat(epd, image: buf, mask: epd->current_frame, |
555 | stage: REPAPER_NORMAL); |
556 | } else if (epd->cleared) { |
557 | repaper_frame_data_repeat(epd, image: epd->current_frame, NULL, |
558 | stage: REPAPER_COMPENSATE); |
559 | repaper_frame_data_repeat(epd, image: epd->current_frame, NULL, |
560 | stage: REPAPER_WHITE); |
561 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_INVERSE); |
562 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_NORMAL); |
563 | |
564 | epd->partial = true; |
565 | } else { |
566 | /* Clear display (anything -> white) */ |
567 | repaper_frame_fixed_repeat(epd, fixed_value: 0xff, stage: REPAPER_COMPENSATE); |
568 | repaper_frame_fixed_repeat(epd, fixed_value: 0xff, stage: REPAPER_WHITE); |
569 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_INVERSE); |
570 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_NORMAL); |
571 | |
572 | /* Assuming a clear (white) screen output an image */ |
573 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_COMPENSATE); |
574 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_WHITE); |
575 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_INVERSE); |
576 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_NORMAL); |
577 | |
578 | epd->cleared = true; |
579 | epd->partial = true; |
580 | } |
581 | |
582 | memcpy(epd->current_frame, buf, fb->width * fb->height / 8); |
583 | |
584 | /* |
585 | * An extra frame write is needed if pixels are set in the bottom line, |
586 | * or else grey lines rises up from the pixels |
587 | */ |
588 | if (epd->pre_border_byte) { |
589 | unsigned int x; |
590 | |
591 | for (x = 0; x < (fb->width / 8); x++) |
592 | if (buf[x + (fb->width * (fb->height - 1) / 8)]) { |
593 | repaper_frame_data_repeat(epd, image: buf, |
594 | mask: epd->current_frame, |
595 | stage: REPAPER_NORMAL); |
596 | break; |
597 | } |
598 | } |
599 | |
600 | out_free: |
601 | kfree(objp: buf); |
602 | out_exit: |
603 | drm_dev_exit(idx); |
604 | |
605 | return ret; |
606 | } |
607 | |
608 | static void power_off(struct repaper_epd *epd) |
609 | { |
610 | /* Turn off power and all signals */ |
611 | gpiod_set_value_cansleep(desc: epd->reset, value: 0); |
612 | gpiod_set_value_cansleep(desc: epd->panel_on, value: 0); |
613 | if (epd->border) |
614 | gpiod_set_value_cansleep(desc: epd->border, value: 0); |
615 | |
616 | /* Ensure SPI MOSI and CLOCK are Low before CS Low */ |
617 | repaper_spi_mosi_low(spi: epd->spi); |
618 | |
619 | /* Discharge pulse */ |
620 | gpiod_set_value_cansleep(desc: epd->discharge, value: 1); |
621 | msleep(msecs: 150); |
622 | gpiod_set_value_cansleep(desc: epd->discharge, value: 0); |
623 | } |
624 | |
625 | static enum drm_mode_status repaper_pipe_mode_valid(struct drm_simple_display_pipe *pipe, |
626 | const struct drm_display_mode *mode) |
627 | { |
628 | struct drm_crtc *crtc = &pipe->crtc; |
629 | struct repaper_epd *epd = drm_to_epd(drm: crtc->dev); |
630 | |
631 | return drm_crtc_helper_mode_valid_fixed(crtc, mode, fixed_mode: epd->mode); |
632 | } |
633 | |
634 | static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe, |
635 | struct drm_crtc_state *crtc_state, |
636 | struct drm_plane_state *plane_state) |
637 | { |
638 | struct repaper_epd *epd = drm_to_epd(drm: pipe->crtc.dev); |
639 | struct spi_device *spi = epd->spi; |
640 | struct device *dev = &spi->dev; |
641 | bool dc_ok = false; |
642 | int i, ret, idx; |
643 | |
644 | if (!drm_dev_enter(dev: pipe->crtc.dev, idx: &idx)) |
645 | return; |
646 | |
647 | DRM_DEBUG_DRIVER("\n" ); |
648 | |
649 | /* Power up sequence */ |
650 | gpiod_set_value_cansleep(desc: epd->reset, value: 0); |
651 | gpiod_set_value_cansleep(desc: epd->panel_on, value: 0); |
652 | gpiod_set_value_cansleep(desc: epd->discharge, value: 0); |
653 | if (epd->border) |
654 | gpiod_set_value_cansleep(desc: epd->border, value: 0); |
655 | repaper_spi_mosi_low(spi); |
656 | usleep_range(min: 5000, max: 10000); |
657 | |
658 | gpiod_set_value_cansleep(desc: epd->panel_on, value: 1); |
659 | /* |
660 | * This delay comes from the repaper.org userspace driver, it's not |
661 | * mentioned in the datasheet. |
662 | */ |
663 | usleep_range(min: 10000, max: 15000); |
664 | gpiod_set_value_cansleep(desc: epd->reset, value: 1); |
665 | if (epd->border) |
666 | gpiod_set_value_cansleep(desc: epd->border, value: 1); |
667 | usleep_range(min: 5000, max: 10000); |
668 | gpiod_set_value_cansleep(desc: epd->reset, value: 0); |
669 | usleep_range(min: 5000, max: 10000); |
670 | gpiod_set_value_cansleep(desc: epd->reset, value: 1); |
671 | usleep_range(min: 5000, max: 10000); |
672 | |
673 | /* Wait for COG to become ready */ |
674 | for (i = 100; i > 0; i--) { |
675 | if (!gpiod_get_value_cansleep(desc: epd->busy)) |
676 | break; |
677 | |
678 | usleep_range(min: 10, max: 100); |
679 | } |
680 | |
681 | if (!i) { |
682 | DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n" ); |
683 | power_off(epd); |
684 | goto out_exit; |
685 | } |
686 | |
687 | repaper_read_id(spi); |
688 | ret = repaper_read_id(spi); |
689 | if (ret != REPAPER_RID_G2_COG_ID) { |
690 | if (ret < 0) |
691 | dev_err(dev, "failed to read chip (%d)\n" , ret); |
692 | else |
693 | dev_err(dev, "wrong COG ID 0x%02x\n" , ret); |
694 | power_off(epd); |
695 | goto out_exit; |
696 | } |
697 | |
698 | /* Disable OE */ |
699 | repaper_write_val(spi, reg: 0x02, val: 0x40); |
700 | |
701 | ret = repaper_read_val(spi, reg: 0x0f); |
702 | if (ret < 0 || !(ret & 0x80)) { |
703 | if (ret < 0) |
704 | DRM_DEV_ERROR(dev, "failed to read chip (%d)\n" , ret); |
705 | else |
706 | DRM_DEV_ERROR(dev, "panel is reported broken\n" ); |
707 | power_off(epd); |
708 | goto out_exit; |
709 | } |
710 | |
711 | /* Power saving mode */ |
712 | repaper_write_val(spi, reg: 0x0b, val: 0x02); |
713 | /* Channel select */ |
714 | repaper_write_buf(spi, reg: 0x01, buf: epd->channel_select, len: 8); |
715 | /* High power mode osc */ |
716 | repaper_write_val(spi, reg: 0x07, val: 0xd1); |
717 | /* Power setting */ |
718 | repaper_write_val(spi, reg: 0x08, val: 0x02); |
719 | /* Vcom level */ |
720 | repaper_write_val(spi, reg: 0x09, val: 0xc2); |
721 | /* Power setting */ |
722 | repaper_write_val(spi, reg: 0x04, val: 0x03); |
723 | /* Driver latch on */ |
724 | repaper_write_val(spi, reg: 0x03, val: 0x01); |
725 | /* Driver latch off */ |
726 | repaper_write_val(spi, reg: 0x03, val: 0x00); |
727 | usleep_range(min: 5000, max: 10000); |
728 | |
729 | /* Start chargepump */ |
730 | for (i = 0; i < 4; ++i) { |
731 | /* Charge pump positive voltage on - VGH/VDL on */ |
732 | repaper_write_val(spi, reg: 0x05, val: 0x01); |
733 | msleep(msecs: 240); |
734 | |
735 | /* Charge pump negative voltage on - VGL/VDL on */ |
736 | repaper_write_val(spi, reg: 0x05, val: 0x03); |
737 | msleep(msecs: 40); |
738 | |
739 | /* Charge pump Vcom on - Vcom driver on */ |
740 | repaper_write_val(spi, reg: 0x05, val: 0x0f); |
741 | msleep(msecs: 40); |
742 | |
743 | /* check DC/DC */ |
744 | ret = repaper_read_val(spi, reg: 0x0f); |
745 | if (ret < 0) { |
746 | DRM_DEV_ERROR(dev, "failed to read chip (%d)\n" , ret); |
747 | power_off(epd); |
748 | goto out_exit; |
749 | } |
750 | |
751 | if (ret & 0x40) { |
752 | dc_ok = true; |
753 | break; |
754 | } |
755 | } |
756 | |
757 | if (!dc_ok) { |
758 | DRM_DEV_ERROR(dev, "dc/dc failed\n" ); |
759 | power_off(epd); |
760 | goto out_exit; |
761 | } |
762 | |
763 | /* |
764 | * Output enable to disable |
765 | * The userspace driver sets this to 0x04, but the datasheet says 0x06 |
766 | */ |
767 | repaper_write_val(spi, reg: 0x02, val: 0x04); |
768 | |
769 | epd->partial = false; |
770 | out_exit: |
771 | drm_dev_exit(idx); |
772 | } |
773 | |
774 | static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe) |
775 | { |
776 | struct repaper_epd *epd = drm_to_epd(drm: pipe->crtc.dev); |
777 | struct spi_device *spi = epd->spi; |
778 | unsigned int line; |
779 | |
780 | /* |
781 | * This callback is not protected by drm_dev_enter/exit since we want to |
782 | * turn off the display on regular driver unload. It's highly unlikely |
783 | * that the underlying SPI controller is gone should this be called after |
784 | * unplug. |
785 | */ |
786 | |
787 | DRM_DEBUG_DRIVER("\n" ); |
788 | |
789 | /* Nothing frame */ |
790 | for (line = 0; line < epd->height; line++) |
791 | repaper_one_line(epd, line: 0x7fffu, NULL, fixed_value: 0x00, NULL, |
792 | stage: REPAPER_COMPENSATE); |
793 | |
794 | /* 2.7" */ |
795 | if (epd->border) { |
796 | /* Dummy line */ |
797 | repaper_one_line(epd, line: 0x7fffu, NULL, fixed_value: 0x00, NULL, |
798 | stage: REPAPER_COMPENSATE); |
799 | msleep(msecs: 25); |
800 | gpiod_set_value_cansleep(desc: epd->border, value: 0); |
801 | msleep(msecs: 200); |
802 | gpiod_set_value_cansleep(desc: epd->border, value: 1); |
803 | } else { |
804 | /* Border dummy line */ |
805 | repaper_one_line(epd, line: 0x7fffu, NULL, fixed_value: 0x00, NULL, |
806 | stage: REPAPER_NORMAL); |
807 | msleep(msecs: 200); |
808 | } |
809 | |
810 | /* not described in datasheet */ |
811 | repaper_write_val(spi, reg: 0x0b, val: 0x00); |
812 | /* Latch reset turn on */ |
813 | repaper_write_val(spi, reg: 0x03, val: 0x01); |
814 | /* Power off charge pump Vcom */ |
815 | repaper_write_val(spi, reg: 0x05, val: 0x03); |
816 | /* Power off charge pump neg voltage */ |
817 | repaper_write_val(spi, reg: 0x05, val: 0x01); |
818 | msleep(msecs: 120); |
819 | /* Discharge internal */ |
820 | repaper_write_val(spi, reg: 0x04, val: 0x80); |
821 | /* turn off all charge pumps */ |
822 | repaper_write_val(spi, reg: 0x05, val: 0x00); |
823 | /* Turn off osc */ |
824 | repaper_write_val(spi, reg: 0x07, val: 0x01); |
825 | msleep(msecs: 50); |
826 | |
827 | power_off(epd); |
828 | } |
829 | |
830 | static void repaper_pipe_update(struct drm_simple_display_pipe *pipe, |
831 | struct drm_plane_state *old_state) |
832 | { |
833 | struct drm_plane_state *state = pipe->plane.state; |
834 | struct drm_format_conv_state fmtcnv_state = DRM_FORMAT_CONV_STATE_INIT; |
835 | struct drm_rect rect; |
836 | |
837 | if (!pipe->crtc.state->active) |
838 | return; |
839 | |
840 | if (drm_atomic_helper_damage_merged(old_state, state, rect: &rect)) |
841 | repaper_fb_dirty(fb: state->fb, fmtcnv_state: &fmtcnv_state); |
842 | |
843 | drm_format_conv_state_release(state: &fmtcnv_state); |
844 | } |
845 | |
846 | static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = { |
847 | .mode_valid = repaper_pipe_mode_valid, |
848 | .enable = repaper_pipe_enable, |
849 | .disable = repaper_pipe_disable, |
850 | .update = repaper_pipe_update, |
851 | }; |
852 | |
853 | static int repaper_connector_get_modes(struct drm_connector *connector) |
854 | { |
855 | struct repaper_epd *epd = drm_to_epd(drm: connector->dev); |
856 | |
857 | return drm_connector_helper_get_modes_fixed(connector, fixed_mode: epd->mode); |
858 | } |
859 | |
860 | static const struct drm_connector_helper_funcs repaper_connector_hfuncs = { |
861 | .get_modes = repaper_connector_get_modes, |
862 | }; |
863 | |
864 | static const struct drm_connector_funcs repaper_connector_funcs = { |
865 | .reset = drm_atomic_helper_connector_reset, |
866 | .fill_modes = drm_helper_probe_single_connector_modes, |
867 | .destroy = drm_connector_cleanup, |
868 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
869 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
870 | }; |
871 | |
872 | static const struct drm_mode_config_funcs repaper_mode_config_funcs = { |
873 | .fb_create = drm_gem_fb_create_with_dirty, |
874 | .atomic_check = drm_atomic_helper_check, |
875 | .atomic_commit = drm_atomic_helper_commit, |
876 | }; |
877 | |
878 | static const uint32_t repaper_formats[] = { |
879 | DRM_FORMAT_XRGB8888, |
880 | }; |
881 | |
882 | static const struct drm_display_mode repaper_e1144cs021_mode = { |
883 | DRM_SIMPLE_MODE(128, 96, 29, 22), |
884 | }; |
885 | |
886 | static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00, |
887 | 0x00, 0x0f, 0xff, 0x00 }; |
888 | |
889 | static const struct drm_display_mode repaper_e1190cs021_mode = { |
890 | DRM_SIMPLE_MODE(144, 128, 36, 32), |
891 | }; |
892 | |
893 | static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03, |
894 | 0xfc, 0x00, 0x00, 0xff }; |
895 | |
896 | static const struct drm_display_mode repaper_e2200cs021_mode = { |
897 | DRM_SIMPLE_MODE(200, 96, 46, 22), |
898 | }; |
899 | |
900 | static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00, |
901 | 0x01, 0xff, 0xe0, 0x00 }; |
902 | |
903 | static const struct drm_display_mode repaper_e2271cs021_mode = { |
904 | DRM_SIMPLE_MODE(264, 176, 57, 38), |
905 | }; |
906 | |
907 | static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f, |
908 | 0xff, 0xfe, 0x00, 0x00 }; |
909 | |
910 | DEFINE_DRM_GEM_DMA_FOPS(repaper_fops); |
911 | |
912 | static const struct drm_driver repaper_driver = { |
913 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
914 | .fops = &repaper_fops, |
915 | DRM_GEM_DMA_DRIVER_OPS_VMAP, |
916 | .name = "repaper" , |
917 | .desc = "Pervasive Displays RePaper e-ink panels" , |
918 | .date = "20170405" , |
919 | .major = 1, |
920 | .minor = 0, |
921 | }; |
922 | |
923 | static const struct of_device_id repaper_of_match[] = { |
924 | { .compatible = "pervasive,e1144cs021" , .data = (void *)E1144CS021 }, |
925 | { .compatible = "pervasive,e1190cs021" , .data = (void *)E1190CS021 }, |
926 | { .compatible = "pervasive,e2200cs021" , .data = (void *)E2200CS021 }, |
927 | { .compatible = "pervasive,e2271cs021" , .data = (void *)E2271CS021 }, |
928 | {}, |
929 | }; |
930 | MODULE_DEVICE_TABLE(of, repaper_of_match); |
931 | |
932 | static const struct spi_device_id repaper_id[] = { |
933 | { "e1144cs021" , E1144CS021 }, |
934 | { "e1190cs021" , E1190CS021 }, |
935 | { "e2200cs021" , E2200CS021 }, |
936 | { "e2271cs021" , E2271CS021 }, |
937 | { }, |
938 | }; |
939 | MODULE_DEVICE_TABLE(spi, repaper_id); |
940 | |
941 | static int repaper_probe(struct spi_device *spi) |
942 | { |
943 | const struct drm_display_mode *mode; |
944 | const struct spi_device_id *spi_id; |
945 | struct device *dev = &spi->dev; |
946 | enum repaper_model model; |
947 | const char *thermal_zone; |
948 | struct repaper_epd *epd; |
949 | size_t line_buffer_size; |
950 | struct drm_device *drm; |
951 | const void *match; |
952 | int ret; |
953 | |
954 | match = device_get_match_data(dev); |
955 | if (match) { |
956 | model = (enum repaper_model)(uintptr_t)match; |
957 | } else { |
958 | spi_id = spi_get_device_id(sdev: spi); |
959 | model = (enum repaper_model)spi_id->driver_data; |
960 | } |
961 | |
962 | /* The SPI device is used to allocate dma memory */ |
963 | if (!dev->coherent_dma_mask) { |
964 | ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); |
965 | if (ret) { |
966 | dev_warn(dev, "Failed to set dma mask %d\n" , ret); |
967 | return ret; |
968 | } |
969 | } |
970 | |
971 | epd = devm_drm_dev_alloc(dev, &repaper_driver, |
972 | struct repaper_epd, drm); |
973 | if (IS_ERR(ptr: epd)) |
974 | return PTR_ERR(ptr: epd); |
975 | |
976 | drm = &epd->drm; |
977 | |
978 | ret = drmm_mode_config_init(dev: drm); |
979 | if (ret) |
980 | return ret; |
981 | drm->mode_config.funcs = &repaper_mode_config_funcs; |
982 | |
983 | epd->spi = spi; |
984 | |
985 | epd->panel_on = devm_gpiod_get(dev, con_id: "panel-on" , flags: GPIOD_OUT_LOW); |
986 | if (IS_ERR(ptr: epd->panel_on)) { |
987 | ret = PTR_ERR(ptr: epd->panel_on); |
988 | if (ret != -EPROBE_DEFER) |
989 | DRM_DEV_ERROR(dev, "Failed to get gpio 'panel-on'\n" ); |
990 | return ret; |
991 | } |
992 | |
993 | epd->discharge = devm_gpiod_get(dev, con_id: "discharge" , flags: GPIOD_OUT_LOW); |
994 | if (IS_ERR(ptr: epd->discharge)) { |
995 | ret = PTR_ERR(ptr: epd->discharge); |
996 | if (ret != -EPROBE_DEFER) |
997 | DRM_DEV_ERROR(dev, "Failed to get gpio 'discharge'\n" ); |
998 | return ret; |
999 | } |
1000 | |
1001 | epd->reset = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
1002 | if (IS_ERR(ptr: epd->reset)) { |
1003 | ret = PTR_ERR(ptr: epd->reset); |
1004 | if (ret != -EPROBE_DEFER) |
1005 | DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n" ); |
1006 | return ret; |
1007 | } |
1008 | |
1009 | epd->busy = devm_gpiod_get(dev, con_id: "busy" , flags: GPIOD_IN); |
1010 | if (IS_ERR(ptr: epd->busy)) { |
1011 | ret = PTR_ERR(ptr: epd->busy); |
1012 | if (ret != -EPROBE_DEFER) |
1013 | DRM_DEV_ERROR(dev, "Failed to get gpio 'busy'\n" ); |
1014 | return ret; |
1015 | } |
1016 | |
1017 | if (!device_property_read_string(dev, propname: "pervasive,thermal-zone" , |
1018 | val: &thermal_zone)) { |
1019 | epd->thermal = thermal_zone_get_zone_by_name(name: thermal_zone); |
1020 | if (IS_ERR(ptr: epd->thermal)) { |
1021 | DRM_DEV_ERROR(dev, "Failed to get thermal zone: %s\n" , thermal_zone); |
1022 | return PTR_ERR(ptr: epd->thermal); |
1023 | } |
1024 | } |
1025 | |
1026 | switch (model) { |
1027 | case E1144CS021: |
1028 | mode = &repaper_e1144cs021_mode; |
1029 | epd->channel_select = repaper_e1144cs021_cs; |
1030 | epd->stage_time = 480; |
1031 | epd->bytes_per_scan = 96 / 4; |
1032 | epd->middle_scan = true; /* data-scan-data */ |
1033 | epd->pre_border_byte = false; |
1034 | epd->border_byte = REPAPER_BORDER_BYTE_ZERO; |
1035 | break; |
1036 | |
1037 | case E1190CS021: |
1038 | mode = &repaper_e1190cs021_mode; |
1039 | epd->channel_select = repaper_e1190cs021_cs; |
1040 | epd->stage_time = 480; |
1041 | epd->bytes_per_scan = 128 / 4 / 2; |
1042 | epd->middle_scan = false; /* scan-data-scan */ |
1043 | epd->pre_border_byte = false; |
1044 | epd->border_byte = REPAPER_BORDER_BYTE_SET; |
1045 | break; |
1046 | |
1047 | case E2200CS021: |
1048 | mode = &repaper_e2200cs021_mode; |
1049 | epd->channel_select = repaper_e2200cs021_cs; |
1050 | epd->stage_time = 480; |
1051 | epd->bytes_per_scan = 96 / 4; |
1052 | epd->middle_scan = true; /* data-scan-data */ |
1053 | epd->pre_border_byte = true; |
1054 | epd->border_byte = REPAPER_BORDER_BYTE_NONE; |
1055 | break; |
1056 | |
1057 | case E2271CS021: |
1058 | epd->border = devm_gpiod_get(dev, con_id: "border" , flags: GPIOD_OUT_LOW); |
1059 | if (IS_ERR(ptr: epd->border)) { |
1060 | ret = PTR_ERR(ptr: epd->border); |
1061 | if (ret != -EPROBE_DEFER) |
1062 | DRM_DEV_ERROR(dev, "Failed to get gpio 'border'\n" ); |
1063 | return ret; |
1064 | } |
1065 | |
1066 | mode = &repaper_e2271cs021_mode; |
1067 | epd->channel_select = repaper_e2271cs021_cs; |
1068 | epd->stage_time = 630; |
1069 | epd->bytes_per_scan = 176 / 4; |
1070 | epd->middle_scan = true; /* data-scan-data */ |
1071 | epd->pre_border_byte = true; |
1072 | epd->border_byte = REPAPER_BORDER_BYTE_NONE; |
1073 | break; |
1074 | |
1075 | default: |
1076 | return -ENODEV; |
1077 | } |
1078 | |
1079 | epd->mode = mode; |
1080 | epd->width = mode->hdisplay; |
1081 | epd->height = mode->vdisplay; |
1082 | epd->factored_stage_time = epd->stage_time; |
1083 | |
1084 | line_buffer_size = 2 * epd->width / 8 + epd->bytes_per_scan + 2; |
1085 | epd->line_buffer = devm_kzalloc(dev, size: line_buffer_size, GFP_KERNEL); |
1086 | if (!epd->line_buffer) |
1087 | return -ENOMEM; |
1088 | |
1089 | epd->current_frame = devm_kzalloc(dev, size: epd->width * epd->height / 8, |
1090 | GFP_KERNEL); |
1091 | if (!epd->current_frame) |
1092 | return -ENOMEM; |
1093 | |
1094 | drm->mode_config.min_width = mode->hdisplay; |
1095 | drm->mode_config.max_width = mode->hdisplay; |
1096 | drm->mode_config.min_height = mode->vdisplay; |
1097 | drm->mode_config.max_height = mode->vdisplay; |
1098 | |
1099 | drm_connector_helper_add(connector: &epd->connector, funcs: &repaper_connector_hfuncs); |
1100 | ret = drm_connector_init(dev: drm, connector: &epd->connector, funcs: &repaper_connector_funcs, |
1101 | DRM_MODE_CONNECTOR_SPI); |
1102 | if (ret) |
1103 | return ret; |
1104 | |
1105 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &epd->pipe, funcs: &repaper_pipe_funcs, |
1106 | formats: repaper_formats, ARRAY_SIZE(repaper_formats), |
1107 | NULL, connector: &epd->connector); |
1108 | if (ret) |
1109 | return ret; |
1110 | |
1111 | drm_mode_config_reset(dev: drm); |
1112 | |
1113 | ret = drm_dev_register(dev: drm, flags: 0); |
1114 | if (ret) |
1115 | return ret; |
1116 | |
1117 | spi_set_drvdata(spi, data: drm); |
1118 | |
1119 | DRM_DEBUG_DRIVER("SPI speed: %uMHz\n" , spi->max_speed_hz / 1000000); |
1120 | |
1121 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 0); |
1122 | |
1123 | return 0; |
1124 | } |
1125 | |
1126 | static void repaper_remove(struct spi_device *spi) |
1127 | { |
1128 | struct drm_device *drm = spi_get_drvdata(spi); |
1129 | |
1130 | drm_dev_unplug(dev: drm); |
1131 | drm_atomic_helper_shutdown(dev: drm); |
1132 | } |
1133 | |
1134 | static void repaper_shutdown(struct spi_device *spi) |
1135 | { |
1136 | drm_atomic_helper_shutdown(dev: spi_get_drvdata(spi)); |
1137 | } |
1138 | |
1139 | static struct spi_driver repaper_spi_driver = { |
1140 | .driver = { |
1141 | .name = "repaper" , |
1142 | .of_match_table = repaper_of_match, |
1143 | }, |
1144 | .id_table = repaper_id, |
1145 | .probe = repaper_probe, |
1146 | .remove = repaper_remove, |
1147 | .shutdown = repaper_shutdown, |
1148 | }; |
1149 | module_spi_driver(repaper_spi_driver); |
1150 | |
1151 | MODULE_DESCRIPTION("Pervasive Displays RePaper DRM driver" ); |
1152 | MODULE_AUTHOR("Noralf Trønnes" ); |
1153 | MODULE_LICENSE("GPL" ); |
1154 | |