/*
* DRM driver for Pervasive Displays RePaper branded e-ink panels
*
* Copyright 2013-2017 Pervasive Displays, Inc.
* Copyright 2017 Noralf Trønnes
*
* The driver supports:
* Material Film: Aurora Mb (V231)
* Driver IC: G2 (eTC)
*
* The controller code was taken from the userspace driver:
* https://github.com/repaper/gratis
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/delay.h>
#include <linux/dma-buf.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/sched/clock.h>
#include <linux/spi/spi.h>
#include <linux/thermal.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/tinydrm/tinydrm.h>
#include <drm/tinydrm/tinydrm-helpers.h>
#define REPAPER_RID_G2_COG_ID 0x12
enum repaper_model {
E1144CS021 = 1,
E1190CS021,
E2200CS021,
E2271CS021,
};
enum repaper_stage { /* Image pixel -> Display pixel */
REPAPER_COMPENSATE, /* B -> W, W -> B (Current Image) */
REPAPER_WHITE, /* B -> N, W -> W (Current Image) */
REPAPER_INVERSE, /* B -> N, W -> B (New Image) */
REPAPER_NORMAL /* B -> B, W -> W (New Image) */
};
enum repaper_epd_border_byte {
REPAPER_BORDER_BYTE_NONE,
REPAPER_BORDER_BYTE_ZERO,
REPAPER_BORDER_BYTE_SET,
};
struct repaper_epd {
struct tinydrm_device tinydrm;
struct spi_device *spi;
struct gpio_desc *panel_on;
struct gpio_desc *border;
struct gpio_desc *discharge;
struct gpio_desc *reset;
struct gpio_desc *busy;
struct thermal_zone_device *thermal;
unsigned int height;
unsigned int width;
unsigned int bytes_per_scan;
const u8 *channel_select;
unsigned int stage_time;
unsigned int factored_stage_time;
bool middle_scan;
bool pre_border_byte;
enum repaper_epd_border_byte border_byte;
u8 *line_buffer;
void *current_frame;
bool enabled;
bool cleared;
bool partial;
};
static inline struct repaper_epd *
epd_from_tinydrm(struct tinydrm_device *tdev)
{
return container_of(tdev, struct repaper_epd, tinydrm);
}
static int repaper_spi_transfer(struct spi_device *spi, u8 header,
const void *tx, void *rx, size_t len)
{
void *txbuf = NULL, *rxbuf = NULL;
struct spi_transfer tr[2] = {};
u8 *headerbuf;
int ret;
headerbuf = kmalloc(1, GFP_KERNEL);
if (!headerbuf)
return -ENOMEM;
headerbuf[0] = header;
tr[0].tx_buf = headerbuf;
tr[0].len = 1;
/* Stack allocated tx? */
if (tx && len <= 32) {
txbuf = kmemdup(tx, len, GFP_KERNEL);
if (!txbuf) {
ret = -ENOMEM;
goto out_free;
}
}
if (rx) {
rxbuf = kmalloc(len, GFP_KERNEL);
if (!rxbuf) {
ret = -ENOMEM;
goto out_free;
}
}
tr[1].tx_buf = txbuf ? txbuf : tx;
tr[1].rx_buf = rxbuf;
tr[1].len = len;
ndelay(80);
ret = spi_sync_transfer(spi, tr, 2);
if (rx && !ret)
memcpy(rx, rxbuf, len);
out_free:
kfree(headerbuf);
kfree(txbuf);
kfree(rxbuf);
return ret;
}
static int repaper_write_buf(struct spi_device *spi, u8 reg,
const u8 *buf, size_t len)
{
int ret;
ret = repaper_spi_transfer(spi, 0x70, ®, NULL, 1);
if (ret)
return ret;
return repaper_spi_transfer(spi, 0x72, buf, NULL, len);
}
static int repaper_write_val(struct spi_device *spi, u8 reg, u8 val)
{
return repaper_write_buf(spi, reg, &val, 1);
}
static int repaper_read_val(struct spi_device *spi, u8 reg)
{
int ret;
u8 val;
ret = repaper_spi_transfer(spi, 0x70, ®, NULL, 1);
if (ret)
return ret;
ret = repaper_spi_transfer(spi, 0x73, NULL, &val, 1);
return ret ? ret : val;
}
static int repaper_read_id(struct spi_device *spi)
{
int ret;
u8 id;
ret = repaper_spi_transfer(spi, 0x71, NULL, &id