You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

215 lines
5.4 KiB

// 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");