1
2
3
4
5
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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)
69#define MADCTL_MV BIT(5)
70#define MADCTL_MX BIT(6)
71#define MADCTL_MY BIT(7)
72
73
74#define PANEL_TE_TIMEOUT_MS 33
75
76static struct completion panel_te;
77static int irq_te;
78
79static irqreturn_t panel_te_handler(int irq, void *data)
80{
81 complete(&panel_te);
82 return IRQ_HANDLED;
83}
84
85
86
87
88
89
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
102 if (!te) {
103 irq_te = 0;
104 return 0;
105 }
106
107 irq = gpiod_to_irq(te);
108
109
110 gpiod_put(te);
111
112 if (irq < 0)
113 return irq;
114
115 irq_te = irq;
116 init_completion(&panel_te);
117
118
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
131
132
133
134
135
136
137
138
139
140
141
142
143static int init_display(struct fbtft_par *par)
144{
145 int rc;
146
147 rc = init_tearing_effect_line(par);
148 if (rc)
149 return rc;
150
151
152 write_reg(par, MIPI_DCS_EXIT_SLEEP_MODE);
153 mdelay(120);
154
155
156 write_reg(par, MIPI_DCS_SET_PIXEL_FORMAT, MIPI_DCS_PIXEL_FMT_16BIT);
157 if (HSD20_IPS)
158 write_reg(par, PORCTRL, 0x05, 0x05, 0x00, 0x33, 0x33);
159
160 else
161 write_reg(par, PORCTRL, 0x08, 0x08, 0x00, 0x22, 0x22);
162
163
164
165
166
167 if (HSD20_IPS)
168 write_reg(par, GCTRL, 0x75);
169 else
170 write_reg(par, GCTRL, 0x35);
171
172
173
174
175
176 write_reg(par, VDVVRHEN, 0x01, 0xFF);
177
178
179
180
181
182 if (HSD20_IPS)
183 write_reg(par, VRHS, 0x13);
184 else
185 write_reg(par, VRHS, 0x0B);
186
187
188 write_reg(par, VDVS, 0x20);
189
190
191 if (HSD20_IPS)
192 write_reg(par, VCOMS, 0x22);
193 else
194 write_reg(par, VCOMS, 0x20);
195
196
197 write_reg(par, VCMOFSET, 0x20);
198
199
200
201
202
203
204 write_reg(par, PWCTRL1, 0xA4, 0xA1);
205
206
207 if (irq_te)
208 write_reg(par, MIPI_DCS_SET_TEAR_ON, 0x00);
209
210 write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
211
212 if (HSD20_IPS)
213 write_reg(par, MIPI_DCS_ENTER_INVERT_MODE);
214
215 return 0;
216}
217
218
219
220
221
222
223
224
225
226static int write_vmem(struct fbtft_par *par, size_t offset, size_t len)
227{
228 struct device *dev = par->info->device;
229 int ret;
230
231 if (irq_te) {
232 enable_irq(irq_te);
233 reinit_completion(&panel_te);
234 ret = wait_for_completion_timeout(&panel_te,
235 msecs_to_jiffies(PANEL_TE_TIMEOUT_MS));
236 if (ret == 0)
237 dev_err(dev, "wait panel TE timeout\n");
238
239 disable_irq(irq_te);
240 }
241
242 switch (par->pdata->display.buswidth) {
243 case 8:
244 ret = fbtft_write_vmem16_bus8(par, offset, len);
245 break;
246 case 9:
247 ret = fbtft_write_vmem16_bus9(par, offset, len);
248 break;
249 case 16:
250 ret = fbtft_write_vmem16_bus16(par, offset, len);
251 break;
252 default:
253 dev_err(dev, "Unsupported buswidth %d\n",
254 par->pdata->display.buswidth);
255 ret = 0;
256 break;
257 }
258
259 return ret;
260}
261
262
263
264
265
266
267
268
269static int set_var(struct fbtft_par *par)
270{
271 u8 madctl_par = 0;
272
273 if (par->bgr)
274 madctl_par |= MADCTL_BGR;
275 switch (par->info->var.rotate) {
276 case 0:
277 break;
278 case 90:
279 madctl_par |= (MADCTL_MV | MADCTL_MY);
280 break;
281 case 180:
282 madctl_par |= (MADCTL_MX | MADCTL_MY);
283 break;
284 case 270:
285 madctl_par |= (MADCTL_MV | MADCTL_MX);
286 break;
287 default:
288 return -EINVAL;
289 }
290 write_reg(par, MIPI_DCS_SET_ADDRESS_MODE, madctl_par);
291 return 0;
292}
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308static int set_gamma(struct fbtft_par *par, u32 *curves)
309{
310 int i;
311 int j;
312 int c;
313
314
315
316
317
318
319 static const u8 gamma_par_mask[] = {
320 0xFF,
321 0x3F,
322 0x3F,
323 0x1F,
324 0x1F,
325 0x3F,
326 0x7F,
327 0x77,
328 0x7F,
329 0x3F,
330 0x1F,
331 0x1F,
332 0x3F,
333 0x3F,
334 };
335
336 for (i = 0; i < par->gamma.num_curves; i++) {
337 c = i * par->gamma.num_values;
338 for (j = 0; j < par->gamma.num_values; j++)
339 curves[c + j] &= gamma_par_mask[j];
340 write_reg(par, PVGAMCTRL + i,
341 curves[c + 0], curves[c + 1], curves[c + 2],
342 curves[c + 3], curves[c + 4], curves[c + 5],
343 curves[c + 6], curves[c + 7], curves[c + 8],
344 curves[c + 9], curves[c + 10], curves[c + 11],
345 curves[c + 12], curves[c + 13]);
346 }
347 return 0;
348}
349
350
351
352
353
354
355
356
357
358static int blank(struct fbtft_par *par, bool on)
359{
360 if (on)
361 write_reg(par, MIPI_DCS_SET_DISPLAY_OFF);
362 else
363 write_reg(par, MIPI_DCS_SET_DISPLAY_ON);
364 return 0;
365}
366
367static struct fbtft_display display = {
368 .regwidth = 8,
369 .width = 240,
370 .height = 320,
371 .gamma_num = 2,
372 .gamma_len = 14,
373 .gamma = HSD20_IPS_GAMMA,
374 .fbtftops = {
375 .init_display = init_display,
376 .write_vmem = write_vmem,
377 .set_var = set_var,
378 .set_gamma = set_gamma,
379 .blank = blank,
380 },
381};
382
383FBTFT_REGISTER_DRIVER(DRVNAME, "sitronix,st7789v", &display);
384
385MODULE_ALIAS("spi:" DRVNAME);
386MODULE_ALIAS("platform:" DRVNAME);
387MODULE_ALIAS("spi:st7789v");
388MODULE_ALIAS("platform:st7789v");
389
390MODULE_DESCRIPTION("FB driver for the ST7789V LCD Controller");
391MODULE_AUTHOR("Dennis Menschel");
392MODULE_LICENSE("GPL");
393