1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * FB driver for the ILI9320 LCD Controller |
4 | * |
5 | * Copyright (C) 2013 Noralf Tronnes |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/spi/spi.h> |
12 | #include <linux/delay.h> |
13 | |
14 | #include "fbtft.h" |
15 | |
16 | #define DRVNAME "fb_ili9320" |
17 | #define WIDTH 240 |
18 | #define HEIGHT 320 |
19 | #define DEFAULT_GAMMA "07 07 6 0 0 0 5 5 4 0\n" \ |
20 | "07 08 4 7 5 1 2 0 7 7" |
21 | |
22 | static unsigned int read_devicecode(struct fbtft_par *par) |
23 | { |
24 | u8 rxbuf[8] = {0, }; |
25 | |
26 | write_reg(par, 0x0000); |
27 | par->fbtftops.read(par, rxbuf, 4); |
28 | return (rxbuf[2] << 8) | rxbuf[3]; |
29 | } |
30 | |
31 | static int init_display(struct fbtft_par *par) |
32 | { |
33 | unsigned int devcode; |
34 | |
35 | par->fbtftops.reset(par); |
36 | |
37 | devcode = read_devicecode(par); |
38 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, "Device code: 0x%04X\n" , |
39 | devcode); |
40 | if ((devcode != 0x0000) && (devcode != 0x9320)) |
41 | dev_warn(par->info->device, |
42 | "Unrecognized Device code: 0x%04X (expected 0x9320)\n" , |
43 | devcode); |
44 | |
45 | /* Initialization sequence from ILI9320 Application Notes */ |
46 | |
47 | /* *********** Start Initial Sequence ********* */ |
48 | /* Set the Vcore voltage and this setting is must. */ |
49 | write_reg(par, 0x00E5, 0x8000); |
50 | |
51 | /* Start internal OSC. */ |
52 | write_reg(par, 0x0000, 0x0001); |
53 | |
54 | /* set SS and SM bit */ |
55 | write_reg(par, 0x0001, 0x0100); |
56 | |
57 | /* set 1 line inversion */ |
58 | write_reg(par, 0x0002, 0x0700); |
59 | |
60 | /* Resize register */ |
61 | write_reg(par, 0x0004, 0x0000); |
62 | |
63 | /* set the back and front porch */ |
64 | write_reg(par, 0x0008, 0x0202); |
65 | |
66 | /* set non-display area refresh cycle */ |
67 | write_reg(par, 0x0009, 0x0000); |
68 | |
69 | /* FMARK function */ |
70 | write_reg(par, 0x000A, 0x0000); |
71 | |
72 | /* RGB interface setting */ |
73 | write_reg(par, 0x000C, 0x0000); |
74 | |
75 | /* Frame marker Position */ |
76 | write_reg(par, 0x000D, 0x0000); |
77 | |
78 | /* RGB interface polarity */ |
79 | write_reg(par, 0x000F, 0x0000); |
80 | |
81 | /* ***********Power On sequence *************** */ |
82 | /* SAP, BT[3:0], AP, DSTB, SLP, STB */ |
83 | write_reg(par, 0x0010, 0x0000); |
84 | |
85 | /* DC1[2:0], DC0[2:0], VC[2:0] */ |
86 | write_reg(par, 0x0011, 0x0007); |
87 | |
88 | /* VREG1OUT voltage */ |
89 | write_reg(par, 0x0012, 0x0000); |
90 | |
91 | /* VDV[4:0] for VCOM amplitude */ |
92 | write_reg(par, 0x0013, 0x0000); |
93 | |
94 | /* Dis-charge capacitor power voltage */ |
95 | mdelay(200); |
96 | |
97 | /* SAP, BT[3:0], AP, DSTB, SLP, STB */ |
98 | write_reg(par, 0x0010, 0x17B0); |
99 | |
100 | /* R11h=0x0031 at VCI=3.3V DC1[2:0], DC0[2:0], VC[2:0] */ |
101 | write_reg(par, 0x0011, 0x0031); |
102 | mdelay(50); |
103 | |
104 | /* R12h=0x0138 at VCI=3.3V VREG1OUT voltage */ |
105 | write_reg(par, 0x0012, 0x0138); |
106 | mdelay(50); |
107 | |
108 | /* R13h=0x1800 at VCI=3.3V VDV[4:0] for VCOM amplitude */ |
109 | write_reg(par, 0x0013, 0x1800); |
110 | |
111 | /* R29h=0x0008 at VCI=3.3V VCM[4:0] for VCOMH */ |
112 | write_reg(par, 0x0029, 0x0008); |
113 | mdelay(50); |
114 | |
115 | /* GRAM horizontal Address */ |
116 | write_reg(par, 0x0020, 0x0000); |
117 | |
118 | /* GRAM Vertical Address */ |
119 | write_reg(par, 0x0021, 0x0000); |
120 | |
121 | /* ------------------ Set GRAM area --------------- */ |
122 | /* Horizontal GRAM Start Address */ |
123 | write_reg(par, 0x0050, 0x0000); |
124 | |
125 | /* Horizontal GRAM End Address */ |
126 | write_reg(par, 0x0051, 0x00EF); |
127 | |
128 | /* Vertical GRAM Start Address */ |
129 | write_reg(par, 0x0052, 0x0000); |
130 | |
131 | /* Vertical GRAM End Address */ |
132 | write_reg(par, 0x0053, 0x013F); |
133 | |
134 | /* Gate Scan Line */ |
135 | write_reg(par, 0x0060, 0x2700); |
136 | |
137 | /* NDL,VLE, REV */ |
138 | write_reg(par, 0x0061, 0x0001); |
139 | |
140 | /* set scrolling line */ |
141 | write_reg(par, 0x006A, 0x0000); |
142 | |
143 | /* -------------- Partial Display Control --------- */ |
144 | write_reg(par, 0x0080, 0x0000); |
145 | write_reg(par, 0x0081, 0x0000); |
146 | write_reg(par, 0x0082, 0x0000); |
147 | write_reg(par, 0x0083, 0x0000); |
148 | write_reg(par, 0x0084, 0x0000); |
149 | write_reg(par, 0x0085, 0x0000); |
150 | |
151 | /* -------------- Panel Control ------------------- */ |
152 | write_reg(par, 0x0090, 0x0010); |
153 | write_reg(par, 0x0092, 0x0000); |
154 | write_reg(par, 0x0093, 0x0003); |
155 | write_reg(par, 0x0095, 0x0110); |
156 | write_reg(par, 0x0097, 0x0000); |
157 | write_reg(par, 0x0098, 0x0000); |
158 | write_reg(par, 0x0007, 0x0173); /* 262K color and display ON */ |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) |
164 | { |
165 | switch (par->info->var.rotate) { |
166 | /* R20h = Horizontal GRAM Start Address */ |
167 | /* R21h = Vertical GRAM Start Address */ |
168 | case 0: |
169 | write_reg(par, 0x0020, xs); |
170 | write_reg(par, 0x0021, ys); |
171 | break; |
172 | case 180: |
173 | write_reg(par, 0x0020, WIDTH - 1 - xs); |
174 | write_reg(par, 0x0021, HEIGHT - 1 - ys); |
175 | break; |
176 | case 270: |
177 | write_reg(par, 0x0020, WIDTH - 1 - ys); |
178 | write_reg(par, 0x0021, xs); |
179 | break; |
180 | case 90: |
181 | write_reg(par, 0x0020, ys); |
182 | write_reg(par, 0x0021, HEIGHT - 1 - xs); |
183 | break; |
184 | } |
185 | write_reg(par, 0x0022); /* Write Data to GRAM */ |
186 | } |
187 | |
188 | static int set_var(struct fbtft_par *par) |
189 | { |
190 | switch (par->info->var.rotate) { |
191 | case 0: |
192 | write_reg(par, 0x3, (par->bgr << 12) | 0x30); |
193 | break; |
194 | case 270: |
195 | write_reg(par, 0x3, (par->bgr << 12) | 0x28); |
196 | break; |
197 | case 180: |
198 | write_reg(par, 0x3, (par->bgr << 12) | 0x00); |
199 | break; |
200 | case 90: |
201 | write_reg(par, 0x3, (par->bgr << 12) | 0x18); |
202 | break; |
203 | } |
204 | return 0; |
205 | } |
206 | |
207 | /* |
208 | * Gamma string format: |
209 | * VRP0 VRP1 RP0 RP1 KP0 KP1 KP2 KP3 KP4 KP5 |
210 | * VRN0 VRN1 RN0 RN1 KN0 KN1 KN2 KN3 KN4 KN5 |
211 | */ |
212 | #define CURVE(num, idx) curves[(num) * par->gamma.num_values + (idx)] |
213 | static int set_gamma(struct fbtft_par *par, u32 *curves) |
214 | { |
215 | static const unsigned long mask[] = { |
216 | 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, |
217 | 0x1f, 0x1f, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, |
218 | }; |
219 | int i, j; |
220 | |
221 | /* apply mask */ |
222 | for (i = 0; i < 2; i++) |
223 | for (j = 0; j < 10; j++) |
224 | CURVE(i, j) &= mask[i * par->gamma.num_values + j]; |
225 | |
226 | write_reg(par, 0x0030, CURVE(0, 5) << 8 | CURVE(0, 4)); |
227 | write_reg(par, 0x0031, CURVE(0, 7) << 8 | CURVE(0, 6)); |
228 | write_reg(par, 0x0032, CURVE(0, 9) << 8 | CURVE(0, 8)); |
229 | write_reg(par, 0x0035, CURVE(0, 3) << 8 | CURVE(0, 2)); |
230 | write_reg(par, 0x0036, CURVE(0, 1) << 8 | CURVE(0, 0)); |
231 | |
232 | write_reg(par, 0x0037, CURVE(1, 5) << 8 | CURVE(1, 4)); |
233 | write_reg(par, 0x0038, CURVE(1, 7) << 8 | CURVE(1, 6)); |
234 | write_reg(par, 0x0039, CURVE(1, 9) << 8 | CURVE(1, 8)); |
235 | write_reg(par, 0x003C, CURVE(1, 3) << 8 | CURVE(1, 2)); |
236 | write_reg(par, 0x003D, CURVE(1, 1) << 8 | CURVE(1, 0)); |
237 | |
238 | return 0; |
239 | } |
240 | |
241 | #undef CURVE |
242 | |
243 | static struct fbtft_display display = { |
244 | .regwidth = 16, |
245 | .width = WIDTH, |
246 | .height = HEIGHT, |
247 | .gamma_num = 2, |
248 | .gamma_len = 10, |
249 | .gamma = DEFAULT_GAMMA, |
250 | .fbtftops = { |
251 | .init_display = init_display, |
252 | .set_addr_win = set_addr_win, |
253 | .set_var = set_var, |
254 | .set_gamma = set_gamma, |
255 | }, |
256 | }; |
257 | |
258 | FBTFT_REGISTER_DRIVER(DRVNAME, "ilitek,ili9320" , &display); |
259 | |
260 | MODULE_ALIAS("spi:" DRVNAME); |
261 | MODULE_ALIAS("platform:" DRVNAME); |
262 | MODULE_ALIAS("spi:ili9320" ); |
263 | MODULE_ALIAS("platform:ili9320" ); |
264 | |
265 | MODULE_DESCRIPTION("FB driver for the ILI9320 LCD Controller" ); |
266 | MODULE_AUTHOR("Noralf Tronnes" ); |
267 | MODULE_LICENSE("GPL" ); |
268 | |