18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/n411.c -- Platform device for N411 EPD kit 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2008, Jaya Kumar 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 88c2ecf20Sopenharmony_ci * more details. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This driver is written to be used with the Hecuba display controller 138c2ecf20Sopenharmony_ci * board, and tested with the EInk 800x600 display in 1 bit mode. 148c2ecf20Sopenharmony_ci * The interface between Hecuba and the host is TTL based GPIO. The 158c2ecf20Sopenharmony_ci * GPIO requirements are 8 writable data lines and 6 lines for control. 168c2ecf20Sopenharmony_ci * Only 4 of the controls are actually used here but 6 for future use. 178c2ecf20Sopenharmony_ci * The driver requires the IO addresses for data and control GPIO at 188c2ecf20Sopenharmony_ci * load time. It is also possible to use this display with a standard 198c2ecf20Sopenharmony_ci * PC parallel port. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * General notes: 228c2ecf20Sopenharmony_ci * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR c2io_addr=0xIOADDR 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/errno.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 328c2ecf20Sopenharmony_ci#include <linux/fb.h> 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 358c2ecf20Sopenharmony_ci#include <linux/list.h> 368c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 378c2ecf20Sopenharmony_ci#include <linux/irq.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#include <video/hecubafb.h> 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic unsigned long dio_addr; 428c2ecf20Sopenharmony_cistatic unsigned long cio_addr; 438c2ecf20Sopenharmony_cistatic unsigned long c2io_addr; 448c2ecf20Sopenharmony_cistatic unsigned long splashval; 458c2ecf20Sopenharmony_cistatic unsigned int nosplash; 468c2ecf20Sopenharmony_cistatic unsigned char ctl; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void n411_set_ctl(struct hecubafb_par *par, unsigned char bit, unsigned 498c2ecf20Sopenharmony_ci char state) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci switch (bit) { 528c2ecf20Sopenharmony_ci case HCB_CD_BIT: 538c2ecf20Sopenharmony_ci if (state) 548c2ecf20Sopenharmony_ci ctl &= ~(HCB_CD_BIT); 558c2ecf20Sopenharmony_ci else 568c2ecf20Sopenharmony_ci ctl |= HCB_CD_BIT; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci case HCB_DS_BIT: 598c2ecf20Sopenharmony_ci if (state) 608c2ecf20Sopenharmony_ci ctl &= ~(HCB_DS_BIT); 618c2ecf20Sopenharmony_ci else 628c2ecf20Sopenharmony_ci ctl |= HCB_DS_BIT; 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci outb(ctl, cio_addr); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic unsigned char n411_get_ctl(struct hecubafb_par *par) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return inb(c2io_addr); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void n411_set_data(struct hecubafb_par *par, unsigned char value) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci outb(value, dio_addr); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void n411_wait_for_ack(struct hecubafb_par *par, int clear) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci int timeout; 818c2ecf20Sopenharmony_ci unsigned char tmp; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci timeout = 500; 848c2ecf20Sopenharmony_ci do { 858c2ecf20Sopenharmony_ci tmp = n411_get_ctl(par); 868c2ecf20Sopenharmony_ci if ((tmp & HCB_ACK_BIT) && (!clear)) 878c2ecf20Sopenharmony_ci return; 888c2ecf20Sopenharmony_ci else if (!(tmp & HCB_ACK_BIT) && (clear)) 898c2ecf20Sopenharmony_ci return; 908c2ecf20Sopenharmony_ci udelay(1); 918c2ecf20Sopenharmony_ci } while (timeout--); 928c2ecf20Sopenharmony_ci printk(KERN_ERR "timed out waiting for ack\n"); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic int n411_init_control(struct hecubafb_par *par) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci unsigned char tmp; 988c2ecf20Sopenharmony_ci /* for init, we want the following setup to be set: 998c2ecf20Sopenharmony_ci WUP = lo 1008c2ecf20Sopenharmony_ci ACK = hi 1018c2ecf20Sopenharmony_ci DS = hi 1028c2ecf20Sopenharmony_ci RW = hi 1038c2ecf20Sopenharmony_ci CD = lo 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* write WUP to lo, DS to hi, RW to hi, CD to lo */ 1078c2ecf20Sopenharmony_ci ctl = HCB_WUP_BIT | HCB_RW_BIT | HCB_CD_BIT ; 1088c2ecf20Sopenharmony_ci n411_set_ctl(par, HCB_DS_BIT, 1); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* check ACK is not lo */ 1118c2ecf20Sopenharmony_ci tmp = n411_get_ctl(par); 1128c2ecf20Sopenharmony_ci if (tmp & HCB_ACK_BIT) { 1138c2ecf20Sopenharmony_ci printk(KERN_ERR "Fail because ACK is already low\n"); 1148c2ecf20Sopenharmony_ci return -ENXIO; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int n411_init_board(struct hecubafb_par *par) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci int retval; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci retval = n411_init_control(par); 1268c2ecf20Sopenharmony_ci if (retval) 1278c2ecf20Sopenharmony_ci return retval; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci par->send_command(par, APOLLO_INIT_DISPLAY); 1308c2ecf20Sopenharmony_ci par->send_data(par, 0x81); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* have to wait while display resets */ 1338c2ecf20Sopenharmony_ci udelay(1000); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* if we were told to splash the screen, we just clear it */ 1368c2ecf20Sopenharmony_ci if (!nosplash) { 1378c2ecf20Sopenharmony_ci par->send_command(par, APOLLO_ERASE_DISPLAY); 1388c2ecf20Sopenharmony_ci par->send_data(par, splashval); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct hecuba_board n411_board = { 1458c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1468c2ecf20Sopenharmony_ci .init = n411_init_board, 1478c2ecf20Sopenharmony_ci .set_ctl = n411_set_ctl, 1488c2ecf20Sopenharmony_ci .set_data = n411_set_data, 1498c2ecf20Sopenharmony_ci .wait_for_ack = n411_wait_for_ack, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic struct platform_device *n411_device; 1538c2ecf20Sopenharmony_cistatic int __init n411_init(void) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci if (!dio_addr || !cio_addr || !c2io_addr) { 1578c2ecf20Sopenharmony_ci printk(KERN_WARNING "no IO addresses supplied\n"); 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* request our platform independent driver */ 1628c2ecf20Sopenharmony_ci request_module("hecubafb"); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci n411_device = platform_device_alloc("hecubafb", -1); 1658c2ecf20Sopenharmony_ci if (!n411_device) 1668c2ecf20Sopenharmony_ci return -ENOMEM; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = platform_device_add_data(n411_device, &n411_board, 1698c2ecf20Sopenharmony_ci sizeof(n411_board)); 1708c2ecf20Sopenharmony_ci if (ret) 1718c2ecf20Sopenharmony_ci goto put_plat_device; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* this _add binds hecubafb to n411. hecubafb refcounts n411 */ 1748c2ecf20Sopenharmony_ci ret = platform_device_add(n411_device); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (ret) 1778c2ecf20Sopenharmony_ci goto put_plat_device; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return 0; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ciput_plat_device: 1828c2ecf20Sopenharmony_ci platform_device_put(n411_device); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void __exit n411_exit(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci platform_device_unregister(n411_device); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cimodule_init(n411_init); 1928c2ecf20Sopenharmony_cimodule_exit(n411_exit); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cimodule_param(nosplash, uint, 0); 1958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); 1968c2ecf20Sopenharmony_cimodule_param_hw(dio_addr, ulong, ioport, 0); 1978c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); 1988c2ecf20Sopenharmony_cimodule_param_hw(cio_addr, ulong, ioport, 0); 1998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); 2008c2ecf20Sopenharmony_cimodule_param_hw(c2io_addr, ulong, ioport, 0); 2018c2ecf20Sopenharmony_ciMODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); 2028c2ecf20Sopenharmony_cimodule_param(splashval, ulong, 0); 2038c2ecf20Sopenharmony_ciMODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white"); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("board driver for n411 hecuba/apollo epd kit"); 2068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 2078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2088c2ecf20Sopenharmony_ci 209