1/* $Id: tcxfb.c,v 1.1.1.1 2008/10/15 03:27:05 james26_jang Exp $
2 * tcxfb.c: TCX 24/8bit frame buffer driver
3 *
4 * Copyright (C) 1996,1998 Jakub Jelinek (jj@ultra.linux.cz)
5 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
6 * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
7 */
8
9#include <linux/module.h>
10#include <linux/sched.h>
11#include <linux/kernel.h>
12#include <linux/errno.h>
13#include <linux/string.h>
14#include <linux/mm.h>
15#include <linux/tty.h>
16#include <linux/slab.h>
17#include <linux/vmalloc.h>
18#include <linux/delay.h>
19#include <linux/interrupt.h>
20#include <linux/fb.h>
21#include <linux/init.h>
22#include <linux/selection.h>
23
24#include <video/sbusfb.h>
25#include <asm/io.h>
26#include <asm/sbus.h>
27
28#include <video/fbcon-cfb8.h>
29
30/* THC definitions */
31#define TCX_THC_MISC_REV_SHIFT       16
32#define TCX_THC_MISC_REV_MASK        15
33#define TCX_THC_MISC_VSYNC_DIS       (1 << 25)
34#define TCX_THC_MISC_HSYNC_DIS       (1 << 24)
35#define TCX_THC_MISC_RESET           (1 << 12)
36#define TCX_THC_MISC_VIDEO           (1 << 10)
37#define TCX_THC_MISC_SYNC            (1 << 9)
38#define TCX_THC_MISC_VSYNC           (1 << 8)
39#define TCX_THC_MISC_SYNC_ENAB       (1 << 7)
40#define TCX_THC_MISC_CURS_RES        (1 << 6)
41#define TCX_THC_MISC_INT_ENAB        (1 << 5)
42#define TCX_THC_MISC_INT             (1 << 4)
43#define TCX_THC_MISC_INIT            0x9f
44#define TCX_THC_REV_REV_SHIFT        20
45#define TCX_THC_REV_REV_MASK         15
46#define TCX_THC_REV_MINREV_SHIFT     28
47#define TCX_THC_REV_MINREV_MASK      15
48
49/* The contents are unknown */
50struct tcx_tec {
51	volatile u32 tec_matrix;
52	volatile u32 tec_clip;
53	volatile u32 tec_vdc;
54};
55
56struct tcx_thc {
57	volatile u32 thc_rev;
58        u32 thc_pad0[511];
59	volatile u32 thc_hs;		/* hsync timing */
60	volatile u32 thc_hsdvs;
61	volatile u32 thc_hd;
62	volatile u32 thc_vs;		/* vsync timing */
63	volatile u32 thc_vd;
64	volatile u32 thc_refresh;
65	volatile u32 thc_misc;
66	u32 thc_pad1[56];
67	volatile u32 thc_cursxy;	/* cursor x,y position (16 bits each) */
68	volatile u32 thc_cursmask[32];	/* cursor mask bits */
69	volatile u32 thc_cursbits[32];	/* what to show where mask enabled */
70};
71
72static struct sbus_mmap_map tcx_mmap_map[] = {
73	{ TCX_RAM8BIT,		0,		SBUS_MMAP_FBSIZE(1) },
74	{ TCX_RAM24BIT,		0,		SBUS_MMAP_FBSIZE(4) },
75	{ TCX_UNK3,		0,		SBUS_MMAP_FBSIZE(8) },
76	{ TCX_UNK4,		0,		SBUS_MMAP_FBSIZE(8) },
77	{ TCX_CONTROLPLANE,	0,		SBUS_MMAP_FBSIZE(4) },
78	{ TCX_UNK6,		0,		SBUS_MMAP_FBSIZE(8) },
79	{ TCX_UNK7,		0,		SBUS_MMAP_FBSIZE(8) },
80	{ TCX_TEC,		0,		PAGE_SIZE	    },
81	{ TCX_BTREGS,		0,		PAGE_SIZE	    },
82	{ TCX_THC,		0,		PAGE_SIZE	    },
83	{ TCX_DHC,		0,		PAGE_SIZE	    },
84	{ TCX_ALT,		0,		PAGE_SIZE	    },
85	{ TCX_UNK2,		0,		0x20000		    },
86	{ 0,			0,		0		    }
87};
88
89static void __tcx_set_control_plane (struct fb_info_sbusfb *fb)
90{
91	u32 *p, *pend;
92
93	p = fb->s.tcx.cplane;
94	if (p == NULL)
95		return;
96	for (pend = p + fb->type.fb_size; p < pend; p++) {
97		u32 tmp = sbus_readl(p);
98
99		tmp &= 0xffffff;
100		sbus_writel(tmp, p);
101	}
102}
103
104static void tcx_switch_from_graph (struct fb_info_sbusfb *fb)
105{
106	unsigned long flags;
107
108	spin_lock_irqsave(&fb->lock, flags);
109
110	/* Reset control plane to 8bit mode if necessary */
111	if (fb->open && fb->mmaped)
112		__tcx_set_control_plane (fb);
113
114	spin_unlock_irqrestore(&fb->lock, flags);
115}
116
117static void tcx_loadcmap (struct fb_info_sbusfb *fb, struct display *p, int index, int count)
118{
119	struct bt_regs *bt = fb->s.tcx.bt;
120	unsigned long flags;
121	int i;
122
123	spin_lock_irqsave(&fb->lock, flags);
124	sbus_writel(index << 24, &bt->addr);
125	for (i = index; count--; i++){
126		sbus_writel(fb->color_map CM(i,0) << 24, &bt->color_map);
127		sbus_writel(fb->color_map CM(i,1) << 24, &bt->color_map);
128		sbus_writel(fb->color_map CM(i,2) << 24, &bt->color_map);
129	}
130	sbus_writel(0, &bt->addr);
131	spin_unlock_irqrestore(&fb->lock, flags);
132}
133
134static void tcx_restore_palette (struct fb_info_sbusfb *fb)
135{
136	struct bt_regs *bt = fb->s.tcx.bt;
137	unsigned long flags;
138
139	spin_lock_irqsave(&fb->lock, flags);
140	sbus_writel(0, &bt->addr);
141	sbus_writel(0xffffffff, &bt->color_map);
142	sbus_writel(0xffffffff, &bt->color_map);
143	sbus_writel(0xffffffff, &bt->color_map);
144	spin_unlock_irqrestore(&fb->lock, flags);
145}
146
147static void tcx_setcursormap (struct fb_info_sbusfb *fb, u8 *red, u8 *green, u8 *blue)
148{
149        struct bt_regs *bt = fb->s.tcx.bt;
150	unsigned long flags;
151
152	spin_lock_irqsave(&fb->lock, flags);
153
154	/* Note the 2 << 24 is different from cg6's 1 << 24 */
155	sbus_writel(2 << 24, &bt->addr);
156	sbus_writel(red[0] << 24, &bt->cursor);
157	sbus_writel(green[0] << 24, &bt->cursor);
158	sbus_writel(blue[0] << 24, &bt->cursor);
159	sbus_writel(3 << 24, &bt->addr);
160	sbus_writel(red[1] << 24, &bt->cursor);
161	sbus_writel(green[1] << 24, &bt->cursor);
162	sbus_writel(blue[1] << 24, &bt->cursor);
163	sbus_writel(0, &bt->addr);
164
165	spin_unlock_irqrestore(&fb->lock, flags);
166}
167
168/* Set cursor shape */
169static void tcx_setcurshape (struct fb_info_sbusfb *fb)
170{
171	struct tcx_thc *thc = fb->s.tcx.thc;
172	unsigned long flags;
173	int i;
174
175	spin_lock_irqsave(&fb->lock, flags);
176	for (i = 0; i < 32; i++){
177		sbus_writel(fb->cursor.bits[0][i], &thc->thc_cursmask[i]);
178		sbus_writel(fb->cursor.bits[1][i], &thc->thc_cursbits[i]);
179	}
180	spin_unlock_irqrestore(&fb->lock, flags);
181}
182
183/* Load cursor information */
184static void tcx_setcursor (struct fb_info_sbusfb *fb)
185{
186	struct cg_cursor *c = &fb->cursor;
187	unsigned long flags;
188	unsigned int v;
189
190	spin_lock_irqsave(&fb->lock, flags);
191	if (c->enable)
192		v = ((c->cpos.fbx - c->chot.fbx) << 16)
193		    |((c->cpos.fby - c->chot.fby) & 0xffff);
194	else
195		/* Magic constant to turn off the cursor */
196		v = ((65536-32) << 16) | (65536-32);
197	sbus_writel(v, &fb->s.tcx.thc->thc_cursxy);
198	spin_unlock_irqrestore(&fb->lock, flags);
199}
200
201static void tcx_blank (struct fb_info_sbusfb *fb)
202{
203	unsigned long flags;
204	u32 tmp;
205
206	spin_lock_irqsave(&fb->lock, flags);
207	tmp = sbus_readl(&fb->s.tcx.thc->thc_misc);
208	tmp &= ~TCX_THC_MISC_VIDEO;
209	/* This should put us in power-save */
210	tmp |= TCX_THC_MISC_VSYNC_DIS;
211        tmp |= TCX_THC_MISC_HSYNC_DIS;
212	sbus_writel(tmp, &fb->s.tcx.thc->thc_misc);
213	spin_unlock_irqrestore(&fb->lock, flags);
214}
215
216static void tcx_unblank (struct fb_info_sbusfb *fb)
217{
218	unsigned long flags;
219	u32 tmp;
220
221	spin_lock_irqsave(&fb->lock, flags);
222	tmp = sbus_readl(&fb->s.tcx.thc->thc_misc);
223	tmp &= ~TCX_THC_MISC_VSYNC_DIS;
224	tmp &= ~TCX_THC_MISC_HSYNC_DIS;
225	tmp |= TCX_THC_MISC_VIDEO;
226	sbus_writel(tmp, &fb->s.tcx.thc->thc_misc);
227	spin_unlock_irqrestore(&fb->lock, flags);
228}
229
230static void tcx_reset (struct fb_info_sbusfb *fb)
231{
232	unsigned long flags;
233	u32 tmp;
234
235	spin_lock_irqsave(&fb->lock, flags);
236	if (fb->open && fb->mmaped)
237		__tcx_set_control_plane(fb);
238
239	/* Turn off stuff in the Transform Engine. */
240	sbus_writel(0, &fb->s.tcx.tec->tec_matrix);
241	sbus_writel(0, &fb->s.tcx.tec->tec_clip);
242	sbus_writel(0, &fb->s.tcx.tec->tec_vdc);
243
244	/* Enable cursor in Brooktree DAC. */
245	sbus_writel(0x06 << 24, &fb->s.tcx.bt->addr);
246	tmp = sbus_readl(&fb->s.tcx.bt->control);
247	tmp |= 0x03 << 24;
248	sbus_writel(tmp, &fb->s.tcx.bt->control);
249	spin_unlock_irqrestore(&fb->lock, flags);
250}
251
252static void tcx_margins (struct fb_info_sbusfb *fb, struct display *p, int x_margin, int y_margin)
253{
254	p->screen_base += (y_margin - fb->y_margin) * p->line_length + (x_margin - fb->x_margin);
255}
256
257static char idstring[60] __initdata = { 0 };
258
259char __init *tcxfb_init(struct fb_info_sbusfb *fb)
260{
261	struct fb_fix_screeninfo *fix = &fb->fix;
262	struct display *disp = &fb->disp;
263	struct fbtype *type = &fb->type;
264	struct sbus_dev *sdev = fb->sbdp;
265	unsigned long phys = sdev->reg_addrs[0].phys_addr;
266	int lowdepth, i, j;
267
268#ifndef FBCON_HAS_CFB8
269	return NULL;
270#endif
271
272	lowdepth = prom_getbool (fb->prom_node, "tcx-8-bit");
273
274	if (lowdepth) {
275		strcpy(fb->info.modename, "TCX8");
276		strcpy(fix->id, "TCX8");
277	} else {
278		strcpy(fb->info.modename, "TCX24");
279		strcpy(fix->id, "TCX24");
280	}
281	fix->line_length = fb->var.xres_virtual;
282	fix->accel = FB_ACCEL_SUN_TCX;
283
284	disp->scrollmode = SCROLL_YREDRAW;
285	if (!disp->screen_base) {
286		disp->screen_base = (char *)
287			sbus_ioremap(&sdev->resource[0], 0,
288				     type->fb_size, "tcx ram");
289	}
290	disp->screen_base += fix->line_length * fb->y_margin + fb->x_margin;
291	fb->s.tcx.tec = (struct tcx_tec *)
292		sbus_ioremap(&sdev->resource[7], 0,
293			     sizeof(struct tcx_tec), "tcx tec");
294	fb->s.tcx.thc = (struct tcx_thc *)
295		sbus_ioremap(&sdev->resource[9], 0,
296			     sizeof(struct tcx_thc), "tcx thc");
297	fb->s.tcx.bt = (struct bt_regs *)
298		sbus_ioremap(&sdev->resource[8], 0,
299			     sizeof(struct bt_regs), "tcx dac");
300	if (!lowdepth) {
301		fb->s.tcx.cplane = (u32 *)
302			sbus_ioremap(&sdev->resource[4], 0,
303				     type->fb_size * sizeof(u32), "tcx cplane");
304		type->fb_depth = 24;
305		fb->switch_from_graph = tcx_switch_from_graph;
306	} else {
307		/* As there can be one tcx in a machine only, we can write directly into
308		   tcx_mmap_map */
309		tcx_mmap_map[1].size = SBUS_MMAP_EMPTY;
310		tcx_mmap_map[4].size = SBUS_MMAP_EMPTY;
311		tcx_mmap_map[5].size = SBUS_MMAP_EMPTY;
312		tcx_mmap_map[6].size = SBUS_MMAP_EMPTY;
313	}
314	fb->dispsw = fbcon_cfb8;
315
316	fb->margins = tcx_margins;
317	fb->loadcmap = tcx_loadcmap;
318	if (prom_getbool (fb->prom_node, "hw-cursor")) {
319		fb->setcursor = tcx_setcursor;
320		fb->setcursormap = tcx_setcursormap;
321		fb->setcurshape = tcx_setcurshape;
322	}
323	fb->restore_palette = tcx_restore_palette;
324	fb->blank = tcx_blank;
325	fb->unblank = tcx_unblank;
326	fb->reset = tcx_reset;
327
328	fb->physbase = 0;
329	for (i = 0; i < 13; i++) {
330		/* tcx_mmap_map has to be sorted by voff, while
331		   order of phys registers from PROM differs a little
332		   bit. Here is the correction */
333		switch (i) {
334		case 10: j = 12; break;
335		case 11:
336		case 12: j = i - 1; break;
337		default: j = i; break;
338		}
339		tcx_mmap_map[i].poff = fb->sbdp->reg_addrs[j].phys_addr;
340	}
341	fb->mmap_map = tcx_mmap_map;
342
343	/* Initialize Brooktree DAC */
344	sbus_writel(0x04 << 24, &fb->s.tcx.bt->addr);         /* color planes */
345	sbus_writel(0xff << 24, &fb->s.tcx.bt->control);
346	sbus_writel(0x05 << 24, &fb->s.tcx.bt->addr);
347	sbus_writel(0x00 << 24, &fb->s.tcx.bt->control);
348	sbus_writel(0x06 << 24, &fb->s.tcx.bt->addr);         /* overlay plane */
349	sbus_writel(0x73 << 24, &fb->s.tcx.bt->control);
350	sbus_writel(0x07 << 24, &fb->s.tcx.bt->addr);
351	sbus_writel(0x00 << 24, &fb->s.tcx.bt->control);
352
353	sprintf(idstring, "tcx at %x.%08lx Rev %d.%d %s",
354		fb->iospace, phys,
355		((sbus_readl(&fb->s.tcx.thc->thc_rev) >> TCX_THC_REV_REV_SHIFT) &
356		 TCX_THC_REV_REV_MASK),
357		((sbus_readl(&fb->s.tcx.thc->thc_rev) >> TCX_THC_REV_MINREV_SHIFT) &
358		 TCX_THC_REV_MINREV_MASK),
359		lowdepth ? "8-bit only" : "24-bit depth");
360
361	tcx_reset(fb);
362
363	return idstring;
364}
365
366MODULE_LICENSE("GPL");
367