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 | #undef DEBUG |
21 | |
22 | #include <linux/module.h> |
23 | #include <linux/kernel.h> |
24 | #include <linux/errno.h> |
25 | #include <linux/string.h> |
26 | #include <linux/mm.h> |
27 | #include <linux/vmalloc.h> |
28 | #include <linux/delay.h> |
29 | #include <linux/interrupt.h> |
30 | #include <linux/fb.h> |
31 | #include <linux/init.h> |
32 | #include <linux/nvram.h> |
33 | #include <linux/of.h> |
34 | #include <linux/of_address.h> |
35 | #include <linux/platform_device.h> |
36 | |
37 | #include "macmodes.h" |
38 | #include "platinumfb.h" |
39 | |
40 | static int default_vmode = VMODE_NVRAM; |
41 | static int default_cmode = CMODE_NVRAM; |
42 | |
43 | struct fb_info_platinum { |
44 | struct fb_info *info; |
45 | |
46 | int vmode, cmode; |
47 | int xres, yres; |
48 | int vxres, vyres; |
49 | int xoffset, yoffset; |
50 | |
51 | struct { |
52 | __u8 red, green, blue; |
53 | } palette[256]; |
54 | u32 pseudo_palette[16]; |
55 | |
56 | volatile struct cmap_regs __iomem *cmap_regs; |
57 | unsigned long cmap_regs_phys; |
58 | |
59 | volatile struct platinum_regs __iomem *platinum_regs; |
60 | unsigned long platinum_regs_phys; |
61 | |
62 | __u8 __iomem *frame_buffer; |
63 | volatile __u8 __iomem *base_frame_buffer; |
64 | unsigned long frame_buffer_phys; |
65 | |
66 | unsigned long total_vram; |
67 | int clktype; |
68 | int dactype; |
69 | |
70 | struct resource rsrc_fb, rsrc_reg; |
71 | }; |
72 | |
73 | /* |
74 | * Frame buffer device API |
75 | */ |
76 | |
77 | static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
78 | u_int transp, struct fb_info *info); |
79 | static int platinumfb_blank(int blank_mode, struct fb_info *info); |
80 | static int platinumfb_set_par (struct fb_info *info); |
81 | static int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info); |
82 | |
83 | /* |
84 | * internal functions |
85 | */ |
86 | |
87 | static inline int platinum_vram_reqd(int video_mode, int color_mode); |
88 | static int read_platinum_sense(struct fb_info_platinum *pinfo); |
89 | static void set_platinum_clock(struct fb_info_platinum *pinfo); |
90 | static void platinum_set_hardware(struct fb_info_platinum *pinfo); |
91 | static int platinum_var_to_par(struct fb_var_screeninfo *var, |
92 | struct fb_info_platinum *pinfo, |
93 | int check_only); |
94 | |
95 | /* |
96 | * Interface used by the world |
97 | */ |
98 | |
99 | static const struct fb_ops platinumfb_ops = { |
100 | .owner = THIS_MODULE, |
101 | FB_DEFAULT_IOMEM_OPS, |
102 | .fb_check_var = platinumfb_check_var, |
103 | .fb_set_par = platinumfb_set_par, |
104 | .fb_setcolreg = platinumfb_setcolreg, |
105 | .fb_blank = platinumfb_blank, |
106 | }; |
107 | |
108 | /* |
109 | * Checks a var structure |
110 | */ |
111 | static int platinumfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info) |
112 | { |
113 | return platinum_var_to_par(var, pinfo: info->par, check_only: 1); |
114 | } |
115 | |
116 | /* |
117 | * Applies current var to display |
118 | */ |
119 | static int platinumfb_set_par (struct fb_info *info) |
120 | { |
121 | struct fb_info_platinum *pinfo = info->par; |
122 | struct platinum_regvals *init; |
123 | int err, offset = 0x20; |
124 | |
125 | if((err = platinum_var_to_par(var: &info->var, pinfo, check_only: 0))) { |
126 | printk (KERN_ERR "platinumfb_set_par: error calling" |
127 | " platinum_var_to_par: %d.\n" , err); |
128 | return err; |
129 | } |
130 | |
131 | platinum_set_hardware(pinfo); |
132 | |
133 | init = platinum_reg_init[pinfo->vmode-1]; |
134 | |
135 | if ((pinfo->vmode == VMODE_832_624_75) && (pinfo->cmode > CMODE_8)) |
136 | offset = 0x10; |
137 | |
138 | info->screen_base = pinfo->frame_buffer + init->fb_offset + offset; |
139 | mutex_lock(&info->mm_lock); |
140 | info->fix.smem_start = (pinfo->frame_buffer_phys) + init->fb_offset + offset; |
141 | mutex_unlock(lock: &info->mm_lock); |
142 | info->fix.visual = (pinfo->cmode == CMODE_8) ? |
143 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR; |
144 | info->fix.line_length = vmode_attrs[pinfo->vmode-1].hres * (1<<pinfo->cmode) |
145 | + offset; |
146 | printk("line_length: %x\n" , info->fix.line_length); |
147 | return 0; |
148 | } |
149 | |
150 | static int platinumfb_blank(int blank, struct fb_info *fb) |
151 | { |
152 | /* |
153 | * Blank the screen if blank_mode != 0, else unblank. If blank == NULL |
154 | * then the caller blanks by setting the CLUT (Color Look Up Table) to all |
155 | * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due |
156 | * to e.g. a video mode which doesn't support it. Implements VESA suspend |
157 | * and powerdown modes on hardware that supports disabling hsync/vsync: |
158 | * blank_mode == 2: suspend vsync |
159 | * blank_mode == 3: suspend hsync |
160 | * blank_mode == 4: powerdown |
161 | */ |
162 | /* [danj] I think there's something fishy about those constants... */ |
163 | /* |
164 | struct fb_info_platinum *info = (struct fb_info_platinum *) fb; |
165 | int ctrl; |
166 | |
167 | ctrl = le32_to_cpup(&info->platinum_regs->ctrl.r) | 0x33; |
168 | if (blank) |
169 | --blank_mode; |
170 | if (blank & VESA_VSYNC_SUSPEND) |
171 | ctrl &= ~3; |
172 | if (blank & VESA_HSYNC_SUSPEND) |
173 | ctrl &= ~0x30; |
174 | out_le32(&info->platinum_regs->ctrl.r, ctrl); |
175 | */ |
176 | /* TODO: Figure out how the heck to powerdown this thing! */ |
177 | return 0; |
178 | } |
179 | |
180 | static int platinumfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
181 | u_int transp, struct fb_info *info) |
182 | { |
183 | struct fb_info_platinum *pinfo = info->par; |
184 | volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs; |
185 | |
186 | if (regno > 255) |
187 | return 1; |
188 | |
189 | red >>= 8; |
190 | green >>= 8; |
191 | blue >>= 8; |
192 | |
193 | pinfo->palette[regno].red = red; |
194 | pinfo->palette[regno].green = green; |
195 | pinfo->palette[regno].blue = blue; |
196 | |
197 | out_8(&cmap_regs->addr, regno); /* tell clut what addr to fill */ |
198 | out_8(&cmap_regs->lut, red); /* send one color channel at */ |
199 | out_8(&cmap_regs->lut, green); /* a time... */ |
200 | out_8(&cmap_regs->lut, blue); |
201 | |
202 | if (regno < 16) { |
203 | int i; |
204 | u32 *pal = info->pseudo_palette; |
205 | switch (pinfo->cmode) { |
206 | case CMODE_16: |
207 | pal[regno] = (regno << 10) | (regno << 5) | regno; |
208 | break; |
209 | case CMODE_32: |
210 | i = (regno << 8) | regno; |
211 | pal[regno] = (i << 16) | i; |
212 | break; |
213 | } |
214 | } |
215 | |
216 | return 0; |
217 | } |
218 | |
219 | static inline int platinum_vram_reqd(int video_mode, int color_mode) |
220 | { |
221 | int baseval = vmode_attrs[video_mode-1].hres * (1<<color_mode); |
222 | |
223 | if ((video_mode == VMODE_832_624_75) && (color_mode > CMODE_8)) |
224 | baseval += 0x10; |
225 | else |
226 | baseval += 0x20; |
227 | |
228 | return vmode_attrs[video_mode-1].vres * baseval + 0x1000; |
229 | } |
230 | |
231 | #define STORE_D2(a, d) { \ |
232 | out_8(&cmap_regs->addr, (a+32)); \ |
233 | out_8(&cmap_regs->d2, (d)); \ |
234 | } |
235 | |
236 | static void set_platinum_clock(struct fb_info_platinum *pinfo) |
237 | { |
238 | volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs; |
239 | struct platinum_regvals *init; |
240 | |
241 | init = platinum_reg_init[pinfo->vmode-1]; |
242 | |
243 | STORE_D2(6, 0xc6); |
244 | out_8(&cmap_regs->addr,3+32); |
245 | |
246 | if (in_8(&cmap_regs->d2) == 2) { |
247 | STORE_D2(7, init->clock_params[pinfo->clktype][0]); |
248 | STORE_D2(8, init->clock_params[pinfo->clktype][1]); |
249 | STORE_D2(3, 3); |
250 | } else { |
251 | STORE_D2(4, init->clock_params[pinfo->clktype][0]); |
252 | STORE_D2(5, init->clock_params[pinfo->clktype][1]); |
253 | STORE_D2(3, 2); |
254 | } |
255 | |
256 | __delay(loops: 5000); |
257 | STORE_D2(9, 0xa6); |
258 | } |
259 | |
260 | |
261 | /* Now how about actually saying, Make it so! */ |
262 | /* Some things in here probably don't need to be done each time. */ |
263 | static void platinum_set_hardware(struct fb_info_platinum *pinfo) |
264 | { |
265 | volatile struct platinum_regs __iomem *platinum_regs = pinfo->platinum_regs; |
266 | volatile struct cmap_regs __iomem *cmap_regs = pinfo->cmap_regs; |
267 | struct platinum_regvals *init; |
268 | int i; |
269 | int vmode, cmode; |
270 | |
271 | vmode = pinfo->vmode; |
272 | cmode = pinfo->cmode; |
273 | |
274 | init = platinum_reg_init[vmode - 1]; |
275 | |
276 | /* Initialize display timing registers */ |
277 | out_be32(&platinum_regs->reg[24].r, 7); /* turn display off */ |
278 | |
279 | for (i = 0; i < 26; ++i) |
280 | out_be32(&platinum_regs->reg[i+32].r, init->regs[i]); |
281 | |
282 | out_be32(&platinum_regs->reg[26+32].r, (pinfo->total_vram == 0x100000 ? |
283 | init->offset[cmode] + 4 - cmode : |
284 | init->offset[cmode])); |
285 | out_be32(&platinum_regs->reg[16].r, (unsigned) pinfo->frame_buffer_phys+init->fb_offset+0x10); |
286 | out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]); |
287 | out_be32(&platinum_regs->reg[19].r, (pinfo->total_vram == 0x100000 ? |
288 | init->mode[cmode+1] : |
289 | init->mode[cmode])); |
290 | out_be32(&platinum_regs->reg[20].r, (pinfo->total_vram == 0x100000 ? 0x11 : 0x1011)); |
291 | out_be32(&platinum_regs->reg[21].r, 0x100); |
292 | out_be32(&platinum_regs->reg[22].r, 1); |
293 | out_be32(&platinum_regs->reg[23].r, 1); |
294 | out_be32(&platinum_regs->reg[26].r, 0xc00); |
295 | out_be32(&platinum_regs->reg[27].r, 0x235); |
296 | /* out_be32(&platinum_regs->reg[27].r, 0x2aa); */ |
297 | |
298 | STORE_D2(0, (pinfo->total_vram == 0x100000 ? |
299 | init->dacula_ctrl[cmode] & 0xf : |
300 | init->dacula_ctrl[cmode])); |
301 | STORE_D2(1, 4); |
302 | STORE_D2(2, 0); |
303 | |
304 | set_platinum_clock(pinfo); |
305 | |
306 | out_be32(&platinum_regs->reg[24].r, 0); /* turn display on */ |
307 | } |
308 | |
309 | /* |
310 | * Set misc info vars for this driver |
311 | */ |
312 | static void platinum_init_info(struct fb_info *info, |
313 | struct fb_info_platinum *pinfo) |
314 | { |
315 | /* Fill fb_info */ |
316 | info->fbops = &platinumfb_ops; |
317 | info->pseudo_palette = pinfo->pseudo_palette; |
318 | info->screen_base = pinfo->frame_buffer + 0x20; |
319 | |
320 | fb_alloc_cmap(cmap: &info->cmap, len: 256, transp: 0); |
321 | |
322 | /* Fill fix common fields */ |
323 | strcpy(p: info->fix.id, q: "platinum" ); |
324 | info->fix.mmio_start = pinfo->platinum_regs_phys; |
325 | info->fix.mmio_len = 0x1000; |
326 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
327 | info->fix.smem_start = pinfo->frame_buffer_phys + 0x20; /* will be updated later */ |
328 | info->fix.smem_len = pinfo->total_vram - 0x20; |
329 | info->fix.ywrapstep = 0; |
330 | info->fix.xpanstep = 0; |
331 | info->fix.ypanstep = 0; |
332 | info->fix.type_aux = 0; |
333 | info->fix.accel = FB_ACCEL_NONE; |
334 | } |
335 | |
336 | |
337 | static int platinum_init_fb(struct fb_info *info) |
338 | { |
339 | struct fb_info_platinum *pinfo = info->par; |
340 | struct fb_var_screeninfo var; |
341 | int sense, rc; |
342 | |
343 | sense = read_platinum_sense(pinfo); |
344 | printk(KERN_INFO "platinumfb: Monitor sense value = 0x%x, " , sense); |
345 | |
346 | if (IS_REACHABLE(CONFIG_NVRAM) && default_vmode == VMODE_NVRAM) |
347 | default_vmode = nvram_read_byte(NV_VMODE); |
348 | if (default_vmode <= 0 || default_vmode > VMODE_MAX || |
349 | !platinum_reg_init[default_vmode - 1]) { |
350 | default_vmode = mac_map_monitor_sense(sense); |
351 | if (!platinum_reg_init[default_vmode - 1]) |
352 | default_vmode = VMODE_640_480_60; |
353 | } |
354 | |
355 | if (IS_REACHABLE(CONFIG_NVRAM) && default_cmode == CMODE_NVRAM) |
356 | default_cmode = nvram_read_byte(NV_CMODE); |
357 | if (default_cmode < CMODE_8 || default_cmode > CMODE_32) |
358 | default_cmode = CMODE_8; |
359 | /* |
360 | * Reduce the pixel size if we don't have enough VRAM. |
361 | */ |
362 | while(default_cmode > CMODE_8 && |
363 | platinum_vram_reqd(video_mode: default_vmode, color_mode: default_cmode) > pinfo->total_vram) |
364 | default_cmode--; |
365 | |
366 | printk("platinumfb: Using video mode %d and color mode %d.\n" , default_vmode, default_cmode); |
367 | |
368 | /* Setup default var */ |
369 | if (mac_vmode_to_var(vmode: default_vmode, cmode: default_cmode, var: &var) < 0) { |
370 | /* This shouldn't happen! */ |
371 | printk("mac_vmode_to_var(%d, %d,) failed\n" , default_vmode, default_cmode); |
372 | try_again: |
373 | default_vmode = VMODE_640_480_60; |
374 | default_cmode = CMODE_8; |
375 | if (mac_vmode_to_var(vmode: default_vmode, cmode: default_cmode, var: &var) < 0) { |
376 | printk(KERN_ERR "platinumfb: mac_vmode_to_var() failed\n" ); |
377 | return -ENXIO; |
378 | } |
379 | } |
380 | |
381 | /* Initialize info structure */ |
382 | platinum_init_info(info, pinfo); |
383 | |
384 | /* Apply default var */ |
385 | info->var = var; |
386 | var.activate = FB_ACTIVATE_NOW; |
387 | rc = fb_set_var(info, var: &var); |
388 | if (rc && (default_vmode != VMODE_640_480_60 || default_cmode != CMODE_8)) |
389 | goto try_again; |
390 | |
391 | /* Register with fbdev layer */ |
392 | rc = register_framebuffer(fb_info: info); |
393 | if (rc < 0) |
394 | return rc; |
395 | |
396 | fb_info(info, "Apple Platinum frame buffer device\n" ); |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | /* |
402 | * Get the monitor sense value. |
403 | * Note that this can be called before calibrate_delay, |
404 | * so we can't use udelay. |
405 | */ |
406 | static int read_platinum_sense(struct fb_info_platinum *info) |
407 | { |
408 | volatile struct platinum_regs __iomem *platinum_regs = info->platinum_regs; |
409 | int sense; |
410 | |
411 | out_be32(&platinum_regs->reg[23].r, 7); /* turn off drivers */ |
412 | __delay(loops: 2000); |
413 | sense = (~in_be32(&platinum_regs->reg[23].r) & 7) << 8; |
414 | |
415 | /* drive each sense line low in turn and collect the other 2 */ |
416 | out_be32(&platinum_regs->reg[23].r, 3); /* drive A low */ |
417 | __delay(loops: 2000); |
418 | sense |= (~in_be32(&platinum_regs->reg[23].r) & 3) << 4; |
419 | out_be32(&platinum_regs->reg[23].r, 5); /* drive B low */ |
420 | __delay(loops: 2000); |
421 | sense |= (~in_be32(&platinum_regs->reg[23].r) & 4) << 1; |
422 | sense |= (~in_be32(&platinum_regs->reg[23].r) & 1) << 2; |
423 | out_be32(&platinum_regs->reg[23].r, 6); /* drive C low */ |
424 | __delay(loops: 2000); |
425 | sense |= (~in_be32(&platinum_regs->reg[23].r) & 6) >> 1; |
426 | |
427 | out_be32(&platinum_regs->reg[23].r, 7); /* turn off drivers */ |
428 | |
429 | return sense; |
430 | } |
431 | |
432 | /* |
433 | * This routine takes a user-supplied var, and picks the best vmode/cmode from it. |
434 | * It also updates the var structure to the actual mode data obtained |
435 | */ |
436 | static int platinum_var_to_par(struct fb_var_screeninfo *var, |
437 | struct fb_info_platinum *pinfo, |
438 | int check_only) |
439 | { |
440 | int vmode, cmode; |
441 | |
442 | if (mac_var_to_vmode(var, vmode: &vmode, cmode: &cmode) != 0) { |
443 | printk(KERN_ERR "platinum_var_to_par: mac_var_to_vmode unsuccessful.\n" ); |
444 | printk(KERN_ERR "platinum_var_to_par: var->xres = %d\n" , var->xres); |
445 | printk(KERN_ERR "platinum_var_to_par: var->yres = %d\n" , var->yres); |
446 | printk(KERN_ERR "platinum_var_to_par: var->xres_virtual = %d\n" , var->xres_virtual); |
447 | printk(KERN_ERR "platinum_var_to_par: var->yres_virtual = %d\n" , var->yres_virtual); |
448 | printk(KERN_ERR "platinum_var_to_par: var->bits_per_pixel = %d\n" , var->bits_per_pixel); |
449 | printk(KERN_ERR "platinum_var_to_par: var->pixclock = %d\n" , var->pixclock); |
450 | printk(KERN_ERR "platinum_var_to_par: var->vmode = %d\n" , var->vmode); |
451 | return -EINVAL; |
452 | } |
453 | |
454 | if (!platinum_reg_init[vmode-1]) { |
455 | printk(KERN_ERR "platinum_var_to_par, vmode %d not valid.\n" , vmode); |
456 | return -EINVAL; |
457 | } |
458 | |
459 | if (platinum_vram_reqd(video_mode: vmode, color_mode: cmode) > pinfo->total_vram) { |
460 | printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n" , vmode, cmode); |
461 | return -EINVAL; |
462 | } |
463 | |
464 | if (mac_vmode_to_var(vmode, cmode, var)) |
465 | return -EINVAL; |
466 | |
467 | if (check_only) |
468 | return 0; |
469 | |
470 | pinfo->vmode = vmode; |
471 | pinfo->cmode = cmode; |
472 | pinfo->xres = vmode_attrs[vmode-1].hres; |
473 | pinfo->yres = vmode_attrs[vmode-1].vres; |
474 | pinfo->xoffset = 0; |
475 | pinfo->yoffset = 0; |
476 | pinfo->vxres = pinfo->xres; |
477 | pinfo->vyres = pinfo->yres; |
478 | |
479 | return 0; |
480 | } |
481 | |
482 | |
483 | /* |
484 | * Parse user specified options (`video=platinumfb:') |
485 | */ |
486 | static int __init platinumfb_setup(char *options) |
487 | { |
488 | char *this_opt; |
489 | |
490 | if (!options || !*options) |
491 | return 0; |
492 | |
493 | while ((this_opt = strsep(&options, "," )) != NULL) { |
494 | if (!strncmp(this_opt, "vmode:" , 6)) { |
495 | int vmode = simple_strtoul(this_opt+6, NULL, 0); |
496 | if (vmode > 0 && vmode <= VMODE_MAX) |
497 | default_vmode = vmode; |
498 | } else if (!strncmp(this_opt, "cmode:" , 6)) { |
499 | int depth = simple_strtoul(this_opt+6, NULL, 0); |
500 | switch (depth) { |
501 | case 0: |
502 | case 8: |
503 | default_cmode = CMODE_8; |
504 | break; |
505 | case 15: |
506 | case 16: |
507 | default_cmode = CMODE_16; |
508 | break; |
509 | case 24: |
510 | case 32: |
511 | default_cmode = CMODE_32; |
512 | break; |
513 | } |
514 | } |
515 | } |
516 | return 0; |
517 | } |
518 | |
519 | #ifdef __powerpc__ |
520 | #define invalidate_cache(addr) \ |
521 | asm volatile("eieio; dcbf 0,%1" \ |
522 | : "=m" (*(addr)) : "r" (addr) : "memory"); |
523 | #else |
524 | #define invalidate_cache(addr) |
525 | #endif |
526 | |
527 | static int platinumfb_probe(struct platform_device* odev) |
528 | { |
529 | struct device_node *dp = odev->dev.of_node; |
530 | struct fb_info *info; |
531 | struct fb_info_platinum *pinfo; |
532 | volatile __u8 *fbuffer; |
533 | int bank0, bank1, bank2, bank3, rc; |
534 | |
535 | dev_info(&odev->dev, "Found Apple Platinum video hardware\n" ); |
536 | |
537 | info = framebuffer_alloc(size: sizeof(*pinfo), dev: &odev->dev); |
538 | if (!info) |
539 | return -ENOMEM; |
540 | |
541 | pinfo = info->par; |
542 | |
543 | if (of_address_to_resource(dev: dp, index: 0, r: &pinfo->rsrc_reg) || |
544 | of_address_to_resource(dev: dp, index: 1, r: &pinfo->rsrc_fb)) { |
545 | dev_err(&odev->dev, "Can't get resources\n" ); |
546 | framebuffer_release(info); |
547 | return -ENXIO; |
548 | } |
549 | dev_dbg(&odev->dev, " registers : 0x%llx...0x%llx\n" , |
550 | (unsigned long long)pinfo->rsrc_reg.start, |
551 | (unsigned long long)pinfo->rsrc_reg.end); |
552 | dev_dbg(&odev->dev, " framebuffer: 0x%llx...0x%llx\n" , |
553 | (unsigned long long)pinfo->rsrc_fb.start, |
554 | (unsigned long long)pinfo->rsrc_fb.end); |
555 | |
556 | /* Do not try to request register space, they overlap with the |
557 | * northbridge and that can fail. Only request framebuffer |
558 | */ |
559 | if (!request_mem_region(pinfo->rsrc_fb.start, |
560 | resource_size(&pinfo->rsrc_fb), |
561 | "platinumfb framebuffer" )) { |
562 | printk(KERN_ERR "platinumfb: Can't request framebuffer !\n" ); |
563 | framebuffer_release(info); |
564 | return -ENXIO; |
565 | } |
566 | |
567 | /* frame buffer - map only 4MB */ |
568 | pinfo->frame_buffer_phys = pinfo->rsrc_fb.start; |
569 | pinfo->frame_buffer = ioremap_wt(offset: pinfo->rsrc_fb.start, size: 0x400000); |
570 | pinfo->base_frame_buffer = pinfo->frame_buffer; |
571 | |
572 | /* registers */ |
573 | pinfo->platinum_regs_phys = pinfo->rsrc_reg.start; |
574 | pinfo->platinum_regs = ioremap(offset: pinfo->rsrc_reg.start, size: 0x1000); |
575 | |
576 | pinfo->cmap_regs_phys = 0xf301b000; /* XXX not in prom? */ |
577 | request_mem_region(pinfo->cmap_regs_phys, 0x1000, "platinumfb cmap" ); |
578 | pinfo->cmap_regs = ioremap(offset: pinfo->cmap_regs_phys, size: 0x1000); |
579 | |
580 | /* Grok total video ram */ |
581 | out_be32(&pinfo->platinum_regs->reg[16].r, (unsigned)pinfo->frame_buffer_phys); |
582 | out_be32(&pinfo->platinum_regs->reg[20].r, 0x1011); /* select max vram */ |
583 | out_be32(&pinfo->platinum_regs->reg[24].r, 0); /* switch in vram */ |
584 | |
585 | fbuffer = pinfo->base_frame_buffer; |
586 | fbuffer[0x100000] = 0x34; |
587 | fbuffer[0x100008] = 0x0; |
588 | invalidate_cache(&fbuffer[0x100000]); |
589 | fbuffer[0x200000] = 0x56; |
590 | fbuffer[0x200008] = 0x0; |
591 | invalidate_cache(&fbuffer[0x200000]); |
592 | fbuffer[0x300000] = 0x78; |
593 | fbuffer[0x300008] = 0x0; |
594 | invalidate_cache(&fbuffer[0x300000]); |
595 | bank0 = 1; /* builtin 1MB vram, always there */ |
596 | bank1 = fbuffer[0x100000] == 0x34; |
597 | bank2 = fbuffer[0x200000] == 0x56; |
598 | bank3 = fbuffer[0x300000] == 0x78; |
599 | pinfo->total_vram = (bank0 + bank1 + bank2 + bank3) * 0x100000; |
600 | printk(KERN_INFO "platinumfb: Total VRAM = %dMB (%d%d%d%d)\n" , |
601 | (unsigned int) (pinfo->total_vram / 1024 / 1024), |
602 | bank3, bank2, bank1, bank0); |
603 | |
604 | /* |
605 | * Try to determine whether we have an old or a new DACula. |
606 | */ |
607 | out_8(&pinfo->cmap_regs->addr, 0x40); |
608 | pinfo->dactype = in_8(&pinfo->cmap_regs->d2); |
609 | switch (pinfo->dactype) { |
610 | case 0x3c: |
611 | pinfo->clktype = 1; |
612 | printk(KERN_INFO "platinumfb: DACula type 0x3c\n" ); |
613 | break; |
614 | case 0x84: |
615 | pinfo->clktype = 0; |
616 | printk(KERN_INFO "platinumfb: DACula type 0x84\n" ); |
617 | break; |
618 | default: |
619 | pinfo->clktype = 0; |
620 | printk(KERN_INFO "platinumfb: Unknown DACula type: %x\n" , pinfo->dactype); |
621 | break; |
622 | } |
623 | dev_set_drvdata(dev: &odev->dev, data: info); |
624 | |
625 | rc = platinum_init_fb(info); |
626 | if (rc != 0) { |
627 | iounmap(addr: pinfo->frame_buffer); |
628 | iounmap(addr: pinfo->platinum_regs); |
629 | iounmap(addr: pinfo->cmap_regs); |
630 | framebuffer_release(info); |
631 | } |
632 | |
633 | return rc; |
634 | } |
635 | |
636 | static void platinumfb_remove(struct platform_device* odev) |
637 | { |
638 | struct fb_info *info = dev_get_drvdata(dev: &odev->dev); |
639 | struct fb_info_platinum *pinfo = info->par; |
640 | |
641 | unregister_framebuffer (fb_info: info); |
642 | |
643 | /* Unmap frame buffer and registers */ |
644 | iounmap(addr: pinfo->frame_buffer); |
645 | iounmap(addr: pinfo->platinum_regs); |
646 | iounmap(addr: pinfo->cmap_regs); |
647 | |
648 | release_mem_region(pinfo->rsrc_fb.start, |
649 | resource_size(&pinfo->rsrc_fb)); |
650 | |
651 | release_mem_region(pinfo->cmap_regs_phys, 0x1000); |
652 | |
653 | framebuffer_release(info); |
654 | } |
655 | |
656 | static struct of_device_id platinumfb_match[] = |
657 | { |
658 | { |
659 | .name = "platinum" , |
660 | }, |
661 | {}, |
662 | }; |
663 | |
664 | static struct platform_driver platinum_driver = |
665 | { |
666 | .driver = { |
667 | .name = "platinumfb" , |
668 | .of_match_table = platinumfb_match, |
669 | }, |
670 | .probe = platinumfb_probe, |
671 | .remove_new = platinumfb_remove, |
672 | }; |
673 | |
674 | static int __init platinumfb_init(void) |
675 | { |
676 | #ifndef MODULE |
677 | char *option = NULL; |
678 | |
679 | if (fb_get_options(name: "platinumfb" , option: &option)) |
680 | return -ENODEV; |
681 | platinumfb_setup(options: option); |
682 | #endif |
683 | platform_driver_register(&platinum_driver); |
684 | |
685 | return 0; |
686 | } |
687 | |
688 | static void __exit platinumfb_exit(void) |
689 | { |
690 | platform_driver_unregister(&platinum_driver); |
691 | } |
692 | |
693 | MODULE_LICENSE("GPL" ); |
694 | MODULE_DESCRIPTION("framebuffer driver for Apple Platinum video" ); |
695 | module_init(platinumfb_init); |
696 | |
697 | #ifdef MODULE |
698 | module_exit(platinumfb_exit); |
699 | #endif |
700 | |