/* * amr_gpodisp.c - driver for AMR Display in Loongson soc * * Copyright (C) 2012 Loongson Corporation * * 2013-01-31 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "amr_gpodisp.h" void gpio_nand_lock(void); void gpio_nand_unlock(void); #define AMRDISP_GET_IOMUX_LOCK() \ do {\ gpio_nand_lock();\ } while (0) #define AMRDISP_PUT_IOMUX_LOCK() \ do {\ gpio_nand_unlock();\ } while (0) /*----------------------------------------------------------------------------*/ struct amr_gpodisp { struct cdev cdev; struct class *gpodisp_class; dev_t dev; bool display_init; }; struct amr_gpodisp amrdisp_priv; unsigned char disp_mem[108][160]; /*----------------------------------------------------------------------------*/ static void gpo_wr_dat(unsigned char uc_dat) { u32 reg_val = 0; u32 dat_val = (u32)uc_dat; /* A0 -> GPIO26 CS -> GPIO78 RST -> GPIO24 RD -> GPIO35 WD -> GPIO36 DAT -> GPIP38~45 conflict module -> spi0;keypad;emi;iis;pwm */ /* RD set 1 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val |= (0x01 << 3); *((volatile u32 *)0xbc11000c) = reg_val; /* A0 set 1 */ reg_val = *((volatile u32 *)0xbc110000); reg_val |= (0x01 << 26); *((volatile u32 *)0xbc110000) = reg_val; /* CS set 0 */ reg_val = *((volatile u32 *)0xbc110018); reg_val &= ~(0x01 << 14); *((volatile u32 *)0xbc110018) = reg_val; /* WD set 0 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val &= ~(0x01 << 4); *((volatile u32 *)0xbc11000c) = reg_val; /* uc_dat >> data0~7 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val &= ~(0xff << 6); reg_val |= (dat_val << 6); *((volatile u32 *)0xbc11000c) = reg_val; /* WD set 1 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val |= (0x01 << 4); *((volatile u32 *)0xbc11000c) = reg_val; /* CS set 1 */ reg_val = *((volatile u32 *)0xbc110018); reg_val |= (0x01 << 14); *((volatile u32 *)0xbc110018) = reg_val; } static void gpo_wr_cmd(unsigned char uc_cmd) { u32 reg_val = 0; volatile u32 cmd_val = (u32)uc_cmd; /* A0 -> GPIO26 CS -> GPIO78 RST -> GPIO87 RD -> GPIO35 WD -> GPIO36 DAT -> GPIP38~45 conflict module -> spi0;keypad;emi;iis;pwm */ /* RD set 1 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val |= (0x01 << 3); *((volatile u32 *)0xbc11000c) = reg_val; /* A0 set 0 */ reg_val = *((volatile u32 *)0xbc110000); reg_val &= ~(0x01 << 26); *((volatile u32 *)0xbc110000) = reg_val; /* CS set 0 */ reg_val = *((volatile u32 *)0xbc110018); reg_val &= ~(0x01 << 14); *((volatile u32 *)0xbc110018) = reg_val; /* WD set 0 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val &= ~(0x01 << 4); *((volatile u32 *)0xbc11000c) = reg_val; /* uc_dat >> data0~7 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val &= ~(0xff << 6); reg_val |= (cmd_val << 6); *((u32 *)0xbc11000c) = reg_val; /* WD set 1 */ reg_val = *((volatile u32 *)0xbc11000c); reg_val |= (0x01 << 4); *((volatile u32 *)0xbc11000c) = reg_val; /* CS set 1 */ reg_val = *((volatile u32 *)0xbc110018); reg_val |= (0x01 << 14); *((volatile u32 *)0xbc110018) = reg_val; } static void enable_gpo_disp_pin(void) { /* A0 -> GPIO26 CS -> GPIO78 RST -> GPIO87 DAT -> GPIP38~45 conflict module -> spi0;keypad;emi;iis;pwm */ /* not support hw src */ *((u32 *)0xbc110008) = 0; *((u32 *)0xbc110014) = 0; *((u32 *)0xbc110020) = 0; /* set gpioABC output */ *((u32 *)0xbc110004) = 0x7000000 | *((volatile u32 *)0xbc110004); *((u32 *)0xbc110010) = 0x3fd8 | *((volatile u32 *)0xbc110010); *((u32 *)0xbc11001c) = 0x4000 | *((volatile u32 *)0xbc11001C); } static void reset_gpo_disp_dev(void) { //RST -> GPIO87 u32 reg_val = 0; reg_val = *((u32 *)0xbc110000); reg_val &= ~(0x01 << 24); *((u32 *)0xbc110000) = reg_val; udelay(1000); reg_val = *((u32 *)0xbc110000); reg_val |= (0x01 << 24); *((u32 *)0xbc110000) = reg_val; udelay(1000); } static void gpo_disp_backlight_ctrl(unsigned char arg) { u32 reg_val = 0; if (arg) { reg_val = *((u32 *)0xbc110000); reg_val &= ~(0x01 << 25); *((u32 *)0xbc110000) = reg_val; } else { reg_val = *((u32 *)0xbc110000); reg_val |= (0x01 << 25); *((u32 *)0xbc110000) = reg_val; } } static void gpo_disp_ctrl_init(void) { gpo_wr_cmd(0xE2); //System Reset udelay(100000); gpo_wr_cmd(0xAE); //Set Display Disable gpo_wr_cmd(0x25); //Set Temperature Compensation (00-0.00%) gpo_wr_cmd(0x2B); //Set Power Control (Interal VLCD;Panel loading definition>13nF) gpo_wr_cmd(0xE9); //Set LCD Bias ratio:1/10 gpo_wr_cmd(0x81); //Set gain and potentiometer Mode gpo_wr_cmd(0xC0); //Program Gain:01;PM value:xx gpo_wr_cmd(0x89); //Set RAM Address Control gpo_wr_cmd(0xC4); //Set LCD Maping Control (MY=1, MX=0) gpo_wr_cmd(0xDE); //Set COM Scan Function gpo_wr_cmd(0xC8); //Set N-Line Inversion gpo_wr_cmd(0x18); //Set COM Scan Function gpo_wr_cmd(0xA3); //Set Line Rate gpo_wr_cmd(0xD6); //Set Color Mode (64K) gpo_wr_cmd(0xD1); //Set Color Pattern (RGB) gpo_wr_cmd(0x84); //Set Partial Display Off gpo_wr_cmd(0xAD); //Set Display Enable amrdisp_priv.display_init = true; } /*---------------------------------------------------------------------------*/ /* */ static ssize_t gpodisp_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { unsigned char uc_RowCnt, uc_ColCnt; unsigned char uc_RowAddrH, uc_RowAddrL; if (sizeof(disp_mem) > count) return -EFAULT; if (copy_from_user(disp_mem, buf, count)) return -EFAULT; if (amrdisp_priv.display_init == false) gpo_disp_ctrl_init(); for (uc_RowCnt = 0; uc_RowCnt < 160; uc_RowCnt++) { uc_RowAddrH = uc_RowCnt / 16; uc_RowAddrL = uc_RowCnt % 16; gpo_wr_cmd(0x70 | uc_RowAddrH); //Set Row Address(MSB) gpo_wr_cmd(0x60 | uc_RowAddrL); //Set Row Address(LSB) gpo_wr_cmd(0x12); //Set Address(MSB) gpo_wr_cmd(0x05); //Set Address(LSB) for (uc_ColCnt = 0; uc_ColCnt < 108; uc_ColCnt++) { gpo_wr_dat(disp_mem[uc_ColCnt][uc_RowCnt]); } } return 0; } /*----------------------------------------------------------------------------*/ /* */ static ssize_t gpodisp_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { return 0; } /*----------------------------------------------------------------------------*/ /* */ static long gpodisp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { unsigned char val = 0; if (_IOC_TYPE(cmd) != AMRDISP_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > AMRDISP_IOC_MAXNR) return -ENOTTY; __get_user(val, (unsigned char*)arg); AMRDISP_GET_IOMUX_LOCK(); switch(cmd) { case AMRDISP_CMD: if (arg) { gpo_wr_cmd(val); } break; case AMRDISP_DAT: if (arg) { gpo_wr_dat(val); } break; case AMRDISP_RST: reset_gpo_disp_dev(); break; case AMRDISP_INITPIN: enable_gpo_disp_pin(); break; case AMRDISPLAY_CTRL_INIT: gpo_disp_ctrl_init(); break; case AMRDISPLAY_BACKLIGHT: gpo_disp_backlight_ctrl(1); break; case AMRDISPLAY_BACKCLEAR: gpo_disp_backlight_ctrl(0); break; default: return -ENOTTY; } AMRDISP_PUT_IOMUX_LOCK(); return 0; } static int gpodisp_open(struct inode *inode, struct file *filp) { enable_gpo_disp_pin(); reset_gpo_disp_dev(); gpo_disp_ctrl_init(); gpo_disp_backlight_ctrl(1); return 0; } static int gpodisp_release(struct inode *inode, struct file *filp) { return 0; } struct file_operations amrdisp_fops = { .owner = THIS_MODULE, .read = gpodisp_read, .write = gpodisp_write, .unlocked_ioctl = gpodisp_ioctl, .open = gpodisp_open, .release = gpodisp_release, }; /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ static int __init amr_gpodisp_init(void) { int err; int result; int major; result = alloc_chrdev_region(&amrdisp_priv.dev, 0, 0, "amrdisp"); major = MAJOR(amrdisp_priv.dev); if (result < 0) { printk(KERN_WARNING "amrdisp: can't get major %d\n", major); return result; } cdev_init(&amrdisp_priv.cdev, &amrdisp_fops); amrdisp_priv.cdev.owner = THIS_MODULE; amrdisp_priv.cdev.ops = &amrdisp_fops; err = cdev_add(&amrdisp_priv.cdev, amrdisp_priv.dev, 1); if (err) { printk(KERN_NOTICE "Error[%d] cdev_add amrdisp\n", err); return -1; } amrdisp_priv.gpodisp_class = class_create(THIS_MODULE, "amr_gpodisp"); if (IS_ERR(amrdisp_priv.gpodisp_class)) { printk(KERN_ERR "Failed in create amr_cdev class\n"); return -1; } device_create(amrdisp_priv.gpodisp_class, NULL, amrdisp_priv.dev, NULL, "amrdisp"); amrdisp_priv.display_init = false; return 0; } /*----------------------------------------------------------------------------*/ static void __exit amr_gpodisp_exit(void) { cdev_del(&amrdisp_priv.cdev); unregister_chrdev_region(amrdisp_priv.dev, 1); device_destroy(amrdisp_priv.gpodisp_class, amrdisp_priv.dev); class_destroy(amrdisp_priv.gpodisp_class); } /*----------------------------------------------------------------------------*/ module_init(amr_gpodisp_init); module_exit(amr_gpodisp_exit); /*----------------------------------------------------------------------------*/ MODULE_AUTHOR("ansonn.wang@gmail.com"); MODULE_DESCRIPTION("amr Gpo Display driver"); MODULE_LICENSE("GPL"); /*----------------------------------------------------------------------------*/