1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * FB driver for the S6D1121 LCD Controller |
4 | * |
5 | * Copyright (C) 2013 Roman Rolinsky |
6 | * |
7 | * Based on fb_ili9325.c by Noralf Tronnes |
8 | * Based on ili9325.c by Jeroen Domburg |
9 | * Init code from UTFT library by Henning Karlsen |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/init.h> |
15 | #include <linux/delay.h> |
16 | |
17 | #include "fbtft.h" |
18 | |
19 | #define DRVNAME "fb_s6d1121" |
20 | #define WIDTH 240 |
21 | #define HEIGHT 320 |
22 | #define BPP 16 |
23 | #define FPS 20 |
24 | #define DEFAULT_GAMMA "26 09 24 2C 1F 23 24 25 22 26 25 23 0D 00\n" \ |
25 | "1C 1A 13 1D 0B 11 12 10 13 15 36 19 00 0D" |
26 | |
27 | static int init_display(struct fbtft_par *par) |
28 | { |
29 | par->fbtftops.reset(par); |
30 | |
31 | /* Initialization sequence from Lib_UTFT */ |
32 | |
33 | write_reg(par, 0x0011, 0x2004); |
34 | write_reg(par, 0x0013, 0xCC00); |
35 | write_reg(par, 0x0015, 0x2600); |
36 | write_reg(par, 0x0014, 0x252A); |
37 | write_reg(par, 0x0012, 0x0033); |
38 | write_reg(par, 0x0013, 0xCC04); |
39 | write_reg(par, 0x0013, 0xCC06); |
40 | write_reg(par, 0x0013, 0xCC4F); |
41 | write_reg(par, 0x0013, 0x674F); |
42 | write_reg(par, 0x0011, 0x2003); |
43 | write_reg(par, 0x0016, 0x0007); |
44 | write_reg(par, 0x0002, 0x0013); |
45 | write_reg(par, 0x0003, 0x0003); |
46 | write_reg(par, 0x0001, 0x0127); |
47 | write_reg(par, 0x0008, 0x0303); |
48 | write_reg(par, 0x000A, 0x000B); |
49 | write_reg(par, 0x000B, 0x0003); |
50 | write_reg(par, 0x000C, 0x0000); |
51 | write_reg(par, 0x0041, 0x0000); |
52 | write_reg(par, 0x0050, 0x0000); |
53 | write_reg(par, 0x0060, 0x0005); |
54 | write_reg(par, 0x0070, 0x000B); |
55 | write_reg(par, 0x0071, 0x0000); |
56 | write_reg(par, 0x0078, 0x0000); |
57 | write_reg(par, 0x007A, 0x0000); |
58 | write_reg(par, 0x0079, 0x0007); |
59 | write_reg(par, 0x0007, 0x0051); |
60 | write_reg(par, 0x0007, 0x0053); |
61 | write_reg(par, 0x0079, 0x0000); |
62 | |
63 | write_reg(par, 0x0022); /* Write Data to GRAM */ |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) |
69 | { |
70 | switch (par->info->var.rotate) { |
71 | /* R20h = Horizontal GRAM Start Address */ |
72 | /* R21h = Vertical GRAM Start Address */ |
73 | case 0: |
74 | write_reg(par, 0x0020, xs); |
75 | write_reg(par, 0x0021, ys); |
76 | break; |
77 | case 180: |
78 | write_reg(par, 0x0020, WIDTH - 1 - xs); |
79 | write_reg(par, 0x0021, HEIGHT - 1 - ys); |
80 | break; |
81 | case 270: |
82 | write_reg(par, 0x0020, WIDTH - 1 - ys); |
83 | write_reg(par, 0x0021, xs); |
84 | break; |
85 | case 90: |
86 | write_reg(par, 0x0020, ys); |
87 | write_reg(par, 0x0021, HEIGHT - 1 - xs); |
88 | break; |
89 | } |
90 | write_reg(par, 0x0022); /* Write Data to GRAM */ |
91 | } |
92 | |
93 | static int set_var(struct fbtft_par *par) |
94 | { |
95 | switch (par->info->var.rotate) { |
96 | /* AM: GRAM update direction */ |
97 | case 0: |
98 | write_reg(par, 0x03, 0x0003 | (par->bgr << 12)); |
99 | break; |
100 | case 180: |
101 | write_reg(par, 0x03, 0x0000 | (par->bgr << 12)); |
102 | break; |
103 | case 270: |
104 | write_reg(par, 0x03, 0x000A | (par->bgr << 12)); |
105 | break; |
106 | case 90: |
107 | write_reg(par, 0x03, 0x0009 | (par->bgr << 12)); |
108 | break; |
109 | } |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | /* |
115 | * Gamma string format: |
116 | * PKP0 PKP1 PKP2 PKP3 PKP4 PKP5 PKP6 PKP7 PKP8 PKP9 PKP10 PKP11 VRP0 VRP1 |
117 | * PKN0 PKN1 PKN2 PKN3 PKN4 PKN5 PKN6 PKN7 PRN8 PRN9 PRN10 PRN11 VRN0 VRN1 |
118 | */ |
119 | #define CURVE(num, idx) curves[(num) * par->gamma.num_values + (idx)] |
120 | static int set_gamma(struct fbtft_par *par, u32 *curves) |
121 | { |
122 | static const unsigned long mask[] = { |
123 | 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, |
124 | 0x3f, 0x3f, 0x1f, 0x1f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, |
125 | 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x1f, 0x1f, |
126 | }; |
127 | int i, j; |
128 | |
129 | /* apply mask */ |
130 | for (i = 0; i < 2; i++) |
131 | for (j = 0; j < 14; j++) |
132 | CURVE(i, j) &= mask[i * par->gamma.num_values + j]; |
133 | |
134 | write_reg(par, 0x0030, CURVE(0, 1) << 8 | CURVE(0, 0)); |
135 | write_reg(par, 0x0031, CURVE(0, 3) << 8 | CURVE(0, 2)); |
136 | write_reg(par, 0x0032, CURVE(0, 5) << 8 | CURVE(0, 3)); |
137 | write_reg(par, 0x0033, CURVE(0, 7) << 8 | CURVE(0, 6)); |
138 | write_reg(par, 0x0034, CURVE(0, 9) << 8 | CURVE(0, 8)); |
139 | write_reg(par, 0x0035, CURVE(0, 11) << 8 | CURVE(0, 10)); |
140 | |
141 | write_reg(par, 0x0036, CURVE(1, 1) << 8 | CURVE(1, 0)); |
142 | write_reg(par, 0x0037, CURVE(1, 3) << 8 | CURVE(1, 2)); |
143 | write_reg(par, 0x0038, CURVE(1, 5) << 8 | CURVE(1, 4)); |
144 | write_reg(par, 0x0039, CURVE(1, 7) << 8 | CURVE(1, 6)); |
145 | write_reg(par, 0x003A, CURVE(1, 9) << 8 | CURVE(1, 8)); |
146 | write_reg(par, 0x003B, CURVE(1, 11) << 8 | CURVE(1, 10)); |
147 | |
148 | write_reg(par, 0x003C, CURVE(0, 13) << 8 | CURVE(0, 12)); |
149 | write_reg(par, 0x003D, CURVE(1, 13) << 8 | CURVE(1, 12)); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | #undef CURVE |
155 | |
156 | static struct fbtft_display display = { |
157 | .regwidth = 16, |
158 | .width = WIDTH, |
159 | .height = HEIGHT, |
160 | .bpp = BPP, |
161 | .fps = FPS, |
162 | .gamma_num = 2, |
163 | .gamma_len = 14, |
164 | .gamma = DEFAULT_GAMMA, |
165 | .fbtftops = { |
166 | .init_display = init_display, |
167 | .set_addr_win = set_addr_win, |
168 | .set_var = set_var, |
169 | .set_gamma = set_gamma, |
170 | }, |
171 | }; |
172 | |
173 | FBTFT_REGISTER_DRIVER(DRVNAME, "samsung,s6d1121" , &display); |
174 | |
175 | MODULE_ALIAS("spi:" DRVNAME); |
176 | MODULE_ALIAS("platform:" DRVNAME); |
177 | MODULE_ALIAS("spi:s6d1121" ); |
178 | MODULE_ALIAS("platform:s6d1121" ); |
179 | |
180 | MODULE_DESCRIPTION("FB driver for the S6D1121 LCD Controller" ); |
181 | MODULE_AUTHOR("Roman Rolinsky" ); |
182 | MODULE_LICENSE("GPL" ); |
183 | |