^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * linux/drivers/video/n411.c -- Platform device for N411 EPD kit
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (C) 2008, Jaya Kumar
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * This file is subject to the terms and conditions of the GNU General Public
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * License. See the file COPYING in the main directory of this archive for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * more details.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * This driver is written to be used with the Hecuba display controller
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * board, and tested with the EInk 800x600 display in 1 bit mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * The interface between Hecuba and the host is TTL based GPIO. The
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * GPIO requirements are 8 writable data lines and 6 lines for control.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * Only 4 of the controls are actually used here but 6 for future use.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * The driver requires the IO addresses for data and control GPIO at
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * load time. It is also possible to use this display with a standard
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * PC parallel port.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * General notes:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * - User must set dio_addr=0xIOADDR cio_addr=0xIOADDR c2io_addr=0xIOADDR
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #include <linux/string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) #include <linux/interrupt.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) #include <linux/fb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #include <linux/list.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) #include <linux/uaccess.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) #include <linux/irq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) #include <video/hecubafb.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) static unsigned long dio_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) static unsigned long cio_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) static unsigned long c2io_addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static unsigned long splashval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static unsigned int nosplash;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) static unsigned char ctl;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) static void n411_set_ctl(struct hecubafb_par *par, unsigned char bit, unsigned
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) char state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) switch (bit) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) case HCB_CD_BIT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) if (state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) ctl &= ~(HCB_CD_BIT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) ctl |= HCB_CD_BIT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) case HCB_DS_BIT:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) if (state)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) ctl &= ~(HCB_DS_BIT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) ctl |= HCB_DS_BIT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) outb(ctl, cio_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static unsigned char n411_get_ctl(struct hecubafb_par *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) return inb(c2io_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) static void n411_set_data(struct hecubafb_par *par, unsigned char value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) outb(value, dio_addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static void n411_wait_for_ack(struct hecubafb_par *par, int clear)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) int timeout;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) unsigned char tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) timeout = 500;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) do {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) tmp = n411_get_ctl(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) if ((tmp & HCB_ACK_BIT) && (!clear))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) else if (!(tmp & HCB_ACK_BIT) && (clear))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) udelay(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) } while (timeout--);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) printk(KERN_ERR "timed out waiting for ack\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static int n411_init_control(struct hecubafb_par *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) unsigned char tmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) /* for init, we want the following setup to be set:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) WUP = lo
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) ACK = hi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) DS = hi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) RW = hi
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) CD = lo
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) /* write WUP to lo, DS to hi, RW to hi, CD to lo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) ctl = HCB_WUP_BIT | HCB_RW_BIT | HCB_CD_BIT ;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) n411_set_ctl(par, HCB_DS_BIT, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) /* check ACK is not lo */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) tmp = n411_get_ctl(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) if (tmp & HCB_ACK_BIT) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) printk(KERN_ERR "Fail because ACK is already low\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) static int n411_init_board(struct hecubafb_par *par)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) int retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) retval = n411_init_control(par);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (retval)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) return retval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) par->send_command(par, APOLLO_INIT_DISPLAY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) par->send_data(par, 0x81);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* have to wait while display resets */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) udelay(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) /* if we were told to splash the screen, we just clear it */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (!nosplash) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) par->send_command(par, APOLLO_ERASE_DISPLAY);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) par->send_data(par, splashval);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) static struct hecuba_board n411_board = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) .owner = THIS_MODULE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) .init = n411_init_board,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) .set_ctl = n411_set_ctl,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .set_data = n411_set_data,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .wait_for_ack = n411_wait_for_ack,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) static struct platform_device *n411_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) static int __init n411_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) if (!dio_addr || !cio_addr || !c2io_addr) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) printk(KERN_WARNING "no IO addresses supplied\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) /* request our platform independent driver */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) request_module("hecubafb");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) n411_device = platform_device_alloc("hecubafb", -1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (!n411_device)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) ret = platform_device_add_data(n411_device, &n411_board,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) sizeof(n411_board));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) goto put_plat_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) /* this _add binds hecubafb to n411. hecubafb refcounts n411 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) ret = platform_device_add(n411_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) goto put_plat_device;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) put_plat_device:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) platform_device_put(n411_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) static void __exit n411_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) platform_device_unregister(n411_device);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) module_init(n411_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) module_exit(n411_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) module_param(nosplash, uint, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) MODULE_PARM_DESC(nosplash, "Disable doing the splash screen");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) module_param_hw(dio_addr, ulong, ioport, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) MODULE_PARM_DESC(dio_addr, "IO address for data, eg: 0x480");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) module_param_hw(cio_addr, ulong, ioport, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) MODULE_PARM_DESC(cio_addr, "IO address for control, eg: 0x400");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) module_param_hw(c2io_addr, ulong, ioport, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) MODULE_PARM_DESC(c2io_addr, "IO address for secondary control, eg: 0x408");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) module_param(splashval, ulong, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) MODULE_PARM_DESC(splashval, "Splash pattern: 0x00 is black, 0x01 is white");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) MODULE_DESCRIPTION("board driver for n411 hecuba/apollo epd kit");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) MODULE_AUTHOR("Jaya Kumar");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)