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 | |
| 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>
| -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 * | ||
