]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/video/offb.c
[POWERPC] More offb/bootx fixes
[karo-tx-linux.git] / drivers / video / offb.c
1 /*
2  *  linux/drivers/video/offb.c -- Open Firmware based frame buffer device
3  *
4  *      Copyright (C) 1997 Geert Uytterhoeven
5  *
6  *  This driver is partly based on the PowerMac console driver:
7  *
8  *      Copyright (C) 1996 Paul Mackerras
9  *
10  *  This file is subject to the terms and conditions of the GNU General Public
11  *  License. See the file COPYING in the main directory of this archive for
12  *  more details.
13  */
14
15 #include <linux/module.h>
16 #include <linux/kernel.h>
17 #include <linux/errno.h>
18 #include <linux/string.h>
19 #include <linux/mm.h>
20 #include <linux/tty.h>
21 #include <linux/slab.h>
22 #include <linux/vmalloc.h>
23 #include <linux/delay.h>
24 #include <linux/interrupt.h>
25 #include <linux/fb.h>
26 #include <linux/init.h>
27 #include <linux/ioport.h>
28 #include <linux/pci.h>
29 #include <asm/io.h>
30 #include <asm/prom.h>
31
32 #ifdef CONFIG_PPC64
33 #include <asm/pci-bridge.h>
34 #endif
35
36 #ifdef CONFIG_PPC32
37 #include <asm/bootx.h>
38 #endif
39
40 #include "macmodes.h"
41
42 /* Supported palette hacks */
43 enum {
44         cmap_unknown,
45         cmap_m64,               /* ATI Mach64 */
46         cmap_r128,              /* ATI Rage128 */
47         cmap_M3A,               /* ATI Rage Mobility M3 Head A */
48         cmap_M3B,               /* ATI Rage Mobility M3 Head B */
49         cmap_radeon,            /* ATI Radeon */
50         cmap_gxt2000,           /* IBM GXT2000 */
51 };
52
53 struct offb_par {
54         volatile void __iomem *cmap_adr;
55         volatile void __iomem *cmap_data;
56         int cmap_type;
57         int blanked;
58 };
59
60 struct offb_par default_par;
61
62     /*
63      *  Interface used by the world
64      */
65
66 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
67                           u_int transp, struct fb_info *info);
68 static int offb_blank(int blank, struct fb_info *info);
69
70 #ifdef CONFIG_PPC32
71 extern boot_infos_t *boot_infos;
72 #endif
73
74 static struct fb_ops offb_ops = {
75         .owner          = THIS_MODULE,
76         .fb_setcolreg   = offb_setcolreg,
77         .fb_blank       = offb_blank,
78         .fb_fillrect    = cfb_fillrect,
79         .fb_copyarea    = cfb_copyarea,
80         .fb_imageblit   = cfb_imageblit,
81 };
82
83     /*
84      *  Set a single color register. The values supplied are already
85      *  rounded down to the hardware's capabilities (according to the
86      *  entries in the var structure). Return != 0 for invalid regno.
87      */
88
89 static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
90                           u_int transp, struct fb_info *info)
91 {
92         struct offb_par *par = (struct offb_par *) info->par;
93         int i, depth;
94         u32 *pal = info->pseudo_palette;
95
96         depth = info->var.bits_per_pixel;
97         if (depth == 16)
98                 depth = (info->var.green.length == 5) ? 15 : 16;
99
100         if (regno > 255 ||
101             (depth == 16 && regno > 63) ||
102             (depth == 15 && regno > 31))
103                 return 1;
104
105         if (regno < 16) {
106                 switch (depth) {
107                 case 15:
108                         pal[regno] = (regno << 10) | (regno << 5) | regno;
109                         break;
110                 case 16:
111                         pal[regno] = (regno << 11) | (regno << 5) | regno;
112                         break;
113                 case 24:
114                         pal[regno] = (regno << 16) | (regno << 8) | regno;
115                         break;
116                 case 32:
117                         i = (regno << 8) | regno;
118                         pal[regno] = (i << 16) | i;
119                         break;
120                 }
121         }
122
123         red >>= 8;
124         green >>= 8;
125         blue >>= 8;
126
127         if (!par->cmap_adr)
128                 return 0;
129
130         switch (par->cmap_type) {
131         case cmap_m64:
132                 writeb(regno, par->cmap_adr);
133                 writeb(red, par->cmap_data);
134                 writeb(green, par->cmap_data);
135                 writeb(blue, par->cmap_data);
136                 break;
137         case cmap_M3A:
138                 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
139                 out_le32(par->cmap_adr + 0x58,
140                          in_le32(par->cmap_adr + 0x58) & ~0x20);
141         case cmap_r128:
142                 /* Set palette index & data */
143                 out_8(par->cmap_adr + 0xb0, regno);
144                 out_le32(par->cmap_adr + 0xb4,
145                          (red << 16 | green << 8 | blue));
146                 break;
147         case cmap_M3B:
148                 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
149                 out_le32(par->cmap_adr + 0x58,
150                          in_le32(par->cmap_adr + 0x58) | 0x20);
151                 /* Set palette index & data */
152                 out_8(par->cmap_adr + 0xb0, regno);
153                 out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
154                 break;
155         case cmap_radeon:
156                 /* Set palette index & data (could be smarter) */
157                 out_8(par->cmap_adr + 0xb0, regno);
158                 out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
159                 break;
160         case cmap_gxt2000:
161                 out_le32((unsigned __iomem *) par->cmap_adr + regno,
162                          (red << 16 | green << 8 | blue));
163                 break;
164         }
165
166         return 0;
167 }
168
169     /*
170      *  Blank the display.
171      */
172
173 static int offb_blank(int blank, struct fb_info *info)
174 {
175         struct offb_par *par = (struct offb_par *) info->par;
176         int i, j;
177
178         if (!par->cmap_adr)
179                 return 0;
180
181         if (!par->blanked)
182                 if (!blank)
183                         return 0;
184
185         par->blanked = blank;
186
187         if (blank)
188                 for (i = 0; i < 256; i++) {
189                         switch (par->cmap_type) {
190                         case cmap_m64:
191                                 writeb(i, par->cmap_adr);
192                                 for (j = 0; j < 3; j++)
193                                         writeb(0, par->cmap_data);
194                                 break;
195                         case cmap_M3A:
196                                 /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
197                                 out_le32(par->cmap_adr + 0x58,
198                                          in_le32(par->cmap_adr + 0x58) & ~0x20);
199                         case cmap_r128:
200                                 /* Set palette index & data */
201                                 out_8(par->cmap_adr + 0xb0, i);
202                                 out_le32(par->cmap_adr + 0xb4, 0);
203                                 break;
204                         case cmap_M3B:
205                                 /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
206                                 out_le32(par->cmap_adr + 0x58,
207                                          in_le32(par->cmap_adr + 0x58) | 0x20);
208                                 /* Set palette index & data */
209                                 out_8(par->cmap_adr + 0xb0, i);
210                                 out_le32(par->cmap_adr + 0xb4, 0);
211                                 break;
212                         case cmap_radeon:
213                                 out_8(par->cmap_adr + 0xb0, i);
214                                 out_le32(par->cmap_adr + 0xb4, 0);
215                                 break;
216                         case cmap_gxt2000:
217                                 out_le32((unsigned __iomem *) par->cmap_adr + i,
218                                          0);
219                                 break;
220                         }
221         } else
222                 fb_set_cmap(&info->cmap, info);
223         return 0;
224 }
225
226
227 static void __iomem *offb_map_reg(struct device_node *np, int index,
228                                   unsigned long offset, unsigned long size)
229 {
230         struct resource r;
231
232         if (of_address_to_resource(np, index, &r))
233                 return 0;
234         if ((r.start + offset + size) > r.end)
235                 return 0;
236         return ioremap(r.start + offset, size);
237 }
238
239 static void __init offb_init_fb(const char *name, const char *full_name,
240                                 int width, int height, int depth,
241                                 int pitch, unsigned long address,
242                                 struct device_node *dp)
243 {
244         unsigned long res_size = pitch * height * (depth + 7) / 8;
245         struct offb_par *par = &default_par;
246         unsigned long res_start = address;
247         struct fb_fix_screeninfo *fix;
248         struct fb_var_screeninfo *var;
249         struct fb_info *info;
250         int size;
251
252         if (!request_mem_region(res_start, res_size, "offb"))
253                 return;
254
255         printk(KERN_INFO
256                "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
257                width, height, name, address, depth, pitch);
258         if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {
259                 printk(KERN_ERR "%s: can't use depth = %d\n", full_name,
260                        depth);
261                 release_mem_region(res_start, res_size);
262                 return;
263         }
264
265         size = sizeof(struct fb_info) + sizeof(u32) * 17;
266
267         info = kmalloc(size, GFP_ATOMIC);
268         
269         if (info == 0) {
270                 release_mem_region(res_start, res_size);
271                 return;
272         }
273         memset(info, 0, size);
274
275         fix = &info->fix;
276         var = &info->var;
277
278         strcpy(fix->id, "OFfb ");
279         strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
280         fix->id[sizeof(fix->id) - 1] = '\0';
281
282         var->xres = var->xres_virtual = width;
283         var->yres = var->yres_virtual = height;
284         fix->line_length = pitch;
285
286         fix->smem_start = address;
287         fix->smem_len = pitch * height;
288         fix->type = FB_TYPE_PACKED_PIXELS;
289         fix->type_aux = 0;
290
291         par->cmap_type = cmap_unknown;
292         if (depth == 8) {
293                 /* Palette hacks disabled for now */
294                 if (dp && !strncmp(name, "ATY,Rage128", 11)) {
295                         par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
296                         if (par->cmap_adr)
297                                 par->cmap_type = cmap_r128;
298                 } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12)
299                                   || !strncmp(name, "ATY,RageM3p12A", 14))) {
300                         par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
301                         if (par->cmap_adr)
302                                 par->cmap_type = cmap_M3A;
303                 } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) {
304                         par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
305                         if (par->cmap_adr)
306                                 par->cmap_type = cmap_M3B;
307                 } else if (dp && !strncmp(name, "ATY,Rage6", 9)) {
308                         par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);
309                         if (par->cmap_adr)
310                                 par->cmap_type = cmap_radeon;
311                 } else if (!strncmp(name, "ATY,", 4)) {
312                         unsigned long base = address & 0xff000000UL;
313                         par->cmap_adr =
314                             ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
315                         par->cmap_data = par->cmap_adr + 1;
316                         par->cmap_type = cmap_m64;
317                 } else if (dp && device_is_compatible(dp, "pci1014,b7")) {
318                         par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);
319                         if (par->cmap_adr)
320                                 par->cmap_type = cmap_gxt2000;
321                 }
322                 fix->visual = (par->cmap_type != cmap_unknown) ?
323                         FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;
324         } else
325                 fix->visual = FB_VISUAL_TRUECOLOR;
326
327         var->xoffset = var->yoffset = 0;
328         switch (depth) {
329         case 8:
330                 var->bits_per_pixel = 8;
331                 var->red.offset = 0;
332                 var->red.length = 8;
333                 var->green.offset = 0;
334                 var->green.length = 8;
335                 var->blue.offset = 0;
336                 var->blue.length = 8;
337                 var->transp.offset = 0;
338                 var->transp.length = 0;
339                 break;
340         case 15:                /* RGB 555 */
341                 var->bits_per_pixel = 16;
342                 var->red.offset = 10;
343                 var->red.length = 5;
344                 var->green.offset = 5;
345                 var->green.length = 5;
346                 var->blue.offset = 0;
347                 var->blue.length = 5;
348                 var->transp.offset = 0;
349                 var->transp.length = 0;
350                 break;
351         case 16:                /* RGB 565 */
352                 var->bits_per_pixel = 16;
353                 var->red.offset = 11;
354                 var->red.length = 5;
355                 var->green.offset = 5;
356                 var->green.length = 6;
357                 var->blue.offset = 0;
358                 var->blue.length = 5;
359                 var->transp.offset = 0;
360                 var->transp.length = 0;
361                 break;
362         case 32:                /* RGB 888 */
363                 var->bits_per_pixel = 32;
364                 var->red.offset = 16;
365                 var->red.length = 8;
366                 var->green.offset = 8;
367                 var->green.length = 8;
368                 var->blue.offset = 0;
369                 var->blue.length = 8;
370                 var->transp.offset = 24;
371                 var->transp.length = 8;
372                 break;
373         }
374         var->red.msb_right = var->green.msb_right = var->blue.msb_right =
375             var->transp.msb_right = 0;
376         var->grayscale = 0;
377         var->nonstd = 0;
378         var->activate = 0;
379         var->height = var->width = -1;
380         var->pixclock = 10000;
381         var->left_margin = var->right_margin = 16;
382         var->upper_margin = var->lower_margin = 16;
383         var->hsync_len = var->vsync_len = 8;
384         var->sync = 0;
385         var->vmode = FB_VMODE_NONINTERLACED;
386
387         info->fbops = &offb_ops;
388         info->screen_base = ioremap(address, fix->smem_len);
389         info->par = par;
390         info->pseudo_palette = (void *) (info + 1);
391         info->flags = FBINFO_DEFAULT;
392
393         fb_alloc_cmap(&info->cmap, 256, 0);
394
395         if (register_framebuffer(info) < 0) {
396                 kfree(info);
397                 release_mem_region(res_start, res_size);
398                 return;
399         }
400
401         printk(KERN_INFO "fb%d: Open Firmware frame buffer device on %s\n",
402                info->node, full_name);
403 }
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
539 module_init(offb_init);
540 MODULE_LICENSE("GPL");