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