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