diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-07-17 01:05:22 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2008-07-21 20:39:36 -0400 |
commit | 57a20d8fb0d2a05abe40abd6bb29e3f923721f1b (patch) | |
tree | a80237a27e6f5d9809e5a218692b93086ec7afd4 /drivers/video/offb.c | |
parent | c134fd868fb5137984ce1b7a58f3f6dd4171615e (diff) |
fbdev: Teaches offb about palette on radeon r5xx/r6xx
The offb driver already has a collection of hacks to be able to set
the palette on various chips. This adds support for r5xx/r6xx radeons.
This is needed as offb is the only console solution on these currently
and the firmware in some cases sets a really bad color palette. This
fixes using some Radeon X16xx on the Powerstation for example.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers/video/offb.c')
-rw-r--r-- | drivers/video/offb.c | 192 |
1 files changed, 142 insertions, 50 deletions
diff --git a/drivers/video/offb.c b/drivers/video/offb.c index d7b3dcc0dc43..e1d9eeb1aeaf 100644 --- a/drivers/video/offb.c +++ b/drivers/video/offb.c | |||
@@ -47,6 +47,7 @@ enum { | |||
47 | cmap_M3B, /* ATI Rage Mobility M3 Head B */ | 47 | cmap_M3B, /* ATI Rage Mobility M3 Head B */ |
48 | cmap_radeon, /* ATI Radeon */ | 48 | cmap_radeon, /* ATI Radeon */ |
49 | cmap_gxt2000, /* IBM GXT2000 */ | 49 | cmap_gxt2000, /* IBM GXT2000 */ |
50 | cmap_avivo, /* ATI R5xx */ | ||
50 | }; | 51 | }; |
51 | 52 | ||
52 | struct offb_par { | 53 | struct offb_par { |
@@ -58,26 +59,36 @@ struct offb_par { | |||
58 | 59 | ||
59 | struct offb_par default_par; | 60 | struct offb_par default_par; |
60 | 61 | ||
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 | 62 | #ifdef CONFIG_PPC32 |
70 | extern boot_infos_t *boot_infos; | 63 | extern boot_infos_t *boot_infos; |
71 | #endif | 64 | #endif |
72 | 65 | ||
73 | static struct fb_ops offb_ops = { | 66 | /* Definitions used by the Avivo palette hack */ |
74 | .owner = THIS_MODULE, | 67 | #define AVIVO_DC_LUT_RW_SELECT 0x6480 |
75 | .fb_setcolreg = offb_setcolreg, | 68 | #define AVIVO_DC_LUT_RW_MODE 0x6484 |
76 | .fb_blank = offb_blank, | 69 | #define AVIVO_DC_LUT_RW_INDEX 0x6488 |
77 | .fb_fillrect = cfb_fillrect, | 70 | #define AVIVO_DC_LUT_SEQ_COLOR 0x648c |
78 | .fb_copyarea = cfb_copyarea, | 71 | #define AVIVO_DC_LUT_PWL_DATA 0x6490 |
79 | .fb_imageblit = cfb_imageblit, | 72 | #define AVIVO_DC_LUT_30_COLOR 0x6494 |
80 | }; | 73 | #define AVIVO_DC_LUT_READ_PIPE_SELECT 0x6498 |
74 | #define AVIVO_DC_LUT_WRITE_EN_MASK 0x649c | ||
75 | #define AVIVO_DC_LUT_AUTOFILL 0x64a0 | ||
76 | |||
77 | #define AVIVO_DC_LUTA_CONTROL 0x64c0 | ||
78 | #define AVIVO_DC_LUTA_BLACK_OFFSET_BLUE 0x64c4 | ||
79 | #define AVIVO_DC_LUTA_BLACK_OFFSET_GREEN 0x64c8 | ||
80 | #define AVIVO_DC_LUTA_BLACK_OFFSET_RED 0x64cc | ||
81 | #define AVIVO_DC_LUTA_WHITE_OFFSET_BLUE 0x64d0 | ||
82 | #define AVIVO_DC_LUTA_WHITE_OFFSET_GREEN 0x64d4 | ||
83 | #define AVIVO_DC_LUTA_WHITE_OFFSET_RED 0x64d8 | ||
84 | |||
85 | #define AVIVO_DC_LUTB_CONTROL 0x6cc0 | ||
86 | #define AVIVO_DC_LUTB_BLACK_OFFSET_BLUE 0x6cc4 | ||
87 | #define AVIVO_DC_LUTB_BLACK_OFFSET_GREEN 0x6cc8 | ||
88 | #define AVIVO_DC_LUTB_BLACK_OFFSET_RED 0x6ccc | ||
89 | #define AVIVO_DC_LUTB_WHITE_OFFSET_BLUE 0x6cd0 | ||
90 | #define AVIVO_DC_LUTB_WHITE_OFFSET_GREEN 0x6cd4 | ||
91 | #define AVIVO_DC_LUTB_WHITE_OFFSET_RED 0x6cd8 | ||
81 | 92 | ||
82 | /* | 93 | /* |
83 | * Set a single color register. The values supplied are already | 94 | * Set a single color register. The values supplied are already |
@@ -160,6 +171,17 @@ static int offb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | |||
160 | out_le32(((unsigned __iomem *) par->cmap_adr) + regno, | 171 | out_le32(((unsigned __iomem *) par->cmap_adr) + regno, |
161 | (red << 16 | green << 8 | blue)); | 172 | (red << 16 | green << 8 | blue)); |
162 | break; | 173 | break; |
174 | case cmap_avivo: | ||
175 | /* Write to both LUTs for now */ | ||
176 | writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
177 | writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
178 | writel(((red) << 22) | ((green) << 12) | ((blue) << 2), | ||
179 | par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
180 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
181 | writeb(regno, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
182 | writel(((red) << 22) | ((green) << 12) | ((blue) << 2), | ||
183 | par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
184 | break; | ||
163 | } | 185 | } |
164 | 186 | ||
165 | return 0; | 187 | return 0; |
@@ -216,12 +238,59 @@ static int offb_blank(int blank, struct fb_info *info) | |||
216 | out_le32(((unsigned __iomem *) par->cmap_adr) + i, | 238 | out_le32(((unsigned __iomem *) par->cmap_adr) + i, |
217 | 0); | 239 | 0); |
218 | break; | 240 | break; |
241 | case cmap_avivo: | ||
242 | writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
243 | writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
244 | writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
245 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
246 | writeb(i, par->cmap_adr + AVIVO_DC_LUT_RW_INDEX); | ||
247 | writel(0, par->cmap_adr + AVIVO_DC_LUT_30_COLOR); | ||
248 | break; | ||
219 | } | 249 | } |
220 | } else | 250 | } else |
221 | fb_set_cmap(&info->cmap, info); | 251 | fb_set_cmap(&info->cmap, info); |
222 | return 0; | 252 | return 0; |
223 | } | 253 | } |
224 | 254 | ||
255 | static int offb_set_par(struct fb_info *info) | ||
256 | { | ||
257 | struct offb_par *par = (struct offb_par *) info->par; | ||
258 | |||
259 | /* On avivo, initialize palette control */ | ||
260 | if (par->cmap_type == cmap_avivo) { | ||
261 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_CONTROL); | ||
262 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_BLUE); | ||
263 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_GREEN); | ||
264 | writel(0, par->cmap_adr + AVIVO_DC_LUTA_BLACK_OFFSET_RED); | ||
265 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_BLUE); | ||
266 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_GREEN); | ||
267 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTA_WHITE_OFFSET_RED); | ||
268 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_CONTROL); | ||
269 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_BLUE); | ||
270 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_GREEN); | ||
271 | writel(0, par->cmap_adr + AVIVO_DC_LUTB_BLACK_OFFSET_RED); | ||
272 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_BLUE); | ||
273 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_GREEN); | ||
274 | writel(0x0000ffff, par->cmap_adr + AVIVO_DC_LUTB_WHITE_OFFSET_RED); | ||
275 | writel(1, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
276 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); | ||
277 | writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); | ||
278 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_SELECT); | ||
279 | writel(0, par->cmap_adr + AVIVO_DC_LUT_RW_MODE); | ||
280 | writel(0x0000003f, par->cmap_adr + AVIVO_DC_LUT_WRITE_EN_MASK); | ||
281 | } | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static struct fb_ops offb_ops = { | ||
286 | .owner = THIS_MODULE, | ||
287 | .fb_setcolreg = offb_setcolreg, | ||
288 | .fb_set_par = offb_set_par, | ||
289 | .fb_blank = offb_blank, | ||
290 | .fb_fillrect = cfb_fillrect, | ||
291 | .fb_copyarea = cfb_copyarea, | ||
292 | .fb_imageblit = cfb_imageblit, | ||
293 | }; | ||
225 | 294 | ||
226 | static void __iomem *offb_map_reg(struct device_node *np, int index, | 295 | static void __iomem *offb_map_reg(struct device_node *np, int index, |
227 | unsigned long offset, unsigned long size) | 296 | unsigned long offset, unsigned long size) |
@@ -245,6 +314,59 @@ static void __iomem *offb_map_reg(struct device_node *np, int index, | |||
245 | return ioremap(taddr + offset, size); | 314 | return ioremap(taddr + offset, size); |
246 | } | 315 | } |
247 | 316 | ||
317 | static void offb_init_palette_hacks(struct fb_info *info, struct device_node *dp, | ||
318 | const char *name, unsigned long address) | ||
319 | { | ||
320 | struct offb_par *par = (struct offb_par *) info->par; | ||
321 | |||
322 | if (dp && !strncmp(name, "ATY,Rage128", 11)) { | ||
323 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | ||
324 | if (par->cmap_adr) | ||
325 | par->cmap_type = cmap_r128; | ||
326 | } else if (dp && (!strncmp(name, "ATY,RageM3pA", 12) | ||
327 | || !strncmp(name, "ATY,RageM3p12A", 14))) { | ||
328 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | ||
329 | if (par->cmap_adr) | ||
330 | par->cmap_type = cmap_M3A; | ||
331 | } else if (dp && !strncmp(name, "ATY,RageM3pB", 12)) { | ||
332 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | ||
333 | if (par->cmap_adr) | ||
334 | par->cmap_type = cmap_M3B; | ||
335 | } else if (dp && !strncmp(name, "ATY,Rage6", 9)) { | ||
336 | par->cmap_adr = offb_map_reg(dp, 1, 0, 0x1fff); | ||
337 | if (par->cmap_adr) | ||
338 | par->cmap_type = cmap_radeon; | ||
339 | } else if (!strncmp(name, "ATY,", 4)) { | ||
340 | unsigned long base = address & 0xff000000UL; | ||
341 | par->cmap_adr = | ||
342 | ioremap(base + 0x7ff000, 0x1000) + 0xcc0; | ||
343 | par->cmap_data = par->cmap_adr + 1; | ||
344 | par->cmap_type = cmap_m64; | ||
345 | } else if (dp && (of_device_is_compatible(dp, "pci1014,b7") || | ||
346 | of_device_is_compatible(dp, "pci1014,21c"))) { | ||
347 | par->cmap_adr = offb_map_reg(dp, 0, 0x6000, 0x1000); | ||
348 | if (par->cmap_adr) | ||
349 | par->cmap_type = cmap_gxt2000; | ||
350 | } else if (dp && !strncmp(name, "vga,Display-", 12)) { | ||
351 | /* Look for AVIVO initialized by SLOF */ | ||
352 | struct device_node *pciparent = of_get_parent(dp); | ||
353 | const u32 *vid, *did; | ||
354 | vid = of_get_property(pciparent, "vendor-id", NULL); | ||
355 | did = of_get_property(pciparent, "device-id", NULL); | ||
356 | /* This will match most R5xx */ | ||
357 | if (vid && did && *vid == 0x1002 && | ||
358 | ((*did >= 0x7100 && *did < 0x7800) || | ||
359 | (*did >= 0x9400))) { | ||
360 | par->cmap_adr = offb_map_reg(pciparent, 2, 0, 0x10000); | ||
361 | if (par->cmap_adr) | ||
362 | par->cmap_type = cmap_avivo; | ||
363 | } | ||
364 | of_node_put(pciparent); | ||
365 | } | ||
366 | info->fix.visual = (par->cmap_type != cmap_unknown) ? | ||
367 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_STATIC_PSEUDOCOLOR; | ||
368 | } | ||
369 | |||
248 | static void __init offb_init_fb(const char *name, const char *full_name, | 370 | static void __init offb_init_fb(const char *name, const char *full_name, |
249 | int width, int height, int depth, | 371 | int width, int height, int depth, |
250 | int pitch, unsigned long address, | 372 | int pitch, unsigned long address, |
@@ -283,6 +405,7 @@ static void __init offb_init_fb(const char *name, const char *full_name, | |||
283 | 405 | ||
284 | fix = &info->fix; | 406 | fix = &info->fix; |
285 | var = &info->var; | 407 | var = &info->var; |
408 | info->par = par; | ||
286 | 409 | ||
287 | strcpy(fix->id, "OFfb "); | 410 | strcpy(fix->id, "OFfb "); |
288 | strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb ")); | 411 | strncat(fix->id, name, sizeof(fix->id) - sizeof("OFfb ")); |
@@ -298,39 +421,9 @@ static void __init offb_init_fb(const char *name, const char *full_name, | |||
298 | fix->type_aux = 0; | 421 | fix->type_aux = 0; |
299 | 422 | ||
300 | par->cmap_type = cmap_unknown; | 423 | par->cmap_type = cmap_unknown; |
301 | if (depth == 8) { | 424 | if (depth == 8) |
302 | if (dp && !strncmp(name, "ATY,Rage128", 11)) { | 425 | offb_init_palette_hacks(info, dp, name, address); |
303 | par->cmap_adr = offb_map_reg(dp, 2, 0, 0x1fff); | 426 | else |
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; | 427 | fix->visual = FB_VISUAL_TRUECOLOR; |
335 | 428 | ||
336 | var->xoffset = var->yoffset = 0; | 429 | var->xoffset = var->yoffset = 0; |
@@ -395,7 +488,6 @@ static void __init offb_init_fb(const char *name, const char *full_name, | |||
395 | 488 | ||
396 | info->fbops = &offb_ops; | 489 | info->fbops = &offb_ops; |
397 | info->screen_base = ioremap(address, fix->smem_len); | 490 | info->screen_base = ioremap(address, fix->smem_len); |
398 | info->par = par; | ||
399 | info->pseudo_palette = (void *) (info + 1); | 491 | info->pseudo_palette = (void *) (info + 1); |
400 | info->flags = FBINFO_DEFAULT | foreign_endian; | 492 | info->flags = FBINFO_DEFAULT | foreign_endian; |
401 | 493 | ||