1783de57cSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
29b78e456SThomas Petazzoni /*
39b78e456SThomas Petazzoni  * FB driver for the ILI9341 LCD display controller
49b78e456SThomas Petazzoni  *
59b78e456SThomas Petazzoni  * This display uses 9-bit SPI: Data/Command bit + 8 data bits
69b78e456SThomas Petazzoni  * For platforms that doesn't support 9-bit, the driver is capable
79b78e456SThomas Petazzoni  * of emulating this using 8-bit transfer.
892def781SMasanari Iida  * This is done by transferring eight 9-bit words in 9 bytes.
99b78e456SThomas Petazzoni  *
109b78e456SThomas Petazzoni  * Copyright (C) 2013 Christian Vogelgsang
119b78e456SThomas Petazzoni  * Based on adafruit22fb.c by Noralf Tronnes
129b78e456SThomas Petazzoni  */
139b78e456SThomas Petazzoni 
149b78e456SThomas Petazzoni #include <linux/module.h>
159b78e456SThomas Petazzoni #include <linux/kernel.h>
169b78e456SThomas Petazzoni #include <linux/init.h>
179b78e456SThomas Petazzoni #include <linux/delay.h>
18467786c1SPriit Laes #include <video/mipi_display.h>
199b78e456SThomas Petazzoni 
209b78e456SThomas Petazzoni #include "fbtft.h"
219b78e456SThomas Petazzoni 
229b78e456SThomas Petazzoni #define DRVNAME		"fb_ili9341"
239b78e456SThomas Petazzoni #define WIDTH		240
249b78e456SThomas Petazzoni #define HEIGHT		320
259b78e456SThomas Petazzoni #define TXBUFLEN	(4 * PAGE_SIZE)
269b78e456SThomas Petazzoni #define DEFAULT_GAMMA	"1F 1A 18 0A 0F 06 45 87 32 0A 07 02 07 05 00\n" \
279b78e456SThomas Petazzoni 			"00 25 27 05 10 09 3A 78 4D 05 18 0D 38 3A 1F"
289b78e456SThomas Petazzoni 
init_display(struct fbtft_par * par)299b78e456SThomas Petazzoni static int init_display(struct fbtft_par *par)
309b78e456SThomas Petazzoni {
319b78e456SThomas Petazzoni 	par->fbtftops.reset(par);
329b78e456SThomas Petazzoni 
339b78e456SThomas Petazzoni 	/* startup sequence for MI0283QT-9A */
34467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SOFT_RESET);
359b78e456SThomas Petazzoni 	mdelay(5);
36467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
379b78e456SThomas Petazzoni 	/* --------------------------------------------------------- */
389b78e456SThomas Petazzoni 	write_reg(par, 0xCF, 0x00, 0x83, 0x30);
399b78e456SThomas Petazzoni 	write_reg(par, 0xED, 0x64, 0x03, 0x12, 0x81);
409b78e456SThomas Petazzoni 	write_reg(par, 0xE8, 0x85, 0x01, 0x79);
419b78e456SThomas Petazzoni 	write_reg(par, 0xCB, 0x39, 0X2C, 0x00, 0x34, 0x02);
429b78e456SThomas Petazzoni 	write_reg(par, 0xF7, 0x20);
439b78e456SThomas Petazzoni 	write_reg(par, 0xEA, 0x00, 0x00);
449b78e456SThomas Petazzoni 	/* ------------power control-------------------------------- */
459b78e456SThomas Petazzoni 	write_reg(par, 0xC0, 0x26);
469b78e456SThomas Petazzoni 	write_reg(par, 0xC1, 0x11);
479b78e456SThomas Petazzoni 	/* ------------VCOM --------- */
489b78e456SThomas Petazzoni 	write_reg(par, 0xC5, 0x35, 0x3E);
499b78e456SThomas Petazzoni 	write_reg(par, 0xC7, 0xBE);
509b78e456SThomas Petazzoni 	/* ------------memory access control------------------------ */
51467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, 0x55); /* 16bit pixel */
529b78e456SThomas Petazzoni 	/* ------------frame rate----------------------------------- */
539b78e456SThomas Petazzoni 	write_reg(par, 0xB1, 0x00, 0x1B);
549b78e456SThomas Petazzoni 	/* ------------Gamma---------------------------------------- */
559b78e456SThomas Petazzoni 	/* write_reg(par, 0xF2, 0x08); */ /* Gamma Function Disable */
56467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SET_GAMMA_CURVE, 0x01);
579b78e456SThomas Petazzoni 	/* ------------display-------------------------------------- */
589b78e456SThomas Petazzoni 	write_reg(par, 0xB7, 0x07); /* entry mode set */
599b78e456SThomas Petazzoni 	write_reg(par, 0xB6, 0x0A, 0x82, 0x27, 0x00);
60467786c1SPriit Laes 	write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
619b78e456SThomas Petazzoni 	mdelay(100);
62467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
639b78e456SThomas Petazzoni 	mdelay(20);
649b78e456SThomas Petazzoni 
659b78e456SThomas Petazzoni 	return 0;
669b78e456SThomas Petazzoni }
679b78e456SThomas Petazzoni 
set_addr_win(struct fbtft_par * par,int xs,int ys,int xe,int ye)689b78e456SThomas Petazzoni static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye)
699b78e456SThomas Petazzoni {
70467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
719b78e456SThomas Petazzoni 		  (xs >> 8) & 0xFF, xs & 0xFF, (xe >> 8) & 0xFF, xe & 0xFF);
729b78e456SThomas Petazzoni 
73467786c1SPriit Laes 	write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
749b78e456SThomas Petazzoni 		  (ys >> 8) & 0xFF, ys & 0xFF, (ye >> 8) & 0xFF, ye & 0xFF);
759b78e456SThomas Petazzoni 
76467786c1SPriit Laes 	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
779b78e456SThomas Petazzoni }
789b78e456SThomas Petazzoni 
79467786c1SPriit Laes #define MEM_Y   BIT(7) /* MY row address order */
80467786c1SPriit Laes #define MEM_X   BIT(6) /* MX column address order */
81467786c1SPriit Laes #define MEM_V   BIT(5) /* MV row / column exchange */
82467786c1SPriit Laes #define MEM_L   BIT(4) /* ML vertical refresh order */
83467786c1SPriit Laes #define MEM_H   BIT(2) /* MH horizontal refresh order */
849b78e456SThomas Petazzoni #define MEM_BGR (3) /* RGB-BGR Order */
set_var(struct fbtft_par * par)859b78e456SThomas Petazzoni static int set_var(struct fbtft_par *par)
869b78e456SThomas Petazzoni {
879b78e456SThomas Petazzoni 	switch (par->info->var.rotate) {
889b78e456SThomas Petazzoni 	case 0:
89467786c1SPriit Laes 		write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,
90467786c1SPriit Laes 			  MEM_X | (par->bgr << MEM_BGR));
919b78e456SThomas Petazzoni 		break;
929b78e456SThomas Petazzoni 	case 270:
93467786c1SPriit Laes 		write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,
94467786c1SPriit Laes 			  MEM_V | MEM_L | (par->bgr << MEM_BGR));
959b78e456SThomas Petazzoni 		break;
969b78e456SThomas Petazzoni 	case 180:
97467786c1SPriit Laes 		write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,
98467786c1SPriit Laes 			  MEM_Y | (par->bgr << MEM_BGR));
999b78e456SThomas Petazzoni 		break;
1009b78e456SThomas Petazzoni 	case 90:
101467786c1SPriit Laes 		write_reg(par, MIPI_DCS_SET_ADDRESS_MODE,
102467786c1SPriit Laes 			  MEM_Y | MEM_X | MEM_V | (par->bgr << MEM_BGR));
1039b78e456SThomas Petazzoni 		break;
1049b78e456SThomas Petazzoni 	}
1059b78e456SThomas Petazzoni 
1069b78e456SThomas Petazzoni 	return 0;
1079b78e456SThomas Petazzoni }
1089b78e456SThomas Petazzoni 
1099b78e456SThomas Petazzoni /*
1103a334ea5SEva Rachel Retuya  * Gamma string format:
1113a334ea5SEva Rachel Retuya  *  Positive: Par1 Par2 [...] Par15
1123a334ea5SEva Rachel Retuya  *  Negative: Par1 Par2 [...] Par15
1139b78e456SThomas Petazzoni  */
1140b1533c6SLeonardo Brás #define CURVE(num, idx)  curves[(num) * par->gamma.num_values + (idx)]
set_gamma(struct fbtft_par * par,u32 * curves)11522eb36b8SArnd Bergmann static int set_gamma(struct fbtft_par *par, u32 *curves)
1169b78e456SThomas Petazzoni {
1179b78e456SThomas Petazzoni 	int i;
1189b78e456SThomas Petazzoni 
1199b78e456SThomas Petazzoni 	for (i = 0; i < par->gamma.num_curves; i++)
1209b78e456SThomas Petazzoni 		write_reg(par, 0xE0 + i,
1219b78e456SThomas Petazzoni 			  CURVE(i, 0), CURVE(i, 1), CURVE(i, 2),
1229b78e456SThomas Petazzoni 			  CURVE(i, 3), CURVE(i, 4), CURVE(i, 5),
1239b78e456SThomas Petazzoni 			  CURVE(i, 6), CURVE(i, 7), CURVE(i, 8),
1249b78e456SThomas Petazzoni 			  CURVE(i, 9), CURVE(i, 10), CURVE(i, 11),
1259b78e456SThomas Petazzoni 			  CURVE(i, 12), CURVE(i, 13), CURVE(i, 14));
1269b78e456SThomas Petazzoni 
1279b78e456SThomas Petazzoni 	return 0;
1289b78e456SThomas Petazzoni }
129b4ac6b28SEva Rachel Retuya 
1309b78e456SThomas Petazzoni #undef CURVE
1319b78e456SThomas Petazzoni 
1329b78e456SThomas Petazzoni static struct fbtft_display display = {
1339b78e456SThomas Petazzoni 	.regwidth = 8,
1349b78e456SThomas Petazzoni 	.width = WIDTH,
1359b78e456SThomas Petazzoni 	.height = HEIGHT,
1369b78e456SThomas Petazzoni 	.txbuflen = TXBUFLEN,
1379b78e456SThomas Petazzoni 	.gamma_num = 2,
1389b78e456SThomas Petazzoni 	.gamma_len = 15,
1399b78e456SThomas Petazzoni 	.gamma = DEFAULT_GAMMA,
1409b78e456SThomas Petazzoni 	.fbtftops = {
1419b78e456SThomas Petazzoni 		.init_display = init_display,
1429b78e456SThomas Petazzoni 		.set_addr_win = set_addr_win,
1439b78e456SThomas Petazzoni 		.set_var = set_var,
1449b78e456SThomas Petazzoni 		.set_gamma = set_gamma,
1459b78e456SThomas Petazzoni 	},
1469b78e456SThomas Petazzoni };
1471014c2ceSAnish Bhatt 
148*4912649eSRaphael Gallais-Pou FBTFT_REGISTER_SPI_DRIVER(DRVNAME, "ilitek", "ili9341", &display);
1499b78e456SThomas Petazzoni 
1509b78e456SThomas Petazzoni MODULE_ALIAS("spi:" DRVNAME);
1519b78e456SThomas Petazzoni MODULE_ALIAS("platform:" DRVNAME);
1529b78e456SThomas Petazzoni MODULE_ALIAS("spi:ili9341");
1539b78e456SThomas Petazzoni MODULE_ALIAS("platform:ili9341");
1549b78e456SThomas Petazzoni 
1559b78e456SThomas Petazzoni MODULE_DESCRIPTION("FB driver for the ILI9341 LCD display controller");
1569b78e456SThomas Petazzoni MODULE_AUTHOR("Christian Vogelgsang");
1579b78e456SThomas Petazzoni MODULE_LICENSE("GPL");
158