1 /* $Id: tcxfb.c,v 1.13 2001/09/19 00:04:33 davem 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 */
50 struct tcx_tec {
51 	volatile u32 tec_matrix;
52 	volatile u32 tec_clip;
53 	volatile u32 tec_vdc;
54 };
55 
56 struct 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 
72 static 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 
__tcx_set_control_plane(struct fb_info_sbusfb * fb)89 static 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 
tcx_switch_from_graph(struct fb_info_sbusfb * fb)104 static 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 
tcx_loadcmap(struct fb_info_sbusfb * fb,struct display * p,int index,int count)117 static 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 
tcx_restore_palette(struct fb_info_sbusfb * fb)134 static 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 
tcx_setcursormap(struct fb_info_sbusfb * fb,u8 * red,u8 * green,u8 * blue)147 static 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 */
tcx_setcurshape(struct fb_info_sbusfb * fb)169 static 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 */
tcx_setcursor(struct fb_info_sbusfb * fb)184 static 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 
tcx_blank(struct fb_info_sbusfb * fb)201 static 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 
tcx_unblank(struct fb_info_sbusfb * fb)216 static 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 
tcx_reset(struct fb_info_sbusfb * fb)230 static 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 
tcx_margins(struct fb_info_sbusfb * fb,struct display * p,int x_margin,int y_margin)252 static 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 
257 static char idstring[60] __initdata = { 0 };
258 
tcxfb_init(struct fb_info_sbusfb * fb)259 char __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 
366 MODULE_LICENSE("GPL");
367