aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/auxdisplay
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/auxdisplay')
-rw-r--r--drivers/auxdisplay/Kconfig109
-rw-r--r--drivers/auxdisplay/Makefile6
-rw-r--r--drivers/auxdisplay/cfag12864b.c383
-rw-r--r--drivers/auxdisplay/cfag12864bfb.c180
-rw-r--r--drivers/auxdisplay/ks0108.c166
5 files changed, 844 insertions, 0 deletions
diff --git a/drivers/auxdisplay/Kconfig b/drivers/auxdisplay/Kconfig
new file mode 100644
index 000000000000..0300e7f54cc4
--- /dev/null
+++ b/drivers/auxdisplay/Kconfig
@@ -0,0 +1,109 @@
1#
2# For a description of the syntax of this configuration file,
3# see Documentation/kbuild/kconfig-language.txt.
4#
5# Auxiliary display drivers configuration.
6#
7
8menu "Auxiliary Display support"
9
10config KS0108
11 tristate "KS0108 LCD Controller"
12 depends on PARPORT_PC
13 default n
14 ---help---
15 If you have a LCD controlled by one or more KS0108
16 controllers, say Y. You will need also another more specific
17 driver for your LCD.
18
19 Depends on Parallel Port support. If you say Y at
20 parport, you will be able to compile this as a module (M)
21 and built-in as well (Y).
22
23 To compile this as a module, choose M here:
24 the module will be called ks0108.
25
26 If unsure, say N.
27
28config KS0108_PORT
29 hex "Parallel port where the LCD is connected"
30 depends on KS0108
31 default 0x378
32 ---help---
33 The address of the parallel port where the LCD is connected.
34
35 The first standard parallel port address is 0x378.
36 The second standard parallel port address is 0x278.
37 The third standard parallel port address is 0x3BC.
38
39 You can specify a different address if you need.
40
41 If you don't know what I'm talking about, load the parport module,
42 and execute "dmesg" or "cat /proc/ioports". You can see there how
43 many parallel ports are present and which address each one has.
44
45 Usually you only need to use 0x378.
46
47 If you compile this as a module, you can still override this
48 using the module parameters.
49
50config KS0108_DELAY
51 int "Delay between each control writing (microseconds)"
52 depends on KS0108
53 default "2"
54 ---help---
55 Amount of time the ks0108 should wait between each control write
56 to the parallel port.
57
58 If your driver seems to miss random writings, increment this.
59
60 If you don't know what I'm talking about, ignore it.
61
62 If you compile this as a module, you can still override this
63 value using the module parameters.
64
65config CFAG12864B
66 tristate "CFAG12864B LCD"
67 depends on X86
68 depends on FB
69 depends on KS0108
70 default n
71 ---help---
72 If you have a Crystalfontz 128x64 2-color LCD, cfag12864b Series,
73 say Y. You also need the ks0108 LCD Controller driver.
74
75 For help about how to wire your LCD to the parallel port,
76 check Documentation/auxdisplay/cfag12864b
77
78 Depends on the x86 arch and the framebuffer support.
79
80 The LCD framebuffer driver can be attached to a console.
81 It will work fine. However, you can't attach it to the fbdev driver
82 of the xorg server.
83
84 To compile this as a module, choose M here:
85 the modules will be called cfag12864b and cfag12864bfb.
86
87 If unsure, say N.
88
89config CFAG12864B_RATE
90 int "Refresh rate (hertz)"
91 depends on CFAG12864B
92 default "20"
93 ---help---
94 Refresh rate of the LCD.
95
96 As the LCD is not memory mapped, the driver has to make the work by
97 software. This means you should be careful setting this value higher.
98 If your CPUs are really slow or you feel the system is slowed down,
99 decrease the value.
100
101 Be careful modifying this value to a very high value:
102 You can freeze the computer, or the LCD maybe can't draw as fast as you
103 are requesting.
104
105 If you don't know what I'm talking about, ignore it.
106
107 If you compile this as a module, you can still override this
108 value using the module parameters.
109endmenu
diff --git a/drivers/auxdisplay/Makefile b/drivers/auxdisplay/Makefile
new file mode 100644
index 000000000000..8a8936a468b9
--- /dev/null
+++ b/drivers/auxdisplay/Makefile
@@ -0,0 +1,6 @@
1#
2# Makefile for the kernel auxiliary displays device drivers.
3#
4
5obj-$(CONFIG_KS0108) += ks0108.o
6obj-$(CONFIG_CFAG12864B) += cfag12864b.o cfag12864bfb.o
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
48static unsigned int cfag12864b_rate = CONFIG_CFAG12864B_RATE;
49module_param(cfag12864b_rate, uint, S_IRUGO);
50MODULE_PARM_DESC(cfag12864b_rate,
51 "Refresh rate (hertzs)");
52
53unsigned 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
84static unsigned char cfag12864b_state;
85
86static void cfag12864b_set(void)
87{
88 ks0108_writecontrol(cfag12864b_state);
89}
90
91static 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
99static void cfag12864b_e(unsigned char state)
100{
101 cfag12864b_setbit(state, CFAG12864B_BIT_E);
102 cfag12864b_set();
103}
104
105static void cfag12864b_cs1(unsigned char state)
106{
107 cfag12864b_setbit(state, CFAG12864B_BIT_CS1);
108}
109
110static void cfag12864b_cs2(unsigned char state)
111{
112 cfag12864b_setbit(state, CFAG12864B_BIT_CS2);
113}
114
115static void cfag12864b_di(unsigned char state)
116{
117 cfag12864b_setbit(state, CFAG12864B_BIT_DI);
118}
119
120static 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
134static 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
142static 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
150static 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
158static 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
166static 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
174static 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
182static void cfag12864b_nop(void)
183{
184 cfag12864b_startline(0);
185}
186
187/*
188 * cfag12864b Internal Commands
189 */
190
191static void cfag12864b_on(void)
192{
193 cfag12864b_setcontrollers(1, 1);
194 cfag12864b_displaystate(1);
195}
196
197static void cfag12864b_off(void)
198{
199 cfag12864b_setcontrollers(1, 1);
200 cfag12864b_displaystate(0);
201}
202
203static 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
220unsigned char *cfag12864b_buffer;
221static unsigned char *cfag12864b_cache;
222static DEFINE_MUTEX(cfag12864b_mutex);
223static unsigned char cfag12864b_updating;
224static void cfag12864b_update(struct work_struct *delayed_work);
225static struct workqueue_struct *cfag12864b_workqueue;
226static DECLARE_DELAYED_WORK(cfag12864b_work, cfag12864b_update);
227
228static void cfag12864b_queue(void)
229{
230 queue_delayed_work(cfag12864b_workqueue, &cfag12864b_work,
231 HZ / cfag12864b_rate);
232}
233
234unsigned 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
252void 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
265unsigned char cfag12864b_isenabled(void)
266{
267 return cfag12864b_updating;
268}
269
270static 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
308EXPORT_SYMBOL_GPL(cfag12864b_buffer);
309EXPORT_SYMBOL_GPL(cfag12864b_getrate);
310EXPORT_SYMBOL_GPL(cfag12864b_enable);
311EXPORT_SYMBOL_GPL(cfag12864b_disable);
312EXPORT_SYMBOL_GPL(cfag12864b_isenabled);
313
314/*
315 * Module Init & Exit
316 */
317
318static 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
359cachealloced:
360 kfree(cfag12864b_cache);
361
362bufferalloced:
363 free_page((unsigned long) cfag12864b_buffer);
364
365none:
366 return ret;
367}
368
369static 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
378module_init(cfag12864b_init);
379module_exit(cfag12864b_exit);
380
381MODULE_LICENSE("GPL v2");
382MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>");
383MODULE_DESCRIPTION("cfag12864b LCD driver");
diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c
new file mode 100644
index 000000000000..94765e78315f
--- /dev/null
+++ b/drivers/auxdisplay/cfag12864bfb.c
@@ -0,0 +1,180 @@
1/*
2 * Filename: cfag12864bfb.c
3 * Version: 0.1.0
4 * Description: cfag12864b LCD framebuffer driver
5 * License: GPLv2
6 * Depends: cfag12864b
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/delay.h>
30#include <linux/errno.h>
31#include <linux/fb.h>
32#include <linux/mm.h>
33#include <linux/platform_device.h>
34#include <linux/slab.h>
35#include <linux/string.h>
36#include <linux/uaccess.h>
37#include <linux/cfag12864b.h>
38
39#define CFAG12864BFB_NAME "cfag12864bfb"
40
41static struct fb_fix_screeninfo cfag12864bfb_fix __initdata = {
42 .id = "cfag12864b",
43 .type = FB_TYPE_PACKED_PIXELS,
44 .visual = FB_VISUAL_MONO10,
45 .xpanstep = 0,
46 .ypanstep = 0,
47 .ywrapstep = 0,
48 .line_length = CFAG12864B_WIDTH / 8,
49 .accel = FB_ACCEL_NONE,
50};
51
52static struct fb_var_screeninfo cfag12864bfb_var __initdata = {
53 .xres = CFAG12864B_WIDTH,
54 .yres = CFAG12864B_HEIGHT,
55 .xres_virtual = CFAG12864B_WIDTH,
56 .yres_virtual = CFAG12864B_HEIGHT,
57 .bits_per_pixel = 1,
58 .red = { 0, 1, 0 },
59 .green = { 0, 1, 0 },
60 .blue = { 0, 1, 0 },
61 .left_margin = 0,
62 .right_margin = 0,
63 .upper_margin = 0,
64 .lower_margin = 0,
65 .vmode = FB_VMODE_NONINTERLACED,
66};
67
68static int cfag12864bfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
69{
70 return vm_insert_page(vma, vma->vm_start,
71 virt_to_page(cfag12864b_buffer));
72}
73
74static struct fb_ops cfag12864bfb_ops = {
75 .owner = THIS_MODULE,
76 .fb_fillrect = cfb_fillrect,
77 .fb_copyarea = cfb_copyarea,
78 .fb_imageblit = cfb_imageblit,
79 .fb_mmap = cfag12864bfb_mmap,
80};
81
82static int __init cfag12864bfb_probe(struct platform_device *device)
83{
84 int ret = -EINVAL;
85 struct fb_info *info = framebuffer_alloc(0, &device->dev);
86
87 if (!info)
88 goto none;
89
90 info->screen_base = (char __iomem *) cfag12864b_buffer;
91 info->screen_size = CFAG12864B_SIZE;
92 info->fbops = &cfag12864bfb_ops;
93 info->fix = cfag12864bfb_fix;
94 info->var = cfag12864bfb_var;
95 info->pseudo_palette = NULL;
96 info->par = NULL;
97 info->flags = FBINFO_FLAG_DEFAULT;
98
99 if (register_framebuffer(info) < 0)
100 goto fballoced;
101
102 platform_set_drvdata(device, info);
103
104 printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
105 info->fix.id);
106
107 return 0;
108
109fballoced:
110 framebuffer_release(info);
111
112none:
113 return ret;
114}
115
116static int cfag12864bfb_remove(struct platform_device *device)
117{
118 struct fb_info *info = platform_get_drvdata(device);
119
120 if (info) {
121 unregister_framebuffer(info);
122 framebuffer_release(info);
123 }
124
125 return 0;
126}
127
128static struct platform_driver cfag12864bfb_driver = {
129 .probe = cfag12864bfb_probe,
130 .remove = cfag12864bfb_remove,
131 .driver = {
132 .name = CFAG12864BFB_NAME,
133 },
134};
135
136static struct platform_device *cfag12864bfb_device;
137
138static int __init cfag12864bfb_init(void)
139{
140 int ret;
141
142 if (cfag12864b_enable()) {
143 printk(KERN_ERR CFAG12864BFB_NAME ": ERROR: "
144 "can't enable cfag12864b refreshing (being used)\n");
145 return -ENODEV;
146 }
147
148 ret = platform_driver_register(&cfag12864bfb_driver);
149
150 if (!ret) {
151 cfag12864bfb_device =
152 platform_device_alloc(CFAG12864BFB_NAME, 0);
153
154 if (cfag12864bfb_device)
155 ret = platform_device_add(cfag12864bfb_device);
156 else
157 ret = -ENOMEM;
158
159 if (ret) {
160 platform_device_put(cfag12864bfb_device);
161 platform_driver_unregister(&cfag12864bfb_driver);
162 }
163 }
164
165 return ret;
166}
167
168static void __exit cfag12864bfb_exit(void)
169{
170 platform_device_unregister(cfag12864bfb_device);
171 platform_driver_unregister(&cfag12864bfb_driver);
172 cfag12864b_disable();
173}
174
175module_init(cfag12864bfb_init);
176module_exit(cfag12864bfb_exit);
177
178MODULE_LICENSE("GPL v2");
179MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>");
180MODULE_DESCRIPTION("cfag12864b LCD framebuffer driver");
diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c
new file mode 100644
index 000000000000..a637575b9106
--- /dev/null
+++ b/drivers/auxdisplay/ks0108.c
@@ -0,0 +1,166 @@
1/*
2 * Filename: ks0108.c
3 * Version: 0.1.0
4 * Description: ks0108 LCD Controller driver
5 * License: GPLv2
6 * Depends: parport
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/delay.h>
30#include <linux/fs.h>
31#include <linux/io.h>
32#include <linux/parport.h>
33#include <linux/uaccess.h>
34#include <linux/ks0108.h>
35
36#define KS0108_NAME "ks0108"
37
38/*
39 * Module Parameters
40 */
41
42static unsigned int ks0108_port = CONFIG_KS0108_PORT;
43module_param(ks0108_port, uint, S_IRUGO);
44MODULE_PARM_DESC(ks0108_port, "Parallel port where the LCD is connected");
45
46static unsigned int ks0108_delay = CONFIG_KS0108_DELAY;
47module_param(ks0108_delay, uint, S_IRUGO);
48MODULE_PARM_DESC(ks0108_delay, "Delay between each control writing (microseconds)");
49
50/*
51 * Device
52 */
53
54static struct parport *ks0108_parport;
55static struct pardevice *ks0108_pardevice;
56
57/*
58 * ks0108 Exported Commands (don't lock)
59 *
60 * You _should_ lock in the top driver: This functions _should not_
61 * get race conditions in any way. Locking for each byte here would be
62 * so slow and useless.
63 *
64 * There are not bit definitions because they are not flags,
65 * just arbitrary combinations defined by the documentation for each
66 * function in the ks0108 LCD controller. If you want to know what means
67 * a specific combination, look at the function's name.
68 *
69 * The ks0108_writecontrol bits need to be reverted ^(0,1,3) because
70 * the parallel port also revert them using a "not" logic gate.
71 */
72
73#define bit(n) (((unsigned char)1)<<(n))
74
75void ks0108_writedata(unsigned char byte)
76{
77 parport_write_data(ks0108_parport, byte);
78}
79
80void ks0108_writecontrol(unsigned char byte)
81{
82 udelay(ks0108_delay);
83 parport_write_control(ks0108_parport, byte ^ (bit(0) | bit(1) | bit(3)));
84}
85
86void ks0108_displaystate(unsigned char state)
87{
88 ks0108_writedata((state ? bit(0) : 0) | bit(1) | bit(2) | bit(3) | bit(4) | bit(5));
89}
90
91void ks0108_startline(unsigned char startline)
92{
93 ks0108_writedata(min(startline,(unsigned char)63) | bit(6) | bit(7));
94}
95
96void ks0108_address(unsigned char address)
97{
98 ks0108_writedata(min(address,(unsigned char)63) | bit(6));
99}
100
101void ks0108_page(unsigned char page)
102{
103 ks0108_writedata(min(page,(unsigned char)7) | bit(3) | bit(4) | bit(5) | bit(7));
104}
105
106EXPORT_SYMBOL_GPL(ks0108_writedata);
107EXPORT_SYMBOL_GPL(ks0108_writecontrol);
108EXPORT_SYMBOL_GPL(ks0108_displaystate);
109EXPORT_SYMBOL_GPL(ks0108_startline);
110EXPORT_SYMBOL_GPL(ks0108_address);
111EXPORT_SYMBOL_GPL(ks0108_page);
112
113/*
114 * Module Init & Exit
115 */
116
117static int __init ks0108_init(void)
118{
119 int result;
120 int ret = -EINVAL;
121
122 ks0108_parport = parport_find_base(ks0108_port);
123 if (ks0108_parport == NULL) {
124 printk(KERN_ERR KS0108_NAME ": ERROR: "
125 "parport didn't find %i port\n", ks0108_port);
126 goto none;
127 }
128
129 ks0108_pardevice = parport_register_device(ks0108_parport, KS0108_NAME,
130 NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
131 if (ks0108_pardevice == NULL) {
132 printk(KERN_ERR KS0108_NAME ": ERROR: "
133 "parport didn't register new device\n");
134 goto none;
135 }
136
137 result = parport_claim(ks0108_pardevice);
138 if (result != 0) {
139 printk(KERN_ERR KS0108_NAME ": ERROR: "
140 "can't claim %i parport, maybe in use\n", ks0108_port);
141 ret = result;
142 goto registered;
143 }
144
145 return 0;
146
147registered:
148 parport_unregister_device(ks0108_pardevice);
149
150none:
151 return ret;
152}
153
154static void __exit ks0108_exit(void)
155{
156 parport_release(ks0108_pardevice);
157 parport_unregister_device(ks0108_pardevice);
158}
159
160module_init(ks0108_init);
161module_exit(ks0108_exit);
162
163MODULE_LICENSE("GPL v2");
164MODULE_AUTHOR("Miguel Ojeda Sandonis <maxextreme@gmail.com>");
165MODULE_DESCRIPTION("ks0108 LCD Controller driver");
166