Commit | Line | Data |
---|---|---|
5795354f AM |
1 | /* |
2 | * FB driver for the SSD1305 OLED Controller | |
3 | * | |
4 | * based on SSD1306 driver by Noralf Tronnes | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/module.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/gpio.h> | |
21 | #include <linux/delay.h> | |
22 | ||
23 | #include "fbtft.h" | |
24 | ||
25 | #define DRVNAME "fb_ssd1305" | |
26 | ||
27 | #define WIDTH 128 | |
28 | #define HEIGHT 64 | |
29 | ||
30 | /* | |
31 | * write_reg() caveat: | |
32 | * | |
33 | * This doesn't work because D/C has to be LOW for both values: | |
34 | * write_reg(par, val1, val2); | |
35 | * | |
36 | * Do it like this: | |
37 | * write_reg(par, val1); | |
38 | * write_reg(par, val2); | |
39 | */ | |
40 | ||
41 | /* Init sequence taken from the Adafruit SSD1306 Arduino library */ | |
42 | static int init_display(struct fbtft_par *par) | |
43 | { | |
44 | par->fbtftops.reset(par); | |
45 | ||
46 | if (par->gamma.curves[0] == 0) { | |
47 | mutex_lock(&par->gamma.lock); | |
48 | if (par->info->var.yres == 64) | |
49 | par->gamma.curves[0] = 0xCF; | |
50 | else | |
51 | par->gamma.curves[0] = 0x8F; | |
52 | mutex_unlock(&par->gamma.lock); | |
53 | } | |
54 | ||
55 | /* Set Display OFF */ | |
56 | write_reg(par, 0xAE); | |
57 | ||
58 | /* Set Display Clock Divide Ratio/ Oscillator Frequency */ | |
59 | write_reg(par, 0xD5); | |
60 | write_reg(par, 0x80); | |
61 | ||
62 | /* Set Multiplex Ratio */ | |
63 | write_reg(par, 0xA8); | |
64 | if (par->info->var.yres == 64) | |
65 | write_reg(par, 0x3F); | |
66 | else | |
67 | write_reg(par, 0x1F); | |
68 | ||
69 | /* Set Display Offset */ | |
70 | write_reg(par, 0xD3); | |
71 | write_reg(par, 0x0); | |
72 | ||
73 | /* Set Display Start Line */ | |
74 | write_reg(par, 0x40 | 0x0); | |
75 | ||
76 | /* Charge Pump Setting */ | |
77 | write_reg(par, 0x8D); | |
78 | /* A[2] = 1b, Enable charge pump during display on */ | |
79 | write_reg(par, 0x14); | |
80 | ||
81 | /* Set Memory Addressing Mode */ | |
82 | write_reg(par, 0x20); | |
83 | /* Vertical addressing mode */ | |
84 | write_reg(par, 0x01); | |
85 | ||
86 | /* | |
87 | * Set Segment Re-map | |
88 | * column address 127 is mapped to SEG0 | |
89 | */ | |
90 | write_reg(par, 0xA0 | ((par->info->var.rotate == 180) ? 0x0 : 0x1)); | |
91 | ||
92 | /* | |
93 | * Set COM Output Scan Direction | |
94 | * remapped mode. Scan from COM[N-1] to COM0 | |
95 | */ | |
96 | write_reg(par, ((par->info->var.rotate == 180) ? 0xC8 : 0xC0)); | |
97 | ||
98 | /* Set COM Pins Hardware Configuration */ | |
99 | write_reg(par, 0xDA); | |
100 | if (par->info->var.yres == 64) { | |
101 | /* A[4]=1b, Alternative COM pin configuration */ | |
102 | write_reg(par, 0x12); | |
103 | } else { | |
104 | /* A[4]=0b, Sequential COM pin configuration */ | |
105 | write_reg(par, 0x02); | |
106 | } | |
107 | ||
108 | /* Set Pre-charge Period */ | |
109 | write_reg(par, 0xD9); | |
110 | write_reg(par, 0xF1); | |
111 | ||
112 | /* | |
113 | * Entire Display ON | |
114 | * Resume to RAM content display. Output follows RAM content | |
115 | */ | |
116 | write_reg(par, 0xA4); | |
117 | ||
118 | /* | |
119 | * Set Normal Display | |
120 | * 0 in RAM: OFF in display panel | |
121 | * 1 in RAM: ON in display panel | |
122 | */ | |
123 | write_reg(par, 0xA6); | |
124 | ||
125 | /* Set Display ON */ | |
126 | write_reg(par, 0xAF); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static void set_addr_win(struct fbtft_par *par, int xs, int ys, int xe, int ye) | |
132 | { | |
133 | /* Set Lower Column Start Address for Page Addressing Mode */ | |
134 | write_reg(par, 0x00 | ((par->info->var.rotate == 180) ? 0x0 : 0x4)); | |
135 | /* Set Higher Column Start Address for Page Addressing Mode */ | |
136 | write_reg(par, 0x10 | 0x0); | |
137 | /* Set Display Start Line */ | |
138 | write_reg(par, 0x40 | 0x0); | |
139 | } | |
140 | ||
141 | static int blank(struct fbtft_par *par, bool on) | |
142 | { | |
143 | if (on) | |
144 | write_reg(par, 0xAE); | |
145 | else | |
146 | write_reg(par, 0xAF); | |
147 | return 0; | |
148 | } | |
149 | ||
150 | /* Gamma is used to control Contrast */ | |
151 | static int set_gamma(struct fbtft_par *par, unsigned long *curves) | |
152 | { | |
153 | curves[0] &= 0xFF; | |
154 | /* Set Contrast Control for BANK0 */ | |
155 | write_reg(par, 0x81); | |
156 | write_reg(par, curves[0]); | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | static int write_vmem(struct fbtft_par *par, size_t offset, size_t len) | |
162 | { | |
163 | u16 *vmem16 = (u16 *)par->info->screen_buffer; | |
164 | u8 *buf = par->txbuf.buf; | |
165 | int x, y, i; | |
166 | int ret; | |
167 | ||
168 | for (x = 0; x < par->info->var.xres; x++) { | |
169 | for (y = 0; y < par->info->var.yres / 8; y++) { | |
170 | *buf = 0x00; | |
171 | for (i = 0; i < 8; i++) | |
172 | *buf |= (vmem16[(y * 8 + i) * | |
173 | par->info->var.xres + x] ? | |
174 | 1 : 0) << i; | |
175 | buf++; | |
176 | } | |
177 | } | |
178 | ||
179 | /* Write data */ | |
180 | gpio_set_value(par->gpio.dc, 1); | |
181 | ret = par->fbtftops.write(par, par->txbuf.buf, | |
182 | par->info->var.xres * par->info->var.yres / | |
183 | 8); | |
184 | if (ret < 0) | |
185 | dev_err(par->info->device, "write failed and returned: %d\n", | |
186 | ret); | |
187 | return ret; | |
188 | } | |
189 | ||
190 | static struct fbtft_display display = { | |
191 | .regwidth = 8, | |
192 | .width = WIDTH, | |
193 | .height = HEIGHT, | |
194 | .txbuflen = WIDTH * HEIGHT / 8, | |
195 | .gamma_num = 1, | |
196 | .gamma_len = 1, | |
197 | .gamma = "00", | |
198 | .fbtftops = { | |
199 | .write_vmem = write_vmem, | |
200 | .init_display = init_display, | |
201 | .set_addr_win = set_addr_win, | |
202 | .blank = blank, | |
203 | .set_gamma = set_gamma, | |
204 | }, | |
205 | }; | |
206 | ||
207 | FBTFT_REGISTER_DRIVER(DRVNAME, "solomon,ssd1305", &display); | |
208 | ||
209 | MODULE_ALIAS("spi:" DRVNAME); | |
210 | MODULE_ALIAS("platform:" DRVNAME); | |
211 | MODULE_ALIAS("spi:ssd1305"); | |
212 | MODULE_ALIAS("platform:ssd1305"); | |
213 | ||
214 | MODULE_DESCRIPTION("SSD1305 OLED Driver"); | |
215 | MODULE_AUTHOR("Alexey Mednyy"); | |
216 | MODULE_LICENSE("GPL"); |