diff options
Diffstat (limited to 'drivers/video/fbdev/offb.c')
-rw-r--r-- | drivers/video/fbdev/offb.c | 687 |
1 files changed, 687 insertions, 0 deletions
diff --git a/drivers/video/fbdev/offb.c b/drivers/video/fbdev/offb.c new file mode 100644 index 000000000000..7d44d669d5b6 --- /dev/null +++ b/drivers/video/fbdev/offb.c | |||
@@ -0,0 +1,687 @@ | |||
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/vmalloc.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/of.h> | ||
23 | #include <linux/of_address.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 | |||
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_simple, /* 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 | cmap_avivo, /* ATI R5xx */ | ||
51 | cmap_qemu, /* qemu vga */ | ||
52 | }; | ||
53 | |||
54 | struct offb_par { | ||
55 | volatile void __iomem *cmap_adr; | ||
56 | volatile void __iomem *cmap_data; | ||
57 | int cmap_type; | ||
58 | int blanked; | ||
59 | }; | ||
60 | |||
61 | struct offb_par default_par; | ||
62 | |||
63 | #ifdef CONFIG_PPC32 | ||
64 | extern boot_infos_t *boot_infos; | ||
65 | #endif | ||
66 | |||
67 | /* Definitions used by the Avivo palette hack */ | ||
68 | #define AVIVO_DC_LUT_RW_SELECT 0x6480 | ||
69 | #define AVIVO_DC_LUT_RW_MODE 0x6484 | ||
70 | #define AVIVO_DC_LUT_RW_INDEX 0x6488 | ||
71 | #define AVIVO_DC_LUT_SEQ_COLOR 0x648c | ||
72 | #define AVIVO_DC_LUT_PWL_DATA 0x6490 | ||
73 | #define AVIVO_DC_LUT_30_COLOR 0x6494 | ||
74 | #define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 | ||
75 | #define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c | ||
76 | #define AVIVO_DC_LUT_AUTOFILL 0x64a0 | ||
77 | |||
78 | #define AVIVO_DC_LUTA_CONTROL 0x64c0 | ||
79 | #define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 | ||
80 | #define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 | ||
81 | #define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc | ||
82 | #define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 | ||
83 | #define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 | ||
84 | #define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 | ||
85 | |||
86 | #define AVIVO_DC_LUTB_CONTROL 0x6cc0 | ||
87 | #define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 | ||
88 | #define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 | ||
89 | #define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc | ||
90 | #define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 | ||
91 | #define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 | ||
92 | #define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 | ||
93 | |||
94 | #define FB_RIGHT_POS(p, bpp) (fb_be_math(p) ? 0 : (32 - (bpp))) | ||
95 | |||
96 | static inline u32 offb_cmap_byteswap(struct fb_info *info, u32 value) | ||
97 | { | ||
98 | u32 bpp = info->var.bits_per_pixel; | ||
99 | |||
100 | return cpu_to_be32(value) >> FB_RIGHT_POS(info, bpp); | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | * Set a single color register. The values supplied are already | ||
105 | * rounded down to the hardware's capabilities (according to the | ||
106 | * entries in the var structure). Return != 0 for invalid regno. | ||
107 | */ | ||
108 | |||
109 | static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | ||
110 | u_int transp, struct fb_info *info) | ||
111 | { | ||
112 | struct offb_par *par = (struct offb_par *) info->par; | ||
113 | |||
114 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { | ||
115 | u32 *pal = info->pseudo_palette; | ||
116 | u32 cr = red >> (16 - info->var.red.length); | ||
117 | u32 cg = green >> (16 - info->var.green.length); | ||
118 | u32 cb = blue >> (16 - info->var.blue.length); | ||
119 | u32 value; | ||
120 | |||
121 | if (regno >= 16) | ||
122 | return -EINVAL; | ||
123 | |||
124 | value = (cr << info->var.red.offset) | | ||
125 | (cg << info->var.green.offset) | | ||
126 | (cb << info->var.blue.offset); | ||
127 | if (info->var.transp.length > 0) { | ||
128 | u32 mask = (1 << info->var.transp.length) - 1; | ||
129 | mask <<= info->var.transp.offset; | ||
130 | value |= mask; | ||
131 | } | ||
132 | pal[regno] = offb_cmap_byteswap(info, value); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | if (regno > 255) | ||
137 | return -EINVAL; | ||
138 | |||
139 | red >>= 8; | ||
140 | green >>= 8; | ||
141 | blue >>= 8; | ||
142 | |||
143 | if (!par->cmap_adr) | ||
144 | return 0; | ||
145 | |||
146 | switch (par->cmap_type) { | ||
147 | case cmap_simple: | ||
148 | writeb(regno, par->cmap_adr); | ||
149 | writeb(red, par->cmap_data); | ||
150 | writeb(green, par->cmap_data); | ||
151 | writeb(blue, par->cmap_data); | ||
152 | break; | ||
153 | case cmap_M3A: | ||
154 | /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ | ||
155 | out_le32(par->cmap_adr + 0x58, | ||
156 | in_le32(par->cmap_adr + 0x58) & ~0x20); | ||
157 | case cmap_r128: | ||
158 | /* Set palette index & data */ | ||
159 | out_8(par->cmap_adr + 0xb0, regno); | ||
160 | out_le32(par->cmap_adr + 0xb4, | ||
161 | (red << 16 | green << 8 | blue)); | ||
162 | break; | ||
163 | case cmap_M3B: | ||
164 | /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ | ||
165 | out_le32(par->cmap_adr + 0x58, | ||
166 | in_le32(par->cmap_adr + 0x58) | 0x20); | ||
167 | /* Set palette index & data */ | ||
168 | out_8(par->cmap_adr + 0xb0, regno); | ||
169 | out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue)); | ||
170 | break; | ||
171 | case cmap_radeon: | ||
172 | /* Set palette index & data (could be smarter) */ | ||
173 | out_8(par->cmap_adr + 0xb0, regno); | ||
174 | out_le32(par->cmap_adr + 0xb4, (red << 16 | green << 8 | blue)); | ||
175 | break; | ||
176 | case cmap_gxt2000: | ||
177 | out_le32(((unsigned __iomem *) par->cmap_adr) + regno, | ||
178 | (red << 16 | green << 8 | blue)); | ||
179 | break; | ||
180 | case cmap_avivo: | ||
181 | /* Write to both LUTs for now */ | ||
182 | writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
183 | writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
184 | writel(((red) << 22) | ((green) << 12) | ((blue) << 2), | ||
185 | par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
186 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
187 | writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
188 | writel(((red) << 22) | ((green) << 12) | ((blue) << 2), | ||
189 | par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
190 | break; | ||
191 | } | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * Blank the display. | ||
198 | */ | ||
199 | |||
200 | static int offb_blank(int blank, struct fb_info *info) | ||
201 | { | ||
202 | struct offb_par *par = (struct offb_par *) info->par; | ||
203 | int i, j; | ||
204 | |||
205 | if (!par->cmap_adr) | ||
206 | return 0; | ||
207 | |||
208 | if (!par->blanked) | ||
209 | if (!blank) | ||
210 | return 0; | ||
211 | |||
212 | par->blanked = blank; | ||
213 | |||
214 | if (blank) | ||
215 | for (i = 0; i < 256; i++) { | ||
216 | switch (par->cmap_type) { | ||
217 | case cmap_simple: | ||
218 | writeb(i, par->cmap_adr); | ||
219 | for (j = 0; j < 3; j++) | ||
220 | writeb(0, par->cmap_data); | ||
221 | break; | ||
222 | case cmap_M3A: | ||
223 | /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */ | ||
224 | out_le32(par->cmap_adr + 0x58, | ||
225 | in_le32(par->cmap_adr + 0x58) & ~0x20); | ||
226 | case cmap_r128: | ||
227 | /* Set palette index & data */ | ||
228 | out_8(par->cmap_adr + 0xb0, i); | ||
229 | out_le32(par->cmap_adr + 0xb4, 0); | ||
230 | break; | ||
231 | case cmap_M3B: | ||
232 | /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */ | ||
233 | out_le32(par->cmap_adr + 0x58, | ||
234 | in_le32(par->cmap_adr + 0x58) | 0x20); | ||
235 | /* Set palette index & data */ | ||
236 | out_8(par->cmap_adr + 0xb0, i); | ||
237 | out_le32(par->cmap_adr + 0xb4, 0); | ||
238 | break; | ||
239 | case cmap_radeon: | ||
240 | out_8(par->cmap_adr + 0xb0, i); | ||
241 | out_le32(par->cmap_adr + 0xb4, 0); | ||
242 | break; | ||
243 | case cmap_gxt2000: | ||
244 | out_le32(((unsigned __iomem *) par->cmap_adr) + i, | ||
245 | 0); | ||
246 | break; | ||
247 | case cmap_avivo: | ||
248 | writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
249 | writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
250 | writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
251 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
252 | writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
253 | writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
254 | break; | ||
255 | } | ||
256 | } else | ||
257 | fb_set_cmap(&info->cmap, info); | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int offb_set_par(struct fb_info *info) | ||
262 | { | ||
263 | struct offb_par *par = (struct offb_par *) info->par; | ||
264 | |||
265 | /* On avivo, initialize palette control */ | ||
266 | if (par->cmap_type == cmap_avivo) { | ||
267 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL); | ||
268 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE); | ||
269 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN); | ||
270 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED); | ||
271 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE); | ||
272 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN); | ||
273 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED); | ||
274 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL); | ||
275 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE); | ||
276 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN); | ||
277 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED); | ||
278 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE); | ||
279 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN); | ||
280 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED); | ||
281 | writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
282 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); | ||
283 | writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); | ||
284 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
285 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); | ||
286 | writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); | ||
287 | } | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static void offb_destroy(struct fb_info *info) | ||
292 | { | ||
293 | if (info->screen_base) | ||
294 | iounmap(info->screen_base); | ||
295 | release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); | ||
296 | framebuffer_release(info); | ||
297 | } | ||
298 | |||
299 | static struct fb_ops offb_ops = { | ||
300 | .owner = THIS_MODULE, | ||
301 | .fb_destroy = offb_destroy, | ||
302 | .fb_setcolreg = offb_setcolreg, | ||
303 | .fb_set_par = offb_set_par, | ||
304 | .fb_blank = offb_blank, | ||
305 | .fb_fillrect = cfb_fillrect, | ||
306 | .fb_copyarea = cfb_copyarea, | ||
307 | .fb_imageblit = cfb_imageblit, | ||
308 | }; | ||
309 | |||
310 | static void __iomem *offb_map_reg(struct device_node *np, int index, | ||
311 | unsigned long offset, unsigned long size) | ||
312 | { | ||
313 | const __be32 *addrp; | ||
314 | u64 asize, taddr; | ||
315 | unsigned int flags; | ||
316 | |||
317 | addrp = of_get_pci_address(np, index, &asize, &flags); | ||
318 | if (addrp == NULL) | ||
319 | addrp = of_get_address(np, index, &asize, &flags); | ||
320 | if (addrp == NULL) | ||
321 | return NULL; | ||
322 | if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) | ||
323 | return NULL; | ||
324 | if ((offset + size) > asize) | ||
325 | return NULL; | ||
326 | taddr = of_translate_address(np, addrp); | ||
327 | if (taddr == OF_BAD_ADDR) | ||
328 | return NULL; | ||
329 | return ioremap(taddr + offset, size); | ||
330 | } | ||
331 | |||
332 | static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp, | ||
333 | const char *name, unsigned long address) | ||
334 | { | ||
335 | struct offb_par *par = (struct offb_par *) info->par; | ||
336 | |||
337 | if (dp && !strncmp(name, "ATY,Rage128", 11)) { | ||
338 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | ||
339 | if (par->cmap_adr) | ||
340 | par->cmap_type = cmap_r128; | ||
341 | } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) | ||
342 | || !strncmp(name, "ATY,RageM3p12A", 14))) { | ||
343 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | ||
344 | if (par->cmap_adr) | ||
345 | par->cmap_type = cmap_M3A; | ||
346 | } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { | ||
347 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | ||
348 | if (par->cmap_adr) | ||
349 | par->cmap_type = cmap_M3B; | ||
350 | } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { | ||
351 | par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); | ||
352 | if (par->cmap_adr) | ||
353 | par->cmap_type = cmap_radeon; | ||
354 | } else if (!strncmp(name, "ATY,", 4)) { | ||
355 | unsigned long base = address & 0xff000000UL; | ||
356 | par->cmap_adr = | ||
357 | ioremap(base + 0x7ff000, 0x1000) + 0xcc0; | ||
358 | par->cmap_data = par->cmap_adr + 1; | ||
359 | par->cmap_type = cmap_simple; | ||
360 | } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") || | ||
361 | of_device_is_compatible(dp, "pci1014,21c"))) { | ||
362 | par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); | ||
363 | if (par->cmap_adr) | ||
364 | par->cmap_type = cmap_gxt2000; | ||
365 | } else if (dp && !strncmp(name, "vga,Display-", 12)) { | ||
366 | /* Look for AVIVO initialized by SLOF */ | ||
367 | struct device_node *pciparent = of_get_parent(dp); | ||
368 | const u32 *vid, *did; | ||
369 | vid = of_get_property(pciparent, "vendor-id", NULL); | ||
370 | did = of_get_property(pciparent, "device-id", NULL); | ||
371 | /* This will match most R5xx */ | ||
372 | if (vid && did && *vid == 0x1002 && | ||
373 | ((*did >= 0x7100 && *did < 0x7800) || | ||
374 | (*did >= 0x9400))) { | ||
375 | par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000); | ||
376 | if (par->cmap_adr) | ||
377 | par->cmap_type = cmap_avivo; | ||
378 | } | ||
379 | of_node_put(pciparent); | ||
380 | } else if (dp && of_device_is_compatible(dp, "qemu,std-vga")) { | ||
381 | #ifdef __BIG_ENDIAN | ||
382 | const __be32 io_of_addr[3] = { 0x01000000, 0x0, 0x0 }; | ||
383 | #else | ||
384 | const __be32 io_of_addr[3] = { 0x00000001, 0x0, 0x0 }; | ||
385 | #endif | ||
386 | u64 io_addr = of_translate_address(dp, io_of_addr); | ||
387 | if (io_addr != OF_BAD_ADDR) { | ||
388 | par->cmap_adr = ioremap(io_addr + 0x3c8, 2); | ||
389 | if (par->cmap_adr) { | ||
390 | par->cmap_type = cmap_simple; | ||
391 | par->cmap_data = par->cmap_adr + 1; | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | info->fix.visual = (par->cmap_type != cmap_unknown) ? | ||
396 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; | ||
397 | } | ||
398 | |||
399 | static void __init offb_init_fb(const char *name, const char *full_name, | ||
400 | int width, int height, int depth, | ||
401 | int pitch, unsigned long address, | ||
402 | int foreign_endian, struct device_node *dp) | ||
403 | { | ||
404 | unsigned long res_size = pitch * height; | ||
405 | struct offb_par *par = &default_par; | ||
406 | unsigned long res_start = address; | ||
407 | struct fb_fix_screeninfo *fix; | ||
408 | struct fb_var_screeninfo *var; | ||
409 | struct fb_info *info; | ||
410 | |||
411 | if (!request_mem_region(res_start, res_size, "offb")) | ||
412 | return; | ||
413 | |||
414 | printk(KERN_INFO | ||
415 | "Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d\n", | ||
416 | width, height, name, address, depth, pitch); | ||
417 | if (depth != 8 && depth != 15 && depth != 16 && depth != 32) { | ||
418 | printk(KERN_ERR "%s: can't use depth = %d\n", full_name, | ||
419 | depth); | ||
420 | release_mem_region(res_start, res_size); | ||
421 | return; | ||
422 | } | ||
423 | |||
424 | info = framebuffer_alloc(sizeof(u32) * 16, NULL); | ||
425 | |||
426 | if (info == 0) { | ||
427 | release_mem_region(res_start, res_size); | ||
428 | return; | ||
429 | } | ||
430 | |||
431 | fix = &info->fix; | ||
432 | var = &info->var; | ||
433 | info->par = par; | ||
434 | |||
435 | strcpy(fix->id, "OFfb "); | ||
436 | strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb ")); | ||
437 | fix->id[sizeof(fix->id) - 1] = '\0'; | ||
438 | |||
439 | var->xres = var->xres_virtual = width; | ||
440 | var->yres = var->yres_virtual = height; | ||
441 | fix->line_length = pitch; | ||
442 | |||
443 | fix->smem_start = address; | ||
444 | fix->smem_len = pitch * height; | ||
445 | fix->type = FB_TYPE_PACKED_PIXELS; | ||
446 | fix->type_aux = 0; | ||
447 | |||
448 | par->cmap_type = cmap_unknown; | ||
449 | if (depth == 8) | ||
450 | offb_init_palette_hacks(info, dp, name, address); | ||
451 | else | ||
452 | fix->visual = FB_VISUAL_TRUECOLOR; | ||
453 | |||
454 | var->xoffset = var->yoffset = 0; | ||
455 | switch (depth) { | ||
456 | case 8: | ||
457 | var->bits_per_pixel = 8; | ||
458 | var->red.offset = 0; | ||
459 | var->red.length = 8; | ||
460 | var->green.offset = 0; | ||
461 | var->green.length = 8; | ||
462 | var->blue.offset = 0; | ||
463 | var->blue.length = 8; | ||
464 | var->transp.offset = 0; | ||
465 | var->transp.length = 0; | ||
466 | break; | ||
467 | case 15: /* RGB 555 */ | ||
468 | var->bits_per_pixel = 16; | ||
469 | var->red.offset = 10; | ||
470 | var->red.length = 5; | ||
471 | var->green.offset = 5; | ||
472 | var->green.length = 5; | ||
473 | var->blue.offset = 0; | ||
474 | var->blue.length = 5; | ||
475 | var->transp.offset = 0; | ||
476 | var->transp.length = 0; | ||
477 | break; | ||
478 | case 16: /* RGB 565 */ | ||
479 | var->bits_per_pixel = 16; | ||
480 | var->red.offset = 11; | ||
481 | var->red.length = 5; | ||
482 | var->green.offset = 5; | ||
483 | var->green.length = 6; | ||
484 | var->blue.offset = 0; | ||
485 | var->blue.length = 5; | ||
486 | var->transp.offset = 0; | ||
487 | var->transp.length = 0; | ||
488 | break; | ||
489 | case 32: /* RGB 888 */ | ||
490 | var->bits_per_pixel = 32; | ||
491 | var->red.offset = 16; | ||
492 | var->red.length = 8; | ||
493 | var->green.offset = 8; | ||
494 | var->green.length = 8; | ||
495 | var->blue.offset = 0; | ||
496 | var->blue.length = 8; | ||
497 | var->transp.offset = 24; | ||
498 | var->transp.length = 8; | ||
499 | break; | ||
500 | } | ||
501 | var->red.msb_right = var->green.msb_right = var->blue.msb_right = | ||
502 | var->transp.msb_right = 0; | ||
503 | var->grayscale = 0; | ||
504 | var->nonstd = 0; | ||
505 | var->activate = 0; | ||
506 | var->height = var->width = -1; | ||
507 | var->pixclock = 10000; | ||
508 | var->left_margin = var->right_margin = 16; | ||
509 | var->upper_margin = var->lower_margin = 16; | ||
510 | var->hsync_len = var->vsync_len = 8; | ||
511 | var->sync = 0; | ||
512 | var->vmode = FB_VMODE_NONINTERLACED; | ||
513 | |||
514 | /* set offb aperture size for generic probing */ | ||
515 | info->apertures = alloc_apertures(1); | ||
516 | if (!info->apertures) | ||
517 | goto out_aper; | ||
518 | info->apertures->ranges[0].base = address; | ||
519 | info->apertures->ranges[0].size = fix->smem_len; | ||
520 | |||
521 | info->fbops = &offb_ops; | ||
522 | info->screen_base = ioremap(address, fix->smem_len); | ||
523 | info->pseudo_palette = (void *) (info + 1); | ||
524 | info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE | foreign_endian; | ||
525 | |||
526 | fb_alloc_cmap(&info->cmap, 256, 0); | ||
527 | |||
528 | if (register_framebuffer(info) < 0) | ||
529 | goto out_err; | ||
530 | |||
531 | fb_info(info, "Open Firmware frame buffer device on %s\n", full_name); | ||
532 | return; | ||
533 | |||
534 | out_err: | ||
535 | iounmap(info->screen_base); | ||
536 | out_aper: | ||
537 | iounmap(par->cmap_adr); | ||
538 | par->cmap_adr = NULL; | ||
539 | framebuffer_release(info); | ||
540 | release_mem_region(res_start, res_size); | ||
541 | } | ||
542 | |||
543 | |||
544 | static void __init offb_init_nodriver(struct device_node *dp, int no_real_node) | ||
545 | { | ||
546 | unsigned int len; | ||
547 | int i, width = 640, height = 480, depth = 8, pitch = 640; | ||
548 | unsigned int flags, rsize, addr_prop = 0; | ||
549 | unsigned long max_size = 0; | ||
550 | u64 rstart, address = OF_BAD_ADDR; | ||
551 | const __be32 *pp, *addrp, *up; | ||
552 | u64 asize; | ||
553 | int foreign_endian = 0; | ||
554 | |||
555 | #ifdef __BIG_ENDIAN | ||
556 | if (of_get_property(dp, "little-endian", NULL)) | ||
557 | foreign_endian = FBINFO_FOREIGN_ENDIAN; | ||
558 | #else | ||
559 | if (of_get_property(dp, "big-endian", NULL)) | ||
560 | foreign_endian = FBINFO_FOREIGN_ENDIAN; | ||
561 | #endif | ||
562 | |||
563 | pp = of_get_property(dp, "linux,bootx-depth", &len); | ||
564 | if (pp == NULL) | ||
565 | pp = of_get_property(dp, "depth", &len); | ||
566 | if (pp && len == sizeof(u32)) | ||
567 | depth = be32_to_cpup(pp); | ||
568 | |||
569 | pp = of_get_property(dp, "linux,bootx-width", &len); | ||
570 | if (pp == NULL) | ||
571 | pp = of_get_property(dp, "width", &len); | ||
572 | if (pp && len == sizeof(u32)) | ||
573 | width = be32_to_cpup(pp); | ||
574 | |||
575 | pp = of_get_property(dp, "linux,bootx-height", &len); | ||
576 | if (pp == NULL) | ||
577 | pp = of_get_property(dp, "height", &len); | ||
578 | if (pp && len == sizeof(u32)) | ||
579 | height = be32_to_cpup(pp); | ||
580 | |||
581 | pp = of_get_property(dp, "linux,bootx-linebytes", &len); | ||
582 | if (pp == NULL) | ||
583 | pp = of_get_property(dp, "linebytes", &len); | ||
584 | if (pp && len == sizeof(u32) && (*pp != 0xffffffffu)) | ||
585 | pitch = be32_to_cpup(pp); | ||
586 | else | ||
587 | pitch = width * ((depth + 7) / 8); | ||
588 | |||
589 | rsize = (unsigned long)pitch * (unsigned long)height; | ||
590 | |||
591 | /* Ok, now we try to figure out the address of the framebuffer. | ||
592 | * | ||
593 | * Unfortunately, Open Firmware doesn't provide a standard way to do | ||
594 | * so. All we can do is a dodgy heuristic that happens to work in | ||
595 | * practice. On most machines, the "address" property contains what | ||
596 | * we need, though not on Matrox cards found in IBM machines. What I've | ||
597 | * found that appears to give good results is to go through the PCI | ||
598 | * ranges and pick one that is both big enough and if possible encloses | ||
599 | * the "address" property. If none match, we pick the biggest | ||
600 | */ | ||
601 | up = of_get_property(dp, "linux,bootx-addr", &len); | ||
602 | if (up == NULL) | ||
603 | up = of_get_property(dp, "address", &len); | ||
604 | if (up && len == sizeof(u32)) | ||
605 | addr_prop = *up; | ||
606 | |||
607 | /* Hack for when BootX is passing us */ | ||
608 | if (no_real_node) | ||
609 | goto skip_addr; | ||
610 | |||
611 | for (i = 0; (addrp = of_get_address(dp, i, &asize, &flags)) | ||
612 | != NULL; i++) { | ||
613 | int match_addrp = 0; | ||
614 | |||
615 | if (!(flags & IORESOURCE_MEM)) | ||
616 | continue; | ||
617 | if (asize < rsize) | ||
618 | continue; | ||
619 | rstart = of_translate_address(dp, addrp); | ||
620 | if (rstart == OF_BAD_ADDR) | ||
621 | continue; | ||
622 | if (addr_prop && (rstart <= addr_prop) && | ||
623 | ((rstart + asize) >= (addr_prop + rsize))) | ||
624 | match_addrp = 1; | ||
625 | if (match_addrp) { | ||
626 | address = addr_prop; | ||
627 | break; | ||
628 | } | ||
629 | if (rsize > max_size) { | ||
630 | max_size = rsize; | ||
631 | address = OF_BAD_ADDR; | ||
632 | } | ||
633 | |||
634 | if (address == OF_BAD_ADDR) | ||
635 | address = rstart; | ||
636 | } | ||
637 | skip_addr: | ||
638 | if (address == OF_BAD_ADDR && addr_prop) | ||
639 | address = (u64)addr_prop; | ||
640 | if (address != OF_BAD_ADDR) { | ||
641 | /* kludge for valkyrie */ | ||
642 | if (strcmp(dp->name, "valkyrie") == 0) | ||
643 | address += 0x1000; | ||
644 | offb_init_fb(no_real_node ? "bootx" : dp->name, | ||
645 | no_real_node ? "display" : dp->full_name, | ||
646 | width, height, depth, pitch, address, | ||
647 | foreign_endian, no_real_node ? NULL : dp); | ||
648 | } | ||
649 | } | ||
650 | |||
651 | static int __init offb_init(void) | ||
652 | { | ||
653 | struct device_node *dp = NULL, *boot_disp = NULL; | ||
654 | |||
655 | if (fb_get_options("offb", NULL)) | ||
656 | return -ENODEV; | ||
657 | |||
658 | /* Check if we have a MacOS display without a node spec */ | ||
659 | if (of_get_property(of_chosen, "linux,bootx-noscreen", NULL) != NULL) { | ||
660 | /* The old code tried to work out which node was the MacOS | ||
661 | * display based on the address. I'm dropping that since the | ||
662 | * lack of a node spec only happens with old BootX versions | ||
663 | * (users can update) and with this code, they'll still get | ||
664 | * a display (just not the palette hacks). | ||
665 | */ | ||
666 | offb_init_nodriver(of_chosen, 1); | ||
667 | } | ||
668 | |||
669 | for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { | ||
670 | if (of_get_property(dp, "linux,opened", NULL) && | ||
671 | of_get_property(dp, "linux,boot-display", NULL)) { | ||
672 | boot_disp = dp; | ||
673 | offb_init_nodriver(dp, 0); | ||
674 | } | ||
675 | } | ||
676 | for (dp = NULL; (dp = of_find_node_by_type(dp, "display"));) { | ||
677 | if (of_get_property(dp, "linux,opened", NULL) && | ||
678 | dp != boot_disp) | ||
679 | offb_init_nodriver(dp, 0); | ||
680 | } | ||
681 | |||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | |||
686 | module_init(offb_init); | ||
687 | MODULE_LICENSE("GPL"); | ||