437 lines
10 KiB
C
437 lines
10 KiB
C
/*
|
|
* amr_gpodisp.c - driver for AMR Display in Loongson soc
|
|
*
|
|
* Copyright (C) 2012 Loongson Corporation
|
|
*
|
|
* 2013-01-31
|
|
*/
|
|
#include <linux/types.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/module.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/init.h>
|
|
#include <linux/err.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/completion.h>
|
|
#include <asm/io.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/fcntl.h>
|
|
#include <asm/uaccess.h>
|
|
#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");
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
|
|
|