162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/drivers/video/n411.c -- Platform device for N411 EPD kit 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2008, Jaya Kumar 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 862306a36Sopenharmony_ci * more details. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This driver is written to be used with the Hecuba display controller 1362306a36Sopenharmony_ci * board, and tested with the EInk 800x600 display in 1 bit mode. 1462306a36Sopenharmony_ci * The interface between Hecuba and the host is TTL based GPIO. The 1562306a36Sopenharmony_ci * GPIO requirements are 8 writable data lines and 6 lines for control. 1662306a36Sopenharmony_ci * Only 4 of the controls are actually used here but 6 for future use. 1762306a36Sopenharmony_ci * The driver requires the IO addresses for data and control GPIO at 1862306a36Sopenharmony_ci * load time. It is also possible to use this display with a standard 1962306a36Sopenharmony_ci * PC parallel port. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * General notes: 2262306a36Sopenharmony_ci * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR c2io_addr=0xIOADDR 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/kernel.h> 2862306a36Sopenharmony_ci#include <linux/errno.h> 2962306a36Sopenharmony_ci#include <linux/string.h> 3062306a36Sopenharmony_ci#include <linux/delay.h> 3162306a36Sopenharmony_ci#include <linux/interrupt.h> 3262306a36Sopenharmony_ci#include <linux/fb.h> 3362306a36Sopenharmony_ci#include <linux/init.h> 3462306a36Sopenharmony_ci#include <linux/platform_device.h> 3562306a36Sopenharmony_ci#include <linux/list.h> 3662306a36Sopenharmony_ci#include <linux/uaccess.h> 3762306a36Sopenharmony_ci#include <linux/irq.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include <video/hecubafb.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic unsigned long dio_addr; 4262306a36Sopenharmony_cistatic unsigned long cio_addr; 4362306a36Sopenharmony_cistatic unsigned long c2io_addr; 4462306a36Sopenharmony_cistatic unsigned long splashval; 4562306a36Sopenharmony_cistatic unsigned int nosplash; 4662306a36Sopenharmony_cistatic unsigned char ctl; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic void n411_set_ctl(struct hecubafb_par *par, unsigned char bit, unsigned 4962306a36Sopenharmony_ci char state) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci switch (bit) { 5262306a36Sopenharmony_ci case HCB_CD_BIT: 5362306a36Sopenharmony_ci if (state) 5462306a36Sopenharmony_ci ctl &= ~(HCB_CD_BIT); 5562306a36Sopenharmony_ci else 5662306a36Sopenharmony_ci ctl |= HCB_CD_BIT; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci case HCB_DS_BIT: 5962306a36Sopenharmony_ci if (state) 6062306a36Sopenharmony_ci ctl &= ~(HCB_DS_BIT); 6162306a36Sopenharmony_ci else 6262306a36Sopenharmony_ci ctl |= HCB_DS_BIT; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci outb(ctl, cio_addr); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic unsigned char n411_get_ctl(struct hecubafb_par *par) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return inb(c2io_addr); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void n411_set_data(struct hecubafb_par *par, unsigned char value) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci outb(value, dio_addr); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic void n411_wait_for_ack(struct hecubafb_par *par, int clear) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int timeout; 8162306a36Sopenharmony_ci unsigned char tmp; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci timeout = 500; 8462306a36Sopenharmony_ci do { 8562306a36Sopenharmony_ci tmp = n411_get_ctl(par); 8662306a36Sopenharmony_ci if ((tmp & HCB_ACK_BIT) && (!clear)) 8762306a36Sopenharmony_ci return; 8862306a36Sopenharmony_ci else if (!(tmp & HCB_ACK_BIT) && (clear)) 8962306a36Sopenharmony_ci return; 9062306a36Sopenharmony_ci udelay(1); 9162306a36Sopenharmony_ci } while (timeout--); 9262306a36Sopenharmony_ci printk(KERN_ERR "timed out waiting for ack\n"); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int n411_init_control(struct hecubafb_par *par) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci unsigned char tmp; 9862306a36Sopenharmony_ci /* for init, we want the following setup to be set: 9962306a36Sopenharmony_ci WUP = lo 10062306a36Sopenharmony_ci ACK = hi 10162306a36Sopenharmony_ci DS = hi 10262306a36Sopenharmony_ci RW = hi 10362306a36Sopenharmony_ci CD = lo 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* write WUP to lo, DS to hi, RW to hi, CD to lo */ 10762306a36Sopenharmony_ci ctl = HCB_WUP_BIT | HCB_RW_BIT | HCB_CD_BIT ; 10862306a36Sopenharmony_ci n411_set_ctl(par, HCB_DS_BIT, 1); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci /* check ACK is not lo */ 11162306a36Sopenharmony_ci tmp = n411_get_ctl(par); 11262306a36Sopenharmony_ci if (tmp & HCB_ACK_BIT) { 11362306a36Sopenharmony_ci printk(KERN_ERR "Fail because ACK is already low\n"); 11462306a36Sopenharmony_ci return -ENXIO; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return 0; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int n411_init_board(struct hecubafb_par *par) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int retval; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci retval = n411_init_control(par); 12662306a36Sopenharmony_ci if (retval) 12762306a36Sopenharmony_ci return retval; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci par->send_command(par, APOLLO_INIT_DISPLAY); 13062306a36Sopenharmony_ci par->send_data(par, 0x81); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* have to wait while display resets */ 13362306a36Sopenharmony_ci udelay(1000); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* if we were told to splash the screen, we just clear it */ 13662306a36Sopenharmony_ci if (!nosplash) { 13762306a36Sopenharmony_ci par->send_command(par, APOLLO_ERASE_DISPLAY); 13862306a36Sopenharmony_ci par->send_data(par, splashval); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct hecuba_board n411_board = { 14562306a36Sopenharmony_ci .owner = THIS_MODULE, 14662306a36Sopenharmony_ci .init = n411_init_board, 14762306a36Sopenharmony_ci .set_ctl = n411_set_ctl, 14862306a36Sopenharmony_ci .set_data = n411_set_data, 14962306a36Sopenharmony_ci .wait_for_ack = n411_wait_for_ack, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct platform_device *n411_device; 15362306a36Sopenharmony_cistatic int __init n411_init(void) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int ret; 15662306a36Sopenharmony_ci if (!dio_addr || !cio_addr || !c2io_addr) { 15762306a36Sopenharmony_ci printk(KERN_WARNING "no IO addresses supplied\n"); 15862306a36Sopenharmony_ci return -EINVAL; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* request our platform independent driver */ 16262306a36Sopenharmony_ci request_module("hecubafb"); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci n411_device = platform_device_alloc("hecubafb", -1); 16562306a36Sopenharmony_ci if (!n411_device) 16662306a36Sopenharmony_ci return -ENOMEM; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ret = platform_device_add_data(n411_device, &n411_board, 16962306a36Sopenharmony_ci sizeof(n411_board)); 17062306a36Sopenharmony_ci if (ret) 17162306a36Sopenharmony_ci goto put_plat_device; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* this _add binds hecubafb to n411. hecubafb refcounts n411 */ 17462306a36Sopenharmony_ci ret = platform_device_add(n411_device); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (ret) 17762306a36Sopenharmony_ci goto put_plat_device; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ciput_plat_device: 18262306a36Sopenharmony_ci platform_device_put(n411_device); 18362306a36Sopenharmony_ci return ret; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void __exit n411_exit(void) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci platform_device_unregister(n411_device); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cimodule_init(n411_init); 19262306a36Sopenharmony_cimodule_exit(n411_exit); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cimodule_param(nosplash, uint, 0); 19562306a36Sopenharmony_ciMODULE_PARM_DESC(nosplash, "Disable doing the splash screen"); 19662306a36Sopenharmony_cimodule_param_hw(dio_addr, ulong, ioport, 0); 19762306a36Sopenharmony_ciMODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480"); 19862306a36Sopenharmony_cimodule_param_hw(cio_addr, ulong, ioport, 0); 19962306a36Sopenharmony_ciMODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400"); 20062306a36Sopenharmony_cimodule_param_hw(c2io_addr, ulong, ioport, 0); 20162306a36Sopenharmony_ciMODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408"); 20262306a36Sopenharmony_cimodule_param(splashval, ulong, 0); 20362306a36Sopenharmony_ciMODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white"); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciMODULE_DESCRIPTION("board driver for n411 hecuba/apollo epd kit"); 20662306a36Sopenharmony_ciMODULE_AUTHOR("Jaya Kumar"); 20762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 20862306a36Sopenharmony_ci 209