Commit | Line | Data |
---|---|---|
883daf0a TP |
1 | #include <linux/module.h> |
2 | #include <linux/kernel.h> | |
3 | #include <linux/init.h> | |
4 | #include <linux/gpio.h> | |
5 | #include <linux/spi/spi.h> | |
6 | #include <linux/delay.h> | |
7 | ||
8 | #include "fbtft.h" | |
9 | ||
10 | #define DRVNAME "fb_ssd1351" | |
11 | #define WIDTH 128 | |
12 | #define HEIGHT 128 | |
13 | #define GAMMA_NUM 1 | |
14 | #define GAMMA_LEN 63 | |
15 | #define DEFAULT_GAMMA "0 2 2 2 2 2 2 2 " \ | |
16 | "2 2 2 2 2 2 2 2 " \ | |
17 | "2 2 2 2 2 2 2 2 " \ | |
18 | "2 2 2 2 2 2 2 2 " \ | |
19 | "2 2 2 2 2 2 2 2 " \ | |
20 | "2 2 2 2 2 2 2 2 " \ | |
21 | "2 2 2 2 2 2 2 2 " \ | |
22 | "2 2 2 2 2 2 2" \ | |
23 | ||
24 | static void register_onboard_backlight(struct fbtft_par *par); | |
25 | ||
26 | static int init_display(struct fbtft_par *par) | |
27 | { | |
883daf0a TP |
28 | if (par->pdata |
29 | && par->pdata->display.backlight == FBTFT_ONBOARD_BACKLIGHT) { | |
30 | /* module uses onboard GPIO for panel power */ | |
31 | par->fbtftops.register_backlight = register_onboard_backlight; | |
32 | } | |
33 | ||
34 | par->fbtftops.reset(par); | |
35 | ||
36 | write_reg(par, 0xfd, 0x12); /* Command Lock */ | |
37 | write_reg(par, 0xfd, 0xb1); /* Command Lock */ | |
38 | write_reg(par, 0xae); /* Display Off */ | |
39 | write_reg(par, 0xb3, 0xf1); /* Front Clock Div */ | |
40 | write_reg(par, 0xca, 0x7f); /* Set Mux Ratio */ | |
41 | write_reg(par, 0x15, 0x00, 0x7f); /* Set Column Address */ | |
42 | write_reg(par, 0x75, 0x00, 0x7f); /* Set Row Address */ | |
43 | write_reg(par, 0xa1, 0x00); /* Set Display Start Line */ | |
44 | write_reg(par, 0xa2, 0x00); /* Set Display Offset */ | |
45 | write_reg(par, 0xb5, 0x00); /* Set GPIO */ | |
46 | write_reg(par, 0xab, 0x01); /* Set Function Selection */ | |
47 | write_reg(par, 0xb1, 0x32); /* Set Phase Length */ | |
48 | write_reg(par, 0xb4, 0xa0, 0xb5, 0x55); /* Set Segment Low Voltage */ | |
49 | write_reg(par, 0xbb, 0x17); /* Set Precharge Voltage */ | |
50 | write_reg(par, 0xbe, 0x05); /* Set VComH Voltage */ | |
51 | write_reg(par, 0xc1, 0xc8, 0x80, 0xc8); /* Set Contrast */ | |
52 | write_reg(par, 0xc7, 0x0f); /* Set Master Contrast */ | |
53 | write_reg(par, 0xb6, 0x01); /* Set Second Precharge Period */ | |
54 | write_reg(par, 0xa6); /* Set Display Mode Reset */ | |
55 | write_reg(par, 0xaf); /* Set Sleep Mode Display On */ | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) | |
61 | { | |
883daf0a TP |
62 | write_reg(par, 0x15, xs, xe); |
63 | write_reg(par, 0x75, ys, ye); | |
64 | write_reg(par, 0x5c); | |
65 | } | |
66 | ||
67 | static int set_var(struct fbtft_par *par) | |
68 | { | |
1c41494a | 69 | unsigned int remap; |
54a8de19 | 70 | |
883daf0a TP |
71 | if (par->fbtftops.init_display != init_display) { |
72 | /* don't risk messing up register A0h */ | |
73 | fbtft_par_dbg(DEBUG_INIT_DISPLAY, par, | |
74 | "%s: skipping since custom init_display() is used\n", | |
75 | __func__); | |
76 | return 0; | |
77 | } | |
78 | ||
79 | remap = 0x60 | (par->bgr << 2); /* Set Colour Depth */ | |
80 | ||
81 | switch (par->info->var.rotate) { | |
82 | case 0: | |
153fe946 | 83 | write_reg(par, 0xA0, remap | 0x00 | 1<<4); |
883daf0a TP |
84 | break; |
85 | case 270: | |
153fe946 | 86 | write_reg(par, 0xA0, remap | 0x03 | 1<<4); |
883daf0a TP |
87 | break; |
88 | case 180: | |
153fe946 | 89 | write_reg(par, 0xA0, remap | 0x02); |
883daf0a TP |
90 | break; |
91 | case 90: | |
153fe946 | 92 | write_reg(par, 0xA0, remap | 0x01); |
883daf0a TP |
93 | break; |
94 | } | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
99 | /* | |
ed400c96 CM |
100 | * Grayscale Lookup Table |
101 | * GS1 - GS63 | |
102 | * The driver Gamma curve contains the relative values between the entries | |
103 | * in the Lookup table. | |
104 | * | |
105 | * From datasheet: | |
106 | * 8.8 Gray Scale Decoder | |
107 | * | |
108 | * there are total 180 Gamma Settings (Setting 0 to Setting 180) | |
109 | * available for the Gray Scale table. | |
110 | * | |
111 | * The gray scale is defined in incremental way, with reference | |
112 | * to the length of previous table entry: | |
113 | * Setting of GS1 has to be >= 0 | |
114 | * Setting of GS2 has to be > Setting of GS1 +1 | |
115 | * Setting of GS3 has to be > Setting of GS2 +1 | |
116 | * : | |
117 | * Setting of GS63 has to be > Setting of GS62 +1 | |
118 | * | |
119 | */ | |
883daf0a TP |
120 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) |
121 | { | |
122 | unsigned long tmp[GAMMA_NUM * GAMMA_LEN]; | |
123 | int i, acc = 0; | |
124 | ||
883daf0a TP |
125 | for (i = 0; i < 63; i++) { |
126 | if (i > 0 && curves[i] < 2) { | |
127 | dev_err(par->info->device, | |
128 | "Illegal value in Grayscale Lookup Table at index %d. " \ | |
129 | "Must be greater than 1\n", i); | |
130 | return -EINVAL; | |
131 | } | |
132 | acc += curves[i]; | |
133 | tmp[i] = acc; | |
134 | if (acc > 180) { | |
135 | dev_err(par->info->device, | |
136 | "Illegal value(s) in Grayscale Lookup Table. " \ | |
137 | "At index=%d, the accumulated value has exceeded 180\n", i); | |
138 | return -EINVAL; | |
139 | } | |
140 | } | |
141 | ||
142 | write_reg(par, 0xB8, | |
143 | tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], | |
144 | tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14], tmp[15], | |
145 | tmp[16], tmp[17], tmp[18], tmp[19], tmp[20], tmp[21], tmp[22], tmp[23], | |
146 | tmp[24], tmp[25], tmp[26], tmp[27], tmp[28], tmp[29], tmp[30], tmp[31], | |
147 | tmp[32], tmp[33], tmp[34], tmp[35], tmp[36], tmp[37], tmp[38], tmp[39], | |
148 | tmp[40], tmp[41], tmp[42], tmp[43], tmp[44], tmp[45], tmp[46], tmp[47], | |
149 | tmp[48], tmp[49], tmp[50], tmp[51], tmp[52], tmp[53], tmp[54], tmp[55], | |
150 | tmp[56], tmp[57], tmp[58], tmp[59], tmp[60], tmp[61], tmp[62]); | |
151 | ||
152 | return 0; | |
153 | } | |
154 | ||
155 | static int blank(struct fbtft_par *par, bool on) | |
156 | { | |
157 | fbtft_par_dbg(DEBUG_BLANK, par, "%s(blank=%s)\n", | |
158 | __func__, on ? "true" : "false"); | |
159 | if (on) | |
160 | write_reg(par, 0xAE); | |
161 | else | |
162 | write_reg(par, 0xAF); | |
163 | return 0; | |
164 | } | |
165 | ||
883daf0a TP |
166 | static struct fbtft_display display = { |
167 | .regwidth = 8, | |
168 | .width = WIDTH, | |
169 | .height = HEIGHT, | |
170 | .gamma_num = GAMMA_NUM, | |
171 | .gamma_len = GAMMA_LEN, | |
172 | .gamma = DEFAULT_GAMMA, | |
173 | .fbtftops = { | |
174 | .init_display = init_display, | |
175 | .set_addr_win = set_addr_win, | |
176 | .set_var = set_var, | |
177 | .set_gamma = set_gamma, | |
178 | .blank = blank, | |
179 | }, | |
180 | }; | |
181 | ||
182 | #ifdef CONFIG_FB_BACKLIGHT | |
183 | static int update_onboard_backlight(struct backlight_device *bd) | |
184 | { | |
185 | struct fbtft_par *par = bl_get_data(bd); | |
186 | bool on; | |
187 | ||
188 | fbtft_par_dbg(DEBUG_BACKLIGHT, par, | |
189 | "%s: power=%d, fb_blank=%d\n", | |
190 | __func__, bd->props.power, bd->props.fb_blank); | |
191 | ||
192 | on = (bd->props.power == FB_BLANK_UNBLANK) | |
193 | && (bd->props.fb_blank == FB_BLANK_UNBLANK); | |
194 | /* Onboard backlight connected to GPIO0 on SSD1351, GPIO1 unused */ | |
195 | write_reg(par, 0xB5, on ? 0x03 : 0x02); | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
23801e34 MR |
200 | static const struct backlight_ops bl_ops = { |
201 | .update_status = update_onboard_backlight, | |
202 | }; | |
203 | ||
883daf0a TP |
204 | static void register_onboard_backlight(struct fbtft_par *par) |
205 | { | |
206 | struct backlight_device *bd; | |
207 | struct backlight_properties bl_props = { 0, }; | |
883daf0a | 208 | |
883daf0a TP |
209 | bl_props.type = BACKLIGHT_RAW; |
210 | bl_props.power = FB_BLANK_POWERDOWN; | |
211 | ||
212 | bd = backlight_device_register(dev_driver_string(par->info->device), | |
23801e34 | 213 | par->info->device, par, &bl_ops, &bl_props); |
883daf0a TP |
214 | if (IS_ERR(bd)) { |
215 | dev_err(par->info->device, | |
216 | "cannot register backlight device (%ld)\n", | |
217 | PTR_ERR(bd)); | |
218 | return; | |
219 | } | |
220 | par->info->bl_dev = bd; | |
221 | ||
222 | if (!par->fbtftops.unregister_backlight) | |
223 | par->fbtftops.unregister_backlight = fbtft_unregister_backlight; | |
224 | } | |
225 | #else | |
226 | static void register_onboard_backlight(struct fbtft_par *par) { }; | |
227 | #endif | |
228 | ||
883daf0a TP |
229 | FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1351", &display); |
230 | ||
231 | MODULE_ALIAS("spi:" DRVNAME); | |
232 | MODULE_ALIAS("platform:" DRVNAME); | |
233 | MODULE_ALIAS("spi:ssd1351"); | |
234 | MODULE_ALIAS("platform:ssd1351"); | |
235 | ||
236 | MODULE_DESCRIPTION("SSD1351 OLED Driver"); | |
237 | MODULE_AUTHOR("James Davies"); | |
238 | MODULE_LICENSE("GPL"); |