1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Filename: ks0108.c |
4 | * Version: 0.1.0 |
5 | * Description: ks0108 LCD Controller driver |
6 | * Depends: parport |
7 | * |
8 | * Author: Copyright (C) Miguel Ojeda <ojeda@kernel.org> |
9 | * Date: 2006-10-31 |
10 | */ |
11 | |
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
13 | |
14 | #include <linux/init.h> |
15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/parport.h> |
19 | #include <linux/ks0108.h> |
20 | |
21 | #define KS0108_NAME "ks0108" |
22 | |
23 | /* |
24 | * Module Parameters |
25 | */ |
26 | |
27 | static unsigned int ks0108_port = CONFIG_KS0108_PORT; |
28 | module_param(ks0108_port, uint, 0444); |
29 | MODULE_PARM_DESC(ks0108_port, "Parallel port where the LCD is connected" ); |
30 | |
31 | static unsigned int ks0108_delay = CONFIG_KS0108_DELAY; |
32 | module_param(ks0108_delay, uint, 0444); |
33 | MODULE_PARM_DESC(ks0108_delay, "Delay between each control writing (microseconds)" ); |
34 | |
35 | /* |
36 | * Device |
37 | */ |
38 | |
39 | static struct parport *ks0108_parport; |
40 | static struct pardevice *ks0108_pardevice; |
41 | |
42 | /* |
43 | * ks0108 Exported Commands (don't lock) |
44 | * |
45 | * You _should_ lock in the top driver: This functions _should not_ |
46 | * get race conditions in any way. Locking for each byte here would be |
47 | * so slow and useless. |
48 | * |
49 | * There are not bit definitions because they are not flags, |
50 | * just arbitrary combinations defined by the documentation for each |
51 | * function in the ks0108 LCD controller. If you want to know what means |
52 | * a specific combination, look at the function's name. |
53 | * |
54 | * The ks0108_writecontrol bits need to be reverted ^(0,1,3) because |
55 | * the parallel port also revert them using a "not" logic gate. |
56 | */ |
57 | |
58 | #define bit(n) (((unsigned char)1)<<(n)) |
59 | |
60 | void ks0108_writedata(unsigned char byte) |
61 | { |
62 | parport_write_data(ks0108_parport, byte); |
63 | } |
64 | |
65 | void ks0108_writecontrol(unsigned char byte) |
66 | { |
67 | udelay(ks0108_delay); |
68 | parport_write_control(ks0108_parport, byte ^ (bit(0) | bit(1) | bit(3))); |
69 | } |
70 | |
71 | void ks0108_displaystate(unsigned char state) |
72 | { |
73 | ks0108_writedata(byte: (state ? bit(0) : 0) | bit(1) | bit(2) | bit(3) | bit(4) | bit(5)); |
74 | } |
75 | |
76 | void ks0108_startline(unsigned char startline) |
77 | { |
78 | ks0108_writedata(min_t(unsigned char, startline, 63) | bit(6) | |
79 | bit(7)); |
80 | } |
81 | |
82 | void ks0108_address(unsigned char address) |
83 | { |
84 | ks0108_writedata(min_t(unsigned char, address, 63) | bit(6)); |
85 | } |
86 | |
87 | void ks0108_page(unsigned char page) |
88 | { |
89 | ks0108_writedata(min_t(unsigned char, page, 7) | bit(3) | bit(4) | |
90 | bit(5) | bit(7)); |
91 | } |
92 | |
93 | EXPORT_SYMBOL_GPL(ks0108_writedata); |
94 | EXPORT_SYMBOL_GPL(ks0108_writecontrol); |
95 | EXPORT_SYMBOL_GPL(ks0108_displaystate); |
96 | EXPORT_SYMBOL_GPL(ks0108_startline); |
97 | EXPORT_SYMBOL_GPL(ks0108_address); |
98 | EXPORT_SYMBOL_GPL(ks0108_page); |
99 | |
100 | /* |
101 | * Is the module inited? |
102 | */ |
103 | |
104 | static unsigned char ks0108_inited; |
105 | unsigned char ks0108_isinited(void) |
106 | { |
107 | return ks0108_inited; |
108 | } |
109 | EXPORT_SYMBOL_GPL(ks0108_isinited); |
110 | |
111 | static void ks0108_parport_attach(struct parport *port) |
112 | { |
113 | struct pardev_cb ks0108_cb; |
114 | |
115 | if (port->base != ks0108_port) |
116 | return; |
117 | |
118 | memset(&ks0108_cb, 0, sizeof(ks0108_cb)); |
119 | ks0108_cb.flags = PARPORT_DEV_EXCL; |
120 | ks0108_pardevice = parport_register_dev_model(port, KS0108_NAME, |
121 | par_dev_cb: &ks0108_cb, cnt: 0); |
122 | if (!ks0108_pardevice) { |
123 | pr_err("ERROR: parport didn't register new device\n" ); |
124 | return; |
125 | } |
126 | if (parport_claim(dev: ks0108_pardevice)) { |
127 | pr_err("could not claim access to parport %i. Aborting.\n" , |
128 | ks0108_port); |
129 | goto err_unreg_device; |
130 | } |
131 | |
132 | ks0108_parport = port; |
133 | ks0108_inited = 1; |
134 | return; |
135 | |
136 | err_unreg_device: |
137 | parport_unregister_device(dev: ks0108_pardevice); |
138 | ks0108_pardevice = NULL; |
139 | } |
140 | |
141 | static void ks0108_parport_detach(struct parport *port) |
142 | { |
143 | if (port->base != ks0108_port) |
144 | return; |
145 | |
146 | if (!ks0108_pardevice) { |
147 | pr_err("%s: already unregistered.\n" , KS0108_NAME); |
148 | return; |
149 | } |
150 | |
151 | parport_release(dev: ks0108_pardevice); |
152 | parport_unregister_device(dev: ks0108_pardevice); |
153 | ks0108_pardevice = NULL; |
154 | ks0108_parport = NULL; |
155 | } |
156 | |
157 | /* |
158 | * Module Init & Exit |
159 | */ |
160 | |
161 | static struct parport_driver ks0108_parport_driver = { |
162 | .name = "ks0108" , |
163 | .match_port = ks0108_parport_attach, |
164 | .detach = ks0108_parport_detach, |
165 | .devmodel = true, |
166 | }; |
167 | module_parport_driver(ks0108_parport_driver); |
168 | |
169 | MODULE_LICENSE("GPL v2" ); |
170 | MODULE_AUTHOR("Miguel Ojeda <ojeda@kernel.org>" ); |
171 | MODULE_DESCRIPTION("ks0108 LCD Controller driver" ); |
172 | |
173 | |