#include <linux/delay.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <drm/clients/drm_client_setup.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_shmem.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#define PIXPAPER_CMD_PANEL_SETTING 0x00
#define PIXPAPER_CMD_POWER_SETTING 0x01
#define PIXPAPER_CMD_POWER_OFF 0x02
#define PIXPAPER_CMD_POWER_OFF_SEQUENCE 0x03
#define PIXPAPER_CMD_POWER_ON 0x04
#define PIXPAPER_CMD_BOOSTER_SOFT_START 0x06
#define PIXPAPER_CMD_DEEP_SLEEP 0x07
#define PIXPAPER_CMD_DATA_START_TRANSMISSION 0x10
#define PIXPAPER_CMD_DISPLAY_REFRESH 0x12
#define PIXPAPER_CMD_PLL_CONTROL 0x30
#define PIXPAPER_CMD_TEMP_SENSOR_CALIB 0x41
#define PIXPAPER_CMD_UNKNOWN_4D 0x4D
#define PIXPAPER_CMD_VCOM_INTERVAL 0x50
#define PIXPAPER_CMD_UNKNOWN_60 0x60
#define PIXPAPER_CMD_RESOLUTION_SETTING 0x61
#define PIXPAPER_CMD_GATE_SOURCE_START 0x65
#define PIXPAPER_CMD_UNKNOWN_B4 0xB4
#define PIXPAPER_CMD_UNKNOWN_B5 0xB5
#define PIXPAPER_CMD_UNKNOWN_E0 0xE0
#define PIXPAPER_CMD_POWER_SAVING 0xE3
#define PIXPAPER_CMD_UNKNOWN_E7 0xE7
#define PIXPAPER_CMD_UNKNOWN_E9 0xE9
#define PIXPAPER_PSR_RST_N BIT(0)
#define PIXPAPER_PSR_SHD_N BIT(1)
#define PIXPAPER_PSR_SHL BIT(2)
#define PIXPAPER_PSR_UD BIT(3)
#define PIXPAPER_PSR_PST_MODE BIT(5)
#define PIXPAPER_PSR_RES_MASK (3 << 6)
#define PIXPAPER_PSR_RES_176x296 (0x0 << 6)
#define PIXPAPER_PSR_RES_128x296 (0x1 << 6)
#define PIXPAPER_PSR_RES_128x250 (0x2 << 6)
#define PIXPAPER_PSR_RES_112x204 (0x3 << 6)
#define PIXPAPER_PSR_CONFIG \
(PIXPAPER_PSR_RST_N | PIXPAPER_PSR_SHD_N | PIXPAPER_PSR_SHL | \
PIXPAPER_PSR_UD)
#define PIXPAPER_PSR2_VC_LUTZ \
(1 << 0)
#define PIXPAPER_PSR2_NORG \
(1 << 1)
#define PIXPAPER_PSR2_TIEG \
(1 << 2)
#define PIXPAPER_PSR2_TS_AUTO \
(1 << 3)
#define PIXPAPER_PSR2_VCMZ \
(1 << 4)
#define PIXPAPER_PSR2_FOPT \
(1 << 5)
#define PIXPAPER_PSR_CONFIG2 \
(PIXPAPER_PSR2_VC_LUTZ | \
PIXPAPER_PSR2_TS_AUTO)
#define PIXPAPER_PWR_VDG_EN \
(1 << 0)
#define PIXPAPER_PWR_VDS_EN \
(1 << 1)
#define PIXPAPER_PWR_VSC_EN \
(1 << 2)
#define PIXPAPER_PWR_V_MODE \
(1 << 3)
#define PIXPAPER_PWR_CONFIG1 \
(PIXPAPER_PWR_VDG_EN | PIXPAPER_PWR_VDS_EN | \
PIXPAPER_PWR_VSC_EN)
#define PIXPAPER_PWR_VGPN_MASK \
(3 << 0)
#define PIXPAPER_PWR_VGPN_20V (0x0 << 0)
#define PIXPAPER_PWR_VGPN_17V (0x1 << 0)
#define PIXPAPER_PWR_VGPN_15V (0x2 << 0)
#define PIXPAPER_PWR_VGPN_10V (0x3 << 0)
#define PIXPAPER_PWR_CONFIG2 PIXPAPER_PWR_VGPN_20V
#define PIXPAPER_PWR_VSP_8_2V 0x22
#define PIXPAPER_PWR_VSPL_15V 0x78
#define PIXPAPER_PWR_VSN_4V 0x0A
#define PIXPAPER_PFS_T_VDS_OFF_MASK \
(3 << 0)
#define PIXPAPER_PFS_T_VDS_OFF_20MS (0x0 << 0)
#define PIXPAPER_PFS_T_VDS_OFF_40MS (0x1 << 0)
#define PIXPAPER_PFS_T_VDS_OFF_60MS (0x2 << 0)
#define PIXPAPER_PFS_T_VDS_OFF_80MS (0x3 << 0)
#define PIXPAPER_PFS_T_VDPG_OFF_MASK \
(3 << 4)
#define PIXPAPER_PFS_T_VDPG_OFF_20MS (0x0 << 4)
#define PIXPAPER_PFS_T_VDPG_OFF_40MS (0x1 << 4)
#define PIXPAPER_PFS_T_VDPG_OFF_60MS (0x2 << 4)
#define PIXPAPER_PFS_T_VDPG_OFF_80MS (0x3 << 4)
#define PIXPAPER_PFS_CONFIG1 \
(PIXPAPER_PFS_T_VDS_OFF_20MS | \
PIXPAPER_PFS_T_VDPG_OFF_20MS)
#define PIXPAPER_PFS_VGP_EXT_MASK \
(0xF << 0)
#define PIXPAPER_PFS_VGP_EXT_0MS (0x0 << 0)
#define PIXPAPER_PFS_VGP_EXT_500MS (0x1 << 0)
#define PIXPAPER_PFS_VGP_EXT_1000MS (0x2 << 0)
#define PIXPAPER_PFS_VGP_EXT_1500MS (0x3 << 0)
#define PIXPAPER_PFS_VGP_EXT_2000MS (0x4 << 0)
#define PIXPAPER_PFS_VGP_EXT_2500MS (0x5 << 0)
#define PIXPAPER_PFS_VGP_EXT_3000MS (0x6 << 0)
#define PIXPAPER_PFS_VGP_EXT_3500MS (0x7 << 0)
#define PIXPAPER_PFS_VGP_EXT_4000MS (0x8 << 0)
#define PIXPAPER_PFS_VGP_EXT_4500MS (0x9 << 0)
#define PIXPAPER_PFS_VGP_EXT_5000MS (0xA << 0)
#define PIXPAPER_PFS_VGP_EXT_5500MS (0xB << 0)
#define PIXPAPER_PFS_VGP_EXT_6000MS (0xC << 0)
#define PIXPAPER_PFS_VGP_EXT_6500MS (0xD << 0)
#define PIXPAPER_PFS_VGP_LEN_MASK \
(0xF << 4)
#define PIXPAPER_PFS_VGP_LEN_0MS (0x0 << 4)
#define PIXPAPER_PFS_VGP_LEN_500MS (0x1 << 4)
#define PIXPAPER_PFS_VGP_LEN_1000MS (0x2 << 4)
#define PIXPAPER_PFS_VGP_LEN_1500MS (0x3 << 4)
#define PIXPAPER_PFS_VGP_LEN_2000MS (0x4 << 4)
#define PIXPAPER_PFS_VGP_LEN_2500MS (0x5 << 4)
#define PIXPAPER_PFS_VGP_LEN_3000MS (0x6 << 4)
#define PIXPAPER_PFS_VGP_LEN_3500MS (0x7 << 4)
#define PIXPAPER_PFS_VGP_LEN_4000MS (0x8 << 4)
#define PIXPAPER_PFS_VGP_LEN_4500MS (0x9 << 4)
#define PIXPAPER_PFS_VGP_LEN_5000MS (0xA << 4)
#define PIXPAPER_PFS_VGP_LEN_5500MS (0xB << 4)
#define PIXPAPER_PFS_VGP_LEN_6000MS (0xC << 4)
#define PIXPAPER_PFS_VGP_LEN_6500MS (0xD << 4)
#define PIXPAPER_PFS_CONFIG2 \
(PIXPAPER_PFS_VGP_EXT_1000MS | \
PIXPAPER_PFS_VGP_LEN_2500MS)
#define PIXPAPER_PFS_XON_LEN_MASK \
(0xF << 0)
#define PIXPAPER_PFS_XON_LEN_0MS (0x0 << 0)
#define PIXPAPER_PFS_XON_LEN_500MS (0x1 << 0)
#define PIXPAPER_PFS_XON_LEN_1000MS (0x2 << 0)
#define PIXPAPER_PFS_XON_LEN_1500MS (0x3 << 0)
#define PIXPAPER_PFS_XON_LEN_2000MS (0x4 << 0)
#define PIXPAPER_PFS_XON_LEN_2500MS (0x5 << 0)
#define PIXPAPER_PFS_XON_LEN_3000MS (0x6 << 0)
#define PIXPAPER_PFS_XON_LEN_3500MS (0x7 << 0)
#define PIXPAPER_PFS_XON_LEN_4000MS (0x8 << 0)
#define PIXPAPER_PFS_XON_LEN_4500MS (0x9 << 0)
#define PIXPAPER_PFS_XON_LEN_5000MS (0xA << 0)
#define PIXPAPER_PFS_XON_LEN_5500MS (0xB << 0)
#define PIXPAPER_PFS_XON_LEN_6000MS (0xC << 0)
#define PIXPAPER_PFS_XON_DLY_MASK \
(0xF << 4)
#define PIXPAPER_PFS_XON_DLY_0MS (0x0 << 4)
#define PIXPAPER_PFS_XON_DLY_500MS (0x1 << 4)
#define PIXPAPER_PFS_XON_DLY_1000MS (0x2 << 4)
#define PIXPAPER_PFS_XON_DLY_1500MS (0x3 << 4)
#define PIXPAPER_PFS_XON_DLY_2000MS (0x4 << 4)
#define PIXPAPER_PFS_XON_DLY_2500MS (0x5 << 4)
#define PIXPAPER_PFS_XON_DLY_3000MS (0x6 << 4)
#define PIXPAPER_PFS_XON_DLY_3500MS (0x7 << 4)
#define PIXPAPER_PFS_XON_DLY_4000MS (0x8 << 4)
#define PIXPAPER_PFS_XON_DLY_4500MS (0x9 << 4)
#define PIXPAPER_PFS_XON_DLY_5000MS (0xA << 4)
#define PIXPAPER_PFS_XON_DLY_5500MS (0xB << 4)
#define PIXPAPER_PFS_XON_DLY_6000MS (0xC << 4)
#define PIXPAPER_PFS_CONFIG3 \
(PIXPAPER_PFS_XON_LEN_2000MS | \
PIXPAPER_PFS_XON_DLY_2000MS)
#define PIXPAPER_BTST_PHA_SFT_MASK \
(3 << 0)
#define PIXPAPER_BTST_PHA_SFT_10MS (0x0 << 0)
#define PIXPAPER_BTST_PHA_SFT_20MS (0x1 << 0)
#define PIXPAPER_BTST_PHA_SFT_30MS (0x2 << 0)
#define PIXPAPER_BTST_PHA_SFT_40MS (0x3 << 0)
#define PIXPAPER_BTST_PHB_SFT_MASK \
(3 << 2)
#define PIXPAPER_BTST_PHB_SFT_10MS (0x0 << 2)
#define PIXPAPER_BTST_PHB_SFT_20MS (0x1 << 2)
#define PIXPAPER_BTST_PHB_SFT_30MS (0x2 << 2)
#define PIXPAPER_BTST_PHB_SFT_40MS (0x3 << 2)
#define PIXPAPER_BTST_CONFIG1 \
(PIXPAPER_BTST_PHA_SFT_40MS | \
PIXPAPER_BTST_PHB_SFT_40MS)
#define PIXPAPER_BTST_CONFIG2 0x0A
#define PIXPAPER_BTST_CONFIG3 0x2F
#define PIXPAPER_BTST_CONFIG4 0x25
#define PIXPAPER_BTST_CONFIG5 0x22
#define PIXPAPER_BTST_CONFIG6 0x2E
#define PIXPAPER_BTST_CONFIG7 0x21
#define PIXPAPER_DRF_VCOM_AC 0x00
#define PIXPAPER_DRF_VCOM_DC 0x01
#define PIXPAPER_PLL_FR_MASK (0x7 << 0)
#define PIXPAPER_PLL_FR_12_5HZ (0x0 << 0)
#define PIXPAPER_PLL_FR_25HZ (0x1 << 0)
#define PIXPAPER_PLL_FR_50HZ (0x2 << 0)
#define PIXPAPER_PLL_FR_65HZ (0x3 << 0)
#define PIXPAPER_PLL_FR_75HZ (0x4 << 0)
#define PIXPAPER_PLL_FR_85HZ (0x5 << 0)
#define PIXPAPER_PLL_FR_100HZ (0x6 << 0)
#define PIXPAPER_PLL_FR_120HZ (0x7 << 0)
#define PIXPAPER_PLL_DFR \
(1 << 3)
#define PIXPAPER_PLL_CONFIG \
(PIXPAPER_PLL_FR_50HZ)
#define PIXPAPER_TSE_TO_MASK \
(0xF << 0)
#define PIXPAPER_TSE_TO_POS_0C (0x0 << 0)
#define PIXPAPER_TSE_TO_POS_0_5C (0x1 << 0)
#define PIXPAPER_TSE_TO_POS_1C (0x2 << 0)
#define PIXPAPER_TSE_TO_POS_1_5C (0x3 << 0)
#define PIXPAPER_TSE_TO_POS_2C (0x4 << 0)
#define PIXPAPER_TSE_TO_POS_2_5C (0x5 << 0)
#define PIXPAPER_TSE_TO_POS_3C (0x6 << 0)
#define PIXPAPER_TSE_TO_POS_3_5C (0x7 << 0)
#define PIXPAPER_TSE_TO_NEG_4C (0x8 << 0)
#define PIXPAPER_TSE_TO_NEG_3_5C (0x9 << 0)
#define PIXPAPER_TSE_TO_NEG_3C (0xA << 0)
#define PIXPAPER_TSE_TO_NEG_2_5C (0xB << 0)
#define PIXPAPER_TSE_TO_NEG_2C (0xC << 0)
#define PIXPAPER_TSE_TO_NEG_1_5C (0xD << 0)
#define PIXPAPER_TSE_TO_NEG_1C (0xE << 0)
#define PIXPAPER_TSE_TO_NEG_0_5C (0xF << 0)
#define PIXPAPER_TSE_TO_FINE_MASK \
(0x3 << 4)
#define PIXPAPER_TSE_TO_FINE_0C (0x0 << 4)
#define PIXPAPER_TSE_TO_FINE_0_25C (0x1 << 4)
#define PIXPAPER_TSE_ENABLE \
(0 << 7)
#define PIXPAPER_TSE_DISABLE \
(1 << 7)
#define PIXPAPER_TSE_CONFIG \
(PIXPAPER_TSE_TO_POS_0C | PIXPAPER_TSE_TO_FINE_0C | \
PIXPAPER_TSE_ENABLE)
#define PIXPAPER_UNKNOWN_4D_CONFIG \
0x78
#define PIXPAPER_CDI_INTERVAL_MASK \
(0xF << 0)
#define PIXPAPER_CDI_17_HSYNC (0x0 << 0)
#define PIXPAPER_CDI_16_HSYNC (0x1 << 0)
#define PIXPAPER_CDI_15_HSYNC (0x2 << 0)
#define PIXPAPER_CDI_14_HSYNC (0x3 << 0)
#define PIXPAPER_CDI_13_HSYNC (0x4 << 0)
#define PIXPAPER_CDI_12_HSYNC (0x5 << 0)
#define PIXPAPER_CDI_11_HSYNC (0x6 << 0)
#define PIXPAPER_CDI_10_HSYNC (0x7 << 0)
#define PIXPAPER_CDI_9_HSYNC (0x8 << 0)
#define PIXPAPER_CDI_8_HSYNC (0x9 << 0)
#define PIXPAPER_CDI_7_HSYNC (0xA << 0)
#define PIXPAPER_CDI_6_HSYNC (0xB << 0)
#define PIXPAPER_CDI_5_HSYNC (0xC << 0)
#define PIXPAPER_CDI_4_HSYNC (0xD << 0)
#define PIXPAPER_CDI_3_HSYNC (0xE << 0)
#define PIXPAPER_CDI_2_HSYNC (0xF << 0)
#define PIXPAPER_CDI_DDX \
(1 << 4)
#define PIXPAPER_CDI_VBD_MASK \
(0x7 << 5)
#define PIXPAPER_CDI_VBD_FLOAT (0x0 << 5)
#define PIXPAPER_CDI_VBD_GRAY3_DDX0 \
(0x1 << 5)
#define PIXPAPER_CDI_VBD_GRAY2_DDX0 \
(0x2 << 5)
#define PIXPAPER_CDI_VBD_GRAY1_DDX0 \
(0x3 << 5)
#define PIXPAPER_CDI_VBD_GRAY0_DDX0 \
(0x4 << 5)
#define PIXPAPER_CDI_VBD_GRAY0_DDX1 \
(0x0 << 5)
#define PIXPAPER_CDI_VBD_GRAY1_DDX1 \
(0x1 << 5)
#define PIXPAPER_CDI_VBD_GRAY2_DDX1 \
(0x2 << 5)
#define PIXPAPER_CDI_VBD_GRAY3_DDX1 \
(0x3 << 5)
#define PIXPAPER_CDI_VBD_FLOAT_DDX1 (0x4 << 5)
#define PIXPAPER_CDI_CONFIG \
(PIXPAPER_CDI_10_HSYNC | PIXPAPER_CDI_DDX | \
PIXPAPER_CDI_VBD_GRAY1_DDX1)
#define PIXPAPER_UNKNOWN_60_CONFIG1 \
0x02
#define PIXPAPER_UNKNOWN_60_CONFIG2 \
0x02
#define PIXPAPER_TRES_HRES_H \
((PIXPAPER_PANEL_BUFFER_WIDTH >> 8) & \
0xFF)
#define PIXPAPER_TRES_HRES_L \
(PIXPAPER_PANEL_BUFFER_WIDTH & \
0xFF)
#define PIXPAPER_TRES_VRES_H \
((PIXPAPER_HEIGHT >> 8) & \
0xFF)
#define PIXPAPER_TRES_VRES_L \
(PIXPAPER_HEIGHT & \
0xFF)
#define PIXPAPER_GSST_S_START 0x00
#define PIXPAPER_GSST_RESERVED 0x00
#define PIXPAPER_GSST_G_START_H \
0x00
#define PIXPAPER_GSST_G_START_L \
0x00
#define PIXPAPER_UNKNOWN_B4_CONFIG \
0xD0
#define PIXPAPER_UNKNOWN_B5_CONFIG \
0x03
#define PIXPAPER_UNKNOWN_E0_CONFIG \
0x00
#define PIXPAPER_PWS_VCOM_W_MASK \
(0xF \
<< 4)
#define PIXPAPER_PWS_VCOM_W_0 (0x0 << 4)
#define PIXPAPER_PWS_VCOM_W_1 (0x1 << 4)
#define PIXPAPER_PWS_VCOM_W_2 (0x2 << 4)
#define PIXPAPER_PWS_VCOM_W_3 (0x3 << 4)
#define PIXPAPER_PWS_VCOM_W_4 (0x4 << 4)
#define PIXPAPER_PWS_VCOM_W_5 (0x5 << 4)
#define PIXPAPER_PWS_VCOM_W_6 (0x6 << 4)
#define PIXPAPER_PWS_VCOM_W_7 (0x7 << 4)
#define PIXPAPER_PWS_VCOM_W_8 (0x8 << 4)
#define PIXPAPER_PWS_VCOM_W_9 (0x9 << 4)
#define PIXPAPER_PWS_VCOM_W_10 (0xA << 4)
#define PIXPAPER_PWS_VCOM_W_11 (0xB << 4)
#define PIXPAPER_PWS_VCOM_W_12 (0xC << 4)
#define PIXPAPER_PWS_VCOM_W_13 (0xD << 4)
#define PIXPAPER_PWS_VCOM_W_14 (0xE << 4)
#define PIXPAPER_PWS_VCOM_W_15 (0xF << 4)
#define PIXPAPER_PWS_SD_W_MASK \
(0xF << 0)
#define PIXPAPER_PWS_SD_W_0 (0x0 << 0)
#define PIXPAPER_PWS_SD_W_1 (0x1 << 0)
#define PIXPAPER_PWS_SD_W_2 (0x2 << 0)
#define PIXPAPER_PWS_SD_W_3 (0x3 << 0)
#define PIXPAPER_PWS_SD_W_4 (0x4 << 0)
#define PIXPAPER_PWS_SD_W_5 (0x5 << 0)
#define PIXPAPER_PWS_SD_W_6 (0x6 << 0)
#define PIXPAPER_PWS_SD_W_7 (0x7 << 0)
#define PIXPAPER_PWS_SD_W_8 (0x8 << 0)
#define PIXPAPER_PWS_SD_W_9 (0x9 << 0)
#define PIXPAPER_PWS_SD_W_10 (0xA << 0)
#define PIXPAPER_PWS_SD_W_11 (0xB << 0)
#define PIXPAPER_PWS_SD_W_12 (0xC << 0)
#define PIXPAPER_PWS_SD_W_13 (0xD << 0)
#define PIXPAPER_PWS_SD_W_14 (0xE << 0)
#define PIXPAPER_PWS_SD_W_15 (0xF << 0)
#define PIXPAPER_PWS_CONFIG \
(PIXPAPER_PWS_VCOM_W_2 | \
PIXPAPER_PWS_SD_W_2)
#define PIXPAPER_UNKNOWN_E7_CONFIG \
0x1C
#define PIXPAPER_UNKNOWN_E9_CONFIG \
0x01
MODULE_IMPORT_NS("DMA_BUF");
#define PIXPAPER_WIDTH 122
#define PIXPAPER_HEIGHT 250
#define PIXPAPER_HTOTAL 128
#define PIXPAPER_HFP 2
#define PIXPAPER_HSYNC 2
#define PIXPAPER_HBP (PIXPAPER_HTOTAL - PIXPAPER_WIDTH - PIXPAPER_HFP - PIXPAPER_HSYNC)
#define PIXPAPER_VTOTAL (250 + 55)
#define PIXPAPER_VFP 2
#define PIXPAPER_VSYNC 2
#define PIXPAPER_VBP (55 - PIXPAPER_VFP - PIXPAPER_VSYNC)
#define PIXPAPER_PIXEL_CLOCK 1952
#define PIXPAPER_WIDTH_MM 24
#define PIXPAPER_HEIGHT_MM 49
#define PIXPAPER_SPI_BITS_PER_WORD 8
#define PIXPAPER_SPI_SPEED_DEFAULT 1000000
#define PIXPAPER_PANEL_BUFFER_WIDTH 128
#define PIXPAPER_PANEL_BUFFER_TWO_BYTES_PER_ROW (PIXPAPER_PANEL_BUFFER_WIDTH / 4)
#define PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL 60
#define PIXPAPER_COLOR_THRESHOLD_HIGH_CHANNEL 200
#define PIXPAPER_COLOR_THRESHOLD_YELLOW_MIN_GREEN 180
struct pixpaper_error_ctx {
int errno_code;
};
struct pixpaper_panel {
struct drm_device drm;
struct drm_plane plane;
struct drm_crtc crtc;
struct drm_encoder encoder;
struct drm_connector connector;
struct spi_device *spi;
struct gpio_desc *reset;
struct gpio_desc *busy;
struct gpio_desc *dc;
};
static inline struct pixpaper_panel *to_pixpaper_panel(struct drm_device *drm)
{
return container_of(drm, struct pixpaper_panel, drm);
}
static void pixpaper_wait_for_panel(struct pixpaper_panel *panel)
{
unsigned int timeout_ms = 10000;
unsigned long timeout_jiffies = jiffies + msecs_to_jiffies(timeout_ms);
usleep_range(1000, 1500);
while (gpiod_get_value_cansleep(panel->busy) != 1) {
if (time_after(jiffies, timeout_jiffies)) {
drm_warn(&panel->drm, "Busy wait timed out\n");
return;
}
usleep_range(100, 200);
}
}
static void pixpaper_spi_sync(struct spi_device *spi, struct spi_message *msg,
struct pixpaper_error_ctx *err)
{
if (err->errno_code)
return;
int ret = spi_sync(spi, msg);
if (ret < 0)
err->errno_code = ret;
}
static void pixpaper_send_cmd(struct pixpaper_panel *panel, u8 cmd,
struct pixpaper_error_ctx *err)
{
if (err->errno_code)
return;
struct spi_transfer xfer = {
.tx_buf = &cmd,
.len = 1,
};
struct spi_message msg;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
gpiod_set_value_cansleep(panel->dc, 0);
usleep_range(1, 5);
pixpaper_spi_sync(panel->spi, &msg, err);
}
static void pixpaper_send_data(struct pixpaper_panel *panel, u8 data,
struct pixpaper_error_ctx *err)
{
if (err->errno_code)
return;
struct spi_transfer xfer = {
.tx_buf = &data,
.len = 1,
};
struct spi_message msg;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
gpiod_set_value_cansleep(panel->dc, 1);
usleep_range(1, 5);
pixpaper_spi_sync(panel->spi, &msg, err);
}
static int pixpaper_panel_hw_init(struct pixpaper_panel *panel)
{
struct pixpaper_error_ctx err = { .errno_code = 0 };
gpiod_set_value_cansleep(panel->reset, 0);
msleep(50);
gpiod_set_value_cansleep(panel->reset, 1);
msleep(50);
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_4D, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_4D_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_PANEL_SETTING, &err);
pixpaper_send_data(panel, PIXPAPER_PSR_CONFIG, &err);
pixpaper_send_data(panel, PIXPAPER_PSR_CONFIG2, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_POWER_SETTING, &err);
pixpaper_send_data(panel, PIXPAPER_PWR_CONFIG1, &err);
pixpaper_send_data(panel, PIXPAPER_PWR_CONFIG2, &err);
pixpaper_send_data(panel, PIXPAPER_PWR_VSP_8_2V, &err);
pixpaper_send_data(panel, PIXPAPER_PWR_VSPL_15V, &err);
pixpaper_send_data(panel, PIXPAPER_PWR_VSN_4V, &err);
pixpaper_send_data(panel, PIXPAPER_PWR_VSP_8_2V, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_POWER_OFF_SEQUENCE, &err);
pixpaper_send_data(panel, PIXPAPER_PFS_CONFIG1, &err);
pixpaper_send_data(panel, PIXPAPER_PFS_CONFIG2, &err);
pixpaper_send_data(panel, PIXPAPER_PFS_CONFIG3, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_BOOSTER_SOFT_START, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG1, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG2, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG3, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG4, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG5, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG6, &err);
pixpaper_send_data(panel, PIXPAPER_BTST_CONFIG7, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_PLL_CONTROL, &err);
pixpaper_send_data(panel, PIXPAPER_PLL_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_TEMP_SENSOR_CALIB, &err);
pixpaper_send_data(panel, PIXPAPER_TSE_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_VCOM_INTERVAL, &err);
pixpaper_send_data(panel, PIXPAPER_CDI_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_60, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_60_CONFIG1, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_60_CONFIG2, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_RESOLUTION_SETTING, &err);
pixpaper_send_data(panel, PIXPAPER_TRES_HRES_H, &err);
pixpaper_send_data(panel, PIXPAPER_TRES_HRES_L, &err);
pixpaper_send_data(panel, PIXPAPER_TRES_VRES_H, &err);
pixpaper_send_data(panel, PIXPAPER_TRES_VRES_L, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_GATE_SOURCE_START, &err);
pixpaper_send_data(panel, PIXPAPER_GSST_S_START, &err);
pixpaper_send_data(panel, PIXPAPER_GSST_RESERVED, &err);
pixpaper_send_data(panel, PIXPAPER_GSST_G_START_H, &err);
pixpaper_send_data(panel, PIXPAPER_GSST_G_START_L, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_E7, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_E7_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_POWER_SAVING, &err);
pixpaper_send_data(panel, PIXPAPER_PWS_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_E0, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_E0_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_B4, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_B4_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_B5, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_B5_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_UNKNOWN_E9, &err);
pixpaper_send_data(panel, PIXPAPER_UNKNOWN_E9_CONFIG, &err);
if (err.errno_code)
goto init_fail;
pixpaper_wait_for_panel(panel);
return 0;
init_fail:
drm_err(&panel->drm, "Hardware initialization failed (err=%d)\n",
err.errno_code);
return err.errno_code;
}
static u8 pack_pixels_to_byte(__le32 *src_pixels, int i, int j,
struct drm_framebuffer *fb)
{
u8 packed_byte = 0;
int k;
for (k = 0; k < 4; k++) {
int current_pixel_x = j * 4 + k;
u8 two_bit_val;
if (current_pixel_x < PIXPAPER_WIDTH) {
u32 pixel_offset =
(i * (fb->pitches[0] / 4)) + current_pixel_x;
u32 pixel = le32_to_cpu(src_pixels[pixel_offset]);
u32 r = (pixel >> 16) & 0xFF;
u32 g = (pixel >> 8) & 0xFF;
u32 b = pixel & 0xFF;
if (r < PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL &&
g < PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL &&
b < PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL) {
two_bit_val = 0b00;
} else if (r > PIXPAPER_COLOR_THRESHOLD_HIGH_CHANNEL &&
g > PIXPAPER_COLOR_THRESHOLD_HIGH_CHANNEL &&
b > PIXPAPER_COLOR_THRESHOLD_HIGH_CHANNEL) {
two_bit_val = 0b01;
} else if (r > PIXPAPER_COLOR_THRESHOLD_HIGH_CHANNEL &&
g < PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL &&
b < PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL) {
two_bit_val = 0b11;
} else if (r > PIXPAPER_COLOR_THRESHOLD_HIGH_CHANNEL &&
g > PIXPAPER_COLOR_THRESHOLD_YELLOW_MIN_GREEN &&
b < PIXPAPER_COLOR_THRESHOLD_LOW_CHANNEL) {
two_bit_val = 0b10;
} else {
two_bit_val = 0b01;
}
} else {
two_bit_val = 0b01;
}
packed_byte |= two_bit_val << ((3 - k) * 2);
}
return packed_byte;
}
static int pixpaper_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *new_plane_state =
drm_atomic_get_new_plane_state(state, plane);
struct drm_crtc *new_crtc = new_plane_state->crtc;
struct drm_crtc_state *new_crtc_state = NULL;
int ret;
if (new_crtc)
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
ret = drm_atomic_helper_check_plane_state(new_plane_state,
new_crtc_state, DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING, false, false);
if (ret)
return ret;
else if (!new_plane_state->visible)
return 0;
return 0;
}
static int pixpaper_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state =
drm_atomic_get_new_crtc_state(state, crtc);
if (!crtc_state->enable)
return 0;
return drm_atomic_helper_check_crtc_primary_plane(crtc_state);
}
static void pixpaper_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct pixpaper_panel *panel = to_pixpaper_panel(crtc->dev);
struct drm_device *drm = &panel->drm;
int idx;
struct pixpaper_error_ctx err = { .errno_code = 0 };
if (!drm_dev_enter(drm, &idx))
return;
pixpaper_send_cmd(panel, PIXPAPER_CMD_POWER_ON, &err);
if (err.errno_code) {
drm_err_once(drm, "Failed to send PON command: %d\n", err.errno_code);
goto exit_drm_dev;
}
pixpaper_wait_for_panel(panel);
drm_dbg(drm, "Panel enabled and powered on\n");
exit_drm_dev:
drm_dev_exit(idx);
}
static void pixpaper_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct pixpaper_panel *panel = to_pixpaper_panel(crtc->dev);
struct drm_device *drm = &panel->drm;
struct pixpaper_error_ctx err = { .errno_code = 0 };
int idx;
if (!drm_dev_enter(drm, &idx))
return;
pixpaper_send_cmd(panel, PIXPAPER_CMD_POWER_OFF, &err);
if (err.errno_code) {
drm_err_once(drm, "Failed to send POF command: %d\n", err.errno_code);
goto exit_drm_dev;
}
pixpaper_wait_for_panel(panel);
drm_dbg(drm, "Panel disabled\n");
exit_drm_dev:
drm_dev_exit(idx);
}
static void pixpaper_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state)
{
struct drm_plane_state *plane_state =
drm_atomic_get_new_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state =
to_drm_shadow_plane_state(plane_state);
struct drm_crtc *crtc = plane_state->crtc;
struct pixpaper_panel *panel = to_pixpaper_panel(crtc->dev);
struct drm_device *drm = &panel->drm;
struct drm_framebuffer *fb = plane_state->fb;
struct iosys_map map = shadow_plane_state->data[0];
void *vaddr = map.vaddr;
int i, j, idx;
__le32 *src_pixels = NULL;
struct pixpaper_error_ctx err = { .errno_code = 0 };
if (!drm_dev_enter(drm, &idx))
return;
drm_dbg(drm, "Starting frame update (phys=%dx%d, buf_w=%d)\n",
PIXPAPER_WIDTH, PIXPAPER_HEIGHT, PIXPAPER_PANEL_BUFFER_WIDTH);
if (!fb || !plane_state->visible) {
drm_err_once(drm, "No framebuffer or plane not visible, skipping update\n");
goto update_cleanup;
}
src_pixels = (__le32 *)vaddr;
pixpaper_send_cmd(panel, PIXPAPER_CMD_DATA_START_TRANSMISSION, &err);
if (err.errno_code)
goto update_cleanup;
pixpaper_wait_for_panel(panel);
for (i = 0; i < PIXPAPER_HEIGHT; i++) {
for (j = 0; j < PIXPAPER_PANEL_BUFFER_TWO_BYTES_PER_ROW; j++) {
u8 packed_byte =
pack_pixels_to_byte(src_pixels, i, j, fb);
pixpaper_wait_for_panel(panel);
pixpaper_send_data(panel, packed_byte, &err);
}
}
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_POWER_ON, &err);
if (err.errno_code) {
drm_err_once(drm, "Failed to send PON command: %d\n", err.errno_code);
goto update_cleanup;
}
pixpaper_wait_for_panel(panel);
pixpaper_send_cmd(panel, PIXPAPER_CMD_DISPLAY_REFRESH, &err);
pixpaper_send_data(panel, PIXPAPER_DRF_VCOM_AC, &err);
if (err.errno_code) {
drm_err_once(drm, "Failed sending data after DRF: %d\n", err.errno_code);
goto update_cleanup;
}
pixpaper_wait_for_panel(panel);
update_cleanup:
if (err.errno_code && err.errno_code != -ETIMEDOUT)
drm_err_once(drm, "Frame update function failed with error %d\n", err.errno_code);
drm_dev_exit(idx);
}
static const struct drm_display_mode pixpaper_mode = {
.clock = PIXPAPER_PIXEL_CLOCK,
.hdisplay = PIXPAPER_WIDTH,
.hsync_start = PIXPAPER_WIDTH + PIXPAPER_HFP,
.hsync_end = PIXPAPER_WIDTH + PIXPAPER_HFP + PIXPAPER_HSYNC,
.htotal = PIXPAPER_HTOTAL,
.vdisplay = PIXPAPER_HEIGHT,
.vsync_start = PIXPAPER_HEIGHT + PIXPAPER_VFP,
.vsync_end = PIXPAPER_HEIGHT + PIXPAPER_VFP + PIXPAPER_VSYNC,
.vtotal = PIXPAPER_VTOTAL,
.width_mm = PIXPAPER_WIDTH_MM,
.height_mm = PIXPAPER_HEIGHT_MM,
.type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
};
static int pixpaper_connector_get_modes(struct drm_connector *connector)
{
return drm_connector_helper_get_modes_fixed(connector, &pixpaper_mode);
}
static const struct drm_plane_funcs pixpaper_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
DRM_GEM_SHADOW_PLANE_FUNCS,
};
static const struct drm_plane_helper_funcs pixpaper_plane_helper_funcs = {
DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
.atomic_check = pixpaper_plane_helper_atomic_check,
.atomic_update = pixpaper_plane_atomic_update,
};
static const struct drm_crtc_funcs pixpaper_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
};
static enum drm_mode_status
pixpaper_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode)
{
if (mode->hdisplay == PIXPAPER_WIDTH &&
mode->vdisplay == PIXPAPER_HEIGHT) {
return MODE_OK;
}
return MODE_BAD;
}
static const struct drm_crtc_helper_funcs pixpaper_crtc_helper_funcs = {
.mode_valid = pixpaper_mode_valid,
.atomic_check = pixpaper_crtc_helper_atomic_check,
.atomic_enable = pixpaper_crtc_atomic_enable,
.atomic_disable = pixpaper_crtc_atomic_disable,
};
static const struct drm_encoder_funcs pixpaper_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
static const struct drm_connector_funcs pixpaper_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = drm_connector_cleanup,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
static const struct drm_connector_helper_funcs pixpaper_connector_helper_funcs = {
.get_modes = pixpaper_connector_get_modes,
};
DEFINE_DRM_GEM_FOPS(pixpaper_fops);
static struct drm_driver pixpaper_drm_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &pixpaper_fops,
.name = "pixpaper",
.desc = "DRM driver for PIXPAPER e-ink",
.major = 1,
.minor = 0,
DRM_GEM_SHMEM_DRIVER_OPS,
DRM_FBDEV_SHMEM_DRIVER_OPS,
};
static const struct drm_mode_config_funcs pixpaper_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
};
static int pixpaper_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct pixpaper_panel *panel;
struct drm_device *drm;
int ret;
panel = devm_drm_dev_alloc(dev, &pixpaper_drm_driver,
struct pixpaper_panel, drm);
if (IS_ERR(panel))
return PTR_ERR(panel);
drm = &panel->drm;
panel->spi = spi;
spi_set_drvdata(spi, panel);
spi->mode = SPI_MODE_0;
spi->bits_per_word = PIXPAPER_SPI_BITS_PER_WORD;
if (!spi->max_speed_hz) {
drm_warn(drm,
"spi-max-frequency not specified in DT, using default %u Hz\n",
PIXPAPER_SPI_SPEED_DEFAULT);
spi->max_speed_hz = PIXPAPER_SPI_SPEED_DEFAULT;
}
ret = spi_setup(spi);
if (ret < 0) {
drm_err(drm, "SPI setup failed: %d\n", ret);
return ret;
}
if (!dev->dma_mask)
dev->dma_mask = &dev->coherent_dma_mask;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
drm_err(drm, "Failed to set DMA mask: %d\n", ret);
return ret;
}
panel->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(panel->reset))
return PTR_ERR(panel->reset);
panel->busy = devm_gpiod_get(dev, "busy", GPIOD_IN);
if (IS_ERR(panel->busy))
return PTR_ERR(panel->busy);
panel->dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_HIGH);
if (IS_ERR(panel->dc))
return PTR_ERR(panel->dc);
ret = pixpaper_panel_hw_init(panel);
if (ret) {
drm_err(drm, "Panel hardware initialization failed: %d\n", ret);
return ret;
}
ret = drmm_mode_config_init(drm);
if (ret)
return ret;
drm->mode_config.funcs = &pixpaper_mode_config_funcs;
drm->mode_config.min_width = PIXPAPER_WIDTH;
drm->mode_config.max_width = PIXPAPER_WIDTH;
drm->mode_config.min_height = PIXPAPER_HEIGHT;
drm->mode_config.max_height = PIXPAPER_HEIGHT;
ret = drm_universal_plane_init(drm, &panel->plane, 1,
&pixpaper_plane_funcs,
(const uint32_t[]){ DRM_FORMAT_XRGB8888 },
1, NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
if (ret)
return ret;
drm_plane_helper_add(&panel->plane, &pixpaper_plane_helper_funcs);
ret = drm_crtc_init_with_planes(drm, &panel->crtc, &panel->plane, NULL,
&pixpaper_crtc_funcs, NULL);
if (ret)
return ret;
drm_crtc_helper_add(&panel->crtc, &pixpaper_crtc_helper_funcs);
ret = drm_encoder_init(drm, &panel->encoder, &pixpaper_encoder_funcs,
DRM_MODE_ENCODER_NONE, NULL);
if (ret)
return ret;
panel->encoder.possible_crtcs = drm_crtc_mask(&panel->crtc);
ret = drm_connector_init(drm, &panel->connector,
&pixpaper_connector_funcs,
DRM_MODE_CONNECTOR_SPI);
if (ret)
return ret;
drm_connector_helper_add(&panel->connector,
&pixpaper_connector_helper_funcs);
drm_connector_attach_encoder(&panel->connector, &panel->encoder);
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret)
return ret;
drm_client_setup(drm, NULL);
return 0;
}
static void pixpaper_remove(struct spi_device *spi)
{
struct pixpaper_panel *panel = spi_get_drvdata(spi);
if (!panel)
return;
drm_dev_unplug(&panel->drm);
drm_atomic_helper_shutdown(&panel->drm);
}
static const struct spi_device_id pixpaper_ids[] = { { "pixpaper", 0 }, {} };
MODULE_DEVICE_TABLE(spi, pixpaper_ids);
static const struct of_device_id pixpaper_dt_ids[] = {
{ .compatible = "mayqueen,pixpaper" },
{}
};
MODULE_DEVICE_TABLE(of, pixpaper_dt_ids);
static struct spi_driver pixpaper_spi_driver = {
.driver = {
.name = "pixpaper",
.of_match_table = pixpaper_dt_ids,
},
.id_table = pixpaper_ids,
.probe = pixpaper_probe,
.remove = pixpaper_remove,
};
module_spi_driver(pixpaper_spi_driver);
MODULE_AUTHOR("LiangCheng Wang");
MODULE_DESCRIPTION("DRM SPI driver for PIXPAPER e-ink panel");
MODULE_LICENSE("GPL");