18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DRM driver for Pervasive Displays RePaper branded e-ink panels 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2013-2017 Pervasive Displays, Inc. 68c2ecf20Sopenharmony_ci * Copyright 2017 Noralf Trønnes 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * The driver supports: 98c2ecf20Sopenharmony_ci * Material Film: Aurora Mb (V231) 108c2ecf20Sopenharmony_ci * Driver IC: G2 (eTC) 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * The controller code was taken from the userspace driver: 138c2ecf20Sopenharmony_ci * https://github.com/repaper/gratis 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 188c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/property.h> 218c2ecf20Sopenharmony_ci#include <linux/sched/clock.h> 228c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 238c2ecf20Sopenharmony_ci#include <linux/thermal.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 268c2ecf20Sopenharmony_ci#include <drm/drm_connector.h> 278c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_drv.h> 298c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 308c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 318c2ecf20Sopenharmony_ci#include <drm/drm_format_helper.h> 328c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 338c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 348c2ecf20Sopenharmony_ci#include <drm/drm_managed.h> 358c2ecf20Sopenharmony_ci#include <drm/drm_modes.h> 368c2ecf20Sopenharmony_ci#include <drm/drm_rect.h> 378c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 388c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define REPAPER_RID_G2_COG_ID 0x12 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cienum repaper_model { 438c2ecf20Sopenharmony_ci /* 0 is reserved to avoid clashing with NULL */ 448c2ecf20Sopenharmony_ci E1144CS021 = 1, 458c2ecf20Sopenharmony_ci E1190CS021, 468c2ecf20Sopenharmony_ci E2200CS021, 478c2ecf20Sopenharmony_ci E2271CS021, 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cienum repaper_stage { /* Image pixel -> Display pixel */ 518c2ecf20Sopenharmony_ci REPAPER_COMPENSATE, /* B -> W, W -> B (Current Image) */ 528c2ecf20Sopenharmony_ci REPAPER_WHITE, /* B -> N, W -> W (Current Image) */ 538c2ecf20Sopenharmony_ci REPAPER_INVERSE, /* B -> N, W -> B (New Image) */ 548c2ecf20Sopenharmony_ci REPAPER_NORMAL /* B -> B, W -> W (New Image) */ 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cienum repaper_epd_border_byte { 588c2ecf20Sopenharmony_ci REPAPER_BORDER_BYTE_NONE, 598c2ecf20Sopenharmony_ci REPAPER_BORDER_BYTE_ZERO, 608c2ecf20Sopenharmony_ci REPAPER_BORDER_BYTE_SET, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct repaper_epd { 648c2ecf20Sopenharmony_ci struct drm_device drm; 658c2ecf20Sopenharmony_ci struct drm_simple_display_pipe pipe; 668c2ecf20Sopenharmony_ci const struct drm_display_mode *mode; 678c2ecf20Sopenharmony_ci struct drm_connector connector; 688c2ecf20Sopenharmony_ci struct spi_device *spi; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci struct gpio_desc *panel_on; 718c2ecf20Sopenharmony_ci struct gpio_desc *border; 728c2ecf20Sopenharmony_ci struct gpio_desc *discharge; 738c2ecf20Sopenharmony_ci struct gpio_desc *reset; 748c2ecf20Sopenharmony_ci struct gpio_desc *busy; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci struct thermal_zone_device *thermal; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci unsigned int height; 798c2ecf20Sopenharmony_ci unsigned int width; 808c2ecf20Sopenharmony_ci unsigned int bytes_per_scan; 818c2ecf20Sopenharmony_ci const u8 *channel_select; 828c2ecf20Sopenharmony_ci unsigned int stage_time; 838c2ecf20Sopenharmony_ci unsigned int factored_stage_time; 848c2ecf20Sopenharmony_ci bool middle_scan; 858c2ecf20Sopenharmony_ci bool pre_border_byte; 868c2ecf20Sopenharmony_ci enum repaper_epd_border_byte border_byte; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci u8 *line_buffer; 898c2ecf20Sopenharmony_ci void *current_frame; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci bool cleared; 928c2ecf20Sopenharmony_ci bool partial; 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic inline struct repaper_epd *drm_to_epd(struct drm_device *drm) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return container_of(drm, struct repaper_epd, drm); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int repaper_spi_transfer(struct spi_device *spi, u8 header, 1018c2ecf20Sopenharmony_ci const void *tx, void *rx, size_t len) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci void *txbuf = NULL, *rxbuf = NULL; 1048c2ecf20Sopenharmony_ci struct spi_transfer tr[2] = {}; 1058c2ecf20Sopenharmony_ci u8 *headerbuf; 1068c2ecf20Sopenharmony_ci int ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci headerbuf = kmalloc(1, GFP_KERNEL); 1098c2ecf20Sopenharmony_ci if (!headerbuf) 1108c2ecf20Sopenharmony_ci return -ENOMEM; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci headerbuf[0] = header; 1138c2ecf20Sopenharmony_ci tr[0].tx_buf = headerbuf; 1148c2ecf20Sopenharmony_ci tr[0].len = 1; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* Stack allocated tx? */ 1178c2ecf20Sopenharmony_ci if (tx && len <= 32) { 1188c2ecf20Sopenharmony_ci txbuf = kmemdup(tx, len, GFP_KERNEL); 1198c2ecf20Sopenharmony_ci if (!txbuf) { 1208c2ecf20Sopenharmony_ci ret = -ENOMEM; 1218c2ecf20Sopenharmony_ci goto out_free; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (rx) { 1268c2ecf20Sopenharmony_ci rxbuf = kmalloc(len, GFP_KERNEL); 1278c2ecf20Sopenharmony_ci if (!rxbuf) { 1288c2ecf20Sopenharmony_ci ret = -ENOMEM; 1298c2ecf20Sopenharmony_ci goto out_free; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci tr[1].tx_buf = txbuf ? txbuf : tx; 1348c2ecf20Sopenharmony_ci tr[1].rx_buf = rxbuf; 1358c2ecf20Sopenharmony_ci tr[1].len = len; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ndelay(80); 1388c2ecf20Sopenharmony_ci ret = spi_sync_transfer(spi, tr, 2); 1398c2ecf20Sopenharmony_ci if (rx && !ret) 1408c2ecf20Sopenharmony_ci memcpy(rx, rxbuf, len); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciout_free: 1438c2ecf20Sopenharmony_ci kfree(headerbuf); 1448c2ecf20Sopenharmony_ci kfree(txbuf); 1458c2ecf20Sopenharmony_ci kfree(rxbuf); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return ret; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int repaper_write_buf(struct spi_device *spi, u8 reg, 1518c2ecf20Sopenharmony_ci const u8 *buf, size_t len) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int ret; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ret = repaper_spi_transfer(spi, 0x70, ®, NULL, 1); 1568c2ecf20Sopenharmony_ci if (ret) 1578c2ecf20Sopenharmony_ci return ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return repaper_spi_transfer(spi, 0x72, buf, NULL, len); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int repaper_write_val(struct spi_device *spi, u8 reg, u8 val) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci return repaper_write_buf(spi, reg, &val, 1); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int repaper_read_val(struct spi_device *spi, u8 reg) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci u8 val; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = repaper_spi_transfer(spi, 0x70, ®, NULL, 1); 1738c2ecf20Sopenharmony_ci if (ret) 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = repaper_spi_transfer(spi, 0x73, NULL, &val, 1); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci return ret ? ret : val; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int repaper_read_id(struct spi_device *spi) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci int ret; 1848c2ecf20Sopenharmony_ci u8 id; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = repaper_spi_transfer(spi, 0x71, NULL, &id, 1); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return ret ? ret : id; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void repaper_spi_mosi_low(struct spi_device *spi) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci const u8 buf[1] = { 0 }; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci spi_write(spi, buf, 1); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* pixels on display are numbered from 1 so even is actually bits 1,3,5,... */ 1998c2ecf20Sopenharmony_cistatic void repaper_even_pixels(struct repaper_epd *epd, u8 **pp, 2008c2ecf20Sopenharmony_ci const u8 *data, u8 fixed_value, const u8 *mask, 2018c2ecf20Sopenharmony_ci enum repaper_stage stage) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci unsigned int b; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (b = 0; b < (epd->width / 8); b++) { 2068c2ecf20Sopenharmony_ci if (data) { 2078c2ecf20Sopenharmony_ci u8 pixels = data[b] & 0xaa; 2088c2ecf20Sopenharmony_ci u8 pixel_mask = 0xff; 2098c2ecf20Sopenharmony_ci u8 p1, p2, p3, p4; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (mask) { 2128c2ecf20Sopenharmony_ci pixel_mask = (mask[b] ^ pixels) & 0xaa; 2138c2ecf20Sopenharmony_ci pixel_mask |= pixel_mask >> 1; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci switch (stage) { 2178c2ecf20Sopenharmony_ci case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */ 2188c2ecf20Sopenharmony_ci pixels = 0xaa | ((pixels ^ 0xaa) >> 1); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci case REPAPER_WHITE: /* B -> N, W -> W (Current) */ 2218c2ecf20Sopenharmony_ci pixels = 0x55 + ((pixels ^ 0xaa) >> 1); 2228c2ecf20Sopenharmony_ci break; 2238c2ecf20Sopenharmony_ci case REPAPER_INVERSE: /* B -> N, W -> B (New) */ 2248c2ecf20Sopenharmony_ci pixels = 0x55 | (pixels ^ 0xaa); 2258c2ecf20Sopenharmony_ci break; 2268c2ecf20Sopenharmony_ci case REPAPER_NORMAL: /* B -> B, W -> W (New) */ 2278c2ecf20Sopenharmony_ci pixels = 0xaa | (pixels >> 1); 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55); 2328c2ecf20Sopenharmony_ci p1 = (pixels >> 6) & 0x03; 2338c2ecf20Sopenharmony_ci p2 = (pixels >> 4) & 0x03; 2348c2ecf20Sopenharmony_ci p3 = (pixels >> 2) & 0x03; 2358c2ecf20Sopenharmony_ci p4 = (pixels >> 0) & 0x03; 2368c2ecf20Sopenharmony_ci pixels = (p1 << 0) | (p2 << 2) | (p3 << 4) | (p4 << 6); 2378c2ecf20Sopenharmony_ci *(*pp)++ = pixels; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci *(*pp)++ = fixed_value; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci/* pixels on display are numbered from 1 so odd is actually bits 0,2,4,... */ 2458c2ecf20Sopenharmony_cistatic void repaper_odd_pixels(struct repaper_epd *epd, u8 **pp, 2468c2ecf20Sopenharmony_ci const u8 *data, u8 fixed_value, const u8 *mask, 2478c2ecf20Sopenharmony_ci enum repaper_stage stage) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci unsigned int b; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci for (b = epd->width / 8; b > 0; b--) { 2528c2ecf20Sopenharmony_ci if (data) { 2538c2ecf20Sopenharmony_ci u8 pixels = data[b - 1] & 0x55; 2548c2ecf20Sopenharmony_ci u8 pixel_mask = 0xff; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (mask) { 2578c2ecf20Sopenharmony_ci pixel_mask = (mask[b - 1] ^ pixels) & 0x55; 2588c2ecf20Sopenharmony_ci pixel_mask |= pixel_mask << 1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci switch (stage) { 2628c2ecf20Sopenharmony_ci case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */ 2638c2ecf20Sopenharmony_ci pixels = 0xaa | (pixels ^ 0x55); 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci case REPAPER_WHITE: /* B -> N, W -> W (Current) */ 2668c2ecf20Sopenharmony_ci pixels = 0x55 + (pixels ^ 0x55); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case REPAPER_INVERSE: /* B -> N, W -> B (New) */ 2698c2ecf20Sopenharmony_ci pixels = 0x55 | ((pixels ^ 0x55) << 1); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case REPAPER_NORMAL: /* B -> B, W -> W (New) */ 2728c2ecf20Sopenharmony_ci pixels = 0xaa | pixels; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci pixels = (pixels & pixel_mask) | (~pixel_mask & 0x55); 2778c2ecf20Sopenharmony_ci *(*pp)++ = pixels; 2788c2ecf20Sopenharmony_ci } else { 2798c2ecf20Sopenharmony_ci *(*pp)++ = fixed_value; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* interleave bits: (byte)76543210 -> (16 bit).7.6.5.4.3.2.1 */ 2858c2ecf20Sopenharmony_cistatic inline u16 repaper_interleave_bits(u16 value) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci value = (value | (value << 4)) & 0x0f0f; 2888c2ecf20Sopenharmony_ci value = (value | (value << 2)) & 0x3333; 2898c2ecf20Sopenharmony_ci value = (value | (value << 1)) & 0x5555; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return value; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/* pixels on display are numbered from 1 */ 2958c2ecf20Sopenharmony_cistatic void repaper_all_pixels(struct repaper_epd *epd, u8 **pp, 2968c2ecf20Sopenharmony_ci const u8 *data, u8 fixed_value, const u8 *mask, 2978c2ecf20Sopenharmony_ci enum repaper_stage stage) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci unsigned int b; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci for (b = epd->width / 8; b > 0; b--) { 3028c2ecf20Sopenharmony_ci if (data) { 3038c2ecf20Sopenharmony_ci u16 pixels = repaper_interleave_bits(data[b - 1]); 3048c2ecf20Sopenharmony_ci u16 pixel_mask = 0xffff; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (mask) { 3078c2ecf20Sopenharmony_ci pixel_mask = repaper_interleave_bits(mask[b - 1]); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci pixel_mask = (pixel_mask ^ pixels) & 0x5555; 3108c2ecf20Sopenharmony_ci pixel_mask |= pixel_mask << 1; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci switch (stage) { 3148c2ecf20Sopenharmony_ci case REPAPER_COMPENSATE: /* B -> W, W -> B (Current) */ 3158c2ecf20Sopenharmony_ci pixels = 0xaaaa | (pixels ^ 0x5555); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case REPAPER_WHITE: /* B -> N, W -> W (Current) */ 3188c2ecf20Sopenharmony_ci pixels = 0x5555 + (pixels ^ 0x5555); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci case REPAPER_INVERSE: /* B -> N, W -> B (New) */ 3218c2ecf20Sopenharmony_ci pixels = 0x5555 | ((pixels ^ 0x5555) << 1); 3228c2ecf20Sopenharmony_ci break; 3238c2ecf20Sopenharmony_ci case REPAPER_NORMAL: /* B -> B, W -> W (New) */ 3248c2ecf20Sopenharmony_ci pixels = 0xaaaa | pixels; 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci pixels = (pixels & pixel_mask) | (~pixel_mask & 0x5555); 3298c2ecf20Sopenharmony_ci *(*pp)++ = pixels >> 8; 3308c2ecf20Sopenharmony_ci *(*pp)++ = pixels; 3318c2ecf20Sopenharmony_ci } else { 3328c2ecf20Sopenharmony_ci *(*pp)++ = fixed_value; 3338c2ecf20Sopenharmony_ci *(*pp)++ = fixed_value; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/* output one line of scan and data bytes to the display */ 3398c2ecf20Sopenharmony_cistatic void repaper_one_line(struct repaper_epd *epd, unsigned int line, 3408c2ecf20Sopenharmony_ci const u8 *data, u8 fixed_value, const u8 *mask, 3418c2ecf20Sopenharmony_ci enum repaper_stage stage) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci u8 *p = epd->line_buffer; 3448c2ecf20Sopenharmony_ci unsigned int b; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci repaper_spi_mosi_low(epd->spi); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (epd->pre_border_byte) 3498c2ecf20Sopenharmony_ci *p++ = 0x00; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (epd->middle_scan) { 3528c2ecf20Sopenharmony_ci /* data bytes */ 3538c2ecf20Sopenharmony_ci repaper_odd_pixels(epd, &p, data, fixed_value, mask, stage); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* scan line */ 3568c2ecf20Sopenharmony_ci for (b = epd->bytes_per_scan; b > 0; b--) { 3578c2ecf20Sopenharmony_ci if (line / 4 == b - 1) 3588c2ecf20Sopenharmony_ci *p++ = 0x03 << (2 * (line & 0x03)); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci *p++ = 0x00; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* data bytes */ 3648c2ecf20Sopenharmony_ci repaper_even_pixels(epd, &p, data, fixed_value, mask, stage); 3658c2ecf20Sopenharmony_ci } else { 3668c2ecf20Sopenharmony_ci /* 3678c2ecf20Sopenharmony_ci * even scan line, but as lines on display are numbered from 1, 3688c2ecf20Sopenharmony_ci * line: 1,3,5,... 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci for (b = 0; b < epd->bytes_per_scan; b++) { 3718c2ecf20Sopenharmony_ci if (0 != (line & 0x01) && line / 8 == b) 3728c2ecf20Sopenharmony_ci *p++ = 0xc0 >> (line & 0x06); 3738c2ecf20Sopenharmony_ci else 3748c2ecf20Sopenharmony_ci *p++ = 0x00; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* data bytes */ 3788c2ecf20Sopenharmony_ci repaper_all_pixels(epd, &p, data, fixed_value, mask, stage); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * odd scan line, but as lines on display are numbered from 1, 3828c2ecf20Sopenharmony_ci * line: 0,2,4,6,... 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci for (b = epd->bytes_per_scan; b > 0; b--) { 3858c2ecf20Sopenharmony_ci if (0 == (line & 0x01) && line / 8 == b - 1) 3868c2ecf20Sopenharmony_ci *p++ = 0x03 << (line & 0x06); 3878c2ecf20Sopenharmony_ci else 3888c2ecf20Sopenharmony_ci *p++ = 0x00; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci switch (epd->border_byte) { 3938c2ecf20Sopenharmony_ci case REPAPER_BORDER_BYTE_NONE: 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case REPAPER_BORDER_BYTE_ZERO: 3978c2ecf20Sopenharmony_ci *p++ = 0x00; 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci case REPAPER_BORDER_BYTE_SET: 4018c2ecf20Sopenharmony_ci switch (stage) { 4028c2ecf20Sopenharmony_ci case REPAPER_COMPENSATE: 4038c2ecf20Sopenharmony_ci case REPAPER_WHITE: 4048c2ecf20Sopenharmony_ci case REPAPER_INVERSE: 4058c2ecf20Sopenharmony_ci *p++ = 0x00; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci case REPAPER_NORMAL: 4088c2ecf20Sopenharmony_ci *p++ = 0xaa; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci repaper_write_buf(epd->spi, 0x0a, epd->line_buffer, 4158c2ecf20Sopenharmony_ci p - epd->line_buffer); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Output data to panel */ 4188c2ecf20Sopenharmony_ci repaper_write_val(epd->spi, 0x02, 0x07); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci repaper_spi_mosi_low(epd->spi); 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic void repaper_frame_fixed(struct repaper_epd *epd, u8 fixed_value, 4248c2ecf20Sopenharmony_ci enum repaper_stage stage) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci unsigned int line; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci for (line = 0; line < epd->height; line++) 4298c2ecf20Sopenharmony_ci repaper_one_line(epd, line, NULL, fixed_value, NULL, stage); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void repaper_frame_data(struct repaper_epd *epd, const u8 *image, 4338c2ecf20Sopenharmony_ci const u8 *mask, enum repaper_stage stage) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci unsigned int line; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (!mask) { 4388c2ecf20Sopenharmony_ci for (line = 0; line < epd->height; line++) { 4398c2ecf20Sopenharmony_ci repaper_one_line(epd, line, 4408c2ecf20Sopenharmony_ci &image[line * (epd->width / 8)], 4418c2ecf20Sopenharmony_ci 0, NULL, stage); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci } else { 4448c2ecf20Sopenharmony_ci for (line = 0; line < epd->height; line++) { 4458c2ecf20Sopenharmony_ci size_t n = line * epd->width / 8; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci repaper_one_line(epd, line, &image[n], 0, &mask[n], 4488c2ecf20Sopenharmony_ci stage); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void repaper_frame_fixed_repeat(struct repaper_epd *epd, u8 fixed_value, 4548c2ecf20Sopenharmony_ci enum repaper_stage stage) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci u64 start = local_clock(); 4578c2ecf20Sopenharmony_ci u64 end = start + (epd->factored_stage_time * 1000 * 1000); 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci do { 4608c2ecf20Sopenharmony_ci repaper_frame_fixed(epd, fixed_value, stage); 4618c2ecf20Sopenharmony_ci } while (local_clock() < end); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic void repaper_frame_data_repeat(struct repaper_epd *epd, const u8 *image, 4658c2ecf20Sopenharmony_ci const u8 *mask, enum repaper_stage stage) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci u64 start = local_clock(); 4688c2ecf20Sopenharmony_ci u64 end = start + (epd->factored_stage_time * 1000 * 1000); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci do { 4718c2ecf20Sopenharmony_ci repaper_frame_data(epd, image, mask, stage); 4728c2ecf20Sopenharmony_ci } while (local_clock() < end); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void repaper_get_temperature(struct repaper_epd *epd) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci int ret, temperature = 0; 4788c2ecf20Sopenharmony_ci unsigned int factor10x; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!epd->thermal) 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci ret = thermal_zone_get_temp(epd->thermal, &temperature); 4848c2ecf20Sopenharmony_ci if (ret) { 4858c2ecf20Sopenharmony_ci DRM_DEV_ERROR(&epd->spi->dev, "Failed to get temperature (%d)\n", ret); 4868c2ecf20Sopenharmony_ci return; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci temperature /= 1000; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (temperature <= -10) 4928c2ecf20Sopenharmony_ci factor10x = 170; 4938c2ecf20Sopenharmony_ci else if (temperature <= -5) 4948c2ecf20Sopenharmony_ci factor10x = 120; 4958c2ecf20Sopenharmony_ci else if (temperature <= 5) 4968c2ecf20Sopenharmony_ci factor10x = 80; 4978c2ecf20Sopenharmony_ci else if (temperature <= 10) 4988c2ecf20Sopenharmony_ci factor10x = 40; 4998c2ecf20Sopenharmony_ci else if (temperature <= 15) 5008c2ecf20Sopenharmony_ci factor10x = 30; 5018c2ecf20Sopenharmony_ci else if (temperature <= 20) 5028c2ecf20Sopenharmony_ci factor10x = 20; 5038c2ecf20Sopenharmony_ci else if (temperature <= 40) 5048c2ecf20Sopenharmony_ci factor10x = 10; 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci factor10x = 7; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci epd->factored_stage_time = epd->stage_time * factor10x / 10; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic void repaper_gray8_to_mono_reversed(u8 *buf, u32 width, u32 height) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci u8 *gray8 = buf, *mono = buf; 5148c2ecf20Sopenharmony_ci int y, xb, i; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci for (y = 0; y < height; y++) 5178c2ecf20Sopenharmony_ci for (xb = 0; xb < width / 8; xb++) { 5188c2ecf20Sopenharmony_ci u8 byte = 0x00; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 5218c2ecf20Sopenharmony_ci int x = xb * 8 + i; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci byte >>= 1; 5248c2ecf20Sopenharmony_ci if (gray8[y * width + x] >> 7) 5258c2ecf20Sopenharmony_ci byte |= BIT(7); 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci *mono++ = byte; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int repaper_fb_dirty(struct drm_framebuffer *fb) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); 5348c2ecf20Sopenharmony_ci struct dma_buf_attachment *import_attach = cma_obj->base.import_attach; 5358c2ecf20Sopenharmony_ci struct repaper_epd *epd = drm_to_epd(fb->dev); 5368c2ecf20Sopenharmony_ci struct drm_rect clip; 5378c2ecf20Sopenharmony_ci int idx, ret = 0; 5388c2ecf20Sopenharmony_ci u8 *buf = NULL; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (!drm_dev_enter(fb->dev, &idx)) 5418c2ecf20Sopenharmony_ci return -ENODEV; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* repaper can't do partial updates */ 5448c2ecf20Sopenharmony_ci clip.x1 = 0; 5458c2ecf20Sopenharmony_ci clip.x2 = fb->width; 5468c2ecf20Sopenharmony_ci clip.y1 = 0; 5478c2ecf20Sopenharmony_ci clip.y2 = fb->height; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci repaper_get_temperature(epd); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci DRM_DEBUG("Flushing [FB:%d] st=%ums\n", fb->base.id, 5528c2ecf20Sopenharmony_ci epd->factored_stage_time); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci buf = kmalloc_array(fb->width, fb->height, GFP_KERNEL); 5558c2ecf20Sopenharmony_ci if (!buf) { 5568c2ecf20Sopenharmony_ci ret = -ENOMEM; 5578c2ecf20Sopenharmony_ci goto out_exit; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci if (import_attach) { 5618c2ecf20Sopenharmony_ci ret = dma_buf_begin_cpu_access(import_attach->dmabuf, 5628c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5638c2ecf20Sopenharmony_ci if (ret) 5648c2ecf20Sopenharmony_ci goto out_free; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci drm_fb_xrgb8888_to_gray8(buf, cma_obj->vaddr, fb, &clip); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (import_attach) { 5708c2ecf20Sopenharmony_ci ret = dma_buf_end_cpu_access(import_attach->dmabuf, 5718c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 5728c2ecf20Sopenharmony_ci if (ret) 5738c2ecf20Sopenharmony_ci goto out_free; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci repaper_gray8_to_mono_reversed(buf, fb->width, fb->height); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (epd->partial) { 5798c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, buf, epd->current_frame, 5808c2ecf20Sopenharmony_ci REPAPER_NORMAL); 5818c2ecf20Sopenharmony_ci } else if (epd->cleared) { 5828c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, epd->current_frame, NULL, 5838c2ecf20Sopenharmony_ci REPAPER_COMPENSATE); 5848c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, epd->current_frame, NULL, 5858c2ecf20Sopenharmony_ci REPAPER_WHITE); 5868c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, buf, NULL, REPAPER_INVERSE); 5878c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, buf, NULL, REPAPER_NORMAL); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci epd->partial = true; 5908c2ecf20Sopenharmony_ci } else { 5918c2ecf20Sopenharmony_ci /* Clear display (anything -> white) */ 5928c2ecf20Sopenharmony_ci repaper_frame_fixed_repeat(epd, 0xff, REPAPER_COMPENSATE); 5938c2ecf20Sopenharmony_ci repaper_frame_fixed_repeat(epd, 0xff, REPAPER_WHITE); 5948c2ecf20Sopenharmony_ci repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_INVERSE); 5958c2ecf20Sopenharmony_ci repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_NORMAL); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Assuming a clear (white) screen output an image */ 5988c2ecf20Sopenharmony_ci repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_COMPENSATE); 5998c2ecf20Sopenharmony_ci repaper_frame_fixed_repeat(epd, 0xaa, REPAPER_WHITE); 6008c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, buf, NULL, REPAPER_INVERSE); 6018c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, buf, NULL, REPAPER_NORMAL); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci epd->cleared = true; 6048c2ecf20Sopenharmony_ci epd->partial = true; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci memcpy(epd->current_frame, buf, fb->width * fb->height / 8); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* 6108c2ecf20Sopenharmony_ci * An extra frame write is needed if pixels are set in the bottom line, 6118c2ecf20Sopenharmony_ci * or else grey lines rises up from the pixels 6128c2ecf20Sopenharmony_ci */ 6138c2ecf20Sopenharmony_ci if (epd->pre_border_byte) { 6148c2ecf20Sopenharmony_ci unsigned int x; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci for (x = 0; x < (fb->width / 8); x++) 6178c2ecf20Sopenharmony_ci if (buf[x + (fb->width * (fb->height - 1) / 8)]) { 6188c2ecf20Sopenharmony_ci repaper_frame_data_repeat(epd, buf, 6198c2ecf20Sopenharmony_ci epd->current_frame, 6208c2ecf20Sopenharmony_ci REPAPER_NORMAL); 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci } 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ciout_free: 6268c2ecf20Sopenharmony_ci kfree(buf); 6278c2ecf20Sopenharmony_ciout_exit: 6288c2ecf20Sopenharmony_ci drm_dev_exit(idx); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return ret; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic void power_off(struct repaper_epd *epd) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci /* Turn off power and all signals */ 6368c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->reset, 0); 6378c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->panel_on, 0); 6388c2ecf20Sopenharmony_ci if (epd->border) 6398c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->border, 0); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci /* Ensure SPI MOSI and CLOCK are Low before CS Low */ 6428c2ecf20Sopenharmony_ci repaper_spi_mosi_low(epd->spi); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* Discharge pulse */ 6458c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->discharge, 1); 6468c2ecf20Sopenharmony_ci msleep(150); 6478c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->discharge, 0); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic void repaper_pipe_enable(struct drm_simple_display_pipe *pipe, 6518c2ecf20Sopenharmony_ci struct drm_crtc_state *crtc_state, 6528c2ecf20Sopenharmony_ci struct drm_plane_state *plane_state) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev); 6558c2ecf20Sopenharmony_ci struct spi_device *spi = epd->spi; 6568c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 6578c2ecf20Sopenharmony_ci bool dc_ok = false; 6588c2ecf20Sopenharmony_ci int i, ret, idx; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (!drm_dev_enter(pipe->crtc.dev, &idx)) 6618c2ecf20Sopenharmony_ci return; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\n"); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* Power up sequence */ 6668c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->reset, 0); 6678c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->panel_on, 0); 6688c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->discharge, 0); 6698c2ecf20Sopenharmony_ci if (epd->border) 6708c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->border, 0); 6718c2ecf20Sopenharmony_ci repaper_spi_mosi_low(spi); 6728c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->panel_on, 1); 6758c2ecf20Sopenharmony_ci /* 6768c2ecf20Sopenharmony_ci * This delay comes from the repaper.org userspace driver, it's not 6778c2ecf20Sopenharmony_ci * mentioned in the datasheet. 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci usleep_range(10000, 15000); 6808c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->reset, 1); 6818c2ecf20Sopenharmony_ci if (epd->border) 6828c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->border, 1); 6838c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 6848c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->reset, 0); 6858c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 6868c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->reset, 1); 6878c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* Wait for COG to become ready */ 6908c2ecf20Sopenharmony_ci for (i = 100; i > 0; i--) { 6918c2ecf20Sopenharmony_ci if (!gpiod_get_value_cansleep(epd->busy)) 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci usleep_range(10, 100); 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (!i) { 6988c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n"); 6998c2ecf20Sopenharmony_ci power_off(epd); 7008c2ecf20Sopenharmony_ci goto out_exit; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci repaper_read_id(spi); 7048c2ecf20Sopenharmony_ci ret = repaper_read_id(spi); 7058c2ecf20Sopenharmony_ci if (ret != REPAPER_RID_G2_COG_ID) { 7068c2ecf20Sopenharmony_ci if (ret < 0) 7078c2ecf20Sopenharmony_ci dev_err(dev, "failed to read chip (%d)\n", ret); 7088c2ecf20Sopenharmony_ci else 7098c2ecf20Sopenharmony_ci dev_err(dev, "wrong COG ID 0x%02x\n", ret); 7108c2ecf20Sopenharmony_ci power_off(epd); 7118c2ecf20Sopenharmony_ci goto out_exit; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Disable OE */ 7158c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x02, 0x40); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci ret = repaper_read_val(spi, 0x0f); 7188c2ecf20Sopenharmony_ci if (ret < 0 || !(ret & 0x80)) { 7198c2ecf20Sopenharmony_ci if (ret < 0) 7208c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to read chip (%d)\n", ret); 7218c2ecf20Sopenharmony_ci else 7228c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "panel is reported broken\n"); 7238c2ecf20Sopenharmony_ci power_off(epd); 7248c2ecf20Sopenharmony_ci goto out_exit; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Power saving mode */ 7288c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x0b, 0x02); 7298c2ecf20Sopenharmony_ci /* Channel select */ 7308c2ecf20Sopenharmony_ci repaper_write_buf(spi, 0x01, epd->channel_select, 8); 7318c2ecf20Sopenharmony_ci /* High power mode osc */ 7328c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x07, 0xd1); 7338c2ecf20Sopenharmony_ci /* Power setting */ 7348c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x08, 0x02); 7358c2ecf20Sopenharmony_ci /* Vcom level */ 7368c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x09, 0xc2); 7378c2ecf20Sopenharmony_ci /* Power setting */ 7388c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x04, 0x03); 7398c2ecf20Sopenharmony_ci /* Driver latch on */ 7408c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x03, 0x01); 7418c2ecf20Sopenharmony_ci /* Driver latch off */ 7428c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x03, 0x00); 7438c2ecf20Sopenharmony_ci usleep_range(5000, 10000); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* Start chargepump */ 7468c2ecf20Sopenharmony_ci for (i = 0; i < 4; ++i) { 7478c2ecf20Sopenharmony_ci /* Charge pump positive voltage on - VGH/VDL on */ 7488c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x05, 0x01); 7498c2ecf20Sopenharmony_ci msleep(240); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci /* Charge pump negative voltage on - VGL/VDL on */ 7528c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x05, 0x03); 7538c2ecf20Sopenharmony_ci msleep(40); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci /* Charge pump Vcom on - Vcom driver on */ 7568c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x05, 0x0f); 7578c2ecf20Sopenharmony_ci msleep(40); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* check DC/DC */ 7608c2ecf20Sopenharmony_ci ret = repaper_read_val(spi, 0x0f); 7618c2ecf20Sopenharmony_ci if (ret < 0) { 7628c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to read chip (%d)\n", ret); 7638c2ecf20Sopenharmony_ci power_off(epd); 7648c2ecf20Sopenharmony_ci goto out_exit; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if (ret & 0x40) { 7688c2ecf20Sopenharmony_ci dc_ok = true; 7698c2ecf20Sopenharmony_ci break; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci if (!dc_ok) { 7748c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "dc/dc failed\n"); 7758c2ecf20Sopenharmony_ci power_off(epd); 7768c2ecf20Sopenharmony_ci goto out_exit; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* 7808c2ecf20Sopenharmony_ci * Output enable to disable 7818c2ecf20Sopenharmony_ci * The userspace driver sets this to 0x04, but the datasheet says 0x06 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x02, 0x04); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci epd->partial = false; 7868c2ecf20Sopenharmony_ciout_exit: 7878c2ecf20Sopenharmony_ci drm_dev_exit(idx); 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic void repaper_pipe_disable(struct drm_simple_display_pipe *pipe) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev); 7938c2ecf20Sopenharmony_ci struct spi_device *spi = epd->spi; 7948c2ecf20Sopenharmony_ci unsigned int line; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* 7978c2ecf20Sopenharmony_ci * This callback is not protected by drm_dev_enter/exit since we want to 7988c2ecf20Sopenharmony_ci * turn off the display on regular driver unload. It's highly unlikely 7998c2ecf20Sopenharmony_ci * that the underlying SPI controller is gone should this be called after 8008c2ecf20Sopenharmony_ci * unplug. 8018c2ecf20Sopenharmony_ci */ 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("\n"); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci /* Nothing frame */ 8068c2ecf20Sopenharmony_ci for (line = 0; line < epd->height; line++) 8078c2ecf20Sopenharmony_ci repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL, 8088c2ecf20Sopenharmony_ci REPAPER_COMPENSATE); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci /* 2.7" */ 8118c2ecf20Sopenharmony_ci if (epd->border) { 8128c2ecf20Sopenharmony_ci /* Dummy line */ 8138c2ecf20Sopenharmony_ci repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL, 8148c2ecf20Sopenharmony_ci REPAPER_COMPENSATE); 8158c2ecf20Sopenharmony_ci msleep(25); 8168c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->border, 0); 8178c2ecf20Sopenharmony_ci msleep(200); 8188c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(epd->border, 1); 8198c2ecf20Sopenharmony_ci } else { 8208c2ecf20Sopenharmony_ci /* Border dummy line */ 8218c2ecf20Sopenharmony_ci repaper_one_line(epd, 0x7fffu, NULL, 0x00, NULL, 8228c2ecf20Sopenharmony_ci REPAPER_NORMAL); 8238c2ecf20Sopenharmony_ci msleep(200); 8248c2ecf20Sopenharmony_ci } 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci /* not described in datasheet */ 8278c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x0b, 0x00); 8288c2ecf20Sopenharmony_ci /* Latch reset turn on */ 8298c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x03, 0x01); 8308c2ecf20Sopenharmony_ci /* Power off charge pump Vcom */ 8318c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x05, 0x03); 8328c2ecf20Sopenharmony_ci /* Power off charge pump neg voltage */ 8338c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x05, 0x01); 8348c2ecf20Sopenharmony_ci msleep(120); 8358c2ecf20Sopenharmony_ci /* Discharge internal */ 8368c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x04, 0x80); 8378c2ecf20Sopenharmony_ci /* turn off all charge pumps */ 8388c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x05, 0x00); 8398c2ecf20Sopenharmony_ci /* Turn off osc */ 8408c2ecf20Sopenharmony_ci repaper_write_val(spi, 0x07, 0x01); 8418c2ecf20Sopenharmony_ci msleep(50); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci power_off(epd); 8448c2ecf20Sopenharmony_ci} 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_cistatic void repaper_pipe_update(struct drm_simple_display_pipe *pipe, 8478c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci struct drm_plane_state *state = pipe->plane.state; 8508c2ecf20Sopenharmony_ci struct drm_rect rect; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (!pipe->crtc.state->active) 8538c2ecf20Sopenharmony_ci return; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (drm_atomic_helper_damage_merged(old_state, state, &rect)) 8568c2ecf20Sopenharmony_ci repaper_fb_dirty(state->fb); 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = { 8608c2ecf20Sopenharmony_ci .enable = repaper_pipe_enable, 8618c2ecf20Sopenharmony_ci .disable = repaper_pipe_disable, 8628c2ecf20Sopenharmony_ci .update = repaper_pipe_update, 8638c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 8648c2ecf20Sopenharmony_ci}; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic int repaper_connector_get_modes(struct drm_connector *connector) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct repaper_epd *epd = drm_to_epd(connector->dev); 8698c2ecf20Sopenharmony_ci struct drm_display_mode *mode; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci mode = drm_mode_duplicate(connector->dev, epd->mode); 8728c2ecf20Sopenharmony_ci if (!mode) { 8738c2ecf20Sopenharmony_ci DRM_ERROR("Failed to duplicate mode\n"); 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci drm_mode_set_name(mode); 8788c2ecf20Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 8798c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, mode); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci connector->display_info.width_mm = mode->width_mm; 8828c2ecf20Sopenharmony_ci connector->display_info.height_mm = mode->height_mm; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci return 1; 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs repaper_connector_hfuncs = { 8888c2ecf20Sopenharmony_ci .get_modes = repaper_connector_get_modes, 8898c2ecf20Sopenharmony_ci}; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs repaper_connector_funcs = { 8928c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 8938c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 8948c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 8958c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 8968c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs repaper_mode_config_funcs = { 9008c2ecf20Sopenharmony_ci .fb_create = drm_gem_fb_create_with_dirty, 9018c2ecf20Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 9028c2ecf20Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 9038c2ecf20Sopenharmony_ci}; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cistatic const uint32_t repaper_formats[] = { 9068c2ecf20Sopenharmony_ci DRM_FORMAT_XRGB8888, 9078c2ecf20Sopenharmony_ci}; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic const struct drm_display_mode repaper_e1144cs021_mode = { 9108c2ecf20Sopenharmony_ci DRM_SIMPLE_MODE(128, 96, 29, 22), 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_cistatic const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00, 9148c2ecf20Sopenharmony_ci 0x00, 0x0f, 0xff, 0x00 }; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic const struct drm_display_mode repaper_e1190cs021_mode = { 9178c2ecf20Sopenharmony_ci DRM_SIMPLE_MODE(144, 128, 36, 32), 9188c2ecf20Sopenharmony_ci}; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_cistatic const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03, 9218c2ecf20Sopenharmony_ci 0xfc, 0x00, 0x00, 0xff }; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic const struct drm_display_mode repaper_e2200cs021_mode = { 9248c2ecf20Sopenharmony_ci DRM_SIMPLE_MODE(200, 96, 46, 22), 9258c2ecf20Sopenharmony_ci}; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00, 9288c2ecf20Sopenharmony_ci 0x01, 0xff, 0xe0, 0x00 }; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic const struct drm_display_mode repaper_e2271cs021_mode = { 9318c2ecf20Sopenharmony_ci DRM_SIMPLE_MODE(264, 176, 57, 38), 9328c2ecf20Sopenharmony_ci}; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f, 9358c2ecf20Sopenharmony_ci 0xff, 0xfe, 0x00, 0x00 }; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ciDEFINE_DRM_GEM_CMA_FOPS(repaper_fops); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic struct drm_driver repaper_driver = { 9408c2ecf20Sopenharmony_ci .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 9418c2ecf20Sopenharmony_ci .fops = &repaper_fops, 9428c2ecf20Sopenharmony_ci DRM_GEM_CMA_DRIVER_OPS_VMAP, 9438c2ecf20Sopenharmony_ci .name = "repaper", 9448c2ecf20Sopenharmony_ci .desc = "Pervasive Displays RePaper e-ink panels", 9458c2ecf20Sopenharmony_ci .date = "20170405", 9468c2ecf20Sopenharmony_ci .major = 1, 9478c2ecf20Sopenharmony_ci .minor = 0, 9488c2ecf20Sopenharmony_ci}; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_cistatic const struct of_device_id repaper_of_match[] = { 9518c2ecf20Sopenharmony_ci { .compatible = "pervasive,e1144cs021", .data = (void *)E1144CS021 }, 9528c2ecf20Sopenharmony_ci { .compatible = "pervasive,e1190cs021", .data = (void *)E1190CS021 }, 9538c2ecf20Sopenharmony_ci { .compatible = "pervasive,e2200cs021", .data = (void *)E2200CS021 }, 9548c2ecf20Sopenharmony_ci { .compatible = "pervasive,e2271cs021", .data = (void *)E2271CS021 }, 9558c2ecf20Sopenharmony_ci {}, 9568c2ecf20Sopenharmony_ci}; 9578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, repaper_of_match); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic const struct spi_device_id repaper_id[] = { 9608c2ecf20Sopenharmony_ci { "e1144cs021", E1144CS021 }, 9618c2ecf20Sopenharmony_ci { "e1190cs021", E1190CS021 }, 9628c2ecf20Sopenharmony_ci { "e2200cs021", E2200CS021 }, 9638c2ecf20Sopenharmony_ci { "e2271cs021", E2271CS021 }, 9648c2ecf20Sopenharmony_ci { }, 9658c2ecf20Sopenharmony_ci}; 9668c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, repaper_id); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic int repaper_probe(struct spi_device *spi) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci const struct drm_display_mode *mode; 9718c2ecf20Sopenharmony_ci const struct spi_device_id *spi_id; 9728c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 9738c2ecf20Sopenharmony_ci enum repaper_model model; 9748c2ecf20Sopenharmony_ci const char *thermal_zone; 9758c2ecf20Sopenharmony_ci struct repaper_epd *epd; 9768c2ecf20Sopenharmony_ci size_t line_buffer_size; 9778c2ecf20Sopenharmony_ci struct drm_device *drm; 9788c2ecf20Sopenharmony_ci const void *match; 9798c2ecf20Sopenharmony_ci int ret; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci match = device_get_match_data(dev); 9828c2ecf20Sopenharmony_ci if (match) { 9838c2ecf20Sopenharmony_ci model = (enum repaper_model)match; 9848c2ecf20Sopenharmony_ci } else { 9858c2ecf20Sopenharmony_ci spi_id = spi_get_device_id(spi); 9868c2ecf20Sopenharmony_ci model = (enum repaper_model)spi_id->driver_data; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* The SPI device is used to allocate dma memory */ 9908c2ecf20Sopenharmony_ci if (!dev->coherent_dma_mask) { 9918c2ecf20Sopenharmony_ci ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); 9928c2ecf20Sopenharmony_ci if (ret) { 9938c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to set dma mask %d\n", ret); 9948c2ecf20Sopenharmony_ci return ret; 9958c2ecf20Sopenharmony_ci } 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci epd = devm_drm_dev_alloc(dev, &repaper_driver, 9998c2ecf20Sopenharmony_ci struct repaper_epd, drm); 10008c2ecf20Sopenharmony_ci if (IS_ERR(epd)) 10018c2ecf20Sopenharmony_ci return PTR_ERR(epd); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci drm = &epd->drm; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci ret = drmm_mode_config_init(drm); 10068c2ecf20Sopenharmony_ci if (ret) 10078c2ecf20Sopenharmony_ci return ret; 10088c2ecf20Sopenharmony_ci drm->mode_config.funcs = &repaper_mode_config_funcs; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci epd->spi = spi; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW); 10138c2ecf20Sopenharmony_ci if (IS_ERR(epd->panel_on)) { 10148c2ecf20Sopenharmony_ci ret = PTR_ERR(epd->panel_on); 10158c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10168c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'panel-on'\n"); 10178c2ecf20Sopenharmony_ci return ret; 10188c2ecf20Sopenharmony_ci } 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci epd->discharge = devm_gpiod_get(dev, "discharge", GPIOD_OUT_LOW); 10218c2ecf20Sopenharmony_ci if (IS_ERR(epd->discharge)) { 10228c2ecf20Sopenharmony_ci ret = PTR_ERR(epd->discharge); 10238c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10248c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'discharge'\n"); 10258c2ecf20Sopenharmony_ci return ret; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci epd->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); 10298c2ecf20Sopenharmony_ci if (IS_ERR(epd->reset)) { 10308c2ecf20Sopenharmony_ci ret = PTR_ERR(epd->reset); 10318c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10328c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n"); 10338c2ecf20Sopenharmony_ci return ret; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci epd->busy = devm_gpiod_get(dev, "busy", GPIOD_IN); 10378c2ecf20Sopenharmony_ci if (IS_ERR(epd->busy)) { 10388c2ecf20Sopenharmony_ci ret = PTR_ERR(epd->busy); 10398c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10408c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'busy'\n"); 10418c2ecf20Sopenharmony_ci return ret; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (!device_property_read_string(dev, "pervasive,thermal-zone", 10458c2ecf20Sopenharmony_ci &thermal_zone)) { 10468c2ecf20Sopenharmony_ci epd->thermal = thermal_zone_get_zone_by_name(thermal_zone); 10478c2ecf20Sopenharmony_ci if (IS_ERR(epd->thermal)) { 10488c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get thermal zone: %s\n", thermal_zone); 10498c2ecf20Sopenharmony_ci return PTR_ERR(epd->thermal); 10508c2ecf20Sopenharmony_ci } 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci switch (model) { 10548c2ecf20Sopenharmony_ci case E1144CS021: 10558c2ecf20Sopenharmony_ci mode = &repaper_e1144cs021_mode; 10568c2ecf20Sopenharmony_ci epd->channel_select = repaper_e1144cs021_cs; 10578c2ecf20Sopenharmony_ci epd->stage_time = 480; 10588c2ecf20Sopenharmony_ci epd->bytes_per_scan = 96 / 4; 10598c2ecf20Sopenharmony_ci epd->middle_scan = true; /* data-scan-data */ 10608c2ecf20Sopenharmony_ci epd->pre_border_byte = false; 10618c2ecf20Sopenharmony_ci epd->border_byte = REPAPER_BORDER_BYTE_ZERO; 10628c2ecf20Sopenharmony_ci break; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci case E1190CS021: 10658c2ecf20Sopenharmony_ci mode = &repaper_e1190cs021_mode; 10668c2ecf20Sopenharmony_ci epd->channel_select = repaper_e1190cs021_cs; 10678c2ecf20Sopenharmony_ci epd->stage_time = 480; 10688c2ecf20Sopenharmony_ci epd->bytes_per_scan = 128 / 4 / 2; 10698c2ecf20Sopenharmony_ci epd->middle_scan = false; /* scan-data-scan */ 10708c2ecf20Sopenharmony_ci epd->pre_border_byte = false; 10718c2ecf20Sopenharmony_ci epd->border_byte = REPAPER_BORDER_BYTE_SET; 10728c2ecf20Sopenharmony_ci break; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci case E2200CS021: 10758c2ecf20Sopenharmony_ci mode = &repaper_e2200cs021_mode; 10768c2ecf20Sopenharmony_ci epd->channel_select = repaper_e2200cs021_cs; 10778c2ecf20Sopenharmony_ci epd->stage_time = 480; 10788c2ecf20Sopenharmony_ci epd->bytes_per_scan = 96 / 4; 10798c2ecf20Sopenharmony_ci epd->middle_scan = true; /* data-scan-data */ 10808c2ecf20Sopenharmony_ci epd->pre_border_byte = true; 10818c2ecf20Sopenharmony_ci epd->border_byte = REPAPER_BORDER_BYTE_NONE; 10828c2ecf20Sopenharmony_ci break; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci case E2271CS021: 10858c2ecf20Sopenharmony_ci epd->border = devm_gpiod_get(dev, "border", GPIOD_OUT_LOW); 10868c2ecf20Sopenharmony_ci if (IS_ERR(epd->border)) { 10878c2ecf20Sopenharmony_ci ret = PTR_ERR(epd->border); 10888c2ecf20Sopenharmony_ci if (ret != -EPROBE_DEFER) 10898c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "Failed to get gpio 'border'\n"); 10908c2ecf20Sopenharmony_ci return ret; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci mode = &repaper_e2271cs021_mode; 10948c2ecf20Sopenharmony_ci epd->channel_select = repaper_e2271cs021_cs; 10958c2ecf20Sopenharmony_ci epd->stage_time = 630; 10968c2ecf20Sopenharmony_ci epd->bytes_per_scan = 176 / 4; 10978c2ecf20Sopenharmony_ci epd->middle_scan = true; /* data-scan-data */ 10988c2ecf20Sopenharmony_ci epd->pre_border_byte = true; 10998c2ecf20Sopenharmony_ci epd->border_byte = REPAPER_BORDER_BYTE_NONE; 11008c2ecf20Sopenharmony_ci break; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci default: 11038c2ecf20Sopenharmony_ci return -ENODEV; 11048c2ecf20Sopenharmony_ci } 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci epd->mode = mode; 11078c2ecf20Sopenharmony_ci epd->width = mode->hdisplay; 11088c2ecf20Sopenharmony_ci epd->height = mode->vdisplay; 11098c2ecf20Sopenharmony_ci epd->factored_stage_time = epd->stage_time; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci line_buffer_size = 2 * epd->width / 8 + epd->bytes_per_scan + 2; 11128c2ecf20Sopenharmony_ci epd->line_buffer = devm_kzalloc(dev, line_buffer_size, GFP_KERNEL); 11138c2ecf20Sopenharmony_ci if (!epd->line_buffer) 11148c2ecf20Sopenharmony_ci return -ENOMEM; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci epd->current_frame = devm_kzalloc(dev, epd->width * epd->height / 8, 11178c2ecf20Sopenharmony_ci GFP_KERNEL); 11188c2ecf20Sopenharmony_ci if (!epd->current_frame) 11198c2ecf20Sopenharmony_ci return -ENOMEM; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci drm->mode_config.min_width = mode->hdisplay; 11228c2ecf20Sopenharmony_ci drm->mode_config.max_width = mode->hdisplay; 11238c2ecf20Sopenharmony_ci drm->mode_config.min_height = mode->vdisplay; 11248c2ecf20Sopenharmony_ci drm->mode_config.max_height = mode->vdisplay; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci drm_connector_helper_add(&epd->connector, &repaper_connector_hfuncs); 11278c2ecf20Sopenharmony_ci ret = drm_connector_init(drm, &epd->connector, &repaper_connector_funcs, 11288c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_SPI); 11298c2ecf20Sopenharmony_ci if (ret) 11308c2ecf20Sopenharmony_ci return ret; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs, 11338c2ecf20Sopenharmony_ci repaper_formats, ARRAY_SIZE(repaper_formats), 11348c2ecf20Sopenharmony_ci NULL, &epd->connector); 11358c2ecf20Sopenharmony_ci if (ret) 11368c2ecf20Sopenharmony_ci return ret; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci drm_mode_config_reset(drm); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci ret = drm_dev_register(drm, 0); 11418c2ecf20Sopenharmony_ci if (ret) 11428c2ecf20Sopenharmony_ci return ret; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci spi_set_drvdata(spi, drm); 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000); 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci drm_fbdev_generic_setup(drm, 0); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci return 0; 11518c2ecf20Sopenharmony_ci} 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_cistatic int repaper_remove(struct spi_device *spi) 11548c2ecf20Sopenharmony_ci{ 11558c2ecf20Sopenharmony_ci struct drm_device *drm = spi_get_drvdata(spi); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci drm_dev_unplug(drm); 11588c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(drm); 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic void repaper_shutdown(struct spi_device *spi) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci drm_atomic_helper_shutdown(spi_get_drvdata(spi)); 11668c2ecf20Sopenharmony_ci} 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic struct spi_driver repaper_spi_driver = { 11698c2ecf20Sopenharmony_ci .driver = { 11708c2ecf20Sopenharmony_ci .name = "repaper", 11718c2ecf20Sopenharmony_ci .of_match_table = repaper_of_match, 11728c2ecf20Sopenharmony_ci }, 11738c2ecf20Sopenharmony_ci .id_table = repaper_id, 11748c2ecf20Sopenharmony_ci .probe = repaper_probe, 11758c2ecf20Sopenharmony_ci .remove = repaper_remove, 11768c2ecf20Sopenharmony_ci .shutdown = repaper_shutdown, 11778c2ecf20Sopenharmony_ci}; 11788c2ecf20Sopenharmony_cimodule_spi_driver(repaper_spi_driver); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pervasive Displays RePaper DRM driver"); 11818c2ecf20Sopenharmony_ciMODULE_AUTHOR("Noralf Trønnes"); 11828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1183