OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [video/] [offb.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
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/slab.h>
21
#include <linux/vmalloc.h>
22
#include <linux/delay.h>
23
#include <linux/interrupt.h>
24
#include <linux/fb.h>
25
#include <linux/init.h>
26
#include <linux/ioport.h>
27
#include <linux/pci.h>
28
#include <asm/io.h>
29
#include <asm/prom.h>
30
 
31
#ifdef CONFIG_PPC64
32
#include <asm/pci-bridge.h>
33
#endif
34
 
35
#ifdef CONFIG_PPC32
36
#include <asm/bootx.h>
37
#endif
38
 
39
#include "macmodes.h"
40
 
41
/* Supported palette hacks */
42
enum {
43
        cmap_unknown,
44
        cmap_m64,               /* ATI Mach64 */
45
        cmap_r128,              /* ATI Rage128 */
46
        cmap_M3A,               /* ATI Rage Mobility M3 Head A */
47
        cmap_M3B,               /* ATI Rage Mobility M3 Head B */
48
        cmap_radeon,            /* ATI Radeon */
49
        cmap_gxt2000,           /* IBM GXT2000 */
50
};
51
 
52
struct offb_par {
53
        volatile void __iomem *cmap_adr;
54
        volatile void __iomem *cmap_data;
55
        int cmap_type;
56
        int blanked;
57
};
58
 
59
struct offb_par default_par;
60
 
61
    /*
62
     *  Interface used by the world
63
     */
64
 
65
static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
66
                          u_int transp, struct fb_info *info);
67
static int offb_blank(int blank, struct fb_info *info);
68
 
69
#ifdef CONFIG_PPC32
70
extern boot_infos_t *boot_infos;
71
#endif
72
 
73
static struct fb_ops offb_ops = {
74
        .owner          = THIS_MODULE,
75
        .fb_setcolreg   = offb_setcolreg,
76
        .fb_blank       = offb_blank,
77
        .fb_fillrect    = cfb_fillrect,
78
        .fb_copyarea    = cfb_copyarea,
79
        .fb_imageblit   = cfb_imageblit,
80
};
81
 
82
    /*
83
     *  Set a single color register. The values supplied are already
84
     *  rounded down to the hardware's capabilities (according to the
85
     *  entries in the var structure). Return != 0 for invalid regno.
86
     */
87
 
88
static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
89
                          u_int transp, struct fb_info *info)
90
{
91
        struct offb_par *par = (struct offb_par *) info->par;
92
        int i, depth;
93
        u32 *pal = info->pseudo_palette;
94
 
95
        depth = info->var.bits_per_pixel;
96
        if (depth == 16)
97
                depth = (info->var.green.length == 5) ? 15 : 16;
98
 
99
        if (regno > 255 ||
100
            (depth == 16 && regno > 63) ||
101
            (depth == 15 && regno > 31))
102
                return 1;
103
 
104
        if (regno < 16) {
105
                switch (depth) {
106
                case 15:
107
                        pal[regno] = (regno << 10) | (regno << 5) | regno;
108
                        break;
109
                case 16:
110
                        pal[regno] = (regno << 11) | (regno << 5) | regno;
111
                        break;
112
                case 24:
113
                        pal[regno] = (regno << 16) | (regno << 8) | regno;
114
                        break;
115
                case 32:
116
                        i = (regno << 8) | regno;
117
                        pal[regno] = (i << 16) | i;
118
                        break;
119
                }
120
        }
121
 
122
        red >>= 8;
123
        green >>= 8;
124
        blue >>= 8;
125
 
126
        if (!par->cmap_adr)
127
                return 0;
128
 
129
        switch (par->cmap_type) {
130
        case cmap_m64:
131
                writeb(regno, par->cmap_adr);
132
                writeb(red, par->cmap_data);
133
                writeb(green, par->cmap_data);
134
                writeb(blue, par->cmap_data);
135
                break;
136
        case cmap_M3A:
137
                /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
138
                out_le32(par->cmap_adr + 0x58,
139
                         in_le32(par->cmap_adr + 0x58) & ~0x20);
140
        case cmap_r128:
141
                /* Set palette index & data */
142
                out_8(par->cmap_adr + 0xb0, regno);
143
                out_le32(par->cmap_adr + 0xb4,
144
                         (red << 16 | green << 8 | blue));
145
                break;
146
        case cmap_M3B:
147
                /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
148
                out_le32(par->cmap_adr + 0x58,
149
                         in_le32(par->cmap_adr + 0x58) | 0x20);
150
                /* Set palette index & data */
151
                out_8(par->cmap_adr + 0xb0, regno);
152
                out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
153
                break;
154
        case cmap_radeon:
155
                /* Set palette index & data (could be smarter) */
156
                out_8(par->cmap_adr + 0xb0, regno);
157
                out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue));
158
                break;
159
        case cmap_gxt2000:
160
                out_le32(((unsigned __iomem *) par->cmap_adr) + regno,
161
                         (red << 16 | green << 8 | blue));
162
                break;
163
        }
164
 
165
        return 0;
166
}
167
 
168
    /*
169
     *  Blank the display.
170
     */
171
 
172
static int offb_blank(int blank, struct fb_info *info)
173
{
174
        struct offb_par *par = (struct offb_par *) info->par;
175
        int i, j;
176
 
177
        if (!par->cmap_adr)
178
                return 0;
179
 
180
        if (!par->blanked)
181
                if (!blank)
182
                        return 0;
183
 
184
        par->blanked = blank;
185
 
186
        if (blank)
187
                for (i = 0; i < 256; i++) {
188
                        switch (par->cmap_type) {
189
                        case cmap_m64:
190
                                writeb(i, par->cmap_adr);
191
                                for (j = 0; j < 3; j++)
192
                                        writeb(0, par->cmap_data);
193
                                break;
194
                        case cmap_M3A:
195
                                /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
196
                                out_le32(par->cmap_adr + 0x58,
197
                                         in_le32(par->cmap_adr + 0x58) & ~0x20);
198
                        case cmap_r128:
199
                                /* Set palette index & data */
200
                                out_8(par->cmap_adr + 0xb0, i);
201
                                out_le32(par->cmap_adr + 0xb4, 0);
202
                                break;
203
                        case cmap_M3B:
204
                                /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
205
                                out_le32(par->cmap_adr + 0x58,
206
                                         in_le32(par->cmap_adr + 0x58) | 0x20);
207
                                /* Set palette index & data */
208
                                out_8(par->cmap_adr + 0xb0, i);
209
                                out_le32(par->cmap_adr + 0xb4, 0);
210
                                break;
211
                        case cmap_radeon:
212
                                out_8(par->cmap_adr + 0xb0, i);
213
                                out_le32(par->cmap_adr + 0xb4, 0);
214
                                break;
215
                        case cmap_gxt2000:
216
                                out_le32(((unsigned __iomem *) par->cmap_adr) + i,
217
                                         0);
218
                                break;
219
                        }
220
        } else
221
                fb_set_cmap(&info->cmap, info);
222
        return 0;
223
}
224
 
225
 
226
static void __iomem *offb_map_reg(struct device_node *np, int index,
227
                                  unsigned long offset, unsigned long size)
228
{
229
        const u32 *addrp;
230
        u64 asize, taddr;
231
        unsigned int flags;
232
 
233
        addrp = of_get_pci_address(np, index, &asize, &flags);
234
        if (addrp == NULL)
235
                addrp = of_get_address(np, index, &asize, &flags);
236
        if (addrp == NULL)
237
                return NULL;
238
        if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
239
                return NULL;
240
        if ((offset + size) > asize)
241
                return NULL;
242
        taddr = of_translate_address(np, addrp);
243
        if (taddr == OF_BAD_ADDR)
244
                return NULL;
245
        return ioremap(taddr + offset, size);
246
}
247
 
248
static void __init offb_init_fb(const char *name, const char *full_name,
249
                                int width, int height, int depth,
250
                                int pitch, unsigned long address,
251
                                struct device_node *dp)
252
{
253
        unsigned long res_size = pitch * height * (depth + 7) / 8;
254
        struct offb_par *par = &default_par;
255
        unsigned long res_start = address;
256
        struct fb_fix_screeninfo *fix;
257
        struct fb_var_screeninfo *var;
258
        struct fb_info *info;
259
        int size;
260
 
261
        if (!request_mem_region(res_start, res_size, "offb"))
262
                return;
263
 
264
        printk(KERN_INFO
265
               "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n",
266
               width, height, name, address, depth, pitch);
267
        if (depth != 8 && depth != 15 && depth != 16 && depth != 32) {
268
                printk(KERN_ERR "%s: can't use depth = %d\n", full_name,
269
                       depth);
270
                release_mem_region(res_start, res_size);
271
                return;
272
        }
273
 
274
        size = sizeof(struct fb_info) + sizeof(u32) * 16;
275
 
276
        info = kmalloc(size, GFP_ATOMIC);
277
 
278
        if (info == 0) {
279
                release_mem_region(res_start, res_size);
280
                return;
281
        }
282
        memset(info, 0, size);
283
 
284
        fix = &info->fix;
285
        var = &info->var;
286
 
287
        strcpy(fix->id, "OFfb ");
288
        strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb "));
289
        fix->id[sizeof(fix->id) - 1] = '\0';
290
 
291
        var->xres = var->xres_virtual = width;
292
        var->yres = var->yres_virtual = height;
293
        fix->line_length = pitch;
294
 
295
        fix->smem_start = address;
296
        fix->smem_len = pitch * height;
297
        fix->type = FB_TYPE_PACKED_PIXELS;
298
        fix->type_aux = 0;
299
 
300
        par->cmap_type = cmap_unknown;
301
        if (depth == 8) {
302
                if (dp && !strncmp(name, "ATY,Rage128", 11)) {
303
                        par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
304
                        if (par->cmap_adr)
305
                                par->cmap_type = cmap_r128;
306
                } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12)
307
                                  || !strncmp(name, "ATY,RageM3p12A", 14))) {
308
                        par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
309
                        if (par->cmap_adr)
310
                                par->cmap_type = cmap_M3A;
311
                } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) {
312
                        par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff);
313
                        if (par->cmap_adr)
314
                                par->cmap_type = cmap_M3B;
315
                } else if (dp && !strncmp(name, "ATY,Rage6", 9)) {
316
                        par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff);
317
                        if (par->cmap_adr)
318
                                par->cmap_type = cmap_radeon;
319
                } else if (!strncmp(name, "ATY,", 4)) {
320
                        unsigned long base = address & 0xff000000UL;
321
                        par->cmap_adr =
322
                            ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
323
                        par->cmap_data = par->cmap_adr + 1;
324
                        par->cmap_type = cmap_m64;
325
                } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") ||
326
                                  of_device_is_compatible(dp, "pci1014,21c"))) {
327
                        par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000);
328
                        if (par->cmap_adr)
329
                                par->cmap_type = cmap_gxt2000;
330
                }
331
                fix->visual = (par->cmap_type != cmap_unknown) ?
332
                        FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR;
333
        } else
334
                fix->visual = FB_VISUAL_TRUECOLOR;
335
 
336
        var->xoffset = var->yoffset = 0;
337
        switch (depth) {
338
        case 8:
339
                var->bits_per_pixel = 8;
340
                var->red.offset = 0;
341
                var->red.length = 8;
342
                var->green.offset = 0;
343
                var->green.length = 8;
344
                var->blue.offset = 0;
345
                var->blue.length = 8;
346
                var->transp.offset = 0;
347
                var->transp.length = 0;
348
                break;
349
        case 15:                /* RGB 555 */
350
                var->bits_per_pixel = 16;
351
                var->red.offset = 10;
352
                var->red.length = 5;
353
                var->green.offset = 5;
354
                var->green.length = 5;
355
                var->blue.offset = 0;
356
                var->blue.length = 5;
357
                var->transp.offset = 0;
358
                var->transp.length = 0;
359
                break;
360
        case 16:                /* RGB 565 */
361
                var->bits_per_pixel = 16;
362
                var->red.offset = 11;
363
                var->red.length = 5;
364
                var->green.offset = 5;
365
                var->green.length = 6;
366
                var->blue.offset = 0;
367
                var->blue.length = 5;
368
                var->transp.offset = 0;
369
                var->transp.length = 0;
370
                break;
371
        case 32:                /* RGB 888 */
372
                var->bits_per_pixel = 32;
373
                var->red.offset = 16;
374
                var->red.length = 8;
375
                var->green.offset = 8;
376
                var->green.length = 8;
377
                var->blue.offset = 0;
378
                var->blue.length = 8;
379
                var->transp.offset = 24;
380
                var->transp.length = 8;
381
                break;
382
        }
383
        var->red.msb_right = var->green.msb_right = var->blue.msb_right =
384
            var->transp.msb_right = 0;
385
        var->grayscale = 0;
386
        var->nonstd = 0;
387
        var->activate = 0;
388
        var->height = var->width = -1;
389
        var->pixclock = 10000;
390
        var->left_margin = var->right_margin = 16;
391
        var->upper_margin = var->lower_margin = 16;
392
        var->hsync_len = var->vsync_len = 8;
393
        var->sync = 0;
394
        var->vmode = FB_VMODE_NONINTERLACED;
395
 
396
        info->fbops = &offb_ops;
397
        info->screen_base = ioremap(address, fix->smem_len);
398
        info->par = par;
399
        info->pseudo_palette = (void *) (info + 1);
400
        info->flags = FBINFO_DEFAULT;
401
 
402
        fb_alloc_cmap(&info->cmap, 256, 0);
403
 
404
        if (register_framebuffer(info) < 0) {
405
                iounmap(par->cmap_adr);
406
                par->cmap_adr = NULL;
407
                iounmap(info->screen_base);
408
                kfree(info);
409
                release_mem_region(res_start, res_size);
410
                return;
411
        }
412
 
413
        printk(KERN_INFO "fb%d: Open Firmware frame buffer device on %s\n",
414
               info->node, full_name);
415
}
416
 
417
 
418
static void __init offb_init_nodriver(struct device_node *dp, int no_real_node)
419
{
420
        unsigned int len;
421
        int i, width = 640, height = 480, depth = 8, pitch = 640;
422
        unsigned int flags, rsize, addr_prop = 0;
423
        unsigned long max_size = 0;
424
        u64 rstart, address = OF_BAD_ADDR;
425
        const u32 *pp, *addrp, *up;
426
        u64 asize;
427
 
428
        pp = of_get_property(dp, "linux,bootx-depth", &len);
429
        if (pp == NULL)
430
                pp = of_get_property(dp, "depth", &len);
431
        if (pp && len == sizeof(u32))
432
                depth = *pp;
433
 
434
        pp = of_get_property(dp, "linux,bootx-width", &len);
435
        if (pp == NULL)
436
                pp = of_get_property(dp, "width", &len);
437
        if (pp && len == sizeof(u32))
438
                width = *pp;
439
 
440
        pp = of_get_property(dp, "linux,bootx-height", &len);
441
        if (pp == NULL)
442
                pp = of_get_property(dp, "height", &len);
443
        if (pp && len == sizeof(u32))
444
                height = *pp;
445
 
446
        pp = of_get_property(dp, "linux,bootx-linebytes", &len);
447
        if (pp == NULL)
448
                pp = of_get_property(dp, "linebytes", &len);
449
        if (pp && len == sizeof(u32) && (*pp != 0xffffffffu))
450
                pitch = *pp;
451
        else
452
                pitch = width * ((depth + 7) / 8);
453
 
454
        rsize = (unsigned long)pitch * (unsigned long)height;
455
 
456
        /* Ok, now we try to figure out the address of the framebuffer.
457
         *
458
         * Unfortunately, Open Firmware doesn't provide a standard way to do
459
         * so. All we can do is a dodgy heuristic that happens to work in
460
         * practice. On most machines, the "address" property contains what
461
         * we need, though not on Matrox cards found in IBM machines. What I've
462
         * found that appears to give good results is to go through the PCI
463
         * ranges and pick one that is both big enough and if possible encloses
464
         * the "address" property. If none match, we pick the biggest
465
         */
466
        up = of_get_property(dp, "linux,bootx-addr", &len);
467
        if (up == NULL)
468
                up = of_get_property(dp, "address", &len);
469
        if (up && len == sizeof(u32))
470
                addr_prop = *up;
471
 
472
        /* Hack for when BootX is passing us */
473
        if (no_real_node)
474
                goto skip_addr;
475
 
476
        for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags))
477
                     != NULL; i++) {
478
                int match_addrp = 0;
479
 
480
                if (!(flags & IORESOURCE_MEM))
481
                        continue;
482
                if (asize < rsize)
483
                        continue;
484
                rstart = of_translate_address(dp, addrp);
485
                if (rstart == OF_BAD_ADDR)
486
                        continue;
487
                if (addr_prop && (rstart <= addr_prop) &&
488
                    ((rstart + asize) >= (addr_prop + rsize)))
489
                        match_addrp = 1;
490
                if (match_addrp) {
491
                        address = addr_prop;
492
                        break;
493
                }
494
                if (rsize > max_size) {
495
                        max_size = rsize;
496
                        address = OF_BAD_ADDR;
497
                }
498
 
499
                if (address == OF_BAD_ADDR)
500
                        address = rstart;
501
        }
502
 skip_addr:
503
        if (address == OF_BAD_ADDR && addr_prop)
504
                address = (u64)addr_prop;
505
        if (address != OF_BAD_ADDR) {
506
                /* kludge for valkyrie */
507
                if (strcmp(dp->name, "valkyrie") == 0)
508
                        address += 0x1000;
509
                offb_init_fb(no_real_node ? "bootx" : dp->name,
510
                             no_real_node ? "display" : dp->full_name,
511
                             width, height, depth, pitch, address,
512
                             no_real_node ? NULL : dp);
513
        }
514
}
515
 
516
static int __init offb_init(void)
517
{
518
        struct device_node *dp = NULL, *boot_disp = NULL;
519
 
520
        if (fb_get_options("offb", NULL))
521
                return -ENODEV;
522
 
523
        /* Check if we have a MacOS display without a node spec */
524
        if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) {
525
                /* The old code tried to work out which node was the MacOS
526
                 * display based on the address. I'm dropping that since the
527
                 * lack of a node spec only happens with old BootX versions
528
                 * (users can update) and with this code, they'll still get
529
                 * a display (just not the palette hacks).
530
                 */
531
                offb_init_nodriver(of_chosen, 1);
532
        }
533
 
534
        for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) {
535
                if (of_get_property(dp, "linux,opened", NULL) &&
536
                    of_get_property(dp, "linux,boot-display", NULL)) {
537
                        boot_disp = dp;
538
                        offb_init_nodriver(dp, 0);
539
                }
540
        }
541
        for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) {
542
                if (of_get_property(dp, "linux,opened", NULL) &&
543
                    dp != boot_disp)
544
                        offb_init_nodriver(dp, 0);
545
        }
546
 
547
        return 0;
548
}
549
 
550
 
551
module_init(offb_init);
552
MODULE_LICENSE("GPL");

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.