1 /*
2  *  platinumfb.c -- frame buffer device for the PowerMac 'platinum' display
3  *
4  *  Copyright (C) 1998 Franz Sirl
5  *
6  *  Frame buffer structure from:
7  *    drivers/video/controlfb.c -- frame buffer device for
8  *    Apple 'control' display chip.
9  *    Copyright (C) 1998 Dan Jacobowitz
10  *
11  *  Hardware information from:
12  *    platinum.c: Console support for PowerMac "platinum" display adaptor.
13  *    Copyright (C) 1996 Paul Mackerras and Mark Abene
14  *
15  *  This file is subject to the terms and conditions of the GNU General Public
16  *  License. See the file COPYING in the main directory of this archive for
17  *  more details.
18  */
19 
20 #include <linux/config.h>
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/errno.h>
24 #include <linux/string.h>
25 #include <linux/mm.h>
26 #include <linux/tty.h>
27 #include <linux/slab.h>
28 #include <linux/vmalloc.h>
29 #include <linux/delay.h>
30 #include <linux/interrupt.h>
31 #include <linux/fb.h>
32 #include <linux/selection.h>
33 #include <linux/init.h>
34 #include <linux/pci.h>
35 #include <linux/nvram.h>
36 #ifdef CONFIG_FB_COMPAT_XPMAC
37 #include <asm/vc_ioctl.h>
38 #endif
39 #include <asm/io.h>
40 #include <asm/prom.h>
41 #include <asm/pgtable.h>
42 
43 #include <video/fbcon.h>
44 #include <video/fbcon-cfb8.h>
45 #include <video/fbcon-cfb16.h>
46 #include <video/fbcon-cfb32.h>
47 #include <video/macmodes.h>
48 
49 #include "platinumfb.h"
50 
51 static char fontname[40] __initdata = { 0 };
52 
53 static int currcon = 0;
54 
55 static int default_vmode = VMODE_NVRAM;
56 static int default_cmode = CMODE_NVRAM;
57 
58 struct fb_par_platinum {
59 	int	vmode, cmode;
60 	int	xres, yres;
61 	int	vxres, vyres;
62 	int	xoffset, yoffset;
63 };
64 
65 struct fb_info_platinum {
66 	struct fb_info			fb_info;
67 	struct display			disp;
68 	struct fb_par_platinum		default_par;
69 	struct fb_par_platinum		current_par;
70 
71 	struct {
72 		__u8 red, green, blue;
73 	}				palette[256];
74 
75 	volatile struct cmap_regs	*cmap_regs;
76 	unsigned long			cmap_regs_phys;
77 
78 	volatile struct platinum_regs	*platinum_regs;
79 	unsigned long			platinum_regs_phys;
80 
81 	__u8				*frame_buffer;
82 	volatile __u8			*base_frame_buffer;
83 	unsigned long			frame_buffer_phys;
84 
85 	unsigned long			total_vram;
86 	int				clktype;
87 	int				dactype;
88 
89 	union {
90 #ifdef FBCON_HAS_CFB16
91 		u16 cfb16[16];
92 #endif
93 #ifdef FBCON_HAS_CFB32
94 		u32 cfb32[16];
95 #endif
96 	} fbcon_cmap;
97 };
98 
99 /*
100  * Frame buffer device API
101  */
102 
103 static int platinum_get_fix(struct fb_fix_screeninfo *fix, int con,
104 			    struct fb_info *fb);
105 static int platinum_get_var(struct fb_var_screeninfo *var, int con,
106 			    struct fb_info *fb);
107 static int platinum_set_var(struct fb_var_screeninfo *var, int con,
108 			    struct fb_info *fb);
109 static int platinum_get_cmap(struct fb_cmap *cmap, int kspc, int con,
110 			     struct fb_info *info);
111 static int platinum_set_cmap(struct fb_cmap *cmap, int kspc, int con,
112 			     struct fb_info *info);
113 
114 
115 /*
116  * Interface to the low level console driver
117  */
118 
119 static int platinum_switch(int con, struct fb_info *fb);
120 static int platinum_updatevar(int con, struct fb_info *fb);
121 static void platinum_blank(int blank, struct fb_info *fb);
122 
123 
124 /*
125  * internal functions
126  */
127 
128 static void platinum_of_init(struct device_node *dp);
129 static inline int platinum_vram_reqd(const struct fb_info_platinum* info,
130 					int video_mode,
131 					int color_mode);
132 static int read_platinum_sense(struct fb_info_platinum *info);
133 static void set_platinum_clock(struct fb_info_platinum *info);
134 static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_platinum *info);
135 static int platinum_par_to_var(struct fb_var_screeninfo *var,
136 			       const struct fb_par_platinum *par,
137 			       const struct fb_info_platinum *info);
138 static int platinum_var_to_par(const struct fb_var_screeninfo *var,
139 			       struct fb_par_platinum *par,
140 			       const struct fb_info_platinum *info);
141 static int platinum_encode_fix(struct fb_fix_screeninfo *fix,
142 			       const struct fb_par_platinum *par,
143 			       const struct fb_info_platinum *info);
144 static void platinum_set_dispsw(struct display *disp,
145 				struct fb_info_platinum *info, int cmode,
146 				int accel);
147 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
148 			      u_int *transp, struct fb_info *fb);
149 static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
150 			      u_int transp, struct fb_info *fb);
151 static void do_install_cmap(int con, struct fb_info *info);
152 
153 
154 /*
155  * Interface used by the world
156  */
157 
158 int platinum_init(void);
159 int platinum_setup(char*);
160 
161 static struct fb_ops platinumfb_ops = {
162 	owner:		THIS_MODULE,
163 	fb_get_fix:	platinum_get_fix,
164 	fb_get_var:	platinum_get_var,
165 	fb_set_var:	platinum_set_var,
166 	fb_get_cmap:	platinum_get_cmap,
167 	fb_set_cmap:	platinum_set_cmap,
168 };
169 
platinum_get_fix(struct fb_fix_screeninfo * fix,int con,struct fb_info * fb)170 static int platinum_get_fix(struct fb_fix_screeninfo *fix, int con,
171 			    struct fb_info *fb)
172 {
173 	const struct fb_info_platinum *info = (struct fb_info_platinum *)fb;
174 	struct fb_par_platinum par;
175 
176 	if (con == -1)
177 		par = info->default_par;
178 	else
179 		platinum_var_to_par(&fb_display[con].var, &par, info);
180 
181 	platinum_encode_fix(fix, &par, info);
182 	return 0;
183 }
184 
platinum_get_var(struct fb_var_screeninfo * var,int con,struct fb_info * fb)185 static int platinum_get_var(struct fb_var_screeninfo *var, int con,
186 			    struct fb_info *fb)
187 {
188 	const struct fb_info_platinum *info = (struct fb_info_platinum *)fb;
189 
190 	if (con == -1)
191 		platinum_par_to_var(var, &info->default_par, info);
192 	else
193 		*var = fb_display[con].var;
194 
195 	return 0;
196 }
197 
platinum_set_dispsw(struct display * disp,struct fb_info_platinum * info,int cmode,int accel)198 static void platinum_set_dispsw(struct display *disp,
199 				struct fb_info_platinum *info, int cmode,
200 				int accel)
201 {
202 	switch(cmode) {
203 #ifdef FBCON_HAS_CFB8
204 	    case CMODE_8:
205 		disp->dispsw = &fbcon_cfb8;
206 		break;
207 #endif
208 #ifdef FBCON_HAS_CFB16
209 	    case CMODE_16:
210 		disp->dispsw = &fbcon_cfb16;
211 		disp->dispsw_data = info->fbcon_cmap.cfb16;
212 		break;
213 #endif
214 #ifdef FBCON_HAS_CFB32
215 	    case CMODE_32:
216 		disp->dispsw = &fbcon_cfb32;
217 		disp->dispsw_data = info->fbcon_cmap.cfb32;
218 		break;
219 #endif
220 	    default:
221 		disp->dispsw = &fbcon_dummy;
222 		break;
223 	}
224 }
225 
platinum_set_var(struct fb_var_screeninfo * var,int con,struct fb_info * fb)226 static int platinum_set_var(struct fb_var_screeninfo *var, int con,
227 			    struct fb_info *fb)
228 {
229 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
230 	struct fb_par_platinum par;
231 	struct display *display;
232 	int oldxres, oldyres, oldvxres, oldvyres, oldbpp, err;
233 	int activate = var->activate;
234 	struct platinum_regvals *init;
235 
236 	display = (con >= 0) ? &fb_display[con] : fb->disp;
237 
238 	if((err = platinum_var_to_par(var, &par, info))) {
239 		printk(KERN_ERR "platinum_set_var: error calling platinum_var_to_par: %d.\n", err);
240 		return err;
241 	}
242 
243 	platinum_par_to_var(var, &par, info);
244 
245 	if ((activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) {
246 		printk(KERN_INFO "platinum_set_var: Not activating.\n");
247 		return 0;
248 	}
249 
250 	init = platinum_reg_init[par.vmode-1];
251 
252 	oldxres = display->var.xres;
253 	oldyres = display->var.yres;
254 	oldvxres = display->var.xres_virtual;
255 	oldvyres = display->var.yres_virtual;
256 	oldbpp = display->var.bits_per_pixel;
257 	display->var = *var;
258 
259 	if (oldxres != var->xres || oldyres != var->yres ||
260 	    oldvxres != var->xres_virtual || oldyres != var->yres_virtual ||
261 	    oldbpp != var->bits_per_pixel) {
262 	    struct fb_fix_screeninfo fix;
263 
264 	    platinum_encode_fix(&fix, &par, info);
265 	    display->screen_base = (char *) info->frame_buffer + init->fb_offset + 0x20;
266 	    display->visual = fix.visual;
267 	    display->type = fix.type;
268 	    display->type_aux = fix.type_aux;
269 	    display->ypanstep = fix.ypanstep;
270 	    display->ywrapstep = fix.ywrapstep;
271 	    display->line_length = fix.line_length;
272 	    display->can_soft_blank = 1;
273 	    display->inverse = 0;
274 	    platinum_set_dispsw(display, info, par.cmode, 0);
275 	    display->scrollmode = SCROLL_YREDRAW;
276 	    if (info->fb_info.changevar)
277 	      (*info->fb_info.changevar)(con);
278 	}
279 
280 	if (!info->fb_info.display_fg ||
281 	    info->fb_info.display_fg->vc_num == con)
282 		platinum_set_par(&par, info);
283 
284 	if (oldbpp != var->bits_per_pixel) {
285 	    if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
286 	      return err;
287 	    do_install_cmap(con, &info->fb_info);
288 	}
289 
290 	return 0;
291 }
292 
platinum_get_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)293 static int platinum_get_cmap(struct fb_cmap *cmap, int kspc, int con,
294 			     struct fb_info *info)
295 {
296 	if (!info->display_fg ||
297 	    info->display_fg->vc_num == con)	/* current console? */
298 		return fb_get_cmap(cmap, kspc, platinum_getcolreg, info);
299 	if (fb_display[con].cmap.len)	/* non default colormap? */
300 		fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
301 	else {
302 		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
303 		fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
304 	}
305 	return 0;
306 }
307 
platinum_set_cmap(struct fb_cmap * cmap,int kspc,int con,struct fb_info * info)308 static int platinum_set_cmap(struct fb_cmap *cmap, int kspc, int con,
309 			     struct fb_info *info)
310 {
311 	int err;
312 	struct display *disp;
313 
314 	if (con >= 0)
315 		disp = &fb_display[con];
316 	else
317 		disp = info->disp;
318 	if (!disp->cmap.len) {     /* no colormap allocated? */
319 		int size = disp->var.bits_per_pixel == 16 ? 32 : 256;
320 		if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
321 			return err;
322 	}
323 
324 	if (!info->display_fg ||
325 	    info->display_fg->vc_num == con)	/* current console? */
326 		return fb_set_cmap(cmap, kspc, platinum_setcolreg, info);
327 	else
328 		fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
329 	return 0;
330 }
331 
platinum_switch(int con,struct fb_info * fb)332 static int platinum_switch(int con, struct fb_info *fb)
333 {
334 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
335 	struct fb_par_platinum par;
336 
337 	if (fb_display[currcon].cmap.len)
338 		fb_get_cmap(&fb_display[currcon].cmap, 1, platinum_getcolreg,
339 			    fb);
340 	currcon = con;
341 
342 	platinum_var_to_par(&fb_display[con].var, &par, info);
343 	platinum_set_par(&par, info);
344 	platinum_set_dispsw(&fb_display[con], info, par.cmode, 0);
345 	do_install_cmap(con, fb);
346 
347 	return 1;
348 }
349 
platinum_updatevar(int con,struct fb_info * fb)350 static int platinum_updatevar(int con, struct fb_info *fb)
351 {
352 	printk(KERN_ERR "platinum_updatevar is doing nothing yet.\n");
353 	return 0;
354 }
355 
platinum_blank(int blank,struct fb_info * fb)356 static void platinum_blank(int blank,  struct fb_info *fb)
357 {
358 /*
359  *  Blank the screen if blank_mode != 0, else unblank. If blank == NULL
360  *  then the caller blanks by setting the CLUT (Color Look Up Table) to all
361  *  black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due
362  *  to e.g. a video mode which doesn't support it. Implements VESA suspend
363  *  and powerdown modes on hardware that supports disabling hsync/vsync:
364  *    blank_mode == 2: suspend vsync
365  *    blank_mode == 3: suspend hsync
366  *    blank_mode == 4: powerdown
367  */
368 /* [danj] I think there's something fishy about those constants... */
369 /*
370 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
371 	int	ctrl;
372 
373 	ctrl = ld_le32(&info->platinum_regs->ctrl.r) | 0x33;
374 	if (blank)
375 		--blank_mode;
376 	if (blank & VESA_VSYNC_SUSPEND)
377 		ctrl &= ~3;
378 	if (blank & VESA_HSYNC_SUSPEND)
379 		ctrl &= ~0x30;
380 	out_le32(&info->platinum_regs->ctrl.r, ctrl);
381 */
382 /* TODO: Figure out how the heck to powerdown this thing! */
383     return;
384 }
385 
platinum_getcolreg(u_int regno,u_int * red,u_int * green,u_int * blue,u_int * transp,struct fb_info * fb)386 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green,
387 			      u_int *blue, u_int *transp, struct fb_info *fb)
388 {
389 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
390 
391 	if (regno > 255)
392 		return 1;
393 
394 	*red = (info->palette[regno].red<<8) | info->palette[regno].red;
395 	*green = (info->palette[regno].green<<8) | info->palette[regno].green;
396 	*blue = (info->palette[regno].blue<<8) | info->palette[regno].blue;
397 	*transp = 0;
398 	return 0;
399 }
400 
platinum_setcolreg(u_int regno,u_int red,u_int green,u_int blue,u_int transp,struct fb_info * fb)401 static int platinum_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
402 			      u_int transp, struct fb_info *fb)
403 {
404 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
405 	volatile struct cmap_regs *cmap_regs = info->cmap_regs;
406 
407 	if (regno > 255)
408 		return 1;
409 
410 	red >>= 8;
411 	green >>= 8;
412 	blue >>= 8;
413 
414 	info->palette[regno].red = red;
415 	info->palette[regno].green = green;
416 	info->palette[regno].blue = blue;
417 
418 	out_8(&cmap_regs->addr, regno);		/* tell clut what addr to fill	*/
419 	out_8(&cmap_regs->lut, red);		/* send one color channel at	*/
420 	out_8(&cmap_regs->lut, green);		/* a time...			*/
421 	out_8(&cmap_regs->lut, blue);
422 
423 	if(regno < 16) {
424 #ifdef FBCON_HAS_CFB16
425 		info->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) | (regno << 0);
426 #endif
427 #ifdef FBCON_HAS_CFB32
428 		info->fbcon_cmap.cfb32[regno] = (regno << 24) | (regno << 16) | (regno << 8) | regno;
429 #endif
430 	}
431 	return 0;
432 }
433 
do_install_cmap(int con,struct fb_info * info)434 static void do_install_cmap(int con, struct fb_info *info)
435 {
436 	if (con != currcon)
437 		return;
438 	if (fb_display[con].cmap.len)
439 		fb_set_cmap(&fb_display[con].cmap, 1, platinum_setcolreg,
440 			    info);
441 	else {
442 		int size = fb_display[con].var.bits_per_pixel == 16 ? 32 : 256;
443 		fb_set_cmap(fb_default_cmap(size), 1, platinum_setcolreg,
444 			    info);
445 	}
446 }
447 
platinum_vram_reqd(const struct fb_info_platinum * info,int video_mode,int color_mode)448 static inline int platinum_vram_reqd(const struct fb_info_platinum *info, int video_mode, int color_mode)
449 {
450 	unsigned int pitch =
451 	       (vmode_attrs[video_mode-1].hres * (1<<color_mode) + 0x20);
452 	fixup_pitch(pitch, info, color_mode);
453 	return vmode_attrs[video_mode-1].vres * pitch;
454 }
455 
456 #define STORE_D2(a, d) { \
457 	out_8(&cmap_regs->addr, (a+32)); \
458 	out_8(&cmap_regs->d2, (d)); \
459 }
460 
set_platinum_clock(struct fb_info_platinum * info)461 static void set_platinum_clock(struct fb_info_platinum *info)
462 {
463 	volatile struct cmap_regs *cmap_regs = info->cmap_regs;
464 	struct platinum_regvals	*init;
465 
466 	init = platinum_reg_init[info->current_par.vmode-1];
467 
468 	STORE_D2(6, 0xc6);
469 	out_8(&cmap_regs->addr,3+32);
470 
471 	if (in_8(&cmap_regs->d2) == 2) {
472 		STORE_D2(7, init->clock_params[info->clktype][0]);
473 		STORE_D2(8, init->clock_params[info->clktype][1]);
474 		STORE_D2(3, 3);
475 	} else {
476 		STORE_D2(4, init->clock_params[info->clktype][0]);
477 		STORE_D2(5, init->clock_params[info->clktype][1]);
478 		STORE_D2(3, 2);
479 	}
480 
481 	__delay(5000);
482 	STORE_D2(9, 0xa6);
483 }
484 
485 
486 /* Now how about actually saying, Make it so! */
487 /* Some things in here probably don't need to be done each time. */
platinum_set_par(const struct fb_par_platinum * par,struct fb_info_platinum * info)488 static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_platinum *info)
489 {
490 	volatile struct platinum_regs	*platinum_regs = info->platinum_regs;
491 	volatile struct cmap_regs	*cmap_regs = info->cmap_regs;
492 	struct platinum_regvals		*init;
493 	int				i;
494 	int				vmode, cmode, pitch;
495 
496 	info->current_par = *par;
497 
498 	vmode = par->vmode;
499 	cmode = par->cmode;
500 
501 	init = platinum_reg_init[vmode - 1];
502 
503 	/* Initialize display timing registers */
504 	out_be32(&platinum_regs->reg[24].r, 7);	/* turn display off */
505 
506 	for (i = 0; i < 26; ++i)
507 		out_be32(&platinum_regs->reg[i+32].r, init->regs[i]);
508 
509 	out_be32(&platinum_regs->reg[26+32].r, (info->total_vram == 0x100000 ?
510 						init->offset[cmode] + 4 - cmode :
511 						init->offset[cmode]));
512 	out_be32(&platinum_regs->reg[16].r, (unsigned) info->frame_buffer_phys+init->fb_offset+0x10);
513 	pitch = init->pitch[cmode];
514 	fixup_pitch(pitch, info, cmode);
515 	out_be32(&platinum_regs->reg[18].r, pitch);
516 	out_be32(&platinum_regs->reg[19].r, (info->total_vram == 0x100000 ?
517 					     init->mode[cmode+1] :
518 					     init->mode[cmode]));
519 	out_be32(&platinum_regs->reg[20].r, (info->total_vram == 0x100000 ? 0x11 : 0x1011));
520 	out_be32(&platinum_regs->reg[21].r, 0x100);
521 	out_be32(&platinum_regs->reg[22].r, 1);
522 	out_be32(&platinum_regs->reg[23].r, 1);
523 	out_be32(&platinum_regs->reg[26].r, 0xc00);
524 	out_be32(&platinum_regs->reg[27].r, 0x235);
525 	/* out_be32(&platinum_regs->reg[27].r, 0x2aa); */
526 
527 	STORE_D2(0, (info->total_vram == 0x100000 ?
528 		     init->dacula_ctrl[cmode] & 0xf :
529 		     init->dacula_ctrl[cmode]));
530 	STORE_D2(1, 4);
531 	STORE_D2(2, 0);
532 
533 	set_platinum_clock(info);
534 
535 	out_be32(&platinum_regs->reg[24].r, 0);	/* turn display on */
536 
537 #ifdef CONFIG_FB_COMPAT_XPMAC
538 	if (console_fb_info == &info->fb_info) {
539 		display_info.height = par->yres;
540 		display_info.width = par->xres;
541 		display_info.depth = ( (cmode == CMODE_32) ? 32 :
542 				      ((cmode == CMODE_16) ? 16 : 8));
543 		display_info.pitch = vmode_attrs[vmode-1].hres * (1<<cmode) + 0x20;
544 		fixup_pitch(display_info.pitch, info, cmode);
545 		display_info.mode = vmode;
546 		strncpy(display_info.name, "platinum",
547 			sizeof(display_info.name));
548 		display_info.fb_address = info->frame_buffer_phys + init->fb_offset + 0x20;
549 		display_info.cmap_adr_address = info->cmap_regs_phys;
550 		display_info.cmap_data_address = info->cmap_regs_phys + 0x30;
551 		display_info.disp_reg_address = info->platinum_regs_phys;
552 
553 	}
554 #endif /* CONFIG_FB_COMPAT_XPMAC */
555 }
556 
557 
init_platinum(struct fb_info_platinum * info)558 static int __init init_platinum(struct fb_info_platinum *info)
559 {
560 	struct fb_var_screeninfo var;
561 	struct display *disp;
562 	int sense;
563 	int j,k;
564 
565 	sense = read_platinum_sense(info);
566 	printk(KERN_INFO "Monitor sense value = 0x%x, ", sense);
567 
568 #ifdef CONFIG_NVRAM
569 	if (default_vmode == VMODE_NVRAM) {
570 		default_vmode = nvram_read_byte(NV_VMODE);
571 		if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
572 		    !platinum_reg_init[default_vmode-1])
573 			default_vmode = VMODE_CHOOSE;
574 	}
575 	if (default_cmode == CMODE_NVRAM)
576 		default_cmode = nvram_read_byte(NV_CMODE);
577 #endif
578 	if (default_vmode == VMODE_CHOOSE)
579 		default_vmode = mac_map_monitor_sense(sense);
580 	if (default_vmode <= 0 || default_vmode > VMODE_MAX)
581 		default_vmode = VMODE_640_480_60;
582 	if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
583 		default_cmode = CMODE_8;
584 	/*
585 	 * Reduce the pixel size if we don't have enough VRAM.
586 	 */
587 	while(default_cmode > CMODE_8 && platinum_vram_reqd(info, default_vmode, default_cmode)
588 	    > info->total_vram)
589 		default_cmode--;
590 
591 	printk("using video mode %d and color mode %d.\n", default_vmode, default_cmode);
592 
593 	mac_vmode_to_var(default_vmode, default_cmode, &var);
594 
595 	if (platinum_var_to_par(&var, &info->default_par, info)) {
596 		printk(KERN_ERR "platinumfb: can't set default video mode\n");
597 		return 0;
598 	}
599 
600 	disp = &info->disp;
601 
602 	strcpy(info->fb_info.modename, "platinum");
603 	info->fb_info.node = -1;
604 	info->fb_info.fbops = &platinumfb_ops;
605 	info->fb_info.disp = disp;
606 	strcpy(info->fb_info.fontname, fontname);
607 	info->fb_info.changevar = NULL;
608 	info->fb_info.switch_con = &platinum_switch;
609 	info->fb_info.updatevar = &platinum_updatevar;
610 	info->fb_info.blank = &platinum_blank;
611 	info->fb_info.flags = FBINFO_FLAG_DEFAULT;
612 
613 	for (j = 0; j < 16; j++) {
614 		k = color_table[j];
615 		info->palette[j].red = default_red[k];
616 		info->palette[j].green = default_grn[k];
617 		info->palette[j].blue = default_blu[k];
618 	}
619 	platinum_set_var(&var, -1, &info->fb_info);
620 
621 	if (register_framebuffer(&info->fb_info) < 0)
622 		return 0;
623 
624 	printk(KERN_INFO "fb%d: platinum frame buffer device\n",
625 	       GET_FB_IDX(info->fb_info.node));
626 
627 	return 1;
628 }
629 
platinum_init(void)630 int __init platinum_init(void)
631 {
632 	struct device_node *dp;
633 
634 	dp = find_devices("platinum");
635 	if (dp != 0)
636 		platinum_of_init(dp);
637 	return 0;
638 }
639 
640 #ifdef __powerpc__
641 #define invalidate_cache(addr) \
642 	asm volatile("eieio; dcbf 0,%1" \
643 	: "=m" (*(addr)) : "r" (addr) : "memory");
644 #else
645 #define invalidate_cache(addr)
646 #endif
647 
platinum_of_init(struct device_node * dp)648 static void __init platinum_of_init(struct device_node *dp)
649 {
650 	struct fb_info_platinum	*info;
651 	unsigned long		addr, size;
652 	volatile __u8		*fbuffer;
653 	int			i, bank0, bank1, bank2, bank3;
654 
655 	if(dp->n_addrs != 2) {
656 		printk(KERN_ERR "expecting 2 address for platinum (got %d)", dp->n_addrs);
657 		return;
658 	}
659 
660 	info = kmalloc(sizeof(*info), GFP_ATOMIC);
661 	if (info == 0)
662 		return;
663 	memset(info, 0, sizeof(*info));
664 
665 	/* Map in frame buffer and registers */
666 	for (i = 0; i < dp->n_addrs; ++i) {
667 		addr = dp->addrs[i].address;
668 		size = dp->addrs[i].size;
669 		/* Let's assume we can request either all or nothing */
670 		if (!request_mem_region(addr, size, "platinumfb")) {
671 			kfree(info);
672 			return;
673 		}
674 		if (size >= 0x400000) {
675 			/* frame buffer - map only 4MB */
676 			info->frame_buffer_phys = addr;
677 			info->frame_buffer = __ioremap(addr, 0x400000, _PAGE_WRITETHRU);
678 			info->base_frame_buffer = info->frame_buffer;
679 		} else {
680 			/* registers */
681 			info->platinum_regs_phys = addr;
682 			info->platinum_regs = ioremap(addr, size);
683 		}
684 	}
685 
686 	info->cmap_regs_phys = 0xf301b000;	/* XXX not in prom? */
687 	request_mem_region(info->cmap_regs_phys, 0x1000, "platinumfb cmap");
688 	info->cmap_regs = ioremap(info->cmap_regs_phys, 0x1000);
689 
690 	/* Grok total video ram */
691 	out_be32(&info->platinum_regs->reg[16].r, (unsigned)info->frame_buffer_phys);
692 	out_be32(&info->platinum_regs->reg[20].r, 0x1011);	/* select max vram */
693 	out_be32(&info->platinum_regs->reg[24].r, 0);	/* switch in vram */
694 
695 	fbuffer = info->base_frame_buffer;
696 	fbuffer[0x100000] = 0x34;
697 	fbuffer[0x100008] = 0x0;
698 	invalidate_cache(&fbuffer[0x100000]);
699 	fbuffer[0x200000] = 0x56;
700 	fbuffer[0x200008] = 0x0;
701 	invalidate_cache(&fbuffer[0x200000]);
702 	fbuffer[0x300000] = 0x78;
703 	fbuffer[0x300008] = 0x0;
704 	invalidate_cache(&fbuffer[0x300000]);
705 	bank0 = 1; /* builtin 1MB vram, always there */
706 	bank1 = fbuffer[0x100000] == 0x34;
707 	bank2 = fbuffer[0x200000] == 0x56;
708 	bank3 = fbuffer[0x300000] == 0x78;
709 	info->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000;
710 	printk(KERN_INFO "Total VRAM = %dMB %d%d%d%d\n", (int) (info->total_vram / 1024 / 1024), bank3, bank2, bank1, bank0);
711 
712 	/*
713 	 * Try to determine whether we have an old or a new DACula.
714 	 */
715 	out_8(&info->cmap_regs->addr, 0x40);
716 	info->dactype = in_8(&info->cmap_regs->d2);
717 	switch (info->dactype) {
718 	case 0x3c:
719 		info->clktype = 1;
720 		break;
721 	case 0x84:
722 		info->clktype = 0;
723 		break;
724 	default:
725 		info->clktype = 0;
726 		printk(KERN_INFO "Unknown DACula type: %x\n", info->dactype);
727 		break;
728 	}
729 
730 	if (!init_platinum(info)) {
731 		kfree(info);
732 		return;
733 	}
734 
735 #ifdef CONFIG_FB_COMPAT_XPMAC
736 	if (!console_fb_info)
737 		console_fb_info = &info->fb_info;
738 #endif
739 }
740 
741 /*
742  * Get the monitor sense value.
743  * Note that this can be called before calibrate_delay,
744  * so we can't use udelay.
745  */
read_platinum_sense(struct fb_info_platinum * info)746 static int read_platinum_sense(struct fb_info_platinum *info)
747 {
748 	volatile struct platinum_regs *platinum_regs = info->platinum_regs;
749 	int sense;
750 
751 	out_be32(&platinum_regs->reg[23].r, 7);	/* turn off drivers */
752 	__delay(2000);
753 	sense = (~in_be32(&platinum_regs->reg[23].r) & 7) << 8;
754 
755 	/* drive each sense line low in turn and collect the other 2 */
756 	out_be32(&platinum_regs->reg[23].r, 3);	/* drive A low */
757 	__delay(2000);
758 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 3) << 4;
759 	out_be32(&platinum_regs->reg[23].r, 5);	/* drive B low */
760 	__delay(2000);
761 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 4) << 1;
762 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 1) << 2;
763 	out_be32(&platinum_regs->reg[23].r, 6);	/* drive C low */
764 	__delay(2000);
765 	sense |= (~in_be32(&platinum_regs->reg[23].r) & 6) >> 1;
766 
767 	out_be32(&platinum_regs->reg[23].r, 7);	/* turn off drivers */
768 
769 	return sense;
770 }
771 
772 /* This routine takes a user-supplied var, and picks the best vmode/cmode from it. */
platinum_var_to_par(const struct fb_var_screeninfo * var,struct fb_par_platinum * par,const struct fb_info_platinum * info)773 static int platinum_var_to_par(const struct fb_var_screeninfo *var,
774 			       struct fb_par_platinum *par,
775 			       const struct fb_info_platinum *info)
776 {
777 	if(mac_var_to_vmode(var, &par->vmode, &par->cmode) != 0) {
778 		printk(KERN_ERR "platinum_var_to_par: mac_var_to_vmode unsuccessful.\n");
779 		printk(KERN_ERR "platinum_var_to_par: var->xres = %d\n", var->xres);
780 		printk(KERN_ERR "platinum_var_to_par: var->yres = %d\n", var->yres);
781 		printk(KERN_ERR "platinum_var_to_par: var->xres_virtual = %d\n", var->xres_virtual);
782 		printk(KERN_ERR "platinum_var_to_par: var->yres_virtual = %d\n", var->yres_virtual);
783 		printk(KERN_ERR "platinum_var_to_par: var->bits_per_pixel = %d\n", var->bits_per_pixel);
784 		printk(KERN_ERR "platinum_var_to_par: var->pixclock = %d\n", var->pixclock);
785 		printk(KERN_ERR "platinum_var_to_par: var->vmode = %d\n", var->vmode);
786 		return -EINVAL;
787 	}
788 
789 	if(!platinum_reg_init[par->vmode-1]) {
790 		printk(KERN_ERR "platinum_var_to_par, vmode %d not valid.\n", par->vmode);
791 		return -EINVAL;
792 	}
793 
794 	if (platinum_vram_reqd(info, par->vmode, par->cmode) > info->total_vram) {
795 		printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", par->vmode, par->cmode);
796 		return -EINVAL;
797 	}
798 
799 	par->xres = vmode_attrs[par->vmode-1].hres;
800 	par->yres = vmode_attrs[par->vmode-1].vres;
801 	par->xoffset = 0;
802 	par->yoffset = 0;
803 	par->vxres = par->xres;
804 	par->vyres = par->yres;
805 
806 	return 0;
807 }
808 
platinum_par_to_var(struct fb_var_screeninfo * var,const struct fb_par_platinum * par,const struct fb_info_platinum * info)809 static int platinum_par_to_var(struct fb_var_screeninfo *var,
810 			       const struct fb_par_platinum *par,
811 			       const struct fb_info_platinum *info)
812 {
813 	return mac_vmode_to_var(par->vmode, par->cmode, var);
814 }
815 
platinum_encode_fix(struct fb_fix_screeninfo * fix,const struct fb_par_platinum * par,const struct fb_info_platinum * info)816 static int platinum_encode_fix(struct fb_fix_screeninfo *fix,
817 			       const struct fb_par_platinum *par,
818 			       const struct fb_info_platinum *info)
819 {
820 	struct platinum_regvals *init;
821 
822 	init = platinum_reg_init[par->vmode-1];
823 
824 	memset(fix, 0, sizeof(*fix));
825 	strcpy(fix->id, "platinum");
826 	fix->smem_start = (info->frame_buffer_phys) + init->fb_offset + 0x20;
827 	fix->smem_len = (u32) info->total_vram;
828 	fix->mmio_start = (info->platinum_regs_phys);
829 	fix->mmio_len = 0x1000;
830 	fix->type = FB_TYPE_PACKED_PIXELS;
831 	fix->type_aux = 0;
832 	fix->ywrapstep = 0;
833 	fix->xpanstep = 0;
834 	fix->ypanstep = 0;
835 	fix->visual = (par->cmode == CMODE_8) ?
836 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
837 	fix->line_length = vmode_attrs[par->vmode-1].hres * (1<<par->cmode) + 0x20;
838 	fixup_pitch(fix->line_length, info, par->cmode);
839 
840 	return 0;
841 }
842 
843 
844 /*
845  * Parse user speficied options (`video=platinumfb:')
846  */
platinum_setup(char * options)847 int __init platinum_setup(char *options)
848 {
849 	char *this_opt;
850 
851 	if (!options || !*options)
852 		return 0;
853 
854 	while ((this_opt = strsep(&options, ",")) != NULL) {
855 		if (!strncmp(this_opt, "font:", 5)) {
856 			char *p;
857 			int i;
858 
859 			p = this_opt + 5;
860 			for (i = 0; i < sizeof(fontname) - 1; i++)
861 				if (!*p || *p == ' ' || *p == ',')
862 					break;
863 			memcpy(fontname, this_opt + 5, i);
864 			fontname[i] = 0;
865 		}
866 		if (!strncmp(this_opt, "vmode:", 6)) {
867 	    		int vmode = simple_strtoul(this_opt+6, NULL, 0);
868 	    	if (vmode > 0 && vmode <= VMODE_MAX)
869 			default_vmode = vmode;
870 		} else if (!strncmp(this_opt, "cmode:", 6)) {
871 			int depth = simple_strtoul(this_opt+6, NULL, 0);
872 			switch (depth) {
873 			 case 0:
874 			 case 8:
875 			    default_cmode = CMODE_8;
876 			    break;
877 			 case 15:
878 			 case 16:
879 			    default_cmode = CMODE_16;
880 			    break;
881 			 case 24:
882 			 case 32:
883 			    default_cmode = CMODE_32;
884 			    break;
885 			}
886 		}
887 	}
888 	return 0;
889 }
890 
891 MODULE_LICENSE("GPL");
892