18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Filename: ks0108.c 48c2ecf20Sopenharmony_ci * Version: 0.1.0 58c2ecf20Sopenharmony_ci * Description: ks0108 LCD Controller driver 68c2ecf20Sopenharmony_ci * Depends: parport 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Copyright (C) Miguel Ojeda Sandonis 98c2ecf20Sopenharmony_ci * Date: 2006-10-31 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/fs.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/parport.h> 218c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 228c2ecf20Sopenharmony_ci#include <linux/ks0108.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define KS0108_NAME "ks0108" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Module Parameters 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic unsigned int ks0108_port = CONFIG_KS0108_PORT; 318c2ecf20Sopenharmony_cimodule_param(ks0108_port, uint, S_IRUGO); 328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ks0108_port, "Parallel port where the LCD is connected"); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic unsigned int ks0108_delay = CONFIG_KS0108_DELAY; 358c2ecf20Sopenharmony_cimodule_param(ks0108_delay, uint, S_IRUGO); 368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ks0108_delay, "Delay between each control writing (microseconds)"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * Device 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic struct parport *ks0108_parport; 438c2ecf20Sopenharmony_cistatic struct pardevice *ks0108_pardevice; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * ks0108 Exported Commands (don't lock) 478c2ecf20Sopenharmony_ci * 488c2ecf20Sopenharmony_ci * You _should_ lock in the top driver: This functions _should not_ 498c2ecf20Sopenharmony_ci * get race conditions in any way. Locking for each byte here would be 508c2ecf20Sopenharmony_ci * so slow and useless. 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * There are not bit definitions because they are not flags, 538c2ecf20Sopenharmony_ci * just arbitrary combinations defined by the documentation for each 548c2ecf20Sopenharmony_ci * function in the ks0108 LCD controller. If you want to know what means 558c2ecf20Sopenharmony_ci * a specific combination, look at the function's name. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * The ks0108_writecontrol bits need to be reverted ^(0,1,3) because 588c2ecf20Sopenharmony_ci * the parallel port also revert them using a "not" logic gate. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define bit(n) (((unsigned char)1)<<(n)) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_civoid ks0108_writedata(unsigned char byte) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci parport_write_data(ks0108_parport, byte); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_civoid ks0108_writecontrol(unsigned char byte) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci udelay(ks0108_delay); 718c2ecf20Sopenharmony_ci parport_write_control(ks0108_parport, byte ^ (bit(0) | bit(1) | bit(3))); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_civoid ks0108_displaystate(unsigned char state) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci ks0108_writedata((state ? bit(0) : 0) | bit(1) | bit(2) | bit(3) | bit(4) | bit(5)); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid ks0108_startline(unsigned char startline) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci ks0108_writedata(min_t(unsigned char, startline, 63) | bit(6) | 828c2ecf20Sopenharmony_ci bit(7)); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_civoid ks0108_address(unsigned char address) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci ks0108_writedata(min_t(unsigned char, address, 63) | bit(6)); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid ks0108_page(unsigned char page) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci ks0108_writedata(min_t(unsigned char, page, 7) | bit(3) | bit(4) | 938c2ecf20Sopenharmony_ci bit(5) | bit(7)); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_writedata); 978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_writecontrol); 988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_displaystate); 998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_startline); 1008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_address); 1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_page); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* 1048c2ecf20Sopenharmony_ci * Is the module inited? 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic unsigned char ks0108_inited; 1088c2ecf20Sopenharmony_ciunsigned char ks0108_isinited(void) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci return ks0108_inited; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ks0108_isinited); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic void ks0108_parport_attach(struct parport *port) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct pardev_cb ks0108_cb; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (port->base != ks0108_port) 1198c2ecf20Sopenharmony_ci return; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci memset(&ks0108_cb, 0, sizeof(ks0108_cb)); 1228c2ecf20Sopenharmony_ci ks0108_cb.flags = PARPORT_DEV_EXCL; 1238c2ecf20Sopenharmony_ci ks0108_pardevice = parport_register_dev_model(port, KS0108_NAME, 1248c2ecf20Sopenharmony_ci &ks0108_cb, 0); 1258c2ecf20Sopenharmony_ci if (!ks0108_pardevice) { 1268c2ecf20Sopenharmony_ci pr_err("ERROR: parport didn't register new device\n"); 1278c2ecf20Sopenharmony_ci return; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci if (parport_claim(ks0108_pardevice)) { 1308c2ecf20Sopenharmony_ci pr_err("could not claim access to parport %i. Aborting.\n", 1318c2ecf20Sopenharmony_ci ks0108_port); 1328c2ecf20Sopenharmony_ci goto err_unreg_device; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ks0108_parport = port; 1368c2ecf20Sopenharmony_ci ks0108_inited = 1; 1378c2ecf20Sopenharmony_ci return; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cierr_unreg_device: 1408c2ecf20Sopenharmony_ci parport_unregister_device(ks0108_pardevice); 1418c2ecf20Sopenharmony_ci ks0108_pardevice = NULL; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void ks0108_parport_detach(struct parport *port) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci if (port->base != ks0108_port) 1478c2ecf20Sopenharmony_ci return; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (!ks0108_pardevice) { 1508c2ecf20Sopenharmony_ci pr_err("%s: already unregistered.\n", KS0108_NAME); 1518c2ecf20Sopenharmony_ci return; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci parport_release(ks0108_pardevice); 1558c2ecf20Sopenharmony_ci parport_unregister_device(ks0108_pardevice); 1568c2ecf20Sopenharmony_ci ks0108_pardevice = NULL; 1578c2ecf20Sopenharmony_ci ks0108_parport = NULL; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* 1618c2ecf20Sopenharmony_ci * Module Init & Exit 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic struct parport_driver ks0108_parport_driver = { 1658c2ecf20Sopenharmony_ci .name = "ks0108", 1668c2ecf20Sopenharmony_ci .match_port = ks0108_parport_attach, 1678c2ecf20Sopenharmony_ci .detach = ks0108_parport_detach, 1688c2ecf20Sopenharmony_ci .devmodel = true, 1698c2ecf20Sopenharmony_ci}; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int __init ks0108_init(void) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci return parport_register_driver(&ks0108_parport_driver); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic void __exit ks0108_exit(void) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci parport_unregister_driver(&ks0108_parport_driver); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cimodule_init(ks0108_init); 1828c2ecf20Sopenharmony_cimodule_exit(ks0108_exit); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>"); 1868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ks0108 LCD Controller driver"); 1878c2ecf20Sopenharmony_ci 188