1 /*
2  *      linux/drivers/video/pmag-ba-fb.c
3  *
4  *	PMAG-BA TurboChannel framebuffer card support ... derived from:
5  *	"HP300 Topcat framebuffer support (derived from macfb of all things)
6  *	Phil Blundell <philb@gnu.org> 1998", the original code can be
7  *      found in the file hpfb.c in the same directory.
8  *
9  *	Based on digital document:
10  * 	"PMAG-BA TURBOchannel Color Frame Buffer
11  *	 Functional Specification", Revision 1.2, August 27, 1990
12  *
13  *      DECstation related code Copyright (C) 1999, 2000, 2001 by
14  *      Michael Engel <engel@unix-ag.org>,
15  *      Karsten Merker <merker@linuxtag.org> and
16  *	Harald Koerfgen.
17  *      This file is subject to the terms and conditions of the GNU General
18  *      Public License.  See the file COPYING in the main directory of this
19  *      archive for more details.
20  *
21  */
22 #include <linux/module.h>
23 #include <linux/kernel.h>
24 #include <linux/sched.h>
25 #include <linux/errno.h>
26 #include <linux/string.h>
27 #include <linux/timer.h>
28 #include <linux/mm.h>
29 #include <linux/tty.h>
30 #include <linux/slab.h>
31 #include <linux/delay.h>
32 #include <linux/init.h>
33 #include <linux/fb.h>
34 #include <asm/bootinfo.h>
35 #include <asm/dec/machtype.h>
36 #include <asm/dec/tc.h>
37 #include "pmag-ba-fb.h"
38 
39 #include <video/fbcon.h>
40 #include <video/fbcon-mfb.h>
41 #include <video/fbcon-cfb2.h>
42 #include <video/fbcon-cfb4.h>
43 #include <video/fbcon-cfb8.h>
44 
45 #define arraysize(x)    (sizeof(x)/sizeof(*(x)))
46 
47 struct pmag_ba_ramdac_regs {
48 	unsigned char addr_low;
49 	unsigned char pad0[3];
50 	unsigned char addr_hi;
51 	unsigned char pad1[3];
52 	unsigned char data;
53 	unsigned char pad2[3];
54 	unsigned char cmap;
55 };
56 
57 struct pmag_ba_my_fb_info {
58 	struct fb_info info;
59 	struct pmag_ba_ramdac_regs *bt459_regs;
60 	unsigned long pmagba_fb_start;
61 	unsigned long pmagba_fb_size;
62 	unsigned long pmagba_fb_line_length;
63 };
64 
65 static struct display disp;
66 /*
67  * Max 3 TURBOchannel slots -> max 3 PMAG-BA :)
68  */
69 static struct pmag_ba_my_fb_info pmagba_fb_info[3];
70 
71 static struct fb_var_screeninfo pmagbafb_defined = {
72 	0, 0, 0, 0,		/* W,H, W, H (virtual) load xres,xres_virtual */
73 	0, 0,			/* virtual -> visible no offset */
74 	0,			/* depth -> load bits_per_pixel */
75 	0,			/* greyscale ? */
76 	{0, 0, 0},		/* R */
77 	{0, 0, 0},		/* G */
78 	{0, 0, 0},		/* B */
79 	{0, 0, 0},		/* transparency */
80 	0,			/* standard pixel format */
81 	FB_ACTIVATE_NOW,
82 	274, 195,		/* 14" monitor */
83 	FB_ACCEL_NONE,
84 	0L, 0L, 0L, 0L, 0L,
85 	0L, 0L, 0,		/* No sync info */
86 	FB_VMODE_NONINTERLACED,
87 	{0, 0, 0, 0, 0, 0}
88 };
89 
90 struct pmagbafb_par {
91 };
92 
93 static int currcon = 0;
94 static struct pmagbafb_par current_par;
95 
pmagbafb_encode_var(struct fb_var_screeninfo * var,struct pmagbafb_par * par)96 static void pmagbafb_encode_var(struct fb_var_screeninfo *var,
97 				struct pmagbafb_par *par)
98 {
99 	int i = 0;
100 	var->xres = 1024;
101 	var->yres = 864;
102 	var->xres_virtual = 1024;
103 	var->yres_virtual = 864;
104 	var->xoffset = 0;
105 	var->yoffset = 0;
106 	var->bits_per_pixel = 8;
107 	var->grayscale = 0;
108 	var->red.offset = 0;
109 	var->red.length = 8;
110 	var->red.msb_right = 0;
111 	var->green.offset = 0;
112 	var->green.length = 8;
113 	var->green.msb_right = 0;
114 	var->blue.offset = 0;
115 	var->blue.length = 8;
116 	var->blue.msb_right = 0;
117 	var->transp.offset = 0;
118 	var->transp.length = 0;
119 	var->transp.msb_right = 0;
120 	var->nonstd = 0;
121 	var->activate = 1;
122 	var->height = -1;
123 	var->width = -1;
124 	var->vmode = FB_VMODE_NONINTERLACED;
125 	var->pixclock = 0;
126 	var->sync = 0;
127 	var->left_margin = 0;
128 	var->right_margin = 0;
129 	var->upper_margin = 0;
130 	var->lower_margin = 0;
131 	var->hsync_len = 0;
132 	var->vsync_len = 0;
133 	for (i = 0; i < arraysize(var->reserved); i++)
134 		var->reserved[i] = 0;
135 }
136 
pmagbafb_get_par(struct pmagbafb_par * par)137 static void pmagbafb_get_par(struct pmagbafb_par *par)
138 {
139 	*par = current_par;
140 }
141 
pmagba_fb_update_var(int con,struct fb_info * info)142 static int pmagba_fb_update_var(int con, struct fb_info *info)
143 {
144 	return 0;
145 }
146 
pmagba_do_fb_set_var(struct fb_var_screeninfo * var,int isactive)147 static int pmagba_do_fb_set_var(struct fb_var_screeninfo *var,
148 				int isactive)
149 {
150 	struct pmagbafb_par par;
151 
152 	pmagbafb_get_par(&par);
153 	pmagbafb_encode_var(var, &par);
154 	return 0;
155 }
156 
157 /*
158  * Turn hardware cursor off
159  */
pmagbafb_erase_cursor(struct pmag_ba_my_fb_info * info)160 void pmagbafb_erase_cursor(struct pmag_ba_my_fb_info *info)
161 {
162 	info->bt459_regs->addr_low = 0;
163 	info->bt459_regs->addr_hi = 3;
164 	info->bt459_regs->data = 0;
165 }
166 
167 /*
168  * Write to a Bt459 color map register
169  */
pmag_ba_bt459_write_colormap(struct pmag_ba_my_fb_info * info,int reg,__u8 red,__u8 green,__u8 blue)170 void pmag_ba_bt459_write_colormap(struct pmag_ba_my_fb_info *info,
171 				  int reg, __u8 red, __u8 green, __u8 blue)
172 {
173 	info->bt459_regs->addr_low = (__u8) reg;
174 	info->bt459_regs->addr_hi = 0;
175 	info->bt459_regs->cmap = red;
176 	info->bt459_regs->cmap = green;
177 	info->bt459_regs->cmap = blue;
178 }
179 
180 /*
181  * Get the palette
182  */
183 
pmagbafb_get_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)184 static int pmagbafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
185 			     struct fb_info *info)
186 {
187 	unsigned int i;
188 	unsigned int length;
189 
190 	if (((cmap->start) + (cmap->len)) >= 256) {
191 		length = 256 - (cmap->start);
192 	} else {
193 		length = cmap->len;
194 	}
195 	for (i = 0; i < length; i++) {
196 		/*
197 		 * TODO
198 		 */
199 	}
200 	return 0;
201 }
202 
203 /*
204  * Set the palette.
205  */
pmagbafb_set_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)206 static int pmagbafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
207 			     struct fb_info *info)
208 {
209 	unsigned int i;
210 	__u8 cmap_red, cmap_green, cmap_blue;
211 	unsigned int length;
212 
213 	if (((cmap->start) + (cmap->len)) >= 256)
214 		length = 256 - (cmap->start);
215 	else
216 		length = cmap->len;
217 
218 	for (i = 0; i < length; i++) {
219 		cmap_red = ((cmap->red[i]) >> 8);	/* The cmap fields are 16 bits    */
220 		cmap_green = ((cmap->green[i]) >> 8);	/* wide, but the harware colormap */
221 		cmap_blue = ((cmap->blue[i]) >> 8);	/* registers are only 8 bits wide */
222 
223 		pmag_ba_bt459_write_colormap((struct pmag_ba_my_fb_info *)
224 					     info, cmap->start + i,
225 					     cmap_red, cmap_green,
226 					     cmap_blue);
227 	}
228 	return 0;
229 }
230 
pmagbafb_get_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)231 static int pmagbafb_get_var(struct fb_var_screeninfo *var, int con,
232 			    struct fb_info *info)
233 {
234 	struct pmagbafb_par par;
235 	if (con == -1) {
236 		pmagbafb_get_par(&par);
237 		pmagbafb_encode_var(var, &par);
238 	} else
239 		*var = fb_display[con].var;
240 	return 0;
241 }
242 
243 
pmagbafb_set_var(struct fb_var_screeninfo * var,int con,struct fb_info * info)244 static int pmagbafb_set_var(struct fb_var_screeninfo *var, int con,
245 			    struct fb_info *info)
246 {
247 	int err;
248 
249 	if ((err = pmagba_do_fb_set_var(var, 1)))
250 		return err;
251 	return 0;
252 }
253 
pmagbafb_encode_fix(struct fb_fix_screeninfo * fix,struct pmagbafb_par * par,struct pmag_ba_my_fb_info * info)254 static void pmagbafb_encode_fix(struct fb_fix_screeninfo *fix,
255 				struct pmagbafb_par *par,
256 				struct pmag_ba_my_fb_info *info)
257 {
258 	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
259 	strcpy(fix->id, "PMAG-BA");
260 
261 	fix->smem_start = info->pmagba_fb_start;
262 	fix->smem_len = info->pmagba_fb_size;
263 	fix->type = FB_TYPE_PACKED_PIXELS;
264 	fix->visual = FB_VISUAL_PSEUDOCOLOR;
265 	fix->xpanstep = 0;
266 	fix->ypanstep = 0;
267 	fix->ywrapstep = 0;
268 	fix->line_length = info->pmagba_fb_line_length;
269 }
270 
pmagbafb_get_fix(struct fb_fix_screeninfo * fix,int con,struct fb_info * info)271 static int pmagbafb_get_fix(struct fb_fix_screeninfo *fix, int con,
272 			    struct fb_info *info)
273 {
274 	struct pmagbafb_par par;
275 
276 	pmagbafb_get_par(&par);
277 	pmagbafb_encode_fix(fix, &par, (struct pmag_ba_my_fb_info *) info);
278 
279 	return 0;
280 }
281 
282 
pmagbafb_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg,int con,struct fb_info * info)283 static int pmagbafb_ioctl(struct inode *inode, struct file *file,
284 			  unsigned int cmd, unsigned long arg, int con,
285 			  struct fb_info *info)
286 {
287 	return -EINVAL;
288 }
289 
pmagbafb_switch(int con,struct fb_info * info)290 static int pmagbafb_switch(int con, struct fb_info *info)
291 {
292 	pmagba_do_fb_set_var(&fb_display[con].var, 1);
293 	currcon = con;
294 
295 	return 0;
296 }
297 
298 /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
299 
pmagbafb_blank(int blank,struct fb_info * info)300 static void pmagbafb_blank(int blank, struct fb_info *info)
301 {
302 	/* Not supported */
303 }
304 
pmagbafb_open(struct fb_info * info,int user)305 static int pmagbafb_open(struct fb_info *info, int user)
306 {
307 	/*
308 	 * Nothing, only a usage count for the moment
309 	 */
310 	MOD_INC_USE_COUNT;
311 	return (0);
312 }
313 
pmagbafb_set_disp(int con,struct pmag_ba_my_fb_info * info)314 static void pmagbafb_set_disp(int con, struct pmag_ba_my_fb_info *info)
315 {
316 	struct fb_fix_screeninfo fix;
317 	struct display *display;
318 
319 	if (con >= 0)
320 		display = &fb_display[con];
321 	else
322 		display = &disp;	/* used during initialization */
323 
324 	pmagbafb_get_fix(&fix, con, (struct fb_info *) info);
325 
326 	display->screen_base = (char *) fix.smem_start;
327 	display->visual = fix.visual;
328 	display->type = fix.type;
329 	display->type_aux = fix.type_aux;
330 	display->ypanstep = fix.ypanstep;
331 	display->ywrapstep = fix.ywrapstep;
332 	display->line_length = fix.line_length;
333 	display->next_line = fix.line_length;
334 	display->can_soft_blank = 0;
335 	display->inverse = 0;
336 	display->scrollmode = SCROLL_YREDRAW;
337 	display->dispsw = &fbcon_cfb8;
338 }
339 
pmagbafb_release(struct fb_info * info,int user)340 static int pmagbafb_release(struct fb_info *info, int user)
341 {
342 	MOD_DEC_USE_COUNT;
343 	return (0);
344 }
345 
346 static struct fb_ops pmagbafb_ops = {
347 	owner:THIS_MODULE,
348 	fb_open:pmagbafb_open,
349 	fb_release:pmagbafb_release,
350 	fb_get_fix:pmagbafb_get_fix,
351 	fb_get_var:pmagbafb_get_var,
352 	fb_set_var:pmagbafb_set_var,
353 	fb_get_cmap:pmagbafb_get_cmap,
354 	fb_set_cmap:pmagbafb_set_cmap,
355 	fb_ioctl:pmagbafb_ioctl,
356 	fb_mmap:0,
357 	fb_rasterimg:0
358 };
359 
pmagbafb_init_one(int slot)360 int __init pmagbafb_init_one(int slot)
361 {
362 	unsigned long base_addr = get_tc_base_addr(slot);
363 	struct pmag_ba_my_fb_info *ip =
364 	    (struct pmag_ba_my_fb_info *) &pmagba_fb_info[slot];
365 
366 	printk("PMAG-BA framebuffer in slot %d\n", slot);
367 
368 	/*
369 	 * Framebuffer display memory base address and friends
370 	 */
371 	ip->bt459_regs =
372 	    (struct pmag_ba_ramdac_regs *) (base_addr +
373 					    PMAG_BA_BT459_OFFSET);
374 	ip->pmagba_fb_start = base_addr + PMAG_BA_ONBOARD_FBMEM_OFFSET;
375 	ip->pmagba_fb_size = 1024 * 864;
376 	ip->pmagba_fb_line_length = 1024;
377 
378 	/*
379 	 * Configure the Bt459 RAM DAC
380 	 */
381 	pmagbafb_erase_cursor(ip);
382 
383 	/*
384 	 *      Fill in the available video resolution
385 	 */
386 
387 	pmagbafb_defined.xres = 1024;
388 	pmagbafb_defined.yres = 864;
389 	pmagbafb_defined.xres_virtual = 1024;
390 	pmagbafb_defined.yres_virtual = 864;
391 	pmagbafb_defined.bits_per_pixel = 8;
392 
393 	/*
394 	 *      Let there be consoles..
395 	 */
396 	strcpy(ip->info.modename, "PMAG-BA");
397 	ip->info.changevar = NULL;
398 	ip->info.node = -1;
399 	ip->info.fbops = &pmagbafb_ops;
400 	ip->info.disp = &disp;
401 	ip->info.switch_con = &pmagbafb_switch;
402 	ip->info.updatevar = &pmagba_fb_update_var;
403 	ip->info.blank = &pmagbafb_blank;
404 	ip->info.flags = FBINFO_FLAG_DEFAULT;
405 
406 	pmagba_do_fb_set_var(&pmagbafb_defined, 1);
407 	pmagbafb_get_var(&disp.var, -1, (struct fb_info *) ip);
408 	pmagbafb_set_disp(-1, ip);
409 
410 	if (register_framebuffer((struct fb_info *) ip) < 0)
411 		return 1;
412 
413 	return 0;
414 }
415 
416 /*
417  * Initialise the framebuffer
418  */
419 
pmagbafb_init(void)420 int __init pmagbafb_init(void)
421 {
422 	int sid;
423 	int found = 0;
424 
425 	if (TURBOCHANNEL) {
426 		while ((sid = search_tc_card("PMAG-BA")) >= 0) {
427 			found = 1;
428 			claim_tc_card(sid);
429 			pmagbafb_init_one(sid);
430 		}
431 		return found ? 0 : -ENODEV;
432 	} else {
433 		return -ENODEV;
434 	}
435 }
436 
437 MODULE_LICENSE("GPL");
438