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 | { |
514 | struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, plane: 0); |
515 | struct repaper_epd *epd = drm_to_epd(drm: fb->dev); |
516 | unsigned int dst_pitch = 0; |
517 | struct iosys_map dst, vmap; |
518 | struct drm_rect clip; |
519 | int idx, ret = 0; |
520 | u8 *buf = NULL; |
521 | |
522 | if (!drm_dev_enter(dev: fb->dev, idx: &idx)) |
523 | return -ENODEV; |
524 | |
525 | /* repaper can't do partial updates */ |
526 | clip.x1 = 0; |
527 | clip.x2 = fb->width; |
528 | clip.y1 = 0; |
529 | clip.y2 = fb->height; |
530 | |
531 | repaper_get_temperature(epd); |
532 | |
533 | DRM_DEBUG("Flushing [FB:%d] st=%ums\n" , fb->base.id, |
534 | epd->factored_stage_time); |
535 | |
536 | buf = kmalloc(size: fb->width * fb->height / 8, GFP_KERNEL); |
537 | if (!buf) { |
538 | ret = -ENOMEM; |
539 | goto out_exit; |
540 | } |
541 | |
542 | ret = drm_gem_fb_begin_cpu_access(fb, dir: DMA_FROM_DEVICE); |
543 | if (ret) |
544 | goto out_free; |
545 | |
546 | iosys_map_set_vaddr(map: &dst, vaddr: buf); |
547 | iosys_map_set_vaddr(map: &vmap, vaddr: dma_obj->vaddr); |
548 | drm_fb_xrgb8888_to_mono(dst: &dst, dst_pitch: &dst_pitch, src: &vmap, fb, clip: &clip); |
549 | |
550 | drm_gem_fb_end_cpu_access(fb, dir: DMA_FROM_DEVICE); |
551 | |
552 | if (epd->partial) { |
553 | repaper_frame_data_repeat(epd, image: buf, mask: epd->current_frame, |
554 | stage: REPAPER_NORMAL); |
555 | } else if (epd->cleared) { |
556 | repaper_frame_data_repeat(epd, image: epd->current_frame, NULL, |
557 | stage: REPAPER_COMPENSATE); |
558 | repaper_frame_data_repeat(epd, image: epd->current_frame, NULL, |
559 | stage: REPAPER_WHITE); |
560 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_INVERSE); |
561 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_NORMAL); |
562 | |
563 | epd->partial = true; |
564 | } else { |
565 | /* Clear display (anything -> white) */ |
566 | repaper_frame_fixed_repeat(epd, fixed_value: 0xff, stage: REPAPER_COMPENSATE); |
567 | repaper_frame_fixed_repeat(epd, fixed_value: 0xff, stage: REPAPER_WHITE); |
568 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_INVERSE); |
569 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_NORMAL); |
570 | |
571 | /* Assuming a clear (white) screen output an image */ |
572 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_COMPENSATE); |
573 | repaper_frame_fixed_repeat(epd, fixed_value: 0xaa, stage: REPAPER_WHITE); |
574 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_INVERSE); |
575 | repaper_frame_data_repeat(epd, image: buf, NULL, stage: REPAPER_NORMAL); |
576 | |
577 | epd->cleared = true; |
578 | epd->partial = true; |
579 | } |
580 | |
581 | memcpy(epd->current_frame, buf, fb->width * fb->height / 8); |
582 | |
583 | /* |
584 | * An extra frame write is needed if pixels are set in the bottom line, |
585 | * or else grey lines rises up from the pixels |
586 | */ |
587 | if (epd->pre_border_byte) { |
588 | unsigned int x; |
589 | |
590 | for (x = 0; x < (fb->width / 8); x++) |
591 | if (buf[x + (fb->width * (fb->height - 1) / 8)]) { |
592 | repaper_frame_data_repeat(epd, image: buf, |
593 | mask: epd->current_frame, |
594 | stage: REPAPER_NORMAL); |
595 | break; |
596 | } |
597 | } |
598 | |
599 | out_free: |
600 | kfree(objp: buf); |
601 | out_exit: |
602 | drm_dev_exit(idx); |
603 | |
604 | return ret; |
605 | } |
606 | |
607 | static void power_off(struct repaper_epd *epd) |
608 | { |
609 | /* Turn off power and all signals */ |
610 | gpiod_set_value_cansleep(desc: epd->reset, value: 0); |
611 | gpiod_set_value_cansleep(desc: epd->panel_on, value: 0); |
612 | if (epd->border) |
613 | gpiod_set_value_cansleep(desc: epd->border, value: 0); |
614 | |
615 | /* Ensure SPI MOSI and CLOCK are Low before CS Low */ |
616 | repaper_spi_mosi_low(spi: epd->spi); |
617 | |
618 | /* Discharge pulse */ |
619 | gpiod_set_value_cansleep(desc: epd->discharge, value: 1); |
620 | msleep(msecs: 150); |
621 | gpiod_set_value_cansleep(desc: epd->discharge, value: 0); |
622 | } |
623 | |
624 | static enum drm_mode_status repaper_pipe_mode_valid(struct drm_simple_display_pipe *pipe, |
625 | const struct drm_display_mode *mode) |
626 | { |
627 | struct drm_crtc *crtc = &pipe->crtc; |
628 | struct repaper_epd *epd = drm_to_epd(drm: crtc->dev); |
629 | |
630 | return drm_crtc_helper_mode_valid_fixed(crtc, mode, fixed_mode: epd->mode); |
631 | } |
632 | |
633 | static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe, |
634 | struct drm_crtc_state *crtc_state, |
635 | struct drm_plane_state *plane_state) |
636 | { |
637 | struct repaper_epd *epd = drm_to_epd(drm: pipe->crtc.dev); |
638 | struct spi_device *spi = epd->spi; |
639 | struct device *dev = &spi->dev; |
640 | bool dc_ok = false; |
641 | int i, ret, idx; |
642 | |
643 | if (!drm_dev_enter(dev: pipe->crtc.dev, idx: &idx)) |
644 | return; |
645 | |
646 | DRM_DEBUG_DRIVER("\n" ); |
647 | |
648 | /* Power up sequence */ |
649 | gpiod_set_value_cansleep(desc: epd->reset, value: 0); |
650 | gpiod_set_value_cansleep(desc: epd->panel_on, value: 0); |
651 | gpiod_set_value_cansleep(desc: epd->discharge, value: 0); |
652 | if (epd->border) |
653 | gpiod_set_value_cansleep(desc: epd->border, value: 0); |
654 | repaper_spi_mosi_low(spi); |
655 | usleep_range(min: 5000, max: 10000); |
656 | |
657 | gpiod_set_value_cansleep(desc: epd->panel_on, value: 1); |
658 | /* |
659 | * This delay comes from the repaper.org userspace driver, it's not |
660 | * mentioned in the datasheet. |
661 | */ |
662 | usleep_range(min: 10000, max: 15000); |
663 | gpiod_set_value_cansleep(desc: epd->reset, value: 1); |
664 | if (epd->border) |
665 | gpiod_set_value_cansleep(desc: epd->border, value: 1); |
666 | usleep_range(min: 5000, max: 10000); |
667 | gpiod_set_value_cansleep(desc: epd->reset, value: 0); |
668 | usleep_range(min: 5000, max: 10000); |
669 | gpiod_set_value_cansleep(desc: epd->reset, value: 1); |
670 | usleep_range(min: 5000, max: 10000); |
671 | |
672 | /* Wait for COG to become ready */ |
673 | for (i = 100; i > 0; i--) { |
674 | if (!gpiod_get_value_cansleep(desc: epd->busy)) |
675 | break; |
676 | |
677 | usleep_range(min: 10, max: 100); |
678 | } |
679 | |
680 | if (!i) { |
681 | DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n" ); |
682 | power_off(epd); |
683 | goto out_exit; |
684 | } |
685 | |
686 | repaper_read_id(spi); |
687 | ret = repaper_read_id(spi); |
688 | if (ret != REPAPER_RID_G2_COG_ID) { |
689 | if (ret < 0) |
690 | dev_err(dev, "failed to read chip (%d)\n" , ret); |
691 | else |
692 | dev_err(dev, "wrong COG ID 0x%02x\n" , ret); |
693 | power_off(epd); |
694 | goto out_exit; |
695 | } |
696 | |
697 | /* Disable OE */ |
698 | repaper_write_val(spi, reg: 0x02, val: 0x40); |
699 | |
700 | ret = repaper_read_val(spi, reg: 0x0f); |
701 | if (ret < 0 || !(ret & 0x80)) { |
702 | if (ret < 0) |
703 | DRM_DEV_ERROR(dev, "failed to read chip (%d)\n" , ret); |
704 | else |
705 | DRM_DEV_ERROR(dev, "panel is reported broken\n" ); |
706 | power_off(epd); |
707 | goto out_exit; |
708 | } |
709 | |
710 | /* Power saving mode */ |
711 | repaper_write_val(spi, reg: 0x0b, val: 0x02); |
712 | /* Channel select */ |
713 | repaper_write_buf(spi, reg: 0x01, buf: epd->channel_select, len: 8); |
714 | /* High power mode osc */ |
715 | repaper_write_val(spi, reg: 0x07, val: 0xd1); |
716 | /* Power setting */ |
717 | repaper_write_val(spi, reg: 0x08, val: 0x02); |
718 | /* Vcom level */ |
719 | repaper_write_val(spi, reg: 0x09, val: 0xc2); |
720 | /* Power setting */ |
721 | repaper_write_val(spi, reg: 0x04, val: 0x03); |
722 | /* Driver latch on */ |
723 | repaper_write_val(spi, reg: 0x03, val: 0x01); |
724 | /* Driver latch off */ |
725 | repaper_write_val(spi, reg: 0x03, val: 0x00); |
726 | usleep_range(min: 5000, max: 10000); |
727 | |
728 | /* Start chargepump */ |
729 | for (i = 0; i < 4; ++i) { |
730 | /* Charge pump positive voltage on - VGH/VDL on */ |
731 | repaper_write_val(spi, reg: 0x05, val: 0x01); |
732 | msleep(msecs: 240); |
733 | |
734 | /* Charge pump negative voltage on - VGL/VDL on */ |
735 | repaper_write_val(spi, reg: 0x05, val: 0x03); |
736 | msleep(msecs: 40); |
737 | |
738 | /* Charge pump Vcom on - Vcom driver on */ |
739 | repaper_write_val(spi, reg: 0x05, val: 0x0f); |
740 | msleep(msecs: 40); |
741 | |
742 | /* check DC/DC */ |
743 | ret = repaper_read_val(spi, reg: 0x0f); |
744 | if (ret < 0) { |
745 | DRM_DEV_ERROR(dev, "failed to read chip (%d)\n" , ret); |
746 | power_off(epd); |
747 | goto out_exit; |
748 | } |
749 | |
750 | if (ret & 0x40) { |
751 | dc_ok = true; |
752 | break; |
753 | } |
754 | } |
755 | |
756 | if (!dc_ok) { |
757 | DRM_DEV_ERROR(dev, "dc/dc failed\n" ); |
758 | power_off(epd); |
759 | goto out_exit; |
760 | } |
761 | |
762 | /* |
763 | * Output enable to disable |
764 | * The userspace driver sets this to 0x04, but the datasheet says 0x06 |
765 | */ |
766 | repaper_write_val(spi, reg: 0x02, val: 0x04); |
767 | |
768 | epd->partial = false; |
769 | out_exit: |
770 | drm_dev_exit(idx); |
771 | } |
772 | |
773 | static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe) |
774 | { |
775 | struct repaper_epd *epd = drm_to_epd(drm: pipe->crtc.dev); |
776 | struct spi_device *spi = epd->spi; |
777 | unsigned int line; |
778 | |
779 | /* |
780 | * This callback is not protected by drm_dev_enter/exit since we want to |
781 | * turn off the display on regular driver unload. It's highly unlikely |
782 | * that the underlying SPI controller is gone should this be called after |
783 | * unplug. |
784 | */ |
785 | |
786 | DRM_DEBUG_DRIVER("\n" ); |
787 | |
788 | /* Nothing frame */ |
789 | for (line = 0; line < epd->height; line++) |
790 | repaper_one_line(epd, line: 0x7fffu, NULL, fixed_value: 0x00, NULL, |
791 | stage: REPAPER_COMPENSATE); |
792 | |
793 | /* 2.7" */ |
794 | if (epd->border) { |
795 | /* Dummy line */ |
796 | repaper_one_line(epd, line: 0x7fffu, NULL, fixed_value: 0x00, NULL, |
797 | stage: REPAPER_COMPENSATE); |
798 | msleep(msecs: 25); |
799 | gpiod_set_value_cansleep(desc: epd->border, value: 0); |
800 | msleep(msecs: 200); |
801 | gpiod_set_value_cansleep(desc: epd->border, value: 1); |
802 | } else { |
803 | /* Border dummy line */ |
804 | repaper_one_line(epd, line: 0x7fffu, NULL, fixed_value: 0x00, NULL, |
805 | stage: REPAPER_NORMAL); |
806 | msleep(msecs: 200); |
807 | } |
808 | |
809 | /* not described in datasheet */ |
810 | repaper_write_val(spi, reg: 0x0b, val: 0x00); |
811 | /* Latch reset turn on */ |
812 | repaper_write_val(spi, reg: 0x03, val: 0x01); |
813 | /* Power off charge pump Vcom */ |
814 | repaper_write_val(spi, reg: 0x05, val: 0x03); |
815 | /* Power off charge pump neg voltage */ |
816 | repaper_write_val(spi, reg: 0x05, val: 0x01); |
817 | msleep(msecs: 120); |
818 | /* Discharge internal */ |
819 | repaper_write_val(spi, reg: 0x04, val: 0x80); |
820 | /* turn off all charge pumps */ |
821 | repaper_write_val(spi, reg: 0x05, val: 0x00); |
822 | /* Turn off osc */ |
823 | repaper_write_val(spi, reg: 0x07, val: 0x01); |
824 | msleep(msecs: 50); |
825 | |
826 | power_off(epd); |
827 | } |
828 | |
829 | static void repaper_pipe_update(struct drm_simple_display_pipe *pipe, |
830 | struct drm_plane_state *old_state) |
831 | { |
832 | struct drm_plane_state *state = pipe->plane.state; |
833 | struct drm_rect rect; |
834 | |
835 | if (!pipe->crtc.state->active) |
836 | return; |
837 | |
838 | if (drm_atomic_helper_damage_merged(old_state, state, rect: &rect)) |
839 | repaper_fb_dirty(fb: state->fb); |
840 | } |
841 | |
842 | static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = { |
843 | .mode_valid = repaper_pipe_mode_valid, |
844 | .enable = repaper_pipe_enable, |
845 | .disable = repaper_pipe_disable, |
846 | .update = repaper_pipe_update, |
847 | }; |
848 | |
849 | static int repaper_connector_get_modes(struct drm_connector *connector) |
850 | { |
851 | struct repaper_epd *epd = drm_to_epd(drm: connector->dev); |
852 | |
853 | return drm_connector_helper_get_modes_fixed(connector, fixed_mode: epd->mode); |
854 | } |
855 | |
856 | static const struct drm_connector_helper_funcs repaper_connector_hfuncs = { |
857 | .get_modes = repaper_connector_get_modes, |
858 | }; |
859 | |
860 | static const struct drm_connector_funcs repaper_connector_funcs = { |
861 | .reset = drm_atomic_helper_connector_reset, |
862 | .fill_modes = drm_helper_probe_single_connector_modes, |
863 | .destroy = drm_connector_cleanup, |
864 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
865 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
866 | }; |
867 | |
868 | static const struct drm_mode_config_funcs repaper_mode_config_funcs = { |
869 | .fb_create = drm_gem_fb_create_with_dirty, |
870 | .atomic_check = drm_atomic_helper_check, |
871 | .atomic_commit = drm_atomic_helper_commit, |
872 | }; |
873 | |
874 | static const uint32_t repaper_formats[] = { |
875 | DRM_FORMAT_XRGB8888, |
876 | }; |
877 | |
878 | static const struct drm_display_mode repaper_e1144cs021_mode = { |
879 | DRM_SIMPLE_MODE(128, 96, 29, 22), |
880 | }; |
881 | |
882 | static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00, |
883 | 0x00, 0x0f, 0xff, 0x00 }; |
884 | |
885 | static const struct drm_display_mode repaper_e1190cs021_mode = { |
886 | DRM_SIMPLE_MODE(144, 128, 36, 32), |
887 | }; |
888 | |
889 | static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03, |
890 | 0xfc, 0x00, 0x00, 0xff }; |
891 | |
892 | static const struct drm_display_mode repaper_e2200cs021_mode = { |
893 | DRM_SIMPLE_MODE(200, 96, 46, 22), |
894 | }; |
895 | |
896 | static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00, |
897 | 0x01, 0xff, 0xe0, 0x00 }; |
898 | |
899 | static const struct drm_display_mode repaper_e2271cs021_mode = { |
900 | DRM_SIMPLE_MODE(264, 176, 57, 38), |
901 | }; |
902 | |
903 | static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f, |
904 | 0xff, 0xfe, 0x00, 0x00 }; |
905 | |
906 | DEFINE_DRM_GEM_DMA_FOPS(repaper_fops); |
907 | |
908 | static const struct drm_driver repaper_driver = { |
909 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, |
910 | .fops = &repaper_fops, |
911 | DRM_GEM_DMA_DRIVER_OPS_VMAP, |
912 | .name = "repaper" , |
913 | .desc = "Pervasive Displays RePaper e-ink panels" , |
914 | .date = "20170405" , |
915 | .major = 1, |
916 | .minor = 0, |
917 | }; |
918 | |
919 | static const struct of_device_id repaper_of_match[] = { |
920 | { .compatible = "pervasive,e1144cs021" , .data = (void *)E1144CS021 }, |
921 | { .compatible = "pervasive,e1190cs021" , .data = (void *)E1190CS021 }, |
922 | { .compatible = "pervasive,e2200cs021" , .data = (void *)E2200CS021 }, |
923 | { .compatible = "pervasive,e2271cs021" , .data = (void *)E2271CS021 }, |
924 | {}, |
925 | }; |
926 | MODULE_DEVICE_TABLE(of, repaper_of_match); |
927 | |
928 | static const struct spi_device_id repaper_id[] = { |
929 | { "e1144cs021" , E1144CS021 }, |
930 | { "e1190cs021" , E1190CS021 }, |
931 | { "e2200cs021" , E2200CS021 }, |
932 | { "e2271cs021" , E2271CS021 }, |
933 | { }, |
934 | }; |
935 | MODULE_DEVICE_TABLE(spi, repaper_id); |
936 | |
937 | static int repaper_probe(struct spi_device *spi) |
938 | { |
939 | const struct drm_display_mode *mode; |
940 | const struct spi_device_id *spi_id; |
941 | struct device *dev = &spi->dev; |
942 | enum repaper_model model; |
943 | const char *thermal_zone; |
944 | struct repaper_epd *epd; |
945 | size_t line_buffer_size; |
946 | struct drm_device *drm; |
947 | const void *match; |
948 | int ret; |
949 | |
950 | match = device_get_match_data(dev); |
951 | if (match) { |
952 | model = (enum repaper_model)(uintptr_t)match; |
953 | } else { |
954 | spi_id = spi_get_device_id(sdev: spi); |
955 | model = (enum repaper_model)spi_id->driver_data; |
956 | } |
957 | |
958 | /* The SPI device is used to allocate dma memory */ |
959 | if (!dev->coherent_dma_mask) { |
960 | ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); |
961 | if (ret) { |
962 | dev_warn(dev, "Failed to set dma mask %d\n" , ret); |
963 | return ret; |
964 | } |
965 | } |
966 | |
967 | epd = devm_drm_dev_alloc(dev, &repaper_driver, |
968 | struct repaper_epd, drm); |
969 | if (IS_ERR(ptr: epd)) |
970 | return PTR_ERR(ptr: epd); |
971 | |
972 | drm = &epd->drm; |
973 | |
974 | ret = drmm_mode_config_init(dev: drm); |
975 | if (ret) |
976 | return ret; |
977 | drm->mode_config.funcs = &repaper_mode_config_funcs; |
978 | |
979 | epd->spi = spi; |
980 | |
981 | epd->panel_on = devm_gpiod_get(dev, con_id: "panel-on" , flags: GPIOD_OUT_LOW); |
982 | if (IS_ERR(ptr: epd->panel_on)) { |
983 | ret = PTR_ERR(ptr: epd->panel_on); |
984 | if (ret != -EPROBE_DEFER) |
985 | DRM_DEV_ERROR(dev, "Failed to get gpio 'panel-on'\n" ); |
986 | return ret; |
987 | } |
988 | |
989 | epd->discharge = devm_gpiod_get(dev, con_id: "discharge" , flags: GPIOD_OUT_LOW); |
990 | if (IS_ERR(ptr: epd->discharge)) { |
991 | ret = PTR_ERR(ptr: epd->discharge); |
992 | if (ret != -EPROBE_DEFER) |
993 | DRM_DEV_ERROR(dev, "Failed to get gpio 'discharge'\n" ); |
994 | return ret; |
995 | } |
996 | |
997 | epd->reset = devm_gpiod_get(dev, con_id: "reset" , flags: GPIOD_OUT_LOW); |
998 | if (IS_ERR(ptr: epd->reset)) { |
999 | ret = PTR_ERR(ptr: epd->reset); |
1000 | if (ret != -EPROBE_DEFER) |
1001 | DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n" ); |
1002 | return ret; |
1003 | } |
1004 | |
1005 | epd->busy = devm_gpiod_get(dev, con_id: "busy" , flags: GPIOD_IN); |
1006 | if (IS_ERR(ptr: epd->busy)) { |
1007 | ret = PTR_ERR(ptr: epd->busy); |
1008 | if (ret != -EPROBE_DEFER) |
1009 | DRM_DEV_ERROR(dev, "Failed to get gpio 'busy'\n" ); |
1010 | return ret; |
1011 | } |
1012 | |
1013 | if (!device_property_read_string(dev, propname: "pervasive,thermal-zone" , |
1014 | val: &thermal_zone)) { |
1015 | epd->thermal = thermal_zone_get_zone_by_name(name: thermal_zone); |
1016 | if (IS_ERR(ptr: epd->thermal)) { |
1017 | DRM_DEV_ERROR(dev, "Failed to get thermal zone: %s\n" , thermal_zone); |
1018 | return PTR_ERR(ptr: epd->thermal); |
1019 | } |
1020 | } |
1021 | |
1022 | switch (model) { |
1023 | case E1144CS021: |
1024 | mode = &repaper_e1144cs021_mode; |
1025 | epd->channel_select = repaper_e1144cs021_cs; |
1026 | epd->stage_time = 480; |
1027 | epd->bytes_per_scan = 96 / 4; |
1028 | epd->middle_scan = true; /* data-scan-data */ |
1029 | epd->pre_border_byte = false; |
1030 | epd->border_byte = REPAPER_BORDER_BYTE_ZERO; |
1031 | break; |
1032 | |
1033 | case E1190CS021: |
1034 | mode = &repaper_e1190cs021_mode; |
1035 | epd->channel_select = repaper_e1190cs021_cs; |
1036 | epd->stage_time = 480; |
1037 | epd->bytes_per_scan = 128 / 4 / 2; |
1038 | epd->middle_scan = false; /* scan-data-scan */ |
1039 | epd->pre_border_byte = false; |
1040 | epd->border_byte = REPAPER_BORDER_BYTE_SET; |
1041 | break; |
1042 | |
1043 | case E2200CS021: |
1044 | mode = &repaper_e2200cs021_mode; |
1045 | epd->channel_select = repaper_e2200cs021_cs; |
1046 | epd->stage_time = 480; |
1047 | epd->bytes_per_scan = 96 / 4; |
1048 | epd->middle_scan = true; /* data-scan-data */ |
1049 | epd->pre_border_byte = true; |
1050 | epd->border_byte = REPAPER_BORDER_BYTE_NONE; |
1051 | break; |
1052 | |
1053 | case E2271CS021: |
1054 | epd->border = devm_gpiod_get(dev, con_id: "border" , flags: GPIOD_OUT_LOW); |
1055 | if (IS_ERR(ptr: epd->border)) { |
1056 | ret = PTR_ERR(ptr: epd->border); |
1057 | if (ret != -EPROBE_DEFER) |
1058 | DRM_DEV_ERROR(dev, "Failed to get gpio 'border'\n" ); |
1059 | return ret; |
1060 | } |
1061 | |
1062 | mode = &repaper_e2271cs021_mode; |
1063 | epd->channel_select = repaper_e2271cs021_cs; |
1064 | epd->stage_time = 630; |
1065 | epd->bytes_per_scan = 176 / 4; |
1066 | epd->middle_scan = true; /* data-scan-data */ |
1067 | epd->pre_border_byte = true; |
1068 | epd->border_byte = REPAPER_BORDER_BYTE_NONE; |
1069 | break; |
1070 | |
1071 | default: |
1072 | return -ENODEV; |
1073 | } |
1074 | |
1075 | epd->mode = mode; |
1076 | epd->width = mode->hdisplay; |
1077 | epd->height = mode->vdisplay; |
1078 | epd->factored_stage_time = epd->stage_time; |
1079 | |
1080 | line_buffer_size = 2 * epd->width / 8 + epd->bytes_per_scan + 2; |
1081 | epd->line_buffer = devm_kzalloc(dev, size: line_buffer_size, GFP_KERNEL); |
1082 | if (!epd->line_buffer) |
1083 | return -ENOMEM; |
1084 | |
1085 | epd->current_frame = devm_kzalloc(dev, size: epd->width * epd->height / 8, |
1086 | GFP_KERNEL); |
1087 | if (!epd->current_frame) |
1088 | return -ENOMEM; |
1089 | |
1090 | drm->mode_config.min_width = mode->hdisplay; |
1091 | drm->mode_config.max_width = mode->hdisplay; |
1092 | drm->mode_config.min_height = mode->vdisplay; |
1093 | drm->mode_config.max_height = mode->vdisplay; |
1094 | |
1095 | drm_connector_helper_add(connector: &epd->connector, funcs: &repaper_connector_hfuncs); |
1096 | ret = drm_connector_init(dev: drm, connector: &epd->connector, funcs: &repaper_connector_funcs, |
1097 | DRM_MODE_CONNECTOR_SPI); |
1098 | if (ret) |
1099 | return ret; |
1100 | |
1101 | ret = drm_simple_display_pipe_init(dev: drm, pipe: &epd->pipe, funcs: &repaper_pipe_funcs, |
1102 | formats: repaper_formats, ARRAY_SIZE(repaper_formats), |
1103 | NULL, connector: &epd->connector); |
1104 | if (ret) |
1105 | return ret; |
1106 | |
1107 | drm_mode_config_reset(dev: drm); |
1108 | |
1109 | ret = drm_dev_register(dev: drm, flags: 0); |
1110 | if (ret) |
1111 | return ret; |
1112 | |
1113 | spi_set_drvdata(spi, data: drm); |
1114 | |
1115 | DRM_DEBUG_DRIVER("SPI speed: %uMHz\n" , spi->max_speed_hz / 1000000); |
1116 | |
1117 | drm_fbdev_generic_setup(dev: drm, preferred_bpp: 0); |
1118 | |
1119 | return 0; |
1120 | } |
1121 | |
1122 | static void repaper_remove(struct spi_device *spi) |
1123 | { |
1124 | struct drm_device *drm = spi_get_drvdata(spi); |
1125 | |
1126 | drm_dev_unplug(dev: drm); |
1127 | drm_atomic_helper_shutdown(dev: drm); |
1128 | } |
1129 | |
1130 | static void repaper_shutdown(struct spi_device *spi) |
1131 | { |
1132 | drm_atomic_helper_shutdown(dev: spi_get_drvdata(spi)); |
1133 | } |
1134 | |
1135 | static struct spi_driver repaper_spi_driver = { |
1136 | .driver = { |
1137 | .name = "repaper" , |
1138 | .of_match_table = repaper_of_match, |
1139 | }, |
1140 | .id_table = repaper_id, |
1141 | .probe = repaper_probe, |
1142 | .remove = repaper_remove, |
1143 | .shutdown = repaper_shutdown, |
1144 | }; |
1145 | module_spi_driver(repaper_spi_driver); |
1146 | |
1147 | MODULE_DESCRIPTION("Pervasive Displays RePaper DRM driver" ); |
1148 | MODULE_AUTHOR("Noralf Trønnes" ); |
1149 | MODULE_LICENSE("GPL" ); |
1150 | |