fb_st7789v.c (9367B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * FB driver for the ST7789V LCD Controller 4 * 5 * Copyright (C) 2015 Dennis Menschel 6 */ 7 8#include <linux/bitops.h> 9#include <linux/delay.h> 10#include <linux/gpio/consumer.h> 11#include <linux/init.h> 12#include <linux/kernel.h> 13#include <linux/interrupt.h> 14#include <linux/completion.h> 15#include <linux/module.h> 16 17#include <video/mipi_display.h> 18 19#include "fbtft.h" 20 21#define DRVNAME "fb_st7789v" 22 23#define DEFAULT_GAMMA \ 24 "70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25\n" \ 25 "70 2C 2E 15 10 09 48 33 53 0B 19 18 20 25" 26 27#define HSD20_IPS_GAMMA \ 28 "D0 05 0A 09 08 05 2E 44 45 0F 17 16 2B 33\n" \ 29 "D0 05 0A 09 08 05 2E 43 45 0F 16 16 2B 33" 30 31#define HSD20_IPS 1 32 33/** 34 * enum st7789v_command - ST7789V display controller commands 35 * 36 * @PORCTRL: porch setting 37 * @GCTRL: gate control 38 * @VCOMS: VCOM setting 39 * @VDVVRHEN: VDV and VRH command enable 40 * @VRHS: VRH set 41 * @VDVS: VDV set 42 * @VCMOFSET: VCOM offset set 43 * @PWCTRL1: power control 1 44 * @PVGAMCTRL: positive voltage gamma control 45 * @NVGAMCTRL: negative voltage gamma control 46 * 47 * The command names are the same as those found in the datasheet to ease 48 * looking up their semantics and usage. 49 * 50 * Note that the ST7789V display controller offers quite a few more commands 51 * which have been omitted from this list as they are not used at the moment. 52 * Furthermore, commands that are compliant with the MIPI DCS have been left 53 * out as well to avoid duplicate entries. 54 */ 55enum st7789v_command { 56 PORCTRL = 0xB2, 57 GCTRL = 0xB7, 58 VCOMS = 0xBB, 59 VDVVRHEN = 0xC2, 60 VRHS = 0xC3, 61 VDVS = 0xC4, 62 VCMOFSET = 0xC5, 63 PWCTRL1 = 0xD0, 64 PVGAMCTRL = 0xE0, 65 NVGAMCTRL = 0xE1, 66}; 67 68#define MADCTL_BGR BIT(3) /* bitmask for RGB/BGR order */ 69#define MADCTL_MV BIT(5) /* bitmask for page/column order */ 70#define MADCTL_MX BIT(6) /* bitmask for column address order */ 71#define MADCTL_MY BIT(7) /* bitmask for page address order */ 72 73/* 60Hz for 16.6ms, configured as 2*16.6ms */ 74#define PANEL_TE_TIMEOUT_MS 33 75 76static struct completion panel_te; /* completion for panel TE line */ 77static int irq_te; /* Linux IRQ for LCD TE line */ 78 79static irqreturn_t panel_te_handler(int irq, void *data) 80{ 81 complete(&panel_te); 82 return IRQ_HANDLED; 83} 84 85/* 86 * init_tearing_effect_line() - init tearing effect line. 87 * @par: FBTFT parameter object. 88 * 89 * Return: 0 on success, or a negative error code otherwise. 90 */ 91static int init_tearing_effect_line(struct fbtft_par *par) 92{ 93 struct device *dev = par->info->device; 94 struct gpio_desc *te; 95 int rc, irq; 96 97 te = gpiod_get_optional(dev, "te", GPIOD_IN); 98 if (IS_ERR(te)) 99 return dev_err_probe(dev, PTR_ERR(te), "Failed to request te GPIO\n"); 100 101 /* if te is NULL, indicating no configuration, directly return success */ 102 if (!te) { 103 irq_te = 0; 104 return 0; 105 } 106 107 irq = gpiod_to_irq(te); 108 109 /* GPIO is locked as an IRQ, we may drop the reference */ 110 gpiod_put(te); 111 112 if (irq < 0) 113 return irq; 114 115 irq_te = irq; 116 init_completion(&panel_te); 117 118 /* The effective state is high and lasts no more than 1000 microseconds */ 119 rc = devm_request_irq(dev, irq_te, panel_te_handler, 120 IRQF_TRIGGER_RISING, "TE_GPIO", par); 121 if (rc) 122 return dev_err_probe(dev, rc, "TE IRQ request failed.\n"); 123 124 disable_irq_nosync(irq_te); 125 126 return 0; 127} 128 129/** 130 * init_display() - initialize the display controller 131 * 132 * @par: FBTFT parameter object 133 * 134 * Most of the commands in this init function set their parameters to the 135 * same default values which are already in place after the display has been 136 * powered up. (The main exception to this rule is the pixel format which 137 * would default to 18 instead of 16 bit per pixel.) 138 * Nonetheless, this sequence can be used as a template for concrete 139 * displays which usually need some adjustments. 140 * 141 * Return: 0 on success, < 0 if error occurred. 142 */ 143static int init_display(struct fbtft_par *par) 144{ 145 int rc; 146 147 par->fbtftops.reset(par); 148 149 rc = init_tearing_effect_line(par); 150 if (rc) 151 return rc; 152 153 /* turn off sleep mode */ 154 write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE); 155 mdelay(120); 156 157 /* set pixel format to RGB-565 */ 158 write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT); 159 if (HSD20_IPS) 160 write_reg(par, PORCTRL, 0x05, 0x05, 0x00, 0x33, 0x33); 161 162 else 163 write_reg(par, PORCTRL, 0x08, 0x08, 0x00, 0x22, 0x22); 164 165 /* 166 * VGH = 13.26V 167 * VGL = -10.43V 168 */ 169 if (HSD20_IPS) 170 write_reg(par, GCTRL, 0x75); 171 else 172 write_reg(par, GCTRL, 0x35); 173 174 /* 175 * VDV and VRH register values come from command write 176 * (instead of NVM) 177 */ 178 write_reg(par, VDVVRHEN, 0x01, 0xFF); 179 180 /* 181 * VAP = 4.1V + (VCOM + VCOM offset + 0.5 * VDV) 182 * VAN = -4.1V + (VCOM + VCOM offset + 0.5 * VDV) 183 */ 184 if (HSD20_IPS) 185 write_reg(par, VRHS, 0x13); 186 else 187 write_reg(par, VRHS, 0x0B); 188 189 /* VDV = 0V */ 190 write_reg(par, VDVS, 0x20); 191 192 /* VCOM = 0.9V */ 193 if (HSD20_IPS) 194 write_reg(par, VCOMS, 0x22); 195 else 196 write_reg(par, VCOMS, 0x20); 197 198 /* VCOM offset = 0V */ 199 write_reg(par, VCMOFSET, 0x20); 200 201 /* 202 * AVDD = 6.8V 203 * AVCL = -4.8V 204 * VDS = 2.3V 205 */ 206 write_reg(par, PWCTRL1, 0xA4, 0xA1); 207 208 /* TE line output is off by default when powering on */ 209 if (irq_te) 210 write_reg(par, MIPI_DCS_SET_TEAR_ON, 0x00); 211 212 write_reg(par, MIPI_DCS_SET_DISPLAY_ON); 213 214 if (HSD20_IPS) 215 write_reg(par, MIPI_DCS_ENTER_INVERT_MODE); 216 217 return 0; 218} 219 220/* 221 * write_vmem() - write data to display. 222 * @par: FBTFT parameter object. 223 * @offset: offset from screen_buffer. 224 * @len: the length of data to be writte. 225 * 226 * Return: 0 on success, or a negative error code otherwise. 227 */ 228static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) 229{ 230 struct device *dev = par->info->device; 231 int ret; 232 233 if (irq_te) { 234 enable_irq(irq_te); 235 reinit_completion(&panel_te); 236 ret = wait_for_completion_timeout(&panel_te, 237 msecs_to_jiffies(PANEL_TE_TIMEOUT_MS)); 238 if (ret == 0) 239 dev_err(dev, "wait panel TE timeout\n"); 240 241 disable_irq(irq_te); 242 } 243 244 switch (par->pdata->display.buswidth) { 245 case 8: 246 ret = fbtft_write_vmem16_bus8(par, offset, len); 247 break; 248 case 9: 249 ret = fbtft_write_vmem16_bus9(par, offset, len); 250 break; 251 case 16: 252 ret = fbtft_write_vmem16_bus16(par, offset, len); 253 break; 254 default: 255 dev_err(dev, "Unsupported buswidth %d\n", 256 par->pdata->display.buswidth); 257 ret = 0; 258 break; 259 } 260 261 return ret; 262} 263 264/** 265 * set_var() - apply LCD properties like rotation and BGR mode 266 * 267 * @par: FBTFT parameter object 268 * 269 * Return: 0 on success, < 0 if error occurred. 270 */ 271static int set_var(struct fbtft_par *par) 272{ 273 u8 madctl_par = 0; 274 275 if (par->bgr) 276 madctl_par |= MADCTL_BGR; 277 switch (par->info->var.rotate) { 278 case 0: 279 break; 280 case 90: 281 madctl_par |= (MADCTL_MV | MADCTL_MY); 282 break; 283 case 180: 284 madctl_par |= (MADCTL_MX | MADCTL_MY); 285 break; 286 case 270: 287 madctl_par |= (MADCTL_MV | MADCTL_MX); 288 break; 289 default: 290 return -EINVAL; 291 } 292 write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl_par); 293 return 0; 294} 295 296/** 297 * set_gamma() - set gamma curves 298 * 299 * @par: FBTFT parameter object 300 * @curves: gamma curves 301 * 302 * Before the gamma curves are applied, they are preprocessed with a bitmask 303 * to ensure syntactically correct input for the display controller. 304 * This implies that the curves input parameter might be changed by this 305 * function and that illegal gamma values are auto-corrected and not 306 * reported as errors. 307 * 308 * Return: 0 on success, < 0 if error occurred. 309 */ 310static int set_gamma(struct fbtft_par *par, u32 *curves) 311{ 312 int i; 313 int j; 314 int c; /* curve index offset */ 315 316 /* 317 * Bitmasks for gamma curve command parameters. 318 * The masks are the same for both positive and negative voltage 319 * gamma curves. 320 */ 321 static const u8 gamma_par_mask[] = { 322 0xFF, /* V63[3:0], V0[3:0]*/ 323 0x3F, /* V1[5:0] */ 324 0x3F, /* V2[5:0] */ 325 0x1F, /* V4[4:0] */ 326 0x1F, /* V6[4:0] */ 327 0x3F, /* J0[1:0], V13[3:0] */ 328 0x7F, /* V20[6:0] */ 329 0x77, /* V36[2:0], V27[2:0] */ 330 0x7F, /* V43[6:0] */ 331 0x3F, /* J1[1:0], V50[3:0] */ 332 0x1F, /* V57[4:0] */ 333 0x1F, /* V59[4:0] */ 334 0x3F, /* V61[5:0] */ 335 0x3F, /* V62[5:0] */ 336 }; 337 338 for (i = 0; i < par->gamma.num_curves; i++) { 339 c = i * par->gamma.num_values; 340 for (j = 0; j < par->gamma.num_values; j++) 341 curves[c + j] &= gamma_par_mask[j]; 342 write_reg(par, PVGAMCTRL + i, 343 curves[c + 0], curves[c + 1], curves[c + 2], 344 curves[c + 3], curves[c + 4], curves[c + 5], 345 curves[c + 6], curves[c + 7], curves[c + 8], 346 curves[c + 9], curves[c + 10], curves[c + 11], 347 curves[c + 12], curves[c + 13]); 348 } 349 return 0; 350} 351 352/** 353 * blank() - blank the display 354 * 355 * @par: FBTFT parameter object 356 * @on: whether to enable or disable blanking the display 357 * 358 * Return: 0 on success, < 0 if error occurred. 359 */ 360static int blank(struct fbtft_par *par, bool on) 361{ 362 if (on) 363 write_reg(par, MIPI_DCS_SET_DISPLAY_OFF); 364 else 365 write_reg(par, MIPI_DCS_SET_DISPLAY_ON); 366 return 0; 367} 368 369static struct fbtft_display display = { 370 .regwidth = 8, 371 .width = 240, 372 .height = 320, 373 .gamma_num = 2, 374 .gamma_len = 14, 375 .gamma = HSD20_IPS_GAMMA, 376 .fbtftops = { 377 .init_display = init_display, 378 .write_vmem = write_vmem, 379 .set_var = set_var, 380 .set_gamma = set_gamma, 381 .blank = blank, 382 }, 383}; 384 385FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display); 386 387MODULE_ALIAS("spi:" DRVNAME); 388MODULE_ALIAS("platform:" DRVNAME); 389MODULE_ALIAS("spi:st7789v"); 390MODULE_ALIAS("platform:st7789v"); 391 392MODULE_DESCRIPTION("FB driver for the ST7789V LCD Controller"); 393MODULE_AUTHOR("Dennis Menschel"); 394MODULE_LICENSE("GPL");