1 /* $Id: bwtwofb.c,v 1.15 2001/09/19 00:04:33 davem Exp $
2 * bwtwofb.c: BWtwo frame buffer driver
3 *
4 * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
5 * Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
6 * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be)
7 * Copyright (C) 1998 Pavel Machek (pavel@ucw.cz)
8 */
9
10 #include <linux/config.h>
11 #include <linux/module.h>
12 #include <linux/sched.h>
13 #include <linux/kernel.h>
14 #include <linux/errno.h>
15 #include <linux/string.h>
16 #include <linux/mm.h>
17 #include <linux/tty.h>
18 #include <linux/slab.h>
19 #include <linux/vmalloc.h>
20 #include <linux/delay.h>
21 #include <linux/interrupt.h>
22 #include <linux/fb.h>
23 #include <linux/init.h>
24 #include <linux/selection.h>
25
26 #include <video/sbusfb.h>
27 #include <asm/io.h>
28 #if !defined(__sparc_v9__) && !defined(__mc68000__)
29 #include <asm/sun4paddr.h>
30 #endif
31
32 #include <video/fbcon-mfb.h>
33
34 /* OBio addresses for the bwtwo registers */
35 #define BWTWO_REGISTER_OFFSET 0x400000
36
37 struct bw2_regs {
38 struct bt_regs bt;
39 volatile u8 control;
40 volatile u8 status;
41 volatile u8 cursor_start;
42 volatile u8 cursor_end;
43 volatile u8 h_blank_start;
44 volatile u8 h_blank_end;
45 volatile u8 h_sync_start;
46 volatile u8 h_sync_end;
47 volatile u8 comp_sync_end;
48 volatile u8 v_blank_start_high;
49 volatile u8 v_blank_start_low;
50 volatile u8 v_blank_end;
51 volatile u8 v_sync_start;
52 volatile u8 v_sync_end;
53 volatile u8 xfer_holdoff_start;
54 volatile u8 xfer_holdoff_end;
55 };
56
57 /* Status Register Constants */
58 #define BWTWO_SR_RES_MASK 0x70
59 #define BWTWO_SR_1600_1280 0x50
60 #define BWTWO_SR_1152_900_76_A 0x40
61 #define BWTWO_SR_1152_900_76_B 0x60
62 #define BWTWO_SR_ID_MASK 0x0f
63 #define BWTWO_SR_ID_MONO 0x02
64 #define BWTWO_SR_ID_MONO_ECL 0x03
65 #define BWTWO_SR_ID_MSYNC 0x04
66 #define BWTWO_SR_ID_NOCONN 0x0a
67
68 /* Control Register Constants */
69 #define BWTWO_CTL_ENABLE_INTS 0x80
70 #define BWTWO_CTL_ENABLE_VIDEO 0x40
71 #define BWTWO_CTL_ENABLE_TIMING 0x20
72 #define BWTWO_CTL_ENABLE_CURCMP 0x10
73 #define BWTWO_CTL_XTAL_MASK 0x0C
74 #define BWTWO_CTL_DIVISOR_MASK 0x03
75
76 /* Status Register Constants */
77 #define BWTWO_STAT_PENDING_INT 0x80
78 #define BWTWO_STAT_MSENSE_MASK 0x70
79 #define BWTWO_STAT_ID_MASK 0x0f
80
81 static struct sbus_mmap_map bw2_mmap_map[] = {
82 { 0, 0, SBUS_MMAP_FBSIZE(1) },
83 { 0, 0, 0 }
84 };
85
bw2_blank(struct fb_info_sbusfb * fb)86 static void bw2_blank (struct fb_info_sbusfb *fb)
87 {
88 unsigned long flags;
89 u8 tmp;
90
91 spin_lock_irqsave(&fb->lock, flags);
92 tmp = sbus_readb(&fb->s.bw2.regs->control);
93 tmp &= ~BWTWO_CTL_ENABLE_VIDEO;
94 sbus_writeb(tmp, &fb->s.bw2.regs->control);
95 spin_unlock_irqrestore(&fb->lock, flags);
96 }
97
bw2_unblank(struct fb_info_sbusfb * fb)98 static void bw2_unblank (struct fb_info_sbusfb *fb)
99 {
100 unsigned long flags;
101 u8 tmp;
102
103 spin_lock_irqsave(&fb->lock, flags);
104 tmp = sbus_readb(&fb->s.bw2.regs->control);
105 tmp |= BWTWO_CTL_ENABLE_VIDEO;
106 sbus_writeb(tmp, &fb->s.bw2.regs->control);
107 spin_unlock_irqrestore(&fb->lock, flags);
108 }
109
bw2_margins(struct fb_info_sbusfb * fb,struct display * p,int x_margin,int y_margin)110 static void bw2_margins (struct fb_info_sbusfb *fb, struct display *p,
111 int x_margin, int y_margin)
112 {
113 p->screen_base += (y_margin - fb->y_margin) *
114 p->line_length + ((x_margin - fb->x_margin) >> 3);
115 }
116
117 static u8 bw2regs_1600[] __initdata = {
118 0x14, 0x8b, 0x15, 0x28, 0x16, 0x03, 0x17, 0x13,
119 0x18, 0x7b, 0x19, 0x05, 0x1a, 0x34, 0x1b, 0x2e,
120 0x1c, 0x00, 0x1d, 0x0a, 0x1e, 0xff, 0x1f, 0x01,
121 0x10, 0x21, 0
122 };
123
124 static u8 bw2regs_ecl[] __initdata = {
125 0x14, 0x65, 0x15, 0x1e, 0x16, 0x04, 0x17, 0x0c,
126 0x18, 0x5e, 0x19, 0x03, 0x1a, 0xa7, 0x1b, 0x23,
127 0x1c, 0x00, 0x1d, 0x08, 0x1e, 0xff, 0x1f, 0x01,
128 0x10, 0x20, 0
129 };
130
131 static u8 bw2regs_analog[] __initdata = {
132 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x03, 0x17, 0x13,
133 0x18, 0xb0, 0x19, 0x03, 0x1a, 0xa6, 0x1b, 0x22,
134 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
135 0x10, 0x20, 0
136 };
137
138 static u8 bw2regs_76hz[] __initdata = {
139 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f,
140 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a,
141 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01,
142 0x10, 0x24, 0
143 };
144
145 static u8 bw2regs_66hz[] __initdata = {
146 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14,
147 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24,
148 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01,
149 0x10, 0x20, 0
150 };
151
152 static char idstring[60] __initdata = { 0 };
153
bwtwofb_init(struct fb_info_sbusfb * fb)154 char __init *bwtwofb_init(struct fb_info_sbusfb *fb)
155 {
156 struct fb_fix_screeninfo *fix = &fb->fix;
157 struct display *disp = &fb->disp;
158 struct fbtype *type = &fb->type;
159 #ifdef CONFIG_SUN4
160 unsigned long phys = sun4_bwtwo_physaddr;
161 struct resource res;
162 #else
163 unsigned long phys = fb->sbdp->reg_addrs[0].phys_addr;
164 #endif
165 struct resource *resp;
166 unsigned int vaddr;
167
168 #ifndef FBCON_HAS_MFB
169 return NULL;
170 #endif
171
172 #ifdef CONFIG_SUN4
173 res.start = phys;
174 res.end = res.start + BWTWO_REGISTER_OFFSET + sizeof(struct bw2_regs) - 1;
175 res.flags = IORESOURCE_IO | (fb->iospace & 0xff);
176 resp = &res;
177 #else
178 resp = &fb->sbdp->resource[0];
179 #endif
180 if (!fb->s.bw2.regs) {
181 fb->s.bw2.regs = (struct bw2_regs *)
182 sbus_ioremap(resp, BWTWO_REGISTER_OFFSET,
183 sizeof(struct bw2_regs), "bw2 regs");
184 if ((!ARCH_SUN4) && (!prom_getbool(fb->prom_node, "width"))) {
185 /* Ugh, broken PROM didn't initialize us.
186 * Let's deal with this ourselves.
187 */
188 u8 status, mon;
189 u8 *p;
190 int sizechange = 0;
191
192 status = sbus_readb(&fb->s.bw2.regs->status);
193 mon = status & BWTWO_SR_RES_MASK;
194 switch (status & BWTWO_SR_ID_MASK) {
195 case BWTWO_SR_ID_MONO_ECL:
196 if (mon == BWTWO_SR_1600_1280) {
197 p = bw2regs_1600;
198 fb->type.fb_width = 1600;
199 fb->type.fb_height = 1280;
200 sizechange = 1;
201 } else
202 p = bw2regs_ecl;
203 break;
204 case BWTWO_SR_ID_MONO:
205 p = bw2regs_analog;
206 break;
207 case BWTWO_SR_ID_MSYNC:
208 if (mon == BWTWO_SR_1152_900_76_A ||
209 mon == BWTWO_SR_1152_900_76_B)
210 p = bw2regs_76hz;
211 else
212 p = bw2regs_66hz;
213 break;
214 case BWTWO_SR_ID_NOCONN:
215 return NULL;
216 default:
217 #ifndef CONFIG_FB_SUN3
218 prom_printf("bw2: can't handle SR %02x\n",
219 status);
220 prom_halt();
221 #endif
222 return NULL; /* fool gcc. */
223 }
224 for ( ; *p; p += 2) {
225 u8 *regp = &((u8 *)fb->s.bw2.regs)[p[0]];
226 sbus_writeb(p[1], regp);
227 }
228 }
229 }
230
231 strcpy(fb->info.modename, "BWtwo");
232 strcpy(fix->id, "BWtwo");
233 fix->line_length = fb->var.xres_virtual >> 3;
234 fix->accel = FB_ACCEL_SUN_BWTWO;
235
236 disp->scrollmode = SCROLL_YREDRAW;
237 disp->inverse = 1;
238 if (!disp->screen_base) {
239 disp->screen_base = (char *)
240 sbus_ioremap(resp, 0, type->fb_size, "bw2 ram");
241 }
242 disp->screen_base += fix->line_length * fb->y_margin + (fb->x_margin >> 3);
243 fb->dispsw = fbcon_mfb;
244 fix->visual = FB_VISUAL_MONO01;
245
246 #ifndef CONFIG_SUN4
247 fb->blank = bw2_blank;
248 fb->unblank = bw2_unblank;
249
250 prom_getproperty(fb->sbdp->prom_node, "address",
251 (char *)&vaddr, sizeof(vaddr));
252 fb->physbase = __get_phys((unsigned long)vaddr);
253
254 #endif
255 fb->margins = bw2_margins;
256 fb->mmap_map = bw2_mmap_map;
257
258 #ifdef __sparc_v9__
259 sprintf(idstring, "bwtwo at %016lx", phys);
260 #else
261 sprintf(idstring, "bwtwo at %x.%08lx", fb->iospace, phys);
262 #endif
263
264 return idstring;
265 }
266
267 MODULE_LICENSE("GPL");
268