xref: /linux/drivers/staging/fbtft/fb_ili9163.c (revision 44f57d78)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * FB driver for the ILI9163 LCD Controller
4  *
5  * Copyright (C) 2015 Kozhevnikov Anatoly
6  *
7  * Based on ili9325.c by Noralf Tronnes and
8  * .S.U.M.O.T.O.Y. by Max MC Costa (https://github.com/sumotoy/TFT_ILI9163C).
9  */
10 
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/init.h>
14 #include <linux/gpio/consumer.h>
15 #include <linux/delay.h>
16 #include <video/mipi_display.h>
17 
18 #include "fbtft.h"
19 
20 #define DRVNAME		"fb_ili9163"
21 #define WIDTH		128
22 #define HEIGHT		128
23 #define BPP		16
24 #define FPS		30
25 
26 #ifdef GAMMA_ADJ
27 #define GAMMA_LEN	15
28 #define GAMMA_NUM	1
29 #define DEFAULT_GAMMA	"36 29 12 22 1C 15 42 B7 2F 13 12 0A 11 0B 06\n"
30 #endif
31 
32 /* ILI9163C commands */
33 #define CMD_FRMCTR1	0xB1 /* Frame Rate Control */
34 			     /*	(In normal mode/Full colors) */
35 #define CMD_FRMCTR2	0xB2 /* Frame Rate Control (In Idle mode/8-colors) */
36 #define CMD_FRMCTR3	0xB3 /* Frame Rate Control */
37 			     /*	(In Partial mode/full colors) */
38 #define CMD_DINVCTR	0xB4 /* Display Inversion Control */
39 #define CMD_RGBBLK	0xB5 /* RGB Interface Blanking Porch setting */
40 #define CMD_DFUNCTR	0xB6 /* Display Function set 5 */
41 #define CMD_SDRVDIR	0xB7 /* Source Driver Direction Control */
42 #define CMD_GDRVDIR	0xB8 /* Gate Driver Direction Control  */
43 
44 #define CMD_PWCTR1	0xC0 /* Power_Control1 */
45 #define CMD_PWCTR2	0xC1 /* Power_Control2 */
46 #define CMD_PWCTR3	0xC2 /* Power_Control3 */
47 #define CMD_PWCTR4	0xC3 /* Power_Control4 */
48 #define CMD_PWCTR5	0xC4 /* Power_Control5 */
49 #define CMD_VCOMCTR1	0xC5 /* VCOM_Control 1 */
50 #define CMD_VCOMCTR2	0xC6 /* VCOM_Control 2 */
51 #define CMD_VCOMOFFS	0xC7 /* VCOM Offset Control */
52 #define CMD_PGAMMAC	0xE0 /* Positive Gamma Correction Setting */
53 #define CMD_NGAMMAC	0xE1 /* Negative Gamma Correction Setting */
54 #define CMD_GAMRSEL	0xF2 /* GAM_R_SEL */
55 
56 /*
57  * This display:
58  * http://www.ebay.com/itm/Replace-Nokia-5110-LCD-1-44-Red-Serial-128X128-SPI-
59  * Color-TFT-LCD-Display-Module-/271422122271
60  * This particular display has a design error! The controller has 3 pins to
61  * configure to constrain the memory and resolution to a fixed dimension (in
62  * that case 128x128) but they leaved those pins configured for 128x160 so
63  * there was several pixel memory addressing problems.
64  * I solved by setup several parameters that dinamically fix the resolution as
65  * needit so below the parameters for this display. If you have a strain or a
66  * correct display (can happen with chinese) you can copy those parameters and
67  * create setup for different displays.
68  */
69 
70 #ifdef RED
71 #define __OFFSET		32 /*see note 2 - this is the red version */
72 #else
73 #define __OFFSET		0  /*see note 2 - this is the black version */
74 #endif
75 
76 static int init_display(struct fbtft_par *par)
77 {
78 	par->fbtftops.reset(par);
79 
80 	if (!par->gpio.cs)
81 		gpiod_set_value(par->gpio.cs, 0);  /* Activate chip */
82 
83 	write_reg(par, MIPI_DCS_SOFT_RESET); /* software reset */
84 	mdelay(500);
85 	write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE); /* exit sleep */
86 	mdelay(5);
87 	write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
88 	/* default gamma curve 3 */
89 	write_reg(par, MIPI_DCS_SET_GAMMA_CURVE, 0x02);
90 #ifdef GAMMA_ADJ
91 	write_reg(par, CMD_GAMRSEL, 0x01); /* Enable Gamma adj */
92 #endif
93 	write_reg(par, MIPI_DCS_ENTER_NORMAL_MODE);
94 	write_reg(par, CMD_DFUNCTR, 0xff, 0x06);
95 	/* Frame Rate Control (In normal mode/Full colors) */
96 	write_reg(par, CMD_FRMCTR1, 0x08, 0x02);
97 	write_reg(par, CMD_DINVCTR, 0x07); /* display inversion  */
98 	/* Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD */
99 	write_reg(par, CMD_PWCTR1, 0x0A, 0x02);
100 	/* Set BT[2:0] for AVDD & VCL & VGH & VGL  */
101 	write_reg(par, CMD_PWCTR2, 0x02);
102 	/* Set VMH[6:0] & VML[6:0] for VOMH & VCOML */
103 	write_reg(par, CMD_VCOMCTR1, 0x50, 0x63);
104 	write_reg(par, CMD_VCOMOFFS, 0);
105 
106 	write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS, 0, 0, 0, WIDTH);
107 	write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS, 0, 0, 0, HEIGHT);
108 
109 	write_reg(par, MIPI_DCS_SET_DISPLAY_ON); /* display ON */
110 	write_reg(par, MIPI_DCS_WRITE_MEMORY_START); /* Memory Write */
111 
112 	return 0;
113 }
114 
115 static void set_addr_win(struct fbtft_par *par, int xs, int ys,
116 			 int xe, int ye)
117 {
118 	switch (par->info->var.rotate) {
119 	case 0:
120 		write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
121 			  xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
122 		write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
123 			  (ys + __OFFSET) >> 8, (ys + __OFFSET) & 0xff,
124 			  (ye + __OFFSET) >> 8, (ye + __OFFSET) & 0xff);
125 		break;
126 	case 90:
127 		write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
128 			  (xs + __OFFSET) >> 8, (xs + __OFFSET) & 0xff,
129 			  (xe + __OFFSET) >> 8, (xe + __OFFSET) & 0xff);
130 		write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
131 			  ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
132 		break;
133 	case 180:
134 	case 270:
135 		write_reg(par, MIPI_DCS_SET_COLUMN_ADDRESS,
136 			  xs >> 8, xs & 0xff, xe >> 8, xe & 0xff);
137 		write_reg(par, MIPI_DCS_SET_PAGE_ADDRESS,
138 			  ys >> 8, ys & 0xff, ye >> 8, ye & 0xff);
139 		break;
140 	default:
141 		/* Fix incorrect setting */
142 		par->info->var.rotate = 0;
143 	}
144 	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
145 }
146 
147 /*
148  * 7) MY:  1(bottom to top),	0(top to bottom)    Row Address Order
149  * 6) MX:  1(R to L),		0(L to R)	    Column Address Order
150  * 5) MV:  1(Exchanged),	0(normal)	    Row/Column exchange
151  * 4) ML:  1(bottom to top),	0(top to bottom)    Vertical Refresh Order
152  * 3) RGB: 1(BGR),		0(RGB)		    Color Space
153  * 2) MH:  1(R to L),		0(L to R)	    Horizontal Refresh Order
154  * 1)
155  * 0)
156  *
157  *	MY, MX, MV, ML,RGB, MH, D1, D0
158  *	0 | 0 | 0 | 0 | 1 | 0 | 0 | 0	//normal
159  *	1 | 0 | 0 | 0 | 1 | 0 | 0 | 0	//Y-Mirror
160  *	0 | 1 | 0 | 0 | 1 | 0 | 0 | 0	//X-Mirror
161  *	1 | 1 | 0 | 0 | 1 | 0 | 0 | 0	//X-Y-Mirror
162  *	0 | 0 | 1 | 0 | 1 | 0 | 0 | 0	//X-Y Exchange
163  *	1 | 0 | 1 | 0 | 1 | 0 | 0 | 0	//X-Y Exchange, Y-Mirror
164  *	0 | 1 | 1 | 0 | 1 | 0 | 0 | 0	//XY exchange
165  *	1 | 1 | 1 | 0 | 1 | 0 | 0 | 0
166  */
167 static int set_var(struct fbtft_par *par)
168 {
169 	u8 mactrl_data = 0; /* Avoid compiler warning */
170 
171 	switch (par->info->var.rotate) {
172 	case 0:
173 		mactrl_data = 0x08;
174 		break;
175 	case 180:
176 		mactrl_data = 0xC8;
177 		break;
178 	case 270:
179 		mactrl_data = 0xA8;
180 		break;
181 	case 90:
182 		mactrl_data = 0x68;
183 		break;
184 	}
185 
186 	/* Colorspcae */
187 	if (par->bgr)
188 		mactrl_data |= BIT(2);
189 	write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, mactrl_data);
190 	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
191 	return 0;
192 }
193 
194 #ifdef GAMMA_ADJ
195 #define CURVE(num, idx)  curves[(num) * par->gamma.num_values + (idx)]
196 static int gamma_adj(struct fbtft_par *par, u32 *curves)
197 {
198 	unsigned long mask[] = {
199 		0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
200 		0x1f, 0x3f, 0x0f, 0x0f, 0x7f, 0x1f,
201 		0x3F, 0x3F, 0x3F, 0x3F, 0x3F};
202 	int i, j;
203 
204 	for (i = 0; i < GAMMA_NUM; i++)
205 		for (j = 0; j < GAMMA_LEN; j++)
206 			CURVE(i, j) &= mask[i * par->gamma.num_values + j];
207 
208 	write_reg(par, CMD_PGAMMAC,
209 		  CURVE(0, 0),
210 		  CURVE(0, 1),
211 		  CURVE(0, 2),
212 		  CURVE(0, 3),
213 		  CURVE(0, 4),
214 		  CURVE(0, 5),
215 		  CURVE(0, 6),
216 		  (CURVE(0, 7) << 4) | CURVE(0, 8),
217 		  CURVE(0, 9),
218 		  CURVE(0, 10),
219 		  CURVE(0, 11),
220 		  CURVE(0, 12),
221 		  CURVE(0, 13),
222 		  CURVE(0, 14),
223 		  CURVE(0, 15));
224 
225 	/* Write Data to GRAM mode */
226 	write_reg(par, MIPI_DCS_WRITE_MEMORY_START);
227 
228 	return 0;
229 }
230 
231 #undef CURVE
232 #endif
233 
234 static struct fbtft_display display = {
235 	.regwidth = 8,
236 	.width = WIDTH,
237 	.height = HEIGHT,
238 	.bpp = BPP,
239 	.fps = FPS,
240 #ifdef GAMMA_ADJ
241 	.gamma_num = GAMMA_NUM,
242 	.gamma_len = GAMMA_LEN,
243 	.gamma = DEFAULT_GAMMA,
244 #endif
245 	.fbtftops = {
246 		.init_display = init_display,
247 		.set_addr_win = set_addr_win,
248 		.set_var = set_var,
249 #ifdef GAMMA_ADJ
250 		.set_gamma = gamma_adj,
251 #endif
252 	},
253 };
254 
255 FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9163", &display);
256 
257 MODULE_ALIAS("spi:" DRVNAME);
258 MODULE_ALIAS("platform:" DRVNAME);
259 MODULE_ALIAS("spi:ili9163");
260 MODULE_ALIAS("platform:ili9163");
261 
262 MODULE_DESCRIPTION("FB driver for the ILI9163 LCD Controller");
263 MODULE_AUTHOR("Kozhevnikov Anatoly");
264 MODULE_LICENSE("GPL");
265