1351f683bSMiguel Ojeda // SPDX-License-Identifier: GPL-2.0
270e84049SMiguel Ojeda Sandonis /*
370e84049SMiguel Ojeda Sandonis * Filename: ks0108.c
470e84049SMiguel Ojeda Sandonis * Version: 0.1.0
570e84049SMiguel Ojeda Sandonis * Description: ks0108 LCD Controller driver
670e84049SMiguel Ojeda Sandonis * Depends: parport
770e84049SMiguel Ojeda Sandonis *
8c131bd0bSMiguel Ojeda * Author: Copyright (C) Miguel Ojeda <ojeda@kernel.org>
970e84049SMiguel Ojeda Sandonis * Date: 2006-10-31
1070e84049SMiguel Ojeda Sandonis */
1170e84049SMiguel Ojeda Sandonis
127faad1dfSSudip Mukherjee #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
137faad1dfSSudip Mukherjee
1470e84049SMiguel Ojeda Sandonis #include <linux/init.h>
1570e84049SMiguel Ojeda Sandonis #include <linux/module.h>
1670e84049SMiguel Ojeda Sandonis #include <linux/kernel.h>
1770e84049SMiguel Ojeda Sandonis #include <linux/delay.h>
1870e84049SMiguel Ojeda Sandonis #include <linux/parport.h>
1970e84049SMiguel Ojeda Sandonis #include <linux/ks0108.h>
2070e84049SMiguel Ojeda Sandonis
2170e84049SMiguel Ojeda Sandonis #define KS0108_NAME "ks0108"
2270e84049SMiguel Ojeda Sandonis
2370e84049SMiguel Ojeda Sandonis /*
2470e84049SMiguel Ojeda Sandonis * Module Parameters
2570e84049SMiguel Ojeda Sandonis */
2670e84049SMiguel Ojeda Sandonis
2770e84049SMiguel Ojeda Sandonis static unsigned int ks0108_port = CONFIG_KS0108_PORT;
28*24ebc044SJinchao Wang module_param(ks0108_port, uint, 0444);
2970e84049SMiguel Ojeda Sandonis MODULE_PARM_DESC(ks0108_port, "Parallel port where the LCD is connected");
3070e84049SMiguel Ojeda Sandonis
3170e84049SMiguel Ojeda Sandonis static unsigned int ks0108_delay = CONFIG_KS0108_DELAY;
32*24ebc044SJinchao Wang module_param(ks0108_delay, uint, 0444);
3370e84049SMiguel Ojeda Sandonis MODULE_PARM_DESC(ks0108_delay, "Delay between each control writing (microseconds)");
3470e84049SMiguel Ojeda Sandonis
3570e84049SMiguel Ojeda Sandonis /*
3670e84049SMiguel Ojeda Sandonis * Device
3770e84049SMiguel Ojeda Sandonis */
3870e84049SMiguel Ojeda Sandonis
3970e84049SMiguel Ojeda Sandonis static struct parport *ks0108_parport;
4070e84049SMiguel Ojeda Sandonis static struct pardevice *ks0108_pardevice;
4170e84049SMiguel Ojeda Sandonis
4270e84049SMiguel Ojeda Sandonis /*
4370e84049SMiguel Ojeda Sandonis * ks0108 Exported Commands (don't lock)
4470e84049SMiguel Ojeda Sandonis *
4570e84049SMiguel Ojeda Sandonis * You _should_ lock in the top driver: This functions _should not_
4670e84049SMiguel Ojeda Sandonis * get race conditions in any way. Locking for each byte here would be
4770e84049SMiguel Ojeda Sandonis * so slow and useless.
4870e84049SMiguel Ojeda Sandonis *
4970e84049SMiguel Ojeda Sandonis * There are not bit definitions because they are not flags,
5070e84049SMiguel Ojeda Sandonis * just arbitrary combinations defined by the documentation for each
5170e84049SMiguel Ojeda Sandonis * function in the ks0108 LCD controller. If you want to know what means
5270e84049SMiguel Ojeda Sandonis * a specific combination, look at the function's name.
5370e84049SMiguel Ojeda Sandonis *
5470e84049SMiguel Ojeda Sandonis * The ks0108_writecontrol bits need to be reverted ^(0,1,3) because
5570e84049SMiguel Ojeda Sandonis * the parallel port also revert them using a "not" logic gate.
5670e84049SMiguel Ojeda Sandonis */
5770e84049SMiguel Ojeda Sandonis
5870e84049SMiguel Ojeda Sandonis #define bit(n) (((unsigned char)1)<<(n))
5970e84049SMiguel Ojeda Sandonis
ks0108_writedata(unsigned char byte)6070e84049SMiguel Ojeda Sandonis void ks0108_writedata(unsigned char byte)
6170e84049SMiguel Ojeda Sandonis {
6270e84049SMiguel Ojeda Sandonis parport_write_data(ks0108_parport, byte);
6370e84049SMiguel Ojeda Sandonis }
6470e84049SMiguel Ojeda Sandonis
ks0108_writecontrol(unsigned char byte)6570e84049SMiguel Ojeda Sandonis void ks0108_writecontrol(unsigned char byte)
6670e84049SMiguel Ojeda Sandonis {
6770e84049SMiguel Ojeda Sandonis udelay(ks0108_delay);
6870e84049SMiguel Ojeda Sandonis parport_write_control(ks0108_parport, byte ^ (bit(0) | bit(1) | bit(3)));
6970e84049SMiguel Ojeda Sandonis }
7070e84049SMiguel Ojeda Sandonis
ks0108_displaystate(unsigned char state)7170e84049SMiguel Ojeda Sandonis void ks0108_displaystate(unsigned char state)
7270e84049SMiguel Ojeda Sandonis {
7370e84049SMiguel Ojeda Sandonis ks0108_writedata((state ? bit(0) : 0) | bit(1) | bit(2) | bit(3) | bit(4) | bit(5));
7470e84049SMiguel Ojeda Sandonis }
7570e84049SMiguel Ojeda Sandonis
ks0108_startline(unsigned char startline)7670e84049SMiguel Ojeda Sandonis void ks0108_startline(unsigned char startline)
7770e84049SMiguel Ojeda Sandonis {
78c9efdbe6SSudip Mukherjee ks0108_writedata(min_t(unsigned char, startline, 63) | bit(6) |
79c9efdbe6SSudip Mukherjee bit(7));
8070e84049SMiguel Ojeda Sandonis }
8170e84049SMiguel Ojeda Sandonis
ks0108_address(unsigned char address)8270e84049SMiguel Ojeda Sandonis void ks0108_address(unsigned char address)
8370e84049SMiguel Ojeda Sandonis {
84c9efdbe6SSudip Mukherjee ks0108_writedata(min_t(unsigned char, address, 63) | bit(6));
8570e84049SMiguel Ojeda Sandonis }
8670e84049SMiguel Ojeda Sandonis
ks0108_page(unsigned char page)8770e84049SMiguel Ojeda Sandonis void ks0108_page(unsigned char page)
8870e84049SMiguel Ojeda Sandonis {
89c9efdbe6SSudip Mukherjee ks0108_writedata(min_t(unsigned char, page, 7) | bit(3) | bit(4) |
90c9efdbe6SSudip Mukherjee bit(5) | bit(7));
9170e84049SMiguel Ojeda Sandonis }
9270e84049SMiguel Ojeda Sandonis
9370e84049SMiguel Ojeda Sandonis EXPORT_SYMBOL_GPL(ks0108_writedata);
9470e84049SMiguel Ojeda Sandonis EXPORT_SYMBOL_GPL(ks0108_writecontrol);
9570e84049SMiguel Ojeda Sandonis EXPORT_SYMBOL_GPL(ks0108_displaystate);
9670e84049SMiguel Ojeda Sandonis EXPORT_SYMBOL_GPL(ks0108_startline);
9770e84049SMiguel Ojeda Sandonis EXPORT_SYMBOL_GPL(ks0108_address);
9870e84049SMiguel Ojeda Sandonis EXPORT_SYMBOL_GPL(ks0108_page);
9970e84049SMiguel Ojeda Sandonis
10070e84049SMiguel Ojeda Sandonis /*
10134173a4aSMiguel Ojeda * Is the module inited?
10234173a4aSMiguel Ojeda */
10334173a4aSMiguel Ojeda
10434173a4aSMiguel Ojeda static unsigned char ks0108_inited;
ks0108_isinited(void)10534173a4aSMiguel Ojeda unsigned char ks0108_isinited(void)
10634173a4aSMiguel Ojeda {
10734173a4aSMiguel Ojeda return ks0108_inited;
10834173a4aSMiguel Ojeda }
10934173a4aSMiguel Ojeda EXPORT_SYMBOL_GPL(ks0108_isinited);
11034173a4aSMiguel Ojeda
ks0108_parport_attach(struct parport * port)1114edd70c1SSudip Mukherjee static void ks0108_parport_attach(struct parport *port)
1124edd70c1SSudip Mukherjee {
1134edd70c1SSudip Mukherjee struct pardev_cb ks0108_cb;
1144edd70c1SSudip Mukherjee
1154edd70c1SSudip Mukherjee if (port->base != ks0108_port)
1164edd70c1SSudip Mukherjee return;
1174edd70c1SSudip Mukherjee
1184edd70c1SSudip Mukherjee memset(&ks0108_cb, 0, sizeof(ks0108_cb));
1194edd70c1SSudip Mukherjee ks0108_cb.flags = PARPORT_DEV_EXCL;
1204edd70c1SSudip Mukherjee ks0108_pardevice = parport_register_dev_model(port, KS0108_NAME,
1214edd70c1SSudip Mukherjee &ks0108_cb, 0);
1224edd70c1SSudip Mukherjee if (!ks0108_pardevice) {
1234edd70c1SSudip Mukherjee pr_err("ERROR: parport didn't register new device\n");
1244edd70c1SSudip Mukherjee return;
1254edd70c1SSudip Mukherjee }
1264edd70c1SSudip Mukherjee if (parport_claim(ks0108_pardevice)) {
1274edd70c1SSudip Mukherjee pr_err("could not claim access to parport %i. Aborting.\n",
1284edd70c1SSudip Mukherjee ks0108_port);
1294edd70c1SSudip Mukherjee goto err_unreg_device;
1304edd70c1SSudip Mukherjee }
1314edd70c1SSudip Mukherjee
13292f26189SSudip Mukherjee ks0108_parport = port;
1334edd70c1SSudip Mukherjee ks0108_inited = 1;
1344edd70c1SSudip Mukherjee return;
1354edd70c1SSudip Mukherjee
1364edd70c1SSudip Mukherjee err_unreg_device:
1374edd70c1SSudip Mukherjee parport_unregister_device(ks0108_pardevice);
1384edd70c1SSudip Mukherjee ks0108_pardevice = NULL;
1394edd70c1SSudip Mukherjee }
1404edd70c1SSudip Mukherjee
ks0108_parport_detach(struct parport * port)1414edd70c1SSudip Mukherjee static void ks0108_parport_detach(struct parport *port)
1424edd70c1SSudip Mukherjee {
1434edd70c1SSudip Mukherjee if (port->base != ks0108_port)
1444edd70c1SSudip Mukherjee return;
1454edd70c1SSudip Mukherjee
1464edd70c1SSudip Mukherjee if (!ks0108_pardevice) {
1474edd70c1SSudip Mukherjee pr_err("%s: already unregistered.\n", KS0108_NAME);
1484edd70c1SSudip Mukherjee return;
1494edd70c1SSudip Mukherjee }
1504edd70c1SSudip Mukherjee
1514edd70c1SSudip Mukherjee parport_release(ks0108_pardevice);
1524edd70c1SSudip Mukherjee parport_unregister_device(ks0108_pardevice);
1534edd70c1SSudip Mukherjee ks0108_pardevice = NULL;
15492f26189SSudip Mukherjee ks0108_parport = NULL;
1554edd70c1SSudip Mukherjee }
1564edd70c1SSudip Mukherjee
15734173a4aSMiguel Ojeda /*
15870e84049SMiguel Ojeda Sandonis * Module Init & Exit
15970e84049SMiguel Ojeda Sandonis */
16070e84049SMiguel Ojeda Sandonis
1614edd70c1SSudip Mukherjee static struct parport_driver ks0108_parport_driver = {
1624edd70c1SSudip Mukherjee .name = "ks0108",
1634edd70c1SSudip Mukherjee .match_port = ks0108_parport_attach,
1644edd70c1SSudip Mukherjee .detach = ks0108_parport_detach,
1654edd70c1SSudip Mukherjee .devmodel = true,
1664edd70c1SSudip Mukherjee };
167f885afe2SAndy Shevchenko module_parport_driver(ks0108_parport_driver);
16870e84049SMiguel Ojeda Sandonis
16970e84049SMiguel Ojeda Sandonis MODULE_LICENSE("GPL v2");
170c131bd0bSMiguel Ojeda MODULE_AUTHOR("Miguel Ojeda <ojeda@kernel.org>");
17170e84049SMiguel Ojeda Sandonis MODULE_DESCRIPTION("ks0108 LCD Controller driver");
17270e84049SMiguel Ojeda Sandonis
173