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