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