diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2006-07-04 03:07:18 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-07-07 06:19:16 -0400 |
commit | 73ea6959b11821ba5ade77fb1d3d4aed52be3b67 (patch) | |
tree | 0082aa5e030c4908a8d6f2ff2347a99a00c65bc5 /drivers/video | |
parent | a45b83957deabbdac9a3d908c6ca4c25f05ce1ad (diff) |
[POWERPC] More offb/bootx fixes
There were still some issues with offb when BootX doesn't provide a
proper display node, this fixes them. This also re-instates the
palette hacks that were disabled a couple of kernel versions ago when
I converted to the new OF parsing, and shuffles some functions around
to avoid prototypes.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/offb.c | 307 |
1 files changed, 161 insertions, 146 deletions
diff --git a/drivers/video/offb.c b/drivers/video/offb.c index 71ce1fa45cf4..faba67228526 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c | |||
@@ -63,8 +63,6 @@ struct offb_par default_par; | |||
63 | * Interface used by the world | 63 | * Interface used by the world |
64 | */ | 64 | */ |
65 | 65 | ||
66 | int offb_init(void); | ||
67 | |||
68 | static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | 66 | static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
69 | u_int transp, struct fb_info *info); | 67 | u_int transp, struct fb_info *info); |
70 | static int offb_blank(int blank, struct fb_info *info); | 68 | static int offb_blank(int blank, struct fb_info *info); |
@@ -73,11 +71,6 @@ static int offb_blank(int blank, struct fb_info *info); | |||
73 | extern boot_infos_t *boot_infos; | 71 | extern boot_infos_t *boot_infos; |
74 | #endif | 72 | #endif |
75 | 73 | ||
76 | static void offb_init_nodriver(struct device_node *); | ||
77 | static void offb_init_fb(const char *name, const char *full_name, | ||
78 | int width, int height, int depth, int pitch, | ||
79 | unsigned long address, struct device_node *dp); | ||
80 | |||
81 | static struct fb_ops offb_ops = { | 74 | static struct fb_ops offb_ops = { |
82 | .owner = THIS_MODULE, | 75 | .owner = THIS_MODULE, |
83 | .fb_setcolreg = offb_setcolreg, | 76 | .fb_setcolreg = offb_setcolreg, |
@@ -230,123 +223,17 @@ static int offb_blank(int blank, struct fb_info *info) | |||
230 | return 0; | 223 | return 0; |
231 | } | 224 | } |
232 | 225 | ||
233 | /* | ||
234 | * Initialisation | ||
235 | */ | ||
236 | 226 | ||
237 | int __init offb_init(void) | 227 | static void __iomem *offb_map_reg(struct device_node *np, int index, |
228 | unsigned long offset, unsigned long size) | ||
238 | { | 229 | { |
239 | struct device_node *dp = NULL, *boot_disp = NULL; | 230 | struct resource r; |
240 | |||
241 | if (fb_get_options("offb", NULL)) | ||
242 | return -ENODEV; | ||
243 | 231 | ||
244 | for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { | 232 | if (of_address_to_resource(np, index, &r)) |
245 | if (get_property(dp, "linux,opened", NULL) && | 233 | return 0; |
246 | get_property(dp, "linux,boot-display", NULL)) { | 234 | if ((r.start + offset + size) > r.end) |
247 | boot_disp = dp; | 235 | return 0; |
248 | offb_init_nodriver(dp); | 236 | return ioremap(r.start + offset, size); |
249 | } | ||
250 | } | ||
251 | for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { | ||
252 | if (get_property(dp, "linux,opened", NULL) && | ||
253 | dp != boot_disp) | ||
254 | offb_init_nodriver(dp); | ||
255 | } | ||
256 | |||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | |||
261 | static void __init offb_init_nodriver(struct device_node *dp) | ||
262 | { | ||
263 | unsigned int len; | ||
264 | int i, width = 640, height = 480, depth = 8, pitch = 640; | ||
265 | unsigned int flags, rsize, addr_prop = 0; | ||
266 | unsigned long max_size = 0; | ||
267 | u64 rstart, address = OF_BAD_ADDR; | ||
268 | u32 *pp, *addrp, *up; | ||
269 | u64 asize; | ||
270 | |||
271 | pp = (u32 *)get_property(dp, "linux,bootx-depth", &len); | ||
272 | if (pp == NULL) | ||
273 | pp = (u32 *)get_property(dp, "depth", &len); | ||
274 | if (pp && len == sizeof(u32)) | ||
275 | depth = *pp; | ||
276 | |||
277 | pp = (u32 *)get_property(dp, "linux,bootx-width", &len); | ||
278 | if (pp == NULL) | ||
279 | pp = (u32 *)get_property(dp, "width", &len); | ||
280 | if (pp && len == sizeof(u32)) | ||
281 | width = *pp; | ||
282 | |||
283 | pp = (u32 *)get_property(dp, "linux,bootx-height", &len); | ||
284 | if (pp == NULL) | ||
285 | pp = (u32 *)get_property(dp, "height", &len); | ||
286 | if (pp && len == sizeof(u32)) | ||
287 | height = *pp; | ||
288 | |||
289 | pp = (u32 *)get_property(dp, "linux,bootx-linebytes", &len); | ||
290 | if (pp == NULL) | ||
291 | pp = (u32 *)get_property(dp, "linebytes", &len); | ||
292 | if (pp && len == sizeof(u32)) | ||
293 | pitch = *pp; | ||
294 | else | ||
295 | pitch = width * ((depth + 7) / 8); | ||
296 | |||
297 | rsize = (unsigned long)pitch * (unsigned long)height; | ||
298 | |||
299 | /* Ok, now we try to figure out the address of the framebuffer. | ||
300 | * | ||
301 | * Unfortunately, Open Firmware doesn't provide a standard way to do | ||
302 | * so. All we can do is a dodgy heuristic that happens to work in | ||
303 | * practice. On most machines, the "address" property contains what | ||
304 | * we need, though not on Matrox cards found in IBM machines. What I've | ||
305 | * found that appears to give good results is to go through the PCI | ||
306 | * ranges and pick one that is both big enough and if possible encloses | ||
307 | * the "address" property. If none match, we pick the biggest | ||
308 | */ | ||
309 | up = (u32 *)get_property(dp, "linux,bootx-addr", &len); | ||
310 | if (up == NULL) | ||
311 | up = (u32 *)get_property(dp, "address", &len); | ||
312 | if (up && len == sizeof(u32)) | ||
313 | addr_prop = *up; | ||
314 | |||
315 | for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) | ||
316 | != NULL; i++) { | ||
317 | int match_addrp = 0; | ||
318 | |||
319 | if (!(flags & IORESOURCE_MEM)) | ||
320 | continue; | ||
321 | if (asize < rsize) | ||
322 | continue; | ||
323 | rstart = of_translate_address(dp, addrp); | ||
324 | if (rstart == OF_BAD_ADDR) | ||
325 | continue; | ||
326 | if (addr_prop && (rstart <= addr_prop) && | ||
327 | ((rstart + asize) >= (addr_prop + rsize))) | ||
328 | match_addrp = 1; | ||
329 | if (match_addrp) { | ||
330 | address = addr_prop; | ||
331 | break; | ||
332 | } | ||
333 | if (rsize > max_size) { | ||
334 | max_size = rsize; | ||
335 | address = OF_BAD_ADDR; | ||
336 | } | ||
337 | |||
338 | if (address == OF_BAD_ADDR) | ||
339 | address = rstart; | ||
340 | } | ||
341 | if (address == OF_BAD_ADDR && addr_prop) | ||
342 | address = (u64)addr_prop; | ||
343 | if (address != OF_BAD_ADDR) { | ||
344 | /* kludge for valkyrie */ | ||
345 | if (strcmp(dp->name, "valkyrie") == 0) | ||
346 | address += 0x1000; | ||
347 | offb_init_fb(dp->name, dp->full_name, width, height, depth, | ||
348 | pitch, address, dp); | ||
349 | } | ||
350 | } | 237 | } |
351 | 238 | ||
352 | static void __init offb_init_fb(const char *name, const char *full_name, | 239 | static void __init offb_init_fb(const char *name, const char *full_name, |
@@ -403,45 +290,39 @@ static void __init offb_init_fb(const char *name, const char *full_name, | |||
403 | 290 | ||
404 | par->cmap_type = cmap_unknown; | 291 | par->cmap_type = cmap_unknown; |
405 | if (depth == 8) { | 292 | if (depth == 8) { |
406 | |||
407 | /* Palette hacks disabled for now */ | 293 | /* Palette hacks disabled for now */ |
408 | #if 0 | ||
409 | if (dp && !strncmp(name, "ATY,Rage128", 11)) { | 294 | if (dp && !strncmp(name, "ATY,Rage128", 11)) { |
410 | unsigned long regbase = dp->addrs[2].address; | 295 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); |
411 | par->cmap_adr = ioremap(regbase, 0x1FFF); | 296 | if (par->cmap_adr) |
412 | par->cmap_type = cmap_r128; | 297 | par->cmap_type = cmap_r128; |
413 | } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) | 298 | } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) |
414 | || !strncmp(name, "ATY,RageM3p12A", 14))) { | 299 | || !strncmp(name, "ATY,RageM3p12A", 14))) { |
415 | unsigned long regbase = | 300 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); |
416 | dp->parent->addrs[2].address; | 301 | if (par->cmap_adr) |
417 | par->cmap_adr = ioremap(regbase, 0x1FFF); | 302 | par->cmap_type = cmap_M3A; |
418 | par->cmap_type = cmap_M3A; | ||
419 | } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { | 303 | } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { |
420 | unsigned long regbase = | 304 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); |
421 | dp->parent->addrs[2].address; | 305 | if (par->cmap_adr) |
422 | par->cmap_adr = ioremap(regbase, 0x1FFF); | 306 | par->cmap_type = cmap_M3B; |
423 | par->cmap_type = cmap_M3B; | ||
424 | } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { | 307 | } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { |
425 | unsigned long regbase = dp->addrs[1].address; | 308 | par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); |
426 | par->cmap_adr = ioremap(regbase, 0x1FFF); | 309 | if (par->cmap_adr) |
427 | par->cmap_type = cmap_radeon; | 310 | par->cmap_type = cmap_radeon; |
428 | } else if (!strncmp(name, "ATY,", 4)) { | 311 | } else if (!strncmp(name, "ATY,", 4)) { |
429 | unsigned long base = address & 0xff000000UL; | 312 | unsigned long base = address & 0xff000000UL; |
430 | par->cmap_adr = | 313 | par->cmap_adr = |
431 | ioremap(base + 0x7ff000, 0x1000) + 0xcc0; | 314 | ioremap(base + 0x7ff000, 0x1000) + 0xcc0; |
432 | par->cmap_data = par->cmap_adr + 1; | 315 | par->cmap_data = par->cmap_adr + 1; |
433 | par->cmap_type = cmap_m64; | 316 | par->cmap_type = cmap_m64; |
434 | } else if (device_is_compatible(dp, "pci1014,b7")) { | 317 | } else if (dp && device_is_compatible(dp, "pci1014,b7")) { |
435 | unsigned long regbase = dp->addrs[0].address; | 318 | par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); |
436 | par->cmap_adr = ioremap(regbase + 0x6000, 0x1000); | 319 | if (par->cmap_adr) |
437 | par->cmap_type = cmap_gxt2000; | 320 | par->cmap_type = cmap_gxt2000; |
438 | } | 321 | } |
439 | #endif | 322 | fix->visual = (par->cmap_type != cmap_unknown) ? |
440 | fix->visual = par->cmap_adr ? FB_VISUAL_PSEUDOCOLOR | 323 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; |
441 | : FB_VISUAL_STATIC_PSEUDOCOLOR; | ||
442 | } else | 324 | } else |
443 | fix->visual = /* par->cmap_adr ? FB_VISUAL_DIRECTCOLOR | 325 | fix->visual = FB_VISUAL_TRUECOLOR; |
444 | : */ FB_VISUAL_TRUECOLOR; | ||
445 | 326 | ||
446 | var->xoffset = var->yoffset = 0; | 327 | var->xoffset = var->yoffset = 0; |
447 | switch (depth) { | 328 | switch (depth) { |
@@ -521,5 +402,139 @@ static void __init offb_init_fb(const char *name, const char *full_name, | |||
521 | info->node, full_name); | 402 | info->node, full_name); |
522 | } | 403 | } |
523 | 404 | ||
405 | |||
406 | static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) | ||
407 | { | ||
408 | unsigned int len; | ||
409 | int i, width = 640, height = 480, depth = 8, pitch = 640; | ||
410 | unsigned int flags, rsize, addr_prop = 0; | ||
411 | unsigned long max_size = 0; | ||
412 | u64 rstart, address = OF_BAD_ADDR; | ||
413 | u32 *pp, *addrp, *up; | ||
414 | u64 asize; | ||
415 | |||
416 | pp = (u32 *)get_property(dp, "linux,bootx-depth", &len); | ||
417 | if (pp == NULL) | ||
418 | pp = (u32 *)get_property(dp, "depth", &len); | ||
419 | if (pp && len == sizeof(u32)) | ||
420 | depth = *pp; | ||
421 | |||
422 | pp = (u32 *)get_property(dp, "linux,bootx-width", &len); | ||
423 | if (pp == NULL) | ||
424 | pp = (u32 *)get_property(dp, "width", &len); | ||
425 | if (pp && len == sizeof(u32)) | ||
426 | width = *pp; | ||
427 | |||
428 | pp = (u32 *)get_property(dp, "linux,bootx-height", &len); | ||
429 | if (pp == NULL) | ||
430 | pp = (u32 *)get_property(dp, "height", &len); | ||
431 | if (pp && len == sizeof(u32)) | ||
432 | height = *pp; | ||
433 | |||
434 | pp = (u32 *)get_property(dp, "linux,bootx-linebytes", &len); | ||
435 | if (pp == NULL) | ||
436 | pp = (u32 *)get_property(dp, "linebytes", &len); | ||
437 | if (pp && len == sizeof(u32)) | ||
438 | pitch = *pp; | ||
439 | else | ||
440 | pitch = width * ((depth + 7) / 8); | ||
441 | |||
442 | rsize = (unsigned long)pitch * (unsigned long)height; | ||
443 | |||
444 | /* Ok, now we try to figure out the address of the framebuffer. | ||
445 | * | ||
446 | * Unfortunately, Open Firmware doesn't provide a standard way to do | ||
447 | * so. All we can do is a dodgy heuristic that happens to work in | ||
448 | * practice. On most machines, the "address" property contains what | ||
449 | * we need, though not on Matrox cards found in IBM machines. What I've | ||
450 | * found that appears to give good results is to go through the PCI | ||
451 | * ranges and pick one that is both big enough and if possible encloses | ||
452 | * the "address" property. If none match, we pick the biggest | ||
453 | */ | ||
454 | up = (u32 *)get_property(dp, "linux,bootx-addr", &len); | ||
455 | if (up == NULL) | ||
456 | up = (u32 *)get_property(dp, "address", &len); | ||
457 | if (up && len == sizeof(u32)) | ||
458 | addr_prop = *up; | ||
459 | |||
460 | /* Hack for when BootX is passing us */ | ||
461 | if (no_real_node) | ||
462 | goto skip_addr; | ||
463 | |||
464 | for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) | ||
465 | != NULL; i++) { | ||
466 | int match_addrp = 0; | ||
467 | |||
468 | if (!(flags & IORESOURCE_MEM)) | ||
469 | continue; | ||
470 | if (asize < rsize) | ||
471 | continue; | ||
472 | rstart = of_translate_address(dp, addrp); | ||
473 | if (rstart == OF_BAD_ADDR) | ||
474 | continue; | ||
475 | if (addr_prop && (rstart <= addr_prop) && | ||
476 | ((rstart + asize) >= (addr_prop + rsize))) | ||
477 | match_addrp = 1; | ||
478 | if (match_addrp) { | ||
479 | address = addr_prop; | ||
480 | break; | ||
481 | } | ||
482 | if (rsize > max_size) { | ||
483 | max_size = rsize; | ||
484 | address = OF_BAD_ADDR; | ||
485 | } | ||
486 | |||
487 | if (address == OF_BAD_ADDR) | ||
488 | address = rstart; | ||
489 | } | ||
490 | skip_addr: | ||
491 | if (address == OF_BAD_ADDR && addr_prop) | ||
492 | address = (u64)addr_prop; | ||
493 | if (address != OF_BAD_ADDR) { | ||
494 | /* kludge for valkyrie */ | ||
495 | if (strcmp(dp->name, "valkyrie") == 0) | ||
496 | address += 0x1000; | ||
497 | offb_init_fb(no_real_node ? "bootx" : dp->name, | ||
498 | no_real_node ? "display" : dp->full_name, | ||
499 | width, height, depth, pitch, address, | ||
500 | no_real_node ? dp : NULL); | ||
501 | } | ||
502 | } | ||
503 | |||
504 | static int __init offb_init(void) | ||
505 | { | ||
506 | struct device_node *dp = NULL, *boot_disp = NULL; | ||
507 | |||
508 | if (fb_get_options("offb", NULL)) | ||
509 | return -ENODEV; | ||
510 | |||
511 | /* Check if we have a MacOS display without a node spec */ | ||
512 | if (get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) { | ||
513 | /* The old code tried to work out which node was the MacOS | ||
514 | * display based on the address. I'm dropping that since the | ||
515 | * lack of a node spec only happens with old BootX versions | ||
516 | * (users can update) and with this code, they'll still get | ||
517 | * a display (just not the palette hacks). | ||
518 | */ | ||
519 | offb_init_nodriver(of_chosen, 1); | ||
520 | } | ||
521 | |||
522 | for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { | ||
523 | if (get_property(dp, "linux,opened", NULL) && | ||
524 | get_property(dp, "linux,boot-display", NULL)) { | ||
525 | boot_disp = dp; | ||
526 | offb_init_nodriver(dp, 0); | ||
527 | } | ||
528 | } | ||
529 | for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { | ||
530 | if (get_property(dp, "linux,opened", NULL) && | ||
531 | dp != boot_disp) | ||
532 | offb_init_nodriver(dp, 0); | ||
533 | } | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | |||
524 | module_init(offb_init); | 539 | module_init(offb_init); |
525 | MODULE_LICENSE("GPL"); | 540 | MODULE_LICENSE("GPL"); |