// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* FB driver for the ST7789V LCD Controller
|
|
*
|
|
* Copyright (C) 2015 Dennis Menschel
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <video/mipi_display.h>
|
|
|
|
#include "fbtft.h"
|
|
|
|
#define DRVNAME "fb_st7789v"
|
|
|
|
static unsigned int width;
|
|
module_param(width, uint, 0000);
|
|
MODULE_PARM_DESC(width, "Display width");
|
|
|
|
static unsigned int height;
|
|
module_param(height, uint, 0000);
|
|
MODULE_PARM_DESC(height, "Display height");
|
|
|
|
static u32 col_offset = 0;
|
|
static u32 row_offset = 0;
|
|
static u8 col_hack_fix_offset = 0;
|
|
static short x_offset = 0;
|
|
static short y_offset = 0;
|
|
|
|
#define ST77XX_MADCTL_MY 0x80
|
|
#define ST77XX_MADCTL_MX 0x40
|
|
#define ST77XX_MADCTL_MV 0x20
|
|
#define ST77XX_MADCTL_ML 0x10
|
|
#define ST77XX_MADCTL_BGR 0x08
|
|
#define ST77XX_MADCTL_RGB 0x00
|
|
|
|
#define DEFAULT_GAMMA \
|
|
"70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25\n" \
|
|
"70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25"
|
|
|
|
/**
|
|
* enum st7789v_command - ST7789V display controller commands
|
|
*
|
|
* @PORCTRL: porch setting
|
|
* @GCTRL: gate control
|
|
* @VCOMS: VCOM setting
|
|
* @VDVVRHEN: VDV and VRH command enable
|
|
* @VRHS: VRH set
|
|
* @VDVS: VDV set
|
|
* @VCMOFSET: VCOM offset set
|
|
* @PWCTRL1: power control 1
|
|
* @PVGAMCTRL: positive voltage gamma control
|
|
* @NVGAMCTRL: negative voltage gamma control
|
|
*
|
|
* The command names are the same as those found in the datasheet to ease
|
|
* looking up their semantics and usage.
|
|
*
|
|
* Note that the ST7789V display controller offers quite a few more commands
|
|
* which have been omitted from this list as they are not used at the moment.
|
|
* Furthermore, commands that are compliant with the MIPI DCS have been left
|
|
* out as well to avoid duplicate entries.
|
|
*/
|
|
enum st7789v_command {
|
|
PORCTRL = 0xB2,
|
|
GCTRL = 0xB7,
|
|
VCOMS = 0xBB,
|
|
VDVVRHEN = 0xC2,
|
|
VRHS = 0xC3,
|
|
VDVS = 0xC4,
|
|
VCMOFSET = 0xC5,
|
|
PWCTRL1 = 0xD0,
|
|
PVGAMCTRL = 0xE0,
|
|
NVGAMCTRL = 0xE1,
|
|
};
|
|
|
|
/**
|
|
* init_display() - initialize the display controller
|
|
*
|
|
* @par: FBTFT parameter object
|
|
*
|
|
* Most of the commands in this init function set their parameters to the
|
|
* same default values which are already in place after the display has been
|
|
* powered up. (The main exception to this rule is the pixel format which
|
|
* would default to 18 instead of 16 bit per pixel.)
|
|
* Nonetheless, this sequence can be used as a template for concrete
|
|
* displays which usually need some adjustments.
|
|
*
|
|
* Return: 0 on success, < 0 if error occurred.
|
|
*/
|
|
static int init_display(struct fbtft_par *par)
|
|
{
|
|
printk(KERN_INFO "ST7789 adafruit fbtft driver\n");
|
|
|
|
width = par->info->var.xres;
|
|
height = par->info->var.yres;
|
|
|
|
if ((width == 0) || (width > 240)) {
|
|
width = 240;
|
|
}
|
|
if ((height == 0) || (height > 320)) {
|
|
height = 320;
|
|
}
|
|
printk(KERN_INFO "Size: (%d, %d)\n", width, height);
|
|
|
|
// Go to sleep
|
|
write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
|
|
// Soft reset
|
|
write_reg(par, MIPI_DCS_SOFT_RESET);
|
|
mdelay(150);
|
|
|
|
/* turn off sleep mode */
|
|
write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
|
|
mdelay(10);
|
|
|
|
/* set pixel format to RGB-565 */
|
|
write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
|
|
|
|
write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, 0);
|
|
write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, 0, 0, 0, 240);
|
|
write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, 0, 0, 320>>8, 320&0xFF);
|
|
write_reg(par, MIPI_DCS_ENTER_INVERT_MODE); // odd hack, displays are inverted
|
|
write_reg(par, MIPI_DCS_ENTER_NORMAL_MODE);
|
|
mdelay(10);
|
|
|
|
|
|
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* set_var() - apply LCD properties like rotation and BGR mode
|
|
*
|
|
* @par: FBTFT parameter object
|
|
*
|
|
* Return: 0 on success, < 0 if error occurred.
|
|
*/
|
|
static int set_var(struct fbtft_par *par)
|
|
{
|
|
u8 addr_mode = 0;
|
|
|
|
switch (par->info->var.rotate) {
|
|
case 0:
|
|
addr_mode = 0;
|
|
x_offset = col_offset;
|
|
y_offset = row_offset;
|
|
break;
|
|
case 90:
|
|
addr_mode = ST77XX_MADCTL_MV | ST77XX_MADCTL_MX;
|
|
x_offset = row_offset;
|
|
y_offset = col_offset;
|
|
break;
|
|
case 180:
|
|
addr_mode = ST77XX_MADCTL_MX | ST77XX_MADCTL_MY;
|
|
x_offset = (240 - width) - col_offset + col_hack_fix_offset;
|
|
// hack tweak to account for extra pixel width to make even
|
|
y_offset = (320 - height) - row_offset;
|
|
break;
|
|
case 270:
|
|
addr_mode = ST77XX_MADCTL_MV | ST77XX_MADCTL_MY;
|
|
x_offset = (320 - height) - row_offset;
|
|
y_offset = (240 - width) - col_offset;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
printk(KERN_INFO "Rotation %d offsets %d %d\n", par->info->var.rotate, x_offset, y_offset);
|
|
|
|
write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* blank() - blank the display
|
|
*
|
|
* @par: FBTFT parameter object
|
|
* @on: whether to enable or disable blanking the display
|
|
*
|
|
* Return: 0 on success, < 0 if error occurred.
|
|
*/
|
|
static int blank(struct fbtft_par *par, bool on)
|
|
{
|
|
if (on)
|
|
write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
|
|
else
|
|
write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
|
|
return 0;
|
|
}
|
|
|
|
static struct fbtft_display display = {
|
|
.regwidth = 8,
|
|
.width = 240,
|
|
.height = 320,
|
|
.gamma_num = 2,
|
|
.gamma_len = 14,
|
|
.gamma = DEFAULT_GAMMA,
|
|
.fbtftops = {
|
|
.init_display = init_display,
|
|
.set_var = set_var,
|
|
.blank = blank,
|
|
},
|
|
};
|
|
|
|
FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display);
|
|
|
|
MODULE_ALIAS("spi:" DRVNAME);
|
|
MODULE_ALIAS("platform:" DRVNAME);
|
|
MODULE_ALIAS("spi:st7789v");
|
|
MODULE_ALIAS("platform:st7789v");
|
|
|
|
MODULE_DESCRIPTION("FB driver for the ST7789V LCD Controller");
|
|
MODULE_AUTHOR("Dennis Menschel");
|
|
MODULE_LICENSE("GPL");
|