1/*
2 * Picvue PVC160206 display driver
3 *
4 * Brian Murphy <brian@murphy.dk>
5 *
6 */
7#include <linux/kernel.h>
8#include <linux/delay.h>
9#include <asm/bootinfo.h>
10#include <asm/lasat/lasat.h>
11#include <linux/module.h>
12#include <linux/init.h>
13#include <linux/errno.h>
14
15#include "picvue.h"
16
17#define PVC_BUSY		0x80
18#define PVC_NLINES		2
19#define PVC_DISPMEM		80
20#define PVC_LINELEN		PVC_DISPMEM / PVC_NLINES
21
22struct pvc_defs *picvue = NULL;
23
24DECLARE_MUTEX(pvc_sem);
25
26static void pvc_reg_write(u32 val)
27{
28	*picvue->reg = val;
29}
30
31static u32 pvc_reg_read(void)
32{
33	u32 tmp = *picvue->reg;
34	return tmp;
35}
36
37static void pvc_write_byte(u32 data, u8 byte)
38{
39	data |= picvue->e;
40	pvc_reg_write(data);
41	data &= ~picvue->data_mask;
42	data |= byte << picvue->data_shift;
43	pvc_reg_write(data);
44	ndelay(220);
45	pvc_reg_write(data & ~picvue->e);
46	ndelay(220);
47}
48
49static u8 pvc_read_byte(u32 data)
50{
51	u8 byte;
52
53	data |= picvue->e;
54	pvc_reg_write(data);
55	ndelay(220);
56	byte = (pvc_reg_read() & picvue->data_mask) >> picvue->data_shift;
57	data &= ~picvue->e;
58	pvc_reg_write(data);
59	ndelay(220);
60	return byte;
61}
62
63static u8 pvc_read_data(void)
64{
65	u32 data = pvc_reg_read();
66	u8 byte;
67	data |= picvue->rw;
68	data &= ~picvue->rs;
69	pvc_reg_write(data);
70	ndelay(40);
71	byte = pvc_read_byte(data);
72	data |= picvue->rs;
73	pvc_reg_write(data);
74	return byte;
75}
76
77#define TIMEOUT 1000
78static int pvc_wait(void)
79{
80	int i = TIMEOUT;
81	int err = 0;
82
83	while ((pvc_read_data() & PVC_BUSY) && i)
84		i--;
85	if (i == 0)
86		err = -ETIME;
87
88	return err;
89}
90
91#define MODE_INST 0
92#define MODE_DATA 1
93static void pvc_write(u8 byte, int mode)
94{
95	u32 data = pvc_reg_read();
96	data &= ~picvue->rw;
97	if (mode == MODE_DATA)
98		data |= picvue->rs;
99	else
100		data &= ~picvue->rs;
101	pvc_reg_write(data);
102	ndelay(40);
103	pvc_write_byte(data, byte);
104	if (mode == MODE_DATA)
105		data &= ~picvue->rs;
106	else
107		data |= picvue->rs;
108	pvc_reg_write(data);
109	pvc_wait();
110}
111
112void pvc_write_string(const unsigned char *str, u8 addr, int line) {
113	int i = 0;
114
115	if (line > 0 && (PVC_NLINES > 1))
116		addr += 0x40 * line;
117	pvc_write(0x80 | addr, MODE_INST);
118
119	while (*str != 0 && i < PVC_LINELEN) {
120		pvc_write(*str++, MODE_DATA);
121		i++;
122	}
123}
124
125#define BM_SIZE			8
126#define MAX_PROGRAMMABLE_CHARS	8
127int pvc_program_cg(int charnum, u8 bitmap[BM_SIZE]) {
128	int i;
129	int addr;
130
131	if (charnum > MAX_PROGRAMMABLE_CHARS)
132		return -ENOENT;
133
134	addr = charnum * 8;
135	pvc_write(0x40 | addr, MODE_INST);
136
137	for (i=0; i<BM_SIZE; i++)
138		pvc_write(bitmap[i], MODE_DATA);
139	return 0;
140}
141
142#define FUNC_SET_CMD	0x20
143#define  EIGHT_BYTE	(1 << 4)
144#define  FOUR_BYTE	0
145#define  TWO_LINES	(1 << 3)
146#define  ONE_LINE	0
147#define  LARGE_FONT	(1 << 2)
148#define  SMALL_FONT	0
149static void pvc_funcset(u8 cmd) {
150	pvc_write(FUNC_SET_CMD | (cmd & (EIGHT_BYTE|TWO_LINES|LARGE_FONT)), MODE_INST);
151}
152
153#define ENTRYMODE_CMD		0x4
154#define  AUTO_INC		(1 << 1)
155#define  AUTO_DEC		0
156#define  CURSOR_FOLLOWS_DISP	(1 << 0)
157static void pvc_entrymode(u8 cmd) {
158	pvc_write(ENTRYMODE_CMD | (cmd & (AUTO_INC|CURSOR_FOLLOWS_DISP)), MODE_INST);
159}
160
161#define DISP_CNT_CMD	0x08
162#define  DISP_OFF	0
163#define  DISP_ON	(1 << 2)
164#define  CUR_ON		(1 << 1)
165#define  CUR_BLINK	(1 << 0)
166void pvc_dispcnt(u8 cmd) {
167	pvc_write(DISP_CNT_CMD | (cmd & (DISP_ON|CUR_ON|CUR_BLINK)), MODE_INST);
168}
169
170#define MOVE_CMD	0x10
171#define  DISPLAY	(1 << 3)
172#define  CURSOR		0
173#define  RIGHT		(1 << 2)
174#define  LEFT		0
175void pvc_move(u8 cmd) {
176	pvc_write(MOVE_CMD | (cmd & (DISPLAY|RIGHT)), MODE_INST);
177}
178
179#define CLEAR_CMD	0x1
180void pvc_clear(void) {
181	pvc_write(CLEAR_CMD, MODE_INST);
182}
183
184#define HOME_CMD	0x2
185void pvc_home(void) {
186	pvc_write(HOME_CMD, MODE_INST);
187}
188
189int pvc_init(void)
190{
191	u8 cmd = EIGHT_BYTE;
192
193	if (PVC_NLINES == 2)
194		cmd |= (SMALL_FONT|TWO_LINES);
195	else
196		cmd |= (LARGE_FONT|ONE_LINE);
197	pvc_funcset(cmd);
198	pvc_dispcnt(DISP_ON);
199	pvc_entrymode(AUTO_INC);
200
201	return 0;
202}
203
204module_init(pvc_init);
205MODULE_LICENSE("GPL");
206