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