diff options
Diffstat (limited to 'drivers/auxdisplay/cfag12864b.c')
-rw-r--r-- | drivers/auxdisplay/cfag12864b.c | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c new file mode 100644 index 000000000000..889583dfc1a6 --- /dev/null +++ b/drivers/auxdisplay/cfag12864b.c | |||
@@ -0,0 +1,383 @@ | |||
1 | /* | ||
2 | * Filename: cfag12864b.c | ||
3 | * Version: 0.1.0 | ||
4 | * Description: cfag12864b LCD driver | ||
5 | * License: GPLv2 | ||
6 | * Depends: ks0108 | ||
7 | * | ||
8 | * Author: Copyright (C) Miguel Ojeda Sandonis <maxextreme@gmail.com> | ||
9 | * Date: 2006-10-31 | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #include <linux/init.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/fs.h> | ||
30 | #include <linux/cdev.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/device.h> | ||
33 | #include <linux/jiffies.h> | ||
34 | #include <linux/mutex.h> | ||
35 | #include <linux/uaccess.h> | ||
36 | #include <linux/vmalloc.h> | ||
37 | #include <linux/workqueue.h> | ||
38 | #include <linux/ks0108.h> | ||
39 | #include <linux/cfag12864b.h> | ||
40 | |||
41 | |||
42 | #define CFAG12864B_NAME "cfag12864b" | ||
43 | |||
44 | /* | ||
45 | * Module Parameters | ||
46 | */ | ||
47 | |||
48 | static unsigned int cfag12864b_rate = CONFIG_CFAG12864B_RATE; | ||
49 | module_param(cfag12864b_rate, uint, S_IRUGO); | ||
50 | MODULE_PARM_DESC(cfag12864b_rate, | ||
51 | "Refresh rate (hertzs)"); | ||
52 | |||
53 | unsigned int cfag12864b_getrate(void) | ||
54 | { | ||
55 | return cfag12864b_rate; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * cfag12864b Commands | ||
60 | * | ||
61 | * E = Enable signal | ||
62 | * Everytime E switch from low to high, | ||
63 | * cfag12864b/ks0108 reads the command/data. | ||
64 | * | ||
65 | * CS1 = First ks0108controller. | ||
66 | * If high, the first ks0108 controller receives commands/data. | ||
67 | * | ||
68 | * CS2 = Second ks0108 controller | ||
69 | * If high, the second ks0108 controller receives commands/data. | ||
70 | * | ||
71 | * DI = Data/Instruction | ||
72 | * If low, cfag12864b will expect commands. | ||
73 | * If high, cfag12864b will expect data. | ||
74 | * | ||
75 | */ | ||
76 | |||
77 | #define bit(n) (((unsigned char)1)<<(n)) | ||
78 | |||
79 | #define CFAG12864B_BIT_E (0) | ||
80 | #define CFAG12864B_BIT_CS1 (2) | ||
81 | #define CFAG12864B_BIT_CS2 (1) | ||
82 | #define CFAG12864B_BIT_DI (3) | ||
83 | |||
84 | static unsigned char cfag12864b_state; | ||
85 | |||
86 | static void cfag12864b_set(void) | ||
87 | { | ||
88 | ks0108_writecontrol(cfag12864b_state); | ||
89 | } | ||
90 | |||
91 | static void cfag12864b_setbit(unsigned char state, unsigned char n) | ||
92 | { | ||
93 | if (state) | ||
94 | cfag12864b_state |= bit(n); | ||
95 | else | ||
96 | cfag12864b_state &= ~bit(n); | ||
97 | } | ||
98 | |||
99 | static void cfag12864b_e(unsigned char state) | ||
100 | { | ||
101 | cfag12864b_setbit(state, CFAG12864B_BIT_E); | ||
102 | cfag12864b_set(); | ||
103 | } | ||
104 | |||
105 | static void cfag12864b_cs1(unsigned char state) | ||
106 | { | ||
107 | cfag12864b_setbit(state, CFAG12864B_BIT_CS1); | ||
108 | } | ||
109 | |||
110 | static void cfag12864b_cs2(unsigned char state) | ||
111 | { | ||
112 | cfag12864b_setbit(state, CFAG12864B_BIT_CS2); | ||
113 | } | ||
114 | |||
115 | static void cfag12864b_di(unsigned char state) | ||
116 | { | ||
117 | cfag12864b_setbit(state, CFAG12864B_BIT_DI); | ||
118 | } | ||
119 | |||
120 | static void cfag12864b_setcontrollers(unsigned char first, | ||
121 | unsigned char second) | ||
122 | { | ||
123 | if (first) | ||
124 | cfag12864b_cs1(0); | ||
125 | else | ||
126 | cfag12864b_cs1(1); | ||
127 | |||
128 | if (second) | ||
129 | cfag12864b_cs2(0); | ||
130 | else | ||
131 | cfag12864b_cs2(1); | ||
132 | } | ||
133 | |||
134 | static void cfag12864b_controller(unsigned char which) | ||
135 | { | ||
136 | if (which == 0) | ||
137 | cfag12864b_setcontrollers(1, 0); | ||
138 | else if (which == 1) | ||
139 | cfag12864b_setcontrollers(0, 1); | ||
140 | } | ||
141 | |||
142 | static void cfag12864b_displaystate(unsigned char state) | ||
143 | { | ||
144 | cfag12864b_di(0); | ||
145 | cfag12864b_e(1); | ||
146 | ks0108_displaystate(state); | ||
147 | cfag12864b_e(0); | ||
148 | } | ||
149 | |||
150 | static void cfag12864b_address(unsigned char address) | ||
151 | { | ||
152 | cfag12864b_di(0); | ||
153 | cfag12864b_e(1); | ||
154 | ks0108_address(address); | ||
155 | cfag12864b_e(0); | ||
156 | } | ||
157 | |||
158 | static void cfag12864b_page(unsigned char page) | ||
159 | { | ||
160 | cfag12864b_di(0); | ||
161 | cfag12864b_e(1); | ||
162 | ks0108_page(page); | ||
163 | cfag12864b_e(0); | ||
164 | } | ||
165 | |||
166 | static void cfag12864b_startline(unsigned char startline) | ||
167 | { | ||
168 | cfag12864b_di(0); | ||
169 | cfag12864b_e(1); | ||
170 | ks0108_startline(startline); | ||
171 | cfag12864b_e(0); | ||
172 | } | ||
173 | |||
174 | static void cfag12864b_writebyte(unsigned char byte) | ||
175 | { | ||
176 | cfag12864b_di(1); | ||
177 | cfag12864b_e(1); | ||
178 | ks0108_writedata(byte); | ||
179 | cfag12864b_e(0); | ||
180 | } | ||
181 | |||
182 | static void cfag12864b_nop(void) | ||
183 | { | ||
184 | cfag12864b_startline(0); | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * cfag12864b Internal Commands | ||
189 | */ | ||
190 | |||
191 | static void cfag12864b_on(void) | ||
192 | { | ||
193 | cfag12864b_setcontrollers(1, 1); | ||
194 | cfag12864b_displaystate(1); | ||
195 | } | ||
196 | |||
197 | static void cfag12864b_off(void) | ||
198 | { | ||
199 | cfag12864b_setcontrollers(1, 1); | ||
200 | cfag12864b_displaystate(0); | ||
201 | } | ||
202 | |||
203 | static void cfag12864b_clear(void) | ||
204 | { | ||
205 | unsigned char i, j; | ||
206 | |||
207 | cfag12864b_setcontrollers(1, 1); | ||
208 | for (i = 0; i < CFAG12864B_PAGES; i++) { | ||
209 | cfag12864b_page(i); | ||
210 | cfag12864b_address(0); | ||
211 | for (j = 0; j < CFAG12864B_ADDRESSES; j++) | ||
212 | cfag12864b_writebyte(0); | ||
213 | } | ||
214 | } | ||
215 | |||
216 | /* | ||
217 | * Update work | ||
218 | */ | ||
219 | |||
220 | unsigned char *cfag12864b_buffer; | ||
221 | static unsigned char *cfag12864b_cache; | ||
222 | static DEFINE_MUTEX(cfag12864b_mutex); | ||
223 | static unsigned char cfag12864b_updating; | ||
224 | static void cfag12864b_update(struct work_struct *delayed_work); | ||
225 | static struct workqueue_struct *cfag12864b_workqueue; | ||
226 | static DECLARE_DELAYED_WORK(cfag12864b_work, cfag12864b_update); | ||
227 | |||
228 | static void cfag12864b_queue(void) | ||
229 | { | ||
230 | queue_delayed_work(cfag12864b_workqueue, &cfag12864b_work, | ||
231 | HZ / cfag12864b_rate); | ||
232 | } | ||
233 | |||
234 | unsigned char cfag12864b_enable(void) | ||
235 | { | ||
236 | unsigned char ret; | ||
237 | |||
238 | mutex_lock(&cfag12864b_mutex); | ||
239 | |||
240 | if (!cfag12864b_updating) { | ||
241 | cfag12864b_updating = 1; | ||
242 | cfag12864b_queue(); | ||
243 | ret = 0; | ||
244 | } else | ||
245 | ret = 1; | ||
246 | |||
247 | mutex_unlock(&cfag12864b_mutex); | ||
248 | |||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | void cfag12864b_disable(void) | ||
253 | { | ||
254 | mutex_lock(&cfag12864b_mutex); | ||
255 | |||
256 | if (cfag12864b_updating) { | ||
257 | cfag12864b_updating = 0; | ||
258 | cancel_delayed_work(&cfag12864b_work); | ||
259 | flush_workqueue(cfag12864b_workqueue); | ||
260 | } | ||
261 | |||
262 | mutex_unlock(&cfag12864b_mutex); | ||
263 | } | ||
264 | |||
265 | unsigned char cfag12864b_isenabled(void) | ||
266 | { | ||
267 | return cfag12864b_updating; | ||
268 | } | ||
269 | |||
270 | static void cfag12864b_update(struct work_struct *work) | ||
271 | { | ||
272 | unsigned char c; | ||
273 | unsigned short i, j, k, b; | ||
274 | |||
275 | if (memcmp(cfag12864b_cache, cfag12864b_buffer, CFAG12864B_SIZE)) { | ||
276 | for (i = 0; i < CFAG12864B_CONTROLLERS; i++) { | ||
277 | cfag12864b_controller(i); | ||
278 | cfag12864b_nop(); | ||
279 | for (j = 0; j < CFAG12864B_PAGES; j++) { | ||
280 | cfag12864b_page(j); | ||
281 | cfag12864b_nop(); | ||
282 | cfag12864b_address(0); | ||
283 | cfag12864b_nop(); | ||
284 | for (k = 0; k < CFAG12864B_ADDRESSES; k++) { | ||
285 | for (c = 0, b = 0; b < 8; b++) | ||
286 | if (cfag12864b_buffer | ||
287 | [i * CFAG12864B_ADDRESSES / 8 | ||
288 | + k / 8 + (j * 8 + b) * | ||
289 | CFAG12864B_WIDTH / 8] | ||
290 | & bit(k % 8)) | ||
291 | c |= bit(b); | ||
292 | cfag12864b_writebyte(c); | ||
293 | } | ||
294 | } | ||
295 | } | ||
296 | |||
297 | memcpy(cfag12864b_cache, cfag12864b_buffer, CFAG12864B_SIZE); | ||
298 | } | ||
299 | |||
300 | if (cfag12864b_updating) | ||
301 | cfag12864b_queue(); | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * cfag12864b Exported Symbols | ||
306 | */ | ||
307 | |||
308 | EXPORT_SYMBOL_GPL(cfag12864b_buffer); | ||
309 | EXPORT_SYMBOL_GPL(cfag12864b_getrate); | ||
310 | EXPORT_SYMBOL_GPL(cfag12864b_enable); | ||
311 | EXPORT_SYMBOL_GPL(cfag12864b_disable); | ||
312 | EXPORT_SYMBOL_GPL(cfag12864b_isenabled); | ||
313 | |||
314 | /* | ||
315 | * Module Init & Exit | ||
316 | */ | ||
317 | |||
318 | static int __init cfag12864b_init(void) | ||
319 | { | ||
320 | int ret = -EINVAL; | ||
321 | |||
322 | if (PAGE_SIZE < CFAG12864B_SIZE) { | ||
323 | printk(KERN_ERR CFAG12864B_NAME ": ERROR: " | ||
324 | "page size (%i) < cfag12864b size (%i)\n", | ||
325 | (unsigned int)PAGE_SIZE, CFAG12864B_SIZE); | ||
326 | ret = -ENOMEM; | ||
327 | goto none; | ||
328 | } | ||
329 | |||
330 | cfag12864b_buffer = (unsigned char *) __get_free_page(GFP_KERNEL); | ||
331 | if (cfag12864b_buffer == NULL) { | ||
332 | printk(KERN_ERR CFAG12864B_NAME ": ERROR: " | ||
333 | "can't get a free page\n"); | ||
334 | ret = -ENOMEM; | ||
335 | goto none; | ||
336 | } | ||
337 | |||
338 | cfag12864b_cache = kmalloc(sizeof(unsigned char) * | ||
339 | CFAG12864B_SIZE, GFP_KERNEL); | ||
340 | if (cfag12864b_buffer == NULL) { | ||
341 | printk(KERN_ERR CFAG12864B_NAME ": ERROR: " | ||
342 | "can't alloc cache buffer (%i bytes)\n", | ||
343 | CFAG12864B_SIZE); | ||
344 | ret = -ENOMEM; | ||
345 | goto bufferalloced; | ||
346 | } | ||
347 | |||
348 | cfag12864b_workqueue = create_singlethread_workqueue(CFAG12864B_NAME); | ||
349 | if (cfag12864b_workqueue == NULL) | ||
350 | goto cachealloced; | ||
351 | |||
352 | memset(cfag12864b_buffer, 0, CFAG12864B_SIZE); | ||
353 | |||
354 | cfag12864b_clear(); | ||
355 | cfag12864b_on(); | ||
356 | |||
357 | return 0; | ||
358 | |||
359 | cachealloced: | ||
360 | kfree(cfag12864b_cache); | ||
361 | |||
362 | bufferalloced: | ||
363 | free_page((unsigned long) cfag12864b_buffer); | ||
364 | |||
365 | none: | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | static void __exit cfag12864b_exit(void) | ||
370 | { | ||
371 | cfag12864b_disable(); | ||
372 | cfag12864b_off(); | ||
373 | destroy_workqueue(cfag12864b_workqueue); | ||
374 | kfree(cfag12864b_cache); | ||
375 | free_page((unsigned long) cfag12864b_buffer); | ||
376 | } | ||
377 | |||
378 | module_init(cfag12864b_init); | ||
379 | module_exit(cfag12864b_exit); | ||
380 | |||
381 | MODULE_LICENSE("GPL v2"); | ||
382 | MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>"); | ||
383 | MODULE_DESCRIPTION("cfag12864b LCD driver"); | ||